diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-29 10:46:47 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-11-02 12:02:10 +0000 |
commit | 99677208ff3b216fdfec551fbe548da5520cd6fb (patch) | |
tree | 476a4865c10320249360e859d8fdd3e01833b03a /chromium/weblayer/browser | |
parent | c30a6232df03e1efbd9f3b226777b07e087a1122 (diff) | |
download | qtwebengine-chromium-99677208ff3b216fdfec551fbe548da5520cd6fb.tar.gz |
BASELINE: Update Chromium to 86.0.4240.124
Change-Id: Ide0ff151e94cd665ae6521a446995d34a9d1d644
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/weblayer/browser')
299 files changed, 6940 insertions, 5290 deletions
diff --git a/chromium/weblayer/browser/DEPS b/chromium/weblayer/browser/DEPS index d7477650b25..f1a963c03c5 100644 --- a/chromium/weblayer/browser/DEPS +++ b/chromium/weblayer/browser/DEPS @@ -7,6 +7,7 @@ include_rules = [ "+components/autofill/core/common", "+components/base32", "+components/blocked_content", + "+components/browsing_data/content", "+components/browser_ui", "+components/captive_portal", "+components/cdm/browser", @@ -18,7 +19,11 @@ include_rules = [ "+components/crash/core/common", "+components/download/public/common", "+components/embedder_support", + "+components/error_page/content/browser", + "+components/favicon_base", + "+components/favicon", "+components/find_in_page", + "+components/infobars/android", "+components/infobars/content", "+components/infobars/core", "+components/javascript_dialogs", @@ -34,6 +39,8 @@ include_rules = [ "+components/permissions", "+components/pref_registry", "+components/prefs", + "+components/prerender/browser", + "+components/prerender/common", "+components/resources/android", "+components/safe_browsing/core", "+components/safe_browsing/core/common", @@ -42,11 +49,14 @@ include_rules = [ "+components/security_state/content/content_utils.h", "+components/security_state/core/security_state.h", "+components/sessions", + "+components/signin/core/browser", + "+components/signin/public", "+components/site_isolation", "+components/spellcheck/browser", "+components/ssl_errors", "+components/startup_metric_utils", "+components/strings", + "+components/translate/content/android", "+components/translate/content/browser", "+components/translate/core/browser", "+components/translate/core/common", @@ -59,16 +69,18 @@ include_rules = [ "+components/web_cache/browser", "+components/webrtc", "+content/public", + "+google_apis", "+mojo/public", "+net", "+sandbox", - "+services/metrics/public/cpp/ukm_recorder.h", + "+services/metrics/public/cpp", "+services/network/network_service.h", "+services/network/public", "+services/service_manager", "+storage/browser/quota", "+third_party/blink/public/common", "+third_party/blink/public/mojom", + "+third_party/skia", "+ui/aura", "+ui/android", "+ui/base", diff --git a/chromium/weblayer/browser/OWNERS b/chromium/weblayer/browser/OWNERS index f7104f63cd5..d9811aefda4 100644 --- a/chromium/weblayer/browser/OWNERS +++ b/chromium/weblayer/browser/OWNERS @@ -1,5 +1,3 @@ -per-file weblayer_content_browser_overlay_manifest.*=set noparent -per-file weblayer_content_browser_overlay_manifest.*=file://ipc/SECURITY_OWNERS per-file weblayer_browser_interface_binders.*=set noparent per-file weblayer_browser_interface_binders.*=file://ipc/SECURITY_OWNERS per-file content_browser_client_impl_receiver_bindings.*=set noparent diff --git a/chromium/weblayer/browser/android/javatests/BUILD.gn b/chromium/weblayer/browser/android/javatests/BUILD.gn index d069ae4c405..c8ed3bdcb5d 100644 --- a/chromium/weblayer/browser/android/javatests/BUILD.gn +++ b/chromium/weblayer/browser/android/javatests/BUILD.gn @@ -13,13 +13,17 @@ android_library("weblayer_java_tests") { "src/org/chromium/weblayer/test/CookieManagerTest.java", "src/org/chromium/weblayer/test/CrashReporterTest.java", "src/org/chromium/weblayer/test/DataClearingTest.java", + "src/org/chromium/weblayer/test/DisplayCutoutTest.java", "src/org/chromium/weblayer/test/DowngradeTest.java", "src/org/chromium/weblayer/test/DownloadCallbackTest.java", "src/org/chromium/weblayer/test/ErrorPageCallbackTest.java", "src/org/chromium/weblayer/test/ExecuteScriptTest.java", "src/org/chromium/weblayer/test/ExternalNavigationTest.java", + "src/org/chromium/weblayer/test/FaviconFetcherTest.java", "src/org/chromium/weblayer/test/FindInPageTest.java", "src/org/chromium/weblayer/test/FullscreenCallbackTest.java", + "src/org/chromium/weblayer/test/FullscreenSizeTest.java", + "src/org/chromium/weblayer/test/GoogleAccountsTest.java", "src/org/chromium/weblayer/test/InputTypesTest.java", "src/org/chromium/weblayer/test/NavigationTest.java", "src/org/chromium/weblayer/test/NewTabCallbackTest.java", @@ -31,7 +35,6 @@ android_library("weblayer_java_tests") { "src/org/chromium/weblayer/test/TabCallbackTest.java", "src/org/chromium/weblayer/test/TabListCallbackTest.java", "src/org/chromium/weblayer/test/TabTest.java", - "src/org/chromium/weblayer/test/TestFullscreenCallback.java", "src/org/chromium/weblayer/test/TopControlsTest.java", "src/org/chromium/weblayer/test/WebLayerLoadingTest.java", "src/org/chromium/weblayer/test/WebLayerTest.java", @@ -48,6 +51,7 @@ android_library("weblayer_java_tests") { "//third_party/android_deps:android_support_v4_java", "//third_party/android_deps:androidx_core_core_java", "//third_party/android_deps:androidx_fragment_fragment_java", + "//third_party/android_deps:androidx_test_runner_java", "//third_party/android_deps:espresso_java", "//third_party/android_support_test_runner:rules_java", "//third_party/android_support_test_runner:runner_java", @@ -64,13 +68,17 @@ android_library("weblayer_private_java_tests") { testonly = true sources = [ "src/org/chromium/weblayer/test/BrowserControlsTest.java", + "src/org/chromium/weblayer/test/FullscreenCallbackPrivateTest.java", "src/org/chromium/weblayer/test/GeolocationTest.java", "src/org/chromium/weblayer/test/InfoBarTest.java", "src/org/chromium/weblayer/test/MediaCaptureTest.java", "src/org/chromium/weblayer/test/NetworkChangeNotifierTest.java", + "src/org/chromium/weblayer/test/PageInfoTest.java", "src/org/chromium/weblayer/test/PopupTest.java", "src/org/chromium/weblayer/test/ResourceLoadingTest.java", + "src/org/chromium/weblayer/test/TabPrivateTest.java", "src/org/chromium/weblayer/test/TranslateTest.java", + "src/org/chromium/weblayer/test/UrlBarControllerTest.java", ] deps = [ ":weblayer_java_private_test_support", @@ -81,6 +89,9 @@ android_library("weblayer_private_java_tests") { "//net/android:net_java_test_support", "//third_party/android_deps:android_support_v4_java", "//third_party/android_deps:androidx_fragment_fragment_java", + "//third_party/android_deps:androidx_test_runner_java", + "//third_party/android_deps:espresso_java", + "//third_party/android_sdk:android_test_base_java", "//third_party/android_support_test_runner:rules_java", "//third_party/android_support_test_runner:runner_java", "//third_party/hamcrest:hamcrest_java", @@ -103,6 +114,7 @@ android_library("weblayer_java_test_support") { "src/org/chromium/weblayer/test/NewTabCallbackImpl.java", "src/org/chromium/weblayer/test/ResourceUtil.java", "src/org/chromium/weblayer/test/SiteSettingsActivityTestRule.java", + "src/org/chromium/weblayer/test/TestFullscreenCallback.java", "src/org/chromium/weblayer/test/WebLayerActivityTestRule.java", "src/org/chromium/weblayer/test/WebLayerJUnit4ClassRunner.java", ] @@ -229,6 +241,7 @@ android_test_apk("weblayer_bundle_test_apk") { "//base:base_java", "//base:base_java_test_support", "//content/public/test/android:content_java_test_support", + "//third_party/android_deps:androidx_test_runner_java", "//third_party/android_support_test_runner:rules_java", "//third_party/android_support_test_runner:runner_java", "//third_party/junit:junit", diff --git a/chromium/weblayer/browser/android/javatests/skew/build_weblayer_instrumentation_test_cipd_pkg.py b/chromium/weblayer/browser/android/javatests/skew/build_weblayer_instrumentation_test_cipd_pkg.py index 612977d6020..0868a5d57fe 100755 --- a/chromium/weblayer/browser/android/javatests/skew/build_weblayer_instrumentation_test_cipd_pkg.py +++ b/chromium/weblayer/browser/android/javatests/skew/build_weblayer_instrumentation_test_cipd_pkg.py @@ -14,25 +14,52 @@ # skew test will pick up the new package in successive runs. import argparse +import contextlib import os import shutil import subprocess import sys +import re import tempfile import zipfile +SRC_DIR = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', '..') # Run mb.py out of the current branch for simplicity. -MB_PATH = './tools/mb/mb.py' +MB_PATH = os.path.join('tools', 'mb', 'mb.py') # Get the config specifying the gn args from the location of this script. MB_CONFIG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'mb_config.pyl') +CHROMIUM_VERSION_REGEX = r'\d+\.\d+\.\d+\.\d+$' + # CIPD package path. # https://chrome-infra-packages.appspot.com/p/chromium/testing/weblayer-x86/+/ CIPD_PKG_PATH='chromium/testing/weblayer-x86' + +@contextlib.contextmanager +def temporarily_chdir_to_src(local_paths=None): + """Change directories to chromium/src when entering with block and + then change back to current directory after exiting with block. + + Args: + local_paths: List of paths to change into relative paths. + + Returns: + List of paths relative to chromium/src. + """ + curr_dir = os.getcwd() + paths_rel_to_src = [ + os.path.relpath(p, SRC_DIR) for p in local_paths or []] + try: + os.chdir(SRC_DIR) + yield paths_rel_to_src + finally: + os.chdir(curr_dir) + + def zip_test_target(zip_filename): """Create zip of all deps for weblayer_instrumentation_test_apk. @@ -44,7 +71,7 @@ def zip_test_target(zip_filename): '--master=dummy.master', '--builder=dummy.builder', '--config-file=%s' % MB_CONFIG_PATH, - 'out/Release', + os.path.join('out', 'Release'), 'weblayer_instrumentation_test_apk', zip_filename] print(' '.join(cmd)) @@ -68,6 +95,16 @@ def build_cipd_pkg(input_path, cipd_filename): subprocess.check_call(cmd) +def get_chromium_version(): + with open(os.path.join(SRC_DIR, 'chrome', 'VERSION')) as f: + version = '.'.join(line[line.index('=') + 1:] + for line in f.read().splitlines()) + if not re.match(CHROMIUM_VERSION_REGEX, version): + raise ValueError("Chromium version, '%s', is not in proper format" % + version) + return version + + def main(): parser = argparse.ArgumentParser( description='Package weblayer instrumentation tests for CIPD.') @@ -75,9 +112,11 @@ def main(): '--cipd_out', required=True, help="Output filename for resulting .cipd file.") - args = parser.parse_args() - with tempfile.TemporaryDirectory() as tmp_dir: + args = parser.parse_args() + chromium_version = get_chromium_version() + with tempfile.TemporaryDirectory() as tmp_dir, \ + temporarily_chdir_to_src([args.cipd_out]) as cipd_out_src_rel_paths: # Create zip archive of test target. zip_filename = os.path.join(tmp_dir, 'file.zip') zip_test_target(zip_filename) @@ -91,10 +130,11 @@ def main(): # Create CIPD archive. tmp_cipd_filename = os.path.join(tmp_dir, 'file.cipd') build_cipd_pkg(extracted, tmp_cipd_filename) - shutil.move(tmp_cipd_filename, args.cipd_out) + shutil.move(tmp_cipd_filename, cipd_out_src_rel_paths[0]) - print(('Use "cipd pkg-register %s -verbose -tag \'version:<branch>\'" ' + - 'to upload package to the cipd server.') % args.cipd_out) + print(('Use "cipd pkg-register %s -verbose -tag \'version:%s\'" ' + + 'to upload package to the cipd server.') % + (args.cipd_out, chromium_version)) print('Use "cipd set-ref chromium/testing/weblayer-x86 --version ' + '<CIPD instance version> -ref m<milestone>" to update the ref.') print('The CIPD instance version can be found on the "Instance" line ' + diff --git a/chromium/weblayer/browser/android/javatests/skew/expectations.txt b/chromium/weblayer/browser/android/javatests/skew/expectations.txt index 365f067e95e..cdd7c2a0758 100644 --- a/chromium/weblayer/browser/android/javatests/skew/expectations.txt +++ b/chromium/weblayer/browser/android/javatests/skew/expectations.txt @@ -22,25 +22,26 @@ [ impl_lte_83 ] org.chromium.weblayer.test.TabTest#testBeforeUnloadNoHandler [ Skip ] [ impl_lte_83 ] org.chromium.weblayer.test.TabTest#testBeforeUnloadNoInteraction [ Skip ] -# Replace was removed in https://crrev.com/c/2150968, see https://crbug.com/1070851. -[ impl_lte_83 ] org.chromium.weblayer.test.NavigationTest#testReplace [ Skip ] +# Replace was removed in https://crrev.com/c/2150968. +crbug.com/1070851 [ impl_lte_83 ] org.chromium.weblayer.test.NavigationTest#testReplace [ Skip ] -# Fixed in https://crrev.com/c/2180022, see https://crbug.com/1077243. -[ impl_lte_83 ] org.chromium.weblayer.test.FullscreenCallbackTest#testExitFullscreenWhenTabDestroyed [ Skip ] +# Fixed in https://crrev.com/c/2180022. +crbug.com/1077243 [ impl_lte_83 ] org.chromium.weblayer.test.FullscreenCallbackTest#testExitFullscreenWhenTabDestroyed [ Skip ] -# https://crbug.com/1079491. -[ impl_lte_83 ] org.chromium.weblayer.test.NavigationTest#testSetUserAgentString [ Skip ] +crbug.com/1096570 [ impl_lte_83 ] org.chromium.weblayer.test.ExternalNavigationTest#testExternalIntentWithNoRedirectLaunched [ Skip ] # ------------------------------------- # Tests against older WebLayer clients. # ------------------------------------- -# Intentionally changed in M84, https://crrev.com/c/2130790. See https://crbug.com/1078973. -[ client_lte_83 ] org.chromium.weblayer.test.ExternalNavigationTest#testExternalIntentLaunchedViaOnLoad [ Skip ] -[ client_lte_83 ] org.chromium.weblayer.test.ExternalNavigationTest#testNonHandledExternalIntentWithFallbackUrlThatLaunchesIntentAfterRedirectLaunchesFallbackIntent [ Skip ] +# Intentionally changed in M84, https://crrev.com/c/2130790. +crbug.com/1078973 [ client_lte_83 ] org.chromium.weblayer.test.ExternalNavigationTest#testExternalIntentLaunchedViaOnLoad [ Skip ] +crbug.com/1078973 [ client_lte_83 ] org.chromium.weblayer.test.ExternalNavigationTest#testNonHandledExternalIntentWithFallbackUrlThatLaunchesIntentAfterRedirectLaunchesFallbackIntent [ Skip ] -# Replace was removed in https://crrev.com/c/2150968, see https://crbug.com/1070851. -[ client_lte_83 ] org.chromium.weblayer.test.NavigationTest#testReplace [ Skip ] +# Replace was removed in https://crrev.com/c/2150968. +crbug.com/1070851 [ client_lte_83 ] org.chromium.weblayer.test.NavigationTest#testReplace [ Skip ] -# Test was made private, https://crbug.com/1087451. -[ client_lte_84 ] org.chromium.weblayer.test.TopControlsTest#testBasic [ Skip ] +# Test was made private. +crbug.com/1087451 [ client_lte_84 ] org.chromium.weblayer.test.TopControlsTest#testBasic [ Skip ] + +crbug.com/1096570 [ client_lte_83 ] org.chromium.weblayer.test.ExternalNavigationTest#testExternalIntentWithNoRedirectBlocked [ Skip ] diff --git a/chromium/weblayer/browser/android/javatests/skew/mb_config.pyl b/chromium/weblayer/browser/android/javatests/skew/mb_config.pyl index 726ef6f3fc6..8e4311f0432 100644 --- a/chromium/weblayer/browser/android/javatests/skew/mb_config.pyl +++ b/chromium/weblayer/browser/android/javatests/skew/mb_config.pyl @@ -65,9 +65,10 @@ 'release': { 'gn_args': 'is_debug=false', }, - + # TODO(rmhasan): Add back GOMA support when you can start the goma proxy + # in a recipe. 'release_bot': { - 'mixins': ['release', 'static', 'goma'], + 'mixins': ['release', 'static'], }, 'static': { diff --git a/chromium/weblayer/browser/android/metrics/DEPS b/chromium/weblayer/browser/android/metrics/DEPS index 44a37a03920..3a3f231d4d2 100644 --- a/chromium/weblayer/browser/android/metrics/DEPS +++ b/chromium/weblayer/browser/android/metrics/DEPS @@ -1,4 +1,5 @@ include_rules = [ "+components/metrics", "+google_apis/google_api_keys.h", + "+third_party/metrics_proto" ] diff --git a/chromium/weblayer/browser/android/metrics/metrics_browsertest.cc b/chromium/weblayer/browser/android/metrics/metrics_browsertest.cc new file mode 100644 index 00000000000..ec8bff6d7a6 --- /dev/null +++ b/chromium/weblayer/browser/android/metrics/metrics_browsertest.cc @@ -0,0 +1,208 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <deque> + +#include "base/command_line.h" +#include "base/metrics/metrics_hashes.h" +#include "base/metrics/statistics_recorder.h" +#include "base/no_destructor.h" +#include "base/test/bind_test_util.h" +#include "components/metrics/log_decoder.h" +#include "components/metrics/metrics_log_uploader.h" +#include "components/metrics/metrics_switches.h" +#include "content/public/test/browser_test_utils.h" +#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h" +#include "weblayer/browser/android/metrics/metrics_test_helper.h" +#include "weblayer/browser/android/metrics/weblayer_metrics_service_client.h" +#include "weblayer/browser/profile_impl.h" +#include "weblayer/public/navigation_controller.h" +#include "weblayer/public/profile.h" +#include "weblayer/public/tab.h" +#include "weblayer/shell/browser/shell.h" +#include "weblayer/test/weblayer_browser_test.h" +#include "weblayer/test/weblayer_browser_test_utils.h" + +namespace weblayer { + +namespace { + +bool HasHistogramWithHash(const metrics::ChromeUserMetricsExtension& uma_log, + uint64_t hash) { + for (int i = 0; i < uma_log.histogram_event_size(); ++i) { + if (uma_log.histogram_event(i).name_hash() == hash) { + return true; + } + } + return false; +} + +} // namespace + +class MetricsBrowserTest : public WebLayerBrowserTest { + public: + void SetUp() override { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + command_line->AppendSwitch(metrics::switches::kForceEnableMetricsReporting); + + InstallTestGmsBridge(HasUserConsent(), + base::BindRepeating(&MetricsBrowserTest::OnLogMetrics, + base::Unretained(this))); + WebLayerMetricsServiceClient::GetInstance()->SetFastStartupForTesting(true); + WebLayerMetricsServiceClient::GetInstance()->SetUploadIntervalForTesting( + base::TimeDelta::FromMilliseconds(10)); + WebLayerBrowserTest::SetUp(); + } + + void TearDown() override { + RemoveTestGmsBridge(); + WebLayerBrowserTest::TearDown(); + } + + void OnLogMetrics(metrics::ChromeUserMetricsExtension metric) { + metrics_logs_.push_back(metric); + if (on_new_log_) + std::move(on_new_log_).Run(); + } + + metrics::ChromeUserMetricsExtension WaitForNextMetricsLog() { + if (metrics_logs_.empty()) { + base::RunLoop run_loop; + on_new_log_ = run_loop.QuitClosure(); + run_loop.Run(); + DCHECK(!metrics_logs_.empty()); + } + metrics::ChromeUserMetricsExtension tmp = std::move(metrics_logs_.front()); + metrics_logs_.pop_front(); + return tmp; + } + + size_t GetNumLogs() const { return metrics_logs_.size(); } + + virtual bool HasUserConsent() { return true; } + + private: + std::unique_ptr<Profile> profile_; + std::deque<metrics::ChromeUserMetricsExtension> metrics_logs_; + base::OnceClosure on_new_log_; +}; + +IN_PROC_BROWSER_TEST_F(MetricsBrowserTest, ProtoHasExpectedFields) { + metrics::ChromeUserMetricsExtension log = WaitForNextMetricsLog(); + EXPECT_EQ(metrics::ChromeUserMetricsExtension::ANDROID_WEBLAYER, + log.product()); + EXPECT_TRUE(log.has_client_id()); + EXPECT_TRUE(log.has_session_id()); + + const metrics::SystemProfileProto& system_profile = log.system_profile(); + EXPECT_TRUE(system_profile.has_build_timestamp()); + EXPECT_TRUE(system_profile.has_app_version()); + EXPECT_TRUE(system_profile.has_channel()); + EXPECT_TRUE(system_profile.has_install_date()); + EXPECT_TRUE(system_profile.has_application_locale()); + EXPECT_EQ("Android", system_profile.os().name()); + EXPECT_TRUE(system_profile.os().has_version()); + EXPECT_TRUE(system_profile.hardware().has_system_ram_mb()); + EXPECT_TRUE(system_profile.hardware().has_hardware_class()); + EXPECT_TRUE(system_profile.hardware().has_screen_count()); + EXPECT_TRUE(system_profile.hardware().has_primary_screen_width()); + EXPECT_TRUE(system_profile.hardware().has_primary_screen_height()); + EXPECT_TRUE(system_profile.hardware().has_primary_screen_scale_factor()); + EXPECT_TRUE(system_profile.hardware().has_cpu_architecture()); + EXPECT_TRUE(system_profile.hardware().cpu().has_vendor_name()); + EXPECT_TRUE(system_profile.hardware().cpu().has_signature()); + EXPECT_TRUE(system_profile.hardware().cpu().has_num_cores()); + EXPECT_TRUE(system_profile.hardware().cpu().has_is_hypervisor()); + EXPECT_TRUE(system_profile.hardware().gpu().has_driver_version()); + EXPECT_TRUE(system_profile.hardware().gpu().has_gl_vendor()); + EXPECT_TRUE(system_profile.hardware().gpu().has_gl_renderer()); +} + +IN_PROC_BROWSER_TEST_F(MetricsBrowserTest, PageLoadsEnableMultipleUploads) { + WaitForNextMetricsLog(); + + // At this point, the MetricsService should be asleep, and should not have + // created any more metrics logs. + ASSERT_EQ(0u, GetNumLogs()); + + // The start of a page load should be enough to indicate to the MetricsService + // that the app is "in use" and it's OK to upload the next record. No need to + // wait for onPageFinished, since this behavior should be gated on + // NOTIFICATION_LOAD_START. + shell()->tab()->GetNavigationController()->Navigate(GURL("about:blank")); + + // This may take slightly longer than UPLOAD_INTERVAL_MS, due to the time + // spent processing the metrics log, but should be well within the timeout + // (unless something is broken). + WaitForNextMetricsLog(); + + // If we get here, we got a second metrics log (and the test may pass). If + // there was no second metrics log, then the above call will check fail with a + // timeout. We should not assert the logs are empty however, because it's + // possible we got a metrics log between onPageStarted & onPageFinished, in + // which case onPageFinished would *also* wake up the metrics service, and we + // might potentially have a third metrics log in the queue. +} + +IN_PROC_BROWSER_TEST_F(MetricsBrowserTest, NavigationIncrementsPageLoadCount) { + ASSERT_TRUE(embedded_test_server()->Start()); + metrics::ChromeUserMetricsExtension log = WaitForNextMetricsLog(); + // The initial log should not have a page load count (because nothing was + // loaded). + { + const metrics::SystemProfileProto& system_profile = log.system_profile(); + ASSERT_TRUE(system_profile.has_stability()); + EXPECT_EQ(0, system_profile.stability().page_load_count()); + } + + // Loading a page should increment the page load count. + NavigateAndWaitForCompletion( + embedded_test_server()->GetURL("/simple_page.html"), shell()); + log = WaitForNextMetricsLog(); + { + const metrics::SystemProfileProto& system_profile = log.system_profile(); + ASSERT_TRUE(system_profile.has_stability()); + EXPECT_EQ(1, system_profile.stability().page_load_count()); + } +} + +IN_PROC_BROWSER_TEST_F(MetricsBrowserTest, RendererHistograms) { + base::HistogramTester histogram_tester; + ASSERT_TRUE(embedded_test_server()->Start()); + + NavigateAndWaitForCompletion( + embedded_test_server()->GetURL("/simple_page.html"), shell()); + + uint64_t hash = base::HashMetricName("Android.SeccompStatus.RendererSandbox"); + + bool collect_final_metrics_for_log_called = false; + + WebLayerMetricsServiceClient::GetInstance() + ->SetCollectFinalMetricsForLogClosureForTesting( + base::BindLambdaForTesting( + [&]() { collect_final_metrics_for_log_called = true; })); + + // Not every WaitForNextMetricsLog call will end up calling + // MetricsServiceClient::CollectFinalMetricsForLog since there may already be + // staged logs to send (see ReportingService::SendNextLog). Since we need to + // wait for CollectFinalMetricsForLog to be run after the navigate call above, + // keep calling WaitForNextMetricsLog until CollectFinalMetricsForLog is + // called. + metrics::ChromeUserMetricsExtension uma_log; + while (!collect_final_metrics_for_log_called) + uma_log = WaitForNextMetricsLog(); + + ASSERT_TRUE(HasHistogramWithHash(uma_log, hash)); +} + +class MetricsBrowserTestWithUserOptOut : public MetricsBrowserTest { + bool HasUserConsent() override { return false; } +}; + +IN_PROC_BROWSER_TEST_F(MetricsBrowserTestWithUserOptOut, MetricsNotRecorded) { + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(0u, GetNumLogs()); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/android/metrics/metrics_test_helper.cc b/chromium/weblayer/browser/android/metrics/metrics_test_helper.cc new file mode 100644 index 00000000000..bd0eb5b038d --- /dev/null +++ b/chromium/weblayer/browser/android/metrics/metrics_test_helper.cc @@ -0,0 +1,79 @@ +// 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 "weblayer/browser/android/metrics/metrics_test_helper.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/no_destructor.h" +#include "base/run_loop.h" +#include "weblayer/browser/profile_impl.h" +#include "weblayer/test/weblayer_browsertests_jni/MetricsTestHelper_jni.h" + +namespace weblayer { + +namespace { + +OnLogsMetricsCallback& GetOnLogMetricsCallback() { + static base::NoDestructor<OnLogsMetricsCallback> s_callback; + return *s_callback; +} + +ProfileImpl* GetProfileByName(const std::string& name) { + for (auto* profile : ProfileImpl::GetAllProfiles()) { + if (profile->name() == name) + return profile; + } + + return nullptr; +} + +} // namespace + +void InstallTestGmsBridge(bool has_user_consent, + const OnLogsMetricsCallback on_log_metrics) { + GetOnLogMetricsCallback() = on_log_metrics; + Java_MetricsTestHelper_installTestGmsBridge( + base::android::AttachCurrentThread(), has_user_consent); +} + +void RemoveTestGmsBridge() { + Java_MetricsTestHelper_removeTestGmsBridge( + base::android::AttachCurrentThread()); + GetOnLogMetricsCallback().Reset(); +} + +ProfileImpl* CreateProfile(const std::string& name) { + DCHECK(!GetProfileByName(name)); + JNIEnv* env = base::android::AttachCurrentThread(); + Java_MetricsTestHelper_createProfile( + env, base::android::ConvertUTF8ToJavaString(env, name)); + ProfileImpl* profile = GetProfileByName(name); + // Creating a profile may involve storage partition initialization. Wait for + // the initialization to be completed. + base::RunLoop().RunUntilIdle(); + return profile; +} +void DestroyProfile(const std::string& name) { + DCHECK(GetProfileByName(name)); + JNIEnv* env = base::android::AttachCurrentThread(); + Java_MetricsTestHelper_destroyProfile( + env, base::android::ConvertUTF8ToJavaString(env, name)); +} + +void JNI_MetricsTestHelper_OnLogMetrics( + JNIEnv* env, + const base::android::JavaParamRef<jbyteArray>& data) { + auto& callback = GetOnLogMetricsCallback(); + if (!callback) + return; + + metrics::ChromeUserMetricsExtension proto; + jbyte* src_bytes = env->GetByteArrayElements(data, nullptr); + proto.ParseFromArray(src_bytes, env->GetArrayLength(data.obj())); + env->ReleaseByteArrayElements(data, src_bytes, JNI_ABORT); + callback.Run(proto); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/android/metrics/metrics_test_helper.h b/chromium/weblayer/browser/android/metrics/metrics_test_helper.h new file mode 100644 index 00000000000..ed398ce24de --- /dev/null +++ b/chromium/weblayer/browser/android/metrics/metrics_test_helper.h @@ -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. + +#ifndef WEBLAYER_BROWSER_ANDROID_METRICS_METRICS_TEST_HELPER_H_ +#define WEBLAYER_BROWSER_ANDROID_METRICS_METRICS_TEST_HELPER_H_ + +#include <string> + +#include "base/callback.h" +#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h" + +namespace weblayer { +class ProfileImpl; + +// Various utilities to bridge to Java code for metrics related tests. + +using OnLogsMetricsCallback = + base::RepeatingCallback<void(metrics::ChromeUserMetricsExtension)>; + +// Call this in the SetUp() test harness method to install the test +// GmsBridge and to set the metrics user consent state. +void InstallTestGmsBridge( + bool has_user_consent, + const OnLogsMetricsCallback on_log_metrics = OnLogsMetricsCallback()); + +// Call this in the TearDown() test harness method to remove the GmsBridge. +void RemoveTestGmsBridge(); + +// See Profile::Create()'s comments for the semantics of |name|. +ProfileImpl* CreateProfile(const std::string& name); + +void DestroyProfile(const std::string& name); + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_ANDROID_METRICS_METRICS_TEST_HELPER_H_ diff --git a/chromium/weblayer/browser/ukm_browsertest.cc b/chromium/weblayer/browser/android/metrics/ukm_browsertest.cc index 52b734a4559..53bd2203807 100644 --- a/chromium/weblayer/browser/ukm_browsertest.cc +++ b/chromium/weblayer/browser/android/metrics/ukm_browsertest.cc @@ -4,29 +4,16 @@ #include "base/test/bind_test_util.h" #include "components/ukm/ukm_test_helper.h" +#include "weblayer/browser/android/metrics/metrics_test_helper.h" #include "weblayer/browser/android/metrics/weblayer_metrics_service_client.h" #include "weblayer/browser/profile_impl.h" #include "weblayer/public/navigation_controller.h" #include "weblayer/public/tab.h" -#include "weblayer/shell/android/browsertests_apk/metrics_test_helper.h" #include "weblayer/shell/browser/shell.h" #include "weblayer/test/weblayer_browser_test.h" namespace weblayer { -namespace { - -ProfileImpl* GetProfileByName(const std::string& name) { - for (auto* profile : ProfileImpl::GetAllProfiles()) { - if (profile->name() == name) - return profile; - } - - return nullptr; -} - -} // namespace - class UkmBrowserTest : public WebLayerBrowserTest { public: void SetUp() override { @@ -90,8 +77,8 @@ IN_PROC_BROWSER_TEST_F(UkmBrowserTest, RegularPlusIncognitoCheck) { EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled()); // Creating another regular profile mustn't enable UKM. - CreateProfile("foo"); - GetProfileByName("foo")->SetBooleanSetting(SettingType::UKM_ENABLED, true); + auto* profile = CreateProfile("foo"); + profile->SetBooleanSetting(SettingType::UKM_ENABLED, true); EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled()); // Note WebLayer can only have one incognito profile so we can't test creating @@ -116,8 +103,8 @@ IN_PROC_BROWSER_TEST_F(UkmBrowserTest, IncognitoPlusRegularCheck) { CreateProfile(std::string()); EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled()); - CreateProfile("foo"); - GetProfileByName("foo")->SetBooleanSetting(SettingType::UKM_ENABLED, true); + auto* profile = CreateProfile("foo"); + profile->SetBooleanSetting(SettingType::UKM_ENABLED, true); EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled()); DestroyProfile(std::string()); diff --git a/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.cc b/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.cc index 569fe1d44e2..4cb35797b17 100644 --- a/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.cc +++ b/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.cc @@ -10,6 +10,8 @@ #include "base/base64.h" #include "base/no_destructor.h" +#include "components/metrics/content/content_stability_metrics_provider.h" +#include "components/metrics/content/extensions_helper.h" #include "components/metrics/metrics_provider.h" #include "components/metrics/metrics_service.h" #include "components/page_load_metrics/browser/metrics_web_contents_observer.h" @@ -150,10 +152,13 @@ int WebLayerMetricsServiceClient::GetPackageNameLimitRatePerMille() { void WebLayerMetricsServiceClient::RegisterAdditionalMetricsProviders( metrics::MetricsService* service) { + service->RegisterMetricsProvider( + std::make_unique<metrics::ContentStabilityMetricsProvider>(pref_service(), + nullptr)); service->RegisterMetricsProvider(std::make_unique<PageLoadMetricsProvider>()); } -bool WebLayerMetricsServiceClient::EnablePersistentHistograms() { +bool WebLayerMetricsServiceClient::IsPersistentHistogramsEnabled() { return true; } diff --git a/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.h b/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.h index a25c0516ceb..09db1604fcb 100644 --- a/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.h +++ b/chromium/weblayer/browser/android/metrics/weblayer_metrics_service_client.h @@ -48,7 +48,7 @@ class WebLayerMetricsServiceClient int GetPackageNameLimitRatePerMille() override; void RegisterAdditionalMetricsProviders( metrics::MetricsService* service) override; - bool EnablePersistentHistograms() override; + bool IsPersistentHistogramsEnabled() override; bool IsOffTheRecordSessionActive() override; scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override; diff --git a/chromium/weblayer/browser/android/resource_mapper.cc b/chromium/weblayer/browser/android/resource_mapper.cc index ae5bcdf81d4..aeeb6fbbf99 100644 --- a/chromium/weblayer/browser/android/resource_mapper.cc +++ b/chromium/weblayer/browser/android/resource_mapper.cc @@ -41,6 +41,7 @@ void ConstructMap() { #include "components/resources/android/blocked_content_resource_id.h" #include "components/resources/android/page_info_resource_id.h" #include "components/resources/android/permissions_resource_id.h" +#include "components/resources/android/sms_resource_id.h" #undef LINK_RESOURCE_ID #undef DECLARE_RESOURCE_ID // Make sure ID list sizes match up. diff --git a/chromium/weblayer/browser/autofill_client_impl.cc b/chromium/weblayer/browser/autofill_client_impl.cc index f049ffa513f..cd7e3035575 100644 --- a/chromium/weblayer/browser/autofill_client_impl.cc +++ b/chromium/weblayer/browser/autofill_client_impl.cc @@ -69,6 +69,11 @@ autofill::AddressNormalizer* AutofillClientImpl::GetAddressNormalizer() { return nullptr; } +const GURL& AutofillClientImpl::GetLastCommittedURL() { + NOTREACHED(); + return GURL::EmptyGURL(); +} + security_state::SecurityLevel AutofillClientImpl::GetSecurityLevelForUmaHistograms() { NOTREACHED(); @@ -92,13 +97,13 @@ void AutofillClientImpl::OnUnmaskVerificationResult(PaymentsRpcResult result) { #if !defined(OS_ANDROID) std::vector<std::string> -AutofillClientImpl::GetMerchantWhitelistForVirtualCards() { +AutofillClientImpl::GetAllowedMerchantsForVirtualCards() { NOTREACHED(); return std::vector<std::string>(); } std::vector<std::string> -AutofillClientImpl::GetBinRangeWhitelistForVirtualCards() { +AutofillClientImpl::GetAllowedBinRangesForVirtualCards() { NOTREACHED(); return std::vector<std::string>(); } diff --git a/chromium/weblayer/browser/autofill_client_impl.h b/chromium/weblayer/browser/autofill_client_impl.h index c5edc4a218a..f6b57dabdea 100644 --- a/chromium/weblayer/browser/autofill_client_impl.h +++ b/chromium/weblayer/browser/autofill_client_impl.h @@ -35,6 +35,7 @@ class AutofillClientImpl ukm::UkmRecorder* GetUkmRecorder() override; ukm::SourceId GetUkmSourceId() override; autofill::AddressNormalizer* GetAddressNormalizer() override; + const GURL& GetLastCommittedURL() override; security_state::SecurityLevel GetSecurityLevelForUmaHistograms() override; void ShowAutofillSettings(bool show_credit_card_settings) override; @@ -45,8 +46,8 @@ class AutofillClientImpl void OnUnmaskVerificationResult(PaymentsRpcResult result) override; #if !defined(OS_ANDROID) - std::vector<std::string> GetMerchantWhitelistForVirtualCards() override; - std::vector<std::string> GetBinRangeWhitelistForVirtualCards() override; + std::vector<std::string> GetAllowedMerchantsForVirtualCards() override; + std::vector<std::string> GetAllowedBinRangesForVirtualCards() override; void ShowLocalCardMigrationDialog( base::OnceClosure show_migration_dialog_closure) override; diff --git a/chromium/weblayer/browser/browser_context_impl.cc b/chromium/weblayer/browser/browser_context_impl.cc index 0e99612fdce..bbb9a179615 100644 --- a/chromium/weblayer/browser/browser_context_impl.cc +++ b/chromium/weblayer/browser/browser_context_impl.cc @@ -26,7 +26,7 @@ #include "components/translate/core/browser/translate_prefs.h" #include "components/user_prefs/user_prefs.h" #include "components/variations/variations_client.h" -#include "components/variations/variations_http_header_provider.h" +#include "components/variations/variations_ids_provider.h" #include "content/public/browser/device_service.h" #include "content/public/browser/download_request_utils.h" #include "content/public/browser/resource_context.h" @@ -34,6 +34,7 @@ #include "weblayer/browser/browsing_data_remover_delegate.h" #include "weblayer/browser/browsing_data_remover_delegate_factory.h" #include "weblayer/browser/client_hints_factory.h" +#include "weblayer/browser/default_search_engine.h" #include "weblayer/browser/permissions/permission_manager_factory.h" #include "weblayer/browser/stateful_ssl_host_state_delegate_factory.h" #include "weblayer/public/common/switches.h" @@ -69,6 +70,9 @@ void BindWakeLockProvider( } // namespace namespace prefs { +// Used to persist the public SettingType::NETWORK_PREDICTION_ENABLED API. +const char kNoStatePrefetchEnabled[] = "weblayer.network_prediction_enabled"; + // Used to persist the public SettingType::UKM_ENABLED API. const char kUkmEnabled[] = "weblayer.ukm_enabled"; } // namespace prefs @@ -94,6 +98,12 @@ BrowserContextImpl::BrowserContextImpl(ProfileImpl* profile_impl, this); site_isolation::SiteIsolationPolicy::ApplyPersistedIsolatedOrigins(this); + + // Set the DSE permissions every time the browser context is created for + // simplicity. These permissions are not editable in site settings, so should + // not ever be changed by the user. The site settings entry will link to the + // client app's system level permissions page to handle these. + ResetDsePermissions(this); } BrowserContextImpl::~BrowserContextImpl() { @@ -240,6 +250,7 @@ void BrowserContextImpl::CreateUserPrefService() { void BrowserContextImpl::RegisterPrefs( user_prefs::PrefRegistrySyncable* pref_registry) { + pref_registry->RegisterBooleanPref(prefs::kNoStatePrefetchEnabled, true); pref_registry->RegisterBooleanPref(prefs::kUkmEnabled, false); // This pref is used by captive_portal::CaptivePortalService (as well as other @@ -285,7 +296,7 @@ class BrowserContextImpl::WebLayerVariationsClient } std::string GetVariationsHeader() const override { - return variations::VariationsHttpHeaderProvider::GetInstance() + return variations::VariationsIdsProvider::GetInstance() ->GetClientDataHeader(IsSignedIn()); } diff --git a/chromium/weblayer/browser/browser_context_impl.h b/chromium/weblayer/browser/browser_context_impl.h index 7d888462025..7a8eba34e29 100644 --- a/chromium/weblayer/browser/browser_context_impl.h +++ b/chromium/weblayer/browser/browser_context_impl.h @@ -23,6 +23,7 @@ class ResourceContextImpl; namespace prefs { // WebLayer specific pref names. +extern const char kNoStatePrefetchEnabled[]; extern const char kUkmEnabled[]; } // namespace prefs diff --git a/chromium/weblayer/browser/browser_controls_container_view.cc b/chromium/weblayer/browser/browser_controls_container_view.cc index 81a1f9e9626..a4335283fd8 100644 --- a/chromium/weblayer/browser/browser_controls_container_view.cc +++ b/chromium/weblayer/browser/browser_controls_container_view.cc @@ -52,6 +52,21 @@ int BrowserControlsContainerView::GetControlsHeight() { return controls_layer_ ? controls_layer_->bounds().height() : 0; } +int BrowserControlsContainerView::GetMinHeight() { + return Java_BrowserControlsContainerView_getMinHeight( + AttachCurrentThread(), java_browser_controls_container_view_); +} + +bool BrowserControlsContainerView::OnlyExpandControlsAtPageTop() { + return Java_BrowserControlsContainerView_onlyExpandControlsAtPageTop( + AttachCurrentThread(), java_browser_controls_container_view_); +} + +bool BrowserControlsContainerView::ShouldAnimateBrowserControlsHeightChanges() { + return Java_BrowserControlsContainerView_shouldAnimateBrowserControlsHeightChanges( + AttachCurrentThread(), java_browser_controls_container_view_); +} + int BrowserControlsContainerView::GetContentHeightDelta() { if (!controls_layer_ || !web_contents()) return 0; diff --git a/chromium/weblayer/browser/browser_controls_container_view.h b/chromium/weblayer/browser/browser_controls_container_view.h index 9231531499d..ed73606d75d 100644 --- a/chromium/weblayer/browser/browser_controls_container_view.h +++ b/chromium/weblayer/browser/browser_controls_container_view.h @@ -37,6 +37,17 @@ class BrowserControlsContainerView : public content::WebContentsObserver { // Height needed to display the control. int GetControlsHeight(); + // Returns the minimum height the browser controls can collapse to. + int GetMinHeight(); + + // Returns true if the browser controls should only expand when the page + // contents are scrolled to the top. + bool OnlyExpandControlsAtPageTop(); + + // Returns true if height or offset changes to the browser controls should + // be animated. + bool ShouldAnimateBrowserControlsHeightChanges(); + // Returns the amount of vertical space to take away from the contents. int GetContentHeightDelta(); @@ -54,8 +65,7 @@ class BrowserControlsContainerView : public content::WebContentsObserver { // Sets the offsets of the controls and content. See // BrowserControlsContainerView's javadoc for details on this. - void SetTopControlsOffset(JNIEnv* env, - int content_offset_y); + void SetTopControlsOffset(JNIEnv* env, int content_offset_y); void SetBottomControlsOffset(JNIEnv* env); // Sets the size of |controls_layer_|. diff --git a/chromium/weblayer/browser/browser_controls_navigation_state_handler.cc b/chromium/weblayer/browser/browser_controls_navigation_state_handler.cc index a049f38ca42..dd82111b17e 100644 --- a/chromium/weblayer/browser/browser_controls_navigation_state_handler.cc +++ b/chromium/weblayer/browser/browser_controls_navigation_state_handler.cc @@ -61,8 +61,10 @@ void BrowserControlsNavigationStateHandler::DidStartNavigation( void BrowserControlsNavigationStateHandler::DidFinishNavigation( content::NavigationHandle* navigation_handle) { if (navigation_handle->IsInMainFrame()) { - if (navigation_handle->HasCommitted()) - is_showing_error_page_ = navigation_handle->IsErrorPage(); + if (!navigation_handle->HasCommitted()) { + // There will be no DidFinishLoad or DidFailLoad, so hide the topview + ScheduleStopDelayedForceShow(); + } delegate_->OnUpdateBrowserControlsStateBecauseOfProcessSwitch( navigation_handle->HasCommitted()); } @@ -81,6 +83,10 @@ void BrowserControlsNavigationStateHandler::DidFailLoad( content::RenderFrameHost* render_frame_host, const GURL& validated_url, int error_code) { + const bool is_main_frame = + render_frame_host->GetMainFrame() == render_frame_host; + if (is_main_frame) + ScheduleStopDelayedForceShow(); if (render_frame_host->IsCurrent() && (render_frame_host == web_contents()->GetMainFrame())) { UpdateState(); diff --git a/chromium/weblayer/browser/browser_controls_navigation_state_handler.h b/chromium/weblayer/browser/browser_controls_navigation_state_handler.h index 0ad3b319b3e..de7edb0fa6e 100644 --- a/chromium/weblayer/browser/browser_controls_navigation_state_handler.h +++ b/chromium/weblayer/browser/browser_controls_navigation_state_handler.h @@ -82,9 +82,6 @@ class BrowserControlsNavigationStateHandler // Last value supplied to the delegate. base::Optional<content::BrowserControlsState> last_state_; - // True if an error page is showing. - bool is_showing_error_page_ = false; - // This is cached as WebContents::IsCrashed() does not always return the // right thing. bool is_crashed_ = false; diff --git a/chromium/weblayer/browser/browser_impl.cc b/chromium/weblayer/browser/browser_impl.cc index b7f5dc490b3..04c775fb266 100644 --- a/chromium/weblayer/browser/browser_impl.cc +++ b/chromium/weblayer/browser/browser_impl.cc @@ -12,6 +12,7 @@ #include "base/path_service.h" #include "base/stl_util.h" #include "components/base32/base32.h" +#include "content/public/browser/web_contents.h" #include "content/public/common/web_preferences.h" #include "weblayer/browser/browser_context_impl.h" #include "weblayer/browser/browser_list.h" @@ -114,6 +115,11 @@ BrowserImpl::~BrowserImpl() { TabImpl* BrowserImpl::CreateTabForSessionRestore( std::unique_ptr<content::WebContents> web_contents, const std::string& guid) { + if (!web_contents) { + content::WebContents::CreateParams create_params( + profile_->GetBrowserContext()); + web_contents = content::WebContents::Create(create_params); + } std::unique_ptr<TabImpl> tab = std::make_unique<TabImpl>(profile_, std::move(web_contents), guid); #if defined(OS_ANDROID) @@ -265,6 +271,12 @@ void BrowserImpl::SetWebPreferences(content::WebPreferences* prefs) { #endif } +#if defined(OS_ANDROID) +void BrowserImpl::DestroyTabFromJava(Tab* tab) { + RemoveTab(tab); +} +#endif + void BrowserImpl::AddTab(Tab* tab) { DCHECK(tab); TabImpl* tab_impl = static_cast<TabImpl*>(tab); @@ -277,7 +289,12 @@ void BrowserImpl::AddTab(Tab* tab) { } void BrowserImpl::DestroyTab(Tab* tab) { +#if defined(OS_ANDROID) + Java_BrowserImpl_destroyTabImpl(AttachCurrentThread(), java_impl_, + static_cast<TabImpl*>(tab)->GetJavaTab()); +#else RemoveTab(tab); +#endif } void BrowserImpl::SetActiveTab(Tab* tab) { diff --git a/chromium/weblayer/browser/browser_impl.h b/chromium/weblayer/browser/browser_impl.h index f5487af1cbc..02363c113c0 100644 --- a/chromium/weblayer/browser/browser_impl.h +++ b/chromium/weblayer/browser/browser_impl.h @@ -97,6 +97,14 @@ class BrowserImpl : public Browser { bool GetPasswordEchoEnabled(); void SetWebPreferences(content::WebPreferences* prefs); +#if defined(OS_ANDROID) + // On Android the Java Tab class owns the C++ Tab. DestroyTab() calls to the + // Java Tab class to initiate deletion. This function is called from the Java + // side, and must not call DestroyTab(), otherwise we get stuck in infinite + // recursion. + void DestroyTabFromJava(Tab* tab); +#endif + // Browser: void AddTab(Tab* tab) override; void DestroyTab(Tab* tab) override; diff --git a/chromium/weblayer/browser/browser_main_parts_impl.cc b/chromium/weblayer/browser/browser_main_parts_impl.cc index 7f9e8467a13..b8d317ae48f 100644 --- a/chromium/weblayer/browser/browser_main_parts_impl.cc +++ b/chromium/weblayer/browser/browser_main_parts_impl.cc @@ -6,7 +6,7 @@ #include "base/base_switches.h" #include "base/bind.h" -#include "base/message_loop/message_loop_current.h" +#include "base/task/current_thread.h" #include "base/threading/thread.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" @@ -19,6 +19,7 @@ #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/common/main_function_params.h" +#include "content/public/common/page_visibility_state.h" #include "content/public/common/url_constants.h" #include "services/service_manager/embedder/result_codes.h" #include "ui/base/resource/resource_bundle.h" @@ -27,6 +28,8 @@ #include "weblayer/browser/feature_list_creator.h" #include "weblayer/browser/host_content_settings_map_factory.h" #include "weblayer/browser/i18n_util.h" +#include "weblayer/browser/no_state_prefetch/prerender_link_manager_factory.h" +#include "weblayer/browser/no_state_prefetch/prerender_manager_factory.h" #include "weblayer/browser/permissions/weblayer_permissions_client.h" #include "weblayer/browser/stateful_ssl_host_state_delegate_factory.h" #include "weblayer/browser/translate_accept_languages_factory.h" @@ -54,6 +57,7 @@ #include "ui/base/x/x11_util.h" // nogncheck #endif #if defined(USE_AURA) && defined(USE_X11) +#include "ui/base/ui_base_features.h" #include "ui/events/devices/x11/touch_factory_x11.h" // nogncheck #endif #if !defined(OS_CHROMEOS) && defined(USE_AURA) && defined(OS_LINUX) @@ -80,6 +84,8 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt() { CookieSettingsFactory::GetInstance(); TranslateAcceptLanguagesFactory::GetInstance(); TranslateRankerFactory::GetInstance(); + PrerenderLinkManagerFactory::GetInstance(); + PrerenderManagerFactory::GetInstance(); } void StopMessageLoop(base::OnceClosure quit_closure) { @@ -130,7 +136,8 @@ int BrowserMainPartsImpl::PreCreateThreads() { void BrowserMainPartsImpl::PreMainMessageLoopStart() { #if defined(USE_AURA) && defined(USE_X11) - ui::TouchFactory::SetTouchDeviceListFromCommandLine(); + if (!features::IsUsingOzonePlatform()) + ui::TouchFactory::SetTouchDeviceListFromCommandLine(); #endif } @@ -138,7 +145,8 @@ int BrowserMainPartsImpl::PreEarlyInitialization() { browser_process_ = std::make_unique<BrowserProcess>(std::move(local_state_)); #if defined(USE_X11) - ui::SetDefaultX11ErrorHandlers(); + if (!features::IsUsingOzonePlatform()) + ui::SetDefaultX11ErrorHandlers(); #endif #if defined(USE_AURA) && defined(OS_LINUX) ui::InitializeInputMethodForTesting(); diff --git a/chromium/weblayer/browser/browser_process.cc b/chromium/weblayer/browser/browser_process.cc index b9fbde8f436..ac3929137cc 100644 --- a/chromium/weblayer/browser/browser_process.cc +++ b/chromium/weblayer/browser/browser_process.cc @@ -14,6 +14,7 @@ #include "content/public/browser/network_service_instance.h" #include "services/network/public/cpp/network_quality_tracker.h" #include "weblayer/browser/system_network_context_manager.h" +#include "weblayer/browser/user_agent.h" #if defined(OS_ANDROID) #include "weblayer/browser/safe_browsing/safe_browsing_service.h" @@ -99,12 +100,12 @@ void BrowserProcess::CreateNetworkQualityObserver() { } #if defined(OS_ANDROID) -SafeBrowsingService* BrowserProcess::GetSafeBrowsingService( - std::string user_agent) { +SafeBrowsingService* BrowserProcess::GetSafeBrowsingService() { if (!safe_browsing_service_) { // Create and initialize safe_browsing_service on first get. // Note: Initialize() needs to happen on UI thread. - safe_browsing_service_ = std::make_unique<SafeBrowsingService>(user_agent); + safe_browsing_service_ = + std::make_unique<SafeBrowsingService>(GetUserAgent()); safe_browsing_service_->Initialize(); } return safe_browsing_service_.get(); diff --git a/chromium/weblayer/browser/browser_process.h b/chromium/weblayer/browser/browser_process.h index 2cd3dd1d354..08540a6520d 100644 --- a/chromium/weblayer/browser/browser_process.h +++ b/chromium/weblayer/browser/browser_process.h @@ -49,7 +49,7 @@ class BrowserProcess { network::NetworkQualityTracker* GetNetworkQualityTracker(); #if defined(OS_ANDROID) - SafeBrowsingService* GetSafeBrowsingService(std::string user_agent); + SafeBrowsingService* GetSafeBrowsingService(); void StopSafeBrowsingService(); #endif diff --git a/chromium/weblayer/browser/browsing_data_remover_delegate.cc b/chromium/weblayer/browser/browsing_data_remover_delegate.cc index 2a466fe8cad..66dc3ae5bb8 100644 --- a/chromium/weblayer/browser/browsing_data_remover_delegate.cc +++ b/chromium/weblayer/browser/browsing_data_remover_delegate.cc @@ -5,10 +5,20 @@ #include "weblayer/browser/browsing_data_remover_delegate.h" #include "base/callback.h" -#include "components/prefs/pref_service.h" -#include "components/site_isolation//pref_names.h" +#include "build/build_config.h" +#include "components/browsing_data/content/browsing_data_helper.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/user_prefs/user_prefs.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/browsing_data_filter_builder.h" +#include "services/network/public/mojom/cookie_manager.mojom.h" +#include "weblayer/browser/browser_process.h" +#include "weblayer/browser/default_search_engine.h" +#include "weblayer/browser/favicon/favicon_service_impl.h" +#include "weblayer/browser/favicon/favicon_service_impl_factory.h" +#include "weblayer/browser/host_content_settings_map_factory.h" +#include "weblayer/browser/no_state_prefetch/prerender_manager_factory.h" +#include "weblayer/browser/safe_browsing/safe_browsing_service.h" namespace weblayer { @@ -16,6 +26,8 @@ BrowsingDataRemoverDelegate::BrowsingDataRemoverDelegate( content::BrowserContext* browser_context) : browser_context_(browser_context) {} +BrowsingDataRemoverDelegate::~BrowsingDataRemoverDelegate() = default; + BrowsingDataRemoverDelegate::EmbedderOriginTypeMatcher BrowsingDataRemoverDelegate::GetOriginTypeMatcher() { return EmbedderOriginTypeMatcher(); @@ -37,18 +49,82 @@ void BrowsingDataRemoverDelegate::RemoveEmbedderData( uint64_t remove_mask, content::BrowsingDataFilterBuilder* filter_builder, uint64_t origin_type_mask, - base::OnceClosure callback) { + base::OnceCallback<void(uint64_t)> callback) { + callback_ = std::move(callback); + // Note: if history is ever added to WebLayer, also remove isolated origins // when history is cleared. if (remove_mask & DATA_TYPE_ISOLATED_ORIGINS) { - user_prefs::UserPrefs::Get(browser_context_) - ->ClearPref(site_isolation::prefs::kUserTriggeredIsolatedOrigins); - // Note that this does not clear these sites from the in-memory map in - // ChildProcessSecurityPolicy, since that is not supported at runtime. That - // list of isolated sites is not directly exposed to users, though, and - // will be cleared on next restart. + browsing_data::RemoveSiteIsolationData( + user_prefs::UserPrefs::Get(browser_context_)); + } + + HostContentSettingsMap* host_content_settings_map = + HostContentSettingsMapFactory::GetForBrowserContext(browser_context_); + + if (remove_mask & content::BrowsingDataRemover::DATA_TYPE_CACHE) { + browsing_data::RemovePrerenderCacheData( + PrerenderManagerFactory::GetForBrowserContext(browser_context_)); } - std::move(callback).Run(); + + if (remove_mask & DATA_TYPE_FAVICONS) { + auto* service = + FaviconServiceImplFactory::GetForBrowserContext(browser_context_); + if (service) { + // The favicon database doesn't track enough information to remove + // favicons in a time range. Delete everything. + service->DeleteAndRecreateDatabase(CreateTaskCompletionClosure()); + } + } + + // We ignore the DATA_TYPE_COOKIES request if UNPROTECTED_WEB is not set, + // so that callers who request COOKIES_AND_SITE_DATA with PROTECTED_WEB + // don't accidentally remove the cookies that are associated with the + // UNPROTECTED_WEB origin. This is necessary because cookies are not separated + // between UNPROTECTED_WEB and PROTECTED_WEB. + if (remove_mask & content::BrowsingDataRemover::DATA_TYPE_COOKIES) { + network::mojom::NetworkContext* safe_browsing_context = nullptr; +#if defined(OS_ANDROID) + auto* sb_service = BrowserProcess::GetInstance()->GetSafeBrowsingService(); + if (sb_service) + safe_browsing_context = sb_service->GetNetworkContext(); +#endif + browsing_data::RemoveEmbedderCookieData( + delete_begin, delete_end, filter_builder, host_content_settings_map, + safe_browsing_context, + base::BindOnce( + &BrowsingDataRemoverDelegate::CreateTaskCompletionClosure, + base::Unretained(this))); + } + + if (remove_mask & DATA_TYPE_SITE_SETTINGS) { + browsing_data::RemoveSiteSettingsData(delete_begin, delete_end, + host_content_settings_map); + + // Reset the Default Search Engine permissions to their default. + ResetDsePermissions(browser_context_); + } + + RunCallbackIfDone(); +} + +base::OnceClosure BrowsingDataRemoverDelegate::CreateTaskCompletionClosure() { + ++pending_tasks_; + + return base::BindOnce(&BrowsingDataRemoverDelegate::OnTaskComplete, + weak_ptr_factory_.GetWeakPtr()); +} + +void BrowsingDataRemoverDelegate::OnTaskComplete() { + pending_tasks_--; + RunCallbackIfDone(); +} + +void BrowsingDataRemoverDelegate::RunCallbackIfDone() { + if (pending_tasks_ != 0) + return; + + std::move(callback_).Run(/*failed_data_types=*/0); } } // namespace weblayer diff --git a/chromium/weblayer/browser/browsing_data_remover_delegate.h b/chromium/weblayer/browser/browsing_data_remover_delegate.h index c2a996debba..0672e4683f1 100644 --- a/chromium/weblayer/browser/browsing_data_remover_delegate.h +++ b/chromium/weblayer/browser/browsing_data_remover_delegate.h @@ -5,6 +5,8 @@ #ifndef WEBLAYER_BROWSER_BROWSING_DATA_REMOVER_DELEGATE_H_ #define WEBLAYER_BROWSER_BROWSING_DATA_REMOVER_DELEGATE_H_ +#include "base/callback.h" +#include "base/memory/weak_ptr.h" #include "components/keyed_service/core/keyed_service.h" #include "content/public/browser/browsing_data_remover.h" #include "content/public/browser/browsing_data_remover_delegate.h" @@ -28,10 +30,13 @@ class BrowsingDataRemoverDelegate : public content::BrowsingDataRemoverDelegate, // WebLayer-specific datatypes. DATA_TYPE_ISOLATED_ORIGINS = DATA_TYPE_EMBEDDER_BEGIN, + DATA_TYPE_FAVICONS = DATA_TYPE_EMBEDDER_BEGIN << 1, + DATA_TYPE_SITE_SETTINGS = DATA_TYPE_EMBEDDER_BEGIN << 2, }; explicit BrowsingDataRemoverDelegate( content::BrowserContext* browser_context); + ~BrowsingDataRemoverDelegate() override; BrowsingDataRemoverDelegate(const BrowsingDataRemoverDelegate&) = delete; BrowsingDataRemoverDelegate& operator=(const BrowsingDataRemoverDelegate&) = @@ -47,10 +52,23 @@ class BrowsingDataRemoverDelegate : public content::BrowsingDataRemoverDelegate, uint64_t remove_mask, content::BrowsingDataFilterBuilder* filter_builder, uint64_t origin_type_mask, - base::OnceClosure callback) override; + base::OnceCallback<void(uint64_t)> callback) override; private: + base::OnceClosure CreateTaskCompletionClosure(); + + void OnTaskComplete(); + + void RunCallbackIfDone(); + content::BrowserContext* browser_context_ = nullptr; + + int pending_tasks_ = 0; + + // Completion callback to call when all data are deleted. + base::OnceCallback<void(uint64_t)> callback_; + + base::WeakPtrFactory<BrowsingDataRemoverDelegate> weak_ptr_factory_{this}; }; } // namespace weblayer diff --git a/chromium/weblayer/browser/client_hints_browsertest.cc b/chromium/weblayer/browser/client_hints_browsertest.cc index 3aa644fe269..77e7711f9e0 100644 --- a/chromium/weblayer/browser/client_hints_browsertest.cc +++ b/chromium/weblayer/browser/client_hints_browsertest.cc @@ -142,7 +142,12 @@ IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest, other_server.base_url(), GURL(), ContentSettingsType::CLIENT_HINTS, std::string(), std::make_unique<base::Value>(setting->Clone())); - // New server should now get client hints. + // Settings take affect after navigation only, so the header shouldn't be + // there yet. + EXPECT_EQ(GetSubresourceHeader("device-memory"), "None"); + + // After re-navigating, should have hints. + NavigateAndWaitForCompletion(other_server.GetURL("/echo"), shell()); CheckSubresourceHeaders(); } diff --git a/chromium/weblayer/browser/confirm_infobar_android.cc b/chromium/weblayer/browser/confirm_infobar_android.cc deleted file mode 100644 index 2ce1d44fa97..00000000000 --- a/chromium/weblayer/browser/confirm_infobar_android.cc +++ /dev/null @@ -1,95 +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 "weblayer/browser/confirm_infobar_android.h" - -#include <memory> -#include <utility> - -#include "base/android/jni_string.h" -#include "components/infobars/core/confirm_infobar_delegate.h" -#include "content/public/browser/web_contents.h" -#include "ui/android/window_android.h" -#include "ui/gfx/android/java_bitmap.h" -#include "ui/gfx/image/image.h" -#include "weblayer/browser/infobar_service.h" -#include "weblayer/browser/java/jni/ConfirmInfoBar_jni.h" - -using base::android::JavaParamRef; -using base::android::ScopedJavaLocalRef; - -namespace weblayer { - -// InfoBarService ------------------------------------------------------------- - -std::unique_ptr<infobars::InfoBar> InfoBarService::CreateConfirmInfoBar( - std::unique_ptr<ConfirmInfoBarDelegate> delegate) { - return std::make_unique<ConfirmInfoBar>(std::move(delegate)); -} - -// ConfirmInfoBar ------------------------------------------------------------- - -ConfirmInfoBar::ConfirmInfoBar(std::unique_ptr<ConfirmInfoBarDelegate> delegate) - : InfoBarAndroid(std::move(delegate)) {} - -ConfirmInfoBar::~ConfirmInfoBar() = default; - -base::string16 ConfirmInfoBar::GetTextFor( - ConfirmInfoBarDelegate::InfoBarButton button) { - ConfirmInfoBarDelegate* delegate = GetDelegate(); - return (delegate->GetButtons() & button) ? delegate->GetButtonLabel(button) - : base::string16(); -} - -ConfirmInfoBarDelegate* ConfirmInfoBar::GetDelegate() { - return delegate()->AsConfirmInfoBarDelegate(); -} - -ScopedJavaLocalRef<jobject> ConfirmInfoBar::CreateRenderInfoBar(JNIEnv* env) { - ScopedJavaLocalRef<jstring> ok_button_text = - base::android::ConvertUTF16ToJavaString( - env, GetTextFor(ConfirmInfoBarDelegate::BUTTON_OK)); - ScopedJavaLocalRef<jstring> cancel_button_text = - base::android::ConvertUTF16ToJavaString( - env, GetTextFor(ConfirmInfoBarDelegate::BUTTON_CANCEL)); - ConfirmInfoBarDelegate* delegate = GetDelegate(); - ScopedJavaLocalRef<jstring> message_text = - base::android::ConvertUTF16ToJavaString(env, delegate->GetMessageText()); - ScopedJavaLocalRef<jstring> link_text = - base::android::ConvertUTF16ToJavaString(env, delegate->GetLinkText()); - - ScopedJavaLocalRef<jobject> java_bitmap; - if (delegate->GetIconId() == infobars::InfoBarDelegate::kNoIconID && - !delegate->GetIcon().IsEmpty()) { - java_bitmap = gfx::ConvertToJavaBitmap(delegate->GetIcon().ToSkBitmap()); - } - - return Java_ConfirmInfoBar_create(env, GetJavaIconId(), java_bitmap, - message_text, link_text, ok_button_text, - cancel_button_text); -} - -void ConfirmInfoBar::OnLinkClicked(JNIEnv* env, - const JavaParamRef<jobject>& obj) { - if (!owner()) - return; // We're closing; don't call anything, it might access the owner. - - if (GetDelegate()->LinkClicked(WindowOpenDisposition::NEW_FOREGROUND_TAB)) - RemoveSelf(); -} - -void ConfirmInfoBar::ProcessButton(int action) { - if (!owner()) - return; // We're closing; don't call anything, it might access the owner. - - DCHECK((action == InfoBarAndroid::ACTION_OK) || - (action == InfoBarAndroid::ACTION_CANCEL)); - ConfirmInfoBarDelegate* delegate = GetDelegate(); - if ((action == InfoBarAndroid::ACTION_OK) ? delegate->Accept() - : delegate->Cancel()) { - RemoveSelf(); - } -} - -} // namespace weblayer diff --git a/chromium/weblayer/browser/confirm_infobar_android.h b/chromium/weblayer/browser/confirm_infobar_android.h deleted file mode 100644 index 9611841ef3f..00000000000 --- a/chromium/weblayer/browser/confirm_infobar_android.h +++ /dev/null @@ -1,40 +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 WEBLAYER_BROWSER_CONFIRM_INFOBAR_ANDROID_H_ -#define WEBLAYER_BROWSER_CONFIRM_INFOBAR_ANDROID_H_ - -#include "base/android/scoped_java_ref.h" -#include "base/macros.h" -#include "base/strings/string16.h" -#include "components/infobars/core/confirm_infobar_delegate.h" -#include "weblayer/browser/infobar_android.h" - -namespace weblayer { - -class ConfirmInfoBar : public InfoBarAndroid { - public: - explicit ConfirmInfoBar(std::unique_ptr<ConfirmInfoBarDelegate> delegate); - ~ConfirmInfoBar() override; - - protected: - ConfirmInfoBarDelegate* GetDelegate(); - base::string16 GetTextFor(ConfirmInfoBarDelegate::InfoBarButton button); - - // InfoBarAndroid overrides. - base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar( - JNIEnv* env) override; - - void OnLinkClicked(JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj) override; - - void ProcessButton(int action) override; - - private: - DISALLOW_COPY_AND_ASSIGN(ConfirmInfoBar); -}; - -} // namespace weblayer - -#endif // WEBLAYER_BROWSER_CONFIRM_INFOBAR_ANDROID_H_ diff --git a/chromium/weblayer/browser/content_browser_client_impl.cc b/chromium/weblayer/browser/content_browser_client_impl.cc index 438122ad99b..0418225aad5 100644 --- a/chromium/weblayer/browser/content_browser_client_impl.cc +++ b/chromium/weblayer/browser/content_browser_client_impl.cc @@ -20,6 +20,7 @@ #include "components/blocked_content/popup_blocker.h" #include "components/captive_portal/core/buildflags.h" #include "components/embedder_support/switches.h" +#include "components/error_page/content/browser/net_error_auto_reloader.h" #include "components/network_time/network_time_tracker.h" #include "components/page_load_metrics/browser/metrics_navigation_throttle.h" #include "components/page_load_metrics/browser/metrics_web_contents_observer.h" @@ -28,6 +29,9 @@ #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service_factory.h" #include "components/prefs/scoped_user_pref_update.h" +#include "components/prerender/browser/prerender_manager.h" +#include "components/prerender/common/prerender_url_loader_throttle.h" +#include "components/security_interstitials/content/insecure_form_navigation_throttle.h" #include "components/security_interstitials/content/ssl_cert_reporter.h" #include "components/security_interstitials/content/ssl_error_handler.h" #include "components/security_interstitials/content/ssl_error_navigation_throttle.h" @@ -38,6 +42,7 @@ #include "components/user_prefs/user_prefs.h" #include "components/variations/service/variations_service.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/client_certificate_delegate.h" #include "content/public/browser/devtools_manager_delegate.h" #include "content/public/browser/generated_code_cache_settings.h" @@ -46,7 +51,6 @@ #include "content/public/browser/network_service_instance.h" #include "content/public/browser/page_navigator.h" #include "content/public/browser/render_process_host.h" -#include "content/public/browser/tts_controller.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" #include "content/public/common/service_names.mojom.h" @@ -75,16 +79,20 @@ #include "weblayer/browser/http_auth_handler_impl.h" #include "weblayer/browser/i18n_util.h" #include "weblayer/browser/navigation_controller_impl.h" +#include "weblayer/browser/navigation_error_navigation_throttle.h" +#include "weblayer/browser/navigation_ui_data_impl.h" +#include "weblayer/browser/no_state_prefetch/prerender_manager_factory.h" +#include "weblayer/browser/no_state_prefetch/prerender_utils.h" +#include "weblayer/browser/page_specific_content_settings_delegate.h" #include "weblayer/browser/password_manager_driver_factory.h" #include "weblayer/browser/popup_navigation_delegate_impl.h" #include "weblayer/browser/profile_impl.h" +#include "weblayer/browser/signin_url_loader_throttle.h" #include "weblayer/browser/system_network_context_manager.h" #include "weblayer/browser/tab_impl.h" -#include "weblayer/browser/tab_specific_content_settings_delegate.h" #include "weblayer/browser/user_agent.h" #include "weblayer/browser/web_contents_view_delegate_impl.h" #include "weblayer/browser/weblayer_browser_interface_binders.h" -#include "weblayer/browser/weblayer_content_browser_overlay_manifest.h" #include "weblayer/browser/weblayer_security_blocking_page_factory.h" #include "weblayer/browser/weblayer_speech_recognition_manager_delegate.h" #include "weblayer/common/features.h" @@ -118,6 +126,7 @@ #include "weblayer/browser/devtools_manager_delegate_android.h" #include "weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.h" #include "weblayer/browser/safe_browsing/safe_browsing_service.h" +#include "weblayer/browser/tts_environment_android_impl.h" #endif #if defined(OS_LINUX) || defined(OS_ANDROID) @@ -125,8 +134,8 @@ #endif #if defined(OS_WIN) +#include "sandbox/policy/win/sandbox_win.h" #include "sandbox/win/src/sandbox.h" -#include "services/service_manager/sandbox/win/sandbox_win.h" #endif #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) @@ -155,6 +164,16 @@ bool IsSafebrowsingSupported() { return false; } +bool IsNetworkErrorAutoReloadEnabled() { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kEnableAutoReload)) + return true; + if (command_line.HasSwitch(switches::kDisableAutoReload)) + return false; + return true; +} + bool IsInHostedApp(content::WebContents* web_contents) { return false; } @@ -227,6 +246,14 @@ void RegisterPrefs(PrefRegistrySimple* pref_registry) { variations::VariationsService::RegisterPrefs(pref_registry); } +mojo::PendingRemote<prerender::mojom::PrerenderCanceler> GetPrerenderCanceler( + content::WebContents* web_contents) { + mojo::PendingRemote<prerender::mojom::PrerenderCanceler> canceler; + weblayer::PrerenderContentsFromWebContents(web_contents) + ->AddPrerenderCancelerReceiver(canceler.InitWithNewPipeAndPassReceiver()); + return canceler; +} + } // namespace ContentBrowserClientImpl::ContentBrowserClientImpl(MainParams* params) @@ -278,13 +305,6 @@ ContentBrowserClientImpl::GetDevToolsManagerDelegate() { #endif } -base::Optional<service_manager::Manifest> -ContentBrowserClientImpl::GetServiceManifestOverlay(base::StringPiece name) { - if (name == content::mojom::kBrowserServiceName) - return GetWebLayerContentBrowserOverlayManifest(); - return base::nullopt; -} - void ContentBrowserClientImpl::LogWebFeatureForCurrentPage( content::RenderFrameHost* render_frame_host, blink::mojom::WebFeature feature) { @@ -399,6 +419,23 @@ ContentBrowserClientImpl::CreateURLLoaderThrottles( #endif } + auto signin_throttle = + SigninURLLoaderThrottle::Create(browser_context, wc_getter); + if (signin_throttle) + result.push_back(std::move(signin_throttle)); + + // Create prerender URL throttle. + auto* web_contents = wc_getter.Run(); + auto* prerender_contents = PrerenderContentsFromWebContents(web_contents); + if (prerender_contents && prerender_contents->prerender_mode() != + prerender::mojom::PrerenderMode::kNoPrerender) { + result.push_back(std::make_unique<prerender::PrerenderURLLoaderThrottle>( + prerender_contents->prerender_mode(), + prerender::PrerenderHistograms::GetHistogramPrefix( + prerender_contents->origin()), + GetPrerenderCanceler(web_contents))); + } + return result; } @@ -450,6 +487,40 @@ ContentBrowserClientImpl::GetOriginsRequiringDedicatedProcess() { return site_isolation::GetBrowserSpecificBuiltInIsolatedOrigins(); } +bool ContentBrowserClientImpl::MayReuseHost( + content::RenderProcessHost* process_host) { + // If there is currently a prerender in progress for the host provided, + // it may not be shared. We require prerenders to be by themselves in a + // separate process so that we can monitor their resource usage. + prerender::PrerenderManager* prerender_manager = + PrerenderManagerFactory::GetForBrowserContext( + process_host->GetBrowserContext()); + if (prerender_manager && + !prerender_manager->MayReuseProcessHost(process_host)) { + return false; + } + + return true; +} + +void ContentBrowserClientImpl::OverridePageVisibilityState( + content::RenderFrameHost* render_frame_host, + content::PageVisibilityState* visibility_state) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(render_frame_host); + DCHECK(web_contents); + + prerender::PrerenderManager* prerender_manager = + PrerenderManagerFactory::GetForBrowserContext( + web_contents->GetBrowserContext()); + if (prerender_manager && + prerender_manager->IsWebContentsPrerendering(web_contents, nullptr)) { + *visibility_state = content::PageVisibilityState::kHiddenButPainting; + } +} + bool ContentBrowserClientImpl::ShouldDisableSiteIsolation() { return site_isolation::SiteIsolationPolicy:: ShouldDisableSiteIsolationDueToMemoryThreshold(); @@ -563,6 +634,17 @@ ContentBrowserClientImpl::CreateThrottlesForNavigation( std::vector<std::unique_ptr<content::NavigationThrottle>> throttles; if (handle->IsInMainFrame()) { + NavigationUIDataImpl* navigation_ui_data = + static_cast<NavigationUIDataImpl*>(handle->GetNavigationUIData()); + if ((!navigation_ui_data || + !navigation_ui_data->disable_network_error_auto_reload()) && + IsNetworkErrorAutoReloadEnabled()) { + auto auto_reload_throttle = + error_page::NetErrorAutoReloader::MaybeCreateThrottleFor(handle); + if (auto_reload_throttle) + throttles.push_back(std::move(auto_reload_throttle)); + } + // MetricsNavigationThrottle requires that it runs before // NavigationThrottles that may delay or cancel navigations, so only // NavigationThrottles that don't delay or cancel navigations (e.g. @@ -570,6 +652,10 @@ ContentBrowserClientImpl::CreateThrottlesForNavigation( // behavior) should be added before MetricsNavigationThrottle. throttles.push_back( page_load_metrics::MetricsNavigationThrottle::Create(handle)); + if (TabImpl::FromWebContents(handle->GetWebContents())) { + throttles.push_back( + std::make_unique<NavigationErrorNavigationThrottle>(handle)); + } } // The next highest priority throttle *must* be this as it's responsible for @@ -587,6 +673,15 @@ ContentBrowserClientImpl::CreateThrottlesForNavigation( handle, std::make_unique<SSLCertReporterImpl>(), base::BindOnce(&HandleSSLErrorWrapper), base::BindOnce(&IsInHostedApp))); + std::unique_ptr<security_interstitials::InsecureFormNavigationThrottle> + insecure_form_throttle = security_interstitials:: + InsecureFormNavigationThrottle::MaybeCreateNavigationThrottle( + handle, std::make_unique<WebLayerSecurityBlockingPageFactory>(), + nullptr); + if (insecure_form_throttle) { + throttles.push_back(std::move(insecure_form_throttle)); + } + #if defined(OS_ANDROID) if (handle->IsInMainFrame()) { if (base::FeatureList::IsEnabled(features::kWebLayerSafeBrowsing) && @@ -683,7 +778,7 @@ void ContentBrowserClientImpl::RenderProcessWillLaunch( /*can_persist_data*/ true, /*force_to_support_secure_codecs*/ false)); #endif - TabSpecificContentSettingsDelegate::UpdateRendererContentSettingRules(host); + PageSpecificContentSettingsDelegate::UpdateRendererContentSettingRules(host); } scoped_refptr<content::QuotaPermissionContext> @@ -691,12 +786,6 @@ ContentBrowserClientImpl::CreateQuotaPermissionContext() { return base::MakeRefCounted<permissions::QuotaPermissionContextImpl>(); } -content::TtsPlatform* ContentBrowserClientImpl::GetTtsPlatform() { - // TODO(sky): figure out a better way to integrate this. - content::TtsController::GetInstance()->SetStopSpeakingWhenHidden(true); - return nullptr; -} - void ContentBrowserClientImpl::CreateFeatureListAndFieldTrials() { local_state_ = CreateLocalState(); feature_list_creator_ = @@ -708,7 +797,7 @@ void ContentBrowserClientImpl::CreateFeatureListAndFieldTrials() { #if defined(OS_ANDROID) SafeBrowsingService* ContentBrowserClientImpl::GetSafeBrowsingService() { - return BrowserProcess::GetInstance()->GetSafeBrowsingService(GetUserAgent()); + return BrowserProcess::GetInstance()->GetSafeBrowsingService(); } #endif @@ -809,6 +898,12 @@ ContentBrowserClientImpl::CreateLoginDelegate( auth_info, web_contents, first_auth_attempt, std::move(auth_required_callback)); } + +std::unique_ptr<content::TtsEnvironmentAndroid> +ContentBrowserClientImpl::CreateTtsEnvironmentAndroid() { + return std::make_unique<TtsEnvironmentAndroidImpl>(); +} + #endif // OS_ANDROID content::SpeechRecognitionManagerDelegate* diff --git a/chromium/weblayer/browser/content_browser_client_impl.h b/chromium/weblayer/browser/content_browser_client_impl.h index a8c2ea7c804..881fa76274e 100644 --- a/chromium/weblayer/browser/content_browser_client_impl.h +++ b/chromium/weblayer/browser/content_browser_client_impl.h @@ -39,8 +39,6 @@ class ContentBrowserClientImpl : public content::ContentBrowserClient { content::WebContents* web_contents) override; bool CanShutdownGpuProcessNowOnIOThread() override; content::DevToolsManagerDelegate* GetDevToolsManagerDelegate() override; - base::Optional<service_manager::Manifest> GetServiceManifestOverlay( - base::StringPiece name) override; void LogWebFeatureForCurrentPage(content::RenderFrameHost* render_frame_host, blink::mojom::WebFeature feature) override; std::string GetProduct() override; @@ -66,6 +64,10 @@ class ContentBrowserClientImpl : public content::ContentBrowserClient { int frame_tree_node_id) override; bool IsHandledURL(const GURL& url) override; std::vector<url::Origin> GetOriginsRequiringDedicatedProcess() override; + bool MayReuseHost(content::RenderProcessHost* process_host) override; + void OverridePageVisibilityState( + content::RenderFrameHost* render_frame_host, + content::PageVisibilityState* visibility_state) override; bool ShouldDisableSiteIsolation() override; std::vector<std::string> GetAdditionalSiteIsolationModes() override; void PersistIsolatedOrigin(content::BrowserContext* context, @@ -130,11 +132,12 @@ class ContentBrowserClientImpl : public content::ContentBrowserClient { scoped_refptr<net::HttpResponseHeaders> response_headers, bool first_auth_attempt, LoginAuthRequiredCallback auth_required_callback) override; + std::unique_ptr<content::TtsEnvironmentAndroid> CreateTtsEnvironmentAndroid() + override; #endif // OS_ANDROID content::SpeechRecognitionManagerDelegate* CreateSpeechRecognitionManagerDelegate() override; ukm::UkmService* GetUkmService() override; - content::TtsPlatform* GetTtsPlatform() override; void CreateFeatureListAndFieldTrials(); diff --git a/chromium/weblayer/browser/content_view_render_view.cc b/chromium/weblayer/browser/content_view_render_view.cc index 98cd8bcf37a..1c97f4332db 100644 --- a/chromium/weblayer/browser/content_view_render_view.cc +++ b/chromium/weblayer/browser/content_view_render_view.cc @@ -83,13 +83,26 @@ void ContentViewRenderView::OnPhysicalBackingSizeChanged( JNIEnv* env, const JavaParamRef<jobject>& jweb_contents, jint width, - jint height) { + jint height, + jboolean for_config_change) { bool height_changed = height_ != height; height_ = height; content::WebContents* web_contents = content::WebContents::FromJavaWebContents(jweb_contents); gfx::Size size(width, height); - web_contents->GetNativeView()->OnPhysicalBackingSizeChanged(size); + + // The default resize timeout on Android is 1s. It was chosen with browser + // use case in mind where resize is rare (eg orientation change, fullscreen) + // and users are generally willing to wait for those cases instead of seeing + // a frame at the wrong size. Weblayer currently can be resized while user + // is interacting with the page, in which case the timeout is too long. + // For now, use the default long timeout only for rotation (ie config change) + // and just use a zero timeout for all other cases. + base::Optional<base::TimeDelta> override_deadline; + if (!for_config_change) + override_deadline = base::TimeDelta(); + web_contents->GetNativeView()->OnPhysicalBackingSizeChanged( + size, override_deadline); if (height_changed && !height_changed_listener_.is_null()) height_changed_listener_.Run(); @@ -131,6 +144,13 @@ ContentViewRenderView::GetResourceManager(JNIEnv* env) { return compositor_->GetResourceManager().GetJavaObject(); } +void ContentViewRenderView::UpdateBackgroundColor(JNIEnv* env) { + if (!compositor_) + return; + compositor_->SetBackgroundColor( + Java_ContentViewRenderView_getBackgroundColor(env, java_obj_)); +} + void ContentViewRenderView::UpdateLayerTreeHost() { // TODO(wkorman): Rename Layout to UpdateLayerTreeHost in all Android // Compositor related classes. @@ -166,6 +186,7 @@ void ContentViewRenderView::InitCompositor() { cc::ElementId(root_container_layer_->id())); root_container_layer_->SetIsDrawable(false); compositor_->SetRootLayer(root_container_layer_); + UpdateBackgroundColor(base::android::AttachCurrentThread()); } } // namespace weblayer diff --git a/chromium/weblayer/browser/content_view_render_view.h b/chromium/weblayer/browser/content_view_render_view.h index 3a28dac389f..0cf93c027d5 100644 --- a/chromium/weblayer/browser/content_view_render_view.h +++ b/chromium/weblayer/browser/content_view_render_view.h @@ -49,7 +49,8 @@ class ContentViewRenderView : public content::CompositorClient { JNIEnv* env, const base::android::JavaParamRef<jobject>& jweb_contents, jint width, - jint height); + jint height, + jboolean for_config_change); void SurfaceCreated(JNIEnv* env); void SurfaceDestroyed(JNIEnv* env, jboolean cache_back_buffer); void SurfaceChanged(JNIEnv* env, @@ -61,6 +62,7 @@ class ContentViewRenderView : public content::CompositorClient { void SetNeedsRedraw(JNIEnv* env); void EvictCachedSurface(JNIEnv* env); base::android::ScopedJavaLocalRef<jobject> GetResourceManager(JNIEnv* env); + void UpdateBackgroundColor(JNIEnv* env); // CompositorClient implementation void UpdateLayerTreeHost() override; diff --git a/chromium/weblayer/browser/controls_visibility_reason.h b/chromium/weblayer/browser/controls_visibility_reason.h index 7e6885f0681..e866f91ea01 100644 --- a/chromium/weblayer/browser/controls_visibility_reason.h +++ b/chromium/weblayer/browser/controls_visibility_reason.h @@ -28,6 +28,10 @@ enum class ControlsVisibilityReason { // If accessibility is enabled, controls are forced shown. kAccessibility, + // Browser controls visibility can be set to force them to animate in/out when + // being set or cleared. + kAnimation, + kReasonCount, }; diff --git a/chromium/weblayer/browser/cookie_manager_impl.cc b/chromium/weblayer/browser/cookie_manager_impl.cc index ae1392cb88e..4b5262e549f 100644 --- a/chromium/weblayer/browser/cookie_manager_impl.cc +++ b/chromium/weblayer/browser/cookie_manager_impl.cc @@ -147,9 +147,9 @@ bool CookieManagerImpl::SetCookieInternal(const GURL& url, content::BrowserContext::GetDefaultStoragePartition(browser_context_) ->GetCookieManagerForBrowserProcess() - ->SetCanonicalCookie(*cc, url, net::CookieOptions::MakeAllInclusive(), - net::cookie_util::AdaptCookieInclusionStatusToBool( - std::move(callback))); + ->SetCanonicalCookie( + *cc, url, net::CookieOptions::MakeAllInclusive(), + net::cookie_util::AdaptCookieAccessResultToBool(std::move(callback))); return true; } diff --git a/chromium/weblayer/browser/default_search_engine.cc b/chromium/weblayer/browser/default_search_engine.cc new file mode 100644 index 00000000000..756b43c3185 --- /dev/null +++ b/chromium/weblayer/browser/default_search_engine.cc @@ -0,0 +1,35 @@ +// 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 "weblayer/browser/default_search_engine.h" + +#include "base/no_destructor.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" +#include "content/public/browser/browser_context.h" +#include "url/gurl.h" +#include "url/origin.h" +#include "weblayer/browser/host_content_settings_map_factory.h" + +namespace weblayer { + +const url::Origin& GetDseOrigin() { + static const base::NoDestructor<url::Origin> kOrigin( + url::Origin::Create(GURL("https://www.google.com"))); + return *kOrigin; +} + +bool IsPermissionControlledByDse(ContentSettingsType type, + const url::Origin& origin) { + return type == ContentSettingsType::GEOLOCATION && GetDseOrigin() == origin; +} + +void ResetDsePermissions(content::BrowserContext* browser_context) { + GURL url = GetDseOrigin().GetURL(); + HostContentSettingsMapFactory::GetForBrowserContext(browser_context) + ->SetContentSettingDefaultScope(url, url, + ContentSettingsType::GEOLOCATION, + std::string(), CONTENT_SETTING_ALLOW); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/default_search_engine.h b/chromium/weblayer/browser/default_search_engine.h new file mode 100644 index 00000000000..9ee6b45302e --- /dev/null +++ b/chromium/weblayer/browser/default_search_engine.h @@ -0,0 +1,35 @@ +// 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 WEBLAYER_BROWSER_DEFAULT_SEARCH_ENGINE_H_ +#define WEBLAYER_BROWSER_DEFAULT_SEARCH_ENGINE_H_ + +#include "components/content_settings/core/common/content_settings.h" + +namespace content { +class BrowserContext; +} + +namespace url { +class Origin; +} + +namespace weblayer { +const url::Origin& GetDseOrigin(); + +// Returns whether permissions for |type| and |origin| are controlled by +// WebLayer's default search engine logic. This only applies to the GEOLOCATION +// permission, which will be force allowed and controlled by the client app's +// system level location permissions. +bool IsPermissionControlledByDse(ContentSettingsType type, + const url::Origin& origin); + +// Resets all permissions managed by WebLayer for the default search engine. +// TODO(crbug.com/1063433): If this logic gets more complicated consider +// refactoring SearchPermissionsService to be used in WebLayer. +void ResetDsePermissions(content::BrowserContext* browser_context); + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_DEFAULT_SEARCH_ENGINE_H_ diff --git a/chromium/weblayer/browser/download_browsertest.cc b/chromium/weblayer/browser/download_browsertest.cc index 15918ee9030..c44c8c89fa0 100644 --- a/chromium/weblayer/browser/download_browsertest.cc +++ b/chromium/weblayer/browser/download_browsertest.cc @@ -24,6 +24,7 @@ #include "weblayer/public/navigation_controller.h" #include "weblayer/shell/browser/shell.h" #include "weblayer/test/test_navigation_observer.h" +#include "weblayer/test/weblayer_browser_test_utils.h" namespace weblayer { @@ -281,6 +282,13 @@ IN_PROC_BROWSER_TEST_F(DownloadBrowserTest, Cancel) { } IN_PROC_BROWSER_TEST_F(DownloadBrowserTest, PauseResume) { + // Add an initial navigation to avoid the tab being deleted if the first + // navigation is a download, since we use the tab for convenience in the + // lambda. + OneShotNavigationObserver observer(shell()); + shell()->tab()->GetNavigationController()->Navigate(GURL("about:blank")); + observer.WaitForNavigation(); + set_started_callback(base::BindLambdaForTesting([&](Download* download) { download->Pause(); GURL url = embedded_test_server()->GetURL( diff --git a/chromium/weblayer/browser/download_manager_delegate_impl.cc b/chromium/weblayer/browser/download_manager_delegate_impl.cc index 00c35b6b4a7..1f889a95e92 100644 --- a/chromium/weblayer/browser/download_manager_delegate_impl.cc +++ b/chromium/weblayer/browser/download_manager_delegate_impl.cc @@ -209,6 +209,16 @@ void DownloadManagerDelegateImpl::OnManagerInitialized() { void DownloadManagerDelegateImpl::OnDownloadUpdated( download::DownloadItem* item) { + // If this is the first navigation in a tab it should be closed. Wait until + // the target path is determined or the download is canceled to check. + if (!item->GetTargetFilePath().empty() || + item->GetState() == download::DownloadItem::CANCELLED) { + content::WebContents* web_contents = + content::DownloadItemUtils::GetWebContents(item); + if (web_contents && web_contents->GetController().IsInitialNavigation()) + web_contents->Close(); + } + auto* delegate = GetDelegate(item); if (item->GetState() == download::DownloadItem::COMPLETE || item->GetState() == download::DownloadItem::CANCELLED || diff --git a/chromium/weblayer/browser/error_page_callback_proxy.cc b/chromium/weblayer/browser/error_page_callback_proxy.cc index a823386788f..db7add323f3 100644 --- a/chromium/weblayer/browser/error_page_callback_proxy.cc +++ b/chromium/weblayer/browser/error_page_callback_proxy.cc @@ -4,8 +4,11 @@ #include "weblayer/browser/error_page_callback_proxy.h" +#include "base/android/jni_string.h" #include "url/gurl.h" #include "weblayer/browser/java/jni/ErrorPageCallbackProxy_jni.h" +#include "weblayer/browser/navigation_impl.h" +#include "weblayer/public/error_page.h" #include "weblayer/public/tab.h" using base::android::AttachCurrentThread; @@ -29,6 +32,19 @@ bool ErrorPageCallbackProxy::OnBackToSafety() { return Java_ErrorPageCallbackProxy_onBackToSafety(env, java_impl_); } +std::unique_ptr<ErrorPage> ErrorPageCallbackProxy::GetErrorPageContent( + Navigation* navigation) { + JNIEnv* env = AttachCurrentThread(); + auto error_string = Java_ErrorPageCallbackProxy_getErrorPageContent( + env, java_impl_, + static_cast<NavigationImpl*>(navigation)->java_navigation()); + if (!error_string) + return nullptr; + auto error_page = std::make_unique<ErrorPage>(); + error_page->html = ConvertJavaStringToUTF8(env, error_string); + return error_page; +} + static jlong JNI_ErrorPageCallbackProxy_CreateErrorPageCallbackProxy( JNIEnv* env, const base::android::JavaParamRef<jobject>& proxy, diff --git a/chromium/weblayer/browser/error_page_callback_proxy.h b/chromium/weblayer/browser/error_page_callback_proxy.h index a63fcc2b157..ad005842f9e 100644 --- a/chromium/weblayer/browser/error_page_callback_proxy.h +++ b/chromium/weblayer/browser/error_page_callback_proxy.h @@ -24,6 +24,8 @@ class ErrorPageCallbackProxy : public ErrorPageDelegate { // ErrorPageDelegate: bool OnBackToSafety() override; + std::unique_ptr<ErrorPage> GetErrorPageContent( + Navigation* navigation) override; private: Tab* tab_; diff --git a/chromium/weblayer/browser/errorpage_browsertest.cc b/chromium/weblayer/browser/errorpage_browsertest.cc index ea080df71f0..776b26ba143 100644 --- a/chromium/weblayer/browser/errorpage_browsertest.cc +++ b/chromium/weblayer/browser/errorpage_browsertest.cc @@ -7,10 +7,13 @@ #include "base/macros.h" #include "base/test/bind_test_util.h" #include "base/test/scoped_feature_list.h" +#include "components/error_page/content/browser/net_error_auto_reloader.h" +#include "content/public/test/browser_test_utils.h" #include "content/public/test/url_loader_interceptor.h" -#include "net/base/mock_network_change_notifier.h" #include "net/test/url_request/url_request_failed_job.h" -#include "weblayer/common/features.h" +#include "weblayer/browser/tab_impl.h" +#include "weblayer/public/common/switches.h" +#include "weblayer/public/navigation_controller.h" #include "weblayer/shell/browser/shell.h" #include "weblayer/test/weblayer_browser_test_utils.h" @@ -49,8 +52,49 @@ IN_PROC_BROWSER_TEST_F(ErrorPageBrowserTest, 404WithEmptyBody) { class ErrorPageReloadBrowserTest : public ErrorPageBrowserTest { public: - ErrorPageReloadBrowserTest() { - feature_list_.InitAndEnableFeature(features::kEnableAutoReload); + ErrorPageReloadBrowserTest() = default; + + void SetUpCommandLine(base::CommandLine* command_line) override { + command_line->AppendSwitch(switches::kEnableAutoReload); + ErrorPageBrowserTest::SetUpCommandLine(command_line); + } + + // Helper to perform navigations, whether successful or intercepted for + // simulated failure. Note that this asynchronously initiates the navigation + // and then waits only for the *navigation* to finish; this is in contrast to + // common test utilities which wait for loading to finish. It matters because + // most of NetErrorAutoReloader's interesting behavior is triggered at + // navigation completion and tests may want to observe the immediate side + // effects, such as the scheduling of an auto-reload timer. + // + // Return true if the navigation was successful, or false if it failed. + bool Navigate(const GURL& url, bool disable_network_error_auto_reload = false) + WARN_UNUSED_RESULT { + content::TestNavigationManager navigation(web_contents(), url); + NavigationController::NavigateParams params; + params.disable_network_error_auto_reload = + disable_network_error_auto_reload; + shell()->tab()->GetNavigationController()->Navigate(url, params); + navigation.WaitForNavigationFinished(); + return navigation.was_successful(); + } + + // Returns the time-delay of the currently scheduled auto-reload task, if one + // is scheduled. If no auto-reload is scheduled, this returns null. + base::Optional<base::TimeDelta> GetCurrentAutoReloadDelay() { + auto* auto_reloader = + error_page::NetErrorAutoReloader::FromWebContents(web_contents()); + if (!auto_reloader) + return base::nullopt; + const base::Optional<base::OneShotTimer>& timer = + auto_reloader->next_reload_timer_for_testing(); + if (!timer) + return base::nullopt; + return timer->GetCurrentDelay(); + } + + content::WebContents* web_contents() { + return static_cast<TabImpl*>(shell()->tab())->web_contents(); } private: @@ -58,13 +102,15 @@ class ErrorPageReloadBrowserTest : public ErrorPageBrowserTest { }; IN_PROC_BROWSER_TEST_F(ErrorPageReloadBrowserTest, ReloadOnNetworkChanged) { - // Make sure the renderer thinks it's online, since that is a necessary - // condition for the reload. - net::test::ScopedMockNetworkChangeNotifier mock_network_change_notifier; - mock_network_change_notifier.mock_network_change_notifier() - ->SetConnectionType(net::NetworkChangeNotifier::CONNECTION_4G); - ASSERT_TRUE(embedded_test_server()->Start()); + // Ensure that the NetErrorAutoReloader believes it's online, otherwise it + // does not attempt auto-reload on error pages. + error_page::NetErrorAutoReloader::CreateForWebContents(web_contents()); + auto* reloader = + error_page::NetErrorAutoReloader::FromWebContents(web_contents()); + reloader->DisableConnectionChangeObservationForTesting(); + reloader->OnConnectionChanged(network::mojom::ConnectionType::CONNECTION_4G); + GURL url = embedded_test_server()->GetURL("/error_page"); // We send net::ERR_NETWORK_CHANGED on the first load, and the reload should // get a net::OK response. @@ -88,4 +134,48 @@ IN_PROC_BROWSER_TEST_F(ErrorPageReloadBrowserTest, ReloadOnNetworkChanged) { NavigateAndWaitForCompletion(url, shell()); } +// By default auto reload is enabled. +IN_PROC_BROWSER_TEST_F(ErrorPageReloadBrowserTest, AutoReloadDefault) { + ASSERT_TRUE(embedded_test_server()->Start()); + // Ensure that the NetErrorAutoReloader believes it's online, otherwise it + // does not attempt auto-reload on error pages. + error_page::NetErrorAutoReloader::CreateForWebContents(web_contents()); + auto* reloader = + error_page::NetErrorAutoReloader::FromWebContents(web_contents()); + reloader->DisableConnectionChangeObservationForTesting(); + reloader->OnConnectionChanged(network::mojom::ConnectionType::CONNECTION_4G); + + GURL url = embedded_test_server()->GetURL("/error_page"); + content::URLLoaderInterceptor interceptor(base::BindLambdaForTesting( + [&url](content::URLLoaderInterceptor::RequestParams* params) { + if (params->url_request.url == url) { + params->client->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_NETWORK_CHANGED)); + return true; + } + return false; + })); + + EXPECT_FALSE(Navigate(url)); + EXPECT_EQ(error_page::NetErrorAutoReloader::GetNextReloadDelayForTesting(0), + GetCurrentAutoReloadDelay()); +} + +IN_PROC_BROWSER_TEST_F(ErrorPageReloadBrowserTest, AutoReloadDisabled) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url = embedded_test_server()->GetURL("/error_page"); + content::URLLoaderInterceptor interceptor(base::BindLambdaForTesting( + [&url](content::URLLoaderInterceptor::RequestParams* params) { + if (params->url_request.url == url) { + params->client->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_NETWORK_CHANGED)); + return true; + } + return false; + })); + + EXPECT_FALSE(Navigate(url, true)); + EXPECT_EQ(base::nullopt, GetCurrentAutoReloadDelay()); +} + } // namespace weblayer diff --git a/chromium/weblayer/browser/favicon/favicon_backend_wrapper.cc b/chromium/weblayer/browser/favicon/favicon_backend_wrapper.cc new file mode 100644 index 00000000000..9f3108b7495 --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_backend_wrapper.cc @@ -0,0 +1,193 @@ +// 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 "weblayer/browser/favicon/favicon_backend_wrapper.h" + +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/sequenced_task_runner.h" +#include "components/favicon/core/favicon_backend.h" +#include "components/favicon/core/favicon_database.h" + +namespace weblayer { + +// Removing out of date entries can be costly. To avoid blocking the thread +// this code runs on, the work is potentially throttled. Specifically at +// most |kMaxNumberOfEntriesToRemoveAtATime| are removed during a single call. +// If |kMaxNumberOfEntriesToRemoveAtATime| are removed, then there may be more +// entries that can be removed, so the timer is restarted with a shorter time +// out (|kTimeDeltaForRunningExpireWithRemainingWork|). +constexpr base::TimeDelta kTimeDeltaForRunningExpireNoRemainingWork = + base::TimeDelta::FromHours(1); +constexpr int kMaxNumberOfEntriesToRemoveAtATime = 100; + +FaviconBackendWrapper::FaviconBackendWrapper( + scoped_refptr<base::SequencedTaskRunner> task_runner) + : base::RefCountedDeleteOnSequence<FaviconBackendWrapper>(task_runner), + task_runner_(task_runner) {} + +void FaviconBackendWrapper::Init(const base::FilePath& db_path) { + db_path_ = db_path; + favicon_backend_ = favicon::FaviconBackend::Create(db_path, this); + if (!favicon_backend_) { + LOG(WARNING) << "Could not initialize the favicon database."; + + // The favicon db is not critical. On failure initializing try deleting + // the file and repeating. Note that FaviconDatabase already tries to + // initialize twice. + base::DeleteFile(db_path); + + favicon_backend_ = favicon::FaviconBackend::Create(db_path, this); + if (!favicon_backend_) { + LOG(WARNING) << "Could not initialize db second time, giving up."; + return; + } + } + + expire_timer_.Start(FROM_HERE, kTimeDeltaForRunningExpireWithRemainingWork, + this, &FaviconBackendWrapper::OnExpireTimerFired); +} + +void FaviconBackendWrapper::Shutdown() { + // Ensures there isn't a reference to this in the task runner (by way of the + // task the timer posts). + commit_timer_.Stop(); + expire_timer_.Stop(); +} + +void FaviconBackendWrapper::DeleteAndRecreateDatabase() { + Shutdown(); + favicon_backend_.reset(); + base::DeleteFile(db_path_); + Init(db_path_); +} + +std::vector<favicon_base::FaviconRawBitmapResult> +FaviconBackendWrapper::GetFaviconsForUrl( + const GURL& page_url, + const favicon_base::IconTypeSet& icon_types, + const std::vector<int>& desired_sizes) { + if (!favicon_backend_) + return {}; + return favicon_backend_->GetFaviconsForUrl(page_url, icon_types, + desired_sizes, + /* fallback_to_host */ false); +} + +void FaviconBackendWrapper::SetFaviconsOutOfDateForPage(const GURL& page_url) { + if (favicon_backend_ && + favicon_backend_->SetFaviconsOutOfDateForPage(page_url)) { + ScheduleCommit(); + } +} + +void FaviconBackendWrapper::SetFavicons(const base::flat_set<GURL>& page_urls, + favicon_base::IconType icon_type, + const GURL& icon_url, + const std::vector<SkBitmap>& bitmaps) { + if (favicon_backend_ && + favicon_backend_ + ->SetFavicons(page_urls, icon_type, icon_url, bitmaps, + favicon::FaviconBitmapType::ON_VISIT) + .did_change_database()) { + ScheduleCommit(); + } +} + +void FaviconBackendWrapper::CloneFaviconMappingsForPages( + const GURL& page_url_to_read, + const favicon_base::IconTypeSet& icon_types, + const base::flat_set<GURL>& page_urls_to_write) { + if (!favicon_backend_) + return; + + std::set<GURL> changed_urls = favicon_backend_->CloneFaviconMappingsForPages( + {page_url_to_read}, icon_types, page_urls_to_write); + if (!changed_urls.empty()) + ScheduleCommit(); +} + +std::vector<favicon_base::FaviconRawBitmapResult> +FaviconBackendWrapper::GetFavicon(const GURL& icon_url, + favicon_base::IconType icon_type, + const std::vector<int>& desired_sizes) { + return UpdateFaviconMappingsAndFetch({}, icon_url, icon_type, desired_sizes); +} + +std::vector<favicon_base::FaviconRawBitmapResult> +FaviconBackendWrapper::UpdateFaviconMappingsAndFetch( + const base::flat_set<GURL>& page_urls, + const GURL& icon_url, + favicon_base::IconType icon_type, + const std::vector<int>& desired_sizes) { + if (!favicon_backend_) + return {}; + auto result = favicon_backend_->UpdateFaviconMappingsAndFetch( + page_urls, icon_url, icon_type, desired_sizes); + if (!result.updated_page_urls.empty()) + ScheduleCommit(); + return result.bitmap_results; +} + +void FaviconBackendWrapper::DeleteFaviconMappings( + const base::flat_set<GURL>& page_urls, + favicon_base::IconType icon_type) { + if (!favicon_backend_) + return; + + auto deleted_page_urls = + favicon_backend_->DeleteFaviconMappings(page_urls, icon_type); + if (!deleted_page_urls.empty()) + ScheduleCommit(); +} + +std::vector<GURL> FaviconBackendWrapper::GetCachedRecentRedirectsForPage( + const GURL& page_url) { + // By only returning |page_url| this code won't set the favicon on redirects. + // If that becomes necessary, we would need this class to know about + // redirects. Chrome does this by way of HistoryService remembering redirects + // for recent pages. See |HistoryBackend::recent_redirects_|. + return {page_url}; +} + +FaviconBackendWrapper::~FaviconBackendWrapper() = default; + +void FaviconBackendWrapper::ScheduleCommit() { + if (!commit_timer_.IsRunning()) { + // 10 seconds matches that of HistoryBackend. + commit_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(10), this, + &FaviconBackendWrapper::Commit); + } +} + +void FaviconBackendWrapper::Commit() { + if (favicon_backend_) + favicon_backend_->Commit(); +} + +void FaviconBackendWrapper::OnExpireTimerFired() { + if (!favicon_backend_) + return; + + // See comments above |kTimeDeltaForRunningExpireNoRemainingWork| for a + // description of this logic. + favicon::FaviconDatabase* db = favicon_backend_->db(); + auto icon_ids = db->GetFaviconsLastUpdatedBefore( + base::Time::Now() - kTimeDeltaWhenEntriesAreRemoved, + kMaxNumberOfEntriesToRemoveAtATime); + for (favicon_base::FaviconID icon_id : icon_ids) { + db->DeleteFavicon(icon_id); + db->DeleteIconMappingsForFaviconId(icon_id); + } + if (!icon_ids.empty()) + Commit(); + const base::TimeDelta delta = + icon_ids.size() == kMaxNumberOfEntriesToRemoveAtATime + ? kTimeDeltaForRunningExpireWithRemainingWork + : kTimeDeltaForRunningExpireNoRemainingWork; + expire_timer_.Start(FROM_HERE, delta, this, + &FaviconBackendWrapper::OnExpireTimerFired); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/favicon/favicon_backend_wrapper.h b/chromium/weblayer/browser/favicon/favicon_backend_wrapper.h new file mode 100644 index 00000000000..eb74c63ca03 --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_backend_wrapper.h @@ -0,0 +1,122 @@ +// 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 WEBLAYER_BROWSER_FAVICON_FAVICON_BACKEND_WRAPPER_H_ +#define WEBLAYER_BROWSER_FAVICON_FAVICON_BACKEND_WRAPPER_H_ + +#include <memory> +#include <vector> + +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/ref_counted_delete_on_sequence.h" +#include "base/timer/timer.h" +#include "components/favicon/core/favicon_backend_delegate.h" +#include "components/favicon_base/favicon_types.h" + +class GURL; + +namespace base { +class FilePath; +class SequencedTaskRunner; +} // namespace base + +namespace favicon { +class FaviconBackend; +} + +namespace weblayer { + +// FaviconBackendWrapper runs on a background task-runner and owns the database +// side of favicons. This class largely delegates to favicon::FaviconBackend. +class FaviconBackendWrapper + : public base::RefCountedDeleteOnSequence<FaviconBackendWrapper>, + public favicon::FaviconBackendDelegate { + public: + explicit FaviconBackendWrapper( + scoped_refptr<base::SequencedTaskRunner> task_runner); + FaviconBackendWrapper(const FaviconBackendWrapper&) = delete; + FaviconBackendWrapper& operator=(const FaviconBackendWrapper&) = delete; + + void Init(const base::FilePath& db_path); + + void Shutdown(); + + void DeleteAndRecreateDatabase(); + + // All of these functions are called by the FaviconServiceImpl. They call + // through to |favicon_backend_|. + std::vector<favicon_base::FaviconRawBitmapResult> GetFaviconsForUrl( + const GURL& page_url, + const favicon_base::IconTypeSet& icon_types, + const std::vector<int>& desired_sizes); + void SetFaviconsOutOfDateForPage(const GURL& page_url); + void SetFavicons(const base::flat_set<GURL>& page_urls, + favicon_base::IconType icon_type, + const GURL& icon_url, + const std::vector<SkBitmap>& bitmaps); + void CloneFaviconMappingsForPages( + const GURL& page_url_to_read, + const favicon_base::IconTypeSet& icon_types, + const base::flat_set<GURL>& page_urls_to_write); + std::vector<favicon_base::FaviconRawBitmapResult> GetFavicon( + const GURL& icon_url, + favicon_base::IconType icon_type, + const std::vector<int>& desired_sizes); + std::vector<favicon_base::FaviconRawBitmapResult> + UpdateFaviconMappingsAndFetch(const base::flat_set<GURL>& page_urls, + const GURL& icon_url, + favicon_base::IconType icon_type, + const std::vector<int>& desired_sizes); + void DeleteFaviconMappings(const base::flat_set<GURL>& page_urls, + favicon_base::IconType icon_type); + + // favicon::FaviconBackendDelegate: + std::vector<GURL> GetCachedRecentRedirectsForPage( + const GURL& page_url) override; + + private: + friend class base::RefCountedDeleteOnSequence<FaviconBackendWrapper>; + friend class base::DeleteHelper<FaviconBackendWrapper>; + friend class FaviconBackendWrapperTest; + + ~FaviconBackendWrapper() override; + + void ScheduleCommit(); + void Commit(); + + // Called to expire (remove) out of date icons and restart the timer. + void OnExpireTimerFired(); + + scoped_refptr<base::SequencedTaskRunner> task_runner_; + + // Timer used to delay commits for a short amount of time. This done to + // batch commits. + base::OneShotTimer commit_timer_; + + // The real implementation of the backend. Is there is a problem initializing + // the database this will be null. + std::unique_ptr<favicon::FaviconBackend> favicon_backend_; + + // Timer used to remove items from the database that are likely no longer + // needed. + base::OneShotTimer expire_timer_; + + base::FilePath db_path_; +}; + +// These values are here only for tests. + +// Amount of time before favicons are removed. That is, any favicons downloaded +// before this amount of time are removed. +constexpr base::TimeDelta kTimeDeltaWhenEntriesAreRemoved = + base::TimeDelta::FromDays(30); + +// See comment near kMaxNumberOfEntriesToRemoveAtATime for details on this. +constexpr base::TimeDelta kTimeDeltaForRunningExpireWithRemainingWork = + base::TimeDelta::FromMinutes(2); + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_FAVICON_FAVICON_BACKEND_WRAPPER_H_ diff --git a/chromium/weblayer/browser/favicon/favicon_backend_wrapper_unittest.cc b/chromium/weblayer/browser/favicon/favicon_backend_wrapper_unittest.cc new file mode 100644 index 00000000000..517b68426d8 --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_backend_wrapper_unittest.cc @@ -0,0 +1,130 @@ +// 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 "weblayer/browser/favicon/favicon_backend_wrapper.h" + +#include <vector> + +#include "base/files/file_path.h" +#include "base/files/scoped_temp_dir.h" +#include "base/memory/ref_counted_memory.h" +#include "base/test/task_environment.h" +#include "base/threading/thread_task_runner_handle.h" +#include "components/favicon/core/favicon_backend.h" +#include "components/favicon/core/favicon_database.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace weblayer { +namespace { + +// Blobs for adding favicons. +const unsigned char kBlob1[] = + "12346102356120394751634516591348710478123649165419234519234512349134"; + +} // namespace + +class FaviconBackendWrapperTest : public testing::Test { + protected: + favicon::FaviconBackend* backend() { + return wrapper_->favicon_backend_.get(); + } + + // testing::Test: + void SetUp() override { + // Get a temporary directory for the test DB files. + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + + db_path_ = temp_dir_.GetPath().AppendASCII("test_db"); + } + + void TearDown() override { + wrapper_ = nullptr; + testing::Test::TearDown(); + } + + base::test::SingleThreadTaskEnvironment task_environment_{ + base::test::TaskEnvironment::TimeSource::MOCK_TIME}; + base::ScopedTempDir temp_dir_; + base::FilePath db_path_; + scoped_refptr<FaviconBackendWrapper> wrapper_; +}; + +TEST_F(FaviconBackendWrapperTest, BasicExpire) { + wrapper_ = base::MakeRefCounted<FaviconBackendWrapper>( + base::ThreadTaskRunnerHandle::Get()); + wrapper_->Init(db_path_); + ASSERT_TRUE(backend()); + auto* db = backend()->db(); + + std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1)); + scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data)); + GURL url("http://google.com"); + const base::Time time1 = base::Time::Now(); + favicon_base::FaviconID favicon_id1 = + db->AddFavicon(url, favicon_base::IconType::kTouchIcon, favicon, + favicon::FaviconBitmapType::ON_VISIT, time1, gfx::Size()); + ASSERT_NE(0, favicon_id1); + favicon::IconMappingID icon_mapping_id1 = + db->AddIconMapping(url, favicon_id1); + ASSERT_NE(0, icon_mapping_id1); + + // Fast forward past first expire running. + task_environment_.FastForwardBy(kTimeDeltaForRunningExpireWithRemainingWork * + 2); + // The icon should still be there. + EXPECT_TRUE(db->GetFaviconHeader(favicon_id1, nullptr, nullptr)); + EXPECT_TRUE(db->HasMappingFor(favicon_id1)); + + // Fast forward such that the icon is removed. + task_environment_.FastForwardBy(kTimeDeltaWhenEntriesAreRemoved); + EXPECT_FALSE(db->GetFaviconHeader(favicon_id1, nullptr, nullptr)); + EXPECT_FALSE(db->HasMappingFor(favicon_id1)); +} + +TEST_F(FaviconBackendWrapperTest, ExpireWithOneRemaining) { + wrapper_ = base::MakeRefCounted<FaviconBackendWrapper>( + base::ThreadTaskRunnerHandle::Get()); + wrapper_->Init(db_path_); + ASSERT_TRUE(backend()); + auto* db = backend()->db(); + + // Add two entries. The second is more recent then the first. + std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1)); + scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data)); + GURL url("http://google.com"); + const base::Time time1 = base::Time::Now(); + favicon_base::FaviconID favicon_id1 = + db->AddFavicon(url, favicon_base::IconType::kTouchIcon, favicon, + favicon::FaviconBitmapType::ON_VISIT, time1, gfx::Size()); + ASSERT_NE(0, favicon_id1); + favicon::IconMappingID icon_mapping_id1 = + db->AddIconMapping(url, favicon_id1); + ASSERT_NE(0, icon_mapping_id1); + const base::Time time2 = time1 + kTimeDeltaWhenEntriesAreRemoved / 2; + favicon_base::FaviconID favicon_id2 = + db->AddFavicon(url, favicon_base::IconType::kTouchIcon, favicon, + favicon::FaviconBitmapType::ON_VISIT, time2, gfx::Size()); + ASSERT_NE(0, favicon_id2); + favicon::IconMappingID icon_mapping_id2 = + db->AddIconMapping(url, favicon_id2); + ASSERT_NE(0, icon_mapping_id2); + + // Fast forward such the first entry is expired and should be removed, but + // not the second. + task_environment_.FastForwardBy(kTimeDeltaWhenEntriesAreRemoved + + base::TimeDelta::FromDays(1)); + EXPECT_FALSE(db->GetFaviconHeader(favicon_id1, nullptr, nullptr)); + EXPECT_FALSE(db->HasMappingFor(favicon_id1)); + EXPECT_TRUE(db->GetFaviconHeader(favicon_id2, nullptr, nullptr)); + EXPECT_TRUE(db->HasMappingFor(favicon_id2)); + + // Fast forward enough such that second is removed. + task_environment_.FastForwardBy(kTimeDeltaWhenEntriesAreRemoved + + base::TimeDelta::FromDays(1)); + EXPECT_FALSE(db->GetFaviconHeader(favicon_id2, nullptr, nullptr)); + EXPECT_FALSE(db->HasMappingFor(favicon_id2)); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/favicon/favicon_callback_proxy.cc b/chromium/weblayer/browser/favicon/favicon_callback_proxy.cc new file mode 100644 index 00000000000..4ec3a90edf3 --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_callback_proxy.cc @@ -0,0 +1,45 @@ +// 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 "weblayer/browser/favicon/favicon_callback_proxy.h" + +#include "base/android/jni_string.h" +#include "ui/gfx/android/java_bitmap.h" +#include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_skia.h" +#include "url/gurl.h" +#include "weblayer/browser/java/jni/FaviconCallbackProxy_jni.h" +#include "weblayer/browser/tab_impl.h" +#include "weblayer/public/favicon_fetcher.h" + +namespace weblayer { + +FaviconCallbackProxy::FaviconCallbackProxy(JNIEnv* env, jobject obj, Tab* tab) + : java_proxy_(env, obj), + favicon_fetcher_(tab->CreateFaviconFetcher(this)) {} + +FaviconCallbackProxy::~FaviconCallbackProxy() = default; + +void FaviconCallbackProxy::OnFaviconChanged(const gfx::Image& image) { + SkBitmap favicon = image.AsImageSkia().GetRepresentation(1.0f).GetBitmap(); + JNIEnv* env = base::android::AttachCurrentThread(); + Java_FaviconCallbackProxy_onFaviconChanged( + env, java_proxy_, + favicon.empty() ? nullptr : gfx::ConvertToJavaBitmap(&favicon)); +} + +static jlong JNI_FaviconCallbackProxy_CreateFaviconCallbackProxy( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& proxy, + jlong tab) { + return reinterpret_cast<jlong>( + new FaviconCallbackProxy(env, proxy, reinterpret_cast<TabImpl*>(tab))); +} + +static void JNI_FaviconCallbackProxy_DeleteFaviconCallbackProxy(JNIEnv* env, + jlong proxy) { + delete reinterpret_cast<FaviconCallbackProxy*>(proxy); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/favicon/favicon_callback_proxy.h b/chromium/weblayer/browser/favicon/favicon_callback_proxy.h new file mode 100644 index 00000000000..a15bc12bf75 --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_callback_proxy.h @@ -0,0 +1,39 @@ +// 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 WEBLAYER_BROWSER_FAVICON_FAVICON_CALLBACK_PROXY_H_ +#define WEBLAYER_BROWSER_FAVICON_FAVICON_CALLBACK_PROXY_H_ + +#include <memory> + +#include <jni.h> + +#include "base/android/scoped_java_ref.h" +#include "weblayer/public/favicon_fetcher_delegate.h" + +namespace weblayer { + +class FaviconFetcher; +class Tab; + +// FullscreenCallbackProxy forwards all FullscreenDelegate functions to the +// Java side. There is at most one FullscreenCallbackProxy per Tab. +class FaviconCallbackProxy : public FaviconFetcherDelegate { + public: + FaviconCallbackProxy(JNIEnv* env, jobject obj, Tab* tab); + FaviconCallbackProxy(const FaviconCallbackProxy&) = delete; + FaviconCallbackProxy& operator=(const FaviconCallbackProxy&) = delete; + ~FaviconCallbackProxy() override; + + // FaviconFetcherDelegate: + void OnFaviconChanged(const gfx::Image& image) override; + + private: + base::android::ScopedJavaGlobalRef<jobject> java_proxy_; + std::unique_ptr<FaviconFetcher> favicon_fetcher_; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_FAVICON_FAVICON_CALLBACK_PROXY_H_ diff --git a/chromium/weblayer/browser/favicon/favicon_fetcher_browsertest.cc b/chromium/weblayer/browser/favicon/favicon_fetcher_browsertest.cc new file mode 100644 index 00000000000..b5231214816 --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_fetcher_browsertest.cc @@ -0,0 +1,167 @@ +// 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 "weblayer/browser/favicon/favicon_fetcher_impl.h" + +#include "base/run_loop.h" +#include "build/build_config.h" +#include "components/favicon/content/content_favicon_driver.h" +#include "ui/gfx/image/image.h" +#include "weblayer/browser/browser_context_impl.h" +#include "weblayer/browser/favicon/favicon_fetcher_impl.h" +#include "weblayer/browser/favicon/favicon_service_impl.h" +#include "weblayer/browser/favicon/favicon_service_impl_factory.h" +#include "weblayer/browser/favicon/favicon_service_impl_observer.h" +#include "weblayer/browser/favicon/test_favicon_fetcher_delegate.h" +#include "weblayer/browser/profile_impl.h" +#include "weblayer/browser/tab_impl.h" +#include "weblayer/public/browser.h" +#include "weblayer/public/favicon_fetcher_delegate.h" +#include "weblayer/public/navigation_controller.h" +#include "weblayer/shell/browser/shell.h" +#include "weblayer/test/test_navigation_observer.h" +#include "weblayer/test/weblayer_browser_test.h" +#include "weblayer/test/weblayer_browser_test_utils.h" + +namespace weblayer { +namespace { + +// FaviconServiceImplObserver used to wait for download to fail. +class TestFaviconServiceImplObserver : public FaviconServiceImplObserver { + public: + void Wait() { + ASSERT_EQ(nullptr, run_loop_.get()); + run_loop_ = std::make_unique<base::RunLoop>(); + run_loop_->Run(); + run_loop_.reset(); + } + + // FaviconServiceImplObserver: + void OnUnableToDownloadFavicon() override { + if (run_loop_) + run_loop_->Quit(); + } + + private: + std::unique_ptr<base::RunLoop> run_loop_; +}; + +} // namespace + +using FaviconFetcherBrowserTest = WebLayerBrowserTest; + +IN_PROC_BROWSER_TEST_F(FaviconFetcherBrowserTest, Basic) { + ASSERT_TRUE(embedded_test_server()->Start()); + TestFaviconFetcherDelegate fetcher_delegate; + auto fetcher = shell()->tab()->CreateFaviconFetcher(&fetcher_delegate); + NavigateAndWaitForCompletion( + embedded_test_server()->GetURL("/simple_page_with_favicon.html"), + shell()); + fetcher_delegate.WaitForFavicon(); + EXPECT_FALSE(fetcher_delegate.last_image().IsEmpty()); + EXPECT_EQ(fetcher_delegate.last_image(), fetcher->GetFavicon()); + EXPECT_EQ(1, fetcher_delegate.on_favicon_changed_call_count()); + fetcher_delegate.ClearLastImage(); + + const GURL url2 = + embedded_test_server()->GetURL("/simple_page_with_favicon2.html"); + shell()->tab()->GetNavigationController()->Navigate(url2); + // Favicon doesn't change immediately on navigation. + EXPECT_FALSE(fetcher->GetFavicon().IsEmpty()); + // Favicon does change once start is received. + TestNavigationObserver test_observer( + url2, TestNavigationObserver::NavigationEvent::kStart, shell()); + test_observer.Wait(); + EXPECT_TRUE(fetcher_delegate.last_image().IsEmpty()); + + fetcher_delegate.WaitForNonemptyFavicon(); + EXPECT_FALSE(fetcher_delegate.last_image().IsEmpty()); + EXPECT_EQ(fetcher_delegate.last_image(), fetcher->GetFavicon()); + // OnFaviconChanged() is called twice, once with an empty image (because of + // the navigation), the second with the real image. + EXPECT_EQ(2, fetcher_delegate.on_favicon_changed_call_count()); +} + +IN_PROC_BROWSER_TEST_F(FaviconFetcherBrowserTest, NavigateToPageWithNoFavicon) { + ASSERT_TRUE(embedded_test_server()->Start()); + TestFaviconFetcherDelegate fetcher_delegate; + auto fetcher = shell()->tab()->CreateFaviconFetcher(&fetcher_delegate); + NavigateAndWaitForCompletion( + embedded_test_server()->GetURL("/simple_page_with_favicon.html"), + shell()); + fetcher_delegate.WaitForFavicon(); + fetcher_delegate.ClearLastImage(); + + TestFaviconServiceImplObserver test_observer; + FaviconServiceImplFactory::GetForBrowserContext( + static_cast<TabImpl*>(shell()->tab())->profile()->GetBrowserContext()) + ->set_observer(&test_observer); + + const GURL url2 = embedded_test_server()->GetURL("/simple_page.html"); + shell()->tab()->GetNavigationController()->Navigate(url2); + EXPECT_TRUE(fetcher_delegate.last_image().IsEmpty()); + // The delegate should be notified of the empty image once. + test_observer.Wait(); + EXPECT_TRUE(fetcher_delegate.last_image().IsEmpty()); + EXPECT_EQ(1, fetcher_delegate.on_favicon_changed_call_count()); +} + +IN_PROC_BROWSER_TEST_F(FaviconFetcherBrowserTest, + ContentFaviconDriverLifetime) { + ASSERT_TRUE(embedded_test_server()->Start()); + content::WebContents* web_contents = + static_cast<TabImpl*>(shell()->tab())->web_contents(); + + // Initially there should be no driver (because favicons haven't been + // requested). + EXPECT_EQ(nullptr, + favicon::ContentFaviconDriver::FromWebContents(web_contents)); + + // Request a fetcher, which should trigger creating ContentFaviconDriver. + TestFaviconFetcherDelegate fetcher_delegate; + auto fetcher = shell()->tab()->CreateFaviconFetcher(&fetcher_delegate); + EXPECT_NE(nullptr, + favicon::ContentFaviconDriver::FromWebContents(web_contents)); + + // Destroy the fetcher, which should destroy ContentFaviconDriver. + fetcher.reset(); + EXPECT_EQ(nullptr, + favicon::ContentFaviconDriver::FromWebContents(web_contents)); + + // One more time, and this time navigate. + fetcher = shell()->tab()->CreateFaviconFetcher(&fetcher_delegate); + NavigateAndWaitForCompletion( + embedded_test_server()->GetURL("/simple_page_with_favicon.html"), + shell()); + fetcher_delegate.WaitForFavicon(); + EXPECT_FALSE(fetcher_delegate.last_image().IsEmpty()); +} + +// This test creates a Browser and Tab, which doesn't work well with Java when +// driven from native code. +#if !defined(OS_ANDROID) +IN_PROC_BROWSER_TEST_F(FaviconFetcherBrowserTest, OffTheRecord) { + auto otr_profile = Profile::Create(std::string()); + ProfileImpl* otr_profile_impl = static_cast<ProfileImpl*>(otr_profile.get()); + EXPECT_TRUE(otr_profile_impl->GetBrowserContext()->IsOffTheRecord()); + auto otr_browser = Browser::Create(otr_profile.get(), nullptr); + Tab* tab = otr_browser->CreateTab(); + + // There is no FaviconService for off the record profiles. FaviconService + // writes to disk, which is not appropriate for off the record mode. + EXPECT_EQ(nullptr, FaviconServiceImplFactory::GetForBrowserContext( + otr_profile_impl->GetBrowserContext())); + ASSERT_TRUE(embedded_test_server()->Start()); + TestFaviconFetcherDelegate fetcher_delegate; + auto fetcher = tab->CreateFaviconFetcher(&fetcher_delegate); + NavigateAndWaitForCompletion( + embedded_test_server()->GetURL("/simple_page_with_favicon.html"), tab); + fetcher_delegate.WaitForFavicon(); + EXPECT_FALSE(fetcher_delegate.last_image().IsEmpty()); + EXPECT_EQ(fetcher_delegate.last_image(), fetcher->GetFavicon()); + EXPECT_EQ(1, fetcher_delegate.on_favicon_changed_call_count()); +} +#endif + +} // namespace weblayer diff --git a/chromium/weblayer/browser/favicon/favicon_fetcher_impl.cc b/chromium/weblayer/browser/favicon/favicon_fetcher_impl.cc new file mode 100644 index 00000000000..d333ff22187 --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_fetcher_impl.cc @@ -0,0 +1,27 @@ +// 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 "weblayer/browser/favicon/favicon_fetcher_impl.h" + +#include "ui/gfx/image/image.h" +#include "weblayer/browser/favicon/favicon_tab_helper.h" +#include "weblayer/public/favicon_fetcher_delegate.h" + +#include "base/logging.h" + +namespace weblayer { + +FaviconFetcherImpl::FaviconFetcherImpl(content::WebContents* web_contents, + FaviconFetcherDelegate* delegate) + : web_contents_(web_contents), + observer_subscription_(FaviconTabHelper::FromWebContents(web_contents) + ->RegisterFaviconFetcherDelegate(delegate)) {} + +FaviconFetcherImpl::~FaviconFetcherImpl() = default; + +gfx::Image FaviconFetcherImpl::GetFavicon() { + return FaviconTabHelper::FromWebContents(web_contents_)->favicon(); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/favicon/favicon_fetcher_impl.h b/chromium/weblayer/browser/favicon/favicon_fetcher_impl.h new file mode 100644 index 00000000000..3a68e377b3f --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_fetcher_impl.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 WEBLAYER_BROWSER_FAVICON_FAVICON_FETCHER_IMPL_H_ +#define WEBLAYER_BROWSER_FAVICON_FAVICON_FETCHER_IMPL_H_ + +#include <memory> + +#include "weblayer/browser/favicon/favicon_tab_helper.h" +#include "weblayer/public/favicon_fetcher.h" + +namespace content { +class WebContents; +} + +namespace weblayer { + +class FaviconFetcherDelegate; + +// FaviconFetcher implementation that largely delegates to FaviconTabHelper +// for the real implementation. +class FaviconFetcherImpl : public FaviconFetcher { + public: + FaviconFetcherImpl(content::WebContents* web_contents, + FaviconFetcherDelegate* delegate); + FaviconFetcherImpl(const FaviconFetcherImpl&) = delete; + FaviconFetcherImpl& operator=(const FaviconFetcherImpl&) = delete; + ~FaviconFetcherImpl() override; + + // FaviconFetcher: + gfx::Image GetFavicon() override; + + private: + content::WebContents* web_contents_; + std::unique_ptr<FaviconTabHelper::ObserverSubscription> + observer_subscription_; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_FAVICON_FAVICON_FETCHER_IMPL_H_ diff --git a/chromium/weblayer/browser/favicon/favicon_service_impl.cc b/chromium/weblayer/browser/favicon/favicon_service_impl.cc new file mode 100644 index 00000000000..b8ac3421677 --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_service_impl.cc @@ -0,0 +1,249 @@ +// 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 "weblayer/browser/favicon/favicon_service_impl.h" + +#include <stddef.h> + +#include <vector> + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/hash/hash.h" +#include "base/task/task_traits.h" +#include "base/task/thread_pool.h" +#include "build/build_config.h" +#include "components/favicon_base/favicon_util.h" +#include "components/favicon_base/select_favicon_frames.h" +#include "content/public/common/url_constants.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/favicon_size.h" +#include "url/gurl.h" +#include "weblayer/browser/favicon/favicon_backend_wrapper.h" +#include "weblayer/browser/favicon/favicon_service_impl_observer.h" + +namespace weblayer { +namespace { + +bool CanAddUrl(const GURL& url) { + if (!url.is_valid()) + return false; + + if (url.SchemeIs(url::kJavaScriptScheme) || url.SchemeIs(url::kAboutScheme) || + url.SchemeIs(url::kContentScheme) || + url.SchemeIs(content::kChromeDevToolsScheme) || + url.SchemeIs(content::kChromeUIScheme) || + url.SchemeIs(content::kViewSourceScheme)) { + return false; + } + + return true; +} + +// Returns the IconTypeSet for the current platform. This matches the set +// of favicon types that are requested for the platform (see +// FaviconDriverImpl). +favicon_base::IconTypeSet GetIconTypeSet() { +#if defined(OS_ANDROID) + return {favicon_base::IconType::kFavicon, favicon_base::IconType::kTouchIcon, + favicon_base::IconType::kTouchPrecomposedIcon, + favicon_base::IconType::kWebManifestIcon}; +#else + return {favicon_base::IconType::kFavicon}; +#endif +} + +int GetDesiredFaviconSizeInDips() { +#if defined(OS_ANDROID) + // This is treatest as the largest available icon. + return 0; +#else + return gfx::kFaviconSize; +#endif +} + +void OnGotFaviconsForPageUrl( + int desired_size_in_dip, + base::OnceCallback<void(gfx::Image)> callback, + std::vector<favicon_base::FaviconRawBitmapResult> results) { + favicon_base::FaviconImageResult image_result; + image_result.image = favicon_base::SelectFaviconFramesFromPNGs( + results, favicon_base::GetFaviconScales(), desired_size_in_dip); + favicon_base::SetFaviconColorSpace(&image_result.image); + std::move(callback).Run(image_result.image); +} + +} // namespace + +FaviconServiceImpl::FaviconServiceImpl() = default; + +FaviconServiceImpl::~FaviconServiceImpl() { + backend_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&FaviconBackendWrapper::Shutdown, std::move(backend_))); +} + +void FaviconServiceImpl::Init(const base::FilePath& db_path) { + if (!backend_task_runner_) { + // BLOCK_SHUTDOWN matches that of HistoryService. It's done in hopes of + // preventing database corruption. + backend_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner( + {base::MayBlock(), base::WithBaseSyncPrimitives(), + base::TaskPriority::USER_BLOCKING, + base::TaskShutdownBehavior::BLOCK_SHUTDOWN}); + } + + backend_ = base::MakeRefCounted<FaviconBackendWrapper>(backend_task_runner_); + + backend_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&FaviconBackendWrapper::Init, backend_, db_path)); +} + +void FaviconServiceImpl::DeleteAndRecreateDatabase(base::OnceClosure callback) { + backend_task_runner_->PostTaskAndReply( + FROM_HERE, + base::BindOnce(&FaviconBackendWrapper::DeleteAndRecreateDatabase, + backend_), + std::move(callback)); +} + +base::CancelableTaskTracker::TaskId FaviconServiceImpl::GetFaviconForPageUrl( + const GURL& page_url, + base::OnceCallback<void(gfx::Image)> callback, + base::CancelableTaskTracker* tracker) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // The arguments supplied to this function should return an image matching + // that returned by FaviconFetcher. + return tracker->PostTaskAndReplyWithResult( + backend_task_runner_.get(), FROM_HERE, + base::BindOnce(&FaviconBackendWrapper::GetFaviconsForUrl, backend_, + page_url, GetIconTypeSet(), + GetDesiredFaviconSizesInPixels()), + base::BindOnce(&OnGotFaviconsForPageUrl, GetDesiredFaviconSizeInDips(), + std::move(callback))); +} + +base::CancelableTaskTracker::TaskId FaviconServiceImpl::GetFaviconForPageURL( + const GURL& page_url, + const favicon_base::IconTypeSet& icon_types, + int desired_size_in_dip, + favicon_base::FaviconResultsCallback callback, + base::CancelableTaskTracker* tracker) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return tracker->PostTaskAndReplyWithResult( + backend_task_runner_.get(), FROM_HERE, + base::BindOnce(&FaviconBackendWrapper::GetFaviconsForUrl, backend_, + page_url, icon_types, + GetPixelSizesForFaviconScales(desired_size_in_dip)), + std::move(callback)); +} + +void FaviconServiceImpl::SetFaviconOutOfDateForPage(const GURL& page_url) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + backend_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&FaviconBackendWrapper::SetFaviconsOutOfDateForPage, + backend_, page_url)); +} + +void FaviconServiceImpl::SetFavicons(const base::flat_set<GURL>& page_urls, + const GURL& icon_url, + favicon_base::IconType icon_type, + const gfx::Image& image) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + base::flat_set<GURL> page_urls_to_save; + page_urls_to_save.reserve(page_urls.capacity()); + for (const GURL& page_url : page_urls) { + if (CanAddUrl(page_url)) + page_urls_to_save.insert(page_url); + } + + if (page_urls_to_save.empty()) + return; + + backend_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&FaviconBackendWrapper::SetFavicons, backend_, + page_urls_to_save, icon_type, icon_url, + ExtractSkBitmapsToStore(image))); +} + +void FaviconServiceImpl::CloneFaviconMappingsForPages( + const GURL& page_url_to_read, + const favicon_base::IconTypeSet& icon_types, + const base::flat_set<GURL>& page_urls_to_write) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + backend_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&FaviconBackendWrapper::CloneFaviconMappingsForPages, + backend_, page_url_to_read, icon_types, + page_urls_to_write)); +} + +base::CancelableTaskTracker::TaskId FaviconServiceImpl::GetFavicon( + const GURL& icon_url, + favicon_base::IconType icon_type, + int desired_size_in_dip, + favicon_base::FaviconResultsCallback callback, + base::CancelableTaskTracker* tracker) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return tracker->PostTaskAndReplyWithResult( + backend_task_runner_.get(), FROM_HERE, + base::BindOnce(&FaviconBackendWrapper::GetFavicon, backend_, icon_url, + icon_type, + GetPixelSizesForFaviconScales(desired_size_in_dip)), + std::move(callback)); +} + +base::CancelableTaskTracker::TaskId +FaviconServiceImpl::UpdateFaviconMappingsAndFetch( + const base::flat_set<GURL>& page_urls, + const GURL& icon_url, + favicon_base::IconType icon_type, + int desired_size_in_dip, + favicon_base::FaviconResultsCallback callback, + base::CancelableTaskTracker* tracker) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return tracker->PostTaskAndReplyWithResult( + backend_task_runner_.get(), FROM_HERE, + base::BindOnce(&FaviconBackendWrapper::UpdateFaviconMappingsAndFetch, + backend_, page_urls, icon_url, icon_type, + GetPixelSizesForFaviconScales(desired_size_in_dip)), + std::move(callback)); +} + +void FaviconServiceImpl::DeleteFaviconMappings( + const base::flat_set<GURL>& page_urls, + favicon_base::IconType icon_type) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + backend_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&FaviconBackendWrapper::DeleteFaviconMappings, + backend_, page_urls, icon_type)); +} + +void FaviconServiceImpl::UnableToDownloadFavicon(const GURL& icon_url) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + missing_favicon_urls_.insert(base::FastHash(icon_url.spec())); + if (observer_) + observer_->OnUnableToDownloadFavicon(); +} + +void FaviconServiceImpl::ClearUnableToDownloadFavicons() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + missing_favicon_urls_.clear(); +} + +bool FaviconServiceImpl::WasUnableToDownloadFavicon( + const GURL& icon_url) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + MissingFaviconUrlHash url_hash = base::FastHash(icon_url.spec()); + return missing_favicon_urls_.find(url_hash) != missing_favicon_urls_.end(); +} + +// static +std::vector<int> FaviconServiceImpl::GetDesiredFaviconSizesInPixels() { + return GetPixelSizesForFaviconScales(GetDesiredFaviconSizeInDips()); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/favicon/favicon_service_impl.h b/chromium/weblayer/browser/favicon/favicon_service_impl.h new file mode 100644 index 00000000000..4d1a71db34d --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_service_impl.h @@ -0,0 +1,105 @@ +// 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 WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_H_ +#define WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_H_ + +#include <unordered_set> + +#include "base/memory/ref_counted.h" +#include "base/sequence_checker.h" +#include "components/favicon/core/core_favicon_service.h" + +namespace base { +class FilePath; +} + +namespace weblayer { + +class FaviconBackendWrapper; +class FaviconServiceImplObserver; + +// FaviconServiceImpl provides the front end (ui side) access to the favicon +// database. Most functions are processed async on the backend task-runner. +class FaviconServiceImpl : public favicon::CoreFaviconService { + public: + FaviconServiceImpl(); + FaviconServiceImpl(const FaviconServiceImpl&) = delete; + FaviconServiceImpl& operator=(const FaviconServiceImpl&) = delete; + ~FaviconServiceImpl() override; + + void Init(const base::FilePath& db_path); + + void set_observer(FaviconServiceImplObserver* observer) { + observer_ = observer; + } + + // Deletes the database and recreates it, notifying |callback| when done. + void DeleteAndRecreateDatabase(base::OnceClosure callback); + + // Requests the favicon image for a url (page). The returned image matches + // that returned from FaviconFetcher. + base::CancelableTaskTracker::TaskId GetFaviconForPageUrl( + const GURL& page_url, + base::OnceCallback<void(gfx::Image)> callback, + base::CancelableTaskTracker* tracker); + + // favicon::CoreFaviconService: + base::CancelableTaskTracker::TaskId GetFaviconForPageURL( + const GURL& page_url, + const favicon_base::IconTypeSet& icon_types, + int desired_size_in_dip, + favicon_base::FaviconResultsCallback callback, + base::CancelableTaskTracker* tracker) override; + void SetFaviconOutOfDateForPage(const GURL& page_url) override; + void SetFavicons(const base::flat_set<GURL>& page_urls, + const GURL& icon_url, + favicon_base::IconType icon_type, + const gfx::Image& image) override; + void CloneFaviconMappingsForPages( + const GURL& page_url_to_read, + const favicon_base::IconTypeSet& icon_types, + const base::flat_set<GURL>& page_urls_to_write) override; + base::CancelableTaskTracker::TaskId GetFavicon( + const GURL& icon_url, + favicon_base::IconType icon_type, + int desired_size_in_dip, + favicon_base::FaviconResultsCallback callback, + base::CancelableTaskTracker* tracker) override; + base::CancelableTaskTracker::TaskId UpdateFaviconMappingsAndFetch( + const base::flat_set<GURL>& page_urls, + const GURL& icon_url, + favicon_base::IconType icon_type, + int desired_size_in_dip, + favicon_base::FaviconResultsCallback callback, + base::CancelableTaskTracker* tracker) override; + void DeleteFaviconMappings(const base::flat_set<GURL>& page_urls, + favicon_base::IconType icon_type) override; + void UnableToDownloadFavicon(const GURL& icon_url) override; + void ClearUnableToDownloadFavicons() override; + bool WasUnableToDownloadFavicon(const GURL& icon_url) const override; + + private: + using MissingFaviconUrlHash = size_t; + SEQUENCE_CHECKER(sequence_checker_); + + // Returns the desired favicon sizes for the current platform. + static std::vector<int> GetDesiredFaviconSizesInPixels(); + + // The TaskRunner to which FaviconServiceBackend tasks are posted. Nullptr + // once Cleanup() is called. + scoped_refptr<base::SequencedTaskRunner> backend_task_runner_; + + scoped_refptr<FaviconBackendWrapper> backend_; + + // Hashes of the favicon urls that were unable to be downloaded. + std::unordered_set<MissingFaviconUrlHash> missing_favicon_urls_; + + // This is only used in tests, where only a single observer is necessary. + FaviconServiceImplObserver* observer_ = nullptr; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_H_ diff --git a/chromium/weblayer/browser/favicon/favicon_service_impl_factory.cc b/chromium/weblayer/browser/favicon/favicon_service_impl_factory.cc new file mode 100644 index 00000000000..f75045536f2 --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_service_impl_factory.cc @@ -0,0 +1,50 @@ +// 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 "weblayer/browser/favicon/favicon_service_impl_factory.h" + +#include "base/files/file_path.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" +#include "weblayer/browser/favicon/favicon_service_impl.h" + +namespace weblayer { + +// static +FaviconServiceImpl* FaviconServiceImplFactory::GetForBrowserContext( + content::BrowserContext* browser_context) { + if (!browser_context->IsOffTheRecord()) { + return static_cast<FaviconServiceImpl*>( + GetInstance()->GetServiceForBrowserContext(browser_context, true)); + } + return nullptr; +} + +// static +FaviconServiceImplFactory* FaviconServiceImplFactory::GetInstance() { + static base::NoDestructor<FaviconServiceImplFactory> factory; + return factory.get(); +} + +FaviconServiceImplFactory::FaviconServiceImplFactory() + : BrowserContextKeyedServiceFactory( + "FaviconServiceImpl", + BrowserContextDependencyManager::GetInstance()) {} + +FaviconServiceImplFactory::~FaviconServiceImplFactory() = default; + +KeyedService* FaviconServiceImplFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + DCHECK(!context->IsOffTheRecord()); + std::unique_ptr<FaviconServiceImpl> service = + std::make_unique<FaviconServiceImpl>(); + service->Init(context->GetPath().AppendASCII("Favicons")); + return service.release(); +} + +bool FaviconServiceImplFactory::ServiceIsNULLWhileTesting() const { + return true; +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/favicon/favicon_service_impl_factory.h b/chromium/weblayer/browser/favicon/favicon_service_impl_factory.h new file mode 100644 index 00000000000..f852eb6389d --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_service_impl_factory.h @@ -0,0 +1,47 @@ +// 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 WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_FACTORY_H_ +#define WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace content { +class BrowserContext; +} + +namespace weblayer { + +class FaviconServiceImpl; + +// BrowserContextKeyedServiceFactory for getting the FaviconServiceImpl. +class FaviconServiceImplFactory : public BrowserContextKeyedServiceFactory { + public: + FaviconServiceImplFactory(const FaviconServiceImplFactory&) = delete; + FaviconServiceImplFactory& operator=(const FaviconServiceImplFactory&) = + delete; + + // Off the record profiles do not have a FaviconServiceImpl. + static FaviconServiceImpl* GetForBrowserContext( + content::BrowserContext* browser_context); + + // Returns the FaviconServiceFactory singleton. + static FaviconServiceImplFactory* GetInstance(); + + private: + friend class base::NoDestructor<FaviconServiceImplFactory>; + + FaviconServiceImplFactory(); + ~FaviconServiceImplFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + bool ServiceIsNULLWhileTesting() const override; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_FACTORY_H_ diff --git a/chromium/weblayer/browser/favicon/favicon_service_impl_observer.h b/chromium/weblayer/browser/favicon/favicon_service_impl_observer.h new file mode 100644 index 00000000000..ea647045074 --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_service_impl_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 WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_OBSERVER_H_ +#define WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_OBSERVER_H_ + +namespace weblayer { + +class FaviconServiceImplObserver { + public: + // Called from FaviconServiceImpl::UnableToDownloadFavicon. + virtual void OnUnableToDownloadFavicon() {} + + protected: + virtual ~FaviconServiceImplObserver() = default; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_FAVICON_FAVICON_SERVICE_IMPL_OBSERVER_H_ diff --git a/chromium/weblayer/browser/favicon/favicon_tab_helper.cc b/chromium/weblayer/browser/favicon/favicon_tab_helper.cc new file mode 100644 index 00000000000..4abff65e7d3 --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_tab_helper.cc @@ -0,0 +1,124 @@ +// 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 "weblayer/browser/favicon/favicon_tab_helper.h" + +#include "components/favicon/content/content_favicon_driver.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/web_contents.h" +#include "weblayer/browser/favicon/favicon_service_impl.h" +#include "weblayer/browser/favicon/favicon_service_impl_factory.h" +#include "weblayer/public/favicon_fetcher_delegate.h" + +namespace weblayer { +namespace { + +bool IsSquareImage(const gfx::Image& image) { + return !image.IsEmpty() && image.Width() == image.Height(); +} + +// Returns true if |image_a| is better than |image_b|. A value of false means +// |image_a| is not better than |image_b|. Either image may be empty, if both +// are empty false is returned. +bool IsImageBetterThan(const gfx::Image& image_a, const gfx::Image& image_b) { + // Any image is better than an empty image. + if (!image_a.IsEmpty() && image_b.IsEmpty()) + return true; + + // Prefer square favicons as they will scale much better. + if (IsSquareImage(image_a) && !IsSquareImage(image_b)) + return true; + + return image_a.Width() > image_b.Width(); +} + +} // namespace + +FaviconTabHelper::ObserverSubscription::ObserverSubscription( + FaviconTabHelper* helper, + FaviconFetcherDelegate* delegate) + : helper_(helper), delegate_(delegate) { + helper_->AddDelegate(delegate_); +} + +FaviconTabHelper::ObserverSubscription::~ObserverSubscription() { + helper_->RemoveDelegate(delegate_); +} + +FaviconTabHelper::~FaviconTabHelper() { + // All of the ObserverSubscriptions should have been destroyed before this. + DCHECK_EQ(0, observer_count_); +} + +std::unique_ptr<FaviconTabHelper::ObserverSubscription> +FaviconTabHelper::RegisterFaviconFetcherDelegate( + FaviconFetcherDelegate* delegate) { + // WrapUnique as constructor is private. + return base::WrapUnique(new ObserverSubscription(this, delegate)); +} + +FaviconTabHelper::FaviconTabHelper(content::WebContents* contents) + : WebContentsObserver(contents) { +} + +void FaviconTabHelper::AddDelegate(FaviconFetcherDelegate* delegate) { + delegates_.AddObserver(delegate); + if (++observer_count_ == 1) { + FaviconServiceImpl* favicon_service = + FaviconServiceImplFactory::GetForBrowserContext( + web_contents()->GetBrowserContext()); + favicon::ContentFaviconDriver::CreateForWebContents(web_contents(), + favicon_service); + favicon::ContentFaviconDriver::FromWebContents(web_contents()) + ->AddObserver(this); + } +} + +void FaviconTabHelper::RemoveDelegate(FaviconFetcherDelegate* delegate) { + delegates_.RemoveObserver(delegate); + --observer_count_; + DCHECK_GE(observer_count_, 0); + if (observer_count_ == 0) { + favicon::ContentFaviconDriver::FromWebContents(web_contents()) + ->RemoveObserver(this); + // ContentFaviconDriver downloads images, if there are no observers there + // is no need to keep it around. This triggers deleting it. + web_contents()->SetUserData(favicon::ContentFaviconDriver::UserDataKey(), + nullptr); + favicon_ = gfx::Image(); + } +} + +void FaviconTabHelper::OnFaviconUpdated( + favicon::FaviconDriver* favicon_driver, + NotificationIconType notification_icon_type, + const GURL& icon_url, + bool icon_url_changed, + const gfx::Image& image) { + if (!IsImageBetterThan(image, favicon_)) + return; + + favicon_ = image; + for (FaviconFetcherDelegate& delegate : delegates_) + delegate.OnFaviconChanged(favicon_); +} + +void FaviconTabHelper::DidFinishNavigation( + content::NavigationHandle* navigation_handle) { + if (!navigation_handle->IsInMainFrame() || + !navigation_handle->HasCommitted() || navigation_handle->IsErrorPage() || + navigation_handle->IsSameDocument()) { + return; + } + if (favicon_.IsEmpty()) + return; + + favicon_ = gfx::Image(); + for (FaviconFetcherDelegate& delegate : delegates_) + delegate.OnFaviconChanged(favicon_); +} + +WEB_CONTENTS_USER_DATA_KEY_IMPL(FaviconTabHelper) + +} // namespace weblayer diff --git a/chromium/weblayer/browser/favicon/favicon_tab_helper.h b/chromium/weblayer/browser/favicon/favicon_tab_helper.h new file mode 100644 index 00000000000..5558ab16665 --- /dev/null +++ b/chromium/weblayer/browser/favicon/favicon_tab_helper.h @@ -0,0 +1,87 @@ +// 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 WEBLAYER_BROWSER_FAVICON_FAVICON_TAB_HELPER_H_ +#define WEBLAYER_BROWSER_FAVICON_FAVICON_TAB_HELPER_H_ + +#include <memory> + +#include "base/observer_list.h" +#include "components/favicon/core/favicon_driver_observer.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" +#include "ui/gfx/image/image.h" + +namespace weblayer { + +class FaviconFetcherDelegate; + +// FaviconTabHelper is responsible for creating favicon::ContentFaviconDriver +// when necessary. FaviconTabHelper is used by FaviconFetcherImpl and notifies +// FaviconFetcherDelegate when the favicon changes. +class FaviconTabHelper : public content::WebContentsUserData<FaviconTabHelper>, + public content::WebContentsObserver, + public favicon::FaviconDriverObserver { + public: + // Used to track calls to RegisterFaviconFetcherDelegate(). When destroyed + // the FaviconFetcherDelegate is removed. + class ObserverSubscription { + public: + ObserverSubscription(const ObserverSubscription&) = delete; + ObserverSubscription& operator=(const ObserverSubscription&) = delete; + ~ObserverSubscription(); + + private: + friend class FaviconTabHelper; + + ObserverSubscription(FaviconTabHelper* helper, + FaviconFetcherDelegate* delegate); + + FaviconTabHelper* helper_; + FaviconFetcherDelegate* delegate_; + }; + + FaviconTabHelper(const FaviconTabHelper&) = delete; + FaviconTabHelper& operator=(const FaviconTabHelper&) = delete; + ~FaviconTabHelper() override; + + // Called when FaviconFetcherImpl is created. This ensures the necessary + // wiring is in place and notifies |delegate| when the favicon changes. + std::unique_ptr<ObserverSubscription> RegisterFaviconFetcherDelegate( + FaviconFetcherDelegate* delegate); + + // Returns the favicon for the current navigation. + const gfx::Image& favicon() const { return favicon_; } + + private: + friend class content::WebContentsUserData<FaviconTabHelper>; + + explicit FaviconTabHelper(content::WebContents* contents); + + void AddDelegate(FaviconFetcherDelegate* delegate); + void RemoveDelegate(FaviconFetcherDelegate* delegate); + + // favicon::FaviconDriverObserver: + void OnFaviconUpdated(favicon::FaviconDriver* favicon_driver, + NotificationIconType notification_icon_type, + const GURL& icon_url, + bool icon_url_changed, + const gfx::Image& image) override; + + // content::WebContentsObserver: + void DidFinishNavigation( + content::NavigationHandle* navigation_handle) override; + + content::WebContents* web_contents_; + // Number of observers attached. + int observer_count_ = 0; + base::ObserverList<FaviconFetcherDelegate> delegates_; + gfx::Image favicon_; + + WEB_CONTENTS_USER_DATA_KEY_DECL(); +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_FAVICON_FAVICON_TAB_HELPER_H_ diff --git a/chromium/weblayer/browser/favicon/test_favicon_fetcher_delegate.cc b/chromium/weblayer/browser/favicon/test_favicon_fetcher_delegate.cc new file mode 100644 index 00000000000..7c79fe1214a --- /dev/null +++ b/chromium/weblayer/browser/favicon/test_favicon_fetcher_delegate.cc @@ -0,0 +1,46 @@ +// 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 "weblayer/browser/favicon/test_favicon_fetcher_delegate.h" + +#include "base/run_loop.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace weblayer { + +TestFaviconFetcherDelegate::TestFaviconFetcherDelegate() = default; + +TestFaviconFetcherDelegate::~TestFaviconFetcherDelegate() = default; + +void TestFaviconFetcherDelegate::WaitForFavicon() { + ASSERT_EQ(nullptr, run_loop_.get()); + waiting_for_nonempty_image_ = false; + run_loop_ = std::make_unique<base::RunLoop>(); + run_loop_->Run(); + run_loop_.reset(); +} + +void TestFaviconFetcherDelegate::WaitForNonemptyFavicon() { + if (!last_image_.IsEmpty()) + return; + + run_loop_ = std::make_unique<base::RunLoop>(); + waiting_for_nonempty_image_ = true; + run_loop_->Run(); + run_loop_.reset(); +} + +void TestFaviconFetcherDelegate::ClearLastImage() { + last_image_ = gfx::Image(); + on_favicon_changed_call_count_ = 0; +} + +void TestFaviconFetcherDelegate::OnFaviconChanged(const gfx::Image& image) { + last_image_ = image; + ++on_favicon_changed_call_count_; + if (run_loop_ && (!waiting_for_nonempty_image_ || !image.IsEmpty())) + run_loop_->Quit(); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/favicon/test_favicon_fetcher_delegate.h b/chromium/weblayer/browser/favicon/test_favicon_fetcher_delegate.h new file mode 100644 index 00000000000..25300a85ebc --- /dev/null +++ b/chromium/weblayer/browser/favicon/test_favicon_fetcher_delegate.h @@ -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. + +#ifndef WEBLAYER_BROWSER_FAVICON_TEST_FAVICON_FETCHER_DELEGATE_H_ +#define WEBLAYER_BROWSER_FAVICON_TEST_FAVICON_FETCHER_DELEGATE_H_ + +#include <memory> + +#include "ui/gfx/image/image.h" +#include "weblayer/public/favicon_fetcher_delegate.h" + +namespace base { +class RunLoop; +} + +namespace weblayer { + +// Records calls to OnFaviconChanged(). +class TestFaviconFetcherDelegate : public FaviconFetcherDelegate { + public: + TestFaviconFetcherDelegate(); + TestFaviconFetcherDelegate(const TestFaviconFetcherDelegate&) = delete; + TestFaviconFetcherDelegate& operator=(const TestFaviconFetcherDelegate&) = + delete; + ~TestFaviconFetcherDelegate() override; + + // Waits for OnFaviconChanged() to be called. + void WaitForFavicon(); + + // Waits for a non-empty favicon. This returns immediately if a non-empty + // image was supplied to OnFaviconChanged() and ClearLastImage() hasn't been + // called. + void WaitForNonemptyFavicon(); + + void ClearLastImage(); + + const gfx::Image& last_image() const { return last_image_; } + int on_favicon_changed_call_count() const { + return on_favicon_changed_call_count_; + } + + // FaviconFetcherDelegate: + void OnFaviconChanged(const gfx::Image& image) override; + + private: + std::unique_ptr<base::RunLoop> run_loop_; + gfx::Image last_image_; + bool waiting_for_nonempty_image_ = false; + int on_favicon_changed_call_count_ = 0; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_FAVICON_TEST_FAVICON_FETCHER_DELEGATE_H_ diff --git a/chromium/weblayer/browser/file_select_helper.cc b/chromium/weblayer/browser/file_select_helper.cc index 7cc0a81e87d..2e462571ab1 100644 --- a/chromium/weblayer/browser/file_select_helper.cc +++ b/chromium/weblayer/browser/file_select_helper.cc @@ -27,7 +27,7 @@ using blink::mojom::FileChooserParamsPtr; // static void FileSelectHelper::RunFileChooser( content::RenderFrameHost* render_frame_host, - std::unique_ptr<content::FileSelectListener> listener, + scoped_refptr<content::FileSelectListener> listener, const FileChooserParams& params) { // TODO: Should we handle text/json+contacts accept type? @@ -49,7 +49,7 @@ FileSelectHelper::~FileSelectHelper() { void FileSelectHelper::RunFileChooser( content::RenderFrameHost* render_frame_host, - std::unique_ptr<content::FileSelectListener> listener, + scoped_refptr<content::FileSelectListener> listener, FileChooserParamsPtr params) { DCHECK(!web_contents()); DCHECK(listener); diff --git a/chromium/weblayer/browser/file_select_helper.h b/chromium/weblayer/browser/file_select_helper.h index a99977429f6..5619e72a68a 100644 --- a/chromium/weblayer/browser/file_select_helper.h +++ b/chromium/weblayer/browser/file_select_helper.h @@ -40,7 +40,7 @@ class FileSelectHelper : public base::RefCountedThreadSafe< // Show the file chooser dialog. static void RunFileChooser( content::RenderFrameHost* render_frame_host, - std::unique_ptr<content::FileSelectListener> listener, + scoped_refptr<content::FileSelectListener> listener, const blink::mojom::FileChooserParams& params); private: @@ -53,7 +53,7 @@ class FileSelectHelper : public base::RefCountedThreadSafe< ~FileSelectHelper() override; void RunFileChooser(content::RenderFrameHost* render_frame_host, - std::unique_ptr<content::FileSelectListener> listener, + scoped_refptr<content::FileSelectListener> listener, blink::mojom::FileChooserParamsPtr params); // Cleans up and releases this instance. This must be called after the last @@ -85,7 +85,7 @@ class FileSelectHelper : public base::RefCountedThreadSafe< bool AbortIfWebContentsDestroyed(); // |listener_| receives the result of the FileSelectHelper. - std::unique_ptr<content::FileSelectListener> listener_; + scoped_refptr<content::FileSelectListener> listener_; // Dialog box used for choosing files to upload from file form fields. scoped_refptr<ui::SelectFileDialog> select_file_dialog_; diff --git a/chromium/weblayer/browser/google_accounts_browsertest.cc b/chromium/weblayer/browser/google_accounts_browsertest.cc new file mode 100644 index 00000000000..43d25ab4032 --- /dev/null +++ b/chromium/weblayer/browser/google_accounts_browsertest.cc @@ -0,0 +1,229 @@ +// 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 "base/strings/strcat.h" +#include "components/signin/core/browser/signin_header_helper.h" +#include "google_apis/gaia/gaia_switches.h" +#include "google_apis/gaia/gaia_urls.h" +#include "net/base/escape.h" +#include "net/base/url_util.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/default_handlers.h" +#include "net/test/embedded_test_server/http_request.h" +#include "net/test/embedded_test_server/http_response.h" +#include "weblayer/browser/browser_impl.h" +#include "weblayer/browser/profile_impl.h" +#include "weblayer/browser/signin_url_loader_throttle.h" +#include "weblayer/browser/tab_impl.h" +#include "weblayer/public/google_accounts_delegate.h" +#include "weblayer/public/navigation_controller.h" +#include "weblayer/shell/browser/shell.h" +#include "weblayer/test/test_navigation_observer.h" +#include "weblayer/test/weblayer_browser_test.h" +#include "weblayer/test/weblayer_browser_test_utils.h" + +namespace weblayer { +namespace { +constexpr char kGaiaDomain[] = "fakegaia.com"; +constexpr char kGoogleAccountsPath[] = "/manageaccounts"; +constexpr char kGoogleAccountsRedirectPath[] = "/manageaccounts-redirect"; +} // namespace + +class GoogleAccountsBrowserTest : public WebLayerBrowserTest, + public GoogleAccountsDelegate { + public: + // WebLayerBrowserTest: + void SetUpOnMainThread() override { + host_resolver()->AddRule("*", "127.0.0.1"); + shell()->tab()->SetGoogleAccountsDelegate(this); + } + + void SetUpCommandLine(base::CommandLine* command_line) override { + https_server_.RegisterRequestHandler(base::BindRepeating( + &GoogleAccountsBrowserTest::HandleGoogleAccountsRequest, + base::Unretained(this))); + net::test_server::RegisterDefaultHandlers(&https_server_); + ASSERT_TRUE(https_server_.Start()); + command_line->AppendSwitchASCII(switches::kGaiaUrl, GetGaiaURL("/").spec()); + command_line->AppendSwitch("ignore-certificate-errors"); + } + + // GoogleAccountsDelegate: + void OnGoogleAccountsRequest( + const signin::ManageAccountsParams& params) override { + params_ = params; + if (run_loop_) + run_loop_->Quit(); + } + + std::string GetGaiaId() override { return ""; } + + const signin::ManageAccountsParams& WaitForGoogleAccounts() { + if (!params_) { + run_loop_ = std::make_unique<base::RunLoop>(); + run_loop_->Run(); + } + EXPECT_TRUE(params_.has_value()); + return params_.value(); + } + + GURL GetGaiaURL(const std::string& path) { + return https_server_.GetURL(kGaiaDomain, path); + } + + GURL GetNonGaiaURL(const std::string& path) { + return https_server_.GetURL(path); + } + + bool HasReceivedGoogleAccountsResponse() { return params_.has_value(); } + + std::string GetBody() { + return ExecuteScript(shell(), "document.body.innerText", true).GetString(); + } + + protected: + std::string response_header_; + + private: + std::unique_ptr<net::test_server::HttpResponse> HandleGoogleAccountsRequest( + const net::test_server::HttpRequest& request) { + std::string path = request.GetURL().path(); + if (path != kSignOutPath && path != kGoogleAccountsPath && + path != kGoogleAccountsRedirectPath) { + return nullptr; + } + + auto http_response = + std::make_unique<net::test_server::BasicHttpResponse>(); + if (path == kGoogleAccountsRedirectPath) { + http_response->set_code(net::HTTP_MOVED_PERMANENTLY); + http_response->AddCustomHeader( + "Location", + net::UnescapeBinaryURLComponent(request.GetURL().query_piece())); + } else { + http_response->set_code(net::HTTP_OK); + } + + if (base::Contains(request.headers, signin::kChromeConnectedHeader)) { + http_response->AddCustomHeader(signin::kChromeManageAccountsHeader, + response_header_); + } + http_response->set_content(""); + return http_response; + } + + net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS}; + base::Optional<signin::ManageAccountsParams> params_; + std::unique_ptr<base::RunLoop> run_loop_; +}; + +IN_PROC_BROWSER_TEST_F(GoogleAccountsBrowserTest, Basic) { + response_header_ = + "action=ADDSESSION,email=foo@bar.com," + "continue_url=https://blah.com,is_same_tab=true"; + NavigateAndWaitForCompletion(GetGaiaURL(kGoogleAccountsPath), shell()); + const signin::ManageAccountsParams& params = WaitForGoogleAccounts(); + EXPECT_EQ(params.service_type, signin::GAIA_SERVICE_TYPE_ADDSESSION); + EXPECT_EQ(params.email, "foo@bar.com"); + EXPECT_EQ(params.continue_url, "https://blah.com"); + EXPECT_TRUE(params.is_same_tab); +} + +IN_PROC_BROWSER_TEST_F(GoogleAccountsBrowserTest, NonGaiaUrl) { + response_header_ = "action=ADDSESSION"; + NavigateAndWaitForCompletion(GetNonGaiaURL(kGoogleAccountsPath), shell()); + + // Navigate again to make sure the manage accounts response would have been + // received. + NavigateAndWaitForCompletion(GetNonGaiaURL("/echo"), shell()); + EXPECT_FALSE(HasReceivedGoogleAccountsResponse()); +} + +IN_PROC_BROWSER_TEST_F(GoogleAccountsBrowserTest, RedirectFromGaiaURL) { + response_header_ = "action=SIGNUP"; + + GURL final_url = GetGaiaURL(kGoogleAccountsPath); + TestNavigationObserver test_observer( + final_url, TestNavigationObserver::NavigationEvent::kCompletion, + shell()->tab()); + shell()->tab()->GetNavigationController()->Navigate(GetNonGaiaURL( + base::StrCat({kGoogleAccountsRedirectPath, "?", final_url.spec()}))); + test_observer.Wait(); + + const signin::ManageAccountsParams& params = WaitForGoogleAccounts(); + EXPECT_EQ(params.service_type, signin::GAIA_SERVICE_TYPE_SIGNUP); +} + +IN_PROC_BROWSER_TEST_F(GoogleAccountsBrowserTest, RedirectToGaiaURL) { + response_header_ = "action=SIGNUP"; + + GURL final_url = GetNonGaiaURL(kGoogleAccountsPath); + TestNavigationObserver test_observer( + final_url, TestNavigationObserver::NavigationEvent::kCompletion, + shell()->tab()); + shell()->tab()->GetNavigationController()->Navigate(GetGaiaURL( + base::StrCat({kGoogleAccountsRedirectPath, "?", final_url.spec()}))); + test_observer.Wait(); + + const signin::ManageAccountsParams& params = WaitForGoogleAccounts(); + EXPECT_EQ(params.service_type, signin::GAIA_SERVICE_TYPE_SIGNUP); +} + +IN_PROC_BROWSER_TEST_F(GoogleAccountsBrowserTest, AddsQueryParamsToSignoutURL) { + response_header_ = "action=SIGNUP"; + + // Sign out URL on the GAIA domain will get a query param added to it. + GURL sign_out_url = GetGaiaURL(kSignOutPath); + GURL sign_out_url_with_params = + net::AppendQueryParameter(sign_out_url, "manage", "true"); + { + TestNavigationObserver test_observer( + sign_out_url_with_params, + TestNavigationObserver::NavigationEvent::kCompletion, shell()->tab()); + shell()->tab()->GetNavigationController()->Navigate(sign_out_url); + test_observer.Wait(); + } + + // Other URLs will not have query param added. + NavigateAndWaitForCompletion(GetGaiaURL(kGoogleAccountsPath), shell()); + NavigateAndWaitForCompletion(GetNonGaiaURL(kSignOutPath), shell()); + + // Redirecting to sign out URL will also add params. + { + TestNavigationObserver test_observer( + sign_out_url_with_params, + TestNavigationObserver::NavigationEvent::kCompletion, shell()->tab()); + shell()->tab()->GetNavigationController()->Navigate( + GetNonGaiaURL("/server-redirect?" + sign_out_url.spec())); + test_observer.Wait(); + } +} + +IN_PROC_BROWSER_TEST_F(GoogleAccountsBrowserTest, AddsRequestHeaderToGaiaURLs) { + const std::string path = + base::StrCat({"/echoheader?", signin::kChromeConnectedHeader}); + NavigateAndWaitForCompletion(GetGaiaURL(path), shell()); + EXPECT_EQ(GetBody(), + "source=WebLayer,mode=3,enable_account_consistency=true," + "consistency_enabled_by_default=false"); + + // Non GAIA URL should not get the header. + NavigateAndWaitForCompletion(GetNonGaiaURL(path), shell()); + EXPECT_EQ(GetBody(), "None"); +} + +class IncognitoGoogleAccountsBrowserTest : public GoogleAccountsBrowserTest { + public: + IncognitoGoogleAccountsBrowserTest() { SetShellStartsInIncognitoMode(); } +}; + +IN_PROC_BROWSER_TEST_F(IncognitoGoogleAccountsBrowserTest, + HeaderNotAddedForIncognitoBrowser) { + const std::string path = + base::StrCat({"/echoheader?", signin::kChromeConnectedHeader}); + NavigateAndWaitForCompletion(GetGaiaURL(path), shell()); + EXPECT_EQ(GetBody(), "None"); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/google_accounts_callback_proxy.cc b/chromium/weblayer/browser/google_accounts_callback_proxy.cc new file mode 100644 index 00000000000..1464b904327 --- /dev/null +++ b/chromium/weblayer/browser/google_accounts_callback_proxy.cc @@ -0,0 +1,57 @@ +// 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 "weblayer/browser/google_accounts_callback_proxy.h" + +#include "base/android/jni_string.h" +#include "components/signin/core/browser/signin_header_helper.h" +#include "weblayer/browser/java/jni/GoogleAccountsCallbackProxy_jni.h" +#include "weblayer/public/tab.h" + +using base::android::AttachCurrentThread; +using base::android::ConvertUTF8ToJavaString; + +namespace weblayer { + +GoogleAccountsCallbackProxy::GoogleAccountsCallbackProxy(JNIEnv* env, + jobject obj, + Tab* tab) + : tab_(tab), java_impl_(env, obj) { + tab_->SetGoogleAccountsDelegate(this); +} + +GoogleAccountsCallbackProxy::~GoogleAccountsCallbackProxy() { + tab_->SetGoogleAccountsDelegate(nullptr); +} + +void GoogleAccountsCallbackProxy::OnGoogleAccountsRequest( + const signin::ManageAccountsParams& params) { + JNIEnv* env = AttachCurrentThread(); + Java_GoogleAccountsCallbackProxy_onGoogleAccountsRequest( + env, java_impl_, params.service_type, + ConvertUTF8ToJavaString(env, params.email), + ConvertUTF8ToJavaString(env, params.continue_url), params.is_same_tab); +} + +std::string GoogleAccountsCallbackProxy::GetGaiaId() { + return base::android::ConvertJavaStringToUTF8( + Java_GoogleAccountsCallbackProxy_getGaiaId(AttachCurrentThread(), + java_impl_)); +} + +static jlong JNI_GoogleAccountsCallbackProxy_CreateGoogleAccountsCallbackProxy( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& proxy, + jlong tab) { + return reinterpret_cast<jlong>( + new GoogleAccountsCallbackProxy(env, proxy, reinterpret_cast<Tab*>(tab))); +} + +static void JNI_GoogleAccountsCallbackProxy_DeleteGoogleAccountsCallbackProxy( + JNIEnv* env, + jlong proxy) { + delete reinterpret_cast<GoogleAccountsCallbackProxy*>(proxy); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/google_accounts_callback_proxy.h b/chromium/weblayer/browser/google_accounts_callback_proxy.h new file mode 100644 index 00000000000..577fb305a4c --- /dev/null +++ b/chromium/weblayer/browser/google_accounts_callback_proxy.h @@ -0,0 +1,34 @@ +// 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 WEBLAYER_BROWSER_GOOGLE_ACCOUNTS_CALLBACK_PROXY_H_ +#define WEBLAYER_BROWSER_GOOGLE_ACCOUNTS_CALLBACK_PROXY_H_ + +#include <jni.h> + +#include "base/android/scoped_java_ref.h" +#include "weblayer/public/google_accounts_delegate.h" + +namespace weblayer { + +class Tab; + +class GoogleAccountsCallbackProxy : public GoogleAccountsDelegate { + public: + GoogleAccountsCallbackProxy(JNIEnv* env, jobject obj, Tab* tab); + ~GoogleAccountsCallbackProxy() override; + + // GoogleAccountsDelegate: + void OnGoogleAccountsRequest( + const signin::ManageAccountsParams& params) override; + std::string GetGaiaId() override; + + private: + Tab* tab_; + base::android::ScopedJavaGlobalRef<jobject> java_impl_; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_GOOGLE_ACCOUNTS_CALLBACK_PROXY_H_ diff --git a/chromium/weblayer/browser/infobar_android.cc b/chromium/weblayer/browser/infobar_android.cc deleted file mode 100644 index 4aa24af9285..00000000000 --- a/chromium/weblayer/browser/infobar_android.cc +++ /dev/null @@ -1,91 +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 "weblayer/browser/infobar_android.h" - -#include <utility> - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/strings/string_util.h" -#include "components/infobars/core/infobar.h" -#include "components/infobars/core/infobar_delegate.h" -#include "weblayer/browser/android/resource_mapper.h" -#include "weblayer/browser/java/jni/InfoBar_jni.h" - -using base::android::JavaParamRef; -using base::android::JavaRef; - -namespace weblayer { - -// InfoBarAndroid ------------------------------------------------------------- - -InfoBarAndroid::InfoBarAndroid( - std::unique_ptr<infobars::InfoBarDelegate> delegate) - : infobars::InfoBar(std::move(delegate)) {} - -InfoBarAndroid::~InfoBarAndroid() { - if (!java_info_bar_.is_null()) { - JNIEnv* env = base::android::AttachCurrentThread(); - Java_InfoBar_onNativeDestroyed(env, java_info_bar_); - } -} - -void InfoBarAndroid::ReassignJavaInfoBar(InfoBarAndroid* replacement) { - DCHECK(replacement); - if (!java_info_bar_.is_null()) { - replacement->SetJavaInfoBar(java_info_bar_); - java_info_bar_.Reset(); - } -} - -void InfoBarAndroid::SetJavaInfoBar( - const base::android::JavaRef<jobject>& java_info_bar) { - DCHECK(java_info_bar_.is_null()); - java_info_bar_.Reset(java_info_bar); - JNIEnv* env = base::android::AttachCurrentThread(); - Java_InfoBar_setNativeInfoBar(env, java_info_bar, - reinterpret_cast<intptr_t>(this)); -} - -const JavaRef<jobject>& InfoBarAndroid::GetJavaInfoBar() { - return java_info_bar_; -} - -bool InfoBarAndroid::HasSetJavaInfoBar() const { - return !java_info_bar_.is_null(); -} - -int InfoBarAndroid::GetInfoBarIdentifier(JNIEnv* env, - const JavaParamRef<jobject>& obj) { - return delegate()->GetIdentifier(); -} - -void InfoBarAndroid::OnButtonClicked(JNIEnv* env, - const JavaParamRef<jobject>& obj, - jint action) { - ProcessButton(action); -} - -void InfoBarAndroid::OnCloseButtonClicked(JNIEnv* env, - const JavaParamRef<jobject>& obj) { - if (!owner()) - return; // We're closing; don't call anything, it might access the owner. - delegate()->InfoBarDismissed(); - RemoveSelf(); -} - -void InfoBarAndroid::CloseJavaInfoBar() { - if (!java_info_bar_.is_null()) { - JNIEnv* env = base::android::AttachCurrentThread(); - Java_InfoBar_closeInfoBar(env, java_info_bar_); - java_info_bar_.Reset(nullptr); - } -} - -int InfoBarAndroid::GetJavaIconId() { - return weblayer::MapToJavaDrawableId(delegate()->GetIconId()); -} - -} // namespace weblayer diff --git a/chromium/weblayer/browser/infobar_android.h b/chromium/weblayer/browser/infobar_android.h deleted file mode 100644 index 5c319688cff..00000000000 --- a/chromium/weblayer/browser/infobar_android.h +++ /dev/null @@ -1,86 +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 WEBLAYER_BROWSER_INFOBAR_ANDROID_H_ -#define WEBLAYER_BROWSER_INFOBAR_ANDROID_H_ - -#include <string> - -#include "base/android/scoped_java_ref.h" -#include "base/macros.h" -#include "components/infobars/core/infobar.h" - -namespace infobars { -class InfoBarDelegate; -} - -namespace weblayer { - -class InfoBarAndroid : public infobars::InfoBar { - public: - // A Java counterpart will be generated for this enum. - // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.weblayer_private - // GENERATED_JAVA_PREFIX_TO_STRIP: ACTION_ - enum ActionType { - ACTION_NONE = 0, - // Confirm infobar - ACTION_OK = 1, - ACTION_CANCEL = 2, - // Translate infobar - ACTION_TRANSLATE = 3, - ACTION_TRANSLATE_SHOW_ORIGINAL = 4, - }; - - explicit InfoBarAndroid(std::unique_ptr<infobars::InfoBarDelegate> delegate); - ~InfoBarAndroid() override; - - // InfoBar: - virtual base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar( - JNIEnv* env) = 0; - - virtual void SetJavaInfoBar( - const base::android::JavaRef<jobject>& java_info_bar); - const base::android::JavaRef<jobject>& GetJavaInfoBar(); - bool HasSetJavaInfoBar() const; - - // Tells the Java-side counterpart of this InfoBar to point to the replacement - // InfoBar instead of this one. - void ReassignJavaInfoBar(InfoBarAndroid* replacement); - - int GetInfoBarIdentifier(JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj); - virtual void OnLinkClicked(JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj) {} - void OnButtonClicked(JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj, - jint action); - void OnCloseButtonClicked(JNIEnv* env, - const base::android::JavaParamRef<jobject>& obj); - - void CloseJavaInfoBar(); - - // Maps from a Chromium ID (IDR_TRANSLATE) to a Drawable ID. - int GetJavaIconId(); - - // Acquire the java infobar from a different one. This is used to do in-place - // replacements. - virtual void PassJavaInfoBar(InfoBarAndroid* source) {} - - protected: - // Derived classes must implement this method to process the corresponding - // action. - virtual void ProcessButton(int action) = 0; - - void CloseInfoBar(); - InfoBarAndroid* infobar_android() { return this; } - - private: - base::android::ScopedJavaGlobalRef<jobject> java_info_bar_; - - DISALLOW_COPY_AND_ASSIGN(InfoBarAndroid); -}; - -} // namespace weblayer - -#endif // WEBLAYER_BROWSER_INFOBAR_ANDROID_H_ diff --git a/chromium/weblayer/browser/infobar_container_android.cc b/chromium/weblayer/browser/infobar_container_android.cc index 368ab764ce8..6da65a200cf 100644 --- a/chromium/weblayer/browser/infobar_container_android.cc +++ b/chromium/weblayer/browser/infobar_container_android.cc @@ -8,10 +8,10 @@ #include "base/check.h" #include "base/metrics/histogram_functions.h" #include "base/notreached.h" +#include "components/infobars/android/infobar_android.h" #include "components/infobars/core/infobar.h" #include "components/infobars/core/infobar_delegate.h" #include "content/public/browser/web_contents.h" -#include "weblayer/browser/infobar_android.h" #include "weblayer/browser/infobar_service.h" #include "weblayer/browser/java/jni/InfoBarContainer_jni.h" @@ -50,7 +50,8 @@ void InfoBarContainerAndroid::PlatformSpecificAddInfoBar( infobars::InfoBar* infobar, size_t position) { DCHECK(infobar); - InfoBarAndroid* android_bar = static_cast<InfoBarAndroid*>(infobar); + infobars::InfoBarAndroid* android_bar = + static_cast<infobars::InfoBarAndroid*>(infobar); if (!android_bar) { // TODO(bulach): CLANK: implement other types of InfoBars. NOTIMPLEMENTED() << "CLANK: infobar identifier " @@ -61,7 +62,8 @@ void InfoBarContainerAndroid::PlatformSpecificAddInfoBar( AttachJavaInfoBar(android_bar); } -void InfoBarContainerAndroid::AttachJavaInfoBar(InfoBarAndroid* android_bar) { +void InfoBarContainerAndroid::AttachJavaInfoBar( + infobars::InfoBarAndroid* android_bar) { if (android_bar->HasSetJavaInfoBar()) return; JNIEnv* env = base::android::AttachCurrentThread(); @@ -92,13 +94,14 @@ void InfoBarContainerAndroid::AttachJavaInfoBar(InfoBarAndroid* android_bar) { void InfoBarContainerAndroid::PlatformSpecificReplaceInfoBar( infobars::InfoBar* old_infobar, infobars::InfoBar* new_infobar) { - static_cast<InfoBarAndroid*>(new_infobar) - ->PassJavaInfoBar(static_cast<InfoBarAndroid*>(old_infobar)); + static_cast<infobars::InfoBarAndroid*>(new_infobar) + ->PassJavaInfoBar(static_cast<infobars::InfoBarAndroid*>(old_infobar)); } void InfoBarContainerAndroid::PlatformSpecificRemoveInfoBar( infobars::InfoBar* infobar) { - InfoBarAndroid* android_infobar = static_cast<InfoBarAndroid*>(infobar); + infobars::InfoBarAndroid* android_infobar = + static_cast<infobars::InfoBarAndroid*>(infobar); android_infobar->CloseJavaInfoBar(); } diff --git a/chromium/weblayer/browser/infobar_container_android.h b/chromium/weblayer/browser/infobar_container_android.h index 4069cf6b19e..7ad6203b7c2 100644 --- a/chromium/weblayer/browser/infobar_container_android.h +++ b/chromium/weblayer/browser/infobar_container_android.h @@ -16,9 +16,11 @@ #include "base/macros.h" #include "components/infobars/core/infobar_container.h" -namespace weblayer { - +namespace infobars { class InfoBarAndroid; +} + +namespace weblayer { class InfoBarContainerAndroid : public infobars::InfoBarContainer { public: @@ -44,7 +46,7 @@ class InfoBarContainerAndroid : public infobars::InfoBarContainer { // Create the Java equivalent of |android_bar| and add it to the java // container. - void AttachJavaInfoBar(InfoBarAndroid* android_bar); + void AttachJavaInfoBar(infobars::InfoBarAndroid* android_bar); // We're owned by the java infobar, need to use a weak ref so it can destroy // us. diff --git a/chromium/weblayer/browser/infobar_service.cc b/chromium/weblayer/browser/infobar_service.cc index 37af3091f35..a2e60fe6e29 100644 --- a/chromium/weblayer/browser/infobar_service.cc +++ b/chromium/weblayer/browser/infobar_service.cc @@ -4,6 +4,14 @@ #include "weblayer/browser/infobar_service.h" +#include "build/build_config.h" + +#if defined(OS_ANDROID) +#include "base/bind.h" +#include "components/infobars/android/confirm_infobar.h" +#include "weblayer/browser/android/resource_mapper.h" +#endif + namespace weblayer { InfoBarService::InfoBarService(content::WebContents* web_contents) @@ -11,6 +19,20 @@ InfoBarService::InfoBarService(content::WebContents* web_contents) InfoBarService::~InfoBarService() {} +#if defined(OS_ANDROID) +std::unique_ptr<infobars::InfoBar> InfoBarService::CreateConfirmInfoBar( + std::unique_ptr<ConfirmInfoBarDelegate> delegate) { + return std::make_unique<infobars::ConfirmInfoBar>(std::move(delegate), + GetResourceIdMapper()); +} + +// static +infobars::InfoBarAndroid::ResourceIdMapper +InfoBarService::GetResourceIdMapper() { + return base::BindRepeating(&MapToJavaDrawableId); +} +#endif // if defined(OS_ANDROID) + void InfoBarService::WebContentsDestroyed() { // The WebContents is going away; be aggressively paranoid and delete // ourselves lest other parts of the system attempt to add infobars or use diff --git a/chromium/weblayer/browser/infobar_service.h b/chromium/weblayer/browser/infobar_service.h index 95cb1ec184b..1d6f0a14c36 100644 --- a/chromium/weblayer/browser/infobar_service.h +++ b/chromium/weblayer/browser/infobar_service.h @@ -13,6 +13,10 @@ #include "components/infobars/content/content_infobar_manager.h" #include "content/public/browser/web_contents_user_data.h" +#if defined(OS_ANDROID) +#include "components/infobars/android/infobar_android.h" +#endif + namespace content { class WebContents; } @@ -33,6 +37,10 @@ class InfoBarService : public infobars::ContentInfoBarManager, std::unique_ptr<infobars::InfoBar> CreateConfirmInfoBar( std::unique_ptr<ConfirmInfoBarDelegate> delegate) override; +#if defined(OS_ANDROID) + static infobars::InfoBarAndroid::ResourceIdMapper GetResourceIdMapper(); +#endif + protected: explicit InfoBarService(content::WebContents* web_contents); diff --git a/chromium/weblayer/browser/insecure_form_controller_client.cc b/chromium/weblayer/browser/insecure_form_controller_client.cc new file mode 100644 index 00000000000..9b7fed0b16d --- /dev/null +++ b/chromium/weblayer/browser/insecure_form_controller_client.cc @@ -0,0 +1,47 @@ +// 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 "weblayer/browser/insecure_form_controller_client.h" + +#include "content/public/browser/web_contents.h" +#include "weblayer/browser/i18n_util.h" + +namespace weblayer { + +// static +std::unique_ptr<security_interstitials::MetricsHelper> +InsecureFormControllerClient::GetMetricsHelper(const GURL& url) { + security_interstitials::MetricsHelper::ReportDetails settings; + settings.metric_prefix = "insecure_form"; + return std::make_unique<security_interstitials::MetricsHelper>(url, settings, + nullptr); +} + +InsecureFormControllerClient::InsecureFormControllerClient( + content::WebContents* web_contents, + const GURL& form_target_url) + : SecurityInterstitialControllerClient( + web_contents, + GetMetricsHelper(form_target_url), + nullptr, /* prefs */ + i18n::GetApplicationLocale(), + GURL("about:blank") /* default_safe_page */), + web_contents_(web_contents) {} + +InsecureFormControllerClient::~InsecureFormControllerClient() = default; + +void InsecureFormControllerClient::GoBack() { + SecurityInterstitialControllerClient::GoBackAfterNavigationCommitted(); +} + +void InsecureFormControllerClient::Proceed() { + // TODO(crbug.com/1093955): The simple reload logic means the interstitial is + // bypassed with any reload (e.g. F5), ideally this shouldn't be the case. + + // We don't check for repost on the proceed reload since the interstitial + // explains this will submit the form. + web_contents_->GetController().Reload(content::ReloadType::NORMAL, false); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/insecure_form_controller_client.h b/chromium/weblayer/browser/insecure_form_controller_client.h new file mode 100644 index 00000000000..e8c80c58f01 --- /dev/null +++ b/chromium/weblayer/browser/insecure_form_controller_client.h @@ -0,0 +1,43 @@ +// 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 WEBLAYER_BROWSER_INSECURE_FORM_CONTROLLER_CLIENT_H_ +#define WEBLAYER_BROWSER_INSECURE_FORM_CONTROLLER_CLIENT_H_ + +#include "components/security_interstitials/content/security_interstitial_controller_client.h" +#include "components/security_interstitials/core/metrics_helper.h" + +namespace content { +class WebContents; +} + +namespace weblayer { + +// A stripped-down version of the class by the same name in +// //chrome/browser/ssl, which provides basic functionality for interacting with +// the insecure form interstitial. +class InsecureFormControllerClient + : public security_interstitials::SecurityInterstitialControllerClient { + public: + static std::unique_ptr<security_interstitials::MetricsHelper> + GetMetricsHelper(const GURL& url); + + InsecureFormControllerClient(content::WebContents* web_contents, + const GURL& form_target_url); + InsecureFormControllerClient(const InsecureFormControllerClient&) = delete; + InsecureFormControllerClient& operator=(const InsecureFormControllerClient&) = + delete; + ~InsecureFormControllerClient() override; + + // security_interstitials::SecurityInterstitialControllerClient: + void GoBack() override; + void Proceed() override; + + private: + content::WebContents* web_contents_; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_INSECURE_FORM_CONTROLLER_CLIENT_H_ diff --git a/chromium/weblayer/browser/java/AndroidManifest.xml b/chromium/weblayer/browser/java/AndroidManifest.xml new file mode 100644 index 00000000000..20cd3a9dd9b --- /dev/null +++ b/chromium/weblayer/browser/java/AndroidManifest.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2020 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:dist="http://schemas.android.com/apk/distribution" + featureSplit="weblayer"> + + <!-- Due to b/164116603 fusing is false for now. Once that bug is fixed, + the WebLayer module should be fused. --> + <dist:module + dist:onDemand="false"> + <dist:fusing dist:include="false" /> + </dist:module> + + <application /> +</manifest> diff --git a/chromium/weblayer/browser/java/BUILD.gn b/chromium/weblayer/browser/java/BUILD.gn index 38cdda128ce..45c20a4d642 100644 --- a/chromium/weblayer/browser/java/BUILD.gn +++ b/chromium/weblayer/browser/java/BUILD.gn @@ -7,21 +7,26 @@ import("//build/config/android/rules.gni") import("//build/config/locales.gni") import("//weblayer/variables.gni") +_bundle_utils_output = + "$target_gen_dir/org/chromium/weblayer_private/WebLayerBundleUtils.java" + +jinja_template("weblayer_bundle_utils") { + input = "WebLayerBundleUtils.java.jinja2" + output = _bundle_utils_output + variables = "weblayer_in_split=$weblayer_in_split" +} + android_resources("weblayer_resources") { sources = [ - "res/drawable/weblayer_infobar_wrapper_bg.xml", "res/drawable/weblayer_tab_indicator.xml", "res/layout/site_settings_layout.xml", "res/layout/weblayer_infobar_translate_compact_content.xml", - "res/layout/weblayer_infobar_translate_tab_content.xml", - "res/layout/weblayer_translate_menu_item.xml", - "res/layout/weblayer_translate_menu_item_checked.xml", "res/layout/weblayer_url_bar.xml", "res/values/colors.xml", "res/values/dimens.xml", "res/values/styles.xml", ] - custom_package = "org.chromium.weblayer_private" + create_srcjar = false deps = [ ":weblayer_strings_grd", "//components/blocked_content/android:java_resources", @@ -35,11 +40,18 @@ android_resources("weblayer_resources") { "//components/page_info/android:java_resources", "//components/permissions/android:java_resources", "//components/translate/content/android:java_resources", - "//third_party/android_deps:com_google_android_material_material_java", + "//third_party/android_deps:material_design_java", "//weblayer:components_java_strings", ] } +# This resource target is kept in the base module when WebLayer is in a split, +# and allows WebLayer to call onResourcesLoaded() on resources shared with +# WebView. +android_resources("weblayer_base_module_resources") { + custom_package = "org.chromium.weblayer_private.base" +} + generate_product_config_srcjar("weblayer_product_config") { java_package = weblayer_product_config_java_package } @@ -51,6 +63,7 @@ java_cpp_template("resource_id_javagen") { "//components/resources/android/blocked_content_resource_id.h", "//components/resources/android/page_info_resource_id.h", "//components/resources/android/permissions_resource_id.h", + "//components/resources/android/sms_resource_id.h", ] } @@ -65,8 +78,6 @@ java_strings_grd("weblayer_strings_grd") { java_cpp_enum("generated_enums") { sources = [ "//weblayer/browser/controls_visibility_reason.h", - "//weblayer/browser/infobar_android.h", - "//weblayer/browser/translate_utils.h", "//weblayer/public/download.h", "//weblayer/public/navigation.h", "//weblayer/public/new_tab_delegate.h", @@ -74,6 +85,16 @@ java_cpp_enum("generated_enums") { ] } +android_library("base_module_java") { + sources = + [ "org/chromium/weblayer_private/WebViewCompatibilityHelperImpl.java" ] + deps = [ + ":weblayer_base_module_resources", + "//base:base_java", + ] + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] +} + android_library("java") { sources = [ "org/chromium/weblayer_private/ActionModeCallback.java", @@ -84,7 +105,6 @@ android_library("java") { "org/chromium/weblayer_private/BrowserImpl.java", "org/chromium/weblayer_private/BrowserViewController.java", "org/chromium/weblayer_private/ChildProcessServiceImpl.java", - "org/chromium/weblayer_private/ConfirmInfoBar.java", "org/chromium/weblayer_private/ContentViewRenderView.java", "org/chromium/weblayer_private/CookieManagerImpl.java", "org/chromium/weblayer_private/CrashReporterControllerImpl.java", @@ -92,16 +112,14 @@ android_library("java") { "org/chromium/weblayer_private/DownloadImpl.java", "org/chromium/weblayer_private/ErrorPageCallbackProxy.java", "org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java", + "org/chromium/weblayer_private/FaviconCallbackProxy.java", "org/chromium/weblayer_private/FragmentAndroidPermissionDelegate.java", "org/chromium/weblayer_private/FragmentWindowAndroid.java", "org/chromium/weblayer_private/FullscreenCallbackProxy.java", - "org/chromium/weblayer_private/InfoBar.java", - "org/chromium/weblayer_private/InfoBarCompactLayout.java", + "org/chromium/weblayer_private/FullscreenToast.java", + "org/chromium/weblayer_private/GoogleAccountsCallbackProxy.java", "org/chromium/weblayer_private/InfoBarContainer.java", - "org/chromium/weblayer_private/InfoBarContainerLayout.java", "org/chromium/weblayer_private/InfoBarContainerView.java", - "org/chromium/weblayer_private/InfoBarUiItem.java", - "org/chromium/weblayer_private/InfoBarWrapper.java", "org/chromium/weblayer_private/IntentUtils.java", "org/chromium/weblayer_private/InterceptNavigationDelegateClientImpl.java", "org/chromium/weblayer_private/LocaleChangedBroadcastReceiver.java", @@ -116,42 +134,41 @@ android_library("java") { "org/chromium/weblayer_private/ProfileManager.java", "org/chromium/weblayer_private/RemoteFragmentImpl.java", "org/chromium/weblayer_private/SiteSettingsFragmentImpl.java", - "org/chromium/weblayer_private/SwipableOverlayView.java", "org/chromium/weblayer_private/TabCallbackProxy.java", "org/chromium/weblayer_private/TabImpl.java", "org/chromium/weblayer_private/TranslateCompactInfoBar.java", - "org/chromium/weblayer_private/TranslateMenu.java", - "org/chromium/weblayer_private/TranslateMenuHelper.java", - "org/chromium/weblayer_private/TranslateOptions.java", - "org/chromium/weblayer_private/TranslateTabContent.java", - "org/chromium/weblayer_private/TranslateTabLayout.java", "org/chromium/weblayer_private/UrlBarControllerImpl.java", "org/chromium/weblayer_private/WebContentsGestureStateTracker.java", "org/chromium/weblayer_private/WebLayerAccessibilityUtil.java", "org/chromium/weblayer_private/WebLayerExceptionFilter.java", "org/chromium/weblayer_private/WebLayerFactoryImpl.java", "org/chromium/weblayer_private/WebLayerImpl.java", - "org/chromium/weblayer_private/WebLayerNotificationBuilder.java", "org/chromium/weblayer_private/WebLayerNotificationChannels.java", + "org/chromium/weblayer_private/WebLayerNotificationWrapperBuilder.java", "org/chromium/weblayer_private/WebLayerSiteSettingsClient.java", "org/chromium/weblayer_private/WebLayerTabModalPresenter.java", "org/chromium/weblayer_private/WebMessageReplyProxyImpl.java", "org/chromium/weblayer_private/WebShareServiceFactory.java", - "org/chromium/weblayer_private/WebViewCompatibilityHelperImpl.java", "org/chromium/weblayer_private/metrics/MetricsServiceClient.java", "org/chromium/weblayer_private/metrics/UmaUtils.java", "org/chromium/weblayer_private/permissions/PermissionRequestUtils.java", "org/chromium/weblayer_private/resources/ResourceMapper.java", + _bundle_utils_output, ] + resources_package = "org.chromium.weblayer_private" deps = [ + ":base_module_java", ":gms_bridge_java", ":interfaces_java", + ":weblayer_bundle_utils", ":weblayer_resources", "//base:base_java", "//base:jni_java", "//components/autofill/android/provider:java", + "//components/browser_ui/banners/android:java", "//components/browser_ui/client_certificate/android:java", + "//components/browser_ui/display_cutout/android:java", "//components/browser_ui/http_auth/android:java", "//components/browser_ui/media/android:java", "//components/browser_ui/modaldialog/android:java", @@ -159,10 +176,12 @@ android_library("java") { "//components/browser_ui/settings/android:java", "//components/browser_ui/share/android:java", "//components/browser_ui/site_settings/android:java", + "//components/browser_ui/sms/android:java", "//components/browser_ui/styles/android:java", "//components/browser_ui/util/android:java", "//components/browser_ui/webshare/android:java", "//components/browser_ui/widget/android:java", + "//components/content_settings/android:content_settings_enums_java", "//components/content_settings/android:java", "//components/crash/android:handler_java", "//components/crash/android:java", @@ -176,6 +195,7 @@ android_library("java") { "//components/embedder_support/android/metrics:java", "//components/external_intents/android:java", "//components/find_in_page/android:java", + "//components/infobars/android:infobar_android_enums_java", "//components/infobars/android:java", "//components/infobars/core:infobar_enums_java", "//components/javascript_dialogs/android:java", @@ -188,12 +208,13 @@ android_library("java") { "//components/permissions/android:java", "//components/safe_browsing/android:safe_browsing_java", "//components/security_interstitials/content/android:java", + "//components/signin/core/browser:signin_enums_java", "//components/spellcheck/browser/android:java", + "//components/translate/content/android:java", + "//components/translate/content/android:translate_android_enums_java", "//components/url_formatter/android:url_formatter_java", "//components/variations/android:variations_java", "//components/version_info/android:version_constants_java", - "//components/webapk/android/libs/client:java", - "//components/webapk/android/libs/common:java", "//components/webrtc/android:java", "//content/public/android:content_java", "//mojo/public/java:bindings_java", @@ -201,16 +222,19 @@ android_library("java") { "//services/network/public/mojom:cookies_mojom_java", "//services/network/public/mojom:mojom_java", "//services/service_manager/public/java:service_manager_java", + "//third_party/android_deps:androidx_annotation_annotation_java", "//third_party/android_deps:androidx_appcompat_appcompat_java", "//third_party/android_deps:androidx_appcompat_appcompat_resources_java", "//third_party/android_deps:androidx_core_core_java", "//third_party/android_deps:androidx_fragment_fragment_java", + "//third_party/android_deps:androidx_media_media_java", "//third_party/android_deps:androidx_preference_preference_java", - "//third_party/android_deps:com_google_android_material_material_java", + "//third_party/android_deps:material_design_java", "//third_party/blink/public/mojom:android_mojo_bindings_java", "//ui/android:ui_full_java", "//ui/android:ui_java", "//url:gurl_java", + "//url:origin_java", ] srcjar_deps = [ ":generated_enums", @@ -236,7 +260,7 @@ android_resources("weblayer_test_resources") { "res_test/layout/test_layout.xml", "res_test/values/values.xml", ] - custom_package = "org.chromium.weblayer_private.test" + create_srcjar = false } android_library("test_java") { @@ -245,23 +269,34 @@ android_library("test_java") { "org/chromium/weblayer_private/test/TestInfoBar.java", "org/chromium/weblayer_private/test/TestWebLayerImpl.java", ] + resources_package = "org.chromium.weblayer_private.test" deps = [ ":interfaces_java", ":java", ":weblayer_test_resources", + "//base:base_java", "//base:jni_java", + "//components/infobars/android:java", "//components/location/android:location_java", "//components/permissions/android:java", "//content/public/test/android:content_java_test_support", "//net/android:net_java", "//services/device/public/java:geolocation_java", "//services/device/public/java:geolocation_java_test_support", + "//third_party/android_deps:androidx_annotation_annotation_java", "//ui/android:ui_full_java", ] srcjar_deps = [ ":test_aidl" ] annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] } +generate_jni_registration("test_weblayer_jni_registration") { + testonly = true + targets = [ ":test_java" ] + header_output = "$target_gen_dir/$target_name.h" + namespace = "weblayer_test" +} + generate_jni("test_jni") { testonly = true sources = [ @@ -275,14 +310,14 @@ generate_jni("jni") { "org/chromium/weblayer_private/AutocompleteSchemeClassifierImpl.java", "org/chromium/weblayer_private/BrowserControlsContainerView.java", "org/chromium/weblayer_private/BrowserImpl.java", - "org/chromium/weblayer_private/ConfirmInfoBar.java", "org/chromium/weblayer_private/ContentViewRenderView.java", "org/chromium/weblayer_private/CookieManagerImpl.java", "org/chromium/weblayer_private/DownloadCallbackProxy.java", "org/chromium/weblayer_private/DownloadImpl.java", "org/chromium/weblayer_private/ErrorPageCallbackProxy.java", + "org/chromium/weblayer_private/FaviconCallbackProxy.java", "org/chromium/weblayer_private/FullscreenCallbackProxy.java", - "org/chromium/weblayer_private/InfoBar.java", + "org/chromium/weblayer_private/GoogleAccountsCallbackProxy.java", "org/chromium/weblayer_private/InfoBarContainer.java", "org/chromium/weblayer_private/LocaleChangedBroadcastReceiver.java", "org/chromium/weblayer_private/MediaStreamManager.java", @@ -315,6 +350,7 @@ android_library("interfaces_java") { "org/chromium/weblayer_private/interfaces/CookieChangeCause.java", "org/chromium/weblayer_private/interfaces/DownloadError.java", "org/chromium/weblayer_private/interfaces/DownloadState.java", + "org/chromium/weblayer_private/interfaces/GoogleAccountServiceType.java", "org/chromium/weblayer_private/interfaces/LoadError.java", "org/chromium/weblayer_private/interfaces/NavigateParams.java", "org/chromium/weblayer_private/interfaces/NavigationState.java", @@ -375,8 +411,11 @@ android_aidl("aidl") { "org/chromium/weblayer_private/interfaces/IDownload.aidl", "org/chromium/weblayer_private/interfaces/IDownloadCallbackClient.aidl", "org/chromium/weblayer_private/interfaces/IErrorPageCallbackClient.aidl", + "org/chromium/weblayer_private/interfaces/IFaviconFetcher.aidl", + "org/chromium/weblayer_private/interfaces/IFaviconFetcherClient.aidl", "org/chromium/weblayer_private/interfaces/IFindInPageCallbackClient.aidl", "org/chromium/weblayer_private/interfaces/IFullscreenCallbackClient.aidl", + "org/chromium/weblayer_private/interfaces/IGoogleAccountsCallbackClient.aidl", "org/chromium/weblayer_private/interfaces/IMediaCaptureCallbackClient.aidl", "org/chromium/weblayer_private/interfaces/INavigation.aidl", "org/chromium/weblayer_private/interfaces/INavigationController.aidl", diff --git a/chromium/weblayer/browser/java/DEPS b/chromium/weblayer/browser/java/DEPS index 819b5a6c915..75b1b714394 100644 --- a/chromium/weblayer/browser/java/DEPS +++ b/chromium/weblayer/browser/java/DEPS @@ -11,11 +11,13 @@ include_rules = [ "+components/webapk/android/libs", "+services/device/public/java/src/org/chromium/device/geolocation", - # WebLayerNotificationBuilder should be used for all notifications. - "-components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationBuilder.java", + # WebLayerNotificationWrapperBuilder should be used for all notifications. + "-components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationWrapperCompatBuilder.java", + "-components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationWrapperStandardBuilder.java", ] specific_include_rules = { - "WebLayerNotificationBuilder.java": [ - "+components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationBuilder.java", + "WebLayerNotificationWrapperBuilder.java": [ + "+components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationWrapperCompatBuilder.java", + "+components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/NotificationWrapperStandardBuilder.java", ] } diff --git a/chromium/weblayer/browser/java/ResourceId.template b/chromium/weblayer/browser/java/ResourceId.template index 1e16c782f49..ed370063cb5 100644 --- a/chromium/weblayer/browser/java/ResourceId.template +++ b/chromium/weblayer/browser/java/ResourceId.template @@ -14,6 +14,7 @@ class ResourceId { #include "components/resources/android/blocked_content_resource_id.h" #include "components/resources/android/page_info_resource_id.h" #include "components/resources/android/permissions_resource_id.h" +#include "components/resources/android/sms_resource_id.h" }; return resourceList; } diff --git a/chromium/weblayer/browser/java/WebLayerBundleUtils.java.jinja2 b/chromium/weblayer/browser/java/WebLayerBundleUtils.java.jinja2 new file mode 100644 index 00000000000..4b03d9672c6 --- /dev/null +++ b/chromium/weblayer/browser/java/WebLayerBundleUtils.java.jinja2 @@ -0,0 +1,9 @@ +// 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.weblayer_private; + +public final class WebLayerBundleUtils { + public static final boolean IS_WEBLAYER_IN_SPLIT = {{ weblayer_in_split }}; +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java index ce10368c02c..454fb68b70c 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java @@ -11,11 +11,14 @@ import android.view.View; import android.view.ViewParent; import android.widget.FrameLayout; +import androidx.annotation.Nullable; + import org.chromium.base.MathUtils; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; import org.chromium.content_public.browser.WebContents; +import org.chromium.content_public.common.BrowserControlsState; import org.chromium.ui.base.EventOffsetHandler; import org.chromium.ui.resources.dynamics.ViewResourceAdapter; @@ -58,8 +61,27 @@ class BrowserControlsContainerView extends FrameLayout { private static final long SYSTEM_UI_VIEWPORT_UPDATE_DELAY_MS = 500; + private static final int DEFAULT_LAST_SHOWN_AMOUNT = -1; + + /** Stores the state needed to reconstruct offsets after recreating this class. */ + /* package */ static class State { + private final int mControlsOffset; + private final int mContentOffset; + + private State(int controlsOffset, int contentOffset) { + mControlsOffset = controlsOffset; + mContentOffset = contentOffset; + } + } + + private final Delegate mDelegate; private final boolean mIsTop; + // The state returned by a previous BrowserControlsContainerView instance's getState() method. + // This is saved rather than directly applied because layout needs to occur before we can apply + // the offsets. + private State mSavedState; + private long mNativeBrowserControlsContainerView; private ViewResourceAdapter mViewResourceAdapter; @@ -68,7 +90,7 @@ class BrowserControlsContainerView extends FrameLayout { private int mLastWidth; private int mLastHeight; - // view from the client. + // View from the client. private View mView; private ContentViewRenderView mContentViewRenderView; @@ -87,41 +109,70 @@ class BrowserControlsContainerView extends FrameLayout { // bottom-controls, the value ranges from 0 (completely shown) to height (completely hidden). private int mControlsOffset; + // Stores how much of the controls in pixels is visible when a view is set. This does NOT get + // set to 0 when you remove a view; it always stores the most recent control visibility amount + // as of the last time a view was actually set, or a default value. This is used to help mimic + // the positioning behavior of the renderer. + private int mLastShownAmountWithView = DEFAULT_LAST_SHOWN_AMOUNT; + + // The minimum height that the controls should collapse to. Only used for top controls. + private int mMinHeight; + + // Whether the controls should only expand when the page is scrolled to the top. Only used for + // top controls. + private boolean mOnlyExpandControlsAtPageTop; + + // Set to true if changes to the controls height or offset should be animated. + private boolean mShouldAnimate; + // Set to true if |mView| is hidden because the user has scrolled or triggered some action such // that mView is not visible. While |mView| is not visible if this is true, the bitmap from // |mView| may be partially visible. private boolean mInScroll; + // Set to true while we are animating the controls off the screen after removing them. + private boolean mAnimatingOut; + private boolean mIsFullscreen; // Used to delay processing fullscreen requests. private Runnable mSystemUiFullscreenResizeRunnable; - private final Listener mListener; - - public interface Listener { - /** - * Called when the browser-controls are either completely showing, or completely hiding. - */ - public void onBrowserControlsCompletelyShownOrHidden(); - } - - // Used to delay updating the image for the layer. + // Used to delay updating the image for the layer. private final Runnable mRefreshResourceIdRunnable = () -> { - if (mView == null) return; + if (mView == null || mViewResourceAdapter == null) return; BrowserControlsContainerViewJni.get().updateControlsResource( mNativeBrowserControlsContainerView); }; + // Used to delay hiding the controls. + private final Runnable mHideControlsRunnable = this::hideControlsNow; + + // Used to delay showing the controls. + private final Runnable mShowControlsRunnable = this::showControlsNow; + + public interface Delegate { + /** + * Requests that the page height be recalculated due to browser controls height changes. + */ + void refreshPageHeight(); + + /** + * Requests that the browser controls visibility state be changed. + */ + void setAnimationConstraint(@BrowserControlsState int constraint); + } + BrowserControlsContainerView(Context context, ContentViewRenderView contentViewRenderView, - Listener listener, boolean isTop) { + Delegate delegate, boolean isTop, @Nullable State savedState) { super(context); + mDelegate = delegate; mIsTop = isTop; + mSavedState = savedState; mContentViewRenderView = contentViewRenderView; mNativeBrowserControlsContainerView = BrowserControlsContainerViewJni.get().createBrowserControlsContainerView( this, contentViewRenderView.getNativeHandle(), isTop); - mListener = listener; } public void setWebContents(WebContents webContents) { @@ -135,7 +186,8 @@ class BrowserControlsContainerView extends FrameLayout { } public void destroy() { - clearViewAndDestroyResources(); + if (mIsTop) setAnimationsEnabled(false); + setView(null); BrowserControlsContainerViewJni.get().deleteBrowserControlsContainerView( mNativeBrowserControlsContainerView); cancelDelayedFullscreenRunnable(); @@ -172,7 +224,7 @@ class BrowserControlsContainerView extends FrameLayout { */ public int getContentHeightDelta() { if (mView == null) return 0; - return mIsTop ? mContentOffset : getHeight() - mControlsOffset; + return mIsTop ? mContentOffset : mLastHeight - mControlsOffset; } /** @@ -180,15 +232,22 @@ class BrowserControlsContainerView extends FrameLayout { */ public boolean isControlVisible() { // Don't check the visibility of the View itself as it's hidden while scrolling. - return mView != null && Math.abs(mControlsOffset) != getHeight(); + return mView != null && Math.abs(mControlsOffset) != mLastHeight; + } + + public void setAnimationsEnabled(boolean animationsEnabled) { + assert mIsTop; + mShouldAnimate = animationsEnabled; } /** - * Returns true if the controls are completely shown or completely hidden. A return value - * of false indicates the controls are being moved. + * Returns true if the controls are completely expanded or completely collapsed. + * "Completely collapsed" does not necessarily mean hidden; the controls could be at their min + * height, in which case this would return true. A return value of false indicates the controls + * are being moved. */ - public boolean isCompletelyShownOrHidden() { - return mControlsOffset == 0 || Math.abs(mControlsOffset) == getHeight(); + public boolean isCompletelyExpandedOrCollapsed() { + return mControlsOffset == 0 || Math.abs(mControlsOffset) == mLastHeight - mMinHeight; } /** @@ -196,56 +255,89 @@ class BrowserControlsContainerView extends FrameLayout { */ public void setView(View view) { if (mView == view) return; - clearViewAndDestroyResources(); + + if (mView != null && mView.getParent() == this) removeView(mView); mView = view; + if (mView == null) { - setControlsOffset(0, 0); + // If we're animating out the old view, leave the cc::Layer in place so it's visible + // during the animation, and set our visibility to HIDDEN, which will cause + // BrowserControlsOffsetManager to start an animation off the screen. getMinHeight() + // will return 0 while mAnimatingOut is true, so call reportHeightChange() to tell the + // renderer to grab the potentially new height. + if (mShouldAnimate && mControlsOffset != -mLastHeight) { + assert mIsTop; // mShouldAnimate should only be true for top controls. + mAnimatingOut = true; + reportHeightChange(); + mDelegate.setAnimationConstraint(BrowserControlsState.HIDDEN); + mDelegate.refreshPageHeight(); + } else { + destroyLayer(); + } return; } + + mAnimatingOut = false; + destroyLayer(); addView(view, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.UNSPECIFIED_GRAVITY)); - if (getWidth() > 0 && getHeight() > 0) { - view.layout(0, 0, getWidth(), getHeight()); - createAdapterAndLayer(); - } - if (mIsFullscreen) hideControls(); + // The controls will be positioned in onLayout, which will result in showControls or + // hideControls being called. hideControls may delay the hide by a frame, resulting in the + // View flashing during the current frame. To work around this, we hide the controls here. + // showControls will also delay the showing by a frame, but that doesn't cause a flash + // because the bitmap will be visible until the setVisibility call completes. + hideControlsNow(); + mContentViewRenderView.removeCallbacks(mShowControlsRunnable); + mDelegate.setAnimationConstraint(BrowserControlsState.BOTH); + } + + public View getView() { + return mView; } /** - * Does cleanup necessary when mView is to be removed. - * In general prefer calling setView(). This is really an implementation detail of setView() - * and destroy(). + * Sets the minimum height the controls can collapse to. + * Only valid for top controls. */ - private void clearViewAndDestroyResources() { - if (mView == null) return; - if (mView.getParent() == this) removeView(mView); - // TODO: need some sort of destroy to drop reference. - mViewResourceAdapter = null; - BrowserControlsContainerViewJni.get().deleteControlsLayer( - mNativeBrowserControlsContainerView); - mContentViewRenderView.getResourceManager().getDynamicResourceLoader().unregisterResource( - getResourceId()); - mView = null; - mLastWidth = mLastHeight = 0; + public void setMinHeight(int minHeight) { + assert mIsTop; + if (mMinHeight == minHeight) return; + mMinHeight = minHeight; + reportHeightChange(); } - public View getView() { - return mView; + /** + * Sets whether the controls should only expand at the top of the page contents. + * Only valid for top controls. + */ + public void setOnlyExpandControlsAtPageTop(boolean onlyExpandControlsAtPageTop) { + mOnlyExpandControlsAtPageTop = onlyExpandControlsAtPageTop; } /** * Called from ViewAndroidDelegate, see it for details. */ public void onOffsetsChanged(int controlsOffsetY, int contentOffsetY) { - if (mView == null) return; - if (mIsFullscreen) return; - if (controlsOffsetY == 0) { - finishScroll(contentOffsetY); - return; + // Delete the cc::Layer if we reached the end of the animation off the screen. + if (mAnimatingOut && controlsOffsetY == -mLastHeight) { + mAnimatingOut = false; + destroyLayer(); + reportHeightChange(); + // Request a layout so onLayout can update the saved dimensions now that the + // layer has finished animating. + requestLayout(); } - if (!mInScroll) prepareForScroll(); + + if (mIsFullscreen) return; setControlsOffset(controlsOffsetY, contentOffsetY); + if (mControlsOffset == 0 + || (mIsTop && getMinHeight() > 0 + && mControlsOffset == -mLastHeight + getMinHeight())) { + finishScroll(); + } else if (!mInScroll) { + prepareForScroll(); + } } @SuppressLint("NewApi") // Used on O+, invalidateChildInParent used for previous versions. @@ -264,38 +356,57 @@ class BrowserControlsContainerView extends FrameLayout { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - if (mView == null) return; + if (mAnimatingOut) return; int width = right - left; int height = bottom - top; boolean heightChanged = height != mLastHeight; if (!heightChanged && width == mLastWidth) return; + int prevHeight = mLastHeight; mLastWidth = width; mLastHeight = height; if (mLastWidth > 0 && mLastHeight > 0 && mViewResourceAdapter == null) { createAdapterAndLayer(); - } else if (mViewResourceAdapter != null) { - BrowserControlsContainerViewJni.get().setControlsSize( - mNativeBrowserControlsContainerView, mLastWidth, mLastHeight); - if (mWebContents != null) mWebContents.notifyBrowserControlsHeightChanged(); - if (heightChanged) { - // When the height changes cc doesn't generate a new frame, which means this code - // must process the change now. If cc generated a new frame, it would likely be at - // the wrong size. - if (mControlsOffset == 0) { - // The controls are completely visible. - onOffsetsChanged(0, height); + if (mLastShownAmountWithView == DEFAULT_LAST_SHOWN_AMOUNT && mSavedState != null) { + // If there wasn't a View before and we have non-empty saved state from a previous + // BrowserControlsContainerView instance, apply those saved offsets now. We can't + // rely on BrowserControlsOffsetManager to notify us of the correct location as we + // usually do because it only notifies us when offsets change, but it likely didn't + // get destroyed when the BrowserFragment got recreated, so it won't notify us + // because it thinks we already have the correct offsets. + onOffsetsChanged(mSavedState.mControlsOffset, mSavedState.mContentOffset); + } else { + // Ideally we'd leave the controls hidden and wait for BrowserControlsOffsetManager + // to tell us where it wants them, but it communicates with us via frame metadata + // that is generated when the renderer's compositor submits a frame to viz, which is + // paused during page loads. Because of this, we might not get positioning + // information from the renderer for several seconds during page loads, so we need + // to attempt to position the controls ourselves here. + int targetShownAmount; + if (mShouldAnimate) { + // If animations are enabled, leave the amount of visible controls unchanged + // (e.g. hide them if there weren't previously controls or if they were hidden + // before). + targetShownAmount = mIsTop ? (prevHeight + mControlsOffset) + : (prevHeight - mControlsOffset); } else { - // The controls are partially (and possibly completely) hidden. Snap to - // completely hidden. - if (mIsTop) { - onOffsetsChanged(-height, height); - } else { - onOffsetsChanged(height, 0); - } + // If animations are disabled, restore the positioning the controls had the last + // time they were non-null. This mimics the behavior of + // BrowserControlsOffsetManager in the renderer. + targetShownAmount = (mLastShownAmountWithView == DEFAULT_LAST_SHOWN_AMOUNT) + ? height + : mLastShownAmountWithView; } + onOffsetsChanged( + mIsTop ? (targetShownAmount - height) : (height - targetShownAmount), + mIsTop ? targetShownAmount : 0); } + mSavedState = null; + } else if (mViewResourceAdapter != null) { + BrowserControlsContainerViewJni.get().setControlsSize( + mNativeBrowserControlsContainerView, mLastWidth, mLastHeight); } + if (heightChanged) reportHeightChange(); } @Override @@ -306,6 +417,10 @@ class BrowserControlsContainerView extends FrameLayout { cancelDelayedFullscreenRunnable(); } + /* package */ State getState() { + return new State(mControlsOffset, mContentOffset); + } + private void cancelDelayedFullscreenRunnable() { if (mSystemUiFullscreenResizeRunnable == null) return; removeCallbacks(mSystemUiFullscreenResizeRunnable); @@ -336,38 +451,41 @@ class BrowserControlsContainerView extends FrameLayout { // the layer is created and actually shown. Chrome for Android does the same thing. BrowserControlsContainerViewJni.get().createControlsLayer( mNativeBrowserControlsContainerView, getResourceId()); - mLastWidth = getWidth(); - mLastHeight = getHeight(); BrowserControlsContainerViewJni.get().setControlsSize( mNativeBrowserControlsContainerView, mLastWidth, mLastHeight); - if (mIsFullscreen) { - setFullscreenControlsOffset(); - } else { - setControlsOffset(0, mLastHeight); - } } - private void finishScroll(int contentOffsetY) { - mInScroll = false; - setControlsOffset(0, contentOffsetY); - if (BrowserControlsContainerViewJni.get().shouldDelayVisibilityChange()) { - mContentViewRenderView.postOnAnimation(() -> showControls()); - } else { - showControls(); - } + /** + * Destroys the cc::Layer containing the bitmap copy of the View. + */ + private void destroyLayer() { + if (mViewResourceAdapter == null) return; + // TODO: need some sort of destroy to drop reference. + mViewResourceAdapter = null; + BrowserControlsContainerViewJni.get().deleteControlsLayer( + mNativeBrowserControlsContainerView); + mContentViewRenderView.getResourceManager().getDynamicResourceLoader().unregisterResource( + getResourceId()); } private void setControlsOffset(int controlsOffsetY, int contentOffsetY) { // This function is called asynchronously from the gpu, and may be out of sync with the // current values. if (mIsTop) { - mControlsOffset = MathUtils.clamp(controlsOffsetY, -getHeight(), 0); + // Don't snap to min-height because the controls could be animating in from a + // previously lower min-height. + mControlsOffset = MathUtils.clamp(controlsOffsetY, -mLastHeight, 0); } else { - mControlsOffset = MathUtils.clamp(controlsOffsetY, 0, getHeight()); + mControlsOffset = MathUtils.clamp(controlsOffsetY, 0, mLastHeight); } - mContentOffset = MathUtils.clamp(contentOffsetY, 0, getHeight()); - if (isCompletelyShownOrHidden()) { - mListener.onBrowserControlsCompletelyShownOrHidden(); + mContentOffset = MathUtils.clamp(contentOffsetY, 0, mLastHeight); + + if (mView != null) { + mLastShownAmountWithView = + mIsTop ? (mLastHeight + mControlsOffset) : (mLastHeight - mControlsOffset); + } + if (isCompletelyExpandedOrCollapsed()) { + mDelegate.refreshPageHeight(); } if (mIsTop) { BrowserControlsContainerViewJni.get().setTopControlsOffset( @@ -378,21 +496,56 @@ class BrowserControlsContainerView extends FrameLayout { } } + private void reportHeightChange() { + if (mWebContents != null) { + mWebContents.notifyBrowserControlsHeightChanged(); + } + } + private void prepareForScroll() { mInScroll = true; + hideControls(); + } + + private void finishScroll() { + mInScroll = false; + showControls(); + } + + private void hideControls() { if (BrowserControlsContainerViewJni.get().shouldDelayVisibilityChange()) { - mContentViewRenderView.postOnAnimation(() -> hideControls()); + mContentViewRenderView.postOnAnimation(mHideControlsRunnable); } else { - hideControls(); + hideControlsNow(); } } - private void hideControls() { - if (mView != null) mView.setVisibility(View.INVISIBLE); + private void hideControlsNow() { + if (mView != null) { + mView.setVisibility(View.INVISIBLE); + } } private void showControls() { - if (mView != null) mView.setVisibility(View.VISIBLE); + if (BrowserControlsContainerViewJni.get().shouldDelayVisibilityChange()) { + mContentViewRenderView.postOnAnimation(mShowControlsRunnable); + } else { + showControlsNow(); + } + } + + private void showControlsNow() { + if (mView != null) { + if (mIsTop) { + mView.setTranslationY(mControlsOffset); + } + mView.setVisibility(View.VISIBLE); + } + } + + @CalledByNative + /* package */ boolean shouldAnimateBrowserControlsHeightChanges() { + return mShouldAnimate; } @CalledByNative @@ -401,6 +554,17 @@ class BrowserControlsContainerView extends FrameLayout { } @CalledByNative + private int getMinHeight() { + if (mAnimatingOut) return 0; + return Math.min(mLastHeight, mMinHeight); + } + + @CalledByNative + private boolean onlyExpandControlsAtPageTop() { + return mOnlyExpandControlsAtPageTop; + } + + @CalledByNative private void didToggleFullscreenModeForTab(final boolean isFullscreen) { // Delay hiding until after the animation. This comes from Chrome code. if (mSystemUiFullscreenResizeRunnable != null) { @@ -416,16 +580,16 @@ class BrowserControlsContainerView extends FrameLayout { if (mIsFullscreen == isFullscreen) return; mIsFullscreen = isFullscreen; if (mIsFullscreen) { + mAnimatingOut = false; hideControls(); - setFullscreenControlsOffset(); + moveControlsOffScreen(); } else { showControls(); setControlsOffset(0, mIsTop ? mLastHeight : 0); } } - private void setFullscreenControlsOffset() { - assert mIsFullscreen; + private void moveControlsOffScreen() { setControlsOffset(mIsTop ? -mLastHeight : mLastHeight, 0); } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java index 66433bde80a..c814e8cd2cc 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java @@ -14,10 +14,14 @@ import android.view.View; import android.view.ViewGroup; import android.webkit.ValueCallback; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import org.chromium.base.ObserverList; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; +import org.chromium.components.embedder_support.view.ContentView; import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.base.WindowAndroid; import org.chromium.weblayer_private.interfaces.APICallException; @@ -36,7 +40,7 @@ import java.util.List; * Implementation of {@link IBrowser}. */ @JNINamespace("weblayer") -public class BrowserImpl extends IBrowser.Stub { +public class BrowserImpl extends IBrowser.Stub implements View.OnAttachStateChangeListener { private final ObserverList<VisibleSecurityStateObserver> mVisibleSecurityStateObservers = new ObserverList<VisibleSecurityStateObserver>(); @@ -56,6 +60,10 @@ public class BrowserImpl extends IBrowser.Stub { private final ProfileImpl mProfile; private Context mEmbedderActivityContext; private BrowserViewController mViewController; + // Used to save UI state between destroyAttachmentState() and createAttachmentState() calls so + // it can be preserved during device rotations or other events that cause the Fragment to be + // recreated. + private BrowserViewController.State mViewControllerState; private FragmentWindowAndroid mWindowAndroid; private IBrowserClient mClient; private LocaleChangedBroadcastReceiver mLocaleReceiver; @@ -68,6 +76,7 @@ public class BrowserImpl extends IBrowser.Stub { private Boolean mPasswordEchoEnabled; private Boolean mDarkThemeEnabled; private Float mFontScale; + private boolean mViewAttachedToWindow; // Created in the constructor from saved state and used in setClient(). private PersistenceInfo mPersistenceInfo; @@ -118,7 +127,7 @@ public class BrowserImpl extends IBrowser.Stub { return mWindowAndroid; } - public ViewGroup getViewAndroidDelegateContainerView() { + public ContentView getViewAndroidDelegateContainerView() { if (mViewController == null) return null; return mViewController.getContentView(); } @@ -135,7 +144,8 @@ public class BrowserImpl extends IBrowser.Stub { assert mEmbedderActivityContext == null; mWindowAndroid = windowAndroid; mEmbedderActivityContext = embedderAppContext; - mViewController = new BrowserViewController(windowAndroid); + mViewController = new BrowserViewController( + windowAndroid, this, mViewControllerState, mFragmentStoppedForConfigurationChange); mLocaleReceiver = new LocaleChangedBroadcastReceiver(windowAndroid.getContext().get()); mPasswordEchoEnabled = null; } @@ -189,6 +199,20 @@ public class BrowserImpl extends IBrowser.Stub { } @Override + public void setTopViewAndScrollingBehavior(IObjectWrapper viewWrapper, int minHeight, + boolean onlyExpandControlsAtPageTop, boolean animate) { + StrictModeWorkaround.apply(); + if (minHeight < 0) { + throw new IllegalArgumentException("Top view min height must be non-negative."); + } + + getViewController().setTopControlsAnimationsEnabled(animate); + getViewController().setTopView(ObjectWrapper.unwrap(viewWrapper, View.class)); + getViewController().setTopControlsMinHeight(minHeight); + getViewController().setOnlyExpandTopControlsAtPageTop(onlyExpandControlsAtPageTop); + } + + @Override public void setBottomView(IObjectWrapper viewWrapper) { StrictModeWorkaround.apply(); getViewController().setBottomView(ObjectWrapper.unwrap(viewWrapper, View.class)); @@ -208,6 +232,8 @@ public class BrowserImpl extends IBrowser.Stub { (ValueCallback<Boolean>) ObjectWrapper.unwrap(valueCallback, ValueCallback.class)); } + // Only call this if it's guaranteed that Browser is attached to an activity. + @NonNull public BrowserViewController getViewController() { if (mViewController == null) { throw new RuntimeException("Currently Tab requires Activity context, so " @@ -216,6 +242,12 @@ public class BrowserImpl extends IBrowser.Stub { return mViewController; } + // Can be null in the middle of destroy, or if fragment is detached from activity. + @Nullable + public BrowserViewController getPossiblyNullViewController() { + return mViewController; + } + public Context getContext() { if (mWindowAndroid == null) { return null; @@ -368,7 +400,7 @@ public class BrowserImpl extends IBrowser.Stub { return true; } - public TabImpl getActiveTab() { + public @Nullable TabImpl getActiveTab() { return BrowserImplJni.get().getActiveTab(mNativeBrowser); } @@ -415,6 +447,7 @@ public class BrowserImpl extends IBrowser.Stub { destroyTabImpl((TabImpl) iTab); } + @CalledByNative private void destroyTabImpl(TabImpl tab) { tab.destroy(); } @@ -459,9 +492,6 @@ public class BrowserImpl extends IBrowser.Stub { public void onFragmentStop(boolean forConfigurationChange) { mFragmentStoppedForConfigurationChange = forConfigurationChange; mFragmentStarted = false; - if (mFragmentStoppedForConfigurationChange) { - destroyAttachmentState(); - } updateAllTabs(); } @@ -488,14 +518,41 @@ public class BrowserImpl extends IBrowser.Stub { return mFragmentStoppedForConfigurationChange; } + public boolean isViewAttachedToWindow() { + return mViewAttachedToWindow; + } + + @Override + public void onViewAttachedToWindow(View v) { + mViewAttachedToWindow = true; + updateAllTabsViewAttachedState(); + } + + @Override + public void onViewDetachedFromWindow(View v) { + // Note this separate state is needed because v.isAttachedToWindow() + // still returns true inside this call. + mViewAttachedToWindow = false; + updateAllTabsViewAttachedState(); + } + + private void updateAllTabsViewAttachedState() { + for (Object tab : getTabs()) { + ((TabImpl) tab).updateViewAttachedStateFromBrowser(); + } + } + private void destroyAttachmentState() { if (mLocaleReceiver != null) { mLocaleReceiver.destroy(); mLocaleReceiver = null; } if (mViewController != null) { + mViewControllerState = mViewController.getState(); mViewController.destroy(); mViewController = null; + mViewAttachedToWindow = false; + updateAllTabsViewAttachedState(); } if (mWindowAndroid != null) { mWindowAndroid.destroy(); diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java index 436113adde3..fd22fffcfb8 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java @@ -15,10 +15,14 @@ import android.webkit.ValueCallback; import android.widget.FrameLayout; import android.widget.RelativeLayout; +import androidx.annotation.Nullable; + import org.chromium.base.annotations.JNINamespace; import org.chromium.components.browser_ui.modaldialog.AppModalPresenter; +import org.chromium.components.browser_ui.widget.InsetObserverView; import org.chromium.components.embedder_support.view.ContentView; import org.chromium.content_public.browser.WebContents; +import org.chromium.content_public.common.BrowserControlsState; import org.chromium.ui.modaldialog.DialogDismissalCause; import org.chromium.ui.modaldialog.ModalDialogManager; import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType; @@ -31,9 +35,21 @@ import org.chromium.ui.modelutil.PropertyModel; */ @JNINamespace("weblayer") public final class BrowserViewController - implements BrowserControlsContainerView.Listener, + implements BrowserControlsContainerView.Delegate, WebContentsGestureStateTracker.OnGestureStateChangedListener, ModalDialogManager.ModalDialogManagerObserver { + /** Information needed to restore the UI state after recreating the BrowserViewController. */ + /* package */ static class State { + private BrowserControlsContainerView.State mTopControlsState; + private BrowserControlsContainerView.State mBottomControlsState; + + private State(BrowserControlsContainerView.State topControlsState, + BrowserControlsContainerView.State bottomControlsState) { + mTopControlsState = topControlsState; + mBottomControlsState = bottomControlsState; + } + } + private final ContentViewRenderView mContentViewRenderView; // Child of mContentViewRenderView. Be very careful adding Views to this, as any Views are not // accessible (ContentView provides it's own accessible implementation that interacts with @@ -53,12 +69,16 @@ public final class BrowserViewController RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); private final FragmentWindowAndroid mWindowAndroid; + private final View.OnAttachStateChangeListener mOnAttachedStateChangeListener; private final ModalDialogManager mModalDialogManager; private TabImpl mTab; private WebContentsGestureStateTracker mGestureStateTracker; + @BrowserControlsState + private int mBrowserControlsConstraint = BrowserControlsState.BOTH; + /** * The value of mCachedDoBrowserControlsShrinkRendererSize is set when * WebContentsGestureStateTracker begins a gesture. This is necessary as the values should only @@ -66,18 +86,24 @@ public final class BrowserViewController */ private boolean mCachedDoBrowserControlsShrinkRendererSize; - public BrowserViewController(FragmentWindowAndroid windowAndroid) { + public BrowserViewController(FragmentWindowAndroid windowAndroid, + View.OnAttachStateChangeListener listener, @Nullable State savedState, + boolean recreateForConfigurationChange) { mWindowAndroid = windowAndroid; + mOnAttachedStateChangeListener = listener; Context context = mWindowAndroid.getContext().get(); - mContentViewRenderView = new ContentViewRenderView(context); + mContentViewRenderView = new ContentViewRenderView(context, recreateForConfigurationChange); + mContentViewRenderView.addOnAttachStateChangeListener(listener); mContentViewRenderView.onNativeLibraryLoaded( mWindowAndroid, ContentViewRenderView.MODE_SURFACE_VIEW); mTopControlsContainerView = - new BrowserControlsContainerView(context, mContentViewRenderView, this, true); + new BrowserControlsContainerView(context, mContentViewRenderView, this, true, + (savedState == null) ? null : savedState.mTopControlsState); mTopControlsContainerView.setId(View.generateViewId()); mBottomControlsContainerView = - new BrowserControlsContainerView(context, mContentViewRenderView, this, false); + new BrowserControlsContainerView(context, mContentViewRenderView, this, false, + (savedState == null) ? null : savedState.mBottomControlsState); mBottomControlsContainerView.setId(View.generateViewId()); mContentView = ContentView.createContentView( context, mTopControlsContainerView.getEventOffsetHandler(), null /* webContents */); @@ -116,6 +142,7 @@ public final class BrowserViewController public void destroy() { mWindowAndroid.setModalDialogManager(null); setActiveTab(null); + mContentViewRenderView.removeOnAttachStateChangeListener(mOnAttachedStateChangeListener); mTopControlsContainerView.destroy(); mBottomControlsContainerView.destroy(); mContentViewRenderView.destroy(); @@ -126,12 +153,16 @@ public final class BrowserViewController return mContentViewRenderView; } + public InsetObserverView getInsetObserverView() { + return mContentViewRenderView.getInsetObserverView(); + } + /** Returns the ViewGroup into which the InfoBarContainer should be parented. **/ public ViewGroup getInfoBarContainerParentView() { return mContentViewRenderView; } - public ViewGroup getContentView() { + public ContentView getContentView() { return mContentView; } @@ -156,6 +187,8 @@ public final class BrowserViewController if (mTab != null) { mTab.onDidLoseActive(); + mTab.setBrowserControlsVisibilityConstraint( + ImplControlsVisibilityReason.ANIMATION, BrowserControlsState.BOTH); // WebContentsGestureStateTracker is relatively cheap, easier to destroy rather than // update WebContents. mGestureStateTracker.destroy(); @@ -180,6 +213,8 @@ public final class BrowserViewController mTopControlsContainerView.setWebContents(webContents); mBottomControlsContainerView.setWebContents(webContents); if (mTab != null) { + mTab.setBrowserControlsVisibilityConstraint( + ImplControlsVisibilityReason.ANIMATION, mBrowserControlsConstraint); mTab.onDidGainActive(mTopControlsContainerView.getNativeHandle(), mBottomControlsContainerView.getNativeHandle()); mContentView.requestFocus(); @@ -194,6 +229,18 @@ public final class BrowserViewController mTopControlsContainerView.setView(view); } + public void setTopControlsMinHeight(int minHeight) { + mTopControlsContainerView.setMinHeight(minHeight); + } + + public void setOnlyExpandTopControlsAtPageTop(boolean onlyExpandControlsAtPageTop) { + mTopControlsContainerView.setOnlyExpandControlsAtPageTop(onlyExpandControlsAtPageTop); + } + + public void setTopControlsAnimationsEnabled(boolean animationsEnabled) { + mTopControlsContainerView.setAnimationsEnabled(animationsEnabled); + } + public void setBottomView(View view) { mBottomControlsContainerView.setView(view); } @@ -211,11 +258,19 @@ public final class BrowserViewController } @Override - public void onBrowserControlsCompletelyShownOrHidden() { + public void refreshPageHeight() { adjustWebContentsHeightIfNecessary(); } @Override + public void setAnimationConstraint(@BrowserControlsState int constraint) { + mBrowserControlsConstraint = constraint; + if (mTab == null) return; + mTab.setBrowserControlsVisibilityConstraint( + ImplControlsVisibilityReason.ANIMATION, constraint); + } + + @Override public void onGestureStateChanged() { // This is called from |mGestureStateTracker|. assert mGestureStateTracker != null; @@ -237,6 +292,11 @@ public final class BrowserViewController onDialogVisibilityChanged(false); } + /* package */ State getState() { + return new State( + mTopControlsContainerView.getState(), mBottomControlsContainerView.getState()); + } + private void onDialogVisibilityChanged(boolean showing) { if (WebLayerFactoryImpl.getClientMajorVersion() < 82) return; @@ -256,8 +316,8 @@ public final class BrowserViewController private void adjustWebContentsHeightIfNecessary() { if (mGestureStateTracker == null || mGestureStateTracker.isInGestureOrScroll() - || !mTopControlsContainerView.isCompletelyShownOrHidden() - || !mBottomControlsContainerView.isCompletelyShownOrHidden()) { + || !mTopControlsContainerView.isCompletelyExpandedOrCollapsed() + || !mBottomControlsContainerView.isCompletelyExpandedOrCollapsed()) { return; } mContentViewRenderView.setWebContentsHeightDelta( @@ -289,6 +349,10 @@ public final class BrowserViewController || mBottomControlsContainerView.isControlVisible()); } + public boolean shouldAnimateBrowserControlsHeightChanges() { + return mTopControlsContainerView.shouldAnimateBrowserControlsHeightChanges(); + } + /** * Causes the browser controls to be fully shown. Take care in calling this. Normally the * renderer drives the offsets, but this method circumvents that. diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ChildProcessServiceImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ChildProcessServiceImpl.java index 599fd0dbc9d..71788e169fd 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ChildProcessServiceImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ChildProcessServiceImpl.java @@ -25,12 +25,6 @@ import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; public final class ChildProcessServiceImpl extends IChildProcessService.Stub { private ChildProcessService mService; - // This should only be called in M80 or below. - @UsedByReflection("WebLayer") - public static IBinder create(Service service, Context appContext) { - return create(service, appContext, WebLayerImpl.createRemoteContextV80(appContext)); - } - @UsedByReflection("WebLayer") public static IBinder create(Service service, Context appContext, Context remoteContext) { ClassLoaderContextWrapperFactory.setResourceOverrideContext(remoteContext); diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ConfirmInfoBar.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ConfirmInfoBar.java deleted file mode 100644 index 4b49a4e8ebf..00000000000 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ConfirmInfoBar.java +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.weblayer_private; - -import android.graphics.Bitmap; - -import androidx.annotation.ColorRes; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.components.infobars.InfoBarLayout; - -/** - * An infobar that presents the user with several buttons. - * - * TODO(newt): merge this into InfoBar.java. - */ -public class ConfirmInfoBar extends InfoBar { - /** Text shown on the primary button, e.g. "OK". */ - private final String mPrimaryButtonText; - - /** Text shown on the secondary button, e.g. "Cancel".*/ - private final String mSecondaryButtonText; - - /** Text shown on the link, e.g. "Learn more". */ - private final String mLinkText; - - protected ConfirmInfoBar(int iconDrawableId, @ColorRes int iconTintId, Bitmap iconBitmap, - String message, String linkText, String primaryButtonText, String secondaryButtonText) { - super(iconDrawableId, iconTintId, message, iconBitmap); - mPrimaryButtonText = primaryButtonText; - mSecondaryButtonText = secondaryButtonText; - mLinkText = linkText; - } - - @Override - public void createContent(InfoBarLayout layout) { - setButtons(layout, mPrimaryButtonText, mSecondaryButtonText); - if (mLinkText != null && !mLinkText.isEmpty()) layout.appendMessageLinkText(mLinkText); - } - - /** - * If your custom infobar overrides this function, YOU'RE PROBABLY DOING SOMETHING WRONG. - * - * Adds buttons to the infobar. This should only be overridden in cases where an infobar - * requires adding something other than a button for its secondary View on the bottom row - * (almost never). - * - * @param primaryText Text to display on the primary button. - * @param secondaryText Text to display on the secondary button. May be null. - */ - protected void setButtons(InfoBarLayout layout, String primaryText, String secondaryText) { - layout.setButtons(primaryText, secondaryText); - } - - @Override - public void onButtonClicked(final boolean isPrimaryButton) { - int action = isPrimaryButton ? ActionType.OK : ActionType.CANCEL; - onButtonClicked(action); - } - - /** - * Creates and begins the process for showing a ConfirmInfoBar. - * @param iconId ID corresponding to the icon that will be shown for the infobar. - * @param iconBitmap Bitmap to use if there is no equivalent Java resource for - * iconId. - * @param message Message to display to the user indicating what the infobar is for. - * @param linkText Link text to display in addition to the message. - * @param buttonOk String to display on the OK button. - * @param buttonCancel String to display on the Cancel button. - */ - @CalledByNative - private static ConfirmInfoBar create(int iconId, Bitmap iconBitmap, String message, - String linkText, String buttonOk, String buttonCancel) { - ConfirmInfoBar infoBar = new ConfirmInfoBar( - iconId, 0, iconBitmap, message, linkText, buttonOk, buttonCancel); - - return infoBar; - } -} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java index ac0235eaeb2..d5d471c5c57 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java @@ -5,9 +5,13 @@ package org.chromium.weblayer_private; import android.content.Context; +import android.content.res.Configuration; import android.graphics.Color; import android.graphics.PixelFormat; +import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.os.SystemClock; +import android.util.Size; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; @@ -25,9 +29,11 @@ import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; import org.chromium.base.task.PostTask; +import org.chromium.components.browser_ui.widget.InsetObserverView; import org.chromium.content_public.browser.UiThreadTaskTraits; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.WindowAndroid; +import org.chromium.ui.display.DisplayAndroid; import org.chromium.ui.resources.ResourceManager; import java.lang.annotation.Retention; @@ -49,6 +55,8 @@ public class ContentViewRenderView extends RelativeLayout { public static final int MODE_SURFACE_VIEW = 0; public static final int MODE_TEXTURE_VIEW = 1; + private static final int CONFIG_TIMEOUT_MS = 1000; + // A child view of this class. Parent of SurfaceView/TextureView. // Needed to support not resizing the surface when soft keyboard is showing. private final SurfaceParent mSurfaceParent; @@ -62,6 +70,9 @@ public class ContentViewRenderView extends RelativeLayout { // The native side of this object. private long mNativeContentViewRenderView; + // An invisible view that notifies observers of changes to window insets and safe area. + private InsetObserverView mInsetObserverView; + private WindowAndroid mWindowAndroid; private WebContents mWebContents; @@ -79,6 +90,13 @@ public class ContentViewRenderView extends RelativeLayout { private boolean mCompositorHasSurface; + private DisplayAndroid.DisplayAndroidObserver mDisplayAndroidObserver; + + // The time stamp when a configuration was detected (if any). + // This is used along with a timeout to determine if a resize surface resize + // is due to screen rotation. + private long mConfigurationChangedTimestamp; + // Common interface to listen to surface related events. private interface SurfaceEventListener { void surfaceCreated(); @@ -147,10 +165,7 @@ public class ContentViewRenderView extends RelativeLayout { ContentViewRenderViewJni.get().surfaceChanged(mNativeContentViewRenderView, canBeUsedWithSurfaceControl, format, width, height, surface); mCompositorHasSurface = surface != null; - if (mWebContents != null) { - ContentViewRenderViewJni.get().onPhysicalBackingSizeChanged( - mNativeContentViewRenderView, mWebContents, width, height); - } + maybeUpdatePhysicalBackingSize(width, height); } @Override @@ -610,13 +625,30 @@ public class ContentViewRenderView extends RelativeLayout { * hierarchy before the first draw to avoid a black flash that is seen every time a * {@link SurfaceView} is added. * @param context The context used to create this. + * @param recreateForConfigurationChange indicates that views are recreated after BrowserImpl + * is retained, but Activity is recreated, for a + * configuration change. */ - public ContentViewRenderView(Context context) { + public ContentViewRenderView(Context context, boolean recreateForConfigurationChange) { super(context); mSurfaceParent = new SurfaceParent(context); addView(mSurfaceParent, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); - setBackgroundColor(Color.WHITE); + + mInsetObserverView = InsetObserverView.create(context); + addView(mInsetObserverView); + mInsetObserverView.addObserver(new InsetObserverView.WindowInsetObserver() { + @Override + public void onInsetChanged(int left, int top, int right, int bottom) { + if (mWebContents != null && mWebContents.isFullscreenForCurrentTab()) { + updateWebContentsSize(); + } + } + + @Override + public void onSafeAreaChanged(Rect area) {} + }); + if (recreateForConfigurationChange) updateConfigChangeTimeStamp(); } /** @@ -631,6 +663,14 @@ public class ContentViewRenderView extends RelativeLayout { assert mNativeContentViewRenderView != 0; mWindowAndroid = rootWindow; requestMode(mode, (Boolean result) -> {}); + mDisplayAndroidObserver = new DisplayAndroid.DisplayAndroidObserver() { + @Override + public void onRotationChanged(int rotation) { + updateConfigChangeTimeStamp(); + } + }; + mWindowAndroid.getDisplay().addObserver(mDisplayAndroidObserver); + updateBackgroundColor(); } public void requestMode(@Mode int mode, ValueCallback<Boolean> callback) { @@ -665,7 +705,21 @@ public class ContentViewRenderView extends RelativeLayout { private void updateWebContentsSize() { if (mWebContents == null) return; - mWebContents.setSize(getWidth(), getHeight() - mWebContentsHeightDelta); + Size size = getViewportSize(); + mWebContents.setSize(size.getWidth(), size.getHeight() - mWebContentsHeightDelta); + } + + /** {@link CompositorViewHolder#getViewportSize()} for explanation. */ + private Size getViewportSize() { + if (mWebContents.isFullscreenForCurrentTab() + && mWindowAndroid.getKeyboardDelegate().isKeyboardShowing(getContext(), this)) { + Rect visibleRect = new Rect(); + getWindowVisibleDisplayFrame(visibleRect); + return new Size(Math.min(visibleRect.width(), getWidth()), + Math.min(visibleRect.height(), getHeight())); + } + + return new Size(getWidth(), getHeight()); } @Override @@ -689,6 +743,12 @@ public class ContentViewRenderView extends RelativeLayout { } } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + updateBackgroundColor(); + } + /** * Sets the background color of the surface / texture view. This method is necessary because * the background color of ContentViewRenderView itself is covered by the background of @@ -705,6 +765,11 @@ public class ContentViewRenderView extends RelativeLayout { if (mCurrent != null) { mCurrent.setBackgroundColor(color); } + ContentViewRenderViewJni.get().updateBackgroundColor(mNativeContentViewRenderView); + } + + public InsetObserverView getInsetObserverView() { + return mInsetObserverView; } /** @@ -723,6 +788,10 @@ public class ContentViewRenderView extends RelativeLayout { mRequested = null; mCurrent = null; + if (mDisplayAndroidObserver != null) { + mWindowAndroid.getDisplay().removeObserver(mDisplayAndroidObserver); + mDisplayAndroidObserver = null; + } mWindowAndroid = null; while (!mPendingRunnables.isEmpty()) { @@ -739,12 +808,9 @@ public class ContentViewRenderView extends RelativeLayout { assert mNativeContentViewRenderView != 0; mWebContents = webContents; - if (webContents != null) { - if (getWidth() != 0 && getHeight() != 0) { - updateWebContentsSize(); - } - ContentViewRenderViewJni.get().onPhysicalBackingSizeChanged( - mNativeContentViewRenderView, webContents, mPhysicalWidth, mPhysicalHeight); + if (webContents != null && getWidth() != 0 && getHeight() != 0) { + updateWebContentsSize(); + maybeUpdatePhysicalBackingSize(mPhysicalWidth, mPhysicalHeight); } ContentViewRenderViewJni.get().setCurrentWebContents( mNativeContentViewRenderView, webContents); @@ -780,6 +846,18 @@ public class ContentViewRenderView extends RelativeLayout { return mNativeContentViewRenderView; } + private void updateBackgroundColor() { + int uiMode = getContext().getResources().getConfiguration().uiMode; + boolean darkThemeEnabled = + (uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; + setBackgroundColor(darkThemeEnabled ? Color.BLACK : Color.WHITE); + } + + @CalledByNative + private int getBackgroundColor() { + return mBackgroundColor; + } + private boolean shouldAvoidSurfaceResizeForSoftKeyboard() { // TextureView is more common with embedding use cases that should lead to resize. boolean usingSurfaceView = mCurrent != null && mCurrent.getMode() == MODE_SURFACE_VIEW; @@ -793,13 +871,25 @@ public class ContentViewRenderView extends RelativeLayout { return inputMethodManager.isActive(); } + private void updateConfigChangeTimeStamp() { + mConfigurationChangedTimestamp = SystemClock.uptimeMillis(); + } + + private void maybeUpdatePhysicalBackingSize(int width, int height) { + if (mWebContents == null) return; + boolean forConfigChange = + SystemClock.uptimeMillis() - mConfigurationChangedTimestamp < CONFIG_TIMEOUT_MS; + ContentViewRenderViewJni.get().onPhysicalBackingSizeChanged( + mNativeContentViewRenderView, mWebContents, width, height, forConfigChange); + } + @NativeMethods interface Natives { long init(ContentViewRenderView caller, WindowAndroid rootWindow); void destroy(long nativeContentViewRenderView); void setCurrentWebContents(long nativeContentViewRenderView, WebContents webContents); - void onPhysicalBackingSizeChanged( - long nativeContentViewRenderView, WebContents webContents, int width, int height); + void onPhysicalBackingSizeChanged(long nativeContentViewRenderView, WebContents webContents, + int width, int height, boolean forConfigChange); void surfaceCreated(long nativeContentViewRenderView); void surfaceDestroyed(long nativeContentViewRenderView, boolean cacheBackBuffer); void surfaceChanged(long nativeContentViewRenderView, boolean canBeUsedWithSurfaceControl, @@ -807,5 +897,6 @@ public class ContentViewRenderView extends RelativeLayout { void setNeedsRedraw(long nativeContentViewRenderView); void evictCachedSurface(long nativeContentViewRenderView); ResourceManager getResourceManager(long nativeContentViewRenderView); + void updateBackgroundColor(long nativeContentViewRenderView); } } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadCallbackProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadCallbackProxy.java index 8ff0107000e..defbe1e18dd 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadCallbackProxy.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadCallbackProxy.java @@ -9,6 +9,7 @@ import android.content.pm.PackageManager; import android.os.RemoteException; import android.webkit.ValueCallback; +import org.chromium.base.ThreadUtils; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; @@ -64,7 +65,7 @@ public final class DownloadCallbackProxy { String[] requestPermissions = new String[] {permission.WRITE_EXTERNAL_STORAGE}; window.requestPermissions(requestPermissions, (permissions, grantResults) -> { - if (grantResults[0] == PackageManager.PERMISSION_DENIED) { + if (grantResults.length == 0 || grantResults[0] == PackageManager.PERMISSION_DENIED) { DownloadCallbackProxyJni.get().allowDownload(callbackId, false); return; } @@ -91,6 +92,7 @@ public final class DownloadCallbackProxy { ValueCallback<Boolean> callback = new ValueCallback<Boolean>() { @Override public void onReceiveValue(Boolean result) { + ThreadUtils.assertOnUiThread(); if (mNativeDownloadCallbackProxy == 0) { throw new IllegalStateException("Called after destroy()"); } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java index b1046065bc3..cdb14ccbddf 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java @@ -347,7 +347,7 @@ public final class DownloadImpl extends IDownload.Stub { ? WebLayerNotificationChannels.ChannelId.COMPLETED_DOWNLOADS : WebLayerNotificationChannels.ChannelId.ACTIVE_DOWNLOADS; - WebLayerNotificationBuilder builder = WebLayerNotificationBuilder.create( + WebLayerNotificationWrapperBuilder builder = WebLayerNotificationWrapperBuilder.create( channelId, new NotificationMetadata(0, NOTIFICATION_TAG, mNotificationId)); builder.setOngoing(true) .setDeleteIntent(deletePendingIntent) @@ -450,7 +450,7 @@ public final class DownloadImpl extends IDownload.Stub { cancelPendingIntent, 0 /* no action for UMA */); } - notificationManager.notify(builder.buildChromeNotification()); + notificationManager.notify(builder.buildNotificationWrapper()); } /** diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ErrorPageCallbackProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ErrorPageCallbackProxy.java index 639a2003568..f1fd12a6753 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ErrorPageCallbackProxy.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ErrorPageCallbackProxy.java @@ -43,6 +43,12 @@ public final class ErrorPageCallbackProxy { return mClient.onBackToSafety(); } + @CalledByNative + private String getErrorPageContent(NavigationImpl navigation) throws RemoteException { + if (WebLayerFactoryImpl.getClientMajorVersion() < 86) return null; + return mClient.getErrorPageContent(navigation.getClientNavigation()); + } + @NativeMethods interface Natives { long createErrorPageCallbackProxy(ErrorPageCallbackProxy proxy, long tab); diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java index 253c35212d0..fdf5a8e3775 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ExternalNavigationDelegateImpl.java @@ -4,22 +4,18 @@ package org.chromium.weblayer_private; -import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import androidx.annotation.Nullable; -import org.chromium.base.ContextUtils; import org.chromium.base.PackageManagerUtils; import org.chromium.components.embedder_support.util.UrlUtilities; import org.chromium.components.external_intents.ExternalNavigationDelegate; import org.chromium.components.external_intents.ExternalNavigationDelegate.StartActivityIfNeededResult; -import org.chromium.components.external_intents.ExternalNavigationHandler; import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResult; import org.chromium.components.external_intents.ExternalNavigationParams; -import org.chromium.components.webapk.lib.client.ChromeWebApkHostSignature; -import org.chromium.components.webapk.lib.client.WebApkValidator; import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.WindowAndroid; @@ -29,7 +25,6 @@ import org.chromium.url.Origin; * WebLayer's implementation of the {@link ExternalNavigationDelegate}. */ public class ExternalNavigationDelegateImpl implements ExternalNavigationDelegate { - private static boolean sWebApkValidatorInitialized; private final TabImpl mTab; private boolean mTabDestroyed; @@ -43,8 +38,8 @@ public class ExternalNavigationDelegateImpl implements ExternalNavigationDelegat } @Override - public Activity getActivityContext() { - return ContextUtils.activityFromContext(mTab.getBrowser().getContext()); + public Context getContext() { + return mTab.getBrowser().getContext(); } @Override @@ -92,14 +87,6 @@ public class ExternalNavigationDelegateImpl implements ExternalNavigationDelegat return StartActivityIfNeededResult.DID_NOT_HANDLE; } - @Override - public boolean startIncognitoIntent(final Intent intent, final String referrerUrl, - final String fallbackUrl, final boolean needsToCloseTab, final boolean proxy) { - // TODO(crbug.com/1063399): Determine if this behavior should be refined. - ExternalNavigationHandler.startActivity(intent, proxy, this); - return true; - } - // This method should never be invoked as WebLayer does not handle incoming intents. @Override public @OverrideUrlLoadingResult int handleIncognitoIntentTargetingSelf( @@ -196,6 +183,11 @@ public class ExternalNavigationDelegateImpl implements ExternalNavigationDelegat } @Override + public boolean canCloseTabOnIncognitoIntentLaunch() { + return hasValidTab(); + } + + @Override public boolean isIntentForTrustedCallingApp(Intent intent) { return false; } @@ -211,16 +203,6 @@ public class ExternalNavigationDelegateImpl implements ExternalNavigationDelegat } @Override - public boolean isValidWebApk(String packageName) { - if (!sWebApkValidatorInitialized) { - WebApkValidator.init(ChromeWebApkHostSignature.EXPECTED_SIGNATURE, - ChromeWebApkHostSignature.PUBLIC_KEY); - sWebApkValidatorInitialized = true; - } - return WebApkValidator.isValidWebApk(ContextUtils.getApplicationContext(), packageName); - } - - @Override public boolean handleWithAutofillAssistant(ExternalNavigationParams params, Intent targetIntent, String browserFallbackUrl, boolean isGoogleReferrer) { return false; diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/FaviconCallbackProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FaviconCallbackProxy.java new file mode 100644 index 00000000000..3c74a6dcd1b --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FaviconCallbackProxy.java @@ -0,0 +1,64 @@ +// 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.weblayer_private; + +import android.graphics.Bitmap; +import android.os.RemoteException; +import android.util.AndroidRuntimeException; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; +import org.chromium.weblayer_private.interfaces.IFaviconFetcher; +import org.chromium.weblayer_private.interfaces.IFaviconFetcherClient; + +/** + * Owns the c++ ErrorPageCallbackProxy class, which is responsible for forwarding all + * ErrorPageDelegate calls to this class, which in turn forwards to the + * ErrorPageCallbackClient. + */ +@JNINamespace("weblayer") +public final class FaviconCallbackProxy extends IFaviconFetcher.Stub { + private TabImpl mTab; + private long mNativeFaviconCallbackProxy; + private IFaviconFetcherClient mClient; + + FaviconCallbackProxy(TabImpl tab, long nativeTab, IFaviconFetcherClient client) { + assert client != null; + mTab = tab; + mClient = client; + mNativeFaviconCallbackProxy = + FaviconCallbackProxyJni.get().createFaviconCallbackProxy(this, nativeTab); + } + + @Override + public void destroy() { + // As Tab implicitly destroys this, and the embedder is allowed to destroy this, allow + // destroy() to be called multiple times. + if (mNativeFaviconCallbackProxy == 0) { + return; + } + mTab.removeFaviconCallbackProxy(this); + try { + mClient.onDestroyed(); + } catch (RemoteException e) { + throw new AndroidRuntimeException(e); + } + FaviconCallbackProxyJni.get().deleteFaviconCallbackProxy(mNativeFaviconCallbackProxy); + mNativeFaviconCallbackProxy = 0; + mClient = null; + } + + @CalledByNative + private void onFaviconChanged(Bitmap bitmap) throws RemoteException { + mClient.onFaviconChanged(bitmap); + } + + @NativeMethods + interface Natives { + long createFaviconCallbackProxy(FaviconCallbackProxy proxy, long tab); + void deleteFaviconCallbackProxy(long proxy); + } +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/FullscreenCallbackProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FullscreenCallbackProxy.java index 5a631624551..88fe5da222d 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/FullscreenCallbackProxy.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FullscreenCallbackProxy.java @@ -7,6 +7,9 @@ package org.chromium.weblayer_private; import android.os.RemoteException; import android.webkit.ValueCallback; +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.ThreadUtils; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; @@ -22,12 +25,15 @@ import org.chromium.weblayer_private.interfaces.ObjectWrapper; public final class FullscreenCallbackProxy { private long mNativeFullscreenCallbackProxy; private IFullscreenCallbackClient mClient; + private TabImpl mTab; + private FullscreenToast mToast; - FullscreenCallbackProxy(long tab, IFullscreenCallbackClient client) { + FullscreenCallbackProxy(TabImpl tab, long nativeTab, IFullscreenCallbackClient client) { assert client != null; mClient = client; + mTab = tab; mNativeFullscreenCallbackProxy = - FullscreenCallbackProxyJni.get().createFullscreenCallbackProxy(this, tab); + FullscreenCallbackProxyJni.get().createFullscreenCallbackProxy(this, nativeTab); } public void setClient(IFullscreenCallbackClient client) { @@ -39,6 +45,19 @@ public final class FullscreenCallbackProxy { FullscreenCallbackProxyJni.get().deleteFullscreenCallbackProxy( mNativeFullscreenCallbackProxy); mNativeFullscreenCallbackProxy = 0; + destroyToast(); + mTab = null; + } + + public void destroyToast() { + if (mToast == null) return; + mToast.destroy(); + mToast = null; + } + + @VisibleForTesting + public boolean didShowFullscreenToast() { + return mToast != null && mToast.didShowFullscreenToast(); } @CalledByNative @@ -46,18 +65,23 @@ public final class FullscreenCallbackProxy { ValueCallback<Void> exitFullscreenCallback = new ValueCallback<Void>() { @Override public void onReceiveValue(Void result) { + ThreadUtils.assertOnUiThread(); if (mNativeFullscreenCallbackProxy == 0) { throw new IllegalStateException("Called after destroy()"); } + destroyToast(); FullscreenCallbackProxyJni.get().doExitFullscreen(mNativeFullscreenCallbackProxy); } }; + destroyToast(); + mToast = new FullscreenToast(mTab); mClient.enterFullscreen(ObjectWrapper.wrap(exitFullscreenCallback)); } @CalledByNative private void exitFullscreen() throws RemoteException { mClient.exitFullscreen(); + destroyToast(); } @NativeMethods diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/FullscreenToast.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FullscreenToast.java new file mode 100644 index 00000000000..1133555a7a6 --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FullscreenToast.java @@ -0,0 +1,103 @@ +// 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.weblayer_private; + +import android.view.Gravity; +import android.view.View; + +import androidx.annotation.VisibleForTesting; + +import org.chromium.components.embedder_support.view.ContentView; +import org.chromium.ui.widget.Toast; + +/** + * FullscreenToast is responsible for showing toast when fullscreen mode is entered. As the embedder + * is responsible for entering fullscreen mode, there is no guarantee when or if fullscreen mode + * will be entered. This waits for the system to enter fullscreen mode and then show the toast. If + * fullscreen isn't entered after a short delay this assumes the embedder won't enter fullscreen + * and the toast is never shown. + */ +public final class FullscreenToast { + // The tab the toast is showing from. + private TabImpl mTab; + + // View used to register for system ui change notification. + private ContentView mView; + + private View.OnSystemUiVisibilityChangeListener mSystemUiVisibilityChangeListener; + + // Set to true once toast is shown. + private boolean mDidShowToast; + + // The toast. + private Toast mToast; + + FullscreenToast(TabImpl tab) { + mTab = tab; + // TODO(https://crbug.com/1130096): This should really be handled lower down in the stack. + if (tab.getBrowser().getActiveTab() != tab) return; + addSystemUiChangedObserver(); + } + + @VisibleForTesting + public boolean didShowFullscreenToast() { + return mDidShowToast; + } + + public void destroy() { + // This may be called more than once. + if (mTab == null) return; + + if (mSystemUiVisibilityChangeListener != null) { + // mSystemUiVisibilityChangeListener is only installed if mView is non-null. + assert mView != null; + mView.removeOnSystemUiVisibilityChangeListener(mSystemUiVisibilityChangeListener); + mSystemUiVisibilityChangeListener = null; + } + mTab = null; + mView = null; + if (mToast != null) { + mToast.cancel(); + mToast = null; + } + } + + private void addSystemUiChangedObserver() { + if (mTab.getBrowser().getViewAndroidDelegateContainerView() == null) { + return; + } + mView = mTab.getBrowser().getViewAndroidDelegateContainerView(); + mSystemUiVisibilityChangeListener = new View.OnSystemUiVisibilityChangeListener() { + @Override + public void onSystemUiVisibilityChange(int visibility) { + // The listener should have been removed if destroy() was called. + assert mTab != null; + if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { + // No longer in fullscreen. Destroy. + destroy(); + } else if ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0 + && !mDidShowToast) { + // Only show the toast when navigation is hidden and toast wasn't already shown. + showToast(); + mDidShowToast = true; + } + } + }; + mView.addOnSystemUiVisibilityChangeListener(mSystemUiVisibilityChangeListener); + // See class description for details on why a timeout is used. + mView.postDelayed(() -> { + if (!mDidShowToast) destroy(); + }, 1000); + } + + private void showToast() { + assert mToast == null; + mDidShowToast = true; + int resId = R.string.immersive_fullscreen_api_notification; + mToast = Toast.makeText(mView.getContext(), resId, Toast.LENGTH_LONG); + mToast.setGravity(Gravity.TOP | Gravity.CENTER, 0, 0); + mToast.show(); + } +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/GoogleAccountsCallbackProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/GoogleAccountsCallbackProxy.java new file mode 100644 index 00000000000..25b12a730bf --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/GoogleAccountsCallbackProxy.java @@ -0,0 +1,77 @@ +// 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.weblayer_private; + +import android.os.RemoteException; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; +import org.chromium.components.signin.GAIAServiceType; +import org.chromium.weblayer_private.interfaces.GoogleAccountServiceType; +import org.chromium.weblayer_private.interfaces.IGoogleAccountsCallbackClient; + +/** + * Owns the C++ GoogleAccountsCallbackProxy which is responsible for forwarding all calls to this + * class. + */ +@JNINamespace("weblayer") +public final class GoogleAccountsCallbackProxy { + private long mNativeGoogleAccountsCallbackProxy; + private IGoogleAccountsCallbackClient mClient; + + GoogleAccountsCallbackProxy(long tab, IGoogleAccountsCallbackClient client) { + assert client != null; + mClient = client; + mNativeGoogleAccountsCallbackProxy = + GoogleAccountsCallbackProxyJni.get().createGoogleAccountsCallbackProxy(this, tab); + } + + public void setClient(IGoogleAccountsCallbackClient client) { + assert client != null; + mClient = client; + } + + public void destroy() { + GoogleAccountsCallbackProxyJni.get().deleteGoogleAccountsCallbackProxy( + mNativeGoogleAccountsCallbackProxy); + mNativeGoogleAccountsCallbackProxy = 0; + } + + @CalledByNative + private void onGoogleAccountsRequest(@GAIAServiceType int serviceType, String email, + String continueUrl, boolean isSameTab) throws RemoteException { + mClient.onGoogleAccountsRequest( + implTypeToJavaType(serviceType), email, continueUrl, isSameTab); + } + + @CalledByNative + private String getGaiaId() throws RemoteException { + return mClient.getGaiaId(); + } + + @GoogleAccountServiceType + private static int implTypeToJavaType(@GAIAServiceType int type) { + switch (type) { + case GAIAServiceType.GAIA_SERVICE_TYPE_SIGNOUT: + return GoogleAccountServiceType.SIGNOUT; + case GAIAServiceType.GAIA_SERVICE_TYPE_ADDSESSION: + return GoogleAccountServiceType.ADD_SESSION; + // SIGNUP and INCOGNITO should not be possible currently, so pass through to DEFAULT. + case GAIAServiceType.GAIA_SERVICE_TYPE_SIGNUP: + case GAIAServiceType.GAIA_SERVICE_TYPE_INCOGNITO: + case GAIAServiceType.GAIA_SERVICE_TYPE_DEFAULT: + return GoogleAccountServiceType.DEFAULT; + } + assert false; + return GoogleAccountServiceType.DEFAULT; + } + + @NativeMethods + interface Natives { + long createGoogleAccountsCallbackProxy(GoogleAccountsCallbackProxy proxy, long tab); + void deleteGoogleAccountsCallbackProxy(long proxy); + } +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBar.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBar.java deleted file mode 100644 index 69e4754d98b..00000000000 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBar.java +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.weblayer_private; - -import android.content.Context; -import android.graphics.Bitmap; -import android.view.View; -import android.widget.TextView; - -import androidx.annotation.ColorRes; -import androidx.annotation.Nullable; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.NativeMethods; -import org.chromium.chrome.browser.infobar.InfoBarIdentifier; -import org.chromium.components.infobars.InfoBarInteractionHandler; -import org.chromium.components.infobars.InfoBarLayout; -import org.chromium.ui.modelutil.PropertyModel; - -/** - * The base class for all InfoBar classes. - * Note that infobars expire by default when a new navigation occurs. - * Make sure to use setExpireOnNavigation(false) if you want an infobar to be sticky. - */ -@JNINamespace("weblayer") -public abstract class InfoBar implements InfoBarInteractionHandler, InfoBarUiItem { - private static final String TAG = "InfoBar"; - - /** - * Interface for InfoBar to interact with its container. - */ - public interface Container { - /** - * @return True if the infobar is in front. - */ - boolean isFrontInfoBar(InfoBar infoBar); - - /** - * Remove the infobar from its container. - * @param infoBar InfoBar to remove from the View hierarchy. - */ - void removeInfoBar(InfoBar infoBar); - - /** - * Notifies that an infobar's View ({@link InfoBar#getView}) has changed. - */ - void notifyInfoBarViewChanged(); - - /** - * @return True if the container's destroy() method has been called. - */ - boolean isDestroyed(); - } - - private final int mIconDrawableId; - private final Bitmap mIconBitmap; - private final @ColorRes int mIconTintId; - private final CharSequence mMessage; - - private @Nullable Container mContainer; - private @Nullable View mView; - private @Nullable Context mContext; - - private boolean mIsDismissed; - private boolean mControlsEnabled = true; - - private @Nullable PropertyModel mModel; - - // This points to the InfoBarAndroid class not any of its subclasses. - private long mNativeInfoBarPtr; - - /** - * Constructor for regular infobars. - * @param iconDrawableId ID of the resource to use for the Icon. If 0, no icon will be shown. - * @param iconTintId The {@link ColorRes} used as tint for the {@code iconDrawableId}. - * @param message The message to show in the infobar. - * @param iconBitmap Icon to draw, in bitmap form. Used mainly for generated icons. - */ - public InfoBar( - int iconDrawableId, @ColorRes int iconTintId, CharSequence message, Bitmap iconBitmap) { - mIconDrawableId = iconDrawableId; - mIconBitmap = iconBitmap; - mIconTintId = iconTintId; - mMessage = message; - } - - /** - * Stores a pointer to the native-side counterpart of this InfoBar. - * @param nativeInfoBarPtr Pointer to the native InfoBarAndroid, not to its subclass. - */ - @CalledByNative - private final void setNativeInfoBar(long nativeInfoBarPtr) { - mNativeInfoBarPtr = nativeInfoBarPtr; - } - - @CalledByNative - protected void onNativeDestroyed() { - mNativeInfoBarPtr = 0; - } - - /** - * Sets the Context used when creating the InfoBar. - */ - public void setContext(Context context) { - mContext = context; - } - - /** - * @return The {@link Context} used to create the InfoBar. This will be null before the InfoBar - * is added to an {@link InfoBarContainer}, or after the InfoBar is closed. - */ - @Nullable - protected Context getContext() { - return mContext; - } - - /** - * Creates the View that represents the InfoBar. - * @return The View representing the InfoBar. - */ - public final View createView() { - assert mContext != null; - - if (usesCompactLayout()) { - InfoBarCompactLayout layout = new InfoBarCompactLayout( - mContext, this, mIconDrawableId, mIconTintId, mIconBitmap); - createCompactLayoutContent(layout); - mView = layout; - } else { - InfoBarLayout layout = new InfoBarLayout( - mContext, this, mIconDrawableId, mIconTintId, mIconBitmap, mMessage); - createContent(layout); - layout.onContentCreated(); - mView = layout; - } - - return mView; - } - - /** - * @return The model for this infobar if one was created. - */ - @Nullable - PropertyModel getModel() { - return mModel; - } - - /** - * If this returns true, the infobar contents will be replaced with a one-line layout. - * When overriding this, also override {@link #getAccessibilityMessage}. - */ - protected boolean usesCompactLayout() { - return false; - } - - /** - * Prepares the InfoBar for display and adds InfoBar-specific controls to the layout. - * @param layout Layout containing all of the controls. - */ - protected void createContent(InfoBarLayout layout) {} - - /** - * Prepares and inserts views into an {@link InfoBarCompactLayout}. - * {@link #usesCompactLayout} must return 'true' for this function to be called. - * @param layout Layout to plug views into. - */ - protected void createCompactLayoutContent(InfoBarCompactLayout layout) {} - - /** - * Replaces the View currently shown in the infobar with the given View. Triggers the swap - * animation via the InfoBarContainer. - */ - protected void replaceView(View newView) { - mView = newView; - mContainer.notifyInfoBarViewChanged(); - } - - /** - * Returns the View shown in this infobar. Only valid after createView() has been called. - */ - @Override - public View getView() { - return mView; - } - - /** - * Returns the accessibility message to announce when this infobar is first shown. - * Override this if the InfoBar doesn't have {@link R.id.infobar_message}. It is usually the - * case when it is in CompactLayout. - */ - protected CharSequence getAccessibilityMessage(CharSequence defaultTitle) { - return defaultTitle == null ? "" : defaultTitle; - } - - @Override - public CharSequence getAccessibilityText() { - if (mView == null) return ""; - - CharSequence title = null; - TextView messageView = (TextView) mView.findViewById(R.id.infobar_message); - if (messageView != null) { - title = messageView.getText(); - } - title = getAccessibilityMessage(title); - if (title.length() > 0) { - title = title + " "; - } - // TODO(crbug/773717): Avoid string concatenation due to i18n. - return title + mContext.getString(R.string.weblayer_bottom_bar_screen_position); - } - - @Override - public int getPriority() { - return InfoBarPriority.PAGE_TRIGGERED; - } - - @Override - @InfoBarIdentifier - public int getInfoBarIdentifier() { - if (mNativeInfoBarPtr == 0) return InfoBarIdentifier.INVALID; - return InfoBarJni.get().getInfoBarIdentifier(mNativeInfoBarPtr, InfoBar.this); - } - - /** - * @return whether the infobar actually needed closing. - */ - @CalledByNative - private boolean closeInfoBar() { - if (!mIsDismissed) { - mIsDismissed = true; - if (!mContainer.isDestroyed()) { - // If the container was destroyed, it's already been emptied of all its infobars. - onStartedHiding(); - mContainer.removeInfoBar(this); - } - mContainer = null; - mView = null; - mContext = null; - return true; - } - return false; - } - - /** - * @return If the infobar is the front infobar (i.e. visible and not hidden behind other - * infobars). - */ - public boolean isFrontInfoBar() { - return mContainer.isFrontInfoBar(this); - } - - /** - * Called just before the Java infobar has begun hiding. Give the chance to clean up any child - * UI that may remain open. - */ - protected void onStartedHiding() {} - - /** - * Returns pointer to native InfoBarAndroid instance. - * TODO(crbug/1056346): The function is used in subclasses typically to get Tab reference. When - * Tab is modularized, replace this function with the one that returns Tab reference. - */ - protected long getNativeInfoBarPtr() { - return mNativeInfoBarPtr; - } - - /** - * Sets the Container that displays the InfoBar. - */ - public void setContainer(Container container) { - mContainer = container; - } - - /** - * @return Whether or not this InfoBar is already dismissed (i.e. closed). - */ - protected boolean isDismissed() { - return mIsDismissed; - } - - @Override - public boolean areControlsEnabled() { - return mControlsEnabled; - } - - @Override - public void setControlsEnabled(boolean state) { - mControlsEnabled = state; - } - - @Override - public void onClick() { - setControlsEnabled(false); - } - - @Override - public void onButtonClicked(boolean isPrimaryButton) {} - - @Override - public void onLinkClicked() { - if (mNativeInfoBarPtr != 0) InfoBarJni.get().onLinkClicked(mNativeInfoBarPtr, InfoBar.this); - } - - /** - * Performs some action related to the button being clicked. - * @param action The type of action defined in {@link ActionType} in this class. - */ - protected void onButtonClicked(@ActionType int action) { - if (mNativeInfoBarPtr != 0) { - InfoBarJni.get().onButtonClicked(mNativeInfoBarPtr, InfoBar.this, action); - } - } - - @Override - public void onCloseButtonClicked() { - if (mNativeInfoBarPtr != 0 && !mIsDismissed) { - InfoBarJni.get().onCloseButtonClicked(mNativeInfoBarPtr, InfoBar.this); - } - } - - @NativeMethods - interface Natives { - int getInfoBarIdentifier(long nativeInfoBarAndroid, InfoBar caller); - void onLinkClicked(long nativeInfoBarAndroid, InfoBar caller); - void onButtonClicked(long nativeInfoBarAndroid, InfoBar caller, int action); - void onCloseButtonClicked(long nativeInfoBarAndroid, InfoBar caller); - } -} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarCompactLayout.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarCompactLayout.java deleted file mode 100644 index c2303eaea6b..00000000000 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarCompactLayout.java +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.weblayer_private; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import android.text.SpannableString; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.method.LinkMovementMethod; -import android.view.Gravity; -import android.view.View; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import androidx.annotation.ColorRes; -import androidx.annotation.StringRes; -import androidx.appcompat.content.res.AppCompatResources; - -import org.chromium.base.ApiCompatibilityUtils; -import org.chromium.base.Callback; -import org.chromium.components.infobars.InfoBarInteractionHandler; -import org.chromium.components.infobars.InfoBarLayout; -import org.chromium.components.infobars.InfoBarMessageView; -import org.chromium.ui.text.NoUnderlineClickableSpan; -import org.chromium.ui.widget.ChromeImageButton; - -/** - * Lays out controls along a line, sandwiched between an (optional) icon and close button. - * This should only be used by the {@link InfoBar} class, and is created when the InfoBar subclass - * declares itself to be using a compact layout via {@link InfoBar#usesCompactLayout}. - */ -public class InfoBarCompactLayout extends LinearLayout implements View.OnClickListener { - private final InfoBarInteractionHandler mInfoBar; - private final int mCompactInfoBarSize; - private final int mIconWidth; - private final View mCloseButton; - - /** - * Constructs a compat layout for the specified infobar. - * @param context The context used to render. - * @param infoBar {@link InfoBarInteractionHandler} that listens to events. - * @param iconResourceId Resource ID of the icon to use for the infobar. - * @param iconTintId The {@link ColorRes} used as tint for {@code iconResourceId}. - * @param iconBitmap Bitmap for the icon to use, if {@code iconResourceId} is not set. - */ - // TODO(crbug/1056346): ctor is made public to allow access from InfoBar. Once - // InfoBar is modularized, restore access to package private. - public InfoBarCompactLayout(Context context, InfoBarInteractionHandler infoBar, - int iconResourceId, @ColorRes int iconTintId, Bitmap iconBitmap) { - super(context); - mInfoBar = infoBar; - mCompactInfoBarSize = - context.getResources().getDimensionPixelOffset(R.dimen.infobar_compact_size); - mIconWidth = context.getResources().getDimensionPixelOffset(R.dimen.infobar_big_icon_size); - - setOrientation(LinearLayout.HORIZONTAL); - setGravity(Gravity.CENTER_VERTICAL); - - prepareIcon(iconResourceId, iconTintId, iconBitmap); - mCloseButton = prepareCloseButton(); - } - - @Override - public void onClick(View view) { - if (view.getId() == R.id.infobar_close_button) { - mInfoBar.onCloseButtonClicked(); - } else { - assert false; - } - } - - /** - * Inserts a view before the close button. - * @param view View to insert. - * @param weight Weight to assign to it. - */ - // TODO(crbug/1056346): addContent is made public to allow access from InfoBar. Once - // InfoBar is modularized, restore access to protected. - public void addContent(View view, float weight) { - LinearLayout.LayoutParams params; - if (weight <= 0.0f) { - params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, mCompactInfoBarSize); - } else { - params = new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, weight); - } - view.setMinimumHeight(mCompactInfoBarSize); - params.gravity = Gravity.BOTTOM; - addView(view, indexOfChild(mCloseButton), params); - } - - /** - * Adds an icon to the start of the infobar, if the infobar requires one. - * @param iconResourceId Resource ID of the icon to use. - * @param iconTintId The {@link ColorRes} used as tint for {@code iconResourceId}. - * @param iconBitmap Raw {@link Bitmap} to use instead of a resource. - */ - private void prepareIcon(int iconResourceId, @ColorRes int iconTintId, Bitmap iconBitmap) { - ImageView iconView = - InfoBarLayout.createIconView(getContext(), iconResourceId, iconTintId, iconBitmap); - if (iconView != null) { - LinearLayout.LayoutParams iconParams = - new LinearLayout.LayoutParams(mIconWidth, mCompactInfoBarSize); - addView(iconView, iconParams); - } - } - - /** - * Creates a close button that can be inserted into an infobar. - * NOTE: This was forked from //chrome's InfoBarLayout.java, as WebLayer supports only compact - * infobars and does not have a corresponding InfoBarLayout.java. - * @param context Context to grab resources from. - * @return {@link ImageButton} that represents a close button. - */ - static ImageButton createCloseButton(Context context) { - final ColorStateList tint = - AppCompatResources.getColorStateList(context, R.color.default_icon_color); - TypedArray a = - context.obtainStyledAttributes(new int[] {android.R.attr.selectableItemBackground}); - Drawable closeButtonBackground = a.getDrawable(0); - a.recycle(); - - ChromeImageButton closeButton = new ChromeImageButton(context); - closeButton.setId(R.id.infobar_close_button); - closeButton.setImageResource(R.drawable.btn_close); - ApiCompatibilityUtils.setImageTintList(closeButton, tint); - closeButton.setBackground(closeButtonBackground); - closeButton.setContentDescription(context.getString(R.string.weblayer_infobar_close)); - closeButton.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - - return closeButton; - } - - /** Adds a close button to the end of the infobar. */ - private View prepareCloseButton() { - ImageButton closeButton = createCloseButton(getContext()); - closeButton.setOnClickListener(this); - LinearLayout.LayoutParams closeParams = - new LinearLayout.LayoutParams(mCompactInfoBarSize, mCompactInfoBarSize); - addView(closeButton, closeParams); - return closeButton; - } - - /** - * Helps building a standard message to display in a compact InfoBar. The message can feature - * a link to perform and action from this infobar. - */ - public static class MessageBuilder { - private final InfoBarCompactLayout mLayout; - private CharSequence mMessage; - private CharSequence mLink; - - /** @param layout The layout we are building a message view for. */ - public MessageBuilder(InfoBarCompactLayout layout) { - mLayout = layout; - } - - public MessageBuilder withText(CharSequence message) { - assert mMessage == null; - mMessage = message; - - return this; - } - - public MessageBuilder withText(@StringRes int messageResId) { - assert mMessage == null; - mMessage = mLayout.getResources().getString(messageResId); - - return this; - } - - /** Appends a link after the main message, its displayed text being the specified string. */ - public MessageBuilder withLink(CharSequence label, Callback<View> onTapCallback) { - assert mLink == null; - - final Resources resources = mLayout.getResources(); - SpannableString link = new SpannableString(label); - link.setSpan(new NoUnderlineClickableSpan(resources, onTapCallback), 0, label.length(), - Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - mLink = link; - - return this; - } - - /** - * Appends a link after the main message, its displayed text being constructed from the - * given resource ID. - */ - public MessageBuilder withLink(@StringRes int textResId, Callback<View> onTapCallback) { - final Resources resources = mLayout.getResources(); - String label = resources.getString(textResId); - return withLink(label, onTapCallback); - } - - /** Finalizes the message view as set up in the builder and inserts it into the layout. */ - public void buildAndInsert() { - mLayout.addContent(build(), 1f); - } - - /** - * Finalizes the message view as set up in the builder. The caller is responsible for adding - * it to the parent layout. - */ - public View build() { - // TODO(dgn): Should be able to handle ReaderMode and Survey infobars but they have non - // standard interaction models (no button/link, whole bar is a button) or style (large - // rather than default text). Revisit after snowflake review. - - assert mMessage != null; - - final int messagePadding = mLayout.getResources().getDimensionPixelOffset( - R.dimen.infobar_compact_message_vertical_padding); - - SpannableStringBuilder builder = new SpannableStringBuilder(); - builder.append(mMessage); - if (mLink != null) builder.append(" ").append(mLink); - - TextView prompt = new InfoBarMessageView(mLayout.getContext()); - ApiCompatibilityUtils.setTextAppearance( - prompt, R.style.TextAppearance_TextMedium_Primary); - prompt.setText(builder); - prompt.setGravity(Gravity.CENTER_VERTICAL); - prompt.setPadding(0, messagePadding, 0, messagePadding); - - if (mLink != null) prompt.setMovementMethod(LinkMovementMethod.getInstance()); - - return prompt; - } - } -} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainer.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainer.java index 15037971602..f0be246a46d 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainer.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainer.java @@ -16,6 +16,9 @@ import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; import org.chromium.chrome.browser.infobar.InfoBarIdentifier; +import org.chromium.components.infobars.InfoBar; +import org.chromium.components.infobars.InfoBarAnimationListener; +import org.chromium.components.infobars.InfoBarUiItem; import org.chromium.content_public.browser.NavigationHandle; import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContentsObserver; @@ -49,26 +52,6 @@ public class InfoBarContainer implements KeyboardVisibilityListener, InfoBar.Con } /** - * A listener for the InfoBar animations. - */ - public interface InfoBarAnimationListener { - public static final int ANIMATION_TYPE_SHOW = 0; - public static final int ANIMATION_TYPE_SWAP = 1; - public static final int ANIMATION_TYPE_HIDE = 2; - - /** - * Notifies the subscriber when an animation is completed. - */ - void notifyAnimationFinished(int animationType); - - /** - * Notifies the subscriber when all animations are finished. - * @param frontInfoBar The frontmost infobar or {@code null} if none are showing. - */ - void notifyAllAnimationsFinished(InfoBarUiItem frontInfoBar); - } - - /** * An observer that is notified of changes to a {@link InfoBarContainer} object. */ public interface InfoBarContainerObserver { diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerLayout.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerLayout.java deleted file mode 100644 index 4f91d6f437d..00000000000 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerLayout.java +++ /dev/null @@ -1,852 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.weblayer_private; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.Resources; -import android.text.TextUtils; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.widget.FrameLayout; - -import org.chromium.ui.widget.OptimizedFrameLayout; -import org.chromium.weblayer_private.InfoBarContainer.InfoBarAnimationListener; - -import java.util.ArrayList; - -/** - * Layout that displays infobars in a stack. Handles all the animations when adding or removing - * infobars and when swapping infobar contents. - * - * The first infobar to be added is visible at the front of the stack. Later infobars peek up just - * enough behind the front infobar to signal their existence; their contents aren't visible at all. - * The stack has a max depth of three infobars. If additional infobars are added beyond this, they - * won't be visible at all until infobars in front of them are dismissed. - * - * Animation details: - * - Newly added infobars slide up from the bottom and then their contents fade in. - * - Disappearing infobars slide down and away. The remaining infobars, if any, resize to the - * new front infobar's size, then the content of the new front infobar fades in. - * - When swapping the front infobar's content, the old content fades out, the infobar resizes to - * the new content's size, then the new content fades in. - * - Only a single animation happens at a time. If several infobars are added and/or removed in - * quick succession, the animations will be queued and run sequentially. - * - * Note: this class depends only on Android view code; it intentionally does not depend on any other - * infobar code. This is an explicit design decision and should remain this way. - * - * TODO(newt): what happens when detached from window? Do animations run? Do animations jump to end - * values? Should they jump to end values? Does requestLayout() get called when detached - * from window? Probably not; it probably just gets called later when reattached. - * - * TODO(newt): use hardware acceleration? See - * http://blog.danlew.net/2015/10/20/using-hardware-layers-to-improve-animation-performance/ - * and http://developer.android.com/guide/topics/graphics/hardware-accel.html#layers - * - * TODO(newt): handle tall infobars on small devices. Use a ScrollView inside the InfoBarWrapper? - * Make sure InfoBarContainerLayout doesn't extend into tabstrip on tablet. - * - * TODO(newt): Disable key events during animations, perhaps by overriding dispatchKeyEvent(). - * Or can we just call setEnabled() false on the infobar wrapper? Will this cause the buttons - * visual state to change (i.e. to turn gray)? - * - * TODO(newt): finalize animation timings and interpolators. - */ -public class InfoBarContainerLayout extends OptimizedFrameLayout { - /** - * Creates an empty InfoBarContainerLayout. - */ - InfoBarContainerLayout(Context context, Runnable makeContainerVisibleRunnable, - InfoBarAnimationListener animationListener) { - super(context, null); - Resources res = context.getResources(); - mBackInfobarHeight = res.getDimensionPixelSize(R.dimen.infobar_peeking_height); - mFloatingBehavior = new FloatingBehavior(this); - mAnimationListener = animationListener; - mMakeContainerVisibleRunnable = makeContainerVisibleRunnable; - } - - /** - * Adds an infobar to the container. The infobar appearing animation will happen after the - * current animation, if any, finishes. - */ - void addInfoBar(InfoBarUiItem item) { - mItems.add(findInsertIndex(item), item); - processPendingAnimations(); - } - - /** - * Finds the appropriate index in the infobar stack for inserting this item. - * @param item The infobar to be inserted. - */ - private int findInsertIndex(InfoBarUiItem item) { - for (int i = 0; i < mItems.size(); ++i) { - if (item.getPriority() < mItems.get(i).getPriority()) { - return i; - } - } - - return mItems.size(); - } - - /** - * Removes an infobar from the container. The infobar will be animated off the screen if it's - * currently visible. - */ - void removeInfoBar(InfoBarUiItem item) { - mItems.remove(item); - processPendingAnimations(); - } - - /** - * Notifies that an infobar's View ({@link InfoBarUiItem#getView}) has changed. If the infobar - * is visible in the front of the stack, the infobar will fade out the old contents, resize, - * then fade in the new contents. - */ - void notifyInfoBarViewChanged() { - processPendingAnimations(); - } - - /** - * Returns true if any animations are pending or in progress. - */ - boolean isAnimating() { - return mAnimation != null; - } - - ///////////////////////////////////////// - // Implementation details - ///////////////////////////////////////// - - /** The maximum number of infobars visible at any time. */ - private static final int MAX_STACK_DEPTH = 3; - - // Animation durations. - private static final int DURATION_SLIDE_UP_MS = 250; - private static final int DURATION_SLIDE_DOWN_MS = 250; - private static final int DURATION_FADE_MS = 100; - private static final int DURATION_FADE_OUT_MS = 200; - - /** - * Base class for animations inside the InfoBarContainerLayout. - * - * Provides a standardized way to prepare for, run, and clean up after animations. Each subclass - * should implement prepareAnimation(), createAnimator(), and onAnimationEnd() as needed. - */ - private abstract class InfoBarAnimation { - private Animator mAnimator; - - final boolean isStarted() { - return mAnimator != null; - } - - final void start() { - Animator.AnimatorListener listener = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - InfoBarAnimation.this.onAnimationEnd(); - mAnimation = null; - mAnimationListener.notifyAnimationFinished(getAnimationType()); - processPendingAnimations(); - } - }; - - mAnimator = createAnimator(); - mAnimator.addListener(listener); - mAnimator.start(); - } - - /** - * Returns an animator that animates an InfoBarWrapper's y-translation from its current - * value to endValue and updates the side shadow positions on each frame. - */ - ValueAnimator createTranslationYAnimator(final InfoBarWrapper wrapper, float endValue) { - ValueAnimator animator = ValueAnimator.ofFloat(wrapper.getTranslationY(), endValue); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - wrapper.setTranslationY((float) animation.getAnimatedValue()); - mFloatingBehavior.updateShadowPosition(); - } - }); - return animator; - } - - /** - * Called before the animation begins. This is the time to add views to the hierarchy and - * adjust layout parameters. - */ - void prepareAnimation() {} - - /** - * Called to create an Animator which will control the animation. Called after - * prepareAnimation() and after a subsequent layout has happened. - */ - abstract Animator createAnimator(); - - /** - * Called after the animation completes. This is the time to do post-animation cleanup, such - * as removing views from the hierarchy. - */ - void onAnimationEnd() {} - - /** - * Returns the InfoBarAnimationListener.ANIMATION_TYPE_* constant that corresponds to this - * type of animation (showing, swapping, etc). - */ - abstract int getAnimationType(); - } - - /** - * The animation to show the first infobar. The infobar slides up from the bottom; then its - * content fades in. - */ - private class FirstInfoBarAppearingAnimation extends InfoBarAnimation { - private InfoBarUiItem mFrontItem; - private InfoBarWrapper mFrontWrapper; - private View mFrontContents; - - FirstInfoBarAppearingAnimation(InfoBarUiItem frontItem) { - mFrontItem = frontItem; - } - - @Override - void prepareAnimation() { - mFrontContents = mFrontItem.getView(); - mFrontWrapper = new InfoBarWrapper(getContext(), mFrontItem); - mFrontWrapper.addView(mFrontContents); - addWrapper(mFrontWrapper); - } - - @Override - Animator createAnimator() { - mFrontWrapper.setTranslationY(mFrontWrapper.getHeight()); - mFrontContents.setAlpha(0f); - - AnimatorSet animator = new AnimatorSet(); - animator.playSequentially( - createTranslationYAnimator(mFrontWrapper, 0f).setDuration(DURATION_SLIDE_UP_MS), - ObjectAnimator.ofFloat(mFrontContents, View.ALPHA, 1f) - .setDuration(DURATION_FADE_MS)); - return animator; - } - - @Override - void onAnimationEnd() { - announceForAccessibility(mFrontItem.getAccessibilityText()); - } - - @Override - int getAnimationType() { - return InfoBarAnimationListener.ANIMATION_TYPE_SHOW; - } - } - - /** - * The animation to show the a new front-most infobar in front of existing visible infobars. The - * infobar slides up from the bottom; then its content fades in. The previously visible infobars - * will be resized simulatenously to the new desired size. - */ - private class FrontInfoBarAppearingAnimation extends InfoBarAnimation { - private InfoBarUiItem mFrontItem; - private InfoBarWrapper mFrontWrapper; - private InfoBarWrapper mOldFrontWrapper; - private View mFrontContents; - - FrontInfoBarAppearingAnimation(InfoBarUiItem frontItem) { - mFrontItem = frontItem; - } - - @Override - void prepareAnimation() { - mOldFrontWrapper = mInfoBarWrappers.get(0); - - mFrontContents = mFrontItem.getView(); - mFrontWrapper = new InfoBarWrapper(getContext(), mFrontItem); - mFrontWrapper.addView(mFrontContents); - addWrapperToFront(mFrontWrapper); - } - - @Override - Animator createAnimator() { - // After adding the new wrapper, the new front item's view, and the old front item's - // view are both in their wrappers, and the height of the stack as determined by - // FrameLayout will take both into account. This means the height of the container will - // be larger than it needs to be, if the previous old front item is larger than the sum - // of the new front item and mBackInfobarHeight. - // - // First work out how much the container will grow or shrink by. - int heightDelta = - mFrontWrapper.getHeight() + mBackInfobarHeight - mOldFrontWrapper.getHeight(); - - // Now work out where to animate the new front item to / from. - int newFrontStart = mFrontWrapper.getHeight(); - int newFrontEnd = 0; - if (heightDelta < 0) { - // If the container is shrinking, this won't be reflected in the layout just yet. - // The layout will have extra space in it for the previous front infobar, which the - // animation of the new front infobar has to take into account. - newFrontStart -= heightDelta; - newFrontEnd -= heightDelta; - } - mFrontWrapper.setTranslationY(newFrontStart); - mFrontContents.setAlpha(0f); - - // Since we are adding the infobar to the top of the stack, make the container fully - // visible since it could be at hidden or partially hidden state. - mMakeContainerVisibleRunnable.run(); - - AnimatorSet animator = new AnimatorSet(); - animator.play(createTranslationYAnimator(mFrontWrapper, newFrontEnd) - .setDuration(DURATION_SLIDE_UP_MS)); - - // If the container is shrinking, the back infobars need to animate down (from 0 to the - // positive delta). Otherwise they have to animate up (from the negative delta to 0). - int backStart = Math.max(0, heightDelta); - int backEnd = Math.max(-heightDelta, 0); - for (int i = 1; i < mInfoBarWrappers.size(); i++) { - mInfoBarWrappers.get(i).setTranslationY(backStart); - animator.play(createTranslationYAnimator(mInfoBarWrappers.get(i), backEnd) - .setDuration(DURATION_SLIDE_UP_MS)); - } - - animator.play(ObjectAnimator.ofFloat(mFrontContents, View.ALPHA, 1f) - .setDuration(DURATION_FADE_MS)) - .after(DURATION_SLIDE_UP_MS); - - return animator; - } - - @Override - void onAnimationEnd() { - // Remove the old front wrappers view so it won't affect the height of the container any - // more. - mOldFrontWrapper.removeAllViews(); - - // Now set any Y offsets to 0 as there is no need to account for the old front wrapper - // making the container higher than it should be. - for (int i = 0; i < mInfoBarWrappers.size(); i++) { - mInfoBarWrappers.get(i).setTranslationY(0); - } - updateLayoutParams(); - announceForAccessibility(mFrontItem.getAccessibilityText()); - } - - @Override - int getAnimationType() { - return InfoBarAnimationListener.ANIMATION_TYPE_SHOW; - } - } - - /** - * The animation to show a back infobar. The infobar slides up behind the existing infobars, so - * its top edge peeks out just a bit. - */ - private class BackInfoBarAppearingAnimation extends InfoBarAnimation { - private InfoBarWrapper mAppearingWrapper; - - BackInfoBarAppearingAnimation(InfoBarUiItem appearingItem) { - mAppearingWrapper = new InfoBarWrapper(getContext(), appearingItem); - } - - @Override - void prepareAnimation() { - addWrapper(mAppearingWrapper); - } - - @Override - Animator createAnimator() { - mAppearingWrapper.setTranslationY(mAppearingWrapper.getHeight()); - return createTranslationYAnimator(mAppearingWrapper, 0f) - .setDuration(DURATION_SLIDE_UP_MS); - } - - @Override - public void onAnimationEnd() { - mAppearingWrapper.removeView(mAppearingWrapper.getItem().getView()); - } - - @Override - int getAnimationType() { - return InfoBarAnimationListener.ANIMATION_TYPE_SHOW; - } - } - - /** - * The animation to hide the front infobar and reveal the second-to-front infobar. The front - * infobar slides down and off the screen. The back infobar(s) will adjust to the size of the - * new front infobar, and then the new front infobar's contents will fade in. - */ - private class FrontInfoBarDisappearingAndRevealingAnimation extends InfoBarAnimation { - private InfoBarWrapper mOldFrontWrapper; - private InfoBarWrapper mNewFrontWrapper; - private View mNewFrontContents; - - @Override - void prepareAnimation() { - mOldFrontWrapper = mInfoBarWrappers.get(0); - mNewFrontWrapper = mInfoBarWrappers.get(1); - mNewFrontContents = mNewFrontWrapper.getItem().getView(); - mNewFrontWrapper.addView(mNewFrontContents); - } - - @Override - Animator createAnimator() { - // The amount by which mNewFrontWrapper will grow (negative value indicates shrinking). - int deltaHeight = (mNewFrontWrapper.getHeight() - mBackInfobarHeight) - - mOldFrontWrapper.getHeight(); - int startTranslationY = Math.max(deltaHeight, 0); - int endTranslationY = Math.max(-deltaHeight, 0); - - // Slide the front infobar down and away. - AnimatorSet animator = new AnimatorSet(); - mOldFrontWrapper.setTranslationY(startTranslationY); - animator.play(createTranslationYAnimator( - mOldFrontWrapper, startTranslationY + mOldFrontWrapper.getHeight()) - .setDuration(DURATION_SLIDE_UP_MS)); - - // Slide the other infobars to their new positions. - // Note: animator.play() causes these animations to run simultaneously. - for (int i = 1; i < mInfoBarWrappers.size(); i++) { - mInfoBarWrappers.get(i).setTranslationY(startTranslationY); - animator.play(createTranslationYAnimator(mInfoBarWrappers.get(i), endTranslationY) - .setDuration(DURATION_SLIDE_UP_MS)); - } - - mNewFrontContents.setAlpha(0f); - animator.play(ObjectAnimator.ofFloat(mNewFrontContents, View.ALPHA, 1f) - .setDuration(DURATION_FADE_MS)) - .after(DURATION_SLIDE_UP_MS); - - return animator; - } - - @Override - void onAnimationEnd() { - mOldFrontWrapper.removeAllViews(); - removeWrapper(mOldFrontWrapper); - for (int i = 0; i < mInfoBarWrappers.size(); i++) { - mInfoBarWrappers.get(i).setTranslationY(0); - } - announceForAccessibility(mNewFrontWrapper.getItem().getAccessibilityText()); - } - - @Override - int getAnimationType() { - return InfoBarAnimationListener.ANIMATION_TYPE_HIDE; - } - } - - /** - * The animation to hide the backmost infobar, or the front infobar if there's only one infobar. - * The infobar simply slides down out of the container. - */ - private class InfoBarDisappearingAnimation extends InfoBarAnimation { - private InfoBarWrapper mDisappearingWrapper; - - @Override - void prepareAnimation() { - mDisappearingWrapper = mInfoBarWrappers.get(mInfoBarWrappers.size() - 1); - } - - @Override - Animator createAnimator() { - return createTranslationYAnimator( - mDisappearingWrapper, mDisappearingWrapper.getHeight()) - .setDuration(DURATION_SLIDE_DOWN_MS); - } - - @Override - void onAnimationEnd() { - mDisappearingWrapper.removeAllViews(); - removeWrapper(mDisappearingWrapper); - } - - @Override - int getAnimationType() { - return InfoBarAnimationListener.ANIMATION_TYPE_HIDE; - } - } - - /** - * The animation to swap the contents of the front infobar. The current contents fade out, - * then the infobar resizes to fit the new contents, then the new contents fade in. - */ - private class FrontInfoBarSwapContentsAnimation extends InfoBarAnimation { - private InfoBarWrapper mFrontWrapper; - private View mOldContents; - private View mNewContents; - - @Override - void prepareAnimation() { - mFrontWrapper = mInfoBarWrappers.get(0); - mOldContents = mFrontWrapper.getChildAt(0); - mNewContents = mFrontWrapper.getItem().getView(); - mFrontWrapper.addView(mNewContents); - } - - @Override - Animator createAnimator() { - int deltaHeight = mNewContents.getHeight() - mOldContents.getHeight(); - InfoBarContainerLayout.this.setTranslationY(Math.max(0, deltaHeight)); - mNewContents.setAlpha(0f); - - AnimatorSet animator = new AnimatorSet(); - animator.playSequentially(ObjectAnimator.ofFloat(mOldContents, View.ALPHA, 0f) - .setDuration(DURATION_FADE_OUT_MS), - ObjectAnimator - .ofFloat(InfoBarContainerLayout.this, View.TRANSLATION_Y, - Math.max(0, -deltaHeight)) - .setDuration(DURATION_SLIDE_UP_MS), - ObjectAnimator.ofFloat(mNewContents, View.ALPHA, 1f) - .setDuration(DURATION_FADE_OUT_MS)); - return animator; - } - - @Override - void onAnimationEnd() { - mFrontWrapper.removeViewAt(0); - InfoBarContainerLayout.this.setTranslationY(0f); - mFrontWrapper.getItem().setControlsEnabled(true); - announceForAccessibility(mFrontWrapper.getItem().getAccessibilityText()); - } - - @Override - int getAnimationType() { - return InfoBarAnimationListener.ANIMATION_TYPE_SWAP; - } - } - - /** - * Controls whether infobars fill the full available width, or whether they "float" in the - * middle of the available space. The latter case happens if the available space is wider than - * the max width allowed for infobars. - * - * Also handles the shadows on the sides of the infobars in floating mode. The side shadows are - * separate views -- rather than being part of each InfoBarWrapper -- to avoid a double-shadow - * effect, which would happen during animations when two InfoBarWrappers overlap each other. - */ - private static class FloatingBehavior { - /** The InfoBarContainerLayout. */ - private FrameLayout mLayout; - - /** - * The max width of the infobars. If the available space is wider than this, the infobars - * will switch to floating mode. - */ - private final int mMaxWidth; - - /** The width of the left and right shadows. */ - private final int mShadowWidth; - - /** Whether the layout is currently floating. */ - private boolean mIsFloating; - - /** The shadows that appear on the sides of the infobars in floating mode. */ - private View mLeftShadowView; - private View mRightShadowView; - - FloatingBehavior(FrameLayout layout) { - mLayout = layout; - Resources res = mLayout.getContext().getResources(); - mMaxWidth = res.getDimensionPixelSize(R.dimen.infobar_max_width); - mShadowWidth = res.getDimensionPixelSize(R.dimen.infobar_shadow_width); - } - - /** - * This should be called in onMeasure() before super.onMeasure(). The return value is a new - * widthMeasureSpec that should be passed to super.onMeasure(). - */ - int beforeOnMeasure(int widthMeasureSpec) { - int width = MeasureSpec.getSize(widthMeasureSpec); - boolean isFloating = width > mMaxWidth; - if (isFloating != mIsFloating) { - mIsFloating = isFloating; - onIsFloatingChanged(); - } - - if (isFloating) { - int mode = MeasureSpec.getMode(widthMeasureSpec); - width = Math.min(width, mMaxWidth + 2 * mShadowWidth); - widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, mode); - } - return widthMeasureSpec; - } - - /** - * This should be called in onMeasure() after super.onMeasure(). - */ - void afterOnMeasure(int measuredHeight) { - if (!mIsFloating) return; - // Measure side shadows to match the parent view's height. - int widthSpec = MeasureSpec.makeMeasureSpec(mShadowWidth, MeasureSpec.EXACTLY); - int heightSpec = MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY); - mLeftShadowView.measure(widthSpec, heightSpec); - mRightShadowView.measure(widthSpec, heightSpec); - } - - /** - * This should be called whenever the Y-position of an infobar changes. - */ - void updateShadowPosition() { - if (!mIsFloating) return; - float minY = mLayout.getHeight(); - int childCount = mLayout.getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = mLayout.getChildAt(i); - if (child != mLeftShadowView && child != mRightShadowView) { - minY = Math.min(minY, child.getY()); - } - } - mLeftShadowView.setY(minY); - mRightShadowView.setY(minY); - } - - private void onIsFloatingChanged() { - if (mIsFloating) { - initShadowViews(); - mLayout.setPadding(mShadowWidth, 0, mShadowWidth, 0); - mLayout.setClipToPadding(false); - mLayout.addView(mLeftShadowView); - mLayout.addView(mRightShadowView); - } else { - mLayout.setPadding(0, 0, 0, 0); - mLayout.removeView(mLeftShadowView); - mLayout.removeView(mRightShadowView); - } - } - - @SuppressLint("RtlHardcoded") - private void initShadowViews() { - if (mLeftShadowView != null) return; - - mLeftShadowView = new View(mLayout.getContext()); - mLeftShadowView.setBackgroundResource(R.drawable.infobar_shadow_left); - LayoutParams leftLp = new FrameLayout.LayoutParams(0, 0, Gravity.LEFT); - leftLp.leftMargin = -mShadowWidth; - mLeftShadowView.setLayoutParams(leftLp); - - mRightShadowView = new View(mLayout.getContext()); - mRightShadowView.setBackgroundResource(R.drawable.infobar_shadow_left); - LayoutParams rightLp = new FrameLayout.LayoutParams(0, 0, Gravity.RIGHT); - rightLp.rightMargin = -mShadowWidth; - mRightShadowView.setScaleX(-1f); - mRightShadowView.setLayoutParams(rightLp); - } - } - - /** - * The height of back infobars, i.e. the distance between the top of the front infobar and the - * top of the next infobar back. - */ - private final int mBackInfobarHeight; - - /** - * All the Items, in front to back order. - * This list is updated immediately when addInfoBar(), removeInfoBar(), and swapInfoBar() are - * called; so during animations, it does *not* match the currently visible views. - */ - private final ArrayList<InfoBarUiItem> mItems = new ArrayList<>(); - - /** - * The currently visible InfoBarWrappers, in front to back order. - */ - private final ArrayList<InfoBarWrapper> mInfoBarWrappers = new ArrayList<>(); - - /** A observer that is notified when animations finish. */ - private final InfoBarAnimationListener mAnimationListener; - - /** The current animation, or null if no animation is happening currently. */ - private InfoBarAnimation mAnimation; - - private FloatingBehavior mFloatingBehavior; - - /** The runnable to make infobar container fully visible. */ - private Runnable mMakeContainerVisibleRunnable; - - /** - * Determines whether any animations need to run in order to make the visible views match the - * current list of Items in mItems. If so, kicks off the next animation that's needed. - */ - private void processPendingAnimations() { - // If an animation is running, wait until it finishes before beginning the next animation. - if (mAnimation != null) return; - - // The steps below are ordered to minimize movement during animations. In particular, - // removals happen before additions or swaps, and changes are made to back infobars before - // front infobars. - - // First, remove any infobars that are no longer in mItems, if any. Check the back infobars - // before the front. - for (int i = mInfoBarWrappers.size() - 1; i >= 0; i--) { - InfoBarUiItem visibleItem = mInfoBarWrappers.get(i).getItem(); - if (!mItems.contains(visibleItem)) { - if (i == 0 && mInfoBarWrappers.size() >= 2) { - // Remove the front infobar and reveal the second-to-front infobar. - runAnimation(new FrontInfoBarDisappearingAndRevealingAnimation()); - return; - - } else { - // Move the infobar to the very back if it's not already there. - InfoBarWrapper wrapper = mInfoBarWrappers.get(i); - if (i != mInfoBarWrappers.size() - 1) { - removeWrapper(wrapper); - addWrapper(wrapper); - } - - // Remove the backmost infobar (which may be the front infobar). - runAnimation(new InfoBarDisappearingAnimation()); - return; - } - } - } - - // Second, run swap animation on front infobar if needed. - if (!mInfoBarWrappers.isEmpty()) { - InfoBarUiItem frontItem = mInfoBarWrappers.get(0).getItem(); - View frontContents = mInfoBarWrappers.get(0).getChildAt(0); - if (frontContents != frontItem.getView()) { - runAnimation(new FrontInfoBarSwapContentsAnimation()); - return; - } - } - - // Third, check if we should add any infobars in front of visible infobars. This can happen - // if an infobar has been inserted into mItems, in front of the currently visible item. To - // detect this the items at the beginning of mItems are compared against the first item in - // mInfoBarWrappers. - if (!mInfoBarWrappers.isEmpty()) { - // Find the infobar with the highest index that isn't currently being shown. - InfoBarUiItem currentVisibleItem = mInfoBarWrappers.get(0).getItem(); - InfoBarUiItem itemToInsert = null; - for (int checkIndex = 0; checkIndex < mItems.size(); checkIndex++) { - if (mItems.get(checkIndex) == currentVisibleItem) { - // There are no remaining infobars that can possibly override the - // currently displayed one. - break; - } else { - // Found an infobar that isn't being displayed yet. Track it so that - // it can be animated in. - itemToInsert = mItems.get(checkIndex); - } - } - if (itemToInsert != null) { - runAnimation(new FrontInfoBarAppearingAnimation(itemToInsert)); - return; - } - } - - // Fourth, check if we should add any infobars at the back. - int desiredChildCount = Math.min(mItems.size(), MAX_STACK_DEPTH); - if (mInfoBarWrappers.size() < desiredChildCount) { - InfoBarUiItem itemToShow = mItems.get(mInfoBarWrappers.size()); - runAnimation(mInfoBarWrappers.isEmpty() - ? new FirstInfoBarAppearingAnimation(itemToShow) - : new BackInfoBarAppearingAnimation(itemToShow)); - return; - } - - // Fifth, now that we've stabilized, let listeners know that we have no more animations. - InfoBarUiItem frontItem = - mInfoBarWrappers.size() > 0 ? mInfoBarWrappers.get(0).getItem() : null; - mAnimationListener.notifyAllAnimationsFinished(frontItem); - } - - private void runAnimation(InfoBarAnimation animation) { - mAnimation = animation; - mAnimation.prepareAnimation(); - if (isLayoutRequested()) { - // onLayout() will call mAnimation.start(). - } else { - mAnimation.start(); - } - } - - private void addWrapper(InfoBarWrapper wrapper) { - addView(wrapper, 0, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); - mInfoBarWrappers.add(wrapper); - updateLayoutParams(); - } - - private void addWrapperToFront(InfoBarWrapper wrapper) { - addView(wrapper, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); - mInfoBarWrappers.add(0, wrapper); - updateLayoutParams(); - } - - private void removeWrapper(InfoBarWrapper wrapper) { - removeView(wrapper); - mInfoBarWrappers.remove(wrapper); - updateLayoutParams(); - } - - private void updateLayoutParams() { - // Stagger the top margins so the back infobars peek out a bit. - int childCount = mInfoBarWrappers.size(); - for (int i = 0; i < childCount; i++) { - View child = mInfoBarWrappers.get(i); - LayoutParams lp = (LayoutParams) child.getLayoutParams(); - lp.topMargin = (childCount - 1 - i) * mBackInfobarHeight; - child.setLayoutParams(lp); - } - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - widthMeasureSpec = mFloatingBehavior.beforeOnMeasure(widthMeasureSpec); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - mFloatingBehavior.afterOnMeasure(getMeasuredHeight()); - } - - @Override - public void announceForAccessibility(CharSequence text) { - if (TextUtils.isEmpty(text)) return; - super.announceForAccessibility(text); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - mFloatingBehavior.updateShadowPosition(); - - // Animations start after a layout has completed, at which point all views are guaranteed - // to have valid sizes and positions. - if (mAnimation != null && !mAnimation.isStarted()) { - mAnimation.start(); - } - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - // Trap any attempts to fiddle with the infobars while we're animating. - return super.onInterceptTouchEvent(ev) || mAnimation != null - || (!mInfoBarWrappers.isEmpty() - && !mInfoBarWrappers.get(0).getItem().areControlsEnabled()); - } - - @Override - @SuppressLint("ClickableViewAccessibility") - public boolean onTouchEvent(MotionEvent event) { - super.onTouchEvent(event); - // Consume all touch events so they do not reach the ContentView. - return true; - } - - @Override - public boolean onHoverEvent(MotionEvent event) { - super.onHoverEvent(event); - // Consume all hover events so they do not reach the ContentView. In touch exploration mode, - // this prevents the user from interacting with the part of the ContentView behind the - // infobars. http://crbug.com/430701 - return true; - } -} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerView.java index 553608310f2..377ea4d0d4f 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerView.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarContainerView.java @@ -17,6 +17,11 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import org.chromium.base.MathUtils; +import org.chromium.components.browser_ui.banners.SwipableOverlayView; +import org.chromium.components.infobars.InfoBar; +import org.chromium.components.infobars.InfoBarAnimationListener; +import org.chromium.components.infobars.InfoBarContainerLayout; +import org.chromium.components.infobars.InfoBarUiItem; import org.chromium.ui.display.DisplayAndroid; import org.chromium.ui.display.DisplayUtil; @@ -27,7 +32,7 @@ public class InfoBarContainerView extends SwipableOverlayView { /** * Observes container view changes. */ - public interface ContainerViewObserver extends InfoBarContainer.InfoBarAnimationListener { + public interface ContainerViewObserver extends InfoBarAnimationListener { /** * Called when the height of shown content changed. * @param shownFraction The ratio of height of shown content to the height of the container @@ -82,8 +87,8 @@ public class InfoBarContainerView extends SwipableOverlayView { updateLayoutParams(context, isTablet); Runnable makeContainerVisibleRunnable = () -> runUpEventAnimation(true); - mLayout = new InfoBarContainerLayout(context, makeContainerVisibleRunnable, - new InfoBarContainer.InfoBarAnimationListener() { + mLayout = new InfoBarContainerLayout( + context, makeContainerVisibleRunnable, new InfoBarAnimationListener() { @Override public void notifyAnimationFinished(int animationType) { mContainerViewObserver.notifyAnimationFinished(animationType); @@ -187,7 +192,7 @@ public class InfoBarContainerView extends SwipableOverlayView { void addToParentView() { // If mTab is null, destroy() was called. This should not be added after destroyed. assert mTab != null; - super.addToParentView(mParentView, + super.addToParentViewAtIndex(mParentView, mTab.getBrowser().getViewController().getDesiredInfoBarContainerViewIndex()); } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarUiItem.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarUiItem.java deleted file mode 100644 index 5a653d069c3..00000000000 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarUiItem.java +++ /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. - -package org.chromium.weblayer_private; - -import android.view.View; - -import androidx.annotation.IntDef; - -import org.chromium.chrome.browser.infobar.InfoBarIdentifier; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * An interface for items that can be added to an InfoBarContainerLayout. - */ -public interface InfoBarUiItem { - // The infobar priority. - @IntDef({InfoBarPriority.CRITICAL, InfoBarPriority.USER_TRIGGERED, - InfoBarPriority.PAGE_TRIGGERED, InfoBarPriority.BACKGROUND}) - @Retention(RetentionPolicy.SOURCE) - public @interface InfoBarPriority { - int CRITICAL = 0; - int USER_TRIGGERED = 1; - int PAGE_TRIGGERED = 2; - int BACKGROUND = 3; - } - - /** - * Returns the View that represents this infobar. This should have no background or borders; - * a background and shadow will be added by a wrapper view. - */ - View getView(); - - /** - * Returns whether controls for this View should be clickable. If false, all input events on - * this item will be ignored. - */ - boolean areControlsEnabled(); - - /** - * Sets whether or not controls for this View should be clickable. This does not affect the - * visual state of the infobar. - * @param state If false, all input events on this Item will be ignored. - */ - void setControlsEnabled(boolean state); - - /** - * Returns the accessibility text to announce when this infobar is first shown. - */ - CharSequence getAccessibilityText(); - - /** - * Returns the priority of an infobar. High priority infobar is shown in front of low - * priority infobar. If infobars have the same priorities, the most recently added one - * is shown behind previous ones. - * - */ - int getPriority(); - - /** - * Returns the type of infobar, as best as can be determined at this time. See - * components/infobars/core/infobar_delegate.h. - */ - @InfoBarIdentifier - int getInfoBarIdentifier(); -} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarWrapper.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarWrapper.java deleted file mode 100644 index 8a574254a96..00000000000 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InfoBarWrapper.java +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.weblayer_private; - -import android.content.Context; -import android.content.res.Resources; -import android.view.Gravity; -import android.view.View; -import android.widget.FrameLayout; - -/** - * Layout that holds an infobar's contents and provides a background color and a top shadow. - */ -class InfoBarWrapper extends FrameLayout { - private final InfoBarUiItem mItem; - - /** - * Constructor for inflating from Java. - */ - InfoBarWrapper(Context context, InfoBarUiItem item) { - super(context); - mItem = item; - Resources res = context.getResources(); - int peekingHeight = res.getDimensionPixelSize(R.dimen.infobar_peeking_height); - int shadowHeight = res.getDimensionPixelSize(R.dimen.infobar_shadow_height); - setMinimumHeight(peekingHeight + shadowHeight); - - // setBackgroundResource() changes the padding, so call setPadding() second. - setBackgroundResource(R.drawable.weblayer_infobar_wrapper_bg); - setPadding(0, shadowHeight, 0, 0); - } - - InfoBarUiItem getItem() { - return mItem; - } - - @Override - public void onViewAdded(View child) { - child.setLayoutParams(new LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, Gravity.TOP)); - } -} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InterceptNavigationDelegateClientImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InterceptNavigationDelegateClientImpl.java index 32f947fb86e..7a9a7303418 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/InterceptNavigationDelegateClientImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/InterceptNavigationDelegateClientImpl.java @@ -28,6 +28,7 @@ public class InterceptNavigationDelegateClientImpl implements InterceptNavigatio private RedirectHandler mRedirectHandler; private InterceptNavigationDelegateImpl mInterceptNavigationDelegate; private long mLastNavigationWithUserGestureTime = RedirectHandler.INVALID_TIME; + private boolean mDestroyed; InterceptNavigationDelegateClientImpl(TabImpl tab) { mTab = tab; @@ -53,6 +54,7 @@ public class InterceptNavigationDelegateClientImpl implements InterceptNavigatio } public void destroy() { + mDestroyed = true; getWebContents().removeObserver(mWebContentsObserver); mInterceptNavigationDelegate.associateWithWebContents(null); } @@ -118,6 +120,11 @@ public class InterceptNavigationDelegateClientImpl implements InterceptNavigatio @Override public void closeTab() { + // When InterceptNavigationDelegate determines that a tab needs to be closed, it posts a + // task invoking this method. It is possible that in the interim the tab was closed for + // another reason. In that case there is nothing more to do here. + if (mDestroyed) return; + closeTab(mTab); } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaSessionManager.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaSessionManager.java index a36125ace70..bd5934aff36 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaSessionManager.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaSessionManager.java @@ -12,10 +12,10 @@ import android.support.v4.media.session.MediaSessionCompat; import org.chromium.components.browser_ui.media.MediaNotificationController; import org.chromium.components.browser_ui.media.MediaNotificationInfo; import org.chromium.components.browser_ui.media.MediaSessionHelper; -import org.chromium.components.browser_ui.notifications.ChromeNotification; -import org.chromium.components.browser_ui.notifications.ChromeNotificationBuilder; import org.chromium.components.browser_ui.notifications.ForegroundServiceUtils; import org.chromium.components.browser_ui.notifications.NotificationMetadata; +import org.chromium.components.browser_ui.notifications.NotificationWrapper; +import org.chromium.components.browser_ui.notifications.NotificationWrapperBuilder; /** * A glue class for MediaSession. @@ -28,7 +28,7 @@ class MediaSessionManager { @SuppressLint("StaticFieldLeak") static MediaNotificationController sController; - private static int sNotificationId = 0; + private static int sNotificationId; static void serviceStarted(Service service, Intent intent) { if (sController != null && sController.processIntent(service, intent)) return; @@ -37,7 +37,7 @@ class MediaSessionManager { // notification hasn't been shown. See similar logic in {@link // ChromeMediaNotificationControllerDelegate}. MediaNotificationController.finishStartingForegroundServiceOnO( - service, createChromeNotificationBuilder().buildChromeNotification()); + service, createNotificationWrapperBuilder().buildNotificationWrapper()); // Call stopForeground to guarantee Android unset the foreground bit. ForegroundServiceUtils.getInstance().stopForeground( service, Service.STOP_FOREGROUND_REMOVE); @@ -109,8 +109,8 @@ class MediaSessionManager { } @Override - public ChromeNotificationBuilder createChromeNotificationBuilder() { - return MediaSessionManager.createChromeNotificationBuilder(); + public NotificationWrapperBuilder createNotificationWrapperBuilder() { + return MediaSessionManager.createNotificationWrapperBuilder(); } @Override @@ -119,17 +119,17 @@ class MediaSessionManager { } @Override - public void logNotificationShown(ChromeNotification notification) {} + public void logNotificationShown(NotificationWrapper notification) {} } - private static ChromeNotificationBuilder createChromeNotificationBuilder() { + private static NotificationWrapperBuilder createNotificationWrapperBuilder() { ensureNotificationId(); // Only the null tag will work as expected, because {@link Service#startForeground()} only // takes an ID and no tag. If we pass a tag here, then the notification that's used to // display a paused state (no foreground service) will not be identified as the same one // that's used with the foreground service. - return WebLayerNotificationBuilder.create( + return WebLayerNotificationWrapperBuilder.create( WebLayerNotificationChannels.ChannelId.MEDIA_PLAYBACK, new NotificationMetadata(0, null /*notificationTag*/, sNotificationId)); } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaStreamManager.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaStreamManager.java index 90925f10df5..99232d8d698 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaStreamManager.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MediaStreamManager.java @@ -13,13 +13,14 @@ import android.util.AndroidRuntimeException; import android.webkit.ValueCallback; import org.chromium.base.ContextUtils; +import org.chromium.base.ThreadUtils; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; -import org.chromium.components.browser_ui.notifications.ChromeNotification; import org.chromium.components.browser_ui.notifications.NotificationManagerProxy; import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl; import org.chromium.components.browser_ui.notifications.NotificationMetadata; +import org.chromium.components.browser_ui.notifications.NotificationWrapper; import org.chromium.components.browser_ui.notifications.PendingIntentProvider; import org.chromium.components.webrtc.MediaCaptureNotificationUtil; import org.chromium.components.webrtc.MediaCaptureNotificationUtil.MediaType; @@ -178,6 +179,7 @@ public class MediaStreamManager { audio, video, ObjectWrapper.wrap(new ValueCallback<Boolean>() { @Override public void onReceiveValue(Boolean allowed) { + ThreadUtils.assertOnUiThread(); respondToStreamRequest(requestId, allowed.booleanValue()); } })); @@ -223,12 +225,13 @@ public class MediaStreamManager { int mediaType = audio && video ? MediaType.AUDIO_AND_VIDEO : audio ? MediaType.AUDIO_ONLY : MediaType.VIDEO_ONLY; - // TODO(crbug/1076098): don't pass a URL in incognito. - ChromeNotification notification = MediaCaptureNotificationUtil.createNotification( - WebLayerNotificationBuilder.create( + NotificationWrapper notification = MediaCaptureNotificationUtil.createNotification( + WebLayerNotificationWrapperBuilder.create( WebLayerNotificationChannels.ChannelId.WEBRTC_CAM_AND_MIC, new NotificationMetadata(0, AV_STREAM_TAG, mNotificationId)), - mediaType, mTab.getWebContents().getVisibleUrl().getSpec(), + mediaType, + mTab.getProfile().isIncognito() ? null + : mTab.getWebContents().getVisibleUrl().getSpec(), WebLayerImpl.getClientApplicationName(), contentIntent, null /*stopIntent*/); getNotificationManager().notify(notification); diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java index 3957ed46557..31303359040 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java @@ -36,12 +36,18 @@ public final class NavigationControllerImpl extends INavigationController.Stub { if (WebLayerFactoryImpl.getClientMajorVersion() < 83) { assert params == null; } - if (params == null) { - NavigationControllerImplJni.get().navigate(mNativeNavigationController, uri); - } else { - NavigationControllerImplJni.get().navigateWithParams( - mNativeNavigationController, uri, params.mShouldReplaceCurrentEntry); - } + navigate2(uri, params == null ? false : params.mShouldReplaceCurrentEntry, false, false, + false); + } + + @Override + public void navigate2(String uri, boolean shouldReplaceCurrentEntry, + boolean disableIntentProcessing, boolean disableNetworkErrorAutoReload, + boolean enableAutoPlay) throws RemoteException { + StrictModeWorkaround.apply(); + NavigationControllerImplJni.get().navigate(mNativeNavigationController, uri, + shouldReplaceCurrentEntry, disableIntentProcessing, disableNetworkErrorAutoReload, + enableAutoPlay); } @Override @@ -177,9 +183,9 @@ public final class NavigationControllerImpl extends INavigationController.Stub { void setNavigationControllerImpl( long nativeNavigationControllerImpl, NavigationControllerImpl caller); long getNavigationController(long tab); - void navigate(long nativeNavigationControllerImpl, String uri); - void navigateWithParams( - long nativeNavigationControllerImpl, String uri, boolean shouldReplaceCurrentEntry); + void navigate(long nativeNavigationControllerImpl, String uri, + boolean shouldReplaceCurrentEntry, boolean disableIntentProcessing, + boolean disableNetworkErrorAutoReload, boolean enableAutoPlay); void goBack(long nativeNavigationControllerImpl); void goForward(long nativeNavigationControllerImpl); boolean canGoBack(long nativeNavigationControllerImpl); diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java index cff0bc6b92d..7815edb19f1 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java @@ -147,6 +147,20 @@ public final class NavigationImpl extends INavigation.Stub { return NavigationImplJni.get().wasStopCalled(mNativeNavigationImpl); } + @Override + public boolean isPageInitiated() { + StrictModeWorkaround.apply(); + throwIfNativeDestroyed(); + return NavigationImplJni.get().isPageInitiated(mNativeNavigationImpl); + } + + @Override + public boolean isReload() { + StrictModeWorkaround.apply(); + throwIfNativeDestroyed(); + return NavigationImplJni.get().isReload(mNativeNavigationImpl); + } + private void throwIfNativeDestroyed() { if (mNativeNavigationImpl == 0) { throw new IllegalStateException("Using Navigation after native destroyed"); @@ -195,5 +209,7 @@ public final class NavigationImpl extends INavigation.Stub { boolean isValidRequestHeaderName(String name); boolean isValidRequestHeaderValue(String value); boolean setUserAgentString(long nativeNavigationImpl, String value); + boolean isPageInitiated(long nativeNavigationImpl); + boolean isReload(long nativeNavigationImpl); } } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java index 25645acdc76..a547f633d99 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NewTabCallbackProxy.java @@ -52,7 +52,7 @@ public final class NewTabCallbackProxy { // This class should only be created while the tab is attached to a fragment. assert mTab.getBrowser() != null; assert mTab.getBrowser().equals(tab.getBrowser()); - mTab.getClient().onNewTab(tab.getId(), mode); + mTab.getClient().onNewTab(tab.getId(), implTypeToJavaType(mode)); } @CalledByNative diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java index 314b6b7ff29..e9ff295181c 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java @@ -11,8 +11,10 @@ import androidx.annotation.NonNull; import org.chromium.base.StrictModeContext; import org.chromium.base.supplier.Supplier; +import org.chromium.components.browser_ui.site_settings.SiteSettingsClient; import org.chromium.components.content_settings.CookieControlsBridge; import org.chromium.components.content_settings.CookieControlsObserver; +import org.chromium.components.embedder_support.browser_context.BrowserContextHandle; import org.chromium.components.embedder_support.util.UrlConstants; import org.chromium.components.page_info.PageInfoControllerDelegate; import org.chromium.content_public.browser.WebContents; @@ -26,7 +28,7 @@ import org.chromium.weblayer_private.interfaces.SiteSettingsIntentHelper; public class PageInfoControllerDelegateImpl extends PageInfoControllerDelegate { private final Context mContext; private final WebContents mWebContents; - private final String mProfileName; + private final ProfileImpl mProfile; static PageInfoControllerDelegateImpl create(WebContents webContents) { TabImpl tab = TabImpl.fromWebContents(webContents); @@ -45,7 +47,7 @@ public class PageInfoControllerDelegateImpl extends PageInfoControllerDelegate { CookieControlsBridge.isCookieControlsEnabled(profile)); mContext = context; mWebContents = webContents; - mProfileName = profile.getName(); + mProfile = profile; } /** @@ -53,8 +55,8 @@ public class PageInfoControllerDelegateImpl extends PageInfoControllerDelegate { */ @Override public void showSiteSettings(String url) { - Intent intent = - SiteSettingsIntentHelper.createIntentForSingleWebsite(mContext, mProfileName, url); + Intent intent = SiteSettingsIntentHelper.createIntentForSingleWebsite( + mContext, mProfile.getName(), url); // Disabling StrictMode to avoid violations (https://crbug.com/819410). try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { @@ -71,6 +73,24 @@ public class PageInfoControllerDelegateImpl extends PageInfoControllerDelegate { return new CookieControlsBridge(observer, mWebContents, null); } + /** + * {@inheritDoc} + */ + @Override + @NonNull + public BrowserContextHandle getBrowserContext() { + return mProfile; + } + + /** + * {@inheritDoc} + */ + @Override + @NonNull + public SiteSettingsClient getSiteSettingsClient() { + return new WebLayerSiteSettingsClient(getBrowserContext()); + } + private static boolean isHttpOrHttps(GURL url) { String scheme = url.getScheme(); return UrlConstants.HTTP_SCHEME.equals(scheme) || UrlConstants.HTTPS_SCHEME.equals(scheme); diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java index c359dc3513d..c5246c384fd 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java @@ -5,6 +5,7 @@ package org.chromium.weblayer_private; import android.content.Intent; +import android.graphics.Bitmap; import android.text.TextUtils; import android.webkit.ValueCallback; @@ -206,6 +207,16 @@ public final class ProfileImpl extends IProfile.Stub implements BrowserContextHa ProfileImplJni.get().prepareForPossibleCrossOriginNavigation(mNativeProfile); } + @Override + public void getCachedFaviconForPageUri(@NonNull String uri, @NonNull IObjectWrapper callback) { + StrictModeWorkaround.apply(); + checkNotDestroyed(); + ValueCallback<Bitmap> valueCallback = + (ValueCallback<Bitmap>) ObjectWrapper.unwrap(callback, ValueCallback.class); + Callback<Bitmap> baseCallback = valueCallback::onReceiveValue; + ProfileImplJni.get().getCachedFaviconForPageUrl(mNativeProfile, uri, baseCallback); + } + void checkNotDestroyed() { if (!mBeingDeleted) return; throw new IllegalArgumentException("Profile being destroyed: " + mName); @@ -224,6 +235,9 @@ public final class ProfileImpl extends IProfile.Stub implements BrowserContextHa case BrowsingDataType.CACHE: convertedTypes.add(ImplBrowsingDataType.CACHE); break; + case BrowsingDataType.SITE_SETTINGS: + convertedTypes.add(ImplBrowsingDataType.SITE_SETTINGS); + break; default: break; // Skip unrecognized values for forward compatibility. } @@ -274,5 +288,7 @@ public final class ProfileImpl extends IProfile.Stub implements BrowserContextHa void removeBrowserPersistenceStorage( long nativeProfileImpl, String[] ids, Callback<Boolean> callback); void prepareForPossibleCrossOriginNavigation(long nativeProfileImpl); + void getCachedFaviconForPageUrl( + long nativeProfileImpl, String url, Callback<Bitmap> callback); } } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/SiteSettingsFragmentImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/SiteSettingsFragmentImpl.java index 072df069fb9..b42613ee17a 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/SiteSettingsFragmentImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/SiteSettingsFragmentImpl.java @@ -7,13 +7,17 @@ package org.chromium.weblayer_private; import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.util.AttributeSet; import android.view.ContextThemeWrapper; +import android.view.InflateException; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.ViewGroup; +import android.view.ViewStub; import android.view.Window; import androidx.appcompat.app.AppCompatDelegate; @@ -38,6 +42,8 @@ import org.chromium.weblayer_private.interfaces.SiteSettingsFragmentArgs; import org.chromium.weblayer_private.interfaces.SiteSettingsIntentHelper; import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; +import java.lang.reflect.Constructor; + /** * WebLayer's implementation of the client library's SiteSettingsFragment. * @@ -77,11 +83,19 @@ public class SiteSettingsFragmentImpl extends RemoteFragmentImpl { */ private static class PassthroughFragmentActivity extends FragmentActivity implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback { + private static final Class<?>[] VIEW_CONSTRUCTOR_ARGS = + new Class[] {Context.class, AttributeSet.class}; + private final SiteSettingsFragmentImpl mFragmentImpl; private PassthroughFragmentActivity(SiteSettingsFragmentImpl fragmentImpl) { mFragmentImpl = fragmentImpl; attachBaseContext(mFragmentImpl.getWebLayerContext()); + // Register ourselves as a the LayoutInflater factory so we can handle loading Views. + // See onCreateView for information about why this is needed. + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { + getLayoutInflater().setFactory2(this); + } // This class doesn't extend AppCompatActivity, so some appcompat functionality doesn't // get initialized, which leads to some appcompat widgets (like switches) rendering // incorrectly. There are some resource issues with having this class extend @@ -104,6 +118,54 @@ public class SiteSettingsFragmentImpl extends RemoteFragmentImpl { Context.LAYOUT_INFLATER_SERVICE); } + // This method is needed to work around a LayoutInflater bug in Android <N. Before + // LayoutInflater creates an instance of a View, it needs to look up the class by name to + // get a reference to its Constructor. As an optimization, it caches this name to + // Constructor mapping. This cache causes issues if a class gets loaded multiple times with + // different ClassLoaders. In Site Settings, some AndroidX Views get loaded early on with + // the embedding app's ClassLoader, so the Constructor from that ClassLoader's version of + // the class gets cached. When the WebLayer implementation later tries to inflate the same + // class, it instantiates a version from the wrong ClassLoader, which leads to a + // ClassCastException when casting that View to its original class. This was fixed in + // Android N, but to work around it on L & M, we inflate the Views manually here, which + // bypasses LayoutInflater's cache. + @Override + public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { + // If the class doesn't have a '.' in its name, it's probably a built-in Android View, + // which are often referenced by just their class names with no package prefix. For + // these classes we can return null to fall back to LayoutInflater's default behavior. + if (name.indexOf('.') == -1) { + return null; + } + + Class<? extends View> clazz = null; + try { + clazz = context.getClassLoader().loadClass(name).asSubclass(View.class); + LayoutInflater inflater = getLayoutInflater(); + if (inflater.getFilter() != null && !inflater.getFilter().onLoadClass(clazz)) { + throw new InflateException(attrs.getPositionDescription() + + ": Class not allowed to be inflated " + name); + } + + Constructor<? extends View> constructor = + clazz.getConstructor(VIEW_CONSTRUCTOR_ARGS); + constructor.setAccessible(true); + View view = constructor.newInstance(new Object[] {context, attrs}); + if (view instanceof ViewStub) { + // Use the same Context when inflating ViewStub later. + ViewStub viewStub = (ViewStub) view; + viewStub.setLayoutInflater(inflater.cloneInContext(context)); + } + return view; + } catch (Exception e) { + InflateException ie = new InflateException(attrs.getPositionDescription() + + ": Error inflating class " + + (clazz == null ? "<unknown>" : clazz.getName())); + ie.initCause(e); + throw ie; + } + } + @Override public Window getWindow() { return getEmbedderActivity().getWindow(); diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/SwipableOverlayView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/SwipableOverlayView.java deleted file mode 100644 index 7f46f8afcd2..00000000000 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/SwipableOverlayView.java +++ /dev/null @@ -1,421 +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. - -package org.chromium.weblayer_private; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.content.Context; -import android.graphics.Region; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; -import android.widget.FrameLayout; - -import androidx.annotation.IntDef; - -import org.chromium.base.MathUtils; -import org.chromium.content_public.browser.GestureListenerManager; -import org.chromium.content_public.browser.GestureStateListenerWithScroll; -import org.chromium.content_public.browser.WebContents; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * View that slides up from the bottom of the page and slides away as the user scrolls the page. - * Meant to be tacked onto the {@link org.chromium.content_public.browser.WebContents}'s view and - * alerted when either the page scroll position or viewport size changes. - * - * GENERAL BEHAVIOR - * This View is brought onto the screen by sliding upwards from the bottom of the screen. Afterward - * the View slides onto and off of the screen vertically as the user scrolls upwards or - * downwards on the page. - * - * As the scroll offset or the viewport height are updated via a scroll or fling, the difference - * from the initial value is used to determine the View's Y-translation. If a gesture is stopped, - * the View will be snapped back into the center of the screen or entirely off of the screen, based - * on how much of the View is visible, or where the user is currently located on the page. - */ -public abstract class SwipableOverlayView extends FrameLayout { - private static final float FULL_THRESHOLD = 0.5f; - private static final float VERTICAL_FLING_SHOW_THRESHOLD = 0.2f; - private static final float VERTICAL_FLING_HIDE_THRESHOLD = 0.9f; - - @IntDef({Gesture.NONE, Gesture.SCROLLING, Gesture.FLINGING}) - @Retention(RetentionPolicy.SOURCE) - private @interface Gesture { - int NONE = 0; - int SCROLLING = 1; - int FLINGING = 2; - } - - private static final long ANIMATION_DURATION_MS = 250; - - /** Detects when the user is dragging the WebContents. */ - private final GestureStateListenerWithScroll mGestureStateListener; - - /** Listens for changes in the layout. */ - private final View.OnLayoutChangeListener mLayoutChangeListener; - - /** Interpolator used for the animation. */ - private final Interpolator mInterpolator; - - /** Tracks whether the user is scrolling or flinging. */ - private @Gesture int mGestureState; - - /** Animation currently being used to translate the View. */ - private Animator mCurrentAnimation; - - /** Used to determine when the layout has changed and the Viewport must be updated. */ - private int mParentHeight; - - /** Offset from the top of the page when the current gesture was first started. */ - private int mInitialOffsetY; - - /** How tall the View is, including its margins. */ - private int mTotalHeight; - - /** Whether or not the View ever been fully displayed. */ - private boolean mIsBeingDisplayedForFirstTime; - - /** The WebContents to which the overlay is added. */ - private WebContents mWebContents; - - /** - * Creates a SwipableOverlayView. - * @param context Context for acquiring resources. - * @param attrs Attributes from the XML layout inflation. - */ - public SwipableOverlayView(Context context, AttributeSet attrs) { - super(context, attrs); - mGestureStateListener = createGestureStateListener(); - mGestureState = Gesture.NONE; - mLayoutChangeListener = createLayoutChangeListener(); - mInterpolator = new DecelerateInterpolator(1.0f); - - // We make this view 'draw' to provide a placeholder for its animations. - setWillNotDraw(false); - } - - /** - * Set the given WebContents for scrolling changes. - */ - public void setWebContents(WebContents webContents) { - if (mWebContents != null) { - GestureListenerManager.fromWebContents(mWebContents) - .removeListener(mGestureStateListener); - } - - mWebContents = webContents; - // See comment in onLayout() as to why the listener is only attached if mTotalHeight is > 0. - if (mWebContents != null && mTotalHeight > 0) { - GestureListenerManager.fromWebContents(mWebContents).addListener(mGestureStateListener); - } - } - - public WebContents getWebContents() { - return mWebContents; - } - - protected void addToParentView(ViewGroup parentView, int index) { - if (parentView == null) return; - if (getParent() == null) { - parentView.addView(this, index, createLayoutParams()); - - // Listen for the layout to know when to animate the View coming onto the screen. - addOnLayoutChangeListener(mLayoutChangeListener); - } - } - - /** - * Removes the SwipableOverlayView from its parent and stops monitoring the WebContents. - * @return Whether the View was removed from its parent. - */ - public boolean removeFromParentView() { - if (getParent() == null) return false; - - ((ViewGroup) getParent()).removeView(this); - removeOnLayoutChangeListener(mLayoutChangeListener); - return true; - } - - /** - * Creates a set of LayoutParams that makes the View hug the bottom of the screen. Override it - * for other types of behavior. - * @return LayoutParams for use when adding the View to its parent. - */ - public ViewGroup.MarginLayoutParams createLayoutParams() { - return new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, - Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - if (!isAllowedToAutoHide()) setTranslationY(0.0f); - } - - @Override - public void onWindowFocusChanged(boolean hasWindowFocus) { - super.onWindowFocusChanged(hasWindowFocus); - if (!isAllowedToAutoHide()) setTranslationY(0.0f); - } - - /** - * See {@link #android.view.ViewGroup.onLayout(boolean, int, int, int, int)}. - */ - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - // Update the viewport height when the parent View's height changes (e.g. after rotation). - int currentParentHeight = getParent() == null ? 0 : ((View) getParent()).getHeight(); - if (mParentHeight != currentParentHeight) { - mParentHeight = currentParentHeight; - mGestureState = Gesture.NONE; - if (mCurrentAnimation != null) mCurrentAnimation.end(); - } - - // Update the known effective height of the View. - MarginLayoutParams params = (MarginLayoutParams) getLayoutParams(); - mTotalHeight = getMeasuredHeight() + params.topMargin + params.bottomMargin; - - // Adding a listener to GestureListenerManager results in extra IPCs on every frame, which - // is very costly. Only attach the listener if needed. - if (mWebContents != null) { - if (mTotalHeight > 0) { - GestureListenerManager.fromWebContents(mWebContents) - .addListener(mGestureStateListener); - } else { - GestureListenerManager.fromWebContents(mWebContents) - .removeListener(mGestureStateListener); - } - } - - super.onLayout(changed, l, t, r, b); - } - - /** - * Creates a listener than monitors the WebContents for scrolls and flings. - * The listener updates the location of this View to account for the user's gestures. - * @return GestureStateListenerWithScroll to send to the WebContents. - */ - private GestureStateListenerWithScroll createGestureStateListener() { - return new GestureStateListenerWithScroll() { - /** Tracks the previous event's scroll offset to determine if a scroll is up or down. */ - private int mLastScrollOffsetY; - - /** Location of the View when the current gesture was first started. */ - private float mInitialTranslationY; - - /** The initial extent of the scroll when triggered. */ - private float mInitialExtentY; - - @Override - public void onFlingStartGesture(int scrollOffsetY, int scrollExtentY) { - if (!isAllowedToAutoHide() || !cancelCurrentAnimation()) return; - resetInternalScrollState(scrollOffsetY, scrollExtentY); - mGestureState = Gesture.FLINGING; - } - - @Override - public void onFlingEndGesture(int scrollOffsetY, int scrollExtentY) { - if (mGestureState != Gesture.FLINGING) return; - mGestureState = Gesture.NONE; - - updateTranslation(scrollOffsetY, scrollExtentY); - - boolean isScrollingDownward = scrollOffsetY > mLastScrollOffsetY; - - boolean isVisibleInitially = mInitialTranslationY < mTotalHeight; - float percentageVisible = 1.0f - (getTranslationY() / mTotalHeight); - float visibilityThreshold = isVisibleInitially ? VERTICAL_FLING_HIDE_THRESHOLD - : VERTICAL_FLING_SHOW_THRESHOLD; - boolean isVisibleEnough = percentageVisible > visibilityThreshold; - boolean isNearTopOfPage = scrollOffsetY < (mTotalHeight * FULL_THRESHOLD); - - boolean show = (!isScrollingDownward && isVisibleEnough) || isNearTopOfPage; - - runUpEventAnimation(show); - } - - @Override - public void onScrollStarted(int scrollOffsetY, int scrollExtentY) { - if (!isAllowedToAutoHide() || !cancelCurrentAnimation()) return; - resetInternalScrollState(scrollOffsetY, scrollExtentY); - mLastScrollOffsetY = scrollOffsetY; - mGestureState = Gesture.SCROLLING; - } - - @Override - public void onScrollEnded(int scrollOffsetY, int scrollExtentY) { - if (mGestureState != Gesture.SCROLLING) return; - mGestureState = Gesture.NONE; - - updateTranslation(scrollOffsetY, scrollExtentY); - - runUpEventAnimation(shouldSnapToVisibleState(scrollOffsetY)); - } - - @Override - public void onScrollOffsetOrExtentChanged(int scrollOffsetY, int scrollExtentY) { - mLastScrollOffsetY = scrollOffsetY; - - if (!shouldConsumeScroll(scrollOffsetY, scrollExtentY)) { - resetInternalScrollState(scrollOffsetY, scrollExtentY); - return; - } - - // This function is called for both fling and scrolls. - if (mGestureState == Gesture.NONE || !cancelCurrentAnimation() - || isIndependentlyAnimating()) { - return; - } - - updateTranslation(scrollOffsetY, scrollExtentY); - } - - private void updateTranslation(int scrollOffsetY, int scrollExtentY) { - float scrollDiff = - (scrollOffsetY - mInitialOffsetY) + (scrollExtentY - mInitialExtentY); - float translation = - MathUtils.clamp(mInitialTranslationY + scrollDiff, mTotalHeight, 0); - - // If the container has reached the completely shown position, reset the initial - // scroll so any movement will start hiding it again. - if (translation <= 0f) resetInternalScrollState(scrollOffsetY, scrollExtentY); - - setTranslationY(translation); - } - - /** - * Resets the internal values that a scroll or fling will base its calculations off of. - */ - private void resetInternalScrollState(int scrollOffsetY, int scrollExtentY) { - mInitialOffsetY = scrollOffsetY; - mInitialExtentY = scrollExtentY; - mInitialTranslationY = getTranslationY(); - } - }; - } - - /** - * @param scrollOffsetY The current scroll offset on the Y axis. - * @param scrollExtentY The current scroll extent on the Y axis. - * @return Whether or not the scroll should be consumed by the view. - */ - protected boolean shouldConsumeScroll(int scrollOffsetY, int scrollExtentY) { - return true; - } - - /** - * @param scrollOffsetY The current scroll offset on the Y axis. - * @return Whether the view should snap to a visible state. - */ - protected boolean shouldSnapToVisibleState(int scrollOffsetY) { - boolean isNearTopOfPage = scrollOffsetY < (mTotalHeight * FULL_THRESHOLD); - boolean isVisibleEnough = getTranslationY() < mTotalHeight * FULL_THRESHOLD; - return isNearTopOfPage || isVisibleEnough; - } - - /** - * @return Whether or not the view is animating independent of the user's scroll position. - */ - protected boolean isIndependentlyAnimating() { - return false; - } - - /** - * Creates a listener that is used only to animate the View coming onto the screen. - * @return The SimpleOnGestureListener that will monitor the View. - */ - private View.OnLayoutChangeListener createLayoutChangeListener() { - return new View.OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - removeOnLayoutChangeListener(mLayoutChangeListener); - - // Animate the View coming in from the bottom of the screen. - setTranslationY(mTotalHeight); - mIsBeingDisplayedForFirstTime = true; - runUpEventAnimation(true); - } - }; - } - - /** - * Create an animation that snaps the View into position vertically. - * @param visible If true, snaps the View to the bottom-center of the screen. If false, - * translates the View below the bottom-center of the screen so that it is - * effectively invisible. - * @return An animator with the snap animation. - */ - protected Animator createVerticalSnapAnimation(boolean visible) { - float targetTranslationY = visible ? 0.0f : mTotalHeight; - float yDifference = Math.abs(targetTranslationY - getTranslationY()) / mTotalHeight; - long duration = Math.max(0, (long) (ANIMATION_DURATION_MS * yDifference)); - - Animator animator = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, targetTranslationY); - animator.setDuration(duration); - animator.setInterpolator(mInterpolator); - - return animator; - } - - /** - * Run an animation when a gesture has ended (an 'up' motion event). - * @param visible Whether or not the view should be visible. - */ - protected void runUpEventAnimation(boolean visible) { - if (mCurrentAnimation != null) mCurrentAnimation.cancel(); - mCurrentAnimation = createVerticalSnapAnimation(visible); - mCurrentAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mGestureState = Gesture.NONE; - mCurrentAnimation = null; - mIsBeingDisplayedForFirstTime = false; - } - }); - mCurrentAnimation.start(); - } - - /** - * Cancels the current animation, unless the View is coming onto the screen for the first time. - * @return True if the animation was canceled or wasn't running, false otherwise. - */ - private boolean cancelCurrentAnimation() { - if (mIsBeingDisplayedForFirstTime) return false; - if (mCurrentAnimation != null) mCurrentAnimation.cancel(); - return true; - } - - /** - * @return Whether the SwipableOverlayView is allowed to hide itself on scroll. - */ - protected boolean isAllowedToAutoHide() { - return true; - } - - /** - * Override gatherTransparentRegion to make this view's layout a placeholder for its - * animations. This is only called during layout, so it doesn't really make sense to apply - * post-layout properties like it does by default. Together with setWillNotDraw(false), - * this ensures no child animation within this view's layout will be clipped by a SurfaceView. - */ - @Override - public boolean gatherTransparentRegion(Region region) { - float translationY = getTranslationY(); - setTranslationY(0); - boolean result = super.gatherTransparentRegion(region); - // Restoring TranslationY invalidates this view unnecessarily. However, this function - // is called as part of layout, which implies a full redraw is about to occur anyway. - setTranslationY(translationY); - return result; - } -} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java index 382bfb7c605..b8868d621b2 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java @@ -4,6 +4,7 @@ package org.chromium.weblayer_private; +import android.app.Activity; import android.graphics.Bitmap; import android.graphics.RectF; import android.os.Build; @@ -18,6 +19,7 @@ import android.view.ViewStructure; import android.view.autofill.AutofillValue; import android.webkit.ValueCallback; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import org.chromium.base.Callback; @@ -26,15 +28,18 @@ import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; import org.chromium.components.autofill.AutofillActionModeCallback; import org.chromium.components.autofill.AutofillProvider; +import org.chromium.components.browser_ui.display_cutout.DisplayCutoutController; import org.chromium.components.browser_ui.http_auth.LoginPrompt; import org.chromium.components.browser_ui.media.MediaSessionHelper; import org.chromium.components.browser_ui.util.BrowserControlsVisibilityDelegate; import org.chromium.components.browser_ui.util.ComposedBrowserControlsVisibilityDelegate; +import org.chromium.components.browser_ui.widget.InsetObserverView; import org.chromium.components.embedder_support.contextmenu.ContextMenuParams; import org.chromium.components.external_intents.InterceptNavigationDelegateImpl; import org.chromium.components.find_in_page.FindInPageBridge; import org.chromium.components.find_in_page.FindMatchRectsDetails; import org.chromium.components.find_in_page.FindResultBar; +import org.chromium.components.infobars.InfoBar; import org.chromium.components.url_formatter.UrlFormatter; import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.NavigationHandle; @@ -51,8 +56,11 @@ import org.chromium.url.GURL; import org.chromium.weblayer_private.interfaces.APICallException; import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient; import org.chromium.weblayer_private.interfaces.IErrorPageCallbackClient; +import org.chromium.weblayer_private.interfaces.IFaviconFetcher; +import org.chromium.weblayer_private.interfaces.IFaviconFetcherClient; import org.chromium.weblayer_private.interfaces.IFindInPageCallbackClient; import org.chromium.weblayer_private.interfaces.IFullscreenCallbackClient; +import org.chromium.weblayer_private.interfaces.IGoogleAccountsCallbackClient; import org.chromium.weblayer_private.interfaces.IMediaCaptureCallbackClient; import org.chromium.weblayer_private.interfaces.INavigationControllerClient; import org.chromium.weblayer_private.interfaces.IObjectWrapper; @@ -65,8 +73,10 @@ import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** * Implementation of ITab. @@ -86,7 +96,10 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { private ErrorPageCallbackProxy mErrorPageCallbackProxy; private FullscreenCallbackProxy mFullscreenCallbackProxy; private TabViewAndroidDelegate mViewAndroidDelegate; - // BrowserImpl this TabImpl is in. This is only null during creation. + private GoogleAccountsCallbackProxy mGoogleAccountsCallbackProxy; + // BrowserImpl this TabImpl is in. This is null before attached to a Browser. While this is null + // before attached, there are code paths that may trigger calling methods before set. + @Nullable private BrowserImpl mBrowser; private LoginPrompt mLoginPrompt; /** @@ -115,11 +128,14 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { private InterceptNavigationDelegateImpl mInterceptNavigationDelegate; private InfoBarContainer mInfoBarContainer; private MediaSessionHelper mMediaSessionHelper; + private DisplayCutoutController mDisplayCutoutController; private boolean mPostContainerViewInitDone; private WebLayerAccessibilityUtil.Observer mAccessibilityObserver; + private Set<FaviconCallbackProxy> mFaviconCallbackProxies = new HashSet<>(); + private static class InternalAccessDelegateImpl implements ViewEventSink.InternalAccessDelegate { @Override @@ -240,6 +256,11 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { hideFindInPageUiAndNotifyClient(); } } + @Override + public void viewportFitChanged(@WebContentsObserver.ViewportFitType int value) { + ensureDisplayCutoutController(); + mDisplayCutoutController.setViewportFit(value); + } }; mWebContents.addObserver(mWebContentsObserver); @@ -253,7 +274,8 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { mBrowserControlsDelegates.add(delegate); mBrowserControlsVisibility.addDelegate(delegate); } - mConstraintsUpdatedCallback = (constraints) -> onBrowserControlsStateUpdated(constraints); + mConstraintsUpdatedCallback = + (constraint) -> onBrowserControlsConstraintUpdated(constraint); mBrowserControlsVisibility.addObserver(mConstraintsUpdatedCallback); mInterceptNavigationDelegateClient = new InterceptNavigationDelegateClientImpl(this); @@ -309,7 +331,7 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { mWebContents.setTopLevelNativeWindow(mBrowser.getWindowAndroid()); mViewAndroidDelegate.setContainerView(mBrowser.getViewAndroidDelegateContainerView()); doInitAfterSettingContainerView(); - updateWebContentsVisibility(); + updateViewAttachedStateFromBrowser(); boolean attached = (mBrowser.getContext() != null); mInterceptNavigationDelegateClient.onActivityAttachmentChanged(attached); @@ -341,6 +363,11 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { } } + public void updateViewAttachedStateFromBrowser() { + updateWebContentsVisibility(); + updateDisplayCutoutController(); + } + public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) { if (mAutofillProvider == null) return; mAutofillProvider.onProvideAutoFillVirtualStructure(structure, flags); @@ -383,6 +410,7 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { mNativeTab, topControlsContainerViewHandle, bottomControlsContainerViewHandle); mInfoBarContainer.onTabDidGainActive(); updateWebContentsVisibility(); + updateDisplayCutoutController(); } /** @@ -393,8 +421,11 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { mAutofillProvider.hidePopup(); } + if (mFullscreenCallbackProxy != null) mFullscreenCallbackProxy.destroyToast(); + hideFindInPageUiAndNotifyClient(); updateWebContentsVisibility(); + updateDisplayCutoutController(); // This method is called as part of the final phase of TabImpl destruction, at which // point mInfoBarContainer has already been destroyed. @@ -409,8 +440,13 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { * Returns whether this Tab is visible. */ public boolean isVisible() { - return (mBrowser.getActiveTab() == this - && (mBrowser.isStarted() || mBrowser.isFragmentStoppedForConfigurationChange())); + return isActiveTab() + && ((mBrowser.isStarted() && mBrowser.isViewAttachedToWindow()) + || mBrowser.isFragmentStoppedForConfigurationChange()); + } + + private boolean isActiveTab() { + return mBrowser != null && mBrowser.getActiveTab() == this; } private void updateWebContentsVisibility() { @@ -423,6 +459,13 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { } } + private void updateDisplayCutoutController() { + if (mDisplayCutoutController == null) return; + + mDisplayCutoutController.onActivityAttachmentChanged(mBrowser.getWindowAndroid()); + mDisplayCutoutController.maybeUpdateLayout(); + } + public void loadUrl(LoadUrlParams loadUrlParams) { String url = loadUrlParams.getUrl(); if (url == null || url.isEmpty()) return; @@ -491,7 +534,7 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { StrictModeWorkaround.apply(); if (client != null) { if (mFullscreenCallbackProxy == null) { - mFullscreenCallbackProxy = new FullscreenCallbackProxy(mNativeTab, client); + mFullscreenCallbackProxy = new FullscreenCallbackProxy(this, mNativeTab, client); } else { mFullscreenCallbackProxy.setClient(client); } @@ -502,6 +545,38 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { } @Override + public void setGoogleAccountsCallbackClient(IGoogleAccountsCallbackClient client) { + StrictModeWorkaround.apply(); + if (client != null) { + if (mGoogleAccountsCallbackProxy == null) { + mGoogleAccountsCallbackProxy = new GoogleAccountsCallbackProxy(mNativeTab, client); + } else { + mGoogleAccountsCallbackProxy.setClient(client); + } + } else if (mGoogleAccountsCallbackProxy != null) { + mGoogleAccountsCallbackProxy.destroy(); + mGoogleAccountsCallbackProxy = null; + } + } + + @Override + public IFaviconFetcher createFaviconFetcher(IFaviconFetcherClient client) { + StrictModeWorkaround.apply(); + FaviconCallbackProxy proxy = new FaviconCallbackProxy(this, mNativeTab, client); + mFaviconCallbackProxies.add(proxy); + return proxy; + } + + @Override + public void setTranslateTargetLanguage(String targetLanguage) { + TabImplJni.get().setTranslateTargetLanguage(mNativeTab, targetLanguage); + } + + public void removeFaviconCallbackProxy(FaviconCallbackProxy proxy) { + mFaviconCallbackProxies.remove(proxy); + } + + @Override public void executeScript(String script, boolean useSeparateIsolate, IObjectWrapper callback) { StrictModeWorkaround.apply(); Callback<String> nativeCallback = new Callback<String>() { @@ -798,6 +873,11 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { } } + if (mDisplayCutoutController != null) { + mDisplayCutoutController.destroy(); + mDisplayCutoutController = null; + } + if (mTabCallbackProxy != null) { mTabCallbackProxy.destroy(); mTabCallbackProxy = null; @@ -814,8 +894,13 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { mNewTabCallbackProxy.destroy(); mNewTabCallbackProxy = null; } + if (mGoogleAccountsCallbackProxy != null) { + mGoogleAccountsCallbackProxy.destroy(); + mGoogleAccountsCallbackProxy = null; + } mInterceptNavigationDelegateClient.destroy(); + mInterceptNavigationDelegateClient = null; mInterceptNavigationDelegate = null; mInfoBarContainer.destroy(); @@ -824,6 +909,15 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { mMediaStreamManager.destroy(); mMediaStreamManager = null; + // Destroying FaviconCallbackProxy removes from mFaviconCallbackProxies. Copy to avoid + // problems. + Set<FaviconCallbackProxy> faviconCallbackProxies = mFaviconCallbackProxies; + mFaviconCallbackProxies = new HashSet<>(); + for (FaviconCallbackProxy proxy : faviconCallbackProxies) { + proxy.destroy(); + } + assert mFaviconCallbackProxies.isEmpty(); + sTabMap.remove(mId); // ObservableSupplierImpl.addObserver() posts a task to notify the observer, ensure the @@ -852,6 +946,12 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { mBrowserControlsDelegates.get(reason).set(constraint); } + @BrowserControlsState + /* package */ int getBrowserControlsVisibilityConstraint( + @ImplControlsVisibilityReason int reason) { + return mBrowserControlsDelegates.get(reason).get(); + } + @CalledByNative public void showRepostFormWarningDialog() { BrowserViewController viewController = getViewController(); @@ -884,7 +984,7 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { if (mBrowser.getActiveTab() != this) return; - onBrowserControlsStateUpdated(mBrowserControlsVisibility.get()); + onBrowserControlsConstraintUpdated(mBrowserControlsVisibility.get()); } @VisibleForTesting @@ -892,40 +992,86 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { return mBrowserControlsVisibility.get() == BrowserControlsState.BOTH; } - private void onBrowserControlsStateUpdated(int state) { + @VisibleForTesting + public boolean didShowFullscreenToast() { + return mFullscreenCallbackProxy != null + && mFullscreenCallbackProxy.didShowFullscreenToast(); + } + + private void onBrowserControlsConstraintUpdated(int constraint) { + // WARNING: this may be called before attached. This means |mBrowser| may be null. + // If something has overridden the FIP's SHOWN constraint, cancel FIP. This causes FIP to // dismiss when entering fullscreen. - if (state != BrowserControlsState.SHOWN) { + if (constraint != BrowserControlsState.SHOWN) { hideFindInPageUiAndNotifyClient(); } - // Don't animate when hiding the controls. - boolean animate = state != BrowserControlsState.HIDDEN; + BrowserViewController viewController = getViewController(); + // Don't animate when hiding the controls unless an animation was requested by + // BrowserControlsContainerView. + boolean animate = constraint != BrowserControlsState.HIDDEN + || (viewController != null + && viewController.shouldAnimateBrowserControlsHeightChanges()); - // If the renderer is not controlling the offsets (possiblye hung or crashed). Then this + // If the renderer is not controlling the offsets (possibly hung or crashed). Then this // needs to force the controls to show (because notification from the renderer will not // happen). For js dialogs, the renderer's update will come when the dialog is hidden, and // since that animates from 0 height, it causes a flicker since the override is already set // to fully show. Thus, disable animation. - if (state == BrowserControlsState.SHOWN && mBrowser != null - && mBrowser.getActiveTab() == this + if (constraint == BrowserControlsState.SHOWN && isActiveTab() && !TabImplJni.get().isRendererControllingBrowserControlsOffsets(mNativeTab)) { mViewAndroidDelegate.setIgnoreRendererUpdates(true); - getViewController().showControls(); + if (viewController != null) viewController.showControls(); animate = false; } else { mViewAndroidDelegate.setIgnoreRendererUpdates(false); } - TabImplJni.get().updateBrowserControlsState(mNativeTab, state, animate); + TabImplJni.get().updateBrowserControlsConstraint(mNativeTab, constraint, animate); + } + + private void ensureDisplayCutoutController() { + if (mDisplayCutoutController != null) return; + + mDisplayCutoutController = + new DisplayCutoutController(new DisplayCutoutController.Delegate() { + @Override + public Activity getAttachedActivity() { + WindowAndroid window = mBrowser.getWindowAndroid(); + return window == null ? null : window.getActivity().get(); + } + + @Override + public WebContents getWebContents() { + return mWebContents; + } + + @Override + public InsetObserverView getInsetObserverView() { + return mBrowser.getViewController().getInsetObserverView(); + } + + @Override + public boolean isInteractable() { + return isVisible(); + } + }); } /** * Returns the BrowserViewController for this TabImpl, but only if this - * is the active TabImpl. + * is the active TabImpl. Can also return null if in the middle of shutdown + * or Browser is not attached to any activity. */ + @Nullable private BrowserViewController getViewController() { - return (mBrowser.getActiveTab() == this) ? mBrowser.getViewController() : null; + if (!isActiveTab()) return null; + // During rotation it's possible for this to be called before BrowserViewController has been + // updated. Verify BrowserViewController reflects this is the active tab before returning + // it. + BrowserViewController viewController = mBrowser.getPossiblyNullViewController(); + return viewController != null && viewController.getTab() == this ? viewController : null; } @VisibleForTesting @@ -933,6 +1079,16 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { return mInfoBarContainer.getContainerViewForTesting().isAllowedToAutoHide(); } + @VisibleForTesting + public String getTranslateInfoBarTargetLanguageForTesting() { + if (!mInfoBarContainer.hasInfoBars()) return null; + + ArrayList<InfoBar> infobars = mInfoBarContainer.getInfoBarsForTesting(); + TranslateCompactInfoBar translateInfoBar = (TranslateCompactInfoBar) infobars.get(0); + + return translateInfoBar.getTargetLanguageForTesting(); + } + @NativeMethods interface Natives { TabImpl fromWebContents(WebContents webContents); @@ -946,7 +1102,8 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { WebContents getWebContents(long nativeTabImpl); void executeScript(long nativeTabImpl, String script, boolean useSeparateIsolate, Callback<String> callback); - void updateBrowserControlsState(long nativeTabImpl, int newConstraint, boolean animate); + void updateBrowserControlsConstraint( + long nativeTabImpl, int newConstraint, boolean animate); String getGuid(long nativeTabImpl); void captureScreenShot(long nativeTabImpl, float scale, ValueCallback<Pair<Bitmap, Integer>> valueCallback); @@ -960,5 +1117,6 @@ public final class TabImpl extends ITab.Stub implements LoginPrompt.Observer { void unregisterWebMessageCallback(long nativeTabImpl, String jsObjectName); boolean canTranslate(long nativeTabImpl); void showTranslateUi(long nativeTabImpl); + void setTranslateTargetLanguage(long nativeTabImpl, String targetLanguage); } } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java index a97315e6fa4..9e7ca44dd30 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java @@ -20,6 +20,14 @@ import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; import org.chromium.base.metrics.RecordHistogram; +import org.chromium.chrome.browser.infobar.ActionType; +import org.chromium.components.infobars.InfoBar; +import org.chromium.components.infobars.InfoBarCompactLayout; +import org.chromium.components.translate.TranslateMenu; +import org.chromium.components.translate.TranslateMenuHelper; +import org.chromium.components.translate.TranslateOption; +import org.chromium.components.translate.TranslateOptions; +import org.chromium.components.translate.TranslateTabLayout; import org.chromium.ui.widget.Toast; /** @@ -463,6 +471,13 @@ public class TranslateCompactInfoBar extends InfoBar return this.isTabSelectedForTesting(TARGET_TAB_INDEX); } + /** + * Returns the name of the target language. This is only used for automation testing. + */ + public String getTargetLanguageForTesting() { + return mOptions.targetLanguageName(); + } + private void createAndShowSnackbar(int actionId) { // NOTE: WebLayer doesn't have snackbars, so the relevant action is just taken directly. // TODO(blundell): If WebLayer ends up staying with this implementation long-term, update @@ -541,9 +556,8 @@ public class TranslateCompactInfoBar extends InfoBar return mParent != null ? mParent.getWidth() : 0; } - @CalledByNative // Selects the tab corresponding to |actionType| to simulate the user pressing on this tab. - private void selectTabForTesting(int actionType) { + void selectTabForTesting(int actionType) { if (actionType == ActionType.TRANSLATE) { mTabLayout.getTabAt(TARGET_TAB_INDEX).select(); } else if (actionType == ActionType.TRANSLATE_SHOW_ORIGINAL) { @@ -553,18 +567,6 @@ public class TranslateCompactInfoBar extends InfoBar } } - @CalledByNative - // Simulates a click of the overflow menu item for "never translate this language." - private void clickNeverTranslateLanguageMenuItemForTesting() { - onOverflowMenuItemClicked(TranslateMenu.ID_OVERFLOW_NEVER_LANGUAGE); - } - - @CalledByNative - // Simulates a click of the overflow menu item for "never translate this site." - private void clickNeverTranslateSiteMenuItemForTesting() { - onOverflowMenuItemClicked(TranslateMenu.ID_OVERFLOW_NEVER_SITE); - } - @NativeMethods interface Natives { void applyStringTranslateOption(long nativeTranslateCompactInfoBar, diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenu.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenu.java deleted file mode 100644 index cfb1a06c2f5..00000000000 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenu.java +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.weblayer_private; - -import java.util.ArrayList; -import java.util.List; - -/** - * Translate menu config and its item entity definition. - */ -public final class TranslateMenu { - /** - * The menu item entity. - */ - static final class MenuItem { - public final int mType; - public final int mId; - public final String mCode; - public final boolean mWithDivider; - - MenuItem(int itemType, int itemId, boolean withDivider) { - this(itemType, itemId, EMPTY_STRING, withDivider); - } - - MenuItem(int itemType, int itemId, String code) { - this(itemType, itemId, code, false); - } - - MenuItem(int itemType, int itemId, String code, boolean withDivider) { - mType = itemType; - mId = itemId; - mCode = code; - mWithDivider = withDivider; - } - } - - public static final String EMPTY_STRING = ""; - - // Menu type config. - public static final int MENU_OVERFLOW = 0; - public static final int MENU_TARGET_LANGUAGE = 1; - public static final int MENU_SOURCE_LANGUAGE = 2; - - // Menu item type config. - public static final int ITEM_LANGUAGE = 0; - public static final int ITEM_CHECKBOX_OPTION = 1; - public static final int MENU_ITEM_TYPE_COUNT = 2; - - // Menu Item ID config for MENU_OVERFLOW. - public static final int ID_OVERFLOW_MORE_LANGUAGE = 0; - public static final int ID_OVERFLOW_ALWAYS_TRANSLATE = 1; - public static final int ID_OVERFLOW_NEVER_SITE = 2; - public static final int ID_OVERFLOW_NEVER_LANGUAGE = 3; - public static final int ID_OVERFLOW_NOT_THIS_LANGUAGE = 4; - - /** - * Build overflow menu item list. - */ - static List<MenuItem> getOverflowMenu(boolean isIncognito) { - List<MenuItem> menu = new ArrayList<MenuItem>(); - menu.add(new MenuItem(ITEM_CHECKBOX_OPTION, ID_OVERFLOW_MORE_LANGUAGE, true)); - if (!isIncognito) { - // "Always translate" does nothing in incognito mode, so just hide it. - menu.add(new MenuItem(ITEM_CHECKBOX_OPTION, ID_OVERFLOW_ALWAYS_TRANSLATE, false)); - } - menu.add(new MenuItem(ITEM_CHECKBOX_OPTION, ID_OVERFLOW_NEVER_LANGUAGE, false)); - menu.add(new MenuItem(ITEM_CHECKBOX_OPTION, ID_OVERFLOW_NEVER_SITE, false)); - menu.add(new MenuItem(ITEM_CHECKBOX_OPTION, ID_OVERFLOW_NOT_THIS_LANGUAGE, false)); - return menu; - } - - private TranslateMenu() {} -} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenuHelper.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenuHelper.java deleted file mode 100644 index 16454817172..00000000000 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateMenuHelper.java +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.weblayer_private; - -import android.content.Context; -import android.graphics.Rect; -import android.os.Build; -import android.view.ContextThemeWrapper; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.MeasureSpec; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.ListPopupWindow; -import android.widget.PopupWindow; -import android.widget.TextView; - -import androidx.core.content.ContextCompat; - -import java.util.ArrayList; -import java.util.List; - -/** - * A Helper class for managing the Translate Overflow Menu. - */ -public class TranslateMenuHelper implements AdapterView.OnItemClickListener { - private final TranslateMenuListener mMenuListener; - private final TranslateOptions mOptions; - - private ContextThemeWrapper mContextWrapper; - private TranslateMenuAdapter mAdapter; - private View mAnchorView; - private ListPopupWindow mPopup; - private boolean mIsIncognito; - - /** - * Interface for receiving the click event of menu item. - */ - public interface TranslateMenuListener { - void onOverflowMenuItemClicked(int itemId); - void onTargetMenuItemClicked(String code); - void onSourceMenuItemClicked(String code); - } - - public TranslateMenuHelper(Context context, View anchorView, TranslateOptions options, - TranslateMenuListener itemListener, boolean isIncognito) { - mContextWrapper = new ContextThemeWrapper(context, R.style.OverflowMenuThemeOverlay); - mAnchorView = anchorView; - mOptions = options; - mMenuListener = itemListener; - mIsIncognito = isIncognito; - } - - /** - * Build translate menu by menu type. - */ - private List<TranslateMenu.MenuItem> getMenuList(int menuType) { - List<TranslateMenu.MenuItem> menuList = new ArrayList<TranslateMenu.MenuItem>(); - if (menuType == TranslateMenu.MENU_OVERFLOW) { - // TODO(googleo): Add language short list above static menu after its data is ready. - menuList.addAll(TranslateMenu.getOverflowMenu(mIsIncognito)); - } else { - for (int i = 0; i < mOptions.allLanguages().size(); ++i) { - String code = mOptions.allLanguages().get(i).mLanguageCode; - // Avoid source language in the source language list. - if (menuType == TranslateMenu.MENU_SOURCE_LANGUAGE - && code.equals(mOptions.sourceLanguageCode())) { - continue; - } - // Avoid target language in the target language list. - if (menuType == TranslateMenu.MENU_TARGET_LANGUAGE - && code.equals(mOptions.targetLanguageCode())) { - continue; - } - menuList.add(new TranslateMenu.MenuItem(TranslateMenu.ITEM_LANGUAGE, i, code)); - } - } - return menuList; - } - - /** - * Show the overflow menu. - * @param menuType The type of overflow menu to show. - * @param maxwidth Maximum width of menu. Set to 0 when not specified. - */ - public void show(int menuType, int maxWidth) { - if (mPopup == null) { - mPopup = new ListPopupWindow(mContextWrapper, null, android.R.attr.popupMenuStyle); - mPopup.setModal(true); - mPopup.setAnchorView(mAnchorView); - mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); - - // Need to explicitly set the background here. Relying on it being set in the style - // caused an incorrectly drawn background. - // TODO(martiw): We might need a new menu background here. - mPopup.setBackgroundDrawable( - ContextCompat.getDrawable(mContextWrapper, R.drawable.popup_bg_tinted)); - - mPopup.setOnItemClickListener(this); - - // The menu must be shifted down by the height of the anchor view in order to be - // displayed over and above it. - int anchorHeight = mAnchorView.getHeight(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - // Setting a positive offset here shifts the menu down. - mPopup.setVerticalOffset(anchorHeight); - } else { - // The framework's PopupWindow positioning changed between N and M. Setting - // a negative offset here shifts the menu down rather than up. - mPopup.setVerticalOffset(-anchorHeight); - } - - mAdapter = new TranslateMenuAdapter(menuType); - mPopup.setAdapter(mAdapter); - } else { - mAdapter.refreshMenu(menuType); - } - - if (menuType == TranslateMenu.MENU_OVERFLOW) { - // Use measured width when it is a overflow menu. - Rect bgPadding = new Rect(); - mPopup.getBackground().getPadding(bgPadding); - int measuredWidth = measureMenuWidth(mAdapter) + bgPadding.left + bgPadding.right; - mPopup.setWidth((maxWidth > 0 && measuredWidth > maxWidth) ? maxWidth : measuredWidth); - } else { - // Use fixed width otherwise. - int popupWidth = mContextWrapper.getResources().getDimensionPixelSize( - R.dimen.weblayer_infobar_translate_menu_width); - mPopup.setWidth(popupWidth); - } - - // When layout is RTL, set the horizontal offset to align the menu with the left side of the - // screen. - if (mAnchorView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { - int[] tempLocation = new int[2]; - mAnchorView.getLocationOnScreen(tempLocation); - mPopup.setHorizontalOffset(-tempLocation[0]); - } - - if (!mPopup.isShowing()) { - mPopup.show(); - mPopup.getListView().setItemsCanFocus(true); - } - } - - private int measureMenuWidth(TranslateMenuAdapter adapter) { - final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - - final int count = adapter.getCount(); - int width = 0; - int itemType = 0; - View itemView = null; - for (int i = 0; i < count; i++) { - final int positionType = adapter.getItemViewType(i); - if (positionType != itemType) { - itemType = positionType; - itemView = null; - } - itemView = adapter.getView(i, itemView, null); - itemView.measure(widthMeasureSpec, heightMeasureSpec); - width = Math.max(width, itemView.getMeasuredWidth()); - } - return width; - } - - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - dismiss(); - - TranslateMenu.MenuItem item = mAdapter.getItem(position); - switch (mAdapter.mMenuType) { - case TranslateMenu.MENU_OVERFLOW: - mMenuListener.onOverflowMenuItemClicked(item.mId); - return; - case TranslateMenu.MENU_TARGET_LANGUAGE: - mMenuListener.onTargetMenuItemClicked(item.mCode); - return; - case TranslateMenu.MENU_SOURCE_LANGUAGE: - mMenuListener.onSourceMenuItemClicked(item.mCode); - return; - default: - assert false : "Unsupported Menu Item Id"; - } - } - - /** - * Dismisses the translate option menu. - */ - public void dismiss() { - if (isShowing()) { - mPopup.dismiss(); - } - } - - /** - * @return Whether the menu is currently showing. - */ - public boolean isShowing() { - if (mPopup == null) { - return false; - } - return mPopup.isShowing(); - } - - /** - * The provides the views of the menu items and dividers. - */ - private final class TranslateMenuAdapter extends ArrayAdapter<TranslateMenu.MenuItem> { - private final LayoutInflater mInflater; - private int mMenuType; - - public TranslateMenuAdapter(int menuType) { - super(mContextWrapper, R.layout.weblayer_translate_menu_item, getMenuList(menuType)); - mInflater = LayoutInflater.from(mContextWrapper); - mMenuType = menuType; - } - - private void refreshMenu(int menuType) { - // MENU_OVERFLOW is static and it should not reload. - if (menuType == TranslateMenu.MENU_OVERFLOW) return; - - clear(); - - mMenuType = menuType; - addAll(getMenuList(menuType)); - notifyDataSetChanged(); - } - - private String getItemViewText(TranslateMenu.MenuItem item) { - if (mMenuType == TranslateMenu.MENU_OVERFLOW) { - // Overflow menu items are manually defined one by one. - String source = mOptions.sourceLanguageName(); - switch (item.mId) { - case TranslateMenu.ID_OVERFLOW_ALWAYS_TRANSLATE: - return mContextWrapper.getString( - R.string.translate_option_always_translate, source); - case TranslateMenu.ID_OVERFLOW_MORE_LANGUAGE: - return mContextWrapper.getString(R.string.translate_option_more_language); - case TranslateMenu.ID_OVERFLOW_NEVER_SITE: - return mContextWrapper.getString(R.string.translate_never_translate_site); - case TranslateMenu.ID_OVERFLOW_NEVER_LANGUAGE: - return mContextWrapper.getString( - R.string.translate_option_never_translate, source); - case TranslateMenu.ID_OVERFLOW_NOT_THIS_LANGUAGE: - return mContextWrapper.getString( - R.string.translate_option_not_source_language, source); - default: - assert false : "Unexpected Overflow Item Id"; - } - } else { - // Get source and target language menu items text by language code. - return mOptions.getRepresentationFromCode(item.mCode); - } - return ""; - } - - @Override - public int getItemViewType(int position) { - return getItem(position).mType; - } - - @Override - public int getViewTypeCount() { - return TranslateMenu.MENU_ITEM_TYPE_COUNT; - } - - private View getItemView( - View menuItemView, int position, ViewGroup parent, int resourceId) { - if (menuItemView == null) { - menuItemView = mInflater.inflate(resourceId, parent, false); - } - ((TextView) menuItemView.findViewById(R.id.weblayer_menu_item_text)) - .setText(getItemViewText(getItem(position))); - return menuItemView; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View menuItemView = convertView; - switch (getItemViewType(position)) { - case TranslateMenu.ITEM_CHECKBOX_OPTION: - menuItemView = getItemView(menuItemView, position, parent, - R.layout.weblayer_translate_menu_item_checked); - - ImageView checkboxIcon = - menuItemView.findViewById(R.id.weblayer_menu_item_icon); - if (getItem(position).mId == TranslateMenu.ID_OVERFLOW_ALWAYS_TRANSLATE - && mOptions.getTranslateState(TranslateOptions.Type.ALWAYS_LANGUAGE)) { - checkboxIcon.setVisibility(View.VISIBLE); - } else if (getItem(position).mId == TranslateMenu.ID_OVERFLOW_NEVER_LANGUAGE - && mOptions.getTranslateState(TranslateOptions.Type.NEVER_LANGUAGE)) { - checkboxIcon.setVisibility(View.VISIBLE); - } else if (getItem(position).mId == TranslateMenu.ID_OVERFLOW_NEVER_SITE - && mOptions.getTranslateState(TranslateOptions.Type.NEVER_DOMAIN)) { - checkboxIcon.setVisibility(View.VISIBLE); - } else { - checkboxIcon.setVisibility(View.INVISIBLE); - } - - View divider = - (View) menuItemView.findViewById(R.id.weblayer_menu_item_divider); - if (getItem(position).mWithDivider) { - divider.setVisibility(View.VISIBLE); - } - break; - case TranslateMenu.ITEM_LANGUAGE: - menuItemView = getItemView( - menuItemView, position, parent, R.layout.weblayer_translate_menu_item); - break; - default: - assert false : "Unexpected MenuItem type"; - } - return menuItemView; - } - } -} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateOptions.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateOptions.java deleted file mode 100644 index ba38d4e26f6..00000000000 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateOptions.java +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.weblayer_private; - -import android.text.TextUtils; - -import androidx.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * A class that keeps the state of the different translation options and - * languages. - */ -public class TranslateOptions { - /** - * A container for Language Code and it's translated representation and it's native UMA - * specific hashcode. - * For example for Spanish when viewed from a French locale, this will contain es, Espagnol, - * 114573335 - **/ - public static class TranslateLanguageData { - public final String mLanguageCode; - public final String mLanguageRepresentation; - public final Integer mLanguageUMAHashCode; - - public TranslateLanguageData( - String languageCode, String languageRepresentation, Integer uMAhashCode) { - assert languageCode != null; - assert languageRepresentation != null; - mLanguageCode = languageCode; - mLanguageRepresentation = languageRepresentation; - mLanguageUMAHashCode = uMAhashCode; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof TranslateLanguageData)) return false; - TranslateLanguageData other = (TranslateLanguageData) obj; - return this.mLanguageCode.equals(other.mLanguageCode) - && this.mLanguageRepresentation.equals(other.mLanguageRepresentation) - && this.mLanguageUMAHashCode.equals(other.mLanguageUMAHashCode); - } - - @Override - public int hashCode() { - return (mLanguageCode + mLanguageRepresentation).hashCode(); - } - - @Override - public String toString() { - return "mLanguageCode:" + mLanguageCode + " - mlanguageRepresentation " - + mLanguageRepresentation + " - mLanguageUMAHashCode " + mLanguageUMAHashCode; - } - } - - // Values must be numerated from 0 and can't have gaps - // (they're used for indexing mOptions). - @IntDef({Type.NEVER_LANGUAGE, Type.NEVER_DOMAIN, Type.ALWAYS_LANGUAGE}) - @Retention(RetentionPolicy.SOURCE) - public @interface Type { - int NEVER_LANGUAGE = 0; - int NEVER_DOMAIN = 1; - int ALWAYS_LANGUAGE = 2; - - int NUM_ENTRIES = 3; - } - - private String mSourceLanguageCode; - private String mTargetLanguageCode; - - private final ArrayList<TranslateLanguageData> mAllLanguages; - - // language code to translated language name map - // Conceptually final - private Map<String, String> mCodeToRepresentation; - - // Language code to its UMA hashcode representation. - private Map<String, Integer> mCodeToUMAHashCode; - - // Will reflect the state before the object was ever modified - private final boolean[] mOriginalOptions; - - private final String mOriginalSourceLanguageCode; - private final String mOriginalTargetLanguageCode; - private final boolean mTriggeredFromMenu; - - private final boolean[] mOptions; - - private TranslateOptions(String sourceLanguageCode, String targetLanguageCode, - ArrayList<TranslateLanguageData> allLanguages, boolean neverLanguage, - boolean neverDomain, boolean alwaysLanguage, boolean triggeredFromMenu, - boolean[] originalOptions) { - assert Type.NUM_ENTRIES == 3; - mOptions = new boolean[Type.NUM_ENTRIES]; - mOptions[Type.NEVER_LANGUAGE] = neverLanguage; - mOptions[Type.NEVER_DOMAIN] = neverDomain; - mOptions[Type.ALWAYS_LANGUAGE] = alwaysLanguage; - - mOriginalOptions = originalOptions == null ? mOptions.clone() : originalOptions.clone(); - - mSourceLanguageCode = sourceLanguageCode; - mTargetLanguageCode = targetLanguageCode; - mOriginalSourceLanguageCode = mSourceLanguageCode; - mOriginalTargetLanguageCode = mTargetLanguageCode; - mTriggeredFromMenu = triggeredFromMenu; - - mAllLanguages = allLanguages; - mCodeToRepresentation = new HashMap<String, String>(); - mCodeToUMAHashCode = new HashMap<String, Integer>(); - for (TranslateLanguageData language : allLanguages) { - mCodeToRepresentation.put(language.mLanguageCode, language.mLanguageRepresentation); - mCodeToUMAHashCode.put(language.mLanguageCode, language.mLanguageUMAHashCode); - } - } - - /** - * Creates a TranslateOptions by the given data. - */ - public static TranslateOptions create(String sourceLanguageCode, String targetLanguageCode, - String[] languages, String[] codes, boolean alwaysTranslate, boolean triggeredFromMenu, - int[] hashCodes) { - assert languages.length == codes.length; - - ArrayList<TranslateLanguageData> languageList = new ArrayList<TranslateLanguageData>(); - for (int i = 0; i < languages.length; ++i) { - Integer hashCode = hashCodes != null ? Integer.valueOf(hashCodes[i]) : null; - languageList.add(new TranslateLanguageData(codes[i], languages[i], hashCode)); - } - return new TranslateOptions(sourceLanguageCode, targetLanguageCode, languageList, false, - false, alwaysTranslate, triggeredFromMenu, null); - } - - /** - * Returns a copy of the current instance. - */ - TranslateOptions copy() { - return new TranslateOptions(mSourceLanguageCode, mTargetLanguageCode, mAllLanguages, - mOptions[Type.NEVER_LANGUAGE], mOptions[Type.NEVER_DOMAIN], - mOptions[Type.ALWAYS_LANGUAGE], mTriggeredFromMenu, mOriginalOptions); - } - - public String sourceLanguageName() { - return getRepresentationFromCode(mSourceLanguageCode); - } - - public String targetLanguageName() { - return getRepresentationFromCode(mTargetLanguageCode); - } - - public String sourceLanguageCode() { - return mSourceLanguageCode; - } - - public String targetLanguageCode() { - return mTargetLanguageCode; - } - - public boolean triggeredFromMenu() { - return mTriggeredFromMenu; - } - - public boolean optionsChanged() { - return (!mSourceLanguageCode.equals(mOriginalSourceLanguageCode)) - || (!mTargetLanguageCode.equals(mOriginalTargetLanguageCode)) - || (mOptions[Type.NEVER_LANGUAGE] != mOriginalOptions[Type.NEVER_LANGUAGE]) - || (mOptions[Type.NEVER_DOMAIN] != mOriginalOptions[Type.NEVER_DOMAIN]) - || (mOptions[Type.ALWAYS_LANGUAGE] != mOriginalOptions[Type.ALWAYS_LANGUAGE]); - } - - public List<TranslateLanguageData> allLanguages() { - return mAllLanguages; - } - - public boolean getTranslateState(@Type int type) { - return mOptions[type]; - } - - public boolean setSourceLanguage(String languageCode) { - boolean canSet = canSetLanguage(languageCode, mTargetLanguageCode); - if (canSet) mSourceLanguageCode = languageCode; - return canSet; - } - - public boolean setTargetLanguage(String languageCode) { - boolean canSet = canSetLanguage(mSourceLanguageCode, languageCode); - if (canSet) mTargetLanguageCode = languageCode; - return canSet; - } - - /** - * Sets the new state of never translate domain. - * - * @return true if the toggling was possible - */ - public void toggleNeverTranslateDomainState(boolean value) { - mOptions[Type.NEVER_DOMAIN] = value; - } - - /** - * Sets the new state of never translate language. - * - * @return true if the toggling was possible - */ - public boolean toggleNeverTranslateLanguageState(boolean value) { - // Do not toggle if we are activating NeverLanguage but AlwaysTranslate - // for a language pair with the same source language is already active. - if (mOptions[Type.ALWAYS_LANGUAGE] && value) return false; - mOptions[Type.NEVER_LANGUAGE] = value; - return true; - } - - /** - * Sets the new state of never translate a language pair. - * - * @return true if the toggling was possible - */ - public boolean toggleAlwaysTranslateLanguageState(boolean value) { - // Do not toggle if we are activating AlwaysLanguage but NeverLanguage is active already. - if (mOptions[Type.NEVER_LANGUAGE] && value) return false; - mOptions[Type.ALWAYS_LANGUAGE] = value; - return true; - } - - /** - * Gets the language's translated representation from a given language code. - * @param languageCode ISO code for the language - * @return The translated representation of the language, or "" if not found. - */ - public String getRepresentationFromCode(String languageCode) { - return isValidLanguageCode(languageCode) ? mCodeToRepresentation.get(languageCode) : ""; - } - - /** - * Gets the language's UMA hashcode representation from a given language code. - * @param languageCode ISO code for the language - * @return The UMA hashcode representation of the language, or null if not found. - */ - public Integer getUMAHashCodeFromCode(String languageCode) { - return isValidLanguageUMAHashCode(languageCode) ? mCodeToUMAHashCode.get(languageCode) - : null; - } - - private boolean isValidLanguageCode(String languageCode) { - return !TextUtils.isEmpty(languageCode) && mCodeToRepresentation.containsKey(languageCode); - } - - private boolean isValidLanguageUMAHashCode(String languageCode) { - return !TextUtils.isEmpty(languageCode) && mCodeToUMAHashCode.containsKey(languageCode); - } - - private boolean canSetLanguage(String sourceCode, String targetCode) { - return isValidLanguageCode(sourceCode) && isValidLanguageCode(targetCode); - } - - @Override - public String toString() { - return new StringBuilder() - .append(sourceLanguageCode()) - .append(" -> ") - .append(targetLanguageCode()) - .append(" - ") - .append("Never Language:") - .append(mOptions[Type.NEVER_LANGUAGE]) - .append(" Always Language:") - .append(mOptions[Type.ALWAYS_LANGUAGE]) - .append(" Never Domain:") - .append(mOptions[Type.NEVER_DOMAIN]) - .toString(); - } -} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabContent.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabContent.java deleted file mode 100644 index 4cde0b46193..00000000000 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabContent.java +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.weblayer_private; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.util.AttributeSet; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ProgressBar; -import android.widget.TextView; - -/** - * The content of the tab shown in the TranslateTabLayout. - */ -public class TranslateTabContent extends FrameLayout { - private TextView mTextView; - private ProgressBar mProgressBar; - - /** - * Constructor for inflating from XML. - */ - public TranslateTabContent(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mTextView = (TextView) findViewById(R.id.weblayer_translate_infobar_tab_text); - mProgressBar = (ProgressBar) findViewById(R.id.weblayer_translate_infobar_tab_progressbar); - } - - /** - * Sets the text color for all the states (normal, selected, focused) to be this color. - * @param colors The color state list of the title text. - */ - public void setTextColor(ColorStateList colors) { - mTextView.setTextColor(colors); - } - - /** - * Set the title text for this tab. - * @param tabTitle The new title string. - */ - public void setText(CharSequence tabTitle) { - mTextView.setText(tabTitle); - } - - /** Hide progress bar and show text. */ - public void hideProgressBar() { - mProgressBar.setVisibility(View.INVISIBLE); - mTextView.setVisibility(View.VISIBLE); - } - - /** Show progress bar and hide text. */ - public void showProgressBar() { - mTextView.setVisibility(View.INVISIBLE); - mProgressBar.setVisibility(View.VISIBLE); - } -} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabLayout.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabLayout.java deleted file mode 100644 index 37c27f37cc6..00000000000 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateTabLayout.java +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.weblayer_private; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.TypedArray; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.MotionEvent; - -import androidx.annotation.NonNull; - -import com.google.android.material.tabs.TabLayout; - -import org.chromium.base.StrictModeContext; -import org.chromium.components.browser_ui.widget.animation.Interpolators; - -/** - * TabLayout shown in the TranslateCompactInfoBar. - */ -public class TranslateTabLayout extends TabLayout { - /** The tab in which a spinning progress bar is showing. */ - private Tab mTabShowingProgressBar; - - /** The amount of waiting time before starting the scrolling animation. */ - private static final long START_POSITION_WAIT_DURATION_MS = 1000; - - /** The amount of time it takes to scroll to the end during the scrolling animation. */ - private static final long SCROLL_DURATION_MS = 300; - - /** We define the keyframes of the scrolling animation in this object. */ - ObjectAnimator mScrollToEndAnimator; - - /** Start padding of a Tab. Used for width calculation only. Will not be applied to views. */ - private int mTabPaddingStart; - - /** End padding of a Tab. Used for width calculation only. Will not be applied to views. */ - private int mTabPaddingEnd; - - /** - * Constructor for inflating from XML. - */ - @SuppressLint("CustomViewStyleable") // TODO(crbug.com/807725): Remove and fix. - public TranslateTabLayout(Context context, AttributeSet attrs) { - super(context, attrs); - - TypedArray a = context.obtainStyledAttributes( - attrs, R.styleable.TabLayout, 0, R.style.Widget_Design_TabLayout); - mTabPaddingStart = mTabPaddingEnd = - a.getDimensionPixelSize(R.styleable.TabLayout_tabPadding, 0); - mTabPaddingStart = - a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingStart, mTabPaddingStart); - mTabPaddingEnd = - a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingEnd, mTabPaddingEnd); - } - - /** - * Add new Tabs with title strings. - * @param titles Titles of the tabs to be added. - */ - public void addTabs(CharSequence... titles) { - for (CharSequence title : titles) { - addTabWithTitle(title); - } - } - - /** - * Add a new Tab with the title string. - * @param tabTitle Title string of the new tab. - */ - public void addTabWithTitle(CharSequence tabTitle) { - TranslateTabContent tabContent; - // LayoutInflater may trigger accessing the disk. - try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { - tabContent = - (TranslateTabContent) LayoutInflater.from(getContext()) - .inflate(R.layout.weblayer_infobar_translate_tab_content, this, false); - } - // Set text color using tabLayout's ColorStateList. So that the title text will change - // color when selected and unselected. - tabContent.setTextColor(getTabTextColors()); - tabContent.setText(tabTitle); - - Tab tab = newTab(); - tab.setCustomView(tabContent); - tab.setContentDescription(tabTitle); - super.addTab(tab); - } - - /** - * Replace the title string of a tab. - * @param tabPos The position of the tab to modify. - * @param tabTitle The new title string. - */ - public void replaceTabTitle(int tabPos, CharSequence tabTitle) { - if (tabPos < 0 || tabPos >= getTabCount()) { - return; - } - Tab tab = getTabAt(tabPos); - ((TranslateTabContent) tab.getCustomView()).setText(tabTitle); - tab.setContentDescription(tabTitle); - } - - /** - * Show the spinning progress bar on a specified tab. - * @param tabPos The position of the tab to show the progress bar. - */ - public void showProgressBarOnTab(int tabPos) { - if (tabPos < 0 || tabPos >= getTabCount() || mTabShowingProgressBar != null) { - return; - } - mTabShowingProgressBar = getTabAt(tabPos); - - // TODO(martiw) See if we need to setContentDescription as "Translating" here. - - if (tabIsSupported(mTabShowingProgressBar)) { - ((TranslateTabContent) mTabShowingProgressBar.getCustomView()).showProgressBar(); - } - } - - /** - * Hide the spinning progress bar in the tabs. - */ - public void hideProgressBar() { - if (mTabShowingProgressBar == null) return; - - if (tabIsSupported(mTabShowingProgressBar)) { - ((TranslateTabContent) mTabShowingProgressBar.getCustomView()).hideProgressBar(); - } - - mTabShowingProgressBar = null; - } - - // Overridden to block children's touch event when showing progress bar. - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - // Allow touches to propagate to children only if the layout can be interacted with. - if (mTabShowingProgressBar != null) { - return true; - } - endScrollingAnimationIfPlaying(); - return super.onInterceptTouchEvent(ev); - } - - /** Check if the tab is supported in TranslateTabLayout. */ - private boolean tabIsSupported(Tab tab) { - return (tab.getCustomView() instanceof TranslateTabContent); - } - - // Overridden to make sure only supported Tabs can be added. - @Override - public void addTab(@NonNull Tab tab, int position, boolean setSelected) { - if (!tabIsSupported(tab)) { - throw new IllegalArgumentException(); - } - super.addTab(tab, position, setSelected); - } - - // Overrided to make sure only supported Tabs can be added. - @Override - public void addTab(@NonNull Tab tab, boolean setSelected) { - if (!tabIsSupported(tab)) { - throw new IllegalArgumentException(); - } - super.addTab(tab, setSelected); - } - - /** - * Calculate and return the width of a specified tab. Tab doesn't provide a means of getting - * the width so we need to calculate the width by summing up the tab paddings and content width. - * @param position Tab position. - * @return Tab's width in pixels. - */ - private int getTabWidth(int position) { - if (getTabAt(position) == null) return 0; - return getTabAt(position).getCustomView().getWidth() + mTabPaddingStart + mTabPaddingEnd; - } - - /** - * Calculate the total width of all tabs and return it. - * @return Total width of all tabs in pixels. - */ - private int getTabsTotalWidth() { - int totalWidth = 0; - for (int i = 0; i < getTabCount(); i++) { - totalWidth += getTabWidth(i); - } - return totalWidth; - } - - /** - * Calculate the maximum scroll distance (by subtracting layout width from total width of tabs) - * and return it. - * @return Maximum scroll distance in pixels. - */ - private int maxScrollDistance() { - int scrollDistance = getTabsTotalWidth() - getWidth(); - return scrollDistance > 0 ? scrollDistance : 0; - } - - /** - * Perform the scrolling animation if this tablayout has any scrollable distance. - */ - // TODO(crbug.com/900912): Figure out whether setScrollX is actually available. - @SuppressLint("ObjectAnimatorBinding") - public void startScrollingAnimationIfNeeded() { - int maxScrollDistance = maxScrollDistance(); - if (maxScrollDistance == 0) { - return; - } - // The steps of the scrolling animation: - // 1. wait for START_POSITION_WAIT_DURATION_MS. - // 2. scroll to the end in SCROLL_DURATION_MS. - mScrollToEndAnimator = ObjectAnimator.ofInt(this, "scrollX", - getLayoutDirection() == LAYOUT_DIRECTION_RTL ? 0 : maxScrollDistance); - mScrollToEndAnimator.setStartDelay(START_POSITION_WAIT_DURATION_MS); - mScrollToEndAnimator.setDuration(SCROLL_DURATION_MS); - mScrollToEndAnimator.setInterpolator(Interpolators.DECELERATE_INTERPOLATOR); - mScrollToEndAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mScrollToEndAnimator = null; - } - }); - mScrollToEndAnimator.start(); - } - - /** - * End the scrolling animation if it is playing. - */ - public void endScrollingAnimationIfPlaying() { - if (mScrollToEndAnimator != null) mScrollToEndAnimator.end(); - } -} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java index 4f3d7b439f1..496c52078b3 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java @@ -11,6 +11,8 @@ import android.os.Bundle; import android.util.TypedValue; import android.view.Gravity; import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; @@ -18,12 +20,15 @@ import android.widget.TextView; import androidx.annotation.ColorRes; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.core.widget.ImageViewCompat; import org.chromium.base.LifetimeAssert; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; +import org.chromium.components.content_settings.ContentSettingsType; +import org.chromium.components.embedder_support.util.Origin; import org.chromium.components.omnibox.SecurityButtonAnimationDelegate; import org.chromium.components.omnibox.SecurityStatusIcon; import org.chromium.components.page_info.PageInfoController; @@ -68,7 +73,16 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { } @Override - public IObjectWrapper /* View */ createUrlBarView(Bundle options) { + @Deprecated + public IObjectWrapper /* View */ deprecatedCreateUrlBarView(Bundle options) { + return createUrlBarView( + options, /* OnLongClickListener */ null, /* OnLongClickListener */ null); + } + + @Override + public IObjectWrapper /* View */ createUrlBarView(Bundle options, + @Nullable IObjectWrapper /* OnLongClickListener */ clickListener, + @Nullable IObjectWrapper /* OnLongClickListener */ longClickListener) { StrictModeWorkaround.apply(); if (mBrowserImpl == null) { throw new IllegalStateException("UrlBarView cannot be created without a valid Browser"); @@ -76,7 +90,7 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { Context context = mBrowserImpl.getContext(); if (context == null) throw new IllegalStateException("BrowserFragment not attached yet."); - UrlBarView urlBarView = new UrlBarView(context, options); + UrlBarView urlBarView = new UrlBarView(context, options, clickListener, longClickListener); return ObjectWrapper.wrap(urlBarView); } @@ -92,8 +106,12 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { private TextView mUrlTextView; private ImageButton mSecurityButton; private final SecurityButtonAnimationDelegate mSecurityButtonAnimationDelegate; + OnClickListener mUrlBarClickListener; + OnLongClickListener mUrlBarLongClickListener; - public UrlBarView(@NonNull Context context, Bundle options) { + public UrlBarView(@NonNull Context context, @NonNull Bundle options, + @Nullable IObjectWrapper /* OnClickListener */ clickListener, + @Nullable IObjectWrapper /* OnLongClickListener */ longClickListener) { super(context); setGravity(Gravity.CENTER_HORIZONTAL); @@ -110,6 +128,9 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { mSecurityButton = (ImageButton) findViewById(R.id.security_button); mSecurityButtonAnimationDelegate = new SecurityButtonAnimationDelegate( mSecurityButton, mUrlTextView, R.dimen.security_status_icon_size); + mUrlBarClickListener = ObjectWrapper.unwrap(clickListener, OnClickListener.class); + mUrlBarLongClickListener = + ObjectWrapper.unwrap(longClickListener, OnLongClickListener.class); updateView(); } @@ -161,9 +182,24 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { } if (mShowPageInfoWhenUrlTextClicked) { + // Set clicklisteners on the entire UrlBarView. + assert (mUrlBarClickListener == null); + mSecurityButton.setClickable(false); setOnClickListener(v -> { showPageInfoUi(v); }); + + if (mUrlBarLongClickListener != null) { + setOnLongClickListener(mUrlBarLongClickListener); + } } else { + // Set a clicklistener on the security status and TextView separately. This mode + // can be used to create an editable URL bar using WebLayer. mSecurityButton.setOnClickListener(v -> { showPageInfoUi(v); }); + if (mUrlBarClickListener != null) { + mUrlTextView.setOnClickListener(mUrlBarClickListener); + } + if (mUrlBarLongClickListener != null) { + mUrlTextView.setOnLongClickListener(mUrlBarLongClickListener); + } } } @@ -173,7 +209,18 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { webContents, /* contentPublisher= */ null, PageInfoController.OpenedFromSource.TOOLBAR, PageInfoControllerDelegateImpl.create(webContents), - new PermissionParamsListBuilderDelegate(mBrowserImpl.getProfile())); + new PermissionParamsListBuilderDelegate(mBrowserImpl.getProfile()) { + @Override + public String getDelegateAppName( + Origin origin, @ContentSettingsType int type) { + if (type == ContentSettingsType.GEOLOCATION + && WebLayerImpl.isLocationPermissionManaged(origin)) { + return WebLayerImpl.getClientApplicationName(); + } + + return null; + } + }); } @DrawableRes diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerFactoryImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerFactoryImpl.java index 9e79ff718ec..07c937bf42c 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerFactoryImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerFactoryImpl.java @@ -50,6 +50,11 @@ public final class WebLayerFactoryImpl extends IWebLayerFactory.Stub { @Override public boolean isClientSupported() { StrictModeWorkaround.apply(); + // Client changes were required to support WebLayer in a split. + if (ProductConfig.IS_BUNDLE && WebLayerBundleUtils.IS_WEBLAYER_IN_SPLIT + && sClientMajorVersion < 86) { + return false; + } int implMajorVersion = getImplementationMajorVersion(); // While the client always calls this method, the most recently shipped product gets to // decide compatibility. If we instead let the implementation always decide, then we would diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java index c9223c62146..1c3ba5744fb 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java @@ -38,10 +38,12 @@ import org.chromium.base.StrictModeContext; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; +import org.chromium.base.compat.ApiHelperForO; import org.chromium.base.library_loader.LibraryLoader; import org.chromium.base.library_loader.LibraryProcessType; import org.chromium.components.embedder_support.application.ClassLoaderContextWrapperFactory; import org.chromium.components.embedder_support.application.FirebaseConfig; +import org.chromium.components.embedder_support.util.Origin; import org.chromium.content_public.browser.BrowserStartupController; import org.chromium.content_public.browser.ChildProcessCreationParams; import org.chromium.content_public.browser.DeviceUtils; @@ -122,12 +124,6 @@ public final class WebLayerImpl extends IWebLayer.Stub { WebLayerImpl() {} @Override - public void loadAsyncV80( - IObjectWrapper appContextWrapper, IObjectWrapper loadedCallbackWrapper) { - loadAsync(appContextWrapper, null, loadedCallbackWrapper); - } - - @Override public void loadAsync(IObjectWrapper appContextWrapper, IObjectWrapper remoteContextWrapper, IObjectWrapper loadedCallbackWrapper) { StrictModeWorkaround.apply(); @@ -152,11 +148,6 @@ public final class WebLayerImpl extends IWebLayer.Stub { } @Override - public void loadSyncV80(IObjectWrapper appContextWrapper) { - loadSync(appContextWrapper, null); - } - - @Override public void loadSync(IObjectWrapper appContextWrapper, IObjectWrapper remoteContextWrapper) { StrictModeWorkaround.apply(); init(appContextWrapper, remoteContextWrapper); @@ -207,7 +198,9 @@ public final class WebLayerImpl extends IWebLayer.Stub { notifyWebViewRunningInProcess(remoteContext.getClassLoader()); } - Context appContext = minimalInitForContext(appContextWrapper, remoteContextWrapper); + remoteContext = processRemoteContext(remoteContext); + Context appContext = minimalInitForContext( + ObjectWrapper.unwrap(appContextWrapper, Context.class), remoteContext); PackageInfo packageInfo = WebViewFactory.getLoadedPackageInfo(); // If a remote context is not provided, the client is an older version that loads the native @@ -221,7 +214,8 @@ public final class WebLayerImpl extends IWebLayer.Stub { FirebaseConfig.getFirebaseAppIdForPackage(packageInfo.packageName)); // TODO: The call to onResourcesLoaded() can be slow, we may need to parallelize this with // other expensive startup tasks. - R.onResourcesLoaded(forceCorrectPackageId(remoteContext)); + org.chromium.weblayer_private.base.R.onResourcesLoaded( + forceCorrectPackageId(remoteContext)); SelectionPopupController.setMustUseWebContentsContext(); ResourceBundle.setAvailablePakLocales(new String[] {}, ProductConfig.UNCOMPRESSED_LOCALES); @@ -304,17 +298,12 @@ public final class WebLayerImpl extends IWebLayer.Stub { } @Override - public ICrashReporterController getCrashReporterControllerV80(IObjectWrapper appContext) { - StrictModeWorkaround.apply(); - return getCrashReporterController(appContext, null); - } - - @Override public ICrashReporterController getCrashReporterController( IObjectWrapper appContext, IObjectWrapper remoteContext) { StrictModeWorkaround.apply(); // This is a no-op if init has already happened. - WebLayerImpl.minimalInitForContext(appContext, remoteContext); + WebLayerImpl.minimalInitForContext(ObjectWrapper.unwrap(appContext, Context.class), + processRemoteContext(ObjectWrapper.unwrap(remoteContext, Context.class))); return CrashReporterControllerImpl.getInstance(); } @@ -371,20 +360,6 @@ public final class WebLayerImpl extends IWebLayer.Stub { WebLayerImplJni.get().registerExternalExperimentIDs(trialName, experimentIDs); } - /** - * Creates a remote context. This should only be used for backwards compatibility when the - * client was not sending the remote context. - */ - public static Context createRemoteContextV80(Context appContext) { - try { - return appContext.createPackageContext( - WebViewFactory.getLoadedPackageInfo().packageName, - Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE); - } catch (PackageManager.NameNotFoundException e) { - throw new AndroidRuntimeException(e); - } - } - public static Intent createIntent() { if (sClient == null) { throw new IllegalStateException("WebLayer should have been initialized already."); @@ -429,6 +404,13 @@ public final class WebLayerImpl extends IWebLayer.Stub { .toString(); } + public static boolean isLocationPermissionManaged(Origin origin) { + if (origin == null) { + return false; + } + return WebLayerImplJni.get().isLocationPermissionManaged(origin.toString()); + } + /** * Converts the given id into a resource ID that can be shown in system UI, such as * notifications. @@ -467,16 +449,11 @@ public final class WebLayerImpl extends IWebLayer.Stub { * Performs the minimal initialization needed for a context. This is used for example in * CrashReporterControllerImpl, so it can be used before full WebLayer initialization. */ - private static Context minimalInitForContext( - IObjectWrapper appContextWrapper, IObjectWrapper remoteContextWrapper) { + private static Context minimalInitForContext(Context appContext, Context remoteContext) { if (ContextUtils.getApplicationContext() != null) { return ContextUtils.getApplicationContext(); } - Context appContext = ObjectWrapper.unwrap(appContextWrapper, Context.class); - Context remoteContext = ObjectWrapper.unwrap(remoteContextWrapper, Context.class); - if (remoteContext == null) { - remoteContext = createRemoteContextV80(appContext); - } + assert remoteContext != null; ClassLoaderContextWrapperFactory.setResourceOverrideContext(remoteContext); // Wrap the app context so that it can be used to load WebLayer implementation classes. appContext = ClassLoaderContextWrapperFactory.get(appContext); @@ -685,7 +662,9 @@ public final class WebLayerImpl extends IWebLayer.Stub { } private static void notifyWebViewRunningInProcess(ClassLoader webViewClassLoader) { - try { + // TODO(crbug.com/1112001): Investigate why loading classes causes strict mode + // violations in some situations. + try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { Class<?> webViewChromiumFactoryProviderClass = Class.forName("com.android.webview.chromium.WebViewChromiumFactoryProvider", true, webViewClassLoader); @@ -693,8 +672,20 @@ public final class WebLayerImpl extends IWebLayer.Stub { "setWebLayerRunningInSameProcess"); setter.invoke(null); } catch (Exception e) { - Log.w(TAG, "Unable to notify WebView running in process", e); + Log.w(TAG, "Unable to notify WebView running in process."); + } + } + + private static Context processRemoteContext(Context remoteContext) { + // If WebLayer is in a DFM, make sure the correct resources are used. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + try { + return ApiHelperForO.createContextForSplit(remoteContext, "weblayer"); + } catch (PackageManager.NameNotFoundException e) { + // WebLayer is not in a split, the original context will have the resources. + } } + return remoteContext; } @CalledByNative @@ -710,5 +701,6 @@ public final class WebLayerImpl extends IWebLayer.Stub { void setIsWebViewCompatMode(boolean value); String getUserAgentString(); void registerExternalExperimentIDs(String trialName, int[] experimentIDs); + boolean isLocationPermissionManaged(String origin); } } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationBuilder.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationWrapperBuilder.java index 50984bb6403..fac8a475265 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationBuilder.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerNotificationWrapperBuilder.java @@ -15,32 +15,32 @@ import android.webkit.WebViewFactory; import androidx.annotation.NonNull; import org.chromium.base.ContextUtils; -import org.chromium.components.browser_ui.notifications.ChromeNotificationBuilder; -import org.chromium.components.browser_ui.notifications.NotificationBuilder; import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl; import org.chromium.components.browser_ui.notifications.NotificationMetadata; +import org.chromium.components.browser_ui.notifications.NotificationWrapperBuilder; +import org.chromium.components.browser_ui.notifications.NotificationWrapperStandardBuilder; import org.chromium.components.browser_ui.notifications.channels.ChannelsInitializer; /** A notification builder for WebLayer which has extra logic to make icons work correctly. */ -final class WebLayerNotificationBuilder extends NotificationBuilder { +final class WebLayerNotificationWrapperBuilder extends NotificationWrapperStandardBuilder { /** Creates a notification builder. */ - public static WebLayerNotificationBuilder create( + public static WebLayerNotificationWrapperBuilder create( @WebLayerNotificationChannels.ChannelId String channelId, @NonNull NotificationMetadata metadata) { Context appContext = ContextUtils.getApplicationContext(); ChannelsInitializer initializer = new ChannelsInitializer(new NotificationManagerProxyImpl(appContext), WebLayerNotificationChannels.getInstance(), appContext.getResources()); - return new WebLayerNotificationBuilder(appContext, channelId, initializer, metadata); + return new WebLayerNotificationWrapperBuilder(appContext, channelId, initializer, metadata); } - private WebLayerNotificationBuilder(Context context, String channelId, + private WebLayerNotificationWrapperBuilder(Context context, String channelId, ChannelsInitializer channelsInitializer, NotificationMetadata metadata) { super(context, channelId, channelsInitializer, metadata); } @Override - public ChromeNotificationBuilder setSmallIcon(int icon) { + public NotificationWrapperBuilder setSmallIcon(int icon) { if (WebLayerImpl.isAndroidResource(icon)) { super.setSmallIcon(icon); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { @@ -54,7 +54,8 @@ final class WebLayerNotificationBuilder extends NotificationBuilder { @Override @SuppressWarnings("deprecation") - public ChromeNotificationBuilder addAction(int icon, CharSequence title, PendingIntent intent) { + public NotificationWrapperBuilder addAction( + int icon, CharSequence title, PendingIntent intent) { if (WebLayerImpl.isAndroidResource(icon)) { super.addAction(icon, title, intent); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerSiteSettingsClient.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerSiteSettingsClient.java index cabbef5f87a..3d03b906ec3 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerSiteSettingsClient.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerSiteSettingsClient.java @@ -11,12 +11,13 @@ import androidx.annotation.Nullable; import androidx.preference.Preference; import org.chromium.base.Callback; +import org.chromium.base.ContextUtils; import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate; import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory.Type; import org.chromium.components.browser_ui.site_settings.SiteSettingsClient; import org.chromium.components.browser_ui.site_settings.SiteSettingsHelpClient; -import org.chromium.components.browser_ui.site_settings.SiteSettingsPrefClient; import org.chromium.components.browser_ui.site_settings.WebappSettingsClient; +import org.chromium.components.content_settings.ContentSettingsType; import org.chromium.components.embedder_support.browser_context.BrowserContextHandle; import org.chromium.components.embedder_support.util.Origin; @@ -26,9 +27,8 @@ import java.util.Set; /** * A SiteSettingsClient instance that contains WebLayer-specific Site Settings logic. */ -public class WebLayerSiteSettingsClient - implements SiteSettingsClient, ManagedPreferenceDelegate, SiteSettingsHelpClient, - SiteSettingsPrefClient, WebappSettingsClient { +public class WebLayerSiteSettingsClient implements SiteSettingsClient, ManagedPreferenceDelegate, + SiteSettingsHelpClient, WebappSettingsClient { private final BrowserContextHandle mBrowserContextHandle; public WebLayerSiteSettingsClient(BrowserContextHandle browserContextHandle) { @@ -53,11 +53,6 @@ public class WebLayerSiteSettingsClient } @Override - public SiteSettingsPrefClient getSiteSettingsPrefClient() { - return this; - } - - @Override public WebappSettingsClient getWebappSettingsClient() { return this; } @@ -86,6 +81,33 @@ public class WebLayerSiteSettingsClient return null; } + @Override + public String getAppName() { + return WebLayerImpl.getClientApplicationName(); + } + + @Override + @Nullable + public String getDelegateAppNameForOrigin(Origin origin, @ContentSettingsType int type) { + if (WebLayerImpl.isLocationPermissionManaged(origin) + && type == ContentSettingsType.GEOLOCATION) { + return WebLayerImpl.getClientApplicationName(); + } + + return null; + } + + @Override + @Nullable + public String getDelegatePackageNameForOrigin(Origin origin, @ContentSettingsType int type) { + if (WebLayerImpl.isLocationPermissionManaged(origin) + && type == ContentSettingsType.GEOLOCATION) { + return ContextUtils.getApplicationContext().getPackageName(); + } + + return null; + } + // ManagedPrefrenceDelegate implementation: // A no-op because WebLayer doesn't support managed preferences. @@ -118,22 +140,6 @@ public class WebLayerSiteSettingsClient @Override public void launchProtectedContentHelpAndFeedbackActivity(Activity currentActivity) {} - // SiteSettingsPrefClient implementation: - - // The quiet notification UI is a Chrome-specific feature for now. - @Override - public boolean getEnableQuietNotificationPermissionUi() { - return false; - } - @Override - public void setEnableQuietNotificationPermissionUi(boolean newValue) {} - @Override - public void clearEnableNotificationPermissionUi() {} - - // WebLayer doesn't support notifications yet. - @Override - public void setNotificationsVibrateEnabled(boolean newValue) {} - // WebappSettingsClient implementation: // A no-op since WebLayer doesn't support webapps. @@ -146,16 +152,4 @@ public class WebLayerSiteSettingsClient public Set<String> getAllDelegatedNotificationOrigins() { return Collections.EMPTY_SET; } - - @Override - @Nullable - public String getNotificationDelegateAppNameForOrigin(Origin origin) { - return null; - } - - @Override - @Nullable - public String getNotificationDelegatePackageNameForOrigin(Origin origin) { - return null; - } } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerTabModalPresenter.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerTabModalPresenter.java index b1be612a9ff..aef1e1b037b 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerTabModalPresenter.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerTabModalPresenter.java @@ -5,16 +5,15 @@ package org.chromium.weblayer_private; import android.content.Context; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; -import org.chromium.base.StrictModeContext; import org.chromium.components.browser_ui.modaldialog.R; import org.chromium.components.browser_ui.modaldialog.TabModalPresenter; import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.common.BrowserControlsState; +import org.chromium.ui.LayoutInflaterUtils; import org.chromium.ui.modelutil.PropertyModel; /** @@ -53,11 +52,8 @@ public class WebLayerTabModalPresenter extends TabModalPresenter { } private FrameLayout loadDialogContainer() { - // LayoutInflater may trigger accessing the disk. - try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { - return (FrameLayout) LayoutInflater.from(mContext).inflate( - R.layout.modal_dialog_container, null); - } + return (FrameLayout) LayoutInflaterUtils.inflate( + mContext, R.layout.modal_dialog_container, null); } @Override diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/BrowsingDataType.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/BrowsingDataType.java index 840bfae909c..cea8ff43b11 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/BrowsingDataType.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/BrowsingDataType.java @@ -9,9 +9,11 @@ import androidx.annotation.IntDef; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -@IntDef({BrowsingDataType.COOKIES_AND_SITE_DATA, BrowsingDataType.CACHE}) +@IntDef({BrowsingDataType.COOKIES_AND_SITE_DATA, BrowsingDataType.CACHE, + BrowsingDataType.SITE_SETTINGS}) @Retention(RetentionPolicy.SOURCE) public @interface BrowsingDataType { int COOKIES_AND_SITE_DATA = 0; int CACHE = 1; + int SITE_SETTINGS = 2; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/GoogleAccountServiceType.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/GoogleAccountServiceType.java new file mode 100644 index 00000000000..f7e0c81de68 --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/GoogleAccountServiceType.java @@ -0,0 +1,19 @@ +// 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.weblayer_private.interfaces; + +import androidx.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@IntDef({GoogleAccountServiceType.SIGNOUT, GoogleAccountServiceType.ADD_SESSION, + GoogleAccountServiceType.DEFAULT}) +@Retention(RetentionPolicy.SOURCE) +public @interface GoogleAccountServiceType { + int SIGNOUT = 0; + int ADD_SESSION = 1; + int DEFAULT = 2; +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl index ed9a123fe9c..40eb4208e27 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl @@ -36,4 +36,7 @@ interface IBrowser { void setBottomView(in IObjectWrapper view) = 10; ITab createTab() = 11; + + void setTopViewAndScrollingBehavior(in IObjectWrapper view, in int minHeight, + in boolean pinToContentTop, in boolean animate) = 12; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IErrorPageCallbackClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IErrorPageCallbackClient.aidl index 1dfc22b1a2e..f361f315806 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IErrorPageCallbackClient.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IErrorPageCallbackClient.aidl @@ -4,10 +4,13 @@ package org.chromium.weblayer_private.interfaces; +import org.chromium.weblayer_private.interfaces.IClientNavigation; + /** * Allows the client to override the default way of handling user interactions * with error pages (such as SSL interstitials). */ interface IErrorPageCallbackClient { boolean onBackToSafety() = 0; + String getErrorPageContent(IClientNavigation navigation) = 1; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IFaviconFetcher.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IFaviconFetcher.aidl new file mode 100644 index 00000000000..eec59fea8b8 --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IFaviconFetcher.aidl @@ -0,0 +1,9 @@ +// 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.weblayer_private.interfaces; + +interface IFaviconFetcher { + void destroy() = 1; +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IFaviconFetcherClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IFaviconFetcherClient.aidl new file mode 100644 index 00000000000..b94402cd747 --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IFaviconFetcherClient.aidl @@ -0,0 +1,10 @@ +// 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.weblayer_private.interfaces; + +interface IFaviconFetcherClient { + void onDestroyed() = 1; + void onFaviconChanged(in Bitmap bitmap) = 2; +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IGoogleAccountsCallbackClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IGoogleAccountsCallbackClient.aidl new file mode 100644 index 00000000000..50589c668b3 --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IGoogleAccountsCallbackClient.aidl @@ -0,0 +1,10 @@ +// 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.weblayer_private.interfaces; + +interface IGoogleAccountsCallbackClient { + void onGoogleAccountsRequest(int serviceType, in String email, in String continueUrl, boolean isSameTab) = 0; + String getGaiaId() = 1; +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl index 9d42c486155..5c94e3418ea 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl @@ -29,4 +29,8 @@ interface INavigation { boolean isDownload() = 9; boolean wasStopCalled() = 10; + + // @since 86 + boolean isPageInitiated() = 11; + boolean isReload() = 12; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl index 653b9a6af12..e2ffa2a15fd 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationController.aidl @@ -36,4 +36,11 @@ interface INavigationController { // Added in 85. boolean isNavigationEntrySkippable(int index) = 13; + + // Added in 86. + void navigate2(in String uri, + in boolean shouldReplaceEntry, + in boolean disableIntentProcessing, + in boolean disableNetworkErrorAutoReload, + in boolean enableAutoPlay) = 14; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl index 6ec60700f85..269814d69db 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfile.aidl @@ -36,4 +36,8 @@ interface IProfile { void removeBrowserPersistenceStorage(in String[] ids, in IObjectWrapper resultCallback) = 10; void prepareForPossibleCrossOriginNavigation() = 11; + + // Added in Version 86. + void getCachedFaviconForPageUri(in String uri, + in IObjectWrapper resultCallback) = 12; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl index c029b9a6c18..21c50841f8b 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl @@ -8,8 +8,11 @@ import java.util.List; import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient; import org.chromium.weblayer_private.interfaces.IErrorPageCallbackClient; +import org.chromium.weblayer_private.interfaces.IFaviconFetcher; +import org.chromium.weblayer_private.interfaces.IFaviconFetcherClient; import org.chromium.weblayer_private.interfaces.IFindInPageCallbackClient; import org.chromium.weblayer_private.interfaces.IFullscreenCallbackClient; +import org.chromium.weblayer_private.interfaces.IGoogleAccountsCallbackClient; import org.chromium.weblayer_private.interfaces.IMediaCaptureCallbackClient; import org.chromium.weblayer_private.interfaces.INavigationController; import org.chromium.weblayer_private.interfaces.INavigationControllerClient; @@ -57,8 +60,6 @@ interface ITab { // Added in 85 boolean setData(in Map data) = 17; - - // Added in 85 Map getData() = 18; void registerWebMessageCallback(in String jsObjectName, in List<String> allowedOrigins, @@ -66,4 +67,9 @@ interface ITab { void unregisterWebMessageCallback(in String jsObjectName) = 20; boolean canTranslate() = 21; void showTranslateUi() = 22; + + // Added in 86 + void setGoogleAccountsCallbackClient(IGoogleAccountsCallbackClient client) = 23; + IFaviconFetcher createFaviconFetcher(IFaviconFetcherClient client) = 24; + void setTranslateTargetLanguage(in String targetLanguage) = 25; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IUrlBarController.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IUrlBarController.aidl index 212e8c2f4ff..c69b042ef66 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IUrlBarController.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IUrlBarController.aidl @@ -7,5 +7,13 @@ package org.chromium.weblayer_private.interfaces; import org.chromium.weblayer_private.interfaces.IObjectWrapper; interface IUrlBarController { - IObjectWrapper /* View */ createUrlBarView(in Bundle options) = 0; + + // Deprecated in 84, use createUrlBarView with three arguments. + IObjectWrapper /* View */ deprecatedCreateUrlBarView(in Bundle options) = 0; + + // Since 86 + IObjectWrapper /* View */ createUrlBarView( + in Bundle options, + in IObjectWrapper /* View.OnClickListener */ textClickListener, + in IObjectWrapper /* View.OnLongClickListener */ textLongClickListener) = 1; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl index 7c8af14c1f6..965b11e0199 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl @@ -16,12 +16,8 @@ import org.chromium.weblayer_private.interfaces.ISiteSettingsFragment; import org.chromium.weblayer_private.interfaces.IWebLayerClient; interface IWebLayer { - // Deprecated, use loadAsync(). - void loadAsyncV80(in IObjectWrapper appContext, - in IObjectWrapper loadedCallback) = 1; - - // Deprecated, use loadSync(). - void loadSyncV80(in IObjectWrapper appContext) = 2; + // ID 1 was loadAsyncV80 and was removed in M86. + // ID 2 was loadSyncV80 and was removed in M86. // Creates the WebLayer counterpart to a BrowserFragment - a BrowserFragmentImpl // @@ -41,9 +37,7 @@ interface IWebLayer { // Returns whether or not the DevTools remote debugging server is enabled. boolean isRemoteDebuggingEnabled() = 6; - // Deprecated, use getCrashReporterController(). - ICrashReporterController getCrashReporterControllerV80( - in IObjectWrapper appContext) = 7; + // ID 7 was getCrashReporterControllerV80 and was removed in M86. // Initializes WebLayer and starts loading. // diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/NavigateParams.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/NavigateParams.java index e79e137ce48..b5c9ab8fb46 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/NavigateParams.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/NavigateParams.java @@ -15,7 +15,7 @@ import android.os.Parcelable; * @since 83 */ public class NavigateParams implements Parcelable { - public boolean mShouldReplaceCurrentEntry = false; + public boolean mShouldReplaceCurrentEntry; public static final Parcelable.Creator<NavigateParams> CREATOR = new Parcelable.Creator<NavigateParams>() { diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/PRESUBMIT.py b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/PRESUBMIT.py index c375ef3b7aa..300806a9568 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/PRESUBMIT.py +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/PRESUBMIT.py @@ -149,7 +149,7 @@ def _CompareApiDumpForFiles(input_api, output_api, aidl_files): def CheckChangeOnUpload(input_api, output_api): filter_lambda = lambda x: input_api.FilterSourceFile( - x, white_list=[r'.*\.aidl$' ]) + x, files_to_check=[r'.*\.aidl$' ]) aidl_files = [] for f in input_api.AffectedFiles(include_deletes=False, file_filter=filter_lambda): diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/SettingType.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/SettingType.java index 1b37b08d9b3..b15bc939d5f 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/SettingType.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/SettingType.java @@ -11,11 +11,12 @@ import java.lang.annotation.RetentionPolicy; @IntDef({SettingType.BASIC_SAFE_BROWSING_ENABLED, SettingType.UKM_ENABLED, SettingType.EXTENDED_REPORTING_SAFE_BROWSING_ENABLED, - SettingType.REAL_TIME_SAFE_BROWSING_ENABLED}) + SettingType.REAL_TIME_SAFE_BROWSING_ENABLED, SettingType.NETWORK_PREDICTION_ENABLED}) @Retention(RetentionPolicy.SOURCE) public @interface SettingType { int BASIC_SAFE_BROWSING_ENABLED = 0; int UKM_ENABLED = 1; int EXTENDED_REPORTING_SAFE_BROWSING_ENABLED = 2; int REAL_TIME_SAFE_BROWSING_ENABLED = 3; + int NETWORK_PREDICTION_ENABLED = 4; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/ITestWebLayer.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/ITestWebLayer.aidl index ac1cd24f40f..53eff3330ed 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/ITestWebLayer.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/ITestWebLayer.aidl @@ -44,4 +44,13 @@ interface ITestWebLayer { void forceNetworkConnectivityState(in boolean networkAvailable) = 13; boolean canInfoBarContainerScroll(in ITab tab) = 14; + + String getDisplayedUrl(IObjectWrapper /* View */ urlBarView) = 15; + + // Returns the target language of the currently-showing translate infobar, or null if no translate + // infobar is currently showing. + String getTranslateInfoBarTargetLanguage(in ITab tab) = 16; + + // Returns true if a fullscreen toast was shown for |tab|. + boolean didShowFullscreenToast(in ITab tab) = 17; } diff --git a/chromium/weblayer/browser/java/res/drawable/weblayer_infobar_wrapper_bg.xml b/chromium/weblayer/browser/java/res/drawable/weblayer_infobar_wrapper_bg.xml deleted file mode 100644 index e119c1148fa..00000000000 --- a/chromium/weblayer/browser/java/res/drawable/weblayer_infobar_wrapper_bg.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2015 The Chromium Authors. All rights reserved. - Use of this source code is governed by a BSD-style license that can be - found in the LICENSE file. --> - -<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > - <item> - <bitmap - android:src="@drawable/infobar_shadow_top" - android:gravity="top|fill_horizontal" - android:tileMode="disabled" /> - </item> - <item - android:top="@dimen/infobar_shadow_height" - android:drawable="@color/infobar_background_color" /> -</layer-list> diff --git a/chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_compact_content.xml b/chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_compact_content.xml index 82149b5b142..f124f32821a 100644 --- a/chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_compact_content.xml +++ b/chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_compact_content.xml @@ -11,7 +11,7 @@ android:gravity="center_vertical" android:orientation="horizontal"> <!-- TODO(huayinz): Change app:tabIndicatorColor to some common color reference --> - <org.chromium.weblayer_private.TranslateTabLayout + <org.chromium.components.translate.TranslateTabLayout android:id="@+id/weblayer_translate_infobar_tabs" android:layout_width="0dp" android:layout_height="match_parent" diff --git a/chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_tab_content.xml b/chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_tab_content.xml deleted file mode 100644 index 3be8f467c43..00000000000 --- a/chromium/weblayer/browser/java/res/layout/weblayer_infobar_translate_tab_content.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2017 The Chromium Authors. All rights reserved. - Use of this source code is governed by a BSD-style license that can be - found in the LICENSE file. --> - -<org.chromium.weblayer_private.TranslateTabContent - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/weblayer_translate_tabcontent" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - <!-- Add both the textView and progressBar to the tab, and only keep one of them visible. - This way the width of the Tab will always be fixed no matter which one is visible. --> - <TextView - android:id="@+id/weblayer_translate_infobar_tab_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:textAppearance="@style/TextAppearance.Design.Tab" - android:visibility="visible" - android:singleLine="true" /> - <ProgressBar - android:id="@+id/weblayer_translate_infobar_tab_progressbar" - android:layout_width="@dimen/infobar_small_icon_size" - android:layout_height="@dimen/infobar_small_icon_size" - android:layout_gravity="center" - android:indeterminate="true" - android:visibility="invisible" /> -</org.chromium.weblayer_private.TranslateTabContent> diff --git a/chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item.xml b/chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item.xml deleted file mode 100644 index a7948450da3..00000000000 --- a/chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2017 The Chromium Authors. All rights reserved. - Use of this source code is governed by a BSD-style license that can be - found in the LICENSE file. --> - -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - style="@style/WebLayerAppMenuItem" - android:layout_width="match_parent" - android:layout_height="wrap_content" > - - <TextView - android:id="@+id/weblayer_menu_item_text" - android:textAppearance="?android:attr/textAppearanceLargePopupMenu" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="start" - android:gravity="center_vertical" - android:paddingTop="13dp" - android:paddingBottom="13dp" /> - -</FrameLayout> diff --git a/chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item_checked.xml b/chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item_checked.xml deleted file mode 100644 index a4bd2cf4692..00000000000 --- a/chromium/weblayer/browser/java/res/layout/weblayer_translate_menu_item_checked.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2017 The Chromium Authors. All rights reserved. - Use of this source code is governed by a BSD-style license that can be - found in the LICENSE file. --> - -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <LinearLayout - style="@style/WebLayerAppMenuItem" - android:layout_width="match_parent" - android:layout_height="wrap_content" > - - <TextView - android:id="@+id/weblayer_menu_item_text" - android:textAppearance="?android:attr/textAppearanceLargePopupMenu" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:layout_gravity="start" - android:gravity="center_vertical" - android:paddingTop="13dp" - android:paddingBottom="13dp" - android:paddingEnd="16dp" /> - <org.chromium.ui.widget.ChromeImageView - android:id="@+id/weblayer_menu_item_icon" - android:src="@drawable/ic_check_googblue_24dp" - android:layout_width="24dp" - android:layout_height="match_parent" - android:layout_gravity="end" - android:gravity="center_vertical" - app:tint="@color/default_icon_color_tint_list" /> - - </LinearLayout> - - <View - android:id="@+id/weblayer_menu_item_divider" - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="@color/divider_line_bg_color" - android:visibility="gone" /> - -</LinearLayout> - diff --git a/chromium/weblayer/browser/java/res/values/dimens.xml b/chromium/weblayer/browser/java/res/values/dimens.xml index 0eee3fc9758..30a1496801f 100644 --- a/chromium/weblayer/browser/java/res/values/dimens.xml +++ b/chromium/weblayer/browser/java/res/values/dimens.xml @@ -9,7 +9,6 @@ <!-- Dimensions for compact translate infobar. --> <dimen name="weblayer_infobar_translate_fade_edge_length">18dp</dimen> - <dimen name="weblayer_infobar_translate_menu_width">260dp</dimen> <!-- Dimens of tab indicator --> <dimen name="weblayer_tab_indicator_radius">3dp</dimen> diff --git a/chromium/weblayer/browser/java/res/values/styles.xml b/chromium/weblayer/browser/java/res/values/styles.xml index f362fd9ab69..ff8d17d2a84 100644 --- a/chromium/weblayer/browser/java/res/values/styles.xml +++ b/chromium/weblayer/browser/java/res/values/styles.xml @@ -8,15 +8,9 @@ <item name="spinnerStyle">@style/SpinnerStyle</item> <item name="preferenceTheme">@style/PreferenceTheme</item> <item name="alertDialogTheme">@style/Theme.Chromium.AlertDialog</item> - </style> - - <!-- The following styles may be used to style views provided by a CustomViewBinder or attached - to the app menu as headers or footers. --> - <!-- Styling for an app menu item row. --> - <style name="WebLayerAppMenuItem"> - <item name="android:paddingStart">16dp</item> - <item name="android:paddingEnd">16dp</item> - <item name="android:background">?attr/listChoiceBackgroundIndicator</item> + <!-- Text style attributes used by the preference_material.xml layout. --> + <item name="android:textAppearanceListItem">@style/TextAppearance.TextLarge.Primary</item> + <item name="android:textColorSecondary">@color/default_text_color_secondary_list</item> </style> </resources> diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_af.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_af.xtb index 62d289ff62c..403610204ab 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_af.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_af.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="af"> -<translation id="1068672505746868501">Moet nooit bladsye in <ph name="SOURCE_LANGUAGE" /> vertaal nie</translation> -<translation id="124116460088058876">Meer tale</translation> -<translation id="1285320974508926690">Moet nooit hierdie werf vertaal nie</translation> -<translation id="290376772003165898">Bladsy is nie in <ph name="LANGUAGE" /> nie?</translation> -<translation id="5684874026226664614">Oeps. Hierdie bladsy kon nie vertaal word nie.</translation> -<translation id="6040143037577758943">Maak toe</translation> -<translation id="6831043979455480757">Vertaal</translation> -<translation id="7243308994586599757">Opsies is naby die onderkant van die skerm beskikbaar</translation> -<translation id="773466115871691567">Vertaal altyd bladsye in <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Webblaaieraktiwiteit</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_am.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_am.xtb index ec9bad225cb..2fd6e46a16e 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_am.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_am.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="am"> -<translation id="1068672505746868501">በ<ph name="SOURCE_LANGUAGE" /> ውስጥ ገጾችን በጭራሽ አትተርጉም</translation> -<translation id="124116460088058876">ተጨማሪ ቋንቋዎች</translation> -<translation id="1285320974508926690">ይህን ጣቢያ በጭራሽ አትተርጉም</translation> -<translation id="290376772003165898">ገጽ በ<ph name="LANGUAGE" /> አይደለም?</translation> -<translation id="5684874026226664614">ውይ። ይህ ገጽ ሊተረጎም አይችልም።</translation> -<translation id="6040143037577758943">ዝጋ</translation> -<translation id="6831043979455480757">መተርጎም</translation> -<translation id="7243308994586599757">አማራጮች ከማያ ገጹ ግርጌ አጠገብ ይገኛሉ</translation> -<translation id="773466115871691567">ገጾችን ሁልጊዜ በ<ph name="SOURCE_LANGUAGE" /> ተርጉም</translation> <translation id="8298278839890148234">የድር አሳሽ እንቅስቃሴ</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ar.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ar.xtb index b631d5aa80b..55fcefece9b 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_ar.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ar.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="ar"> -<translation id="1068672505746868501">عدم ترجمة الصفحات باللغة <ph name="SOURCE_LANGUAGE" /> مُطلقًا</translation> -<translation id="124116460088058876">مزيد من اللغات</translation> -<translation id="1285320974508926690">عدم ترجمة هذا الموقع مطلقًا</translation> -<translation id="290376772003165898">أليست الصفحة باللغة <ph name="LANGUAGE" />؟</translation> -<translation id="5684874026226664614">عفوًا. تعذرت ترجمة هذه الصفحة.</translation> -<translation id="6040143037577758943">إغلاق</translation> -<translation id="6831043979455480757">ترجمة</translation> -<translation id="7243308994586599757">الخيارات المتاحة بالقرب من الجزء السفلي من الشاشة</translation> -<translation id="773466115871691567">ترجمة الصفحات باللغة <ph name="SOURCE_LANGUAGE" /> دائمًا</translation> <translation id="8298278839890148234">نشاط التصفُّح على الويب</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_as.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_as.xtb index 409550b1d05..db63c417200 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_as.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_as.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="as"> -<translation id="1068672505746868501">পৃষ্ঠা <ph name="SOURCE_LANGUAGE" />লৈ কেতিয়াও অনুবাদ নকৰিব</translation> -<translation id="124116460088058876">অধিক ভাষা</translation> -<translation id="1285320974508926690">এই ছাইটটো কেতিয়াও অনুবাদ নকৰিব</translation> -<translation id="290376772003165898">পৃষ্ঠাটো <ph name="LANGUAGE" /> ভাষাত নাই নেকি?</translation> -<translation id="5684874026226664614">ওঁহ এই পৃষ্ঠাটো অনুবাদ কৰিব পৰা নগ’ল।</translation> -<translation id="6040143037577758943">বন্ধ কৰক</translation> -<translation id="6831043979455480757">অনুবাদ কৰক</translation> -<translation id="7243308994586599757">স্ক্ৰীণৰ কাষৰ বুটামত উপলব্ধ বিকল্প</translation> -<translation id="773466115871691567">সদায় পৃষ্ঠাসমূহ <ph name="SOURCE_LANGUAGE" />লৈ অনুবাদ কৰক</translation> <translation id="8298278839890148234">ৱেব ব্ৰাউজাৰৰ কার্যকলাপ</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_az.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_az.xtb index cd8a4a44f50..684871cc3c3 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_az.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_az.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="az"> -<translation id="1068672505746868501">Heç vaxt <ph name="SOURCE_LANGUAGE" /> dilində olan səhifələri tərcümə etməyin</translation> -<translation id="124116460088058876">Digər dillər</translation> -<translation id="1285320974508926690">Bu saytı heç vaxt tərcümə etməyin</translation> -<translation id="290376772003165898">Səhifə <ph name="LANGUAGE" /> dilində deyil?</translation> -<translation id="5684874026226664614">Bu səhifə tərcümə edilə bilmir. Niyəsini bilmirik.</translation> -<translation id="6040143037577758943">Qapat</translation> -<translation id="6831043979455480757">Tərcümə et</translation> -<translation id="7243308994586599757">Seçənəklər ekranın aşağısına yaxın yerdə əlçatandır</translation> -<translation id="773466115871691567">Səhifələri həmişə <ph name="SOURCE_LANGUAGE" /> dilinə tərcümə edin</translation> <translation id="8298278839890148234">Veb axtarış fəaliyyəti</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_be.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_be.xtb index eff070e867a..4c18cf28a2f 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_be.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_be.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="be"> -<translation id="1068672505746868501">Ніколі не перакладаць старонкі на наступнай мове: <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Іншыя мовы</translation> -<translation id="1285320974508926690">Ніколі не перакладаць гэты сайт</translation> -<translation id="290376772003165898">Мова старонкі – не <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Памылка. Не ўдалося перакласці гэту старонку.</translation> -<translation id="6040143037577758943">Закрыць</translation> -<translation id="6831043979455480757">Перакласці</translation> -<translation id="7243308994586599757">Параметры знаходзяцца ў ніжняй частцы экрана</translation> -<translation id="773466115871691567">Заўсёды перакладаць старонкі на наступнай мове: <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Дзеянні ў вэб-браўзеры</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_bg.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_bg.xtb index a55fe167dff..1e7789939c0 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_bg.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_bg.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="bg"> -<translation id="1068672505746868501">Страниците на <ph name="SOURCE_LANGUAGE" /> да не се превеждат</translation> -<translation id="124116460088058876">Още езици</translation> -<translation id="1285320974508926690">Този сайт да не се превежда никога</translation> -<translation id="290376772003165898">Страницата не е на <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Ами сега! Тази страница не можа да се преведе.</translation> -<translation id="6040143037577758943">Затваряне</translation> -<translation id="6831043979455480757">Превод</translation> -<translation id="7243308994586599757">Опциите са в долната част на екрана</translation> -<translation id="773466115871691567">Страниците на <ph name="SOURCE_LANGUAGE" /> да се превеждат винаги</translation> <translation id="8298278839890148234">Активност в уеб браузъра</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_bn.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_bn.xtb index 81f76d848f2..374b81798d7 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_bn.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_bn.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="bn"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> ভাষার পৃষ্ঠার অনুবাদ কখনও দেখতে চাই না</translation> -<translation id="124116460088058876">আরও ভাষা</translation> -<translation id="1285320974508926690">কখনই এই সাইটটিকে অনুবাদ করবেন না</translation> -<translation id="290376772003165898">পৃষ্ঠাটি <ph name="LANGUAGE" /> ভাষায় নয়?</translation> -<translation id="5684874026226664614">ওহো৷ এই পৃষ্ঠাটির অনুবাদ করা যাবে না৷</translation> -<translation id="6040143037577758943">বন্ধ</translation> -<translation id="6831043979455480757">অনুবাদ</translation> -<translation id="7243308994586599757">স্ক্রীনের প্রায় নীচের দিকে বিকল্পগুলি উপলব্ধ</translation> -<translation id="773466115871691567">সব সময় <ph name="SOURCE_LANGUAGE" /> ভাষার পৃষ্ঠার অনুবাদ দেখতে চাই</translation> <translation id="8298278839890148234">ওয়েব ব্রাউজার অ্যাক্টিভিটি</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_bs.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_bs.xtb index 5d1aefcaea1..4fb2dbb390e 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_bs.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_bs.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="bs"> -<translation id="1068672505746868501">Nemoj nikada prevoditi stranice na <ph name="SOURCE_LANGUAGE" /> jezik</translation> -<translation id="124116460088058876">Više jezika</translation> -<translation id="1285320974508926690">Nikada ne prevodi ovu web lokaciju</translation> -<translation id="290376772003165898">Ovo nije <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Ups. Prijevod ove stranice nije uspio.</translation> -<translation id="6040143037577758943">Zatvori</translation> -<translation id="6831043979455480757">Prevedi</translation> -<translation id="7243308994586599757">Opcije su dostupne pri dnu ekrana</translation> -<translation id="773466115871691567">Uvijek prevedi stranice na <ph name="SOURCE_LANGUAGE" /> jezik</translation> <translation id="8298278839890148234">Aktivnost web preglednika</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ca.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ca.xtb index 3c181c2e235..ba2d4e4da24 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_ca.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ca.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="ca"> -<translation id="1068672505746868501">No tradueixis mai les pàgines en <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Més idiomes</translation> -<translation id="1285320974508926690">No tradueixis mai aquest lloc</translation> -<translation id="290376772003165898">La pàgina no està en <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Aquesta pàgina no s'ha pogut traduir.</translation> -<translation id="6040143037577758943">Tanca</translation> -<translation id="6831043979455480757">Tradueix</translation> -<translation id="7243308994586599757">Opcions disponibles a la part inferior de la pantalla</translation> -<translation id="773466115871691567">Tradueix sempre les pàgines en <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Activitat del navegador web</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_cs.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_cs.xtb index 7e7b91a9744..5710c42229a 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_cs.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_cs.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="cs"> -<translation id="1068672505746868501">Stránky v jazyce <ph name="SOURCE_LANGUAGE" /> nikdy nepřekládat</translation> -<translation id="124116460088058876">Další jazyky</translation> -<translation id="1285320974508926690">Tento web nikdy nepřekládat</translation> -<translation id="290376772003165898">Stránka není v jazyce <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Jejda. Tuto stránku se nepodařilo přeložit.</translation> -<translation id="6040143037577758943">Zavřít</translation> -<translation id="6831043979455480757">Přeložit</translation> -<translation id="7243308994586599757">Možnosti jsou k dispozici ve spodní části obrazovky</translation> -<translation id="773466115871691567">Stránky v jazyce <ph name="SOURCE_LANGUAGE" /> vždy překládat</translation> <translation id="8298278839890148234">Aktivita ve webovém prohlížeči</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_da.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_da.xtb index 571f36b83ac..63df7f53aa1 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_da.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_da.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="da"> -<translation id="1068672505746868501">Oversæt aldrig sider på <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Flere sprog</translation> -<translation id="1285320974508926690">Oversæt aldrig dette website</translation> -<translation id="290376772003165898">Er siden ikke på <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Ups! Denne side kunne ikke oversættes.</translation> -<translation id="6040143037577758943">Luk</translation> -<translation id="6831043979455480757">Oversæt</translation> -<translation id="7243308994586599757">Du finder indstillingerne nederst på skærmen</translation> -<translation id="773466115871691567">Oversæt altid sider på <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Webbrowseraktivitet</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_de.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_de.xtb index 65cd87c466a..a6c724e09b6 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_de.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_de.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="de"> -<translation id="1068672505746868501">Seiten auf <ph name="SOURCE_LANGUAGE" /> nie übersetzen</translation> -<translation id="124116460088058876">Weitere Sprachen</translation> -<translation id="1285320974508926690">Diese Website nie übersetzen</translation> -<translation id="290376772003165898">Diese Seite ist nicht auf <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Hoppla! Diese Seite konnte nicht übersetzt werden.</translation> -<translation id="6040143037577758943">Schließen</translation> -<translation id="6831043979455480757">Übersetzen</translation> -<translation id="7243308994586599757">Optionen unten auf dem Bildschirm verfügbar</translation> -<translation id="773466115871691567">Seiten auf <ph name="SOURCE_LANGUAGE" /> immer übersetzen</translation> <translation id="8298278839890148234">Webbrowseraktivitäten</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_el.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_el.xtb index 1e22b4a980c..91f7efb7d08 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_el.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_el.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="el"> -<translation id="1068672505746868501">Να μην γίνεται μετάφραση σελίδων στα <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Περισσότερες γλώσσες</translation> -<translation id="1285320974508926690">Να μην γίνεται ποτέ μετάφραση αυτού του ιστότοπου</translation> -<translation id="290376772003165898">Η σελίδα δεν είναι στα <ph name="LANGUAGE" />;</translation> -<translation id="5684874026226664614">Ωχ. Δεν ήταν δυνατή η μετάφραση αυτής της σελίδας.</translation> -<translation id="6040143037577758943">Κλείσιμο</translation> -<translation id="6831043979455480757">Μετάφραση</translation> -<translation id="7243308994586599757">Διαθέσιμες επιλογές κοντά κάτω μέρος της οθόνης</translation> -<translation id="773466115871691567">Να μεταφράζονται πάντα οι σελίδες προς τα <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Δραστηριότητα προγράμματος περιήγησης στον ιστό</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_en-GB.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_en-GB.xtb index 8f63e62c0db..078cecbe647 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_en-GB.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_en-GB.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="en-GB"> -<translation id="1068672505746868501">Never translate pages in <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">More languages</translation> -<translation id="1285320974508926690">Never translate this site</translation> -<translation id="290376772003165898">Page is not in <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Oops. This page could not be translated.</translation> -<translation id="6040143037577758943">Close</translation> -<translation id="6831043979455480757">Translate</translation> -<translation id="7243308994586599757">Options available near bottom of the screen</translation> -<translation id="773466115871691567">Always translate pages in <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Web browser activity</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_es-419.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_es-419.xtb index 276fe173c58..e529fa34f49 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_es-419.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_es-419.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="es-419"> -<translation id="1068672505746868501">No traducir nunca páginas en <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Más idiomas</translation> -<translation id="1285320974508926690">Nunca traducir este sitio</translation> -<translation id="290376772003165898">¿La página no está en <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">No se puede traducir esta página.</translation> -<translation id="6040143037577758943">Cerrar</translation> -<translation id="6831043979455480757">Traducir</translation> -<translation id="7243308994586599757">Opciones disponibles junto a la parte inferior de la pantalla</translation> -<translation id="773466115871691567">Traducir siempre las páginas en <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Actividad del navegador web</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_es.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_es.xtb index 8b61d2a834e..fcf6da55978 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_es.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_es.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="es"> -<translation id="1068672505746868501">No traducir nunca las páginas en <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Más idiomas</translation> -<translation id="1285320974508926690">No traducir nunca este sitio</translation> -<translation id="290376772003165898">¿La página no está en <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">¡Vaya! No se ha podido traducir esta página.</translation> -<translation id="6040143037577758943">Cerrar</translation> -<translation id="6831043979455480757">Traducir</translation> -<translation id="7243308994586599757">Opciones disponibles cerca de la parte inferior de la pantalla</translation> -<translation id="773466115871691567">Traducir siempre las páginas en <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Actividad del navegador web</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_et.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_et.xtb index ff14c3ea03e..37ada0e9df4 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_et.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_et.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="et"> -<translation id="1068672505746868501">Ära tõlgi kunagi <ph name="SOURCE_LANGUAGE" /> keeles olevaid lehti</translation> -<translation id="124116460088058876">Rohkem keeli</translation> -<translation id="1285320974508926690">Ära kunagi seda saiti tõlgi</translation> -<translation id="290376772003165898">Kas leht ei ole <ph name="LANGUAGE" /> keeles?</translation> -<translation id="5684874026226664614">Vabandust. Lehte ei õnnestunud tõlkida.</translation> -<translation id="6040143037577758943">Sulge</translation> -<translation id="6831043979455480757">Tõlgi</translation> -<translation id="7243308994586599757">Valikud on saadaval ekraani allosas</translation> -<translation id="773466115871691567">Tõlgi alati <ph name="SOURCE_LANGUAGE" /> keeles olevad lehed</translation> <translation id="8298278839890148234">Veebibrauseri tegevused</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_eu.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_eu.xtb index 0fe579c1cb0..2698f9fb351 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_eu.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_eu.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="eu"> -<translation id="1068672505746868501">Ez itzuli inoiz <ph name="SOURCE_LANGUAGE" /> darabilten orriak</translation> -<translation id="124116460088058876">Hizkuntza gehiago</translation> -<translation id="1285320974508926690">Ez itzuli inoiz webgune hau</translation> -<translation id="290376772003165898">Ez al da <ph name="LANGUAGE" /> orriko hizkuntza?</translation> -<translation id="5684874026226664614">Ezin izan da orria itzuli.</translation> -<translation id="6040143037577758943">Itxi</translation> -<translation id="6831043979455480757">Itzuli</translation> -<translation id="7243308994586599757">Pantailaren behealdean agertzen dira dauden aukerak</translation> -<translation id="773466115871691567">Itzuli beti <ph name="SOURCE_LANGUAGE" /> darabilten orriak</translation> <translation id="8298278839890148234">Sareko arakatze-jarduerak</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_fa.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_fa.xtb index b3cf59018eb..77daea83d86 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_fa.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_fa.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="fa"> -<translation id="1068672505746868501">هرگز صفحههای <ph name="SOURCE_LANGUAGE" /> ترجمه نشوند</translation> -<translation id="124116460088058876">زبانهای بیشتر</translation> -<translation id="1285320974508926690">این سایت هرگز ترجمه نشود</translation> -<translation id="290376772003165898">صفحه به زبان <ph name="LANGUAGE" /> وجود ندارد؟</translation> -<translation id="5684874026226664614">متأسفیم. این صفحه ترجمه نشد.</translation> -<translation id="6040143037577758943">بستن</translation> -<translation id="6831043979455480757">ترجمه</translation> -<translation id="7243308994586599757">گزینهها در نزدیک پایین صفحه نمایش در دسترس هستند</translation> -<translation id="773466115871691567">صفحههای <ph name="SOURCE_LANGUAGE" /> همیشه ترجمه شوند</translation> <translation id="8298278839890148234">فعالیت مرورگر وب</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_fi.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_fi.xtb index e8e035371e0..78a8d793d2e 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_fi.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_fi.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="fi"> -<translation id="1068672505746868501">Älä koskaan käännä kielellä <ph name="SOURCE_LANGUAGE" /> kirjoitettuja sivuja.</translation> -<translation id="124116460088058876">Lisää kieliä</translation> -<translation id="1285320974508926690">Älä käännä tätä sivustoa</translation> -<translation id="290376772003165898">Eikö sivu ole kirjoitettu kielellä <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Hups, tätä sivua ei voi kääntää.</translation> -<translation id="6040143037577758943">Sulje</translation> -<translation id="6831043979455480757">Käännä</translation> -<translation id="7243308994586599757">Asetukset löytyvät näytön alalaidasta.</translation> -<translation id="773466115871691567">Käännä aina kielellä <ph name="SOURCE_LANGUAGE" /> kirjoitut sivut</translation> <translation id="8298278839890148234">Verkon selaustoiminta</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_fil.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_fil.xtb index 01ddf5eb7ec..08f97ffffd4 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_fil.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_fil.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="fil"> -<translation id="1068672505746868501">Huwag kailanman isalin ang mga page sa <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Higit pang wika</translation> -<translation id="1285320974508926690">Huwag isalin kailanman ang site na ito</translation> -<translation id="290376772003165898">Hindi nakasalin ang page sa <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Oops. Hindi maisalin ang pahinang ito.</translation> -<translation id="6040143037577758943">Isara</translation> -<translation id="6831043979455480757">Isalin</translation> -<translation id="7243308994586599757">May mga opsyon malapit sa ibaba ng screen</translation> -<translation id="773466115871691567">Palaging isalin ang mga page sa <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Aktibidad ng web browser</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_fr-CA.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_fr-CA.xtb index 2415250b42a..537978cc551 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_fr-CA.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_fr-CA.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="fr-CA"> -<translation id="1068672505746868501">Ne jamais traduire les pages en <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Plus de langues</translation> -<translation id="1285320974508926690">Ne jamais traduire ce site</translation> -<translation id="290376772003165898">Cette page n'est pas en <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Oups… Impossible de traduire cette page.</translation> -<translation id="6040143037577758943">Fermer</translation> -<translation id="6831043979455480757">Traduire</translation> -<translation id="7243308994586599757">Options disponibles vers le bas de l’écran</translation> -<translation id="773466115871691567">Toujours traduire les pages en <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Activité de navigation Web</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_fr.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_fr.xtb index 334bf0344fd..9810a3e323a 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_fr.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_fr.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="fr"> -<translation id="1068672505746868501">Ne jamais traduire les pages en <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Plus de langues</translation> -<translation id="1285320974508926690">Ne jamais traduire ce site</translation> -<translation id="290376772003165898">La page n'est pas en <ph name="LANGUAGE" /> ?</translation> -<translation id="5684874026226664614">Petit problème… Impossible de traduire cette page.</translation> -<translation id="6040143037577758943">Fermer</translation> -<translation id="6831043979455480757">Traduire</translation> -<translation id="7243308994586599757">Options disponibles au bas de l'écran</translation> -<translation id="773466115871691567">Toujours traduire les pages en <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Activité de navigation sur le Web</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_gl.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_gl.xtb index 3f598e52c3b..c315bcbcd56 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_gl.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_gl.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="gl"> -<translation id="1068672505746868501">Non traducir nunca páxinas en <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Máis idiomas</translation> -<translation id="1285320974508926690">Non traducir nunca este sitio</translation> -<translation id="290376772003165898">A páxina non está en <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Non se puido traducir esta páxina.</translation> -<translation id="6040143037577758943">Pechar</translation> -<translation id="6831043979455480757">Traducir</translation> -<translation id="7243308994586599757">Opcións dispoñibles na parte inferior da pantalla</translation> -<translation id="773466115871691567">Traducir sempre páxinas en <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Actividade de navegación pola Web</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_gu.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_gu.xtb index 957cc2993a6..9e2ee22696d 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_gu.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_gu.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="gu"> -<translation id="1068672505746868501">પેજનો ક્યારેય પણ <ph name="SOURCE_LANGUAGE" />માં અનુવાદ કરશો નહીં</translation> -<translation id="124116460088058876">વધુ ભાષાઓ</translation> -<translation id="1285320974508926690">આ સાઇટનું ક્યારેય ભાષાંતર કરશો નહીં</translation> -<translation id="290376772003165898">પેજ <ph name="LANGUAGE" />માં નથી?</translation> -<translation id="5684874026226664614">અરેરે. આ પૃષ્ઠનો અનુવાદ કરી શકાયો નથી.</translation> -<translation id="6040143037577758943">બંધ કરો</translation> -<translation id="6831043979455480757">અનુવાદ કરો</translation> -<translation id="7243308994586599757">સ્ક્રીનના તળિયા નજીક વિકલ્પો ઉપલબ્ધ છે</translation> -<translation id="773466115871691567">હંમેશાં પેજનો <ph name="SOURCE_LANGUAGE" />માં અનુવાદ કરો</translation> <translation id="8298278839890148234">વેબ બ્રાઉઝરની પ્રવૃત્તિ</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_hi.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_hi.xtb index 6fe2057217c..a9ae0e70db1 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_hi.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_hi.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="hi"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> भाषा के पेज का कभी भी अनुवाद न करें</translation> -<translation id="124116460088058876">ज़्यादा भाषाएं</translation> -<translation id="1285320974508926690">कभी भी इस साइट का अनुवाद न करें</translation> -<translation id="290376772003165898">क्या पेज <ph name="LANGUAGE" /> भाषा में नहीं है?</translation> -<translation id="5684874026226664614">ओह. इस पेज का अनुवाद नहीं किया जा सका.</translation> -<translation id="6040143037577758943">बंद करें</translation> -<translation id="6831043979455480757">अनुवाद करें</translation> -<translation id="7243308994586599757">विकल्प स्क्रीन के नीचे उपलब्ध हैं</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> भाषा के पेज का हमेशा अनुवाद करें</translation> <translation id="8298278839890148234">वेब ब्राउज़र की गतिविधि</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_hr.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_hr.xtb index d9fc4ee6cec..bc0817b9c82 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_hr.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_hr.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="hr"> -<translation id="1068672505746868501">Nikad ne prevodi <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Više jezika</translation> -<translation id="1285320974508926690">Nikad nemoj prevoditi ovu web-lokaciju</translation> -<translation id="290376772003165898">Ovo nije <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Ups. Stranicu nije bilo moguće prevesti.</translation> -<translation id="6040143037577758943">Zatvori</translation> -<translation id="6831043979455480757">Prevedi</translation> -<translation id="7243308994586599757">Opcije dostupne pri dnu zaslona</translation> -<translation id="773466115871691567">Uvijek prevodi <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Aktivnost u web-pregledniku</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_hu.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_hu.xtb index 498d74cc4c9..587b17fb1f5 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_hu.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_hu.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="hu"> -<translation id="1068672505746868501">Soha ne fordítsa le a(z) <ph name="SOURCE_LANGUAGE" /> nyelvű oldalakat</translation> -<translation id="124116460088058876">További nyelvek…</translation> -<translation id="1285320974508926690">Ezt a webhelyet soha ne fordítsa le</translation> -<translation id="290376772003165898">Az oldal nem <ph name="LANGUAGE" /> nyelvű?</translation> -<translation id="5684874026226664614">Hoppá! Az oldalt nem sikerült lefordítani.</translation> -<translation id="6040143037577758943">Bezárás</translation> -<translation id="6831043979455480757">Fordítás</translation> -<translation id="7243308994586599757">A beállítások a képernyő alsó részén találhatók</translation> -<translation id="773466115871691567">Mindig fordítsa le a(z) <ph name="SOURCE_LANGUAGE" /> nyelvű oldalakat</translation> <translation id="8298278839890148234">Böngészős tevékenységek</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_hy.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_hy.xtb index e3c1efcec5e..c5676da7cb3 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_hy.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_hy.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="hy"> -<translation id="1068672505746868501">Երբեք չթարգմանել <ph name="SOURCE_LANGUAGE" /> էջերը</translation> -<translation id="124116460088058876">Այլ լեզուներ</translation> -<translation id="1285320974508926690">Երբեք չթարգմանել այս կայքը</translation> -<translation id="290376772003165898">Էջը <ph name="LANGUAGE" /> չէ՞</translation> -<translation id="5684874026226664614">Չհաջողվեց թարգմանել այս էջը:</translation> -<translation id="6040143037577758943">Փակել</translation> -<translation id="6831043979455480757">Թարգմանել</translation> -<translation id="7243308994586599757">Ընտրանքները հասանելի են էկրանի ստորին հատվածում</translation> -<translation id="773466115871691567">Միշտ թարգմանել <ph name="SOURCE_LANGUAGE" /> էջերը</translation> <translation id="8298278839890148234">Գործողություններ դիտարկիչում</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_id.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_id.xtb index 673f32be14c..31b5fe5d797 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_id.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_id.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="id"> -<translation id="1068672505746868501">Jangan pernah menerjemahkan halaman dalam <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Bahasa lainnya</translation> -<translation id="1285320974508926690">Jangan pernah terjemahkan situs ini</translation> -<translation id="290376772003165898">Halaman tidak dalam bahasa <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Ups. Halaman ini tidak dapat diterjemahkan.</translation> -<translation id="6040143037577758943">Tutup</translation> -<translation id="6831043979455480757">Terjemahkan</translation> -<translation id="7243308994586599757">Opsi terdapat di dekat bagian bawah layar</translation> -<translation id="773466115871691567">Selalu terjemahkan halaman dalam bahasa <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Aktivitas penjelajahan web</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_is.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_is.xtb index 2f77ab69860..923602bb298 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_is.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_is.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="is"> -<translation id="1068672505746868501">Þýða aldrei síður á þessu tungumáli: <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Fleiri tungumál</translation> -<translation id="1285320974508926690">Aldrei þýða þetta vefsvæði</translation> -<translation id="290376772003165898">Er tungumál síðunnar ekki <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Úbbs. Þessa síðu er ekki hægt að þýða.</translation> -<translation id="6040143037577758943">Loka</translation> -<translation id="6831043979455480757">Þýða</translation> -<translation id="7243308994586599757">Valkostir eru neðst á skjánum</translation> -<translation id="773466115871691567">Þýða alltaf síður á þessu tungumáli: <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Vafranotkun</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_it.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_it.xtb index 1f19226503d..c4dc6e6ca5d 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_it.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_it.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="it"> -<translation id="1068672505746868501">Non tradurre mai le pagine in <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Altre lingue</translation> -<translation id="1285320974508926690">Non tradurre mai questo sito</translation> -<translation id="290376772003165898">La pagina non è in <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Spiacenti. Impossibile tradurre questa pagina.</translation> -<translation id="6040143037577758943">Chiudi</translation> -<translation id="6831043979455480757">Traduci</translation> -<translation id="7243308994586599757">Opzioni disponibili nella parte inferiore dello schermo</translation> -<translation id="773466115871691567">Traduci sempre le pagine in <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Attività del browser web</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_iw.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_iw.xtb index 61e44631e2e..bea498feca7 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_iw.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_iw.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="iw"> -<translation id="1068672505746868501">אף פעם אל תתרגם דפים ב<ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">שפות נוספות</translation> -<translation id="1285320974508926690">איני רוצה לקבל תרגום של אתר זה</translation> -<translation id="290376772003165898">הדף לא ב<ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">אופס. לא ניתן היה לתרגם את הדף הזה.</translation> -<translation id="6040143037577758943">סגור</translation> -<translation id="6831043979455480757">תרגום</translation> -<translation id="7243308994586599757">אפשרויות הזמינות באזור החלק התחתון של המסך</translation> -<translation id="773466115871691567">ברצוני לקבל תרגום תמיד דפים ב<ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">פעילות דפדפן אינטרנט</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ja.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ja.xtb index cf3a9d91955..091fdcc87ff 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_ja.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ja.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="ja"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" />のページを翻訳しない</translation> -<translation id="124116460088058876">その他の言語</translation> -<translation id="1285320974508926690">このサイトは翻訳しない</translation> -<translation id="290376772003165898"><ph name="LANGUAGE" />のページではない場合</translation> -<translation id="5684874026226664614">このページを翻訳できませんでした。</translation> -<translation id="6040143037577758943">閉じる</translation> -<translation id="6831043979455480757">翻訳</translation> -<translation id="7243308994586599757">画面の下の方にオプションがあります</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" />のページを常に翻訳する</translation> <translation id="8298278839890148234">ウェブブラウザのアクティビティ</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ka.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ka.xtb index 2aa6a13f2ff..57bc785af7f 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_ka.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ka.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="ka"> -<translation id="1068672505746868501">არასოდეს ითარგმნოს <ph name="SOURCE_LANGUAGE" /> გვერდები</translation> -<translation id="124116460088058876">სხვა ენები</translation> -<translation id="1285320974508926690">არასდროს გადათარგმნო ეს საიტი</translation> -<translation id="290376772003165898">გვერდის ტექსტი არ არის <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">ამ გვერდის გადათარგმნა ვერ მოხერხდა.</translation> -<translation id="6040143037577758943">დახურვა</translation> -<translation id="6831043979455480757">თარგმნა</translation> -<translation id="7243308994586599757">ვარიანტები ხელმისაწვდომია ეკრანის ქვედა ნაწილთან</translation> -<translation id="773466115871691567">ყოველთვის ითარგმნოს <ph name="SOURCE_LANGUAGE" /> გვერდები</translation> <translation id="8298278839890148234">ვებ-ბრაუზერის აქტივობა</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_kk.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_kk.xtb index 2e23049928a..d18117e9c7f 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_kk.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_kk.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="kk"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> тіліндегі беттер ешқашан аударылмасын</translation> -<translation id="124116460088058876">Қосымша тілдер</translation> -<translation id="1285320974508926690">Бұл сайтты ешқашан аудармау</translation> -<translation id="290376772003165898">Бет <ph name="LANGUAGE" /> тілінде емес пе?</translation> -<translation id="5684874026226664614">Бұл бетті аудару мүмкін емес.</translation> -<translation id="6040143037577758943">Жабу</translation> -<translation id="6831043979455480757">Аудару</translation> -<translation id="7243308994586599757">Опциялар экранның төменгі жағында тұрады</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> тіліндегі беттер әрқашан аударылсын</translation> <translation id="8298278839890148234">Браузерді қолдану мәліметі</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_km.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_km.xtb index 79e7f51cb82..312955fb520 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_km.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_km.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="km"> -<translation id="1068672505746868501">កុំបកប្រែទំព័រជាភាសា <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">ភាសាច្រើនទៀត</translation> -<translation id="1285320974508926690">មិនបកប្រែគេហទំព័រនេះទៀតឡើយ</translation> -<translation id="290376772003165898">ទំព័រមិនមានជាភាសា <ph name="LANGUAGE" /> ទេ?</translation> -<translation id="5684874026226664614">អូ។ ទំព័រនេះមិនអាចបកប្រែទេ។</translation> -<translation id="6040143037577758943">បិទ</translation> -<translation id="6831043979455480757">បកប្រែ</translation> -<translation id="7243308994586599757">មានជម្រើសនៅក្បែរផ្នែកខាងក្រោមអេក្រង់</translation> -<translation id="773466115871691567">បកប្រែទំព័រជាភាសា <ph name="SOURCE_LANGUAGE" /> ជានិច្ច</translation> <translation id="8298278839890148234">សកម្មភាពកម្មវិធីរុករកតាមអ៊ីនធឺណិត</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_kn.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_kn.xtb index cabf85f2d68..91524cd5c0f 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_kn.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_kn.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="kn"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> ನಲ್ಲಿನ ಪುಟಗಳನ್ನು ಎಂದಿಗೂ ಅನುವಾದ ಮಾಡಬೇಡಿ</translation> -<translation id="124116460088058876">ಹೆಚ್ಚಿನ ಭಾಷೆಗಳು</translation> -<translation id="1285320974508926690">ಈ ಸೈಟ್ ಅನ್ನು ಎಂದಿಗೂ ಭಾಷಾಂತರಿಸದಿರಿ</translation> -<translation id="290376772003165898"><ph name="LANGUAGE" /> ನಲ್ಲಿನ ಪುಟ ಇಲ್ಲವೇ?</translation> -<translation id="5684874026226664614">ಓಹ್. ಈ ಪುಟವನ್ನು ಅನುವಾದಿಸಲಾಗುವುದಿಲ್ಲ.</translation> -<translation id="6040143037577758943">ಮುಚ್ಚಿರಿ</translation> -<translation id="6831043979455480757">ಅನುವಾದಿಸು</translation> -<translation id="7243308994586599757">ಪರದೆಯ ಕೆಳಗೆ ಲಭ್ಯವಿರುವ ಆಯ್ಕೆಗಳು</translation> -<translation id="773466115871691567">ಯಾವಾಗಲು <ph name="SOURCE_LANGUAGE" /> ನಲ್ಲಿನ ಪುಟಗಳನ್ನು ಅನುವಾದ ಮಾಡಿ</translation> <translation id="8298278839890148234">ವೆಬ್ ಬ್ರೌಸಿಂಗ್ ಚಟುವಟಿಕೆ</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ko.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ko.xtb index de20f5dc367..742a5338444 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_ko.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ko.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="ko"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" />로 된 페이지는 번역하지 않음</translation> -<translation id="124116460088058876">다른 언어</translation> -<translation id="1285320974508926690">이 사이트 번역 안함</translation> -<translation id="290376772003165898">페이지 언어가 <ph name="LANGUAGE" />가 아닌가요?</translation> -<translation id="5684874026226664614">죄송합니다. 이 페이지를 번역할 수 없습니다.</translation> -<translation id="6040143037577758943">닫기</translation> -<translation id="6831043979455480757">번역</translation> -<translation id="7243308994586599757">화면 하단에서 옵션 선택 가능</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" />로 된 페이지를 항상 번역</translation> <translation id="8298278839890148234">웹브라우저 활동</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ky.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ky.xtb index 717031affb9..2d0f4b479b2 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_ky.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ky.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="ky"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> тилиндеги барактар эч качан которулбасын</translation> -<translation id="124116460088058876">Дагы тилдер</translation> -<translation id="1285320974508926690">Бул сайт эч качан которулбасын</translation> -<translation id="290376772003165898">Барак <ph name="LANGUAGE" /> тилинде эмес бекен?</translation> -<translation id="5684874026226664614">Ой, бул бет которулган жок.</translation> -<translation id="6040143037577758943">Жабуу</translation> -<translation id="6831043979455480757">Которуу</translation> -<translation id="7243308994586599757">Параметрлер экрандын түбүндө берилген</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> тилиндеги барактар дайыма которулсун</translation> <translation id="8298278839890148234">Көрүлгөн вебсайттар</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_lo.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_lo.xtb index 7ab378b6803..fba8f18695e 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_lo.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_lo.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="lo"> -<translation id="1068672505746868501">ຢ່າແປໜ້າຕ່າງໆໃນ <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">ພາສາເພີ່ມເຕີມ</translation> -<translation id="1285320974508926690">ຢ່າແປເວັບໄຊທ໌ນີ້</translation> -<translation id="290376772003165898">ໜ້າບໍ່ຢູ່ໃນ <ph name="LANGUAGE" /> ບໍ?</translation> -<translation id="5684874026226664614">ອຸ້ຍ. ບໍ່ສາມາດແປໜ້ານີ້ໄດ້.</translation> -<translation id="6040143037577758943">ປິດ</translation> -<translation id="6831043979455480757">ແປພາສາ</translation> -<translation id="7243308994586599757">ທາງເລືອກມີໃຫ້ໃກ້ປຸ່ມຂອງໜ້າຈໍ</translation> -<translation id="773466115871691567">ແປໜ້າຕ່າງໆສະເໝີໃນ <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">ການເຄື່ອນໄຫວໃນໂປຣແກຣມທ່ອງເວັບ</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_lt.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_lt.xtb index 05d3d0706ff..a1a90e6ff7c 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_lt.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_lt.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="lt"> -<translation id="1068672505746868501">Niekada neversti puslapių, parašytų <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Daugiau kalbų</translation> -<translation id="1285320974508926690">Niekada neversti šios svetainės</translation> -<translation id="290376772003165898">Puslapis parašytas ne <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Oi, nepavyko išversti šio puslapio.</translation> -<translation id="6040143037577758943">Uždaryti</translation> -<translation id="6831043979455480757">Vertėjas</translation> -<translation id="7243308994586599757">Parinktys pasiekiamos netoli ekrano apačios</translation> -<translation id="773466115871691567">Visada versti puslapius, parašytus <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Žiniatinklio naršyklės veikla</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_lv.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_lv.xtb index d53de48768c..5d965478866 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_lv.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_lv.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="lv"> -<translation id="1068672505746868501">Nekad netulkot lapas šādā valodā: <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Citas valodas…</translation> -<translation id="1285320974508926690">Nekad netulkot šo vietni</translation> -<translation id="290376772003165898">Vai lapa nav šajā valodā: <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Hmm... Šo lapu nevarēja iztulkot.</translation> -<translation id="6040143037577758943">Aizvērt</translation> -<translation id="6831043979455480757">Tulkot</translation> -<translation id="7243308994586599757">Opcijas, kas pieejamas ekrāna apakšējā daļā</translation> -<translation id="773466115871691567">Vienmēr tulkot lapas šādā valodā: <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Pārlūkošanas darbības</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_mk.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_mk.xtb index 82359fc6539..2f2d3c2b91b 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_mk.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_mk.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="mk"> -<translation id="1068672505746868501">Никогаш не преведувај страници на <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Повеќе јазици</translation> -<translation id="1285320974508926690">Никогаш не преведувај ја оваа локација</translation> -<translation id="290376772003165898">Страницата не е на <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Оваа страница не може да се преведе.</translation> -<translation id="6040143037577758943">Затвори</translation> -<translation id="6831043979455480757">Преведи</translation> -<translation id="7243308994586599757">Достапни се опции на дното на екранот</translation> -<translation id="773466115871691567">Секогаш преведувај ги страниците на <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Активност на прелистувачот</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ml.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ml.xtb index 8d51f41f713..d4a01802579 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_ml.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ml.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="ml"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> ഭാഷയിലുള്ള പേജുകൾ ഒരിക്കലും വിവർത്തനം ചെയ്യരുത്</translation> -<translation id="124116460088058876">കൂടുതൽ ഭാഷകൾ</translation> -<translation id="1285320974508926690">ഈ സൈറ്റ് ഒരിക്കലും വിവര്ത്തനം ചെയ്യരുത്</translation> -<translation id="290376772003165898">പേജ് <ph name="LANGUAGE" /> ഭാഷയിലല്ലേ?</translation> -<translation id="5684874026226664614">ക്ഷമിക്കണം. ഈ പേജ് വിവർത്തനം ചെയ്യാനായില്ല.</translation> -<translation id="6040143037577758943">അടയ്ക്കുക</translation> -<translation id="6831043979455480757">വിവർത്തനം ചെയ്യുക</translation> -<translation id="7243308994586599757">സ്ക്രീനിന്റെ ചുവടെ ഓപ്ഷനുകൾ ലഭ്യമാണ്</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> ഭാഷയിലുള്ള പേജുകൾ എപ്പോഴും വിവർത്തനം ചെയ്യുക</translation> <translation id="8298278839890148234">വെബ് ബ്രൗസർ ആക്റ്റിവിറ്റി</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_mn.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_mn.xtb index 2f721cf0b56..79d5dac7607 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_mn.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_mn.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="mn"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> хэл дээрх хуудсыг хэзээ ч орчуулахгүй</translation> -<translation id="124116460088058876">Бусад хэл</translation> -<translation id="1285320974508926690">Энэ сайтыг хэзээ ч бүү хөрвүүл</translation> -<translation id="290376772003165898">Хуудас <ph name="LANGUAGE" /> хэл дээр биш байна уу?</translation> -<translation id="5684874026226664614">Өө. Энэ хуудсыг хөрвүүлж чадсангүй.</translation> -<translation id="6040143037577758943">Хаах</translation> -<translation id="6831043979455480757">Хөрвүүлэх</translation> -<translation id="7243308994586599757">Дэлгэцийн доод хэсэгт сонголт боломжтой</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> хэл дээрх хуудсыг байнга орчуулна</translation> <translation id="8298278839890148234">Хөтчийн үйл ажиллагаа</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_mr.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_mr.xtb index 66b36082e3d..5a3e6820752 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_mr.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_mr.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="mr"> -<translation id="1068672505746868501">पेज <ph name="SOURCE_LANGUAGE" /> मध्ये कधीही भाषांतरित करू नका</translation> -<translation id="124116460088058876">आणखी भाषा...</translation> -<translation id="1285320974508926690">या साइटचा कधीही भाषांतर करु नका</translation> -<translation id="290376772003165898">पेज <ph name="LANGUAGE" />मध्ये नाही?</translation> -<translation id="5684874026226664614">अरेरे. हे पृष्ठ भाषांतरित केले जाऊ शकले नाही.</translation> -<translation id="6040143037577758943">बंद करा</translation> -<translation id="6831043979455480757">भाषांतर करा</translation> -<translation id="7243308994586599757">स्क्रीनच्या तळाशी पर्याय उपलब्ध आहेत</translation> -<translation id="773466115871691567">नेहमी पेज <ph name="SOURCE_LANGUAGE" />मध्ये भाषांतरित करा</translation> <translation id="8298278839890148234">वेब ब्राउझर अॅक्टिव्हिटी</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ms.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ms.xtb index 9bca4a8918a..e105952bce6 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_ms.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ms.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="ms"> -<translation id="1068672505746868501">Jangan sekali-kali terjemahkan halaman dalam <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Lagi bahasa</translation> -<translation id="1285320974508926690">Jangan sekali-kali menterjemahkan tapak ini</translation> -<translation id="290376772003165898">Halaman bukan dalam <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Op. Halaman ini tidak dapat diterjemahkan.</translation> -<translation id="6040143037577758943">Tutup</translation> -<translation id="6831043979455480757">Terjemah</translation> -<translation id="7243308994586599757">Pilihan tersedia berhampiran bahagian bawah skrin</translation> -<translation id="773466115871691567">Sentiasa terjemahkan halaman dalam <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Aktiviti penyemakan imbas</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_my.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_my.xtb index ed292bfc27e..e8bf710e16c 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_my.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_my.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="my"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> ဘာသာ စာမျက်နှာများကို ဘယ်သောအခါမျှ ဘာသာမပြန်ရန်</translation> -<translation id="124116460088058876">နောက်ထပ် ဘာသာစကားများ</translation> -<translation id="1285320974508926690">ဒီဆိုက်ကို ဘယ်တော့မှ ဘာသာမပြန်ပါနှင့်</translation> -<translation id="290376772003165898">စာမျက်နှာသည် <ph name="LANGUAGE" /> ဖြင့် ဟုတ်ပါသလား။</translation> -<translation id="5684874026226664614">အူးပ်စ်။ ဒီစာမျက်နှာကို ဘာသာပြန် မရနိုင်ခဲ့ပါ။</translation> -<translation id="6040143037577758943">ပိတ်ရန်</translation> -<translation id="6831043979455480757">ဘာသာပြန်ရန်</translation> -<translation id="7243308994586599757">ရွေးစရာများမှာ မျက်နှာပြင်၏ အောက်ခြေပိုင်းနားမှာ ရှိကြသည်</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> ဘာသာဖြင့် စာမျက်နှာများအားလုံးကို အမြဲဘာသာပြန်ရန်</translation> <translation id="8298278839890148234">ဝဘ်ဘရောင်ဇာ လုပ်ဆောင်ချက်</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ne.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ne.xtb index 11e137141d1..06ec47e44f4 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_ne.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ne.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="ne"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> मा भएका पृष्ठहरूलाई कहिल्यै अनुवाद नगर्नुहोस्</translation> -<translation id="124116460088058876">थप भाषाहरू</translation> -<translation id="1285320974508926690">यो साइट कहिले पनि अनुवाद नगर्नुहोस्</translation> -<translation id="290376772003165898"><ph name="LANGUAGE" /> भाषामा पृष्ठ छैन?</translation> -<translation id="5684874026226664614">ओहो। यो पृष्ठ अनुवादन गर्न सकिएन।</translation> -<translation id="6040143037577758943">बन्द गर्नुहोस्</translation> -<translation id="6831043979455480757">अनुवाद गर्नुहोस्</translation> -<translation id="7243308994586599757">विकल्पहरू स्क्रिनको तल नजिकै उपलब्ध छ</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> मा रहेका पृष्ठहरूलाई सधैँ अनुवाद गर्नुहोस्</translation> <translation id="8298278839890148234">ब्राउजर प्रयोग गरी गरिएको क्रियाकलाप</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_nl.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_nl.xtb index cd20f14e5ca..759b04e50cc 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_nl.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_nl.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="nl"> -<translation id="1068672505746868501">Pagina's in het <ph name="SOURCE_LANGUAGE" /> nooit vertalen</translation> -<translation id="124116460088058876">Meer talen</translation> -<translation id="1285320974508926690">Deze site nooit vertalen</translation> -<translation id="290376772003165898">Is deze pagina niet in het <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Deze pagina kan niet worden vertaald.</translation> -<translation id="6040143037577758943">Sluiten</translation> -<translation id="6831043979455480757">Vertalen</translation> -<translation id="7243308994586599757">Opties beschikbaar onder aan het scherm</translation> -<translation id="773466115871691567">Pagina's in het <ph name="SOURCE_LANGUAGE" /> altijd vertalen</translation> <translation id="8298278839890148234">Webbrowseractivititeit</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_no.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_no.xtb index 0a02095d40b..409a800ee70 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_no.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_no.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="no"> -<translation id="1068672505746868501">Oversett aldri sider på <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Flere språk</translation> -<translation id="1285320974508926690">Oversett aldri dette nettstedet</translation> -<translation id="290376772003165898">Er ikke siden på <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Beklager. Denne siden kunne ikke oversettes.</translation> -<translation id="6040143037577758943">Lukk</translation> -<translation id="6831043979455480757">Oversett</translation> -<translation id="7243308994586599757">Du finner alternativer ved bunnen av skjermen</translation> -<translation id="773466115871691567">Oversett alltid sider på <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Nettleseraktivitet</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_or.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_or.xtb index 8d046c770c1..f88f14b33c3 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_or.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_or.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="or"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" />ରେ ପୃଷ୍ଠାଗୁଡ଼ିକୁ ଅନୁବାଦ କରନ୍ତୁ ନାହିଁ</translation> -<translation id="124116460088058876">ଅନେକ ଭାଷା</translation> -<translation id="1285320974508926690">ଏହି ସାଇଟ୍କୁ କଦାପି ଅନୁବାଦ କରନ୍ତୁ ନାହିଁ</translation> -<translation id="290376772003165898">ପୃଷ୍ଠାଟି <ph name="LANGUAGE" />ରେ ନାହିଁ?</translation> -<translation id="5684874026226664614">ଓହୋଃ! ଏହି ପୃଷ୍ଠା ଅନୁବାଦ କରାଯାଇପାରିଲା ନାହିଁ।</translation> -<translation id="6040143037577758943">ବନ୍ଦ</translation> -<translation id="6831043979455480757">ଅନୁବାଦ କରନ୍ତୁ</translation> -<translation id="7243308994586599757">ସ୍କ୍ରିନ୍ର ନିମ୍ନରେ ବିକଳ୍ପଗୁଡ଼ିକ ଉପଲବ୍ଧ ଅଛି</translation> -<translation id="773466115871691567">ସର୍ବଦା <ph name="SOURCE_LANGUAGE" />ରେ ପୃଷ୍ଠାଗୁଡ଼ିକୁ ଅନୁବାଦ କରନ୍ତୁ</translation> <translation id="8298278839890148234">ୱେବ୍ ବ୍ରାଉଜର୍ କାର୍ଯ୍ୟକଳାପ</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_pa.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_pa.xtb index 6394a67f909..0d4c8c78fba 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_pa.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_pa.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="pa"> -<translation id="1068672505746868501">ਕਦੇ ਵੀ ਪੰਨਿਆਂ ਦਾ ਅਨੁਵਾਦ <ph name="SOURCE_LANGUAGE" /> ਵਿੱਚ ਨਾ ਕਰੋ</translation> -<translation id="124116460088058876">ਹੋਰ ਭਾਸ਼ਾਵਾਂ</translation> -<translation id="1285320974508926690">ਕਦੇ ਵੀ ਇਸ ਸਾਈਟ ਦਾ ਅਨੁਵਾਦ ਨਾ ਕਰੋ</translation> -<translation id="290376772003165898">ਕੀ ਪੰਨਾ <ph name="LANGUAGE" /> ਵਿੱਚ ਨਹੀਂ ਹੈ?</translation> -<translation id="5684874026226664614">ਓਹੋ। ਇਸ ਪੰਨੇ ਦਾ ਅਨੁਵਾਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।</translation> -<translation id="6040143037577758943">ਬੰਦ ਕਰੋ</translation> -<translation id="6831043979455480757">ਅਨੁਵਾਦ ਕਰੋ</translation> -<translation id="7243308994586599757">ਵਿਕਲਪ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਲੇ ਪਾਸੇ ਦੇ ਕੋਲ ਉਪਲਬਧ ਹਨ</translation> -<translation id="773466115871691567">ਪੰਨਿਆਂ ਦਾ ਅਨੁਵਾਦ ਹਮੇਸ਼ਾਂ <ph name="SOURCE_LANGUAGE" /> ਭਾਸ਼ਾ ਵਿੱਚ ਕਰੋ</translation> <translation id="8298278839890148234">ਵੈੱਬ ਬ੍ਰਾਊਜ਼ਰ ਸਰਗਰਮੀ</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_pl.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_pl.xtb index 2185aeb5d3d..52a394f3e02 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_pl.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_pl.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="pl"> -<translation id="1068672505746868501">Nigdy nie tłumacz stron, których językiem jest <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Więcej języków</translation> -<translation id="1285320974508926690">Nigdy nie tłumacz tej witryny</translation> -<translation id="290376772003165898">Język tej strony to nie <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Nie można przetłumaczyć tej strony.</translation> -<translation id="6040143037577758943">Zamknij</translation> -<translation id="6831043979455480757">Tłumacz</translation> -<translation id="7243308994586599757">Opcje dostępne u dołu ekranu</translation> -<translation id="773466115871691567">Zawsze tłumacz strony, których językiem jest <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Aktywność w przeglądarce</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_pt-BR.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_pt-BR.xtb index 45880dbbefa..dd6972f79e3 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_pt-BR.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_pt-BR.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="pt-BR"> -<translation id="1068672505746868501">Nunca traduzir páginas em <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Mais idiomas</translation> -<translation id="1285320974508926690">Nunca traduzir este site</translation> -<translation id="290376772003165898">A página não está em <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Não foi possível traduzir esta página.</translation> -<translation id="6040143037577758943">Fechar</translation> -<translation id="6831043979455480757">Traduzir</translation> -<translation id="7243308994586599757">Opções disponíveis perto da parte inferior da tela</translation> -<translation id="773466115871691567">Sempre traduzir páginas em <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Atividade do navegador da Web</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_pt-PT.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_pt-PT.xtb index 0b06168100d..76966566f3d 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_pt-PT.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_pt-PT.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="pt-PT"> -<translation id="1068672505746868501">Nunca traduzir páginas em <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Mais idiomas</translation> -<translation id="1285320974508926690">Nunca traduzir este site</translation> -<translation id="290376772003165898">A página não está em <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Ups! Não foi possível traduzir esta página.</translation> -<translation id="6040143037577758943">Fechar</translation> -<translation id="6831043979455480757">Traduzir</translation> -<translation id="7243308994586599757">Opções disponíveis junto à parte inferior do ecrã</translation> -<translation id="773466115871691567">Traduza sempre páginas em <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Atividade do navegador de Internet</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ro.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ro.xtb index 87a6bce7c07..12f45d28945 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_ro.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ro.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="ro"> -<translation id="1068672505746868501">Nu traduce niciodată paginile în <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Mai multe limbi</translation> -<translation id="1285320974508926690">Nu traduce niciodată acest site</translation> -<translation id="290376772003165898">Pagina nu este în <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Hopa. Această pagină nu a putut fi tradusă.</translation> -<translation id="6040143037577758943">Închide</translation> -<translation id="6831043979455480757">Tradu</translation> -<translation id="7243308994586599757">Opțiuni disponibile în partea de jos a ecranului</translation> -<translation id="773466115871691567">Tradu întotdeauna paginile în <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Activitatea în browserul web</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ru.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ru.xtb index 733f199d730..401647df65a 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_ru.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ru.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="ru"> -<translation id="1068672505746868501">Не переводить страницы на этом языке: <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Другие языки</translation> -<translation id="1285320974508926690">Никогда не переводить этот сайт</translation> -<translation id="290376772003165898">Язык страницы не <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Не удалось перевести страницу</translation> -<translation id="6040143037577758943">Закрыть</translation> -<translation id="6831043979455480757">Перевести</translation> -<translation id="7243308994586599757">Доступные параметры указаны в нижней части экрана</translation> -<translation id="773466115871691567">Переводить страницы на этом языке: <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Действия в веб-браузере</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_si.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_si.xtb index 90b6f032c71..1e95ef55536 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_si.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_si.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="si"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> හි පිටු කිසිදාක පරිවර්තනය කරන්න එපා</translation> -<translation id="124116460088058876">තවත් භාෂා</translation> -<translation id="1285320974508926690">මෙම අඩවිය කිසිවිට පරිවර්තනය නොකරන්න</translation> -<translation id="290376772003165898">පිටුව <ph name="LANGUAGE" /> බසින් නොවේ ද?</translation> -<translation id="5684874026226664614">අපොයි. මෙම පිටුව පැටවිය නොහැකි විය.</translation> -<translation id="6040143037577758943">වසන්න</translation> -<translation id="6831043979455480757">පරිවර්තනය කරන්න</translation> -<translation id="7243308994586599757">තිරයේ පහළට ආසන්නව විකල්ප ලබා ගත හැකිය</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> හි පිටු සැමවිටම පරිවර්තනය කරන්න</translation> <translation id="8298278839890148234">වෙබ් බ්රවුසර ක්රියාකාරකම</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sk.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sk.xtb index fe4bb0c1612..53b2d4ec263 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_sk.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sk.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="sk"> -<translation id="1068672505746868501">Nikdy neprekladať stránky v jazyku <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Ďalšie jazyky</translation> -<translation id="1285320974508926690">Nikdy neprekladať tieto webové stránky</translation> -<translation id="290376772003165898">Stránka nie je v jazyku <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Hops. Túto stránku nebolo možné preložiť.</translation> -<translation id="6040143037577758943">Zavrieť</translation> -<translation id="6831043979455480757">Preložiť</translation> -<translation id="7243308994586599757">Možnosti sú k dispozícii v dolnej časti obrazovky</translation> -<translation id="773466115871691567">Vždy prekladať stránky v jazyku <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Aktivita webového prehliadača</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sl.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sl.xtb index bc91c54ae43..152968ea560 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_sl.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sl.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="sl"> -<translation id="1068672505746868501">Nikoli ne prevedi strani v jeziku <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Več jezikov</translation> -<translation id="1285320974508926690">Nikoli ne prevedi tega spletnega mesta</translation> -<translation id="290376772003165898">Stran ni v jeziku <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Ojoj, te strani bi bilo mogoče prevesti.</translation> -<translation id="6040143037577758943">Zapri</translation> -<translation id="6831043979455480757">Prevedi</translation> -<translation id="7243308994586599757">Možnosti so na voljo pri dnu zaslona</translation> -<translation id="773466115871691567">Vedno prevedi strani v jeziku <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Dejavnost brskalnika</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sq.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sq.xtb index c88ab6c0cfc..2e0a6950797 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_sq.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sq.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="sq"> -<translation id="1068672505746868501">Mos përkthe asnjëherë faqet që janë në <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Gjuhë të tjera</translation> -<translation id="1285320974508926690">Asnjëherë mos e përkthe këtë sajt</translation> -<translation id="290376772003165898">Faqja nuk është në <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Mos! Kjo faqe nuk mund të përkthehej.</translation> -<translation id="6040143037577758943">Mbyll</translation> -<translation id="6831043979455480757">Përkthe</translation> -<translation id="7243308994586599757">Opsionet janë të disponueshme pranë fundit të ekranit</translation> -<translation id="773466115871691567">Përkthe gjithmonë faqet që janë në <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Aktiviteti i shfletuesit të uebit</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sr-Latn.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sr-Latn.xtb index d66789dacf8..24f260082a7 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_sr-Latn.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sr-Latn.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="sr-Latn"> -<translation id="1068672505746868501">Nikad ne prevodi stranice na jeziku <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Još jezika</translation> -<translation id="1285320974508926690">Nikad ne prevodi ovaj sajt</translation> -<translation id="290376772003165898">Ova stranica nije na jeziku <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Ups, prevođenje ove stranice nije uspelo.</translation> -<translation id="6040143037577758943">Zatvori</translation> -<translation id="6831043979455480757">Prevedi</translation> -<translation id="7243308994586599757">Opcije su dostupne u dnu ekrana</translation> -<translation id="773466115871691567">Uvek predvodi stranice na jeziku <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Aktivnosti u veb-pregledaču</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sr.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sr.xtb index d5eb9aa0fef..9008032cbef 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_sr.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sr.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="sr"> -<translation id="1068672505746868501">Никад не преводи странице на језику <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Још језика</translation> -<translation id="1285320974508926690">Никад не преводи овај сајт</translation> -<translation id="290376772003165898">Ова страница није на језику <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Упс, превођење ове странице није успело.</translation> -<translation id="6040143037577758943">Затвори</translation> -<translation id="6831043979455480757">Преведи</translation> -<translation id="7243308994586599757">Опције су доступне у дну екрана</translation> -<translation id="773466115871691567">Увек предводи странице на језику <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Активности у веб-прегледачу</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sv.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sv.xtb index 532dc6eb359..610cb47e53f 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_sv.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sv.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="sv"> -<translation id="1068672505746868501">Översätt aldrig sidor på <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Fler språk</translation> -<translation id="1285320974508926690">Översätt aldrig den här webbplatsen</translation> -<translation id="290376772003165898">Är sidan inte på <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Det gick inte att översätta sidan.</translation> -<translation id="6040143037577758943">Stäng</translation> -<translation id="6831043979455480757">Översätt</translation> -<translation id="7243308994586599757">Alternativ visas nära skärmens nedre kant</translation> -<translation id="773466115871691567">Översätt alltid sidor på <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Webbläsaraktivitet</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_sw.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_sw.xtb index 2854041492f..cf0a4b711da 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_sw.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_sw.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="sw"> -<translation id="1068672505746868501">Usiwahi kutafsiri kurasa katika lugha ya <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Lugha zaidi</translation> -<translation id="1285320974508926690">Kamwe usitafsiri tovuti hii</translation> -<translation id="290376772003165898">Je, ukurasa huu haupo katika <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Lo! Ukurasa huu haukuweza kutafsiriwa.</translation> -<translation id="6040143037577758943">Funga</translation> -<translation id="6831043979455480757">Tafsiri</translation> -<translation id="7243308994586599757">Chaguo zinapatikana karibu na sehemu ya chini ya skrini</translation> -<translation id="773466115871691567">Zitafsiri kurasa katika <ph name="SOURCE_LANGUAGE" /> wakati wote</translation> <translation id="8298278839890148234">Shughuli za kivinjari</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ta.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ta.xtb index c7f7130e4c5..8a47545a26a 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_ta.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ta.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="ta"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> மொழியில் உள்ள பக்கங்களை ஒருபோதும் மொழிபெயர்க்காதே</translation> -<translation id="124116460088058876">மேலும் மொழிகள்</translation> -<translation id="1285320974508926690">இந்த தளத்தை எப்போதும் மொழிபெயர்க்க வேண்டாம்</translation> -<translation id="290376772003165898"><ph name="LANGUAGE" /> மொழியில் பக்கம் இல்லையா?</translation> -<translation id="5684874026226664614">அச்சச்சோ. இந்தப் பக்கத்தை மொழிபெயர்க்க முடியாது.</translation> -<translation id="6040143037577758943">மூடு</translation> -<translation id="6831043979455480757">மொழிபெயர்</translation> -<translation id="7243308994586599757">திரையின் கீழ்ப்பகுதிக்கு அருகில் கிடைக்கும் விருப்பங்கள்</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> மொழியில் உள்ள பக்கங்களை எப்போதும் மொழிபெயர்</translation> <translation id="8298278839890148234">இணைய உலாவியின் செயல்பாடு</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_te.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_te.xtb index 53279940602..3894c532d87 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_te.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_te.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="te"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" />లో ఉన్న పేజీలను ఎప్పుడూ అనువదించవద్దు</translation> -<translation id="124116460088058876">మరిన్ని భాషలు</translation> -<translation id="1285320974508926690">ఈ సైట్ను ఎప్పటికీ అనువదించవద్దు</translation> -<translation id="290376772003165898">పేజీ <ph name="LANGUAGE" />లో లేదా?</translation> -<translation id="5684874026226664614">అయ్యో. ఈ పేజీని అనువదించడం సాధ్యపడలేదు.</translation> -<translation id="6040143037577758943">మూసివేయి</translation> -<translation id="6831043979455480757">అనువదించు</translation> -<translation id="7243308994586599757">స్క్రీన్ దిగువభాగం సమీపంలో ఎంపికలు అందుబాటులో ఉంటాయి</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" />లో ఉన్న పేజీలను ఎల్లప్పుడూ అనువదించు</translation> <translation id="8298278839890148234">బ్రౌజింగ్ యాక్టివిటీ</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_th.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_th.xtb index 024e4b547a9..1c8e14c9ca0 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_th.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_th.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="th"> -<translation id="1068672505746868501">ไม่ต้องแปลหน้าเว็บภาษา<ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">ภาษาเพิ่มเติม</translation> -<translation id="1285320974508926690">ไม่ต้องแปลเว็บไซต์นี้</translation> -<translation id="290376772003165898">หน้านี้ไม่ใช่ภาษา<ph name="LANGUAGE" />ใช่ไหม</translation> -<translation id="5684874026226664614">อ๊ะ หน้านี้ไม่สามารถแปลได้</translation> -<translation id="6040143037577758943">ปิด</translation> -<translation id="6831043979455480757">แปลภาษา</translation> -<translation id="7243308994586599757">มีตัวเลือกอยู่ทางด้านล่างของหน้าจอ</translation> -<translation id="773466115871691567">แปลหน้าเว็บภาษา<ph name="SOURCE_LANGUAGE" />ทุกครั้ง</translation> <translation id="8298278839890148234">กิจกรรมของเว็บเบราว์เซอร์</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_tr.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_tr.xtb index 2f9a035ea1b..11d4ab4aeef 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_tr.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_tr.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="tr"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> dilindeki sayfaları asla çevirme</translation> -<translation id="124116460088058876">Diğer diller</translation> -<translation id="1285320974508926690">Bu siteyi hiçbir zaman çevirme</translation> -<translation id="290376772003165898">Sayfa <ph name="LANGUAGE" /> dilinde değil mi?</translation> -<translation id="5684874026226664614">Hata! Bu sayfa çevrilemedi.</translation> -<translation id="6040143037577758943">Kapat</translation> -<translation id="6831043979455480757">Çevir</translation> -<translation id="7243308994586599757">Sayfanın altına yakın bir yerde kullanılabilen seçenekler</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> dilindeki sayfaları her zaman çevir</translation> <translation id="8298278839890148234">Web tarayıcısı etkinliği</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_uk.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_uk.xtb index 4a29de8edb5..f8630458317 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_uk.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_uk.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="uk"> -<translation id="1068672505746868501">Ніколи не перекладати сторінки такою мовою: <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Інші мови</translation> -<translation id="1285320974508926690">Ніколи не перекладати цей сайт</translation> -<translation id="290376772003165898">Ця сторінка відображається не такою мовою: <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">На жаль, цю сторінку неможливо перекласти.</translation> -<translation id="6040143037577758943">Закрити</translation> -<translation id="6831043979455480757">Перекласти</translation> -<translation id="7243308994586599757">Опції можна знайти внизу екрана</translation> -<translation id="773466115871691567">Завжди перекладати сторінки такою мовою: <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Дії у веб-переглядачі</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_ur.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_ur.xtb index 4113ad7cdaa..57e46884972 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_ur.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_ur.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="ur"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> کے صفحات کا کبھی ترجمہ نہ کریں</translation> -<translation id="124116460088058876">مزید زبانیں</translation> -<translation id="1285320974508926690">اس سائٹ کا ترجمہ کبھی نہ کریں</translation> -<translation id="290376772003165898">صفحہ <ph name="LANGUAGE" /> میں نہیں ہے؟</translation> -<translation id="5684874026226664614">افوہ۔ اس صفحہ کا ترجمہ نہیں کیا جا سکا۔</translation> -<translation id="6040143037577758943">بند کریں</translation> -<translation id="6831043979455480757">ترجمہ کریں</translation> -<translation id="7243308994586599757">اسکرین کے نچلے حصہ کے قریب اختیارات دستیاب ہیں</translation> -<translation id="773466115871691567">ہمیشہ <ph name="SOURCE_LANGUAGE" /> کے صفحات کا ترجمہ کریں</translation> <translation id="8298278839890148234">ویب براؤزر کی سرگرمی</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_uz.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_uz.xtb index f0af58d90cd..92655c71edf 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_uz.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_uz.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="uz"> -<translation id="1068672505746868501"><ph name="SOURCE_LANGUAGE" /> tilidagi sahifalar hech qachon tarjima qilinmasin</translation> -<translation id="124116460088058876">Boshqa tillar</translation> -<translation id="1285320974508926690">Bu sayt hech qachon tarjima qilinmasin</translation> -<translation id="290376772003165898">Sahifa <ph name="LANGUAGE" /> tilida emasmi?</translation> -<translation id="5684874026226664614">Bu sahifani tarjima qilib bo‘lmadi.</translation> -<translation id="6040143037577758943">Yopish</translation> -<translation id="6831043979455480757">Tarjima</translation> -<translation id="7243308994586599757">Parametrlar ekranning quyi qismiga yaqinroq joyda</translation> -<translation id="773466115871691567"><ph name="SOURCE_LANGUAGE" /> tilidagi sahifalar doim tarjima qilinsin</translation> <translation id="8298278839890148234">Veb-brauzerdagi faoliyat</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_vi.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_vi.xtb index 2744201fd71..2f57bd60947 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_vi.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_vi.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="vi"> -<translation id="1068672505746868501">Không bao giờ dịch các trang viết bằng <ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Ngôn ngữ khác</translation> -<translation id="1285320974508926690">Không bao giờ dịch trang web này</translation> -<translation id="290376772003165898">Trang này không được viết bằng <ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Rất tiếc. Không thể dịch trang này.</translation> -<translation id="6040143037577758943">Đóng</translation> -<translation id="6831043979455480757">Dịch</translation> -<translation id="7243308994586599757">Có các tùy chọn ở gần cuối màn hình</translation> -<translation id="773466115871691567">Luôn dịch các trang viết bằng <ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Hoạt động duyệt web</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_zh-CN.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_zh-CN.xtb index 0c3183bb774..84075ec9807 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_zh-CN.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_zh-CN.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="zh-CN"> -<translation id="1068672505746868501">一律不翻译<ph name="SOURCE_LANGUAGE" />网页</translation> -<translation id="124116460088058876">更多语言</translation> -<translation id="1285320974508926690">一律不翻译此网站</translation> -<translation id="290376772003165898">网页的源语言不是<ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">糟糕,此网页内容无法翻译。</translation> -<translation id="6040143037577758943">关闭</translation> -<translation id="6831043979455480757">翻译</translation> -<translation id="7243308994586599757">选项在靠近屏幕底部的位置</translation> -<translation id="773466115871691567">一律翻译<ph name="SOURCE_LANGUAGE" />网页</translation> <translation id="8298278839890148234">网络浏览器活动</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_zh-HK.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_zh-HK.xtb index 3b6aab868dd..abd42fba040 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_zh-HK.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_zh-HK.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="zh-HK"> -<translation id="1068672505746868501">永不翻譯來源語言為<ph name="SOURCE_LANGUAGE" />的網頁</translation> -<translation id="124116460088058876">更多語言</translation> -<translation id="1285320974508926690">永不翻譯此網站</translation> -<translation id="290376772003165898">網頁的來源語言不是<ph name="LANGUAGE" />嗎?</translation> -<translation id="5684874026226664614">糟糕!系統無法翻譯這個網頁的內容。</translation> -<translation id="6040143037577758943">關閉</translation> -<translation id="6831043979455480757">翻譯</translation> -<translation id="7243308994586599757">您可在畫面底部附近找到選項</translation> -<translation id="773466115871691567">一律翻譯來源語言為<ph name="SOURCE_LANGUAGE" />的網頁</translation> <translation id="8298278839890148234">網絡瀏覽器活動</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_zh-TW.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_zh-TW.xtb index d338d98229a..ecea64ea7f4 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_zh-TW.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_zh-TW.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="zh-TW"> -<translation id="1068672505746868501">一律不翻譯<ph name="SOURCE_LANGUAGE" />網頁</translation> -<translation id="124116460088058876">更多語言</translation> -<translation id="1285320974508926690">一律不翻譯此網站</translation> -<translation id="290376772003165898">不是<ph name="LANGUAGE" />網頁嗎?</translation> -<translation id="5684874026226664614">糟糕!系統無法翻譯這個網頁的內容。</translation> -<translation id="6040143037577758943">關閉</translation> -<translation id="6831043979455480757">翻譯</translation> -<translation id="7243308994586599757">選項在接近畫面底部的位置</translation> -<translation id="773466115871691567">一律翻譯<ph name="SOURCE_LANGUAGE" />網頁</translation> <translation id="8298278839890148234">網路瀏覽器活動</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/translations/weblayer_strings_zu.xtb b/chromium/weblayer/browser/java/translations/weblayer_strings_zu.xtb index 9e01afbe908..1da5d91a768 100644 --- a/chromium/weblayer/browser/java/translations/weblayer_strings_zu.xtb +++ b/chromium/weblayer/browser/java/translations/weblayer_strings_zu.xtb @@ -1,14 +1,5 @@ <?xml version="1.0" ?> <!DOCTYPE translationbundle> <translationbundle lang="zu"> -<translation id="1068672505746868501">Ungahumushi amakhasi ngesi-<ph name="SOURCE_LANGUAGE" /></translation> -<translation id="124116460088058876">Izilimi eziningi</translation> -<translation id="1285320974508926690">Ungalokothi uhumushe leli sayithi</translation> -<translation id="290376772003165898">Ikhasi alikho ngesi-<ph name="LANGUAGE" />?</translation> -<translation id="5684874026226664614">Eshu. Leli khasi alikwazi ukuhunyushwa.</translation> -<translation id="6040143037577758943">Vala</translation> -<translation id="6831043979455480757">Humusha</translation> -<translation id="7243308994586599757">Izinketho ziyatholakala eduze kwangaphansi kwesikrini</translation> -<translation id="773466115871691567">Njalo humusha amakhasi ngesi-<ph name="SOURCE_LANGUAGE" /></translation> <translation id="8298278839890148234">Umsebenzi wesiphequluli sewebhu</translation> </translationbundle>
\ No newline at end of file diff --git a/chromium/weblayer/browser/java/weblayer_strings.grd b/chromium/weblayer/browser/java/weblayer_strings.grd index 79a4dcddd27..cd1e72cc645 100644 --- a/chromium/weblayer/browser/java/weblayer_strings.grd +++ b/chromium/weblayer/browser/java/weblayer_strings.grd @@ -172,34 +172,6 @@ <message name="IDS_WEBLAYER_NOTIFICATION_CHANNEL_GROUP_NAME" desc="The user-facing label for the notification channel group used for notifications arising from web browsing activity."> Web browser activity </message> - <message name="IDS_WEBLAYER_BOTTOM_BAR_SCREEN_POSITION" desc="Accessibility label to inform users about the InfoBar location"> - Options available near bottom of the screen - </message> - <message name="IDS_WEBLAYER_INFOBAR_CLOSE" desc="Accessibility label for the dismiss infobar Button"> - Close - </message> - <!-- TranslateInfoBar --> - <message name="IDS_TRANSLATE_INFOBAR_ERROR"> - Oops. This page could not be translated. - </message> - <message name="IDS_TRANSLATE_BUTTON" desc="Possible texts to display on the translate infobar buttons. [CHAR-LIMIT=24]"> - Translate - </message> - <message name="IDS_TRANSLATE_NEVER_TRANSLATE_SITE" desc="Text to display on the never translate site (like www.google.com) button. [CHAR-LIMIT=64]"> - Never translate this site - </message> - <message name="IDS_TRANSLATE_OPTION_ALWAYS_TRANSLATE" desc="Option in the Chrome menu. User can click the 'Always Translate' option to indicate that they want Chrome to translate pages in this language automatically. Imperative."> - Always translate pages in <ph name="SOURCE_LANGUAGE">%1$s<ex>French</ex></ph> - </message> - <message name="IDS_TRANSLATE_OPTION_NEVER_TRANSLATE" desc="Option in the Chrome menu. User can click the 'Never Translate' option to indicate that they never want Chrome to translate pages in this language. The variable SOURCE_LANGUAGE could be any of 50+ languages supported by Google Translate, like French, Spanish, German, Italian, Japanese, Korean, etc. Imperative."> - Never translate pages in <ph name="SOURCE_LANGUAGE">%1$s<ex>French</ex></ph> - </message> - <message name="IDS_TRANSLATE_OPTION_MORE_LANGUAGE" desc="Option in the Chrome menu. Lets the user open a dialog to choose other target languages for translation, from a list of available languages. [CHAR-LIMIT=64]"> - More languages - </message> - <message name="IDS_TRANSLATE_OPTION_NOT_SOURCE_LANGUAGE" desc="Option in the Chrome menu. Sometimes a web page's source language is not correctly identified by Google Translate, and this menu option lets the user open a submenu to select another language as the source language to translate. Phrased as a question as if to query the user, 'Is this page not in [source language identified]? If so, click here.' [CHAR-LIMIT=64]"> - Page is not in <ph name="LANGUAGE">%1$s<ex>French</ex></ph>? - </message> </messages> </release> </grit> diff --git a/chromium/weblayer/browser/navigation_browsertest.cc b/chromium/weblayer/browser/navigation_browsertest.cc index c573269cc60..283e284874f 100644 --- a/chromium/weblayer/browser/navigation_browsertest.cc +++ b/chromium/weblayer/browser/navigation_browsertest.cc @@ -8,18 +8,22 @@ #include "base/files/file_path.h" #include "base/test/bind_test_util.h" #include "components/variations/net/variations_http_headers.h" -#include "components/variations/variations_http_header_provider.h" +#include "components/variations/variations_ids_provider.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/test/browser_test_utils.h" #include "content/public/test/url_loader_interceptor.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/controllable_http_response.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_response.h" +#include "weblayer/browser/tab_impl.h" +#include "weblayer/public/browser.h" #include "weblayer/public/navigation.h" #include "weblayer/public/navigation_controller.h" #include "weblayer/public/navigation_observer.h" -#include "weblayer/public/tab.h" #include "weblayer/shell/browser/shell.h" #include "weblayer/test/interstitial_utils.h" +#include "weblayer/test/test_navigation_observer.h" #include "weblayer/test/weblayer_browser_test_utils.h" namespace weblayer { @@ -68,8 +72,11 @@ class NavigationObserverImpl : public NavigationObserver { completed_callback_.Run(navigation); } void NavigationFailed(Navigation* navigation) override { - if (failed_callback_) - failed_callback_.Run(navigation); + // As |this| may be deleted when running the callback, the callback must be + // copied before running. To do otherwise results in use-after-free. + auto callback = failed_callback_; + if (callback) + callback.Run(navigation); } private: @@ -80,56 +87,6 @@ class NavigationObserverImpl : public NavigationObserver { Callback failed_callback_; }; -class OneShotNavigationObserver : public NavigationObserver { - public: - explicit OneShotNavigationObserver(Shell* shell) : tab_(shell->tab()) { - tab_->GetNavigationController()->AddObserver(this); - } - - ~OneShotNavigationObserver() override { - tab_->GetNavigationController()->RemoveObserver(this); - } - - void WaitForNavigation() { run_loop_.Run(); } - - bool completed() { return completed_; } - bool is_error_page() { return is_error_page_; } - bool is_download() { return is_download_; } - bool was_stop_called() { return was_stop_called_; } - Navigation::LoadError load_error() { return load_error_; } - int http_status_code() { return http_status_code_; } - NavigationState navigation_state() { return navigation_state_; } - - private: - // NavigationObserver implementation: - void NavigationCompleted(Navigation* navigation) override { - completed_ = true; - Finish(navigation); - } - - void NavigationFailed(Navigation* navigation) override { Finish(navigation); } - - void Finish(Navigation* navigation) { - is_error_page_ = navigation->IsErrorPage(); - is_download_ = navigation->IsDownload(); - was_stop_called_ = navigation->WasStopCalled(); - load_error_ = navigation->GetLoadError(); - http_status_code_ = navigation->GetHttpStatusCode(); - navigation_state_ = navigation->GetState(); - run_loop_.Quit(); - } - - base::RunLoop run_loop_; - Tab* tab_; - bool completed_ = false; - bool is_error_page_ = false; - bool is_download_ = false; - bool was_stop_called_ = false; - Navigation::LoadError load_error_ = Navigation::kNoError; - int http_status_code_ = 0; - NavigationState navigation_state_ = NavigationState::kWaitingResponse; -}; - } // namespace class NavigationBrowserTest : public WebLayerBrowserTest { @@ -150,6 +107,7 @@ IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, NoError) { EXPECT_TRUE(observer.completed()); EXPECT_FALSE(observer.is_error_page()); EXPECT_FALSE(observer.is_download()); + EXPECT_FALSE(observer.is_reload()); EXPECT_FALSE(observer.was_stop_called()); EXPECT_EQ(observer.load_error(), Navigation::kNoError); EXPECT_EQ(observer.http_status_code(), 200); @@ -255,6 +213,25 @@ IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, StopInOnStart) { run_loop.Run(); } +IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, DestroyTabInNavigation) { + ASSERT_TRUE(embedded_test_server()->Start()); + Tab* new_tab = shell()->browser()->CreateTab(); + base::RunLoop run_loop; + std::unique_ptr<NavigationObserverImpl> observer = + std::make_unique<NavigationObserverImpl>( + new_tab->GetNavigationController()); + observer->SetFailedCallback( + base::BindLambdaForTesting([&](Navigation* navigation) { + observer.reset(); + shell()->browser()->DestroyTab(new_tab); + run_loop.Quit(); + })); + new_tab->GetNavigationController()->Navigate( + embedded_test_server()->GetURL("/simple_pageX.html")); + + run_loop.Run(); +} + IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, StopInOnRedirect) { ASSERT_TRUE(embedded_test_server()->Start()); base::RunLoop run_loop; @@ -438,6 +415,22 @@ IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, PageSeesUserAgentString) { run_loop.Run(); } +IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, Reload) { + ASSERT_TRUE(embedded_test_server()->Start()); + + OneShotNavigationObserver observer(shell()); + GetNavigationController()->Navigate( + embedded_test_server()->GetURL("/simple_page.html")); + observer.WaitForNavigation(); + + OneShotNavigationObserver observer2(shell()); + shell()->tab()->ExecuteScript(base::ASCIIToUTF16("location.reload();"), false, + base::DoNothing()); + observer2.WaitForNavigation(); + EXPECT_TRUE(observer2.completed()); + EXPECT_TRUE(observer2.is_reload()); +} + IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SetUserAgentString) { net::test_server::ControllableHttpResponse response_1(embedded_test_server(), "", true); @@ -598,6 +591,63 @@ IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, EXPECT_EQ(custom_ua, new_ua); } +IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, AutoPlayDefault) { + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL url(embedded_test_server()->GetURL("/autoplay.html")); + auto* tab = static_cast<TabImpl*>(shell()->tab()); + NavigateAndWaitForCompletion(url, tab); + + auto* web_contents = tab->web_contents(); + bool playing = false; + // There's no notification to watch that would signal video wasn't autoplayed, + // so instead check once through javascript. + EXPECT_TRUE(content::ExecuteScriptAndExtractBool( + web_contents, + "window.domAutomationController.send(!document.getElementById('vid')." + "paused)", + &playing)); + ASSERT_FALSE(playing); +} + +namespace { + +class WaitForMediaPlaying : public content::WebContentsObserver { + public: + explicit WaitForMediaPlaying(content::WebContents* web_contents) + : WebContentsObserver(web_contents) {} + + // WebContentsObserver override. + void MediaStartedPlaying(const MediaPlayerInfo& info, + const content::MediaPlayerId&) final { + run_loop_.Quit(); + CHECK(info.has_audio); + CHECK(info.has_video); + } + + void Wait() { run_loop_.Run(); } + + private: + base::RunLoop run_loop_; + + DISALLOW_COPY_AND_ASSIGN(WaitForMediaPlaying); +}; + +} // namespace + +IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, AutoPlayEnabled) { + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL url(embedded_test_server()->GetURL("/autoplay.html")); + NavigationController::NavigateParams params; + params.enable_auto_play = true; + GetNavigationController()->Navigate(url, params); + + auto* tab = static_cast<TabImpl*>(shell()->tab()); + WaitForMediaPlaying wait_for_media(tab->web_contents()); + wait_for_media.Wait(); +} + class NavigationBrowserTest2 : public NavigationBrowserTest { public: void SetUp() override { @@ -618,7 +668,7 @@ class NavigationBrowserTest2 : public NavigationBrowserTest { // Forces variations code to set the header. auto* variations_provider = - variations::VariationsHttpHeaderProvider::GetInstance(); + variations::VariationsIdsProvider::GetInstance(); variations_provider->ForceVariationIds({"12", "456", "t789"}, ""); } diff --git a/chromium/weblayer/browser/navigation_controller_impl.cc b/chromium/weblayer/browser/navigation_controller_impl.cc index cabbd2cca24..f14bb94f035 100644 --- a/chromium/weblayer/browser/navigation_controller_impl.cc +++ b/chromium/weblayer/browser/navigation_controller_impl.cc @@ -14,6 +14,7 @@ #include "content/public/browser/navigation_throttle.h" #include "content/public/browser/web_contents.h" #include "ui/base/page_transition_types.h" +#include "weblayer/browser/navigation_ui_data_impl.h" #include "weblayer/browser/tab_impl.h" #include "weblayer/public/navigation_observer.h" @@ -31,6 +32,29 @@ using base::android::ScopedJavaLocalRef; namespace weblayer { +class NavigationControllerImpl::DelayDeletionHelper { + public: + explicit DelayDeletionHelper(NavigationControllerImpl* controller) + : controller_(controller->weak_ptr_factory_.GetWeakPtr()) { + // This should never be called reentrantly. + DCHECK(!controller->should_delay_web_contents_deletion_); + controller->should_delay_web_contents_deletion_ = true; + } + + DelayDeletionHelper(const DelayDeletionHelper&) = delete; + DelayDeletionHelper& operator=(const DelayDeletionHelper&) = delete; + + ~DelayDeletionHelper() { + if (controller_) + controller_->should_delay_web_contents_deletion_ = false; + } + + bool WasControllerDeleted() { return controller_.get() == nullptr; } + + private: + base::WeakPtr<NavigationControllerImpl> controller_; +}; + // NavigationThrottle implementation responsible for delaying certain // operations and performing them when safe. This is necessary as content // does allow certain operations to be called at certain times. For example, @@ -111,6 +135,12 @@ NavigationControllerImpl::CreateNavigationThrottle( return throttle; } +NavigationImpl* NavigationControllerImpl::GetNavigationImplFromHandle( + content::NavigationHandle* handle) { + auto iter = navigation_map_.find(handle); + return iter == navigation_map_.end() ? nullptr : iter->second.get(); +} + #if defined(OS_ANDROID) void NavigationControllerImpl::SetNavigationControllerImpl( JNIEnv* env, @@ -118,18 +148,28 @@ void NavigationControllerImpl::SetNavigationControllerImpl( java_controller_ = java_controller; } -void NavigationControllerImpl::Navigate(JNIEnv* env, - const JavaParamRef<jstring>& url) { - Navigate(GURL(base::android::ConvertJavaStringToUTF8(env, url))); -} - -void NavigationControllerImpl::NavigateWithParams( +void NavigationControllerImpl::Navigate( JNIEnv* env, const JavaParamRef<jstring>& url, - jboolean should_replace_current_entry) { + jboolean should_replace_current_entry, + jboolean disable_intent_processing, + jboolean disable_network_error_auto_reload, + jboolean enable_auto_play) { auto params = std::make_unique<content::NavigationController::LoadURLParams>( GURL(base::android::ConvertJavaStringToUTF8(env, url))); params->should_replace_current_entry = should_replace_current_entry; + // On android, the transition type largely dictates whether intent processing + // happens. PAGE_TRANSITION_TYPED does not process intents, where as + // PAGE_TRANSITION_LINK will (with the caveat that even links may not trigger + // intent processing under some circumstances). + params->transition_type = disable_intent_processing + ? ui::PAGE_TRANSITION_TYPED + : ui::PAGE_TRANSITION_LINK; + if (disable_network_error_auto_reload) + params->navigation_ui_data = std::make_unique<NavigationUIDataImpl>(true); + if (enable_auto_play) + params->was_activated = content::mojom::WasActivatedOption::kYes; + DoNavigate(std::move(params)); } @@ -197,6 +237,13 @@ void NavigationControllerImpl::Navigate( std::make_unique<content::NavigationController::LoadURLParams>(url); load_params->should_replace_current_entry = params.should_replace_current_entry; + if (params.disable_network_error_auto_reload) { + load_params->navigation_ui_data = + std::make_unique<NavigationUIDataImpl>(true); + } + if (params.enable_auto_play) + load_params->was_activated = content::mojom::WasActivatedOption::kYes; + DoNavigate(std::move(load_params)); } @@ -335,6 +382,7 @@ void NavigationControllerImpl::DidFinishNavigation( if (!navigation_handle->IsInMainFrame()) return; + DelayDeletionHelper deletion_helper(this); DCHECK(navigation_map_.find(navigation_handle) != navigation_map_.end()); auto* navigation = navigation_map_[navigation_handle].get(); if (navigation_handle->GetNetErrorCode() == net::OK && @@ -346,10 +394,15 @@ void NavigationControllerImpl::DidFinishNavigation( Java_NavigationControllerImpl_navigationCompleted( AttachCurrentThread(), java_controller_, navigation->java_navigation()); + if (deletion_helper.WasControllerDeleted()) + return; } #endif - for (auto& observer : observers_) + for (auto& observer : observers_) { observer.NavigationCompleted(navigation); + if (deletion_helper.WasControllerDeleted()) + return; + } } else { #if defined(OS_ANDROID) if (java_controller_) { @@ -358,10 +411,15 @@ void NavigationControllerImpl::DidFinishNavigation( Java_NavigationControllerImpl_navigationFailed( AttachCurrentThread(), java_controller_, navigation->java_navigation()); + if (deletion_helper.WasControllerDeleted()) + return; } #endif - for (auto& observer : observers_) + for (auto& observer : observers_) { observer.NavigationFailed(navigation); + if (deletion_helper.WasControllerDeleted()) + return; + } } // Note InsertVisualStateCallback currently does not take into account @@ -459,11 +517,6 @@ void NavigationControllerImpl::DoNavigate( return; } - // For WebLayer's production use cases, navigations from the embedder are most - // appropriately viewed as being from links with user gestures. In particular, - // this ensures that intents resulting from these navigations get launched as - // the embedder expects. - params->transition_type = ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK); params->has_user_gesture = true; web_contents()->GetController().LoadURLWithParams(*params); // So that if the user had entered the UI in a bar it stops flashing the diff --git a/chromium/weblayer/browser/navigation_controller_impl.h b/chromium/weblayer/browser/navigation_controller_impl.h index ce96e147504..f27153add07 100644 --- a/chromium/weblayer/browser/navigation_controller_impl.h +++ b/chromium/weblayer/browser/navigation_controller_impl.h @@ -22,10 +22,12 @@ #endif namespace content { +class NavigationHandle; class NavigationThrottle; } namespace weblayer { +class NavigationImpl; class TabImpl; class NavigationControllerImpl : public NavigationController, @@ -39,15 +41,20 @@ class NavigationControllerImpl : public NavigationController, std::unique_ptr<content::NavigationThrottle> CreateNavigationThrottle( content::NavigationHandle* handle); + // Returns the NavigationImpl for |handle|, or null if there isn't one. + NavigationImpl* GetNavigationImplFromHandle( + content::NavigationHandle* handle); + #if defined(OS_ANDROID) void SetNavigationControllerImpl( JNIEnv* env, const base::android::JavaParamRef<jobject>& java_controller); void Navigate(JNIEnv* env, - const base::android::JavaParamRef<jstring>& url); - void NavigateWithParams(JNIEnv* env, - const base::android::JavaParamRef<jstring>& url, - jboolean should_replace_current_entry); + const base::android::JavaParamRef<jstring>& url, + jboolean should_replace_current_entry, + jboolean disable_intent_processing, + jboolean disable_network_error_auto_reload, + jboolean enable_auto_play); void GoBack(JNIEnv* env) { GoBack(); } void GoForward(JNIEnv* env) { GoForward(); } bool CanGoBack(JNIEnv* env) { return CanGoBack(); } @@ -68,7 +75,13 @@ class NavigationControllerImpl : public NavigationController, bool IsNavigationEntrySkippable(JNIEnv* env, int index); #endif + bool should_delay_web_contents_deletion() { + return should_delay_web_contents_deletion_; + } + private: + class DelayDeletionHelper; + class NavigationThrottleImpl; // Called from NavigationControllerImpl::WillRedirectRequest(). See @@ -129,6 +142,11 @@ class NavigationControllerImpl : public NavigationController, base::android::ScopedJavaGlobalRef<jobject> java_controller_; #endif + // Set to true while processing an observer/callback and it's unsafe to + // delete the WebContents. This is not used for all callbacks, just the + // ones that we need to allow deletion from (such as completed/failed). + bool should_delay_web_contents_deletion_ = false; + base::WeakPtrFactory<NavigationControllerImpl> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(NavigationControllerImpl); diff --git a/chromium/weblayer/browser/navigation_error_navigation_throttle.cc b/chromium/weblayer/browser/navigation_error_navigation_throttle.cc new file mode 100644 index 00000000000..72fcf3c9406 --- /dev/null +++ b/chromium/weblayer/browser/navigation_error_navigation_throttle.cc @@ -0,0 +1,73 @@ +// 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 "weblayer/browser/navigation_error_navigation_throttle.h" + +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/render_frame_host.h" +#include "net/base/net_errors.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" +#include "weblayer/browser/navigation_controller_impl.h" +#include "weblayer/browser/tab_impl.h" +#include "weblayer/common/error_page_helper.mojom.h" +#include "weblayer/public/error_page.h" +#include "weblayer/public/error_page_delegate.h" + +using content::NavigationThrottle; + +namespace weblayer { + +NavigationErrorNavigationThrottle::NavigationErrorNavigationThrottle( + content::NavigationHandle* handle) + : NavigationThrottle(handle) { + // As this calls to the delegate, and the delegate only knows about main + // frames, this should only be used for main frames. + DCHECK(handle->IsInMainFrame()); +} + +NavigationErrorNavigationThrottle::~NavigationErrorNavigationThrottle() = + default; + +NavigationThrottle::ThrottleCheckResult +NavigationErrorNavigationThrottle::WillFailRequest() { + // The embedder is not allowed to replace ssl error pages. + if (navigation_handle()->GetNetErrorCode() == net::Error::OK || + net::IsCertificateError(navigation_handle()->GetNetErrorCode())) { + return NavigationThrottle::PROCEED; + } + + TabImpl* tab = + TabImpl::FromWebContents(navigation_handle()->GetWebContents()); + // Instances of this class are only created if there is a Tab associated + // with the WebContents. + DCHECK(tab); + if (!tab->error_page_delegate()) + return NavigationThrottle::PROCEED; + + NavigationImpl* navigation = + static_cast<NavigationControllerImpl*>(tab->GetNavigationController()) + ->GetNavigationImplFromHandle(navigation_handle()); + // The navigation this was created for should always outlive this. + DCHECK(navigation); + auto error_page = tab->error_page_delegate()->GetErrorPageContent(navigation); + if (!error_page) + return NavigationThrottle::PROCEED; + + mojo::AssociatedRemote<mojom::ErrorPageHelper> remote_error_page_helper; + navigation_handle() + ->GetRenderFrameHost() + ->GetRemoteAssociatedInterfaces() + ->GetInterface(&remote_error_page_helper); + remote_error_page_helper->DisableErrorPageHelperForNextError(); + + return NavigationThrottle::ThrottleCheckResult( + NavigationThrottle::BLOCK_REQUEST, navigation_handle()->GetNetErrorCode(), + error_page->html); +} + +const char* NavigationErrorNavigationThrottle::GetNameForLogging() { + return "NavigationErrorNavigationThrottle"; +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/navigation_error_navigation_throttle.h b/chromium/weblayer/browser/navigation_error_navigation_throttle.h new file mode 100644 index 00000000000..0d70275234d --- /dev/null +++ b/chromium/weblayer/browser/navigation_error_navigation_throttle.h @@ -0,0 +1,32 @@ +// 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 WEBLAYER_BROWSER_NAVIGATION_ERROR_NAVIGATION_THROTTLE_H_ +#define WEBLAYER_BROWSER_NAVIGATION_ERROR_NAVIGATION_THROTTLE_H_ + +#include <memory> + +#include "content/public/browser/navigation_throttle.h" + +namespace weblayer { + +// NavigationThrottle implementation that allows the embedder to inject an +// error page for non-ssl errors. +class NavigationErrorNavigationThrottle : public content::NavigationThrottle { + public: + explicit NavigationErrorNavigationThrottle(content::NavigationHandle* handle); + NavigationErrorNavigationThrottle(const NavigationErrorNavigationThrottle&) = + delete; + NavigationErrorNavigationThrottle& operator=( + const NavigationErrorNavigationThrottle&) = delete; + ~NavigationErrorNavigationThrottle() override; + + // content::NavigationThrottle: + ThrottleCheckResult WillFailRequest() override; + const char* GetNameForLogging() override; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_NAVIGATION_ERROR_NAVIGATION_THROTTLE_H_ diff --git a/chromium/weblayer/browser/navigation_error_navigation_throttle_browsertest.cc b/chromium/weblayer/browser/navigation_error_navigation_throttle_browsertest.cc new file mode 100644 index 00000000000..3c3175f6904 --- /dev/null +++ b/chromium/weblayer/browser/navigation_error_navigation_throttle_browsertest.cc @@ -0,0 +1,94 @@ +// 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 "weblayer/test/weblayer_browser_test.h" + +#include "base/optional.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "build/build_config.h" +#include "content/public/test/url_loader_interceptor.h" +#include "weblayer/browser/tab_impl.h" +#include "weblayer/public/error_page.h" +#include "weblayer/public/error_page_delegate.h" +#include "weblayer/shell/browser/shell.h" +#include "weblayer/test/test_navigation_observer.h" +#include "weblayer/test/weblayer_browser_test_utils.h" + +#include "base/run_loop.h" + +namespace weblayer { + +namespace { + +class TestErrorPageDelegate : public ErrorPageDelegate { + public: + void set_error_page_content(const std::string& value) { content_ = value; } + // ErrorPageDelegate: + bool OnBackToSafety() override { return false; } + std::unique_ptr<ErrorPage> GetErrorPageContent( + Navigation* navigation) override { + if (!content_.has_value()) + return nullptr; + auto error_page = std::make_unique<ErrorPage>(); + error_page->html = *content_; + return error_page; + } + + private: + base::Optional<std::string> content_; +}; + +} // namespace + +using NavigationErrorNavigationThrottleBrowserTest = WebLayerBrowserTest; + +// Verifies the delegate can inject an error page. +IN_PROC_BROWSER_TEST_F(NavigationErrorNavigationThrottleBrowserTest, + InjectErrorPage) { + GURL url("http://doesntexist.com/foo"); + auto interceptor = content::URLLoaderInterceptor::SetupRequestFailForURL( + url, net::ERR_NAME_NOT_RESOLVED); + TestErrorPageDelegate delegate; + delegate.set_error_page_content("<html><head><title>test error</title>"); + shell()->tab()->SetErrorPageDelegate(&delegate); + NavigateAndWaitForFailure(url, shell()); + EXPECT_EQ(base::ASCIIToUTF16("test error"), GetTitle(shell())); +} + +// Verifies the delegate can inject an empty page. +IN_PROC_BROWSER_TEST_F(NavigationErrorNavigationThrottleBrowserTest, + InjectEmptyErrorPage) { + GURL url("http://doesntexist.com/foo"); + auto interceptor = content::URLLoaderInterceptor::SetupRequestFailForURL( + url, net::ERR_NAME_NOT_RESOLVED); + TestErrorPageDelegate delegate; + delegate.set_error_page_content(std::string()); + shell()->tab()->SetErrorPageDelegate(&delegate); + NavigateAndWaitForFailure(url, shell()); + base::Value body_text = + ExecuteScript(shell()->tab(), "document.body.textContent", false); + ASSERT_TRUE(body_text.is_string()); + EXPECT_TRUE(body_text.GetString().empty()); +} + +// Verifies a null return value results in a default error page. +// Network errors only have non-empty error pages on android. +#if defined(OS_ANDROID) +IN_PROC_BROWSER_TEST_F(NavigationErrorNavigationThrottleBrowserTest, + DefaultErrorPage) { + GURL url("http://doesntexist.com/foo"); + auto interceptor = content::URLLoaderInterceptor::SetupRequestFailForURL( + url, net::ERR_NAME_NOT_RESOLVED); + TestErrorPageDelegate delegate; + shell()->tab()->SetErrorPageDelegate(&delegate); + NavigateAndWaitForFailure(url, shell()); + base::Value body_text = + ExecuteScript(shell()->tab(), "document.body.textContent", false); + ASSERT_TRUE(body_text.is_string()); + EXPECT_FALSE(body_text.GetString().empty()); +} +#endif + +} // namespace weblayer diff --git a/chromium/weblayer/browser/navigation_impl.cc b/chromium/weblayer/browser/navigation_impl.cc index e160069f225..a4cb6076eb0 100644 --- a/chromium/weblayer/browser/navigation_impl.cc +++ b/chromium/weblayer/browser/navigation_impl.cc @@ -9,7 +9,7 @@ #include "net/base/net_errors.h" #include "net/http/http_util.h" #include "third_party/blink/public/common/user_agent/user_agent_metadata.h" -#include "third_party/blink/public/mojom/referrer.mojom.h" +#include "third_party/blink/public/mojom/loader/referrer.mojom.h" #if defined(OS_ANDROID) #include "base/android/jni_array.h" @@ -88,6 +88,14 @@ jboolean NavigationImpl::SetUserAgentString( #endif +bool NavigationImpl::IsPageInitiated() { + return navigation_handle_->IsRendererInitiated(); +} + +bool NavigationImpl::IsReload() { + return navigation_handle_->GetReloadType() != content::ReloadType::NONE; +} + GURL NavigationImpl::GetURL() { return navigation_handle_->GetURL(); } diff --git a/chromium/weblayer/browser/navigation_impl.h b/chromium/weblayer/browser/navigation_impl.h index c002d887525..e3513458fe4 100644 --- a/chromium/weblayer/browser/navigation_impl.h +++ b/chromium/weblayer/browser/navigation_impl.h @@ -69,6 +69,8 @@ class NavigationImpl : public Navigation { jboolean SetUserAgentString( JNIEnv* env, const base::android::JavaParamRef<jstring>& value); + jboolean IsPageInitiated(JNIEnv* env) { return IsPageInitiated(); } + jboolean IsReload(JNIEnv* env) { return IsReload(); } base::android::ScopedJavaGlobalRef<jobject> java_navigation() { return java_navigation_; @@ -89,6 +91,8 @@ class NavigationImpl : public Navigation { void SetRequestHeader(const std::string& name, const std::string& value) override; void SetUserAgentString(const std::string& value) override; + bool IsPageInitiated() override; + bool IsReload() override; content::NavigationHandle* navigation_handle_; diff --git a/chromium/weblayer/browser/navigation_ui_data_impl.cc b/chromium/weblayer/browser/navigation_ui_data_impl.cc new file mode 100644 index 00000000000..12435d2eaec --- /dev/null +++ b/chromium/weblayer/browser/navigation_ui_data_impl.cc @@ -0,0 +1,20 @@ +// 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 "weblayer/browser/navigation_ui_data_impl.h" + +namespace weblayer { + +NavigationUIDataImpl::NavigationUIDataImpl( + bool disable_network_error_auto_reload) + : disable_network_error_auto_reload_(disable_network_error_auto_reload) {} + +NavigationUIDataImpl::~NavigationUIDataImpl() = default; + +std::unique_ptr<content::NavigationUIData> NavigationUIDataImpl::Clone() { + return std::make_unique<NavigationUIDataImpl>( + disable_network_error_auto_reload_); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/navigation_ui_data_impl.h b/chromium/weblayer/browser/navigation_ui_data_impl.h new file mode 100644 index 00000000000..afd928ca576 --- /dev/null +++ b/chromium/weblayer/browser/navigation_ui_data_impl.h @@ -0,0 +1,34 @@ +// 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 WEBLAYER_BROWSER_NAVIGATION_UI_DATA_IMPL_H_ +#define WEBLAYER_BROWSER_NAVIGATION_UI_DATA_IMPL_H_ + +#include "content/public/browser/navigation_ui_data.h" + +namespace weblayer { + +// Data that we pass to content::NavigationController::LoadURLWithParams +// and can access from content::NavigationHandle later. +class NavigationUIDataImpl : public content::NavigationUIData { + public: + explicit NavigationUIDataImpl(bool disable_network_error_auto_reload); + NavigationUIDataImpl(const NavigationUIDataImpl&) = delete; + NavigationUIDataImpl& operator=(const NavigationUIDataImpl&) = delete; + ~NavigationUIDataImpl() override; + + // content::NavigationUIData implementation: + std::unique_ptr<content::NavigationUIData> Clone() override; + + bool disable_network_error_auto_reload() const { + return disable_network_error_auto_reload_; + } + + private: + bool disable_network_error_auto_reload_; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_NAVIGATION_UI_DATA_IMPL_H_ diff --git a/chromium/weblayer/browser/no_state_prefetch/no_state_prefetch_browsertest.cc b/chromium/weblayer/browser/no_state_prefetch/no_state_prefetch_browsertest.cc new file mode 100644 index 00000000000..03e5d9af1c5 --- /dev/null +++ b/chromium/weblayer/browser/no_state_prefetch/no_state_prefetch_browsertest.cc @@ -0,0 +1,212 @@ +// 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 <memory> + +#include "base/run_loop.h" +#include "base/test/metrics/histogram_tester.h" +#include "base/threading/platform_thread.h" +#include "build/build_config.h" +#include "components/prerender/browser/prerender_histograms.h" +#include "components/prerender/browser/prerender_manager.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/url_loader_monitor.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "net/test/embedded_test_server/http_request.h" +#include "net/test/embedded_test_server/http_response.h" +#include "services/network/public/cpp/resource_request.h" +#include "weblayer/browser/no_state_prefetch/prerender_link_manager_factory.h" +#include "weblayer/browser/no_state_prefetch/prerender_manager_factory.h" +#include "weblayer/browser/profile_impl.h" +#include "weblayer/browser/tab_impl.h" +#include "weblayer/shell/browser/shell.h" +#include "weblayer/test/weblayer_browser_test.h" +#include "weblayer/test/weblayer_browser_test_utils.h" + +#if defined(OS_ANDROID) +#include "components/ukm/test_ukm_recorder.h" +#include "services/metrics/public/cpp/ukm_builders.h" +#include "weblayer/browser/android/metrics/metrics_test_helper.h" +#endif + +namespace weblayer { + +class NoStatePrefetchBrowserTest : public WebLayerBrowserTest { + public: +#if defined(OS_ANDROID) + void SetUp() override { + InstallTestGmsBridge(/* user_consent= */ true); + + WebLayerBrowserTest::SetUp(); + } + + void TearDown() override { + RemoveTestGmsBridge(); + WebLayerBrowserTest::TearDown(); + } +#endif + + void SetUpOnMainThread() override { + prerendered_page_fetched_ = std::make_unique<base::RunLoop>(); + script_resource_fetched_ = std::make_unique<base::RunLoop>(); + + https_server_ = std::make_unique<net::EmbeddedTestServer>( + net::EmbeddedTestServer::TYPE_HTTPS); + https_server_->RegisterRequestHandler(base::BindRepeating( + &NoStatePrefetchBrowserTest::HandleRequest, base::Unretained(this))); + https_server_->AddDefaultHandlers( + base::FilePath(FILE_PATH_LITERAL("weblayer/test/data"))); + ASSERT_TRUE(https_server_->Start()); + +#if defined(OS_ANDROID) + ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>(); +#endif + } + + // Helper methods. + std::unique_ptr<net::test_server::HttpResponse> HandleRequest( + const net::test_server::HttpRequest& request) { + if (request.GetURL().path().find("prerendered_page") != std::string::npos) { + if (prerendered_page_fetched_) + prerendered_page_fetched_->Quit(); + } + if (request.GetURL().path().find("prefetch.js") != std::string::npos) { + script_fetched_ = true; + auto iter = request.headers.find("Purpose"); + purpose_header_value_ = iter->second; + if (script_resource_fetched_) + script_resource_fetched_->Quit(); + } + if (request.GetURL().path().find("prefetch_meta.js") != std::string::npos) { + script_executed_ = true; + } + if (request.GetURL().path().find("alert.html") != std::string::npos) { + link_rel_next_started_ = true; + } + + // The default handlers will take care of this request. + return nullptr; + } + + void NavigateToPageAndWaitForTitleChange(const GURL& navigate_to, + base::string16 expected_title) { + content::TitleWatcher title_watcher( + static_cast<TabImpl*>(shell()->tab())->web_contents(), expected_title); + NavigateAndWaitForCompletion(navigate_to, shell()); + ASSERT_TRUE(expected_title == title_watcher.WaitAndGetTitle()); + } + + protected: + content::BrowserContext* GetBrowserContext() { + Tab* tab = shell()->tab(); + TabImpl* tab_impl = static_cast<TabImpl*>(tab); + return tab_impl->web_contents()->GetBrowserContext(); + } + + std::unique_ptr<base::RunLoop> prerendered_page_fetched_; + std::unique_ptr<base::RunLoop> script_resource_fetched_; + bool link_rel_next_started_ = false; + bool script_fetched_ = false; + bool script_executed_ = false; + std::string purpose_header_value_; + std::unique_ptr<net::EmbeddedTestServer> https_server_; +#if defined(OS_ANDROID) + std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_; +#endif +}; + +IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, CreatePrerenderManager) { + auto* prerender_manager = + PrerenderManagerFactory::GetForBrowserContext(GetBrowserContext()); + EXPECT_TRUE(prerender_manager); +} + +IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, CreatePrerenderLinkManager) { + auto* prerender_link_manager = + PrerenderLinkManagerFactory::GetForBrowserContext(GetBrowserContext()); + EXPECT_TRUE(prerender_link_manager); +} + +// Test that adding a link-rel prerender tag causes a fetch. +IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, + LinkRelPrerenderPageFetched) { + NavigateAndWaitForCompletion(GURL(https_server_->GetURL("/parent_page.html")), + shell()); + prerendered_page_fetched_->Run(); +} + +// Test that only render blocking resources are loaded during NoStatePrefetch. +IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, + NSPLoadsRenderBlockingResource) { + NavigateAndWaitForCompletion(GURL(https_server_->GetURL("/parent_page.html")), + shell()); + script_resource_fetched_->Run(); + EXPECT_EQ("prefetch", purpose_header_value_); + EXPECT_FALSE(script_executed_); +} + +// Test that navigating to a no-state-prefetched page executes JS and reuses +// prerendered resources. +IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, NavigateToPrerenderedPage) { + NavigateAndWaitForCompletion(GURL(https_server_->GetURL("/parent_page.html")), + shell()); + script_resource_fetched_->Run(); + + // Navigate to the prerendered page and wait for its title to change. + script_fetched_ = false; + NavigateToPageAndWaitForTitleChange( + GURL(https_server_->GetURL("/prerendered_page.html")), + base::ASCIIToUTF16("Prefetch Page")); + + EXPECT_FALSE(script_fetched_); + EXPECT_TRUE(script_executed_); +} + +#if defined(OS_ANDROID) +// Test that no-state-prefetch results in UKM getting recorded. +IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, UKMRecorded) { + GetProfile()->SetBooleanSetting(SettingType::UKM_ENABLED, true); + NavigateAndWaitForCompletion(GURL(https_server_->GetURL("/parent_page.html")), + shell()); + script_resource_fetched_->Run(); + + NavigateToPageAndWaitForTitleChange( + GURL(https_server_->GetURL("/prerendered_page.html")), + base::ASCIIToUTF16("Prefetch Page")); + + auto entries = ukm_recorder_->GetEntriesByName( + ukm::builders::NoStatePrefetch::kEntryName); + ASSERT_EQ(entries.size(), 1u); + const auto* entry = entries[0]; + // FinalStatus must be set to FINAL_STATUS_NOSTATE_PREFETCH_FINISHED. + ukm_recorder_->ExpectEntryMetric( + entry, + ukm::builders::NoStatePrefetch::kPrefetchedRecently_FinalStatusName, 56); + // Origin must be set to ORIGIN_LINK_REL_PRERENDER_SAMEDOMAIN. + ukm_recorder_->ExpectEntryMetric( + entry, ukm::builders::NoStatePrefetch::kPrefetchedRecently_OriginName, 7); +} +#endif + +// link-rel="prerender" happens even when NoStatePrefetch has been disabled. +IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, + LinkRelPrerenderWithNSPDisabled) { + GetProfile()->SetBooleanSetting(SettingType::NETWORK_PREDICTION_ENABLED, + false); + NavigateAndWaitForCompletion(GURL(https_server_->GetURL("/parent_page.html")), + shell()); + prerendered_page_fetched_->Run(); +} + +// link-rel="next" doesn't happen when NoStatePrefetch has been disabled. +IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, LinkRelNextWithNSPDisabled) { + GetProfile()->SetBooleanSetting(SettingType::NETWORK_PREDICTION_ENABLED, + false); + NavigateToPageAndWaitForTitleChange( + GURL(https_server_->GetURL("/parent_page.html")), + base::ASCIIToUTF16("Parent Page")); + EXPECT_FALSE(link_rel_next_started_); +} + +} // namespace weblayer
\ No newline at end of file diff --git a/chromium/weblayer/browser/no_state_prefetch/prerender_link_manager_factory.cc b/chromium/weblayer/browser/no_state_prefetch/prerender_link_manager_factory.cc new file mode 100644 index 00000000000..d7f715628e3 --- /dev/null +++ b/chromium/weblayer/browser/no_state_prefetch/prerender_link_manager_factory.cc @@ -0,0 +1,51 @@ +// 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 "weblayer/browser/no_state_prefetch/prerender_link_manager_factory.h" + +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "components/prerender/browser/prerender_link_manager.h" +#include "components/prerender/browser/prerender_manager.h" +#include "weblayer/browser/no_state_prefetch/prerender_manager_factory.h" + +namespace weblayer { + +// static +prerender::PrerenderLinkManager* +PrerenderLinkManagerFactory::GetForBrowserContext( + content::BrowserContext* browser_context) { + return static_cast<prerender::PrerenderLinkManager*>( + GetInstance()->GetServiceForBrowserContext(browser_context, true)); +} + +// static +PrerenderLinkManagerFactory* PrerenderLinkManagerFactory::GetInstance() { + return base::Singleton<PrerenderLinkManagerFactory>::get(); +} + +PrerenderLinkManagerFactory::PrerenderLinkManagerFactory() + : BrowserContextKeyedServiceFactory( + "PrerenderLinkmanager", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(weblayer::PrerenderManagerFactory::GetInstance()); +} + +KeyedService* PrerenderLinkManagerFactory::BuildServiceInstanceFor( + content::BrowserContext* browser_context) const { + DCHECK(browser_context); + + prerender::PrerenderManager* prerender_manager = + PrerenderManagerFactory::GetForBrowserContext(browser_context); + if (!prerender_manager) + return nullptr; + + return new prerender::PrerenderLinkManager(prerender_manager); +} + +content::BrowserContext* PrerenderLinkManagerFactory::GetBrowserContextToUse( + content::BrowserContext* browser_context) const { + return browser_context; +} + +} // namespace weblayer
\ No newline at end of file diff --git a/chromium/weblayer/browser/no_state_prefetch/prerender_link_manager_factory.h b/chromium/weblayer/browser/no_state_prefetch/prerender_link_manager_factory.h new file mode 100644 index 00000000000..7369249c063 --- /dev/null +++ b/chromium/weblayer/browser/no_state_prefetch/prerender_link_manager_factory.h @@ -0,0 +1,41 @@ +// 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 WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_LINK_MANAGER_FACTORY_H_ +#define WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_LINK_MANAGER_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "components/prerender/browser/prerender_link_manager.h" + +namespace content { +class BrowserContext; +} + +namespace weblayer { + +class PrerenderLinkManagerFactory : public BrowserContextKeyedServiceFactory { + public: + // Returns the prerender::PrerenderLinkManager for |context|. + static prerender::PrerenderLinkManager* GetForBrowserContext( + content::BrowserContext* context); + + static PrerenderLinkManagerFactory* GetInstance(); + + private: + friend struct base::DefaultSingletonTraits<PrerenderLinkManagerFactory>; + + PrerenderLinkManagerFactory(); + ~PrerenderLinkManagerFactory() override = default; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* browser) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_LINK_MANAGER_FACTORY_H_ diff --git a/chromium/weblayer/browser/no_state_prefetch/prerender_manager_delegate_impl.cc b/chromium/weblayer/browser/no_state_prefetch/prerender_manager_delegate_impl.cc new file mode 100644 index 00000000000..7194cdb225e --- /dev/null +++ b/chromium/weblayer/browser/no_state_prefetch/prerender_manager_delegate_impl.cc @@ -0,0 +1,45 @@ +// 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 "weblayer/browser/no_state_prefetch/prerender_manager_delegate_impl.h" + +#include "components/prerender/browser/prerender_contents_delegate.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "weblayer/browser/browser_context_impl.h" +#include "weblayer/browser/cookie_settings_factory.h" +#include "weblayer/browser/profile_impl.h" +#include "weblayer/public/profile.h" + +namespace weblayer { + +PrerenderManagerDelegateImpl::PrerenderManagerDelegateImpl( + content::BrowserContext* browser_context) + : browser_context_(browser_context) {} + +scoped_refptr<content_settings::CookieSettings> +PrerenderManagerDelegateImpl::GetCookieSettings() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + return CookieSettingsFactory::GetForBrowserContext(browser_context_); +} + +std::unique_ptr<prerender::PrerenderContentsDelegate> +PrerenderManagerDelegateImpl::GetPrerenderContentsDelegate() { + return std::make_unique<prerender::PrerenderContentsDelegate>(); +} + +bool PrerenderManagerDelegateImpl::IsNetworkPredictionPreferenceEnabled() { + auto* profile = ProfileImpl::FromBrowserContext(browser_context_); + DCHECK(profile); + + return profile->GetBooleanSetting(SettingType::NETWORK_PREDICTION_ENABLED); +} + +std::string PrerenderManagerDelegateImpl::GetReasonForDisablingPrediction() { + return IsNetworkPredictionPreferenceEnabled() ? "" + : "Disabled by user setting"; +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/no_state_prefetch/prerender_manager_delegate_impl.h b/chromium/weblayer/browser/no_state_prefetch/prerender_manager_delegate_impl.h new file mode 100644 index 00000000000..788a8cad121 --- /dev/null +++ b/chromium/weblayer/browser/no_state_prefetch/prerender_manager_delegate_impl.h @@ -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. + +#ifndef WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_MANAGER_DELEGATE_IMPL_H_ +#define WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_MANAGER_DELEGATE_IMPL_H_ + +#include "components/content_settings/core/browser/cookie_settings.h" +#include "components/prerender/browser/prerender_manager_delegate.h" + +namespace content { +class BrowserContext; +} + +namespace weblayer { + +class PrerenderManagerDelegateImpl + : public prerender::PrerenderManagerDelegate { + public: + explicit PrerenderManagerDelegateImpl( + content::BrowserContext* browser_context); + ~PrerenderManagerDelegateImpl() override = default; + + // PrerenderManagerDelegate overrides. + scoped_refptr<content_settings::CookieSettings> GetCookieSettings() override; + std::unique_ptr<prerender::PrerenderContentsDelegate> + GetPrerenderContentsDelegate() override; + bool IsNetworkPredictionPreferenceEnabled() override; + std::string GetReasonForDisablingPrediction() override; + + private: + content::BrowserContext* browser_context_; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_MANAGER_DELEGATE_IMPL_H_ diff --git a/chromium/weblayer/browser/no_state_prefetch/prerender_manager_factory.cc b/chromium/weblayer/browser/no_state_prefetch/prerender_manager_factory.cc new file mode 100644 index 00000000000..b99e036662f --- /dev/null +++ b/chromium/weblayer/browser/no_state_prefetch/prerender_manager_factory.cc @@ -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. + +#include "weblayer/browser/no_state_prefetch/prerender_manager_factory.h" + +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "components/prerender/browser/prerender_manager.h" +#include "weblayer/browser/no_state_prefetch/prerender_manager_delegate_impl.h" + +namespace weblayer { + +// static +prerender::PrerenderManager* PrerenderManagerFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast<prerender::PrerenderManager*>( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +PrerenderManagerFactory* PrerenderManagerFactory::GetInstance() { + return base::Singleton<PrerenderManagerFactory>::get(); +} + +PrerenderManagerFactory::PrerenderManagerFactory() + : BrowserContextKeyedServiceFactory( + "PrerenderManager", + BrowserContextDependencyManager::GetInstance()) {} + +KeyedService* PrerenderManagerFactory::BuildServiceInstanceFor( + content::BrowserContext* browser_context) const { + return new prerender::PrerenderManager( + browser_context, + std::make_unique<PrerenderManagerDelegateImpl>(browser_context)); +} + +content::BrowserContext* PrerenderManagerFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return context; +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/no_state_prefetch/prerender_manager_factory.h b/chromium/weblayer/browser/no_state_prefetch/prerender_manager_factory.h new file mode 100644 index 00000000000..1587d0bf759 --- /dev/null +++ b/chromium/weblayer/browser/no_state_prefetch/prerender_manager_factory.h @@ -0,0 +1,47 @@ +// 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 WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_MANAGER_FACTORY_H_ +#define WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_MANAGER_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace content { +class BrowserContext; +} + +namespace prerender { +class PrerenderManager; +} + +namespace weblayer { + +// Singleton that owns all PrerenderManagers and associates them with +// BrowserContexts. Listens for the BrowserContext's destruction notification +// and cleans up the associated PrerenderManager. +class PrerenderManagerFactory : public BrowserContextKeyedServiceFactory { + public: + // Returns the PrerenderManager for |context|. + static prerender::PrerenderManager* GetForBrowserContext( + content::BrowserContext* context); + + static PrerenderManagerFactory* GetInstance(); + + private: + friend struct base::DefaultSingletonTraits<PrerenderManagerFactory>; + + PrerenderManagerFactory(); + ~PrerenderManagerFactory() override = default; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* browser) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_MANAGER_FACTORY_H_ diff --git a/chromium/weblayer/browser/no_state_prefetch/prerender_processor_impl_delegate_impl.cc b/chromium/weblayer/browser/no_state_prefetch/prerender_processor_impl_delegate_impl.cc new file mode 100644 index 00000000000..1bc7022b279 --- /dev/null +++ b/chromium/weblayer/browser/no_state_prefetch/prerender_processor_impl_delegate_impl.cc @@ -0,0 +1,19 @@ +// 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 "weblayer/browser/no_state_prefetch/prerender_processor_impl_delegate_impl.h" + +#include "components/prerender/browser/prerender_link_manager.h" +#include "content/public/browser/browser_context.h" +#include "weblayer/browser/no_state_prefetch/prerender_link_manager_factory.h" + +namespace weblayer { + +prerender::PrerenderLinkManager* +PrerenderProcessorImplDelegateImpl::GetPrerenderLinkManager( + content::BrowserContext* browser_context) { + return PrerenderLinkManagerFactory::GetForBrowserContext(browser_context); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/no_state_prefetch/prerender_processor_impl_delegate_impl.h b/chromium/weblayer/browser/no_state_prefetch/prerender_processor_impl_delegate_impl.h new file mode 100644 index 00000000000..1221e85b2da --- /dev/null +++ b/chromium/weblayer/browser/no_state_prefetch/prerender_processor_impl_delegate_impl.h @@ -0,0 +1,33 @@ +// 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 WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_PROCESSOR_IMPL_DELEGATE_IMPL_H_ +#define WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_PROCESSOR_IMPL_DELEGATE_IMPL_H_ + +#include "components/prerender/browser/prerender_processor_impl_delegate.h" + +namespace content { +class BrowserContext; +} + +namespace prerender { +class PrerenderLinkManager; +} + +namespace weblayer { + +class PrerenderProcessorImplDelegateImpl + : public prerender::PrerenderProcessorImplDelegate { + public: + PrerenderProcessorImplDelegateImpl() = default; + ~PrerenderProcessorImplDelegateImpl() override = default; + + // prerender::PrerenderProcessorImplDelegate overrides, + prerender::PrerenderLinkManager* GetPrerenderLinkManager( + content::BrowserContext* browser_context) override; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_PROCESSOR_IMPL_DELEGATE_IMPL_H_ diff --git a/chromium/weblayer/browser/no_state_prefetch/prerender_tab_helper.cc b/chromium/weblayer/browser/no_state_prefetch/prerender_tab_helper.cc new file mode 100644 index 00000000000..3b2d204df1f --- /dev/null +++ b/chromium/weblayer/browser/no_state_prefetch/prerender_tab_helper.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 "weblayer/browser/no_state_prefetch/prerender_tab_helper.h" + +#include "components/prerender/browser/prerender_manager.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/web_contents.h" +#include "weblayer/browser/no_state_prefetch/prerender_manager_factory.h" + +namespace weblayer { + +PrerenderTabHelper::PrerenderTabHelper(content::WebContents* web_contents) + : content::WebContentsObserver(web_contents) {} + +PrerenderTabHelper::~PrerenderTabHelper() = default; + +void PrerenderTabHelper::DidFinishNavigation( + content::NavigationHandle* navigation_handle) { + if (!navigation_handle->IsInMainFrame() || + !navigation_handle->HasCommitted() || navigation_handle->IsErrorPage()) { + return; + } + + prerender::PrerenderManager* prerender_manager = + PrerenderManagerFactory::GetForBrowserContext( + web_contents()->GetBrowserContext()); + + if (prerender_manager && + !prerender_manager->IsWebContentsPrerendering(web_contents(), nullptr)) + prerender_manager->RecordNavigation(navigation_handle->GetURL()); +} + +WEB_CONTENTS_USER_DATA_KEY_IMPL(PrerenderTabHelper) + +} // namespace weblayer
\ No newline at end of file diff --git a/chromium/weblayer/browser/no_state_prefetch/prerender_tab_helper.h b/chromium/weblayer/browser/no_state_prefetch/prerender_tab_helper.h new file mode 100644 index 00000000000..996abda8233 --- /dev/null +++ b/chromium/weblayer/browser/no_state_prefetch/prerender_tab_helper.h @@ -0,0 +1,45 @@ +// 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 WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_TAB_HELPER_H_ +#define WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_TAB_HELPER_H_ + +#include "base/macros.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" + +namespace content { +class WebContents; +} + +namespace prerender { +class PrerenderManager; +} + +namespace weblayer { + +// Notifies the prerender::PrerenderManager with the events happening in the +// prerendered WebContents. +class PrerenderTabHelper + : public content::WebContentsObserver, + public content::WebContentsUserData<PrerenderTabHelper> { + public: + ~PrerenderTabHelper() override; + PrerenderTabHelper(const PrerenderTabHelper&) = delete; + PrerenderTabHelper& operator=(const PrerenderTabHelper&) = delete; + + // content::WebContentsObserver implementation. + void DidFinishNavigation( + content::NavigationHandle* navigation_handle) override; + + private: + explicit PrerenderTabHelper(content::WebContents* web_contents); + friend class content::WebContentsUserData<PrerenderTabHelper>; + + WEB_CONTENTS_USER_DATA_KEY_DECL(); +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_TAB_HELPER_H_
\ No newline at end of file diff --git a/chromium/weblayer/browser/no_state_prefetch/prerender_utils.cc b/chromium/weblayer/browser/no_state_prefetch/prerender_utils.cc new file mode 100644 index 00000000000..177a16adbea --- /dev/null +++ b/chromium/weblayer/browser/no_state_prefetch/prerender_utils.cc @@ -0,0 +1,27 @@ +// 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 "weblayer/browser/no_state_prefetch/prerender_utils.h" + +#include "components/prerender/browser/prerender_contents.h" +#include "components/prerender/browser/prerender_manager.h" +#include "content/public/browser/web_contents.h" +#include "weblayer/browser/no_state_prefetch/prerender_manager_factory.h" + +namespace weblayer { +prerender::PrerenderContents* PrerenderContentsFromWebContents( + content::WebContents* web_contents) { + if (!web_contents) + return nullptr; + + prerender::PrerenderManager* prerender_manager = + PrerenderManagerFactory::GetForBrowserContext( + web_contents->GetBrowserContext()); + if (!prerender_manager) + return nullptr; + + return prerender_manager->GetPrerenderContents(web_contents); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/no_state_prefetch/prerender_utils.h b/chromium/weblayer/browser/no_state_prefetch/prerender_utils.h new file mode 100644 index 00000000000..5064e6f1321 --- /dev/null +++ b/chromium/weblayer/browser/no_state_prefetch/prerender_utils.h @@ -0,0 +1,23 @@ +// 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 WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_UTILS_H_ +#define WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_UTILS_H_ + +namespace content { +class WebContents; +} + +namespace prerender { +class PrerenderContents; +} + +namespace weblayer { + +prerender::PrerenderContents* PrerenderContentsFromWebContents( + content::WebContents* web_contents); + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_NO_STATE_PREFETCH_PRERENDER_UTILS_H_ diff --git a/chromium/weblayer/browser/page_load_metrics_initialize.cc b/chromium/weblayer/browser/page_load_metrics_initialize.cc index cb2978f8951..740b83fd1c8 100644 --- a/chromium/weblayer/browser/page_load_metrics_initialize.cc +++ b/chromium/weblayer/browser/page_load_metrics_initialize.cc @@ -7,6 +7,10 @@ #include "base/macros.h" #include "components/page_load_metrics/browser/metrics_web_contents_observer.h" #include "components/page_load_metrics/browser/page_load_metrics_embedder_base.h" +#include "components/page_load_metrics/browser/page_load_metrics_observer.h" +#include "components/page_load_metrics/browser/page_load_tracker.h" +#include "weblayer/browser/no_state_prefetch/prerender_utils.h" +#include "weblayer/browser/ukm_page_load_metrics_observer.h" namespace weblayer { @@ -27,7 +31,7 @@ class PageLoadMetricsEmbedder // page_load_metrics::PageLoadMetricsEmbedderBase: bool IsNewTabPageUrl(const GURL& url) override { return false; } bool IsPrerender(content::WebContents* web_contents) override { - return false; + return PrerenderContentsFromWebContents(web_contents); } bool IsExtensionUrl(const GURL& url) override { return false; } @@ -35,10 +39,17 @@ class PageLoadMetricsEmbedder // page_load_metrics::PageLoadMetricsEmbedderBase: void RegisterEmbedderObservers( page_load_metrics::PageLoadTracker* tracker) override { + std::unique_ptr<page_load_metrics::PageLoadMetricsObserver> ukm_observer = + UkmPageLoadMetricsObserver::CreateIfNeeded(); + if (ukm_observer) + tracker->AddObserver(std::move(ukm_observer)); + if (g_callback_for_testing) (*g_callback_for_testing).Run(tracker); } - bool IsPrerendering() const override { return false; } + bool IsPrerendering() const override { + return PrerenderContentsFromWebContents(web_contents()); + } }; } // namespace diff --git a/chromium/weblayer/browser/tab_specific_content_settings_delegate.cc b/chromium/weblayer/browser/page_specific_content_settings_delegate.cc index 13c9c791d74..4a18513b7d7 100644 --- a/chromium/weblayer/browser/tab_specific_content_settings_delegate.cc +++ b/chromium/weblayer/browser/page_specific_content_settings_delegate.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "weblayer/browser/tab_specific_content_settings_delegate.h" +#include "weblayer/browser/page_specific_content_settings_delegate.h" #include "base/bind_helpers.h" #include "components/content_settings/core/common/content_settings.h" @@ -25,15 +25,15 @@ void SetContentSettingRules(content::RenderProcessHost* process, } // namespace -TabSpecificContentSettingsDelegate::TabSpecificContentSettingsDelegate( +PageSpecificContentSettingsDelegate::PageSpecificContentSettingsDelegate( content::WebContents* web_contents) : web_contents_(web_contents) {} -TabSpecificContentSettingsDelegate::~TabSpecificContentSettingsDelegate() = +PageSpecificContentSettingsDelegate::~PageSpecificContentSettingsDelegate() = default; // static -void TabSpecificContentSettingsDelegate::UpdateRendererContentSettingRules( +void PageSpecificContentSettingsDelegate::UpdateRendererContentSettingRules( content::RenderProcessHost* process) { RendererContentSettingRules rules; GetRendererContentSettingRules( @@ -43,25 +43,25 @@ void TabSpecificContentSettingsDelegate::UpdateRendererContentSettingRules( weblayer::SetContentSettingRules(process, rules); } -void TabSpecificContentSettingsDelegate::UpdateLocationBar() {} +void PageSpecificContentSettingsDelegate::UpdateLocationBar() {} -void TabSpecificContentSettingsDelegate::SetContentSettingRules( +void PageSpecificContentSettingsDelegate::SetContentSettingRules( content::RenderProcessHost* process, const RendererContentSettingRules& rules) { weblayer::SetContentSettingRules(process, rules); } -PrefService* TabSpecificContentSettingsDelegate::GetPrefs() { +PrefService* PageSpecificContentSettingsDelegate::GetPrefs() { return static_cast<BrowserContextImpl*>(web_contents_->GetBrowserContext()) ->pref_service(); } -HostContentSettingsMap* TabSpecificContentSettingsDelegate::GetSettingsMap() { +HostContentSettingsMap* PageSpecificContentSettingsDelegate::GetSettingsMap() { return HostContentSettingsMapFactory::GetForBrowserContext( web_contents_->GetBrowserContext()); } -ContentSetting TabSpecificContentSettingsDelegate::GetEmbargoSetting( +ContentSetting PageSpecificContentSettingsDelegate::GetEmbargoSetting( const GURL& request_origin, ContentSettingsType permission) { return PermissionDecisionAutoBlockerFactory::GetForBrowserContext( @@ -71,30 +71,51 @@ ContentSetting TabSpecificContentSettingsDelegate::GetEmbargoSetting( } std::vector<storage::FileSystemType> -TabSpecificContentSettingsDelegate::GetAdditionalFileSystemTypes() { +PageSpecificContentSettingsDelegate::GetAdditionalFileSystemTypes() { return {}; } browsing_data::CookieHelper::IsDeletionDisabledCallback -TabSpecificContentSettingsDelegate::GetIsDeletionDisabledCallback() { +PageSpecificContentSettingsDelegate::GetIsDeletionDisabledCallback() { return base::NullCallback(); } -bool TabSpecificContentSettingsDelegate::IsMicrophoneCameraStateChanged( - content_settings::TabSpecificContentSettings::MicrophoneCameraState +bool PageSpecificContentSettingsDelegate::IsMicrophoneCameraStateChanged( + content_settings::PageSpecificContentSettings::MicrophoneCameraState microphone_camera_state, const std::string& media_stream_selected_audio_device, const std::string& media_stream_selected_video_device) { return false; } -content_settings::TabSpecificContentSettings::MicrophoneCameraState -TabSpecificContentSettingsDelegate::GetMicrophoneCameraState() { - return content_settings::TabSpecificContentSettings:: +content_settings::PageSpecificContentSettings::MicrophoneCameraState +PageSpecificContentSettingsDelegate::GetMicrophoneCameraState() { + return content_settings::PageSpecificContentSettings:: MICROPHONE_CAMERA_NOT_ACCESSED; } -void TabSpecificContentSettingsDelegate::OnContentBlocked( +void PageSpecificContentSettingsDelegate::OnContentBlocked( ContentSettingsType type) {} +void PageSpecificContentSettingsDelegate::OnCacheStorageAccessAllowed( + const url::Origin& origin) {} + +void PageSpecificContentSettingsDelegate::OnCookieAccessAllowed( + const net::CookieList& accessed_cookies) {} + +void PageSpecificContentSettingsDelegate::OnDomStorageAccessAllowed( + const url::Origin& origin) {} + +void PageSpecificContentSettingsDelegate::OnFileSystemAccessAllowed( + const url::Origin& origin) {} + +void PageSpecificContentSettingsDelegate::OnIndexedDBAccessAllowed( + const url::Origin& origin) {} + +void PageSpecificContentSettingsDelegate::OnServiceWorkerAccessAllowed( + const url::Origin& origin) {} + +void PageSpecificContentSettingsDelegate::OnWebDatabaseAccessAllowed( + const url::Origin& origin) {} + } // namespace weblayer diff --git a/chromium/weblayer/browser/page_specific_content_settings_delegate.h b/chromium/weblayer/browser/page_specific_content_settings_delegate.h new file mode 100644 index 00000000000..8ebdac3d85d --- /dev/null +++ b/chromium/weblayer/browser/page_specific_content_settings_delegate.h @@ -0,0 +1,61 @@ +// 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 WEBLAYER_BROWSER_PAGE_SPECIFIC_CONTENT_SETTINGS_DELEGATE_H_ +#define WEBLAYER_BROWSER_PAGE_SPECIFIC_CONTENT_SETTINGS_DELEGATE_H_ + +#include "components/content_settings/browser/page_specific_content_settings.h" + +namespace weblayer { + +// Called by PageSpecificContentSettings to handle WebLayer specific logic. +class PageSpecificContentSettingsDelegate + : public content_settings::PageSpecificContentSettings::Delegate { + public: + explicit PageSpecificContentSettingsDelegate( + content::WebContents* web_contents); + ~PageSpecificContentSettingsDelegate() override; + PageSpecificContentSettingsDelegate( + const PageSpecificContentSettingsDelegate&) = delete; + PageSpecificContentSettingsDelegate& operator=( + const PageSpecificContentSettingsDelegate&) = delete; + + static void UpdateRendererContentSettingRules( + content::RenderProcessHost* process); + + private: + // PageSpecificContentSettings::Delegate: + void UpdateLocationBar() override; + void SetContentSettingRules( + content::RenderProcessHost* process, + const RendererContentSettingRules& rules) override; + PrefService* GetPrefs() override; + HostContentSettingsMap* GetSettingsMap() override; + ContentSetting GetEmbargoSetting(const GURL& request_origin, + ContentSettingsType permission) override; + std::vector<storage::FileSystemType> GetAdditionalFileSystemTypes() override; + browsing_data::CookieHelper::IsDeletionDisabledCallback + GetIsDeletionDisabledCallback() override; + bool IsMicrophoneCameraStateChanged( + content_settings::PageSpecificContentSettings::MicrophoneCameraState + microphone_camera_state, + const std::string& media_stream_selected_audio_device, + const std::string& media_stream_selected_video_device) override; + content_settings::PageSpecificContentSettings::MicrophoneCameraState + GetMicrophoneCameraState() override; + void OnContentBlocked(ContentSettingsType type) override; + void OnCacheStorageAccessAllowed(const url::Origin& origin) override; + void OnCookieAccessAllowed(const net::CookieList& accessed_cookies) override; + void OnDomStorageAccessAllowed(const url::Origin& origin) override; + void OnFileSystemAccessAllowed(const url::Origin& origin) override; + void OnIndexedDBAccessAllowed(const url::Origin& origin) override; + void OnServiceWorkerAccessAllowed(const url::Origin& origin) override; + void OnWebDatabaseAccessAllowed(const url::Origin& origin) override; + + content::WebContents* web_contents_; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_PAGE_SPECIFIC_CONTENT_SETTINGS_DELEGATE_H_ diff --git a/chromium/weblayer/browser/permissions/geolocation_permission_context_delegate.cc b/chromium/weblayer/browser/permissions/geolocation_permission_context_delegate.cc index 27e7a80898b..381eef27258 100644 --- a/chromium/weblayer/browser/permissions/geolocation_permission_context_delegate.cc +++ b/chromium/weblayer/browser/permissions/geolocation_permission_context_delegate.cc @@ -5,6 +5,7 @@ #include "weblayer/browser/permissions/geolocation_permission_context_delegate.h" #include "build/build_config.h" +#include "weblayer/browser/default_search_engine.h" #if defined(OS_ANDROID) #include "weblayer/browser/android/permission_request_utils.h" @@ -39,8 +40,8 @@ PrefService* GeolocationPermissionContextDelegate::GetPrefs( bool GeolocationPermissionContextDelegate::IsRequestingOriginDSE( content::BrowserContext* browser_context, const GURL& requesting_origin) { - // TODO(crbug.com/1063433): This may need to be implemented for phase 3. - return false; + return IsPermissionControlledByDse(ContentSettingsType::GEOLOCATION, + url::Origin::Create(requesting_origin)); } void GeolocationPermissionContextDelegate::FinishNotifyPermissionSet( diff --git a/chromium/weblayer/browser/permissions/weblayer_permissions_client.cc b/chromium/weblayer/browser/permissions/weblayer_permissions_client.cc index e2fb5ff077e..24a2d10f0dc 100644 --- a/chromium/weblayer/browser/permissions/weblayer_permissions_client.cc +++ b/chromium/weblayer/browser/permissions/weblayer_permissions_client.cc @@ -6,6 +6,7 @@ #include "components/content_settings/core/browser/cookie_settings.h" #include "weblayer/browser/cookie_settings_factory.h" +#include "weblayer/browser/default_search_engine.h" #include "weblayer/browser/host_content_settings_map_factory.h" #include "weblayer/browser/permissions/permission_decision_auto_blocker_factory.h" #include "weblayer/browser/permissions/permission_manager_factory.h" @@ -34,6 +35,14 @@ WebLayerPermissionsClient::GetCookieSettings( return CookieSettingsFactory::GetForBrowserContext(browser_context); } +bool WebLayerPermissionsClient::IsSubresourceFilterActivated( + content::BrowserContext* browser_context, + const GURL& url) { + // As the web layer does not currently support subresource filtering, the + // activation setting does not change any browser behavior. + return false; +} + permissions::PermissionDecisionAutoBlocker* WebLayerPermissionsClient::GetPermissionDecisionAutoBlocker( content::BrowserContext* browser_context) { @@ -53,6 +62,24 @@ permissions::ChooserContextBase* WebLayerPermissionsClient::GetChooserContext( } #if defined(OS_ANDROID) +bool WebLayerPermissionsClient::IsPermissionControlledByDse( + content::BrowserContext* browser_context, + ContentSettingsType type, + const url::Origin& origin) { + return weblayer::IsPermissionControlledByDse(type, origin); +} + +bool WebLayerPermissionsClient::ResetPermissionIfControlledByDse( + content::BrowserContext* browser_context, + ContentSettingsType type, + const url::Origin& origin) { + if (IsPermissionControlledByDse(browser_context, type, origin)) { + ResetDsePermissions(browser_context); + return true; + } + return false; +} + void WebLayerPermissionsClient::RepromptForAndroidPermissions( content::WebContents* web_contents, const std::vector<ContentSettingsType>& content_settings_types, diff --git a/chromium/weblayer/browser/permissions/weblayer_permissions_client.h b/chromium/weblayer/browser/permissions/weblayer_permissions_client.h index 8bcc5dbc215..79042ef9649 100644 --- a/chromium/weblayer/browser/permissions/weblayer_permissions_client.h +++ b/chromium/weblayer/browser/permissions/weblayer_permissions_client.h @@ -24,6 +24,8 @@ class WebLayerPermissionsClient : public permissions::PermissionsClient { content::BrowserContext* browser_context) override; scoped_refptr<content_settings::CookieSettings> GetCookieSettings( content::BrowserContext* browser_context) override; + bool IsSubresourceFilterActivated(content::BrowserContext* browser_context, + const GURL& url) override; permissions::PermissionDecisionAutoBlocker* GetPermissionDecisionAutoBlocker( content::BrowserContext* browser_context) override; permissions::PermissionManager* GetPermissionManager( @@ -32,6 +34,13 @@ class WebLayerPermissionsClient : public permissions::PermissionsClient { content::BrowserContext* browser_context, ContentSettingsType type) override; #if defined(OS_ANDROID) + bool IsPermissionControlledByDse(content::BrowserContext* browser_context, + ContentSettingsType type, + const url::Origin& origin) override; + bool ResetPermissionIfControlledByDse( + content::BrowserContext* browser_context, + ContentSettingsType type, + const url::Origin& origin) override; void RepromptForAndroidPermissions( content::WebContents* web_contents, const std::vector<ContentSettingsType>& content_settings_types, diff --git a/chromium/weblayer/browser/persistence/browser_persister.cc b/chromium/weblayer/browser/persistence/browser_persister.cc index fac791261ab..61ea0b1ddb4 100644 --- a/chromium/weblayer/browser/persistence/browser_persister.cc +++ b/chromium/weblayer/browser/persistence/browser_persister.cc @@ -64,10 +64,10 @@ BrowserPersister::BrowserPersister(const base::FilePath& path, rebuild_on_next_save_(false), crypto_key_(decryption_key) { browser_->AddObserver(this); - command_storage_manager_->ScheduleGetCurrentSessionCommands( + command_storage_manager_->GetCurrentSessionCommands( base::BindOnce(&BrowserPersister::OnGotCurrentSessionCommands, - base::Unretained(this)), - decryption_key, &cancelable_task_tracker_); + weak_factory_.GetWeakPtr()), + decryption_key); } BrowserPersister::~BrowserPersister() { diff --git a/chromium/weblayer/browser/persistence/browser_persister.h b/chromium/weblayer/browser/persistence/browser_persister.h index a956914ad59..d4578eeacf1 100644 --- a/chromium/weblayer/browser/persistence/browser_persister.h +++ b/chromium/weblayer/browser/persistence/browser_persister.h @@ -14,8 +14,8 @@ #include <vector> #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "base/scoped_observer.h" -#include "base/task/cancelable_task_tracker.h" #include "components/sessions/content/session_tab_helper_delegate.h" #include "components/sessions/core/command_storage_manager_delegate.h" #include "components/sessions/core/session_service_commands.h" @@ -139,7 +139,7 @@ class BrowserPersister : public sessions::CommandStorageManagerDelegate, &TabImpl::RemoveDataObserver> data_observer_{this}; - base::CancelableTaskTracker cancelable_task_tracker_; + base::WeakPtrFactory<BrowserPersister> weak_factory_{this}; }; } // namespace weblayer diff --git a/chromium/weblayer/browser/persistence/browser_persister_browsertest.cc b/chromium/weblayer/browser/persistence/browser_persister_browsertest.cc index 23e30e3217c..a8eeec25f33 100644 --- a/chromium/weblayer/browser/persistence/browser_persister_browsertest.cc +++ b/chromium/weblayer/browser/persistence/browser_persister_browsertest.cc @@ -47,50 +47,6 @@ class BrowserPersisterTestHelper { namespace { using testing::UnorderedElementsAre; -class OneShotNavigationObserver : public NavigationObserver { - public: - explicit OneShotNavigationObserver(Shell* shell) : tab_(shell->tab()) { - tab_->GetNavigationController()->AddObserver(this); - } - - ~OneShotNavigationObserver() override { - tab_->GetNavigationController()->RemoveObserver(this); - } - - void WaitForNavigation() { run_loop_.Run(); } - - bool completed() { return completed_; } - bool is_error_page() { return is_error_page_; } - Navigation::LoadError load_error() { return load_error_; } - int http_status_code() { return http_status_code_; } - NavigationState navigation_state() { return navigation_state_; } - - private: - // NavigationObserver implementation: - void NavigationCompleted(Navigation* navigation) override { - completed_ = true; - Finish(navigation); - } - - void NavigationFailed(Navigation* navigation) override { Finish(navigation); } - - void Finish(Navigation* navigation) { - is_error_page_ = navigation->IsErrorPage(); - load_error_ = navigation->GetLoadError(); - http_status_code_ = navigation->GetHttpStatusCode(); - navigation_state_ = navigation->GetState(); - run_loop_.Quit(); - } - - base::RunLoop run_loop_; - Tab* tab_; - bool completed_ = false; - bool is_error_page_ = false; - Navigation::LoadError load_error_ = Navigation::kNoError; - int http_status_code_ = 0; - NavigationState navigation_state_ = NavigationState::kWaitingResponse; -}; - class BrowserObserverImpl : public BrowserObserver { public: static void WaitForNewTab(Browser* browser) { @@ -393,7 +349,7 @@ IN_PROC_BROWSER_TEST_F(BrowserPersisterTest, MoveBetweenBrowsers) { TabImpl* restored_tab_3 = static_cast<TabImpl*>(browser2->GetTabs()[0]); EXPECT_TRUE(restored_tab_3->web_contents()->GetController().NeedsReload()); restored_tab_3->web_contents()->GetController().LoadIfNecessary(); - content::WaitForLoadStop(restored_tab_3->web_contents()); + EXPECT_TRUE(content::WaitForLoadStop(restored_tab_3->web_contents())); } class BrowserPersisterTestWithTwoPersistedIds : public WebLayerBrowserTest { diff --git a/chromium/weblayer/browser/persistence/browser_persister_file_utils.cc b/chromium/weblayer/browser/persistence/browser_persister_file_utils.cc index ead41f214f9..8cec29c79e1 100644 --- a/chromium/weblayer/browser/persistence/browser_persister_file_utils.cc +++ b/chromium/weblayer/browser/persistence/browser_persister_file_utils.cc @@ -28,7 +28,7 @@ bool RemoveBrowserPersistenceStorageOnBackgroundThread( for (const std::string& id : ids) { DCHECK(!id.empty()); base::FilePath persistence_path = BuildPathForBrowserPersister(path, id); - if (!base::DeleteFile(persistence_path, /* recurse */ false)) + if (!base::DeleteFile(persistence_path)) all_succeeded = false; } return all_succeeded; diff --git a/chromium/weblayer/browser/profile_browsertest.cc b/chromium/weblayer/browser/profile_browsertest.cc index 3b8bbf3983b..9d11a603396 100644 --- a/chromium/weblayer/browser/profile_browsertest.cc +++ b/chromium/weblayer/browser/profile_browsertest.cc @@ -2,26 +2,191 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/test/bind_test_util.h" #include "build/build_config.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" +#include "ui/gfx/image/image_unittest_util.h" +#include "weblayer/browser/default_search_engine.h" +#include "weblayer/browser/favicon/favicon_fetcher_impl.h" +#include "weblayer/browser/favicon/test_favicon_fetcher_delegate.h" +#include "weblayer/browser/host_content_settings_map_factory.h" #include "weblayer/browser/profile_impl.h" +#include "weblayer/browser/tab_impl.h" +#include "weblayer/shell/browser/shell.h" #include "weblayer/test/weblayer_browser_test.h" +#include "weblayer/test/weblayer_browser_test_utils.h" namespace weblayer { -using ProfileBrowsertest = WebLayerBrowserTest; +using ProfileBrowserTest = WebLayerBrowserTest; // TODO(crbug.com/654704): Android does not support PRE_ tests. #if !defined(OS_ANDROID) // UKM enabling via Profile persists across restarts. -IN_PROC_BROWSER_TEST_F(ProfileBrowsertest, PRE_PersistUKM) { +IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, PRE_PersistUKM) { GetProfile()->SetBooleanSetting(SettingType::UKM_ENABLED, true); } -IN_PROC_BROWSER_TEST_F(ProfileBrowsertest, PersistUKM) { +IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, PersistUKM) { ASSERT_TRUE(GetProfile()->GetBooleanSetting(SettingType::UKM_ENABLED)); } +// Enabling Network Prediction via Profile persists across restarts. +IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, PRE_PersistNetworkPrediction) { + GetProfile()->SetBooleanSetting(SettingType::NETWORK_PREDICTION_ENABLED, + false); +} + +IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, PersistNetworkPrediction) { + ASSERT_FALSE( + GetProfile()->GetBooleanSetting(SettingType::NETWORK_PREDICTION_ENABLED)); +} + #endif // !defined(OS_ANDROID) +IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, GetCachedFaviconForPageUrl) { + // Navigation to a page with a favicon. + ASSERT_TRUE(embedded_test_server()->Start()); + TestFaviconFetcherDelegate fetcher_delegate; + auto fetcher = shell()->tab()->CreateFaviconFetcher(&fetcher_delegate); + const GURL url = + embedded_test_server()->GetURL("/simple_page_with_favicon.html"); + NavigateAndWaitForCompletion(url, shell()); + fetcher_delegate.WaitForFavicon(); + EXPECT_FALSE(fetcher_delegate.last_image().IsEmpty()); + EXPECT_EQ(1, fetcher_delegate.on_favicon_changed_call_count()); + + // Request the favicon. + base::RunLoop run_loop; + static_cast<TabImpl*>(shell()->tab()) + ->profile() + ->GetCachedFaviconForPageUrl( + url, base::BindLambdaForTesting([&](gfx::Image image) { + // The last parameter is the max difference allowed for each color + // component. As the image is encoded before saving to disk some + // variance is expected. + EXPECT_TRUE(gfx::test::AreImagesClose( + image, fetcher_delegate.last_image(), 10)); + run_loop.Quit(); + })); + run_loop.Run(); +} + +IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, ClearBrowsingDataDeletesFavicons) { + // Navigate to a page with a favicon. + ASSERT_TRUE(embedded_test_server()->Start()); + TestFaviconFetcherDelegate fetcher_delegate; + auto fetcher = shell()->tab()->CreateFaviconFetcher(&fetcher_delegate); + const GURL url = + embedded_test_server()->GetURL("/simple_page_with_favicon.html"); + NavigateAndWaitForCompletion(url, shell()); + fetcher_delegate.WaitForNonemptyFavicon(); + EXPECT_FALSE(fetcher_delegate.last_image().IsEmpty()); + EXPECT_EQ(1, fetcher_delegate.on_favicon_changed_call_count()); + + // Delete the favicons. + base::RunLoop run_loop; + base::Time now = base::Time::Now(); + ProfileImpl* profile = static_cast<TabImpl*>(shell()->tab())->profile(); + profile->ClearBrowsingData({BrowsingDataType::COOKIES_AND_SITE_DATA}, + now - base::TimeDelta::FromMinutes(5), now, + run_loop.QuitClosure()); + run_loop.Run(); + + // Ask for the cached favicon, there shouldn't be one. + base::RunLoop run_loop2; + profile->GetCachedFaviconForPageUrl( + url, base::BindLambdaForTesting([&](gfx::Image image) { + EXPECT_TRUE(image.IsEmpty()); + run_loop2.Quit(); + })); + run_loop2.Run(); + + // Navigate to another page, and verify favicon is downloaded. + fetcher_delegate.ClearLastImage(); + const GURL url2 = + embedded_test_server()->GetURL("/simple_page_with_favicon2.html"); + NavigateAndWaitForCompletion(url2, shell()); + fetcher_delegate.WaitForNonemptyFavicon(); + EXPECT_FALSE(fetcher_delegate.last_image().IsEmpty()); + EXPECT_EQ(2, fetcher_delegate.on_favicon_changed_call_count()); + + // And fetch the favicon one more time. + base::RunLoop run_loop3; + profile->GetCachedFaviconForPageUrl( + url2, base::BindLambdaForTesting([&](gfx::Image image) { + EXPECT_FALSE(image.IsEmpty()); + // The last parameter is the max difference allowed for each color + // component. As the image is encoded before saving to disk some + // variance is expected. + EXPECT_TRUE(gfx::test::AreImagesClose( + image, fetcher_delegate.last_image(), 10)); + run_loop3.Quit(); + })); + run_loop3.Run(); +} + +// Test default value. +IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, DefaultNetworkPredictionState) { + ASSERT_TRUE( + GetProfile()->GetBooleanSetting(SettingType::NETWORK_PREDICTION_ENABLED)); +} + +IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, ClearSiteSettings) { + auto dse_origin = GetDseOrigin().GetURL(); + auto foo_origin = GURL("http://www.foo.com"); + + auto* settings_map = HostContentSettingsMapFactory::GetForBrowserContext( + static_cast<TabImpl*>(shell()->tab()) + ->web_contents() + ->GetBrowserContext()); + EXPECT_EQ(settings_map->GetContentSetting(dse_origin, dse_origin, + ContentSettingsType::GEOLOCATION, + std::string()), + CONTENT_SETTING_ALLOW); + EXPECT_EQ(settings_map->GetContentSetting(foo_origin, foo_origin, + ContentSettingsType::GEOLOCATION, + std::string()), + CONTENT_SETTING_ASK); + + settings_map->SetContentSettingDefaultScope( + foo_origin, foo_origin, ContentSettingsType::GEOLOCATION, std::string(), + CONTENT_SETTING_ALLOW); + + // Ensure clearing things other than site data doesn't change it + base::RunLoop run_loop; + base::Time now = base::Time::Now(); + ProfileImpl* profile = static_cast<TabImpl*>(shell()->tab())->profile(); + profile->ClearBrowsingData( + {BrowsingDataType::COOKIES_AND_SITE_DATA, BrowsingDataType::CACHE}, + base::Time(), now, run_loop.QuitClosure()); + run_loop.Run(); + + EXPECT_EQ(settings_map->GetContentSetting(dse_origin, dse_origin, + ContentSettingsType::GEOLOCATION, + std::string()), + CONTENT_SETTING_ALLOW); + + EXPECT_EQ(settings_map->GetContentSetting(foo_origin, foo_origin, + ContentSettingsType::GEOLOCATION, + std::string()), + CONTENT_SETTING_ALLOW); + + // Now clear site data. + base::RunLoop run_loop2; + profile->ClearBrowsingData({BrowsingDataType::SITE_SETTINGS}, base::Time(), + now, run_loop2.QuitClosure()); + run_loop2.Run(); + + EXPECT_EQ(settings_map->GetContentSetting(dse_origin, dse_origin, + ContentSettingsType::GEOLOCATION, + std::string()), + CONTENT_SETTING_ALLOW); + EXPECT_EQ(settings_map->GetContentSetting(foo_origin, foo_origin, + ContentSettingsType::GEOLOCATION, + std::string()), + CONTENT_SETTING_ASK); +} + } // namespace weblayer diff --git a/chromium/weblayer/browser/profile_disk_operations.cc b/chromium/weblayer/browser/profile_disk_operations.cc index 736c102a8a3..874a3f30625 100644 --- a/chromium/weblayer/browser/profile_disk_operations.cc +++ b/chromium/weblayer/browser/profile_disk_operations.cc @@ -117,14 +117,14 @@ void MarkProfileAsDeleted(const ProfileInfo& info) { void TryNukeProfileFromDisk(const ProfileInfo& info) { if (info.name.empty()) { // Incognito. Just delete session data. - base::DeleteFileRecursively(ComputeBrowserPersisterDataBaseDir(info)); + base::DeletePathRecursively(ComputeBrowserPersisterDataBaseDir(info)); return; } // This may fail, but that is ok since the marker is not deleted. - base::DeleteFileRecursively(info.data_path); + base::DeletePathRecursively(info.data_path); #if defined(OS_POSIX) - base::DeleteFileRecursively(info.cache_path); + base::DeletePathRecursively(info.cache_path); #endif } @@ -155,13 +155,13 @@ void NukeProfilesMarkedForDeletion() { bool delete_success = true; #if defined(OS_POSIX) delete_success |= - base::DeleteFileRecursively(GetCachePathFromDirName(dir_name)); + base::DeletePathRecursively(GetCachePathFromDirName(dir_name)); #endif // OS_POSIX delete_success |= - base::DeleteFileRecursively(GetDataPathFromDirName(dir_name)); + base::DeletePathRecursively(GetDataPathFromDirName(dir_name)); // Only delete the marker if deletion is successful. if (delete_success) { - base::DeleteFile(marker_path, /*recursive=*/false); + base::DeleteFile(marker_path); } } } diff --git a/chromium/weblayer/browser/profile_impl.cc b/chromium/weblayer/browser/profile_impl.cc index 3cc8cc635af..5595d173216 100644 --- a/chromium/weblayer/browser/profile_impl.cc +++ b/chromium/weblayer/browser/profile_impl.cc @@ -18,6 +18,7 @@ #include "base/task/thread_pool.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/prefs/pref_service.h" #include "components/web_cache/browser/web_cache_manager.h" #include "content/public/browser/browser_task_traits.h" @@ -27,13 +28,18 @@ #include "content/public/browser/download_manager.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/storage_partition.h" +#include "content/public/browser/web_contents.h" #include "services/network/public/mojom/network_context.mojom.h" +#include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_skia.h" #include "weblayer/browser/android/metrics/weblayer_metrics_service_client.h" #include "weblayer/browser/browser_context_impl.h" #include "weblayer/browser/browser_impl.h" #include "weblayer/browser/browser_list.h" #include "weblayer/browser/browsing_data_remover_delegate.h" #include "weblayer/browser/cookie_manager_impl.h" +#include "weblayer/browser/favicon/favicon_service_impl.h" +#include "weblayer/browser/favicon/favicon_service_impl_factory.h" #include "weblayer/browser/persistence/browser_persister_file_utils.h" #include "weblayer/browser/tab_impl.h" @@ -44,6 +50,7 @@ #include "base/android/scoped_java_ref.h" #include "components/safe_browsing/core/common/safe_browsing_prefs.h" #include "components/unified_consent/pref_names.h" +#include "ui/gfx/android/java_bitmap.h" #include "weblayer/browser/browser_process.h" #include "weblayer/browser/java/jni/ProfileImpl_jni.h" #include "weblayer/browser/safe_browsing/safe_browsing_service.h" @@ -111,6 +118,14 @@ void OnDidRemoveBrowserPersistenceStorage( base::android::RunBooleanCallbackAndroid(callback, result); } +void OnDidGetCachedFaviconForPageUrl( + const base::android::ScopedJavaGlobalRef<jobject>& callback, + gfx::Image image) { + SkBitmap favicon = image.AsImageSkia().GetRepresentation(1.0f).GetBitmap(); + base::android::RunObjectCallbackAndroid( + callback, favicon.empty() ? nullptr : gfx::ConvertToJavaBitmap(&favicon)); +} + #endif // OS_ANDROID } // namespace @@ -125,22 +140,30 @@ class ProfileImpl::DataClearer : public content::BrowsingDataRemover::Observer { remover_->AddObserver(this); } - ~DataClearer() override { remover_->RemoveObserver(this); } - - void ClearData(uint64_t mask, base::Time from_time, base::Time to_time) { + void ClearData(ProfileImpl* profile, + uint64_t mask, + base::Time from_time, + base::Time to_time) { uint64_t origin_types = content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB | content::BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB; remover_->RemoveAndReply(from_time, to_time, mask, origin_types, this); } - void OnBrowsingDataRemoverDone() override { + // content::BrowsingDataRemover::Observer: + void OnBrowsingDataRemoverDone(uint64_t failed_data_types) override { + // Remove the observer now as after this returns the BrowserContext may + // be destroyed, which owns |remover_|. + remover_->RemoveObserver(this); std::move(callback_).Run(); delete this; } private: - content::BrowsingDataRemover* const remover_; + // DataClearer deletes itself when removal is done. + ~DataClearer() override = default; + + content::BrowsingDataRemover* remover_; base::OnceCallback<void()> callback_; }; @@ -178,8 +201,16 @@ ProfileImpl::ProfileImpl(const std::string& name) } ProfileImpl::~ProfileImpl() { - if (browser_context_) + // Destroy any scheduled WebContents. These implicitly refer to the + // BrowserContext and must be destroyed before the BrowserContext. + web_contents_to_delete_.clear(); + + if (browser_context_) { + BrowserContextDependencyManager::GetInstance() + ->DestroyBrowserContextServices(browser_context_.get()); browser_context_->ShutdownStoragePartitions(); + } + GetProfiles().erase(this); for (auto& observer : GetObservers()) observer.ProfileDestroyed(this); @@ -202,6 +233,16 @@ void ProfileImpl::RemoveProfileObserver(ProfileObserver* observer) { GetObservers().RemoveObserver(observer); } +void ProfileImpl::DeleteWebContentsSoon( + std::unique_ptr<content::WebContents> web_contents) { + if (web_contents_to_delete_.empty()) { + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&ProfileImpl::DeleteScheduleWebContents, + weak_ptr_factory_.GetWeakPtr())); + } + web_contents_to_delete_.push_back(std::move(web_contents)); +} + BrowserContextImpl* ProfileImpl::GetBrowserContext() { if (browser_context_) return browser_context_.get(); @@ -241,16 +282,22 @@ void ProfileImpl::ClearBrowsingData( remove_mask |= content::BrowsingDataRemover::DATA_TYPE_DOM_STORAGE; remove_mask |= content::BrowsingDataRemover::DATA_TYPE_MEDIA_LICENSES; remove_mask |= BrowsingDataRemoverDelegate::DATA_TYPE_ISOLATED_ORIGINS; + remove_mask |= BrowsingDataRemoverDelegate::DATA_TYPE_FAVICONS; + remove_mask |= content::BrowsingDataRemover::DATA_TYPE_TRUST_TOKENS; + remove_mask |= content::BrowsingDataRemover::DATA_TYPE_CONVERSIONS; break; case BrowsingDataType::CACHE: remove_mask |= content::BrowsingDataRemover::DATA_TYPE_CACHE; ClearRendererCache(); break; + case BrowsingDataType::SITE_SETTINGS: + remove_mask |= BrowsingDataRemoverDelegate::DATA_TYPE_SITE_SETTINGS; + break; default: NOTREACHED(); } } - clearer->ClearData(remove_mask, from_time, to_time); + clearer->ClearData(this, remove_mask, from_time, to_time); } void ProfileImpl::SetDownloadDirectory(const base::FilePath& directory) { @@ -367,14 +414,14 @@ void ProfileImpl::OnProfileMarked(std::unique_ptr<ProfileImpl> profile, // Try to finish all writes and remove all data before nuking the profile. profile->GetBrowserContext()->pref_service()->CommitPendingWrite(); - // Unretained is safe here because DataClearer is owned by - // BrowserContextImpl which is owned by this. + ProfileImpl* raw_profile = profile.get(); auto* clearer = new DataClearer( profile->GetBrowserContext(), base::BindOnce(&ProfileImpl::NukeDataAfterRemovingData, std::move(profile), std::move(done_callback))); uint64_t remove_all_mask = 0xffffffffffffffffull; - clearer->ClearData(remove_all_mask, base::Time::Min(), base::Time::Max()); + clearer->ClearData(raw_profile, remove_all_mask, base::Time::Min(), + base::Time::Max()); } #if defined(OS_ANDROID) @@ -440,9 +487,8 @@ void ProfileImpl::ClearBrowsingData( base::android::JavaIntArrayToIntVector(env, j_data_types, &data_type_ints); std::vector<BrowsingDataType> data_types; data_types.reserve(data_type_ints.size()); - for (int type : data_type_ints) { + for (int type : data_type_ints) data_types.push_back(static_cast<BrowsingDataType>(type)); - } ClearBrowsingData( data_types, base::Time::FromJavaTime(static_cast<int64_t>(j_from_time_millis)), @@ -502,6 +548,16 @@ void ProfileImpl::PrepareForPossibleCrossOriginNavigation(JNIEnv* env) { PrepareForPossibleCrossOriginNavigation(); } +void ProfileImpl::GetCachedFaviconForPageUrl( + JNIEnv* env, + const base::android::JavaRef<jstring>& j_page_url, + const base::android::JavaRef<jobject>& j_callback) { + GetCachedFaviconForPageUrl( + GURL(base::android::ConvertJavaStringToUTF8(j_page_url)), + base::BindOnce(&OnDidGetCachedFaviconForPageUrl, + base::android::ScopedJavaGlobalRef<jobject>(j_callback))); +} + #endif // OS_ANDROID base::FilePath ProfileImpl::GetBrowserPersisterDataBaseDir() const { @@ -513,8 +569,9 @@ void ProfileImpl::SetBooleanSetting(SettingType type, bool value) { switch (type) { case SettingType::BASIC_SAFE_BROWSING_ENABLED: #if defined(OS_ANDROID) - pref_service->SetBoolean(::prefs::kSafeBrowsingEnabled, value); - pref_service->SetBoolean(::prefs::kSafeBrowsingEnhanced, false); + safe_browsing::SetSafeBrowsingState( + pref_service, value ? safe_browsing::STANDARD_PROTECTION + : safe_browsing::NO_SAFE_BROWSING); #endif break; case SettingType::UKM_ENABLED: { @@ -542,6 +599,8 @@ void ProfileImpl::SetBooleanSetting(SettingType type, bool value) { value); #endif break; + case SettingType::NETWORK_PREDICTION_ENABLED: + pref_service->SetBoolean(prefs::kNoStatePrefetchEnabled, value); } } @@ -567,10 +626,26 @@ bool ProfileImpl::GetBooleanSetting(SettingType type) { unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled); #endif return false; + case SettingType::NETWORK_PREDICTION_ENABLED: + return pref_service->GetBoolean(prefs::kNoStatePrefetchEnabled); } NOTREACHED(); } +void ProfileImpl::GetCachedFaviconForPageUrl( + const GURL& page_url, + base::OnceCallback<void(gfx::Image)> callback) { + auto* service = + FaviconServiceImplFactory::GetForBrowserContext(GetBrowserContext()); + if (!service) { + std::move(callback).Run({}); + return; + } + + service->GetFaviconForPageUrl(page_url, std::move(callback), + &cancelable_task_tracker_); +} + void ProfileImpl::PrepareForPossibleCrossOriginNavigation() { content::RenderProcessHost::WarmupSpareRenderProcessHost(GetBrowserContext()); } @@ -581,4 +656,8 @@ int ProfileImpl::GetNumberOfBrowsers() { [this](BrowserImpl* b) { return b->profile() == this; }); } +void ProfileImpl::DeleteScheduleWebContents() { + web_contents_to_delete_.clear(); +} + } // namespace weblayer diff --git a/chromium/weblayer/browser/profile_impl.h b/chromium/weblayer/browser/profile_impl.h index d845d6bc646..5707d850ac9 100644 --- a/chromium/weblayer/browser/profile_impl.h +++ b/chromium/weblayer/browser/profile_impl.h @@ -6,10 +6,12 @@ #define WEBLAYER_BROWSER_PROFILE_IMPL_H_ #include <set> +#include <vector> #include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "base/task/cancelable_task_tracker.h" #include "build/build_config.h" #include "weblayer/browser/i18n_util.h" #include "weblayer/browser/profile_disk_operations.h" @@ -22,6 +24,7 @@ namespace content { class BrowserContext; +class WebContents; } namespace weblayer { @@ -63,6 +66,11 @@ class ProfileImpl : public Profile { static void AddProfileObserver(ProfileObserver* observer); static void RemoveProfileObserver(ProfileObserver* observer); + // Deletes |web_contents| after a delay. This is used if the owning Tab is + // deleted and it's not safe to delete the WebContents. + void DeleteWebContentsSoon( + std::unique_ptr<content::WebContents> web_contents); + BrowserContextImpl* GetBrowserContext(); // Called when the download subsystem has finished initializing. By this point @@ -90,6 +98,9 @@ class ProfileImpl : public Profile { base::flat_set<std::string> ids) override; void SetBooleanSetting(SettingType type, bool value) override; bool GetBooleanSetting(SettingType type) override; + void GetCachedFaviconForPageUrl( + const GURL& page_url, + base::OnceCallback<void(gfx::Image)> callback) override; void PrepareForPossibleCrossOriginNavigation() override; #if defined(OS_ANDROID) @@ -123,6 +134,10 @@ class ProfileImpl : public Profile { const base::android::JavaRef<jobjectArray>& j_ids, const base::android::JavaRef<jobject>& j_callback); void PrepareForPossibleCrossOriginNavigation(JNIEnv* env); + void GetCachedFaviconForPageUrl( + JNIEnv* env, + const base::android::JavaRef<jstring>& j_page_url, + const base::android::JavaRef<jobject>& j_callback); #endif const base::FilePath& download_directory() { return download_directory_; } @@ -148,6 +163,8 @@ class ProfileImpl : public Profile { // Returns the number of Browsers with this profile. int GetNumberOfBrowsers(); + void DeleteScheduleWebContents(); + ProfileInfo info_; std::unique_ptr<BrowserContextImpl> browser_context_; @@ -164,6 +181,16 @@ class ProfileImpl : public Profile { base::android::ScopedJavaGlobalRef<jobject> java_profile_; #endif + // The typical pattern for CancelableTaskTrackers is to have the caller + // supply one. This code is predominantly called from the Java side, where + // CancelableTaskTracker isn't applicable. Because of this, the + // CancelableTaskTracker is owned by Profile. + base::CancelableTaskTracker cancelable_task_tracker_; + + std::vector<std::unique_ptr<content::WebContents>> web_contents_to_delete_; + + base::WeakPtrFactory<ProfileImpl> weak_ptr_factory_{this}; + DISALLOW_COPY_AND_ASSIGN(ProfileImpl); }; diff --git a/chromium/weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.cc b/chromium/weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.cc index d3bee53a080..fe630ad4e39 100644 --- a/chromium/weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.cc +++ b/chromium/weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.cc @@ -13,7 +13,6 @@ #include "weblayer/browser/browser_process.h" #include "weblayer/browser/feature_list_creator.h" #include "weblayer/browser/safe_browsing/safe_browsing_service.h" -#include "weblayer/browser/user_agent.h" #include "weblayer/browser/verdict_cache_manager_factory.h" namespace weblayer { @@ -43,7 +42,7 @@ KeyedService* RealTimeUrlLookupServiceFactory::BuildServiceInstanceFor( auto url_loader_factory = std::make_unique<network::CrossThreadPendingSharedURLLoaderFactory>( BrowserProcess::GetInstance() - ->GetSafeBrowsingService(weblayer::GetUserAgent()) + ->GetSafeBrowsingService() ->GetURLLoaderFactory()); return new safe_browsing::RealTimeUrlLookupService( diff --git a/chromium/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc b/chromium/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc index 77975384186..0af1ccd68e7 100644 --- a/chromium/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc +++ b/chromium/weblayer/browser/safe_browsing/safe_browsing_browsertest.cc @@ -286,4 +286,4 @@ IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest, safe_browsing::SB_THREAT_TYPE_URL_MALWARE, false); } -} // namespace weblayer
\ No newline at end of file +} // namespace weblayer diff --git a/chromium/weblayer/browser/safe_browsing/safe_browsing_service.cc b/chromium/weblayer/browser/safe_browsing/safe_browsing_service.cc index fca167feed4..632d2f84d53 100644 --- a/chromium/weblayer/browser/safe_browsing/safe_browsing_service.cc +++ b/chromium/weblayer/browser/safe_browsing/safe_browsing_service.cc @@ -228,6 +228,12 @@ void SafeBrowsingService::StopDBManagerOnIOThread() { } } +network::mojom::NetworkContext* SafeBrowsingService::GetNetworkContext() { + if (!network_context_) + return nullptr; + return network_context_->GetNetworkContext(); +} + scoped_refptr<network::SharedURLLoaderFactory> SafeBrowsingService::GetURLLoaderFactory() { if (!network_context_) diff --git a/chromium/weblayer/browser/safe_browsing/safe_browsing_service.h b/chromium/weblayer/browser/safe_browsing/safe_browsing_service.h index 1de5c0ff021..129a0ea1234 100644 --- a/chromium/weblayer/browser/safe_browsing/safe_browsing_service.h +++ b/chromium/weblayer/browser/safe_browsing/safe_browsing_service.h @@ -24,6 +24,9 @@ class URLLoaderThrottle; } namespace network { +namespace mojom { +class NetworkContext; +} class SharedURLLoaderFactory; } @@ -43,7 +46,7 @@ class UrlCheckerDelegateImpl; // support for initialization and construction of these objects. class SafeBrowsingService { public: - SafeBrowsingService(const std::string& user_agent); + explicit SafeBrowsingService(const std::string& user_agent); ~SafeBrowsingService(); // Executed on UI thread @@ -57,6 +60,9 @@ class SafeBrowsingService { void AddInterface(service_manager::BinderRegistry* registry, content::RenderProcessHost* render_process_host); void StopDBManager(); + + network::mojom::NetworkContext* GetNetworkContext(); + scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory(); private: diff --git a/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.cc b/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.cc index a616dfef1b7..e55fde61de1 100644 --- a/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.cc +++ b/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.cc @@ -32,12 +32,12 @@ SafeBrowsingUIManager::SafeBrowsingUIManager( SafeBrowsingUIManager::~SafeBrowsingUIManager() = default; void SafeBrowsingUIManager::SendSerializedThreatDetails( + content::BrowserContext* browser_context, const std::string& serialized) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!ping_manager_) { ping_manager_ = ::safe_browsing::PingManager::Create( - safe_browsing_service_->GetURLLoaderFactory(), safe_browsing::GetV4ProtocolConfig(GetProtocolConfigClientName(), false /* auto_update */)); } @@ -46,7 +46,8 @@ void SafeBrowsingUIManager::SendSerializedThreatDetails( return; DVLOG(1) << "Sending serialized threat details"; - ping_manager_->ReportThreatDetails(serialized); + ping_manager_->ReportThreatDetails( + safe_browsing_service_->GetURLLoaderFactory(), serialized); } safe_browsing::BaseBlockingPage* diff --git a/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h b/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h index 989183c9c02..1fb00df3eb8 100644 --- a/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h +++ b/chromium/weblayer/browser/safe_browsing/safe_browsing_ui_manager.h @@ -29,7 +29,8 @@ class SafeBrowsingUIManager : public safe_browsing::BaseUIManager { // Called on the UI thread by the ThreatDetails with the serialized // protocol buffer, so the service can send it over. - void SendSerializedThreatDetails(const std::string& serialized) override; + void SendSerializedThreatDetails(content::BrowserContext* browser_context, + const std::string& serialized) override; protected: ~SafeBrowsingUIManager() override; @@ -50,4 +51,4 @@ class SafeBrowsingUIManager : public safe_browsing::BaseUIManager { } // namespace weblayer -#endif // WEBLAYER_BROWSER_SAFE_BROWSING_SAFE_BROWSING_UI_MANAGER_H_
\ No newline at end of file +#endif // WEBLAYER_BROWSER_SAFE_BROWSING_SAFE_BROWSING_UI_MANAGER_H_ diff --git a/chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc b/chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc index 7ff46a3158c..3287dc72f45 100644 --- a/chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc +++ b/chromium/weblayer/browser/safe_browsing/url_checker_delegate_impl.cc @@ -5,14 +5,31 @@ #include "weblayer/browser/safe_browsing/url_checker_delegate_impl.h" #include "base/bind.h" +#include "components/prerender/browser/prerender_manager.h" #include "components/safe_browsing/core/db/database_manager.h" #include "components/security_interstitials/core/unsafe_resource.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" +#include "weblayer/browser/no_state_prefetch/prerender_manager_factory.h" +#include "weblayer/browser/no_state_prefetch/prerender_utils.h" #include "weblayer/browser/safe_browsing/safe_browsing_ui_manager.h" namespace weblayer { +namespace { + +// Destroys the prerender contents associated with the web_contents, if any. +void DestroyPrerenderContents( + content::WebContents::OnceGetter web_contents_getter) { + content::WebContents* web_contents = std::move(web_contents_getter).Run(); + + auto* prerender_contents = PrerenderContentsFromWebContents(web_contents); + if (prerender_contents) + prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING); +} + +} // namespace + UrlCheckerDelegateImpl::UrlCheckerDelegateImpl( scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager, scoped_refptr<SafeBrowsingUIManager> ui_manager) @@ -27,7 +44,12 @@ UrlCheckerDelegateImpl::UrlCheckerDelegateImpl( UrlCheckerDelegateImpl::~UrlCheckerDelegateImpl() = default; void UrlCheckerDelegateImpl::MaybeDestroyPrerenderContents( - content::WebContents::OnceGetter web_contents_getter) {} + content::WebContents::OnceGetter web_contents_getter) { + // Destroy the prefetch with FINAL_STATUS_SAFEBROSWING. + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&DestroyPrerenderContents, + std::move(web_contents_getter))); +} void UrlCheckerDelegateImpl::StartDisplayingBlockingPageHelper( const security_interstitials::UnsafeResource& resource, diff --git a/chromium/weblayer/browser/signin_url_loader_throttle.cc b/chromium/weblayer/browser/signin_url_loader_throttle.cc new file mode 100644 index 00000000000..7758529a650 --- /dev/null +++ b/chromium/weblayer/browser/signin_url_loader_throttle.cc @@ -0,0 +1,192 @@ +// 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 "weblayer/browser/signin_url_loader_throttle.h" + +#include "base/task/post_task.h" +#include "components/content_settings/core/browser/cookie_settings.h" +#include "components/signin/core/browser/signin_header_helper.h" +#include "components/signin/public/base/account_consistency_method.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_task_traits.h" +#include "google_apis/gaia/gaia_auth_util.h" +#include "net/base/url_util.h" +#include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h" +#include "weblayer/browser/cookie_settings_factory.h" +#include "weblayer/browser/tab_impl.h" +#include "weblayer/public/google_accounts_delegate.h" + +namespace weblayer { + +const char kSignOutPath[] = "/SignOutOptions"; + +namespace { +constexpr char kWebLayerMirrorHeaderSource[] = "WebLayer"; + +GoogleAccountsDelegate* GetDelegate( + const content::WebContents::Getter& web_contents_getter) { + auto* web_contents = web_contents_getter.Run(); + if (!web_contents) + return nullptr; + + auto* tab = TabImpl::FromWebContents(web_contents); + if (!tab) + return nullptr; + + return tab->google_accounts_delegate(); +} + +void ProcessMirrorHeader(content::WebContents::Getter web_contents_getter, + const signin::ManageAccountsParams& params) { + auto* delegate = GetDelegate(web_contents_getter); + if (delegate) + delegate->OnGoogleAccountsRequest(params); +} + +void MaybeAddQueryParams(GURL* url) { + // Add manage=true to query parameters for sign out URLs to make sure we + // receive the Mirror response headers instead of the normal sign out page. + if (gaia::IsGaiaSignonRealm(url->GetOrigin()) && + url->path_piece() == kSignOutPath) { + *url = net::AppendOrReplaceQueryParameter(*url, "manage", "true"); + } +} +} // namespace + +SigninURLLoaderThrottle::~SigninURLLoaderThrottle() = default; + +// static +std::unique_ptr<SigninURLLoaderThrottle> SigninURLLoaderThrottle::Create( + content::BrowserContext* browser_context, + content::WebContents::Getter web_contents_getter) { + if (browser_context->IsOffTheRecord() || !GetDelegate(web_contents_getter)) + return nullptr; + + // Use base::WrapUnique + new because of the constructor is private. + return base::WrapUnique(new SigninURLLoaderThrottle( + browser_context, std::move(web_contents_getter))); +} + +void SigninURLLoaderThrottle::WillStartRequest( + network::ResourceRequest* request, + bool* defer) { + GoogleAccountsDelegate* delegate = GetDelegate(web_contents_getter_); + if (!delegate) + return; + + MaybeAddQueryParams(&request->url); + + request_url_ = request->url; + is_main_frame_ = + static_cast<blink::mojom::ResourceType>(request->resource_type) == + blink::mojom::ResourceType::kMainFrame; + + net::HttpRequestHeaders modified_request_headers; + std::vector<std::string> to_be_removed_request_headers; + ProcessRequest(GURL(), &request->headers, &to_be_removed_request_headers, + &modified_request_headers); + signin::RequestAdapter adapter(request_url_, request->headers, + &modified_request_headers, + &to_be_removed_request_headers); + request_headers_.CopyFrom(request->headers); +} + +void SigninURLLoaderThrottle::WillRedirectRequest( + net::RedirectInfo* redirect_info, + const network::mojom::URLResponseHead& response_head, + bool* defer, + std::vector<std::string>* headers_to_remove, + net::HttpRequestHeaders* modified_headers, + net::HttpRequestHeaders* modified_cors_exempt_request_headers) { + if (!GetDelegate(web_contents_getter_)) + return; + + MaybeAddQueryParams(&redirect_info->new_url); + + ProcessRequest(redirect_info->new_url, &request_headers_, headers_to_remove, + modified_headers); + ProcessResponse(response_head.headers.get()); + + request_url_ = redirect_info->new_url; +} + +void SigninURLLoaderThrottle::WillProcessResponse( + const GURL& response_url, + network::mojom::URLResponseHead* response_head, + bool* defer) { + if (!GetDelegate(web_contents_getter_)) + return; + + ProcessResponse(response_head->headers.get()); +} + +SigninURLLoaderThrottle::SigninURLLoaderThrottle( + content::BrowserContext* browser_context, + content::WebContents::Getter web_contents_getter) + : browser_context_(browser_context), + web_contents_getter_(std::move(web_contents_getter)) {} + +void SigninURLLoaderThrottle::ProcessRequest( + const GURL& new_url, + net::HttpRequestHeaders* original_headers, + std::vector<std::string>* headers_to_remove, + net::HttpRequestHeaders* modified_headers) { + GoogleAccountsDelegate* delegate = GetDelegate(web_contents_getter_); + if (!delegate) + return; + + signin::RequestAdapter request_adapter(request_url_, *original_headers, + modified_headers, headers_to_remove); + // Disable incognito and adding accounts for now. This shouldn't matter in + // practice though since we are skipping the /SignOutOptions page completely + // with the manage=true param. + signin::AppendOrRemoveMirrorRequestHeader( + &request_adapter, new_url, delegate->GetGaiaId(), + signin::AccountConsistencyMethod::kMirror, + CookieSettingsFactory::GetForBrowserContext(browser_context_).get(), + signin::PROFILE_MODE_INCOGNITO_DISABLED | + signin::PROFILE_MODE_ADD_ACCOUNT_DISABLED, + kWebLayerMirrorHeaderSource, true /* force_account_consistency */); + + original_headers->MergeFrom(*modified_headers); + for (const std::string& name : *headers_to_remove) + original_headers->RemoveHeader(name); +} + +void SigninURLLoaderThrottle::ProcessResponse( + const net::HttpResponseHeaders* headers) { + if (!gaia::IsGaiaSignonRealm(request_url_.GetOrigin()) || !is_main_frame_ || + !headers) { + return; + } + + std::string header_value; + if (!headers->GetNormalizedHeader(signin::kChromeManageAccountsHeader, + &header_value)) { + return; + } + + signin::ManageAccountsParams params = + signin::BuildManageAccountsParams(header_value); + if (params.service_type == signin::GAIA_SERVICE_TYPE_NONE) + return; + + // Only process one mirror header per request (multiple headers on the same + // redirect chain are ignored). + if (response_header_processed_) { + LOG(ERROR) << "Multiple X-Chrome-Manage-Accounts headers on a redirect " + << "chain, ignoring"; + return; + } + + response_header_processed_ = true; + + // Post a task even if we are already on the UI thread to avoid making any + // requests while processing a throttle event. + base::PostTask( + FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&ProcessMirrorHeader, web_contents_getter_, params)); +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/signin_url_loader_throttle.h b/chromium/weblayer/browser/signin_url_loader_throttle.h new file mode 100644 index 00000000000..118e46e9751 --- /dev/null +++ b/chromium/weblayer/browser/signin_url_loader_throttle.h @@ -0,0 +1,61 @@ +// 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 WEBLAYER_BROWSER_SIGNIN_URL_LOADER_THROTTLE_H_ +#define WEBLAYER_BROWSER_SIGNIN_URL_LOADER_THROTTLE_H_ + +#include "components/signin/core/browser/signin_header_helper.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/web_contents.h" +#include "net/http/http_request_headers.h" +#include "third_party/blink/public/common/loader/url_loader_throttle.h" + +namespace weblayer { + +// Exposed for testing. +extern const char kSignOutPath[]; + +class SigninURLLoaderThrottle : public blink::URLLoaderThrottle { + public: + ~SigninURLLoaderThrottle() override; + + static std::unique_ptr<SigninURLLoaderThrottle> Create( + content::BrowserContext* browser_context, + content::WebContents::Getter web_contents_getter); + + // blink::URLLoaderThrottle + void WillStartRequest(network::ResourceRequest* request, + bool* defer) override; + void WillRedirectRequest( + net::RedirectInfo* redirect_info, + const network::mojom::URLResponseHead& response_head, + bool* defer, + std::vector<std::string>* headers_to_remove, + net::HttpRequestHeaders* modified_headers, + net::HttpRequestHeaders* modified_cors_exempt_request_headers) override; + void WillProcessResponse(const GURL& response_url, + network::mojom::URLResponseHead* response_head, + bool* defer) override; + + private: + SigninURLLoaderThrottle(content::BrowserContext* browser_context, + content::WebContents::Getter web_contents_getter); + + void ProcessRequest(const GURL& url, + net::HttpRequestHeaders* original_headers, + std::vector<std::string>* headers_to_remove, + net::HttpRequestHeaders* modified_headers); + void ProcessResponse(const net::HttpResponseHeaders* headers); + + content::BrowserContext* browser_context_; + content::WebContents::Getter web_contents_getter_; + net::HttpRequestHeaders request_headers_; + GURL request_url_; + bool is_main_frame_ = false; + bool response_header_processed_ = false; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_SIGNIN_URL_LOADER_THROTTLE_H_ diff --git a/chromium/weblayer/browser/ssl_browsertest.cc b/chromium/weblayer/browser/ssl_browsertest.cc index 0ef9fa45322..4006e99088d 100644 --- a/chromium/weblayer/browser/ssl_browsertest.cc +++ b/chromium/weblayer/browser/ssl_browsertest.cc @@ -8,14 +8,22 @@ #include "base/macros.h" #include "base/optional.h" #include "base/scoped_observer.h" +#include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "components/network_time/network_time_tracker.h" +#include "components/security_interstitials/content/insecure_form_blocking_page.h" +#include "components/security_interstitials/content/ssl_error_assistant.h" #include "components/security_interstitials/content/ssl_error_handler.h" +#include "components/security_interstitials/core/features.h" +#include "net/ssl/ssl_info.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "weblayer/browser/browser_process.h" #include "weblayer/browser/weblayer_security_blocking_page_factory.h" #include "weblayer/public/browser.h" #include "weblayer/public/browser_observer.h" +#include "weblayer/public/error_page.h" +#include "weblayer/public/error_page_delegate.h" +#include "weblayer/public/tab.h" #include "weblayer/shell/browser/shell.h" #include "weblayer/test/interstitial_utils.h" #include "weblayer/test/load_completion_observer.h" @@ -53,6 +61,24 @@ class NewTabWaiter : public BrowserObserver { }; #endif +class TestErrorPageDelegate : public ErrorPageDelegate { + public: + bool was_get_error_page_content_called() const { + return was_get_error_page_content_called_; + } + + // ErrorPageDelegate: + bool OnBackToSafety() override { return false; } + std::unique_ptr<ErrorPage> GetErrorPageContent( + Navigation* navigation) override { + was_get_error_page_content_called_ = true; + return std::make_unique<ErrorPage>(); + } + + private: + bool was_get_error_page_content_called_ = false; +}; + } // namespace class SSLBrowserTest : public WebLayerBrowserTest { @@ -355,4 +381,95 @@ IN_PROC_BROWSER_TEST_F(SSLBrowserTest, BadClockInterstitial) { NavigateToPageWithExpiredCertExpectBadClockInterstitial(); } +// This test verifies that a certificate in the list of known captive portal +// certificates in ssl_error_assistant.asciipb is detected as such. This serves +// to verify that the ssl_error_assistant proto was correctly loaded. +IN_PROC_BROWSER_TEST_F(SSLBrowserTest, + CertificateInKnownCaptivePortalsListDetected) { + net::SSLInfo ssl_info_with_known_captive_portal_cert; + net::HashValue captive_portal_public_key; + + // Set up the SSSLInfo with the certificate of captive-portal.badssl.com + // (taken from ssl_error_assistant.asciipb). + ASSERT_TRUE(captive_portal_public_key.FromString( + "sha256/fjZPHewEHTrMDX3I1ecEIeoy3WFxHyGplOLv28kIbtI=")); + net::HashValueVector public_keys; + public_keys.push_back(captive_portal_public_key); + ssl_info_with_known_captive_portal_cert.public_key_hashes = public_keys; + + EXPECT_TRUE(SSLErrorAssistant().IsKnownCaptivePortalCertificate( + ssl_info_with_known_captive_portal_cert)); +} + +// Verifies an error page is not requested for an ssl error. +IN_PROC_BROWSER_TEST_F(SSLBrowserTest, ErrorPageNotCalledForMismatch) { + TestErrorPageDelegate error_page_delegate; + shell()->tab()->SetErrorPageDelegate(&error_page_delegate); + NavigateToOkPage(); + EXPECT_FALSE(error_page_delegate.was_get_error_page_content_called()); + NavigateToPageWithMismatchedCertExpectSSLInterstitial(); + EXPECT_FALSE(error_page_delegate.was_get_error_page_content_called()); +} + +class SSLBrowserTestWithInsecureFormsWarningEnabled : public SSLBrowserTest { + public: + SSLBrowserTestWithInsecureFormsWarningEnabled() { + feature_list_.InitAndEnableFeature( + security_interstitials::kInsecureFormSubmissionInterstitial); + } + + private: + base::test::ScopedFeatureList feature_list_; +}; + +// Visits a page that displays an insecure form, submits the form, and checks an +// interstitial is shown. +IN_PROC_BROWSER_TEST_F(SSLBrowserTestWithInsecureFormsWarningEnabled, + TestDisplaysInsecureFormSubmissionWarning) { + GURL insecure_form_url = https_server_->GetURL("/insecure_form.html"); + GURL form_target_url = GURL("http://does-not-exist.test/form_target.html?"); + NavigateAndWaitForCompletion(insecure_form_url, shell()); + + // Submit the form and wait for the interstitial to load. + TestNavigationObserver navigation_observer( + form_target_url, TestNavigationObserver::NavigationEvent::kFailure, + shell()); + ExecuteScript(shell(), "submitForm();", false /*use_separate_isolate*/); + navigation_observer.Wait(); + + // Check the correct interstitial loaded. + EXPECT_TRUE(IsShowingInsecureFormInterstitial(shell()->tab())); +} + +class SSLBrowserTestWithInsecureFormsWarningDisabled : public SSLBrowserTest { + public: + SSLBrowserTestWithInsecureFormsWarningDisabled() { + feature_list_.InitAndDisableFeature( + security_interstitials::kInsecureFormSubmissionInterstitial); + } + + private: + base::test::ScopedFeatureList feature_list_; +}; + +// Visits a page that displays an insecure form, submits the form, and checks no +// interstitial is displayed with the feature off. +IN_PROC_BROWSER_TEST_F(SSLBrowserTestWithInsecureFormsWarningDisabled, + TestNoInsecureFormWarning) { + GURL insecure_form_url = https_server_->GetURL("/insecure_form.html"); + GURL form_target_url = GURL("http://does-not-exist.test/form_target.html?"); + NavigateAndWaitForCompletion(insecure_form_url, shell()); + + // Submit the form and wait for the form target to load. We wait for a + // failure since the target url is not served. + TestNavigationObserver navigation_observer( + form_target_url, TestNavigationObserver::NavigationEvent::kFailure, + shell()); + ExecuteScript(shell(), "submitForm();", false /*use_separate_isolate*/); + navigation_observer.Wait(); + + // Check no interstitial loaded. + EXPECT_FALSE(IsShowingSecurityInterstitial(shell()->tab())); +} + } // namespace weblayer diff --git a/chromium/weblayer/browser/system_network_context_manager.h b/chromium/weblayer/browser/system_network_context_manager.h index 2bb7c434920..446553298c4 100644 --- a/chromium/weblayer/browser/system_network_context_manager.h +++ b/chromium/weblayer/browser/system_network_context_manager.h @@ -52,6 +52,10 @@ class SystemNetworkContextManager { // Returns a SharedURLLoaderFactory owned by the SystemNetworkContextManager // that is backed by the SystemNetworkContext. + // NOTE: This factory assumes that the network service is running in the + // browser process, which is a valid assumption for Android. If WebLayer is + // productionized beyond Android, it will need to be extended to handle + // network service crashes. scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory(); private: diff --git a/chromium/weblayer/browser/tab_impl.cc b/chromium/weblayer/browser/tab_impl.cc index 052dbcb9517..cc4bb136cd8 100644 --- a/chromium/weblayer/browser/tab_impl.cc +++ b/chromium/weblayer/browser/tab_impl.cc @@ -21,8 +21,7 @@ #include "components/blocked_content/popup_opener_tab_helper.h" #include "components/blocked_content/popup_tracker.h" #include "components/captive_portal/core/buildflags.h" -#include "components/client_hints/browser/client_hints.h" -#include "components/content_settings/browser/tab_specific_content_settings.h" +#include "components/content_settings/browser/page_specific_content_settings.h" #include "components/find_in_page/find_tab_helper.h" #include "components/find_in_page/find_types.h" #include "components/js_injection/browser/js_communication_host.h" @@ -55,19 +54,22 @@ #include "weblayer/browser/browser_impl.h" #include "weblayer/browser/browser_process.h" #include "weblayer/browser/content_browser_client_impl.h" +#include "weblayer/browser/favicon/favicon_fetcher_impl.h" +#include "weblayer/browser/favicon/favicon_tab_helper.h" #include "weblayer/browser/file_select_helper.h" #include "weblayer/browser/host_content_settings_map_factory.h" #include "weblayer/browser/i18n_util.h" #include "weblayer/browser/infobar_service.h" #include "weblayer/browser/js_communication/web_message_host_factory_wrapper.h" #include "weblayer/browser/navigation_controller_impl.h" +#include "weblayer/browser/no_state_prefetch/prerender_tab_helper.h" #include "weblayer/browser/page_load_metrics_initialize.h" +#include "weblayer/browser/page_specific_content_settings_delegate.h" #include "weblayer/browser/password_manager_driver_factory.h" #include "weblayer/browser/permissions/permission_manager_factory.h" #include "weblayer/browser/persistence/browser_persister.h" #include "weblayer/browser/popup_navigation_delegate_impl.h" #include "weblayer/browser/profile_impl.h" -#include "weblayer/browser/tab_specific_content_settings_delegate.h" #include "weblayer/browser/translate_client_impl.h" #include "weblayer/browser/weblayer_features.h" #include "weblayer/common/isolated_world_ids.h" @@ -88,9 +90,11 @@ #include "base/json/json_writer.h" #include "base/trace_event/trace_event.h" #include "components/autofill/android/provider/autofill_provider_android.h" +#include "components/browser_ui/sms/android/sms_infobar.h" #include "components/embedder_support/android/contextmenu/context_menu_builder.h" #include "components/embedder_support/android/delegate/color_chooser_android.h" #include "components/javascript_dialogs/tab_modal_dialog_manager.h" // nogncheck +#include "components/translate/core/browser/translate_manager.h" #include "ui/android/view_android.h" #include "ui/gfx/android/java_bitmap.h" #include "weblayer/browser/browser_controls_container_view.h" @@ -100,6 +104,7 @@ #include "weblayer/browser/java/jni/TabImpl_jni.h" #include "weblayer/browser/javascript_tab_modal_dialog_manager_delegate_android.h" #include "weblayer/browser/js_communication/web_message_host_factory_proxy.h" +#include "weblayer/browser/translate_client_impl.h" #include "weblayer/browser/weblayer_factory_impl_android.h" #include "weblayer/browser/webrtc/media_stream_manager.h" #endif @@ -242,8 +247,10 @@ static ScopedJavaLocalRef<jobject> JNI_TabImpl_FromWebContents( return nullptr; } -TabImpl::TabImpl(ProfileImpl* profile, const JavaParamRef<jobject>& java_impl) - : TabImpl(profile) { +TabImpl::TabImpl(ProfileImpl* profile, + const JavaParamRef<jobject>& java_impl, + std::unique_ptr<content::WebContents> web_contents) + : TabImpl(profile, std::move(web_contents)) { java_impl_ = java_impl; } #endif @@ -255,16 +262,15 @@ TabImpl::TabImpl(ProfileImpl* profile, web_contents_(std::move(web_contents)), guid_(guid.empty() ? base::GenerateGUID() : guid) { GetTabs().insert(this); - if (web_contents_) { - // This code path is hit when the page requests a new tab, which should - // only be possible from the same profile. - DCHECK_EQ(profile_->GetBrowserContext(), - web_contents_->GetBrowserContext()); - } else { - content::WebContents::CreateParams create_params( - profile_->GetBrowserContext()); - web_contents_ = content::WebContents::Create(create_params); - } + DCHECK(web_contents_); + // This code path is hit when the page requests a new tab, which should + // only be possible from the same profile. + DCHECK_EQ(profile_->GetBrowserContext(), web_contents_->GetBrowserContext()); + + // FaviconTabHelper adds a WebContentsObserver. Create FaviconTabHelper + // before |this| observes the WebContents to ensure favicons are reset before + // notifying weblayer observers of changes. + FaviconTabHelper::CreateForWebContents(web_contents_.get()); // By default renderer initiated navigations inherit the user-agent override // of the current NavigationEntry. For WebLayer, the user-agent override is @@ -297,21 +303,14 @@ TabImpl::TabImpl(ProfileImpl* profile, sessions::SessionTabHelper::CreateForWebContents( web_contents_.get(), - base::BindRepeating(&TabImpl::GetSessionServiceTabHelperDelegate, - base::Unretained(this))); + base::BindRepeating(&TabImpl::GetSessionServiceTabHelperDelegate)); permissions::PermissionRequestManager::CreateForWebContents( web_contents_.get()); - PrefService* local_state = BrowserProcess::GetInstance()->GetLocalState(); - client_hints::ClientHints::CreateForWebContents( + content_settings::PageSpecificContentSettings::CreateForWebContents( web_contents_.get(), - BrowserProcess::GetInstance()->GetNetworkQualityTracker(), - HostContentSettingsMapFactory::GetForBrowserContext( - web_contents_->GetBrowserContext()), - GetUserAgentMetadata(), local_state); - content_settings::TabSpecificContentSettings::CreateForWebContents( - web_contents_.get(), std::make_unique<TabSpecificContentSettingsDelegate>( - web_contents_.get())); + std::make_unique<PageSpecificContentSettingsDelegate>( + web_contents_.get())); blocked_content::PopupBlockerTabHelper::CreateForWebContents( web_contents_.get()); blocked_content::PopupOpenerTabHelper::CreateForWebContents( @@ -343,6 +342,9 @@ TabImpl::TabImpl(ProfileImpl* profile, base::BindRepeating(&OpenCaptivePortalLoginTabInWebContents, web_contents_.get())); #endif + + // PrerenderTabHelper adds a WebContentsObserver. + PrerenderTabHelper::CreateForWebContents(web_contents_.get()); } TabImpl::~TabImpl() { @@ -360,6 +362,18 @@ TabImpl::~TabImpl() { #endif Observe(nullptr); web_contents_->SetDelegate(nullptr); + if (navigation_controller_->should_delay_web_contents_deletion()) { + // Some user-data on WebContents directly or indirectly references this. + // Remove that linkage to avoid use-after-free. + web_contents_->RemoveUserData(&kWebContentsUserDataKey); + web_contents_->RemoveUserData( + autofill::ContentAutofillDriverFactory:: + kContentAutofillDriverFactoryWebContentsUserDataKey); + // Have Profile handle the task posting to ensure the WebContents is + // deleted before Profile. To do otherwise means it would be possible for + // the Profile to outlive the WebContents, which is problematic (crash). + profile_->DeleteWebContentsSoon(std::move(web_contents_)); + } web_contents_.reset(); GetTabs().erase(this); } @@ -407,15 +421,18 @@ void TabImpl::SetFullscreenDelegate(FullscreenDelegate* delegate) { // Whether fullscreen is enabled depends upon whether there is a delegate. If // having a delegate changed, then update the renderer (which is where // fullscreen enabled is tracked). - content::RenderViewHost* host = web_contents_->GetRenderViewHost(); - if (had_delegate != has_delegate && host) - host->OnWebkitPreferencesChanged(); + if (had_delegate != has_delegate) + web_contents_->OnWebPreferencesChanged(); } void TabImpl::SetNewTabDelegate(NewTabDelegate* delegate) { new_tab_delegate_ = delegate; } +void TabImpl::SetGoogleAccountsDelegate(GoogleAccountsDelegate* delegate) { + google_accounts_delegate_ = delegate; +} + void TabImpl::AddObserver(TabObserver* observer) { observers_.AddObserver(observer); } @@ -480,6 +497,11 @@ void TabImpl::ExecuteScriptWithUserGestureForTests( script); } +std::unique_ptr<FaviconFetcher> TabImpl::CreateFaviconFetcher( + FaviconFetcherDelegate* delegate) { + return std::make_unique<FaviconFetcherImpl>(web_contents_.get(), delegate); +} + #if !defined(OS_ANDROID) void TabImpl::AttachToView(views::WebView* web_view) { web_view->SetWebContents(web_contents_.get()); @@ -488,7 +510,7 @@ void TabImpl::AttachToView(views::WebView* web_view) { #endif void TabImpl::WebPreferencesChanged() { - web_contents_->GetRenderViewHost()->OnWebkitPreferencesChanged(); + web_contents_->OnWebPreferencesChanged(); } void TabImpl::SetWebPreferences(content::WebPreferences* prefs) { @@ -547,15 +569,20 @@ void TabImpl::DisableAutofillSystemIntegrationForTesting() { static jlong JNI_TabImpl_CreateTab(JNIEnv* env, jlong profile, const JavaParamRef<jobject>& java_impl) { - return reinterpret_cast<intptr_t>( - new TabImpl(reinterpret_cast<ProfileImpl*>(profile), java_impl)); + ProfileImpl* profile_impl = reinterpret_cast<ProfileImpl*>(profile); + content::WebContents::CreateParams create_params( + profile_impl->GetBrowserContext()); + create_params.initially_hidden = true; + return reinterpret_cast<intptr_t>(new TabImpl( + profile_impl, java_impl, content::WebContents::Create(create_params))); } static void JNI_TabImpl_DeleteTab(JNIEnv* env, jlong tab) { TabImpl* tab_impl = reinterpret_cast<TabImpl*>(tab); DCHECK(tab_impl); DCHECK(tab_impl->browser()); - tab_impl->browser()->DestroyTab(tab_impl); + // Don't call Browser::DestroyTab() as it calls back to the java side. + tab_impl->browser()->DestroyTabFromJava(tab_impl); } ScopedJavaLocalRef<jobject> TabImpl::GetWebContents(JNIEnv* env) { @@ -615,12 +642,15 @@ void TabImpl::OnAutofillProviderChanged( provider->OnJavaAutofillProviderChanged(env, autofill_provider); } -void TabImpl::UpdateBrowserControlsState(JNIEnv* env, - jint raw_new_state, - jboolean animate) { - UpdateBrowserControlsStateImpl( - static_cast<content::BrowserControlsState>(raw_new_state), - current_browser_controls_state_, animate); +void TabImpl::UpdateBrowserControlsConstraint(JNIEnv* env, + jint constraint, + jboolean animate) { + current_browser_controls_visibility_constraint_ = + static_cast<content::BrowserControlsState>(constraint); + // Passing BOTH here means that it doesn't matter what state the controls are + // currently in; don't change the current state unless it's incompatible with + // the new constraint. + UpdateBrowserControlsState(content::BROWSER_CONTROLS_STATE_BOTH, animate); } ScopedJavaLocalRef<jstring> TabImpl::GetGuid(JNIEnv* env) { @@ -671,15 +701,15 @@ TabImpl::ScreenShotErrors TabImpl::PrepareForCaptureScreenShot( return ScreenShotErrors::kNone; } -void TabImpl::UpdateBrowserControlsStateImpl( +void TabImpl::UpdateBrowserControlsState( content::BrowserControlsState new_state, - content::BrowserControlsState old_state, bool animate) { - current_browser_controls_state_ = new_state; if (base::FeatureList::IsEnabled(kImmediatelyHideBrowserControlsForTest)) animate = false; - web_contents_->GetMainFrame()->UpdateBrowserControlsState(new_state, - old_state, animate); + // The constraint is managed by Java code, so re-use the existing constraint + // and only update the desired state. + web_contents_->GetMainFrame()->UpdateBrowserControlsState( + current_browser_controls_visibility_constraint_, new_state, animate); } void TabImpl::CaptureScreenShot( @@ -777,6 +807,16 @@ void TabImpl::ShowTranslateUi(JNIEnv* env) { TranslateClientImpl::FromWebContents(web_contents()) ->ManualTranslateWhenReady(); } + +void TabImpl::SetTranslateTargetLanguage( + JNIEnv* env, + const base::android::JavaParamRef<jstring>& translate_target_lang) { + translate::TranslateManager* translate_manager = + TranslateClientImpl::FromWebContents(web_contents()) + ->GetTranslateManager(); + translate_manager->SetPredefinedTargetLanguage( + base::android::ConvertJavaStringToUTF8(env, translate_target_lang)); +} #endif // OS_ANDROID content::WebContents* TabImpl::OpenURLFromTab( @@ -870,9 +910,26 @@ content::ColorChooser* TabImpl::OpenColorChooser( #endif } +void TabImpl::CreateSmsPrompt(content::RenderFrameHost* render_frame_host, + const url::Origin& origin, + const std::string& one_time_code, + base::OnceClosure on_confirm, + base::OnceClosure on_cancel) { +#if defined(OS_ANDROID) + auto* web_contents = + content::WebContents::FromRenderFrameHost(render_frame_host); + sms::SmsInfoBar::Create( + web_contents, InfoBarService::FromWebContents(web_contents), + InfoBarService::GetResourceIdMapper(), origin, one_time_code, + std::move(on_confirm), std::move(on_cancel)); +#else + NOTREACHED(); +#endif +} + void TabImpl::RunFileChooser( content::RenderFrameHost* render_frame_host, - std::unique_ptr<content::FileSelectListener> listener, + scoped_refptr<content::FileSelectListener> listener, const blink::mojom::FileChooserParams& params) { FileSelectHelper::RunFileChooser(render_frame_host, std::move(listener), params); @@ -888,6 +945,16 @@ int TabImpl::GetTopControlsHeight() { #endif } +int TabImpl::GetTopControlsMinHeight() { +#if defined(OS_ANDROID) + return top_controls_container_view_ + ? top_controls_container_view_->GetMinHeight() + : 0; +#else + return 0; +#endif +} + int TabImpl::GetBottomControlsHeight() { #if defined(OS_ANDROID) return bottom_controls_container_view_ @@ -899,7 +966,7 @@ int TabImpl::GetBottomControlsHeight() { } bool TabImpl::DoBrowserControlsShrinkRendererSize( - const content::WebContents* web_contents) { + content::WebContents* web_contents) { #if defined(OS_ANDROID) TRACE_EVENT0("weblayer", "Java_TabImpl_doBrowserControlsShrinkRendererSize"); return Java_TabImpl_doBrowserControlsShrinkRendererSize(AttachCurrentThread(), @@ -909,6 +976,27 @@ bool TabImpl::DoBrowserControlsShrinkRendererSize( #endif } +bool TabImpl::ShouldAnimateBrowserControlsHeightChanges() { +#if defined(OS_ANDROID) + return top_controls_container_view_ + ? top_controls_container_view_ + ->ShouldAnimateBrowserControlsHeightChanges() + : false; +#else + return false; +#endif +} + +bool TabImpl::OnlyExpandTopControlsAtPageTop() { +#if defined(OS_ANDROID) + return top_controls_container_view_ + ? top_controls_container_view_->OnlyExpandControlsAtPageTop() + : false; +#else + return false; +#endif +} + bool TabImpl::EmbedsFullscreenWidget() { return true; } @@ -1097,19 +1185,18 @@ void TabImpl::OnUpdateBrowserControlsStateBecauseOfProcessSwitch( // This matches the logic of updateAfterRendererProcessSwitch() and // updateEnabledState() in Chrome's TabBrowserControlsConstraintsHelper. if (did_commit && - current_browser_controls_state_ == + current_browser_controls_visibility_constraint_ == content::BROWSER_CONTROLS_STATE_SHOWN && top_controls_container_view_ && top_controls_container_view_->IsFullyVisible()) { // The top-control is fully visible, don't animate this else the controls // bounce around. - UpdateBrowserControlsStateImpl(current_browser_controls_state_, - current_browser_controls_state_, false); + UpdateBrowserControlsState(content::BROWSER_CONTROLS_STATE_SHOWN, false); } else { - UpdateBrowserControlsStateImpl(current_browser_controls_state_, - content::BROWSER_CONTROLS_STATE_SHOWN, - current_browser_controls_state_ != - content::BROWSER_CONTROLS_STATE_HIDDEN); + UpdateBrowserControlsState( + content::BROWSER_CONTROLS_STATE_BOTH, + current_browser_controls_visibility_constraint_ != + content::BROWSER_CONTROLS_STATE_HIDDEN); } } @@ -1183,10 +1270,11 @@ find_in_page::FindTabHelper* TabImpl::GetFindTabHelper() { return find_in_page::FindTabHelper::FromWebContents(web_contents_.get()); } +// static sessions::SessionTabHelperDelegate* TabImpl::GetSessionServiceTabHelperDelegate( content::WebContents* web_contents) { - DCHECK_EQ(web_contents, web_contents_.get()); - return browser_ ? browser_->browser_persister() : nullptr; + TabImpl* tab = FromWebContents(web_contents); + return (tab && tab->browser_) ? tab->browser_->browser_persister() : nullptr; } bool TabImpl::SetDataInternal(const std::map<std::string, std::string>& data) { diff --git a/chromium/weblayer/browser/tab_impl.h b/chromium/weblayer/browser/tab_impl.h index a42739b4856..fb22911457b 100644 --- a/chromium/weblayer/browser/tab_impl.h +++ b/chromium/weblayer/browser/tab_impl.h @@ -99,10 +99,11 @@ class TabImpl : public Tab, // TODO(sky): investigate a better way to not have so many ifdefs. #if defined(OS_ANDROID) TabImpl(ProfileImpl* profile, - const base::android::JavaParamRef<jobject>& java_impl); + const base::android::JavaParamRef<jobject>& java_impl, + std::unique_ptr<content::WebContents> web_contents); #endif explicit TabImpl(ProfileImpl* profile, - std::unique_ptr<content::WebContents> = nullptr, + std::unique_ptr<content::WebContents> web_contents, const std::string& guid = std::string()); ~TabImpl() override; @@ -164,9 +165,9 @@ class TabImpl : public Tab, void OnAutofillProviderChanged( JNIEnv* env, const base::android::JavaParamRef<jobject>& autofill_provider); - void UpdateBrowserControlsState(JNIEnv* env, - jint raw_new_state, - jboolean animate); + void UpdateBrowserControlsConstraint(JNIEnv* env, + jint constraint, + jboolean animate); base::android::ScopedJavaLocalRef<jstring> GetGuid(JNIEnv* env); void CaptureScreenShot( @@ -192,6 +193,9 @@ class TabImpl : public Tab, const base::android::JavaParamRef<jstring>& js_object_name); jboolean CanTranslate(JNIEnv* env); void ShowTranslateUi(JNIEnv* env); + void SetTranslateTargetLanguage( + JNIEnv* env, + const base::android::JavaParamRef<jstring>& translate_target_lang); #endif ErrorPageDelegate* error_page_delegate() { return error_page_delegate_; } @@ -199,10 +203,15 @@ class TabImpl : public Tab, void AddDataObserver(DataObserver* observer); void RemoveDataObserver(DataObserver* observer); + GoogleAccountsDelegate* google_accounts_delegate() { + return google_accounts_delegate_; + } + // Tab: void SetErrorPageDelegate(ErrorPageDelegate* delegate) override; void SetFullscreenDelegate(FullscreenDelegate* delegate) override; void SetNewTabDelegate(NewTabDelegate* delegate) override; + void SetGoogleAccountsDelegate(GoogleAccountsDelegate* delegate) override; void AddObserver(TabObserver* observer) override; void RemoveObserver(TabObserver* observer) override; NavigationController* GetNavigationController() override; @@ -218,6 +227,8 @@ class TabImpl : public Tab, const std::vector<std::string>& js_origins) override; void RemoveWebMessageHostFactory( const base::string16& js_object_name) override; + std::unique_ptr<FaviconFetcher> CreateFaviconFetcher( + FaviconFetcherDelegate* delegate) override; #if !defined(OS_ANDROID) void AttachToView(views::WebView* web_view) override; #endif @@ -248,12 +259,20 @@ class TabImpl : public Tab, const std::vector<blink::mojom::ColorSuggestionPtr>& suggestions) override; void RunFileChooser(content::RenderFrameHost* render_frame_host, - std::unique_ptr<content::FileSelectListener> listener, + scoped_refptr<content::FileSelectListener> listener, const blink::mojom::FileChooserParams& params) override; + void CreateSmsPrompt(content::RenderFrameHost*, + const url::Origin&, + const std::string& one_time_code, + base::OnceClosure on_confirm, + base::OnceClosure on_cancel) override; int GetTopControlsHeight() override; + int GetTopControlsMinHeight() override; int GetBottomControlsHeight() override; bool DoBrowserControlsShrinkRendererSize( - const content::WebContents* web_contents) override; + content::WebContents* web_contents) override; + bool OnlyExpandTopControlsAtPageTop() override; + bool ShouldAnimateBrowserControlsHeightChanges() override; bool EmbedsFullscreenWidget() override; void RequestMediaAccessPermission( content::WebContents* web_contents, @@ -299,9 +318,8 @@ class TabImpl : public Tab, gfx::Rect* src_rect, gfx::Size* output_size); - void UpdateBrowserControlsStateImpl(content::BrowserControlsState new_state, - content::BrowserControlsState old_state, - bool animate); + void UpdateBrowserControlsState(content::BrowserControlsState new_state, + bool animate); #endif // content::WebContentsObserver: @@ -330,7 +348,7 @@ class TabImpl : public Tab, // Returns the FindTabHelper for the page, or null if none exists. find_in_page::FindTabHelper* GetFindTabHelper(); - sessions::SessionTabHelperDelegate* GetSessionServiceTabHelperDelegate( + static sessions::SessionTabHelperDelegate* GetSessionServiceTabHelperDelegate( content::WebContents* web_contents); #if defined(OS_ANDROID) @@ -346,6 +364,7 @@ class TabImpl : public Tab, ErrorPageDelegate* error_page_delegate_ = nullptr; FullscreenDelegate* fullscreen_delegate_ = nullptr; NewTabDelegate* new_tab_delegate_ = nullptr; + GoogleAccountsDelegate* google_accounts_delegate_ = nullptr; ProfileImpl* profile_; std::unique_ptr<content::WebContents> web_contents_; std::unique_ptr<NavigationControllerImpl> navigation_controller_; @@ -359,9 +378,14 @@ class TabImpl : public Tab, std::unique_ptr<BrowserControlsNavigationStateHandler> browser_controls_navigation_state_handler_; - // Last value supplied to UpdateBrowserControlsState(). - content::BrowserControlsState current_browser_controls_state_ = - content::BROWSER_CONTROLS_STATE_SHOWN; + // Last value supplied to UpdateBrowserControlsConstraint(). This *constraint* + // can be SHOWN, if for example a modal dialog is forcing the controls to be + // visible, HIDDEN, if for example fullscreen is forcing the controls to be + // hidden, or BOTH, if either state is viable (e.g. during normal browsing). + // When BOTH, the actual current state could be showing or hidden. + content::BrowserControlsState + current_browser_controls_visibility_constraint_ = + content::BROWSER_CONTROLS_STATE_SHOWN; std::map<std::string, std::unique_ptr<WebMessageHostFactoryProxy>> js_name_to_proxy_; diff --git a/chromium/weblayer/browser/tab_specific_content_settings_delegate.h b/chromium/weblayer/browser/tab_specific_content_settings_delegate.h deleted file mode 100644 index 10dcf2fe26f..00000000000 --- a/chromium/weblayer/browser/tab_specific_content_settings_delegate.h +++ /dev/null @@ -1,54 +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 WEBLAYER_BROWSER_TAB_SPECIFIC_CONTENT_SETTINGS_DELEGATE_H_ -#define WEBLAYER_BROWSER_TAB_SPECIFIC_CONTENT_SETTINGS_DELEGATE_H_ - -#include "components/content_settings/browser/tab_specific_content_settings.h" - -namespace weblayer { - -// Called by TabSpecificContentSettings to handle WebLayer specific logic. -class TabSpecificContentSettingsDelegate - : public content_settings::TabSpecificContentSettings::Delegate { - public: - explicit TabSpecificContentSettingsDelegate( - content::WebContents* web_contents); - ~TabSpecificContentSettingsDelegate() override; - TabSpecificContentSettingsDelegate( - const TabSpecificContentSettingsDelegate&) = delete; - TabSpecificContentSettingsDelegate& operator=( - const TabSpecificContentSettingsDelegate&) = delete; - - static void UpdateRendererContentSettingRules( - content::RenderProcessHost* process); - - private: - // TabSpecificContentSettings::Delegate: - void UpdateLocationBar() override; - void SetContentSettingRules( - content::RenderProcessHost* process, - const RendererContentSettingRules& rules) override; - PrefService* GetPrefs() override; - HostContentSettingsMap* GetSettingsMap() override; - ContentSetting GetEmbargoSetting(const GURL& request_origin, - ContentSettingsType permission) override; - std::vector<storage::FileSystemType> GetAdditionalFileSystemTypes() override; - browsing_data::CookieHelper::IsDeletionDisabledCallback - GetIsDeletionDisabledCallback() override; - bool IsMicrophoneCameraStateChanged( - content_settings::TabSpecificContentSettings::MicrophoneCameraState - microphone_camera_state, - const std::string& media_stream_selected_audio_device, - const std::string& media_stream_selected_video_device) override; - content_settings::TabSpecificContentSettings::MicrophoneCameraState - GetMicrophoneCameraState() override; - void OnContentBlocked(ContentSettingsType type) override; - - content::WebContents* web_contents_; -}; - -} // namespace weblayer - -#endif // WEBLAYER_BROWSER_TAB_SPECIFIC_CONTENT_SETTINGS_DELEGATE_H_ diff --git a/chromium/weblayer/browser/translate_browsertest.cc b/chromium/weblayer/browser/translate_browsertest.cc index 2824d8b91ea..4bde4c23889 100644 --- a/chromium/weblayer/browser/translate_browsertest.cc +++ b/chromium/weblayer/browser/translate_browsertest.cc @@ -23,11 +23,12 @@ #if defined(OS_ANDROID) #include "base/android/build_info.h" +#include "components/infobars/android/infobar_android.h" // nogncheck #include "components/infobars/core/infobar_manager.h" // nogncheck #include "components/translate/core/browser/translate_download_manager.h" -#include "weblayer/browser/infobar_android.h" #include "weblayer/browser/infobar_service.h" #include "weblayer/browser/translate_compact_infobar.h" +#include "weblayer/shell/android/browsertests_apk/translate_test_bridge.h" #endif namespace weblayer { @@ -465,7 +466,8 @@ IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, TranslateInfoBarPresentation) { run_loop.Run(); EXPECT_EQ(1u, infobar_service->infobar_count()); - auto* infobar = static_cast<InfoBarAndroid*>(infobar_service->infobar_at(0)); + auto* infobar = + static_cast<infobars::InfoBarAndroid*>(infobar_service->infobar_at(0)); EXPECT_TRUE(infobar->HasSetJavaInfoBar()); base::RunLoop run_loop2; @@ -520,7 +522,8 @@ IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, TranslationViaInfoBar) { // occurs. auto* infobar = static_cast<TranslateCompactInfoBar*>(infobar_service->infobar_at(0)); - infobar->SelectButtonForTesting(InfoBarAndroid::ActionType::ACTION_TRANSLATE); + TranslateTestBridge::SelectButton( + infobar, infobars::InfoBarAndroid::ActionType::ACTION_TRANSLATE); WaitUntilPageTranslated(shell()); @@ -538,8 +541,9 @@ IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, TranslationViaInfoBar) { // Revert to the source language via the Java infobar and ensure that the // translation is undone. - infobar->SelectButtonForTesting( - InfoBarAndroid::ActionType::ACTION_TRANSLATE_SHOW_ORIGINAL); + TranslateTestBridge::SelectButton( + infobar, + infobars::InfoBarAndroid::ActionType::ACTION_TRANSLATE_SHOW_ORIGINAL); translate_reversion_waiter->Wait(); EXPECT_EQ("fr", translate_client->GetLanguageState().current_language()); @@ -587,8 +591,9 @@ IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, auto* infobar = static_cast<TranslateCompactInfoBar*>(infobar_service->infobar_at(0)); - infobar->ClickOverflowMenuItemForTesting( - TranslateCompactInfoBar::OverflowMenuItemId::NEVER_TRANSLATE_LANGUAGE); + TranslateTestBridge::ClickOverflowMenuItem( + infobar, + TranslateTestBridge::OverflowMenuItemId::NEVER_TRANSLATE_LANGUAGE); // The translate infobar should still be present. EXPECT_EQ(1u, infobar_service->infobar_count()); @@ -657,8 +662,8 @@ IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, auto* infobar = static_cast<TranslateCompactInfoBar*>(infobar_service->infobar_at(0)); - infobar->ClickOverflowMenuItemForTesting( - TranslateCompactInfoBar::OverflowMenuItemId::NEVER_TRANSLATE_SITE); + TranslateTestBridge::ClickOverflowMenuItem( + infobar, TranslateTestBridge::OverflowMenuItemId::NEVER_TRANSLATE_SITE); // The translate infobar should still be present. EXPECT_EQ(1u, infobar_service->infobar_count()); @@ -689,12 +694,14 @@ IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, class NeverTranslateMenuItemTranslateBrowserTest : public TranslateBrowserTest, public testing::WithParamInterface< - TranslateCompactInfoBar::OverflowMenuItemId> {}; + TranslateTestBridge::OverflowMenuItemId> {}; // Test that clicking and unclicking a never translate item ends up being a // no-op. -IN_PROC_BROWSER_TEST_P(NeverTranslateMenuItemTranslateBrowserTest, - TranslateInfoBarToggleAndToggleBackNeverTranslateItem) { +// Disabled due to flakiness on P (crbug.com/1114795). +IN_PROC_BROWSER_TEST_P( + NeverTranslateMenuItemTranslateBrowserTest, + DISABLED_TranslateInfoBarToggleAndToggleBackNeverTranslateItem) { auto* web_contents = static_cast<TabImpl*>(shell()->tab())->web_contents(); auto* infobar_service = InfoBarService::FromWebContents(web_contents); @@ -725,12 +732,12 @@ IN_PROC_BROWSER_TEST_P(NeverTranslateMenuItemTranslateBrowserTest, auto* infobar = static_cast<TranslateCompactInfoBar*>(infobar_service->infobar_at(0)); - infobar->ClickOverflowMenuItemForTesting(GetParam()); + TranslateTestBridge::ClickOverflowMenuItem(infobar, GetParam()); // The translate infobar should still be present. EXPECT_EQ(1u, infobar_service->infobar_count()); - infobar->ClickOverflowMenuItemForTesting(GetParam()); + TranslateTestBridge::ClickOverflowMenuItem(infobar, GetParam()); } // The infobar should be shown on a new navigation to a page in the same @@ -768,8 +775,8 @@ INSTANTIATE_TEST_SUITE_P( All, NeverTranslateMenuItemTranslateBrowserTest, ::testing::Values( - TranslateCompactInfoBar::OverflowMenuItemId::NEVER_TRANSLATE_LANGUAGE, - TranslateCompactInfoBar::OverflowMenuItemId::NEVER_TRANSLATE_SITE)); + TranslateTestBridge::OverflowMenuItemId::NEVER_TRANSLATE_LANGUAGE, + TranslateTestBridge::OverflowMenuItemId::NEVER_TRANSLATE_SITE)); #endif // #if defined(OS_ANDROID) diff --git a/chromium/weblayer/browser/translate_compact_infobar.cc b/chromium/weblayer/browser/translate_compact_infobar.cc index 7584fe4e99a..808fb81b19c 100644 --- a/chromium/weblayer/browser/translate_compact_infobar.cc +++ b/chromium/weblayer/browser/translate_compact_infobar.cc @@ -12,14 +12,16 @@ #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/android/jni_weak_ref.h" +#include "base/bind.h" +#include "components/translate/content/android/translate_utils.h" #include "components/translate/core/browser/translate_infobar_delegate.h" #include "components/variations/variations_associated_data.h" #include "content/public/browser/browser_context.h" +#include "weblayer/browser/android/resource_mapper.h" #include "weblayer/browser/infobar_service.h" #include "weblayer/browser/java/jni/TranslateCompactInfoBar_jni.h" #include "weblayer/browser/tab_impl.h" #include "weblayer/browser/translate_client_impl.h" -#include "weblayer/browser/translate_utils.h" using base::android::JavaParamRef; using base::android::ScopedJavaLocalRef; @@ -33,7 +35,9 @@ const char kTranslateTabDefaultTextColor[] = "translate_tab_default_text_color"; TranslateCompactInfoBar::TranslateCompactInfoBar( std::unique_ptr<translate::TranslateInfoBarDelegate> delegate) - : InfoBarAndroid(std::move(delegate)), action_flags_(FLAG_NONE) { + : infobars::InfoBarAndroid(std::move(delegate), + base::BindRepeating(&MapToJavaDrawableId)), + action_flags_(FLAG_NONE) { GetDelegate()->AddObserver(this); // Flip the translate bit if auto translate is enabled. @@ -50,11 +54,11 @@ ScopedJavaLocalRef<jobject> TranslateCompactInfoBar::CreateRenderInfoBar( translate::TranslateInfoBarDelegate* delegate = GetDelegate(); base::android::ScopedJavaLocalRef<jobjectArray> java_languages = - TranslateUtils::GetJavaLanguages(env, delegate); + translate::TranslateUtils::GetJavaLanguages(env, delegate); base::android::ScopedJavaLocalRef<jobjectArray> java_codes = - TranslateUtils::GetJavaLanguageCodes(env, delegate); + translate::TranslateUtils::GetJavaLanguageCodes(env, delegate); base::android::ScopedJavaLocalRef<jintArray> java_hash_codes = - TranslateUtils::GetJavaLanguageHashCodes(env, delegate); + translate::TranslateUtils::GetJavaLanguageHashCodes(env, delegate); ScopedJavaLocalRef<jstring> source_language_code = base::android::ConvertUTF8ToJavaString( @@ -81,7 +85,7 @@ void TranslateCompactInfoBar::ProcessButton(int action) { return; // We're closing; don't call anything, it might access the owner. translate::TranslateInfoBarDelegate* delegate = GetDelegate(); - if (action == InfoBarAndroid::ACTION_TRANSLATE) { + if (action == infobars::InfoBarAndroid::ACTION_TRANSLATE) { action_flags_ |= FLAG_TRANSLATE; delegate->Translate(); if (delegate->ShouldAutoAlwaysTranslate()) { @@ -89,17 +93,18 @@ void TranslateCompactInfoBar::ProcessButton(int action) { Java_TranslateCompactInfoBar_setAutoAlwaysTranslate(env, GetJavaInfoBar()); } - } else if (action == InfoBarAndroid::ACTION_TRANSLATE_SHOW_ORIGINAL) { + } else if (action == + infobars::InfoBarAndroid::ACTION_TRANSLATE_SHOW_ORIGINAL) { action_flags_ |= FLAG_REVERT; delegate->RevertWithoutClosingInfobar(); } else { - DCHECK_EQ(InfoBarAndroid::ACTION_NONE, action); + DCHECK_EQ(infobars::InfoBarAndroid::ACTION_NONE, action); } } void TranslateCompactInfoBar::SetJavaInfoBar( const base::android::JavaRef<jobject>& java_info_bar) { - InfoBarAndroid::SetJavaInfoBar(java_info_bar); + infobars::InfoBarAndroid::SetJavaInfoBar(java_info_bar); JNIEnv* env = base::android::AttachCurrentThread(); Java_TranslateCompactInfoBar_setNativePtr(env, java_info_bar, reinterpret_cast<intptr_t>(this)); @@ -111,12 +116,12 @@ void TranslateCompactInfoBar::ApplyStringTranslateOption( int option, const JavaParamRef<jstring>& value) { translate::TranslateInfoBarDelegate* delegate = GetDelegate(); - if (option == TranslateUtils::OPTION_SOURCE_CODE) { + if (option == translate::TranslateUtils::OPTION_SOURCE_CODE) { std::string source_code = base::android::ConvertJavaStringToUTF8(env, value); if (delegate->original_language_code().compare(source_code) != 0) delegate->UpdateOriginalLanguage(source_code); - } else if (option == TranslateUtils::OPTION_TARGET_CODE) { + } else if (option == translate::TranslateUtils::OPTION_TARGET_CODE) { std::string target_code = base::android::ConvertJavaStringToUTF8(env, value); if (delegate->target_language_code().compare(target_code) != 0) @@ -132,18 +137,18 @@ void TranslateCompactInfoBar::ApplyBoolTranslateOption( int option, jboolean value) { translate::TranslateInfoBarDelegate* delegate = GetDelegate(); - if (option == TranslateUtils::OPTION_ALWAYS_TRANSLATE) { + if (option == translate::TranslateUtils::OPTION_ALWAYS_TRANSLATE) { if (delegate->ShouldAlwaysTranslate() != value) { action_flags_ |= FLAG_ALWAYS_TRANSLATE; delegate->ToggleAlwaysTranslate(); } - } else if (option == TranslateUtils::OPTION_NEVER_TRANSLATE) { + } else if (option == translate::TranslateUtils::OPTION_NEVER_TRANSLATE) { bool language_blocklisted = !delegate->IsTranslatableLanguageByPrefs(); if (language_blocklisted != value) { action_flags_ |= FLAG_NEVER_LANGUAGE; delegate->ToggleTranslatableLanguageByPrefs(); } - } else if (option == TranslateUtils::OPTION_NEVER_TRANSLATE_SITE) { + } else if (option == translate::TranslateUtils::OPTION_NEVER_TRANSLATE_SITE) { if (delegate->IsSiteBlacklisted() != value) { action_flags_ |= FLAG_NEVER_SITE; delegate->ToggleSiteBlacklist(); @@ -200,6 +205,11 @@ translate::TranslateInfoBarDelegate* TranslateCompactInfoBar::GetDelegate() { void TranslateCompactInfoBar::OnTranslateStepChanged( translate::TranslateStep step, translate::TranslateErrors::Type error_type) { + // If the tab lost active state while translation was occurring, the Java + // infobar will now be gone. In that case there is nothing to do here. + if (!HasSetJavaInfoBar()) + return; // No connected Java infobar + if (!owner()) return; // We're closing; don't call anything. @@ -222,25 +232,4 @@ void TranslateCompactInfoBar::OnTranslateInfoBarDelegateDestroyed( GetDelegate()->RemoveObserver(this); } -void TranslateCompactInfoBar::SelectButtonForTesting(ActionType action_type) { - JNIEnv* env = base::android::AttachCurrentThread(); - Java_TranslateCompactInfoBar_selectTabForTesting(env, GetJavaInfoBar(), - action_type); -} - -void TranslateCompactInfoBar::ClickOverflowMenuItemForTesting( - OverflowMenuItemId item_id) { - JNIEnv* env = base::android::AttachCurrentThread(); - switch (item_id) { - case OverflowMenuItemId::NEVER_TRANSLATE_LANGUAGE: - Java_TranslateCompactInfoBar_clickNeverTranslateLanguageMenuItemForTesting( - env, GetJavaInfoBar()); - return; - case OverflowMenuItemId::NEVER_TRANSLATE_SITE: - Java_TranslateCompactInfoBar_clickNeverTranslateSiteMenuItemForTesting( - env, GetJavaInfoBar()); - return; - } -} - } // namespace weblayer diff --git a/chromium/weblayer/browser/translate_compact_infobar.h b/chromium/weblayer/browser/translate_compact_infobar.h index d4cfc20c3fd..2e85f5ba2d1 100644 --- a/chromium/weblayer/browser/translate_compact_infobar.h +++ b/chromium/weblayer/browser/translate_compact_infobar.h @@ -7,10 +7,10 @@ #include "base/android/scoped_java_ref.h" #include "base/macros.h" +#include "components/infobars/android/infobar_android.h" #include "components/translate/core/browser/translate_infobar_delegate.h" #include "components/translate/core/browser/translate_step.h" #include "components/translate/core/common/translate_errors.h" -#include "weblayer/browser/infobar_android.h" namespace translate { class TranslateInfoBarDelegate; @@ -19,18 +19,13 @@ class TranslateInfoBarDelegate; namespace weblayer { class TranslateCompactInfoBar - : public InfoBarAndroid, + : public infobars::InfoBarAndroid, public translate::TranslateInfoBarDelegate::Observer { public: explicit TranslateCompactInfoBar( std::unique_ptr<translate::TranslateInfoBarDelegate> delegate); ~TranslateCompactInfoBar() override; - enum class OverflowMenuItemId { - NEVER_TRANSLATE_LANGUAGE = 0, - NEVER_TRANSLATE_SITE = 1, - }; - // JNI method specific to string settings in translate. void ApplyStringTranslateOption( JNIEnv* env, @@ -65,15 +60,8 @@ class TranslateCompactInfoBar void OnTranslateInfoBarDelegateDestroyed( translate::TranslateInfoBarDelegate* delegate) override; - // Instructs the Java infobar to select the button corresponding to - // |action_type|. - void SelectButtonForTesting(ActionType action_type); - - // Instructs the Java infobar to click the specified overflow menu item. - void ClickOverflowMenuItemForTesting(OverflowMenuItemId item_id); - private: - // InfoBarAndroid: + // infobars::InfoBarAndroid: base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar( JNIEnv* env) override; void ProcessButton(int action) override; diff --git a/chromium/weblayer/browser/translate_utils.cc b/chromium/weblayer/browser/translate_utils.cc deleted file mode 100644 index aac744a115e..00000000000 --- a/chromium/weblayer/browser/translate_utils.cc +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "weblayer/browser/translate_utils.h" - -#include <stddef.h> - -#include "base/android/jni_array.h" -#include "base/android/jni_string.h" -#include "base/android/jni_weak_ref.h" -#include "components/metrics/metrics_log.h" -#include "components/translate/core/browser/translate_infobar_delegate.h" - -using base::android::JavaParamRef; -using base::android::ScopedJavaLocalRef; - -namespace weblayer { - -ScopedJavaLocalRef<jobjectArray> TranslateUtils::GetJavaLanguages( - JNIEnv* env, - translate::TranslateInfoBarDelegate* delegate) { - std::vector<base::string16> languages; - languages.reserve(delegate->num_languages()); - for (size_t i = 0; i < delegate->num_languages(); ++i) { - languages.push_back(delegate->language_name_at(i)); - } - return base::android::ToJavaArrayOfStrings(env, languages); -} - -ScopedJavaLocalRef<jobjectArray> TranslateUtils::GetJavaLanguageCodes( - JNIEnv* env, - translate::TranslateInfoBarDelegate* delegate) { - std::vector<std::string> codes; - codes.reserve(delegate->num_languages()); - for (size_t i = 0; i < delegate->num_languages(); ++i) { - codes.push_back(delegate->language_code_at(i)); - } - return base::android::ToJavaArrayOfStrings(env, codes); -} - -ScopedJavaLocalRef<jintArray> TranslateUtils::GetJavaLanguageHashCodes( - JNIEnv* env, - translate::TranslateInfoBarDelegate* delegate) { - std::vector<int> hashCodes; - hashCodes.reserve(delegate->num_languages()); - for (size_t i = 0; i < delegate->num_languages(); ++i) { - hashCodes.push_back( - metrics::MetricsLog::Hash(delegate->language_code_at(i))); - } - return base::android::ToJavaIntArray(env, hashCodes); -} - -} // namespace weblayer diff --git a/chromium/weblayer/browser/translate_utils.h b/chromium/weblayer/browser/translate_utils.h deleted file mode 100644 index 9bee56b5d7a..00000000000 --- a/chromium/weblayer/browser/translate_utils.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef WEBLAYER_BROWSER_TRANSLATE_UTILS_H_ -#define WEBLAYER_BROWSER_TRANSLATE_UTILS_H_ - -#include "base/android/jni_android.h" -#include "base/android/scoped_java_ref.h" - -namespace translate { -class TranslateInfoBarDelegate; -} - -namespace weblayer { - -class TranslateUtils { - public: - // A Java counterpart will be generated for this enum. - // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.weblayer_private - // GENERATED_JAVA_PREFIX_TO_STRIP:OPTION_ - enum TranslateOption { - OPTION_SOURCE_CODE, - OPTION_TARGET_CODE, - OPTION_ALWAYS_TRANSLATE, - OPTION_NEVER_TRANSLATE, - OPTION_NEVER_TRANSLATE_SITE - }; - - static base::android::ScopedJavaLocalRef<jobjectArray> GetJavaLanguages( - JNIEnv* env, - translate::TranslateInfoBarDelegate* delegate); - static base::android::ScopedJavaLocalRef<jobjectArray> GetJavaLanguageCodes( - JNIEnv* env, - translate::TranslateInfoBarDelegate* delegate); - static base::android::ScopedJavaLocalRef<jintArray> GetJavaLanguageHashCodes( - JNIEnv* env, - translate::TranslateInfoBarDelegate* delegate); -}; - -} // namespace weblayer - -#endif // WEBLAYER_BROWSER_TRANSLATE_UTILS_H_ diff --git a/chromium/weblayer/browser/tts_environment_android_impl.cc b/chromium/weblayer/browser/tts_environment_android_impl.cc new file mode 100644 index 00000000000..cc69d87c904 --- /dev/null +++ b/chromium/weblayer/browser/tts_environment_android_impl.cc @@ -0,0 +1,36 @@ +// 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 "weblayer/browser/tts_environment_android_impl.h" + +#include "base/callback.h" + +namespace weblayer { + +TtsEnvironmentAndroidImpl::TtsEnvironmentAndroidImpl() = default; + +TtsEnvironmentAndroidImpl::~TtsEnvironmentAndroidImpl() = default; + +bool TtsEnvironmentAndroidImpl::CanSpeakUtterancesFromHiddenWebContents() { + // For simplicity's sake, disallow playing utterances in hidden WebContents. + // Other options are to allow this, and instead cancel any utterances when + // all browsers are paused. + return false; +} + +bool TtsEnvironmentAndroidImpl::CanSpeakNow() { + // Always return true, as by the time we get here we know the WebContents + // is visible (because CanSpeakUtterancesFromHiddenWebContents() returns + // false). Further, when the fragment is paused/stopped the WebContents is + // hidden, which triggers the utterance to stop (because + // CanSpeakUtterancesFromHiddenWebContents() returns false). + return true; +} + +void TtsEnvironmentAndroidImpl::SetCanSpeakNowChangedCallback( + base::RepeatingClosure callback) { + // As CanSpeakNow() always returns true, there is nothing to do here. +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/tts_environment_android_impl.h b/chromium/weblayer/browser/tts_environment_android_impl.h new file mode 100644 index 00000000000..9cca9408ddc --- /dev/null +++ b/chromium/weblayer/browser/tts_environment_android_impl.h @@ -0,0 +1,30 @@ +// 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 WEBLAYER_BROWSER_TTS_ENVIRONMENT_ANDROID_IMPL_H_ +#define WEBLAYER_BROWSER_TTS_ENVIRONMENT_ANDROID_IMPL_H_ + +#include "content/public/browser/tts_environment_android.h" + +namespace weblayer { + +// WebLayer implementation of TtsEnvironmentAndroid. This does not allow +// speech from hidden WebContents. +class TtsEnvironmentAndroidImpl : public content::TtsEnvironmentAndroid { + public: + TtsEnvironmentAndroidImpl(); + TtsEnvironmentAndroidImpl(const TtsEnvironmentAndroidImpl&) = delete; + TtsEnvironmentAndroidImpl& operator=(const TtsEnvironmentAndroidImpl&) = + delete; + ~TtsEnvironmentAndroidImpl() override; + + // TtsEnvironment: + bool CanSpeakUtterancesFromHiddenWebContents() override; + bool CanSpeakNow() override; + void SetCanSpeakNowChangedCallback(base::RepeatingClosure callback) override; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_TTS_ENVIRONMENT_ANDROID_IMPL_H_ diff --git a/chromium/weblayer/browser/ukm_page_load_metrics_observer.cc b/chromium/weblayer/browser/ukm_page_load_metrics_observer.cc new file mode 100644 index 00000000000..d84009acbd9 --- /dev/null +++ b/chromium/weblayer/browser/ukm_page_load_metrics_observer.cc @@ -0,0 +1,41 @@ +// 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 "weblayer/browser/ukm_page_load_metrics_observer.h" + +#include "build/build_config.h" +#include "components/prerender/browser/prerender_manager.h" +#include "components/prerender/browser/prerender_util.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/web_contents.h" +#include "services/metrics/public/cpp/ukm_recorder.h" +#include "weblayer/browser/no_state_prefetch/prerender_manager_factory.h" + +namespace weblayer { + +// static +std::unique_ptr<page_load_metrics::PageLoadMetricsObserver> +UkmPageLoadMetricsObserver::CreateIfNeeded() { + if (!ukm::UkmRecorder::Get()) { + return nullptr; + } + return std::make_unique<UkmPageLoadMetricsObserver>(); +} + +UkmPageLoadMetricsObserver::ObservePolicy UkmPageLoadMetricsObserver::OnCommit( + content::NavigationHandle* navigation_handle, + ukm::SourceId source_id) { +#if defined(OS_ANDROID) + prerender::PrerenderManager* const prerender_manager = + PrerenderManagerFactory::GetForBrowserContext( + navigation_handle->GetWebContents()->GetBrowserContext()); + if (!prerender_manager) + return CONTINUE_OBSERVING; + prerender::RecordNoStatePrefetchMetrics(navigation_handle, source_id, + prerender_manager); +#endif + return CONTINUE_OBSERVING; +} + +} // namespace weblayer diff --git a/chromium/weblayer/browser/ukm_page_load_metrics_observer.h b/chromium/weblayer/browser/ukm_page_load_metrics_observer.h new file mode 100644 index 00000000000..30e17b39c87 --- /dev/null +++ b/chromium/weblayer/browser/ukm_page_load_metrics_observer.h @@ -0,0 +1,35 @@ +// 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 WEBLAYER_BROWSER_UKM_PAGE_LOAD_METRICS_OBSERVER_H_ +#define WEBLAYER_BROWSER_UKM_PAGE_LOAD_METRICS_OBSERVER_H_ + +#include "components/page_load_metrics/browser/page_load_metrics_observer.h" + +namespace content { +class NavigationHandle; +} + +namespace weblayer { + +// If URL-Keyed-Metrics (UKM) is enabled in the system, this is used to +// populate it with top-level page-load metrics. +class UkmPageLoadMetricsObserver + : public page_load_metrics::PageLoadMetricsObserver { + public: + // Returns a UkmPageLoadMetricsObserver, or nullptr if it is not needed. + static std::unique_ptr<page_load_metrics::PageLoadMetricsObserver> + CreateIfNeeded(); + + UkmPageLoadMetricsObserver() = default; + ~UkmPageLoadMetricsObserver() override = default; + + // page_load_metrics::PageLoadMetricsObserver implementation: + ObservePolicy OnCommit(content::NavigationHandle* navigation_handle, + ukm::SourceId source_id) override; +}; + +} // namespace weblayer + +#endif // WEBLAYER_BROWSER_UKM_PAGE_LOAD_METRICS_OBSERVER_H_ diff --git a/chromium/weblayer/browser/url_bar/page_info_browsertest.cc b/chromium/weblayer/browser/url_bar/page_info_browsertest.cc index dc4ad642678..6c5971995ab 100644 --- a/chromium/weblayer/browser/url_bar/page_info_browsertest.cc +++ b/chromium/weblayer/browser/url_bar/page_info_browsertest.cc @@ -76,11 +76,11 @@ IN_PROC_BROWSER_TEST_F(PageInfoBrowserTest, PermissionStatus) { } IN_PROC_BROWSER_TEST_F(PageInfoBrowserTest, - TabSpecificContentSettingsDelegate) { + PageSpecificContentSettingsDelegate) { std::unique_ptr<PageInfoDelegate> page_info_delegate = page_info::GetPageInfoClient()->CreatePageInfoDelegate(GetWebContents()); ASSERT_TRUE(page_info_delegate); - EXPECT_TRUE(page_info_delegate->GetTabSpecificContentSettingsDelegate()); + EXPECT_TRUE(page_info_delegate->GetPageSpecificContentSettingsDelegate()); } IN_PROC_BROWSER_TEST_F(PageInfoBrowserTest, EmbedderNameSet) { diff --git a/chromium/weblayer/browser/url_bar/page_info_delegate_impl.cc b/chromium/weblayer/browser/url_bar/page_info_delegate_impl.cc index f189430f4d0..e79597f6860 100644 --- a/chromium/weblayer/browser/url_bar/page_info_delegate_impl.cc +++ b/chromium/weblayer/browser/url_bar/page_info_delegate_impl.cc @@ -10,10 +10,10 @@ #include "components/security_state/content/content_utils.h" #include "content/public/browser/browser_context.h" #include "weblayer/browser/host_content_settings_map_factory.h" +#include "weblayer/browser/page_specific_content_settings_delegate.h" #include "weblayer/browser/permissions/permission_decision_auto_blocker_factory.h" #include "weblayer/browser/permissions/permission_manager_factory.h" #include "weblayer/browser/stateful_ssl_host_state_delegate_factory.h" -#include "weblayer/browser/tab_specific_content_settings_delegate.h" #if defined(OS_ANDROID) #include "weblayer/browser/weblayer_impl_android.h" @@ -90,6 +90,15 @@ HostContentSettingsMap* PageInfoDelegateImpl::GetContentSettings() { GetBrowserContext()); } +bool PageInfoDelegateImpl::IsSubresourceFilterActivated(const GURL& site_url) { + // As the WebLayer does not support subresource filtering, a site + // will not have ads blocked as a result of this setting. Return false + // so we do not show the ad blocking permission. + // TODO(https://crbug.com/1116095): Add subresource filtering to the + // WebLayer. + return false; +} + bool PageInfoDelegateImpl::IsContentDisplayedInVrHeadset() { // VR is not supported for WebLayer. return false; @@ -108,9 +117,9 @@ PageInfoDelegateImpl::GetVisibleSecurityState() { return *security_state::GetVisibleSecurityState(web_contents_); } -std::unique_ptr<content_settings::TabSpecificContentSettings::Delegate> -PageInfoDelegateImpl::GetTabSpecificContentSettingsDelegate() { - return std::make_unique<TabSpecificContentSettingsDelegate>(web_contents_); +std::unique_ptr<content_settings::PageSpecificContentSettings::Delegate> +PageInfoDelegateImpl::GetPageSpecificContentSettingsDelegate() { + return std::make_unique<PageSpecificContentSettingsDelegate>(web_contents_); } #if defined(OS_ANDROID) diff --git a/chromium/weblayer/browser/url_bar/page_info_delegate_impl.h b/chromium/weblayer/browser/url_bar/page_info_delegate_impl.h index 65939b39072..5d7ce4e1c87 100644 --- a/chromium/weblayer/browser/url_bar/page_info_delegate_impl.h +++ b/chromium/weblayer/browser/url_bar/page_info_delegate_impl.h @@ -43,8 +43,9 @@ class PageInfoDelegateImpl : public PageInfoDelegate { override; StatefulSSLHostStateDelegate* GetStatefulSSLHostStateDelegate() override; HostContentSettingsMap* GetContentSettings() override; - std::unique_ptr<content_settings::TabSpecificContentSettings::Delegate> - GetTabSpecificContentSettingsDelegate() override; + std::unique_ptr<content_settings::PageSpecificContentSettings::Delegate> + GetPageSpecificContentSettingsDelegate() override; + bool IsSubresourceFilterActivated(const GURL& site_url) override; bool IsContentDisplayedInVrHeadset() override; security_state::SecurityLevel GetSecurityLevel() override; security_state::VisibleSecurityState GetVisibleSecurityState() override; diff --git a/chromium/weblayer/browser/url_bar/url_bar_controller_impl.h b/chromium/weblayer/browser/url_bar/url_bar_controller_impl.h index dacd8077746..3e564133d95 100644 --- a/chromium/weblayer/browser/url_bar/url_bar_controller_impl.h +++ b/chromium/weblayer/browser/url_bar/url_bar_controller_impl.h @@ -8,13 +8,14 @@ #include "base/strings/string16.h" #include "build/build_config.h" #include "components/omnibox/browser/location_bar_model_delegate.h" -#include "components/omnibox/browser/location_bar_model_impl.h" #include "weblayer/public/url_bar_controller.h" #if defined(OS_ANDROID) #include "base/android/scoped_java_ref.h" #endif +class LocationBarModelImpl; + namespace content { class WebContents; } diff --git a/chromium/weblayer/browser/weblayer_browser_interface_binders.cc b/chromium/weblayer/browser/weblayer_browser_interface_binders.cc index a0043214a7e..5813a0cb3de 100644 --- a/chromium/weblayer/browser/weblayer_browser_interface_binders.cc +++ b/chromium/weblayer/browser/weblayer_browser_interface_binders.cc @@ -6,6 +6,9 @@ #include "base/bind.h" #include "build/build_config.h" +#include "components/prerender/browser/prerender_contents.h" +#include "components/prerender/browser/prerender_processor_impl.h" +#include "components/prerender/common/prerender_canceler.mojom.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" @@ -13,6 +16,9 @@ #include "content/public/browser/web_ui_controller.h" #include "third_party/blink/public/mojom/installedapp/installed_app_provider.mojom.h" #include "third_party/blink/public/mojom/installedapp/related_application.mojom.h" +#include "third_party/blink/public/mojom/prerender/prerender.mojom.h" +#include "weblayer/browser/no_state_prefetch/prerender_processor_impl_delegate_impl.h" +#include "weblayer/browser/no_state_prefetch/prerender_utils.h" #include "weblayer/browser/translate_client_impl.h" #include "weblayer/browser/webui/weblayer_internals.mojom.h" #include "weblayer/browser/webui/weblayer_internals_ui.h" @@ -65,6 +71,27 @@ void BindPageHandler( concrete_controller->BindInterface(std::move(receiver)); } +void BindPrerenderProcessor( + content::RenderFrameHost* frame_host, + mojo::PendingReceiver<blink::mojom::PrerenderProcessor> receiver) { + prerender::PrerenderProcessorImpl::Create( + frame_host, std::move(receiver), + std::make_unique<PrerenderProcessorImplDelegateImpl>()); +} + +void BindPrerenderCanceler( + content::RenderFrameHost* frame_host, + mojo::PendingReceiver<prerender::mojom::PrerenderCanceler> receiver) { + auto* web_contents = content::WebContents::FromRenderFrameHost(frame_host); + if (!web_contents) + return; + + auto* prerender_contents = PrerenderContentsFromWebContents(web_contents); + if (!prerender_contents) + return; + prerender_contents->AddPrerenderCancelerReceiver(std::move(receiver)); +} + #if defined(OS_ANDROID) // TODO(https://crbug.com/1037884): Remove this. class StubInstalledAppProvider : public blink::mojom::InstalledAppProvider { @@ -109,6 +136,11 @@ void PopulateWebLayerFrameBinders( map->Add<translate::mojom::ContentTranslateDriver>( base::BindRepeating(&BindContentTranslateDriver)); + map->Add<blink::mojom::PrerenderProcessor>( + base::BindRepeating(&BindPrerenderProcessor)); + map->Add<prerender::mojom::PrerenderCanceler>( + base::BindRepeating(&BindPrerenderCanceler)); + #if defined(OS_ANDROID) // TODO(https://crbug.com/1037884): Remove this. map->Add<blink::mojom::InstalledAppProvider>( diff --git a/chromium/weblayer/browser/weblayer_content_browser_overlay_manifest.cc b/chromium/weblayer/browser/weblayer_content_browser_overlay_manifest.cc deleted file mode 100644 index c121bc83b10..00000000000 --- a/chromium/weblayer/browser/weblayer_content_browser_overlay_manifest.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "weblayer/browser/weblayer_content_browser_overlay_manifest.h" - -#include "base/no_destructor.h" -#include "services/service_manager/public/cpp/manifest_builder.h" - -namespace weblayer { - -const service_manager::Manifest& GetWebLayerContentBrowserOverlayManifest() { - static base::NoDestructor<service_manager::Manifest> manifest{ - service_manager::ManifestBuilder() - .Build()}; - return *manifest; -} - -} // namespace weblayer diff --git a/chromium/weblayer/browser/weblayer_content_browser_overlay_manifest.h b/chromium/weblayer/browser/weblayer_content_browser_overlay_manifest.h deleted file mode 100644 index e4ed36670f5..00000000000 --- a/chromium/weblayer/browser/weblayer_content_browser_overlay_manifest.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef WEBLAYER_BROWSER_WEBLAYER_CONTENT_BROWSER_OVERLAY_MANIFEST_H_ -#define WEBLAYER_BROWSER_WEBLAYER_CONTENT_BROWSER_OVERLAY_MANIFEST_H_ - -#include "services/service_manager/public/cpp/manifest.h" - -namespace weblayer { - -// Returns the manifest WebLayer amends to Content's content_browser service -// manifest. This allows WebLayer to extend the capabilities exposed and/or -// required by content_browser service instances, as well as declaring any -// additional in- and out-of-process per-profile packaged services. -const service_manager::Manifest& GetWebLayerContentBrowserOverlayManifest(); - -} // namespace weblayer - -#endif // WEBLAYER_BROWSER_WEBLAYER_CONTENT_BROWSER_OVERLAY_MANIFEST_H_ diff --git a/chromium/weblayer/browser/weblayer_impl_android.cc b/chromium/weblayer/browser/weblayer_impl_android.cc index d07dc34ef0e..478f127a721 100644 --- a/chromium/weblayer/browser/weblayer_impl_android.cc +++ b/chromium/weblayer/browser/weblayer_impl_android.cc @@ -3,12 +3,14 @@ // found in the LICENSE file. #include "weblayer/browser/weblayer_impl_android.h" + #include "base/android/jni_android.h" #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "components/crash/core/common/crash_key.h" #include "components/page_info/android/page_info_client.h" #include "weblayer/browser/android/metrics/weblayer_metrics_service_client.h" +#include "weblayer/browser/default_search_engine.h" #include "weblayer/browser/devtools_server_android.h" #include "weblayer/browser/java/jni/WebLayerImpl_jni.h" #include "weblayer/browser/url_bar/page_info_client_impl.h" @@ -63,4 +65,12 @@ base::string16 GetClientApplicationName() { env, Java_WebLayerImpl_getEmbedderName(env)); } +static jboolean JNI_WebLayerImpl_IsLocationPermissionManaged( + JNIEnv* env, + const base::android::JavaParamRef<jstring>& origin) { + return IsPermissionControlledByDse( + ContentSettingsType::GEOLOCATION, + url::Origin::Create(GURL(ConvertJavaStringToUTF8(origin)))); +} + } // namespace weblayer diff --git a/chromium/weblayer/browser/weblayer_security_blocking_page_factory.cc b/chromium/weblayer/browser/weblayer_security_blocking_page_factory.cc index 37d3676f4f0..b7e9703840f 100644 --- a/chromium/weblayer/browser/weblayer_security_blocking_page_factory.cc +++ b/chromium/weblayer/browser/weblayer_security_blocking_page_factory.cc @@ -6,10 +6,12 @@ #include "components/captive_portal/core/buildflags.h" #include "components/security_interstitials/content/content_metrics_helper.h" +#include "components/security_interstitials/content/insecure_form_blocking_page.h" #include "components/security_interstitials/content/ssl_blocking_page.h" #include "components/security_interstitials/core/metrics_helper.h" #include "content/public/browser/web_contents.h" #include "weblayer/browser/captive_portal_service_factory.h" +#include "weblayer/browser/insecure_form_controller_client.h" #include "weblayer/browser/ssl_error_controller_client.h" #if defined(OS_ANDROID) @@ -201,10 +203,12 @@ std::unique_ptr<security_interstitials::InsecureFormBlockingPage> WebLayerSecurityBlockingPageFactory::CreateInsecureFormBlockingPage( content::WebContents* web_contents, const GURL& request_url) { - // TODO(crbug.com/1093102): Insecure form warnings are not yet implemented in - // Weblayer. - NOTREACHED(); - return nullptr; + std::unique_ptr<InsecureFormControllerClient> client = + std::make_unique<InsecureFormControllerClient>(web_contents, request_url); + auto page = + std::make_unique<security_interstitials::InsecureFormBlockingPage>( + web_contents, request_url, std::move(client)); + return page; } #if defined(OS_ANDROID) diff --git a/chromium/weblayer/browser/weblayer_variations_http_browsertest.cc b/chromium/weblayer/browser/weblayer_variations_http_browsertest.cc index f55317daf82..6db7051cdef 100644 --- a/chromium/weblayer/browser/weblayer_variations_http_browsertest.cc +++ b/chromium/weblayer/browser/weblayer_variations_http_browsertest.cc @@ -4,7 +4,7 @@ #include "weblayer/test/weblayer_browser_test.h" -#include "components/variations/variations_http_header_provider.h" +#include "components/variations/variations_ids_provider.h" #include "content/public/test/network_connection_change_simulator.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/embedded_test_server.h" @@ -12,59 +12,11 @@ #include "net/test/embedded_test_server/http_response.h" #include "weblayer/public/navigation.h" #include "weblayer/public/navigation_controller.h" -#include "weblayer/public/navigation_observer.h" #include "weblayer/public/tab.h" #include "weblayer/shell/browser/shell.h" #include "weblayer/test/weblayer_browser_test_utils.h" namespace weblayer { -namespace { - -class OneShotNavigationObserver : public NavigationObserver { - public: - explicit OneShotNavigationObserver(Shell* shell) : tab_(shell->tab()) { - tab_->GetNavigationController()->AddObserver(this); - } - - ~OneShotNavigationObserver() override { - tab_->GetNavigationController()->RemoveObserver(this); - } - - void WaitForNavigation() { run_loop_.Run(); } - - bool completed() { return completed_; } - bool is_error_page() { return is_error_page_; } - Navigation::LoadError load_error() { return load_error_; } - int http_status_code() { return http_status_code_; } - NavigationState navigation_state() { return navigation_state_; } - - private: - // NavigationObserver implementation: - void NavigationCompleted(Navigation* navigation) override { - completed_ = true; - Finish(navigation); - } - - void NavigationFailed(Navigation* navigation) override { Finish(navigation); } - - void Finish(Navigation* navigation) { - is_error_page_ = navigation->IsErrorPage(); - load_error_ = navigation->GetLoadError(); - http_status_code_ = navigation->GetHttpStatusCode(); - navigation_state_ = navigation->GetState(); - run_loop_.Quit(); - } - - base::RunLoop run_loop_; - Tab* tab_; - bool completed_ = false; - bool is_error_page_ = false; - Navigation::LoadError load_error_ = Navigation::kNoError; - int http_status_code_ = 0; - NavigationState navigation_state_ = NavigationState::kWaitingResponse; -}; - -} // namespace // The purpose of this test is to verify Variations code is correctly wired up // for WebLayer. It's not intended to replicate VariationsHttpHeadersBrowserTest @@ -87,7 +39,7 @@ class WebLayerVariationsHttpBrowserTest : public WebLayerBrowserTest { void SetUpOnMainThread() override { auto* variations_provider = - variations::VariationsHttpHeaderProvider::GetInstance(); + variations::VariationsIdsProvider::GetInstance(); variations_provider->ForceVariationIds({"12", "456", "t789"}, ""); // The test makes requests to google.com which we want to redirect to the |