diff --git a/DEPS b/DEPS index 3231285e..db62c6c 100644 --- a/DEPS +++ b/DEPS
@@ -133,11 +133,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '6c8f5b31ac49be07ce68efb176a803a38810cb44', + 'skia_revision': 'b75be23bc485b3f87c7a2d3574ad5ec57c09ad51', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'c77ce38099096de1347aa59f9d5b93191bf4b4f4', + 'v8_revision': 'ba0df1167f22ea47aff1aef9cc9ebc44320fbe8b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -145,15 +145,15 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '0f861bd8c46f7ae179c425a3be861b1bb8414dd2', + 'angle_revision': 'fc0be0494ee791c1fa914dcd260e1d64c4b726e5', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. - 'swiftshader_revision': 'd354695df3cbfebe261ee554f6db7c768f57e41b', + 'swiftshader_revision': '390d846c3e6c44c5e78e936c07902b2c6b1665cf', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '8f64f4a25dc8311f798947cf073ab152827f5328', + 'pdfium_revision': 'a3097da6c7df969dfc7ee2d93a8072d61486ac34', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling openmax_dl # and whatever else without interference from each other. @@ -200,7 +200,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '178118d233d98876cdf0cb472e7c91614cc1988f', + 'catapult_revision': 'b931deacdf624e10e33f8789de28f05e3ac56438', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -256,7 +256,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'spv_tools_revision': '32af42616abe283411c05992e88e33f08db7884b', + 'spv_tools_revision': 'c8b09744c6a15f3586c26351376bf5c6f656e89f', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -272,7 +272,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': 'cc141797dfc8fd0d10eafb96c9bfab7b341b8bd4', + 'dawn_revision': '00f6b1af41cab2dfa75a0c2759a3602307f2e9fd', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -806,7 +806,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '00aa8077cada1617a2b7b9b9cc441d1503216225', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'db2f00ebdbd640cccdf8ddf91b9995c009cfdc70', 'condition': 'checkout_linux', }, @@ -831,7 +831,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b3aca437d08fb4a2c6f656ad0a0afae3d0396cec', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '867e3c9511394f5bb2f51f09278e5ccc773adc36', 'src/third_party/devtools-node-modules': Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'), @@ -1184,7 +1184,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '7f17b0b76444d19ff5c39bede231acc23fafb348', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '4ff1283572dfd0d120ebd63eebe1189b442db23b', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78', @@ -1396,7 +1396,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c6d9391d98ae5bf66405852d2ab823f4de51d1b0', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@754d7b73cd0385866900e072289aa6d6ffd17e13', 'condition': 'checkout_src_internal', },
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc index 40fdaba..de11b69 100644 --- a/ash/app_list/app_list_controller_impl.cc +++ b/ash/app_list/app_list_controller_impl.cc
@@ -541,6 +541,10 @@ UMA_HISTOGRAM_ENUMERATION(app_list::kAppListToggleMethodHistogram, show_source); } + + for (auto& observer : observers_) + observer.OnAppListToggled(); + return action; }
diff --git a/ash/app_list/app_list_controller_observer.h b/ash/app_list/app_list_controller_observer.h index 920e69b..316fb12 100644 --- a/ash/app_list/app_list_controller_observer.h +++ b/ash/app_list/app_list_controller_observer.h
@@ -14,6 +14,9 @@ public: // Called when the AppList is shown or dismissed. virtual void OnAppListVisibilityChanged(bool shown, int64_t display_id) {} + + // Called when the AppList button is toggled. + virtual void OnAppListToggled() {} }; } // namespace ash
diff --git a/ash/assistant/util/deep_link_util.cc b/ash/assistant/util/deep_link_util.cc index c527ab93..fe204f1 100644 --- a/ash/assistant/util/deep_link_util.cc +++ b/ash/assistant/util/deep_link_util.cc
@@ -152,8 +152,10 @@ return it != params.end() ? base::Optional<std::string>(net::UnescapeURLComponent( it->second, - net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS | - net::UnescapeRule::REPLACE_PLUS_WITH_SPACE)) + net::UnescapeRule::PATH_SEPARATORS | + net::UnescapeRule::REPLACE_PLUS_WITH_SPACE | + net::UnescapeRule:: + URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS)) : base::nullopt; }
diff --git a/ash/assistant/util/deep_link_util_unittest.cc b/ash/assistant/util/deep_link_util_unittest.cc index 1e279c1..783f6b2 100644 --- a/ash/assistant/util/deep_link_util_unittest.cc +++ b/ash/assistant/util/deep_link_util_unittest.cc
@@ -65,9 +65,9 @@ {"query", "googleassistant://send-query?q=query"}, // OK: Query containing spaces and special characters. - {"query with spaces & special characters?", + {"query with / and spaces & special characters?", "googleassistant://" - "send-query?q=query+with+spaces+%26+special+characters%3F"}, + "send-query?q=query+with+%2F+and+spaces+%26+special+characters%3F"}, }; for (const auto& test_case : test_cases) { @@ -137,8 +137,8 @@ AssertDeepLinkParamEq("true", DeepLinkParam::kRelaunch); // Case: Deep link parameter present, URL encoded. - params["q"] = "query+with+spaces+%26+special+characters%3F"; - AssertDeepLinkParamEq("query with spaces & special characters?", + params["q"] = "query+with+%2F+and+spaces+%26+special+characters%3F"; + AssertDeepLinkParamEq("query with / and spaces & special characters?", DeepLinkParam::kQuery); // Case: Deep link parameters absent.
diff --git a/ash/display/window_tree_host_manager.cc b/ash/display/window_tree_host_manager.cc index 2615263..da34601 100644 --- a/ash/display/window_tree_host_manager.cc +++ b/ash/display/window_tree_host_manager.cc
@@ -27,6 +27,7 @@ #include "ash/wm/window_util.h" #include "ash/ws/window_service_owner.h" #include "base/command_line.h" +#include "base/metrics/histogram.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" @@ -291,6 +292,19 @@ } } + // Record display zoom for the primary display for https://crbug.com/955071. + // This can be removed after M79. + const display::ManagedDisplayInfo& display_info = + display_manager->GetDisplayInfo(primary_display_id); + int zoom_percent = std::round(display_info.zoom_factor() * 100); + constexpr int kMaxValue = 300; + constexpr int kBucketSize = 5; + constexpr int kBucketCount = kMaxValue / kBucketSize + 1; + base::LinearHistogram::FactoryGet( + "Ash.Display.PrimaryDisplayZoomAtStartup", kBucketSize, kMaxValue, + kBucketCount, base::HistogramBase::kUmaTargetedHistogramFlag) + ->Add(zoom_percent); + for (auto& observer : observers_) observer.OnDisplaysInitialized(); }
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc index 2c9232a3..81189c53 100644 --- a/ash/shelf/shelf_layout_manager.cc +++ b/ash/shelf/shelf_layout_manager.cc
@@ -641,6 +641,12 @@ MaybeUpdateShelfBackground(AnimationChangeType::IMMEDIATE); } +void ShelfLayoutManager::OnAppListToggled() { + // Reset the Shelf's drag status to deal with the edge case that toggling + // the AppList button while dragging the shelf in auto-hide mode. + gesture_drag_status_ = GestureDragStatus::GESTURE_DRAG_NONE; +} + void ShelfLayoutManager::OnHomeLauncherTargetPositionChanged( bool showing, int64_t display_id) {
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h index 1c72070..83d759c 100644 --- a/ash/shelf/shelf_layout_manager.h +++ b/ash/shelf/shelf_layout_manager.h
@@ -141,6 +141,7 @@ // AppListControllerObserver: void OnAppListVisibilityChanged(bool shown, int64_t display_id) override; + void OnAppListToggled() override; // HomeLauncherGestureHandlerObserver: void OnHomeLauncherTargetPositionChanged(bool showing,
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc index 3aad93f..007a08a 100644 --- a/ash/shelf/shelf_layout_manager_unittest.cc +++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -11,6 +11,7 @@ #include "ash/accelerators/accelerator_table.h" #include "ash/accessibility/accessibility_controller.h" #include "ash/accessibility/test_accessibility_controller_client.h" +#include "ash/app_list/app_list_controller_impl.h" #include "ash/app_list/test/app_list_test_helper.h" #include "ash/app_list/views/app_list_view.h" #include "ash/focus_cycler.h" @@ -22,6 +23,7 @@ #include "ash/public/cpp/shell_window_ids.h" #include "ash/public/cpp/window_properties.h" #include "ash/root_window_controller.h" +#include "ash/screen_util.h" #include "ash/session/session_controller.h" #include "ash/shelf/app_list_button.h" #include "ash/shelf/shelf.h" @@ -82,6 +84,12 @@ namespace ash { namespace { +void PressAppListButton() { + ash::Shell::Get()->app_list_controller()->OnAppListButtonPressed( + display::Screen::GetScreen()->GetPrimaryDisplay().id(), + app_list::AppListShowSource::kShelfButton, base::TimeTicks()); +} + void StepWidgetLayerAnimatorToEnd(views::Widget* widget) { widget->GetNativeView()->layer()->GetAnimator()->Step( base::TimeTicks::Now() + base::TimeDelta::FromSeconds(1)); @@ -2532,6 +2540,59 @@ TestHomeLauncherGestureHandler(/*autohide_shelf=*/true); } +// Tests that the auto-hide shelf has expected behavior when pressing the +// AppList button while the shelf is being dragged by gesture (see +// https://crbug.com/953877). +TEST_F(ShelfLayoutManagerTest, PressAppListBtnWhenShelfBeingDragged) { + // Create a widget to hide the shelf in auto-hide mode. + CreateTestWidget(); + GetPrimaryShelf()->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); + EXPECT_FALSE(GetPrimaryShelf()->IsVisible()); + + const WorkAreaInsets* const work_area = + WorkAreaInsets::ForWindow(GetShelfWidget()->GetNativeWindow()); + gfx::Rect available_bounds = screen_util::GetDisplayBoundsWithShelf( + GetShelfWidget()->GetNativeWindow()); + available_bounds.Inset(work_area->GetAccessibilityInsets()); + + // Emulate to drag the shelf to show it. + gfx::Point gesture_location = display::Screen::GetScreen() + ->GetPrimaryDisplay() + .bounds() + .bottom_center(); + int delta_y = -1; + base::TimeTicks timestamp = base::TimeTicks::Now(); + + ui::GestureEvent start_event = ui::GestureEvent( + gesture_location.x(), gesture_location.y(), ui::EF_NONE, timestamp, + ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, delta_y)); + GetPrimaryShelf()->ProcessGestureEvent(start_event); + gesture_location.Offset(0, delta_y); + delta_y = -20; + timestamp += base::TimeDelta::FromMilliseconds(200); + ui::GestureEvent update_event = ui::GestureEvent( + gesture_location.x(), gesture_location.y(), ui::EF_NONE, timestamp, + ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 0, delta_y)); + GetPrimaryShelf()->ProcessGestureEvent(update_event); + + // Emulate to press the AppList button while dragging the Shelf. + PressAppListButton(); + EXPECT_TRUE(GetPrimaryShelf()->IsVisible()); + + // Press the AppList button to hide the AppList and Shelf. Check the following + // things: + // (1) Shelf is hidden + // (2) Shelf has correct bounds in screen coordinate. + PressAppListButton(); + EXPECT_EQ(available_bounds.bottom_left() + + gfx::Point(0, -kHiddenShelfInScreenPortion).OffsetFromOrigin(), + GetPrimaryShelf() + ->GetShelfViewForTesting() + ->GetBoundsInScreen() + .origin()); + EXPECT_FALSE(GetPrimaryShelf()->IsVisible()); +} + // Tests that tap outside of the AUTO_HIDE_SHOWN shelf should hide it. TEST_F(ShelfLayoutManagerTest, TapOutsideOfAutoHideShownShelf) { views::Widget* widget = CreateTestWidget();
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h index 0a0fb37..860caac 100644 --- a/base/trace_event/builtin_categories.h +++ b/base/trace_event/builtin_categories.h
@@ -172,6 +172,7 @@ X(TRACE_DISABLED_BY_DEFAULT("file")) \ X(TRACE_DISABLED_BY_DEFAULT("fonts")) \ X(TRACE_DISABLED_BY_DEFAULT("gpu_cmd_queue")) \ + X(TRACE_DISABLED_BY_DEFAULT("gpu.dawn")) \ X(TRACE_DISABLED_BY_DEFAULT("gpu.debug")) \ X(TRACE_DISABLED_BY_DEFAULT("gpu_decoder")) \ X(TRACE_DISABLED_BY_DEFAULT("gpu.device")) \
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py index 05e2c1b..f097a4f 100755 --- a/build/android/gyp/write_build_config.py +++ b/build/android/gyp/write_build_config.py
@@ -546,7 +546,6 @@ This type corresponds to an Android app bundle (`.aab` file). --------------- END_MARKDOWN --------------------------------------------------- -TODO(estevenson): Add docs for static library synchronized proguarding. """ import collections @@ -913,6 +912,8 @@ parser.add_option('--incremental-install-json-path', help="Path to the target's generated incremental install " "json.") + + # apk options that are static library specific parser.add_option( '--static-library-dependent-configs', help='GN list of .build_configs of targets that use this target as a ' @@ -920,6 +921,18 @@ parser.add_option( '--static-library-jar-path', help='Equivalent to what normally would be the jar_path for the APK.') + parser.add_option( + '--resource-ids-provider', + help='Path to the .build_config for the APK that this static library ' + 'target uses to generate stable resource IDs.') + parser.add_option( + '--compressed-locales-provider', + help='Path to the .build_config that contains the compressed locales ' + 'Java list for this static library target.') + parser.add_option( + '--uncompressed-locales-provider', + help='Path to the .build_config that contains the uncompressed locales ' + 'Java list for this static library target.') parser.add_option('--tested-apk-config', help='Path to the build config of the tested apk (for an instrumentation ' @@ -1453,13 +1466,24 @@ path: sorted(set(classpath)) for path, classpath in classpath_entries_by_owning_config.iteritems() } - # Order matters here, must match the order passed in. - static_lib_jar_paths = [ - static_lib_jar_paths[x] - for x in options.static_library_dependent_configs - ] - static_lib_jar_paths.append(options.static_library_jar_path) - deps_info['static_library_dependent_apk_jars'] = static_lib_jar_paths + + # resource_ids_provider's jar must go first to ensure the correct R.java + # IDs are used. + ordered_static_lib_jar_paths = [] + if options.resource_ids_provider: + assert (options.resource_ids_provider in options. + static_library_dependent_configs), ( + '--resource-ids-provider must be in ' + '--static-library-dependent-configs') + ordered_static_lib_jar_paths.append( + static_lib_jar_paths[options.resource_ids_provider]) + + ordered_static_lib_jar_paths.extend( + x for x in static_lib_jar_paths.itervalues() + if x not in ordered_static_lib_jar_paths) + ordered_static_lib_jar_paths.append(options.static_library_jar_path) + deps_info[ + 'static_library_dependent_apk_jars'] = ordered_static_lib_jar_paths deps_info['static_library_proguard_mapping_output_paths'] = [ d['proguard_mapping_path'] for d in static_library_dependent_configs_by_path.itervalues() @@ -1675,10 +1699,28 @@ } config['assets'], config['uncompressed_assets'], locale_paks = ( _MergeAssets(deps.All('android_assets'))) - config['compressed_locales_java_list'] = _CreateJavaLocaleListFromAssets( - config['assets'], locale_paks) - config['uncompressed_locales_java_list'] = _CreateJavaLocaleListFromAssets( - config['uncompressed_assets'], locale_paks) + + if options.compressed_locales_provider: + dep_config = GetDepConfig(options.compressed_locales_provider) + if dep_config['type'] == 'android_app_bundle': + dep_config = GetDepConfig(dep_config['base_module_config']) + deps_info['compressed_locales_java_list'] = dep_config[ + 'compressed_locales_java_list'] + else: + deps_info[ + 'compressed_locales_java_list'] = _CreateJavaLocaleListFromAssets( + config['assets'], locale_paks) + + if options.uncompressed_locales_provider: + dep_config = GetDepConfig(options.uncompressed_locales_provider) + if dep_config['type'] == 'android_app_bundle': + dep_config = GetDepConfig(dep_config['base_module_config']) + deps_info['uncompressed_locales_java_list'] = dep_config[ + 'uncompressed_locales_java_list'] + else: + deps_info[ + 'uncompressed_locales_java_list'] = _CreateJavaLocaleListFromAssets( + config['uncompressed_assets'], locale_paks) config['extra_android_manifests'] = filter(None, ( d.get('android_manifest') for d in all_resources_deps))
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index b1842aa..42d2ccd 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -470,17 +470,26 @@ rebase_path(invoker.static_library_jar_path, root_build_dir), ] _dependent_configs = [] - foreach(d, invoker.static_library_dependent_targets) { - _target_label = get_label_info(d, "label_no_toolchain") + foreach(_target, invoker.static_library_dependent_targets) { + _target_name = _target.name + _target_label = get_label_info(_target_name, "label_no_toolchain") deps += [ "$_target_label$build_config_target_suffix" ] - _dep_gen_dir = get_label_info(d, "target_gen_dir") - _dep_name = get_label_info(d, "name") - _dependent_configs += [ "$_dep_gen_dir/$_dep_name.build_config" ] + _dep_gen_dir = get_label_info(_target_name, "target_gen_dir") + _dep_name = get_label_info(_target_name, "name") + _config = + rebase_path("$_dep_gen_dir/$_dep_name.build_config", root_build_dir) + _dependent_configs += [ _config ] + if (_target.is_resource_ids_provider) { + args += [ "--resource-ids-provider=$_config" ] + } + if (_target.is_compressed_locales_provider) { + args += [ "--compressed-locales-provider=$_config" ] + } + if (_target.is_uncompressed_locales_provider) { + args += [ "--uncompressed-locales-provider=$_config" ] + } } - _rebased_dependent_configs = - rebase_path(_dependent_configs, root_build_dir) - args += - [ "--static-library-dependent-configs=$_rebased_dependent_configs" ] + args += [ "--static-library-dependent-configs=$_dependent_configs" ] } if (defined(invoker.gradle_treat_as_prebuilt) && invoker.gradle_treat_as_prebuilt) {
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index 9c19665..b6f0ba1 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -1959,10 +1959,8 @@ _rebased_build_config = rebase_path(invoker.build_config, root_build_dir) defines += [ - "COMPRESSED_LOCALE_LIST=" + - "@FileArg($_rebased_build_config:compressed_locales_java_list)", - "UNCOMPRESSED_LOCALE_LIST=" + - "@FileArg($_rebased_build_config:uncompressed_locales_java_list)", + "COMPRESSED_LOCALE_LIST=" + "@FileArg($_rebased_build_config:deps_info:compressed_locales_java_list)", + "UNCOMPRESSED_LOCALE_LIST=" + "@FileArg($_rebased_build_config:deps_info:uncompressed_locales_java_list)", ] if (defined(invoker.firebase_app_id)) { defines += [ "_FIREBASE_APP_ID=${invoker.firebase_app_id}" ] @@ -2049,9 +2047,13 @@ # resources with acceptable/non-acceptable optimizations. # verify_android_configuration: Enables verification of expected merged # manifest and proguard flags based on a golden file. - # static_library_dependent_targets: A list of targets that use this target - # as a static library. Common Java code from the targets listed in - # static_library_dependent_targets will be moved into this target. + # static_library_dependent_targets: A list of scopes describing targets that + # use this target as a static library. Common Java code from the targets + # listed in static_library_dependent_targets will be moved into this + # target. Scope members are name, is_resource_ids_provider, + # is_compressed_locales_provider, is_uncompressed_locales_provider. + # TODO(estevenson): Add a README for static library targets and document + # additions to "deps_info" in write_build_config.py. # static_library_provider: Specifies a single target that this target will # use as a static library APK. When proguard is enabled, the # static_library_provider target will provide the dex file(s) for this @@ -2281,6 +2283,15 @@ _static_library_apk_java_target_output = _jar_path _static_library_sync_dex_path = "$_gen_dir/static_library_synchronized_proguard.classes.dex.zip" + _resource_ids_provider_deps = [] + foreach(_target, invoker.static_library_dependent_targets) { + if (_target.is_resource_ids_provider) { + assert(_resource_ids_provider_deps == [], + "Can only have 1 resource_ids_provider_dep") + _resource_ids_provider_deps += [ _target.name ] + } + } + _resource_ids_provider_dep = _resource_ids_provider_deps[0] } _uses_static_library = defined(invoker.static_library_provider) @@ -2371,7 +2382,6 @@ "png_to_webp", "resource_blacklist_exceptions", "resource_blacklist_regex", - "resource_ids_provider_dep", "resources_config_path", "shared_resources", "shared_resources_whitelist_locales", @@ -2381,6 +2391,10 @@ version_code = _version_code version_name = _version_name + if (defined(_resource_ids_provider_dep)) { + resource_ids_provider_dep = _resource_ids_provider_dep + } + if (defined(invoker.post_process_package_resources_script)) { post_process_script = invoker.post_process_package_resources_script } @@ -2749,7 +2763,7 @@ ":$_java_target", ] foreach(_dep, invoker.static_library_dependent_targets) { - _target_label = get_label_info(_dep, "label_no_toolchain") + _target_label = get_label_info(_dep.name, "label_no_toolchain") deps += [ "${_target_label}__java" ] } inputs = [ @@ -2857,7 +2871,8 @@ "static_library_dependent_classpath_configs:" + "$_rebased_build_config)", ] - foreach(_apk_as_module, invoker.static_library_dependent_targets) { + foreach(_target, invoker.static_library_dependent_targets) { + _apk_as_module = _target.name _module_config_target = "${_apk_as_module}$build_config_target_suffix" _module_gen_dir = get_label_info(_apk_as_module, "target_gen_dir") @@ -3255,7 +3270,6 @@ "proguard_jar_path", "resource_blacklist_regex", "resource_blacklist_exceptions", - "resource_ids_provider_dep", "resources_config_path", "secondary_abi_loadable_modules", "secondary_abi_shared_libraries", @@ -3370,7 +3384,6 @@ "proguard_jar_path", "resource_blacklist_exceptions", "resource_blacklist_regex", - "resource_ids_provider_dep", "resources_config_path", "secondary_abi_shared_libraries", "shared_libraries",
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index 416bcb5..4406479 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -8914675421772048816 \ No newline at end of file +8914644572533835024 \ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index 8c8fa8f4..a24f789c 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -8914678376428897744 \ No newline at end of file +8914652258186523104 \ No newline at end of file
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc index 5d4c278..19adeb1 100644 --- a/cc/layers/layer_impl.cc +++ b/cc/layers/layer_impl.cc
@@ -143,7 +143,7 @@ draw_properties_.rounded_corner_bounds, draw_properties_.clip_rect, draw_properties_.is_clipped, contents_opaque, draw_properties_.opacity, - effect_node->has_render_surface ? SkBlendMode::kSrcOver + effect_node->HasRenderSurface() ? SkBlendMode::kSrcOver : effect_node->blend_mode, GetSortingContextId()); state->is_fast_rounded_corner = draw_properties_.is_fast_rounded_corner; @@ -169,7 +169,7 @@ draw_properties().rounded_corner_bounds, draw_properties().clip_rect, draw_properties().is_clipped, contents_opaque, draw_properties().opacity, - effect_node->has_render_surface ? SkBlendMode::kSrcOver + effect_node->HasRenderSurface() ? SkBlendMode::kSrcOver : effect_node->blend_mode, GetSortingContextId()); state->is_fast_rounded_corner = draw_properties().is_fast_rounded_corner;
diff --git a/cc/layers/video_layer.h b/cc/layers/video_layer.h index 0ae52c8..45cacd34 100644 --- a/cc/layers/video_layer.h +++ b/cc/layers/video_layer.h
@@ -8,7 +8,7 @@ #include "base/callback.h" #include "cc/cc_export.h" #include "cc/layers/layer.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" namespace media { class VideoFrame; }
diff --git a/cc/layers/video_layer_impl.h b/cc/layers/video_layer_impl.h index 0f715c7..5d3aa87 100644 --- a/cc/layers/video_layer_impl.h +++ b/cc/layers/video_layer_impl.h
@@ -10,7 +10,7 @@ #include "cc/cc_export.h" #include "cc/layers/layer_impl.h" #include "components/viz/common/resources/release_callback.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" namespace media { class VideoFrame;
diff --git a/cc/test/layer_tree_pixel_test.cc b/cc/test/layer_tree_pixel_test.cc index 1027eb2..3cdd0cfe 100644 --- a/cc/test/layer_tree_pixel_test.cc +++ b/cc/test/layer_tree_pixel_test.cc
@@ -264,7 +264,7 @@ root_effect.clip_id = 1; root_effect.stable_id = 1; root_effect.transform_id = 1; - root_effect.has_render_surface = true; + root_effect.render_surface_reason = RenderSurfaceReason::kTest; property_trees->effect_tree.Insert(root_effect, 0); ScrollNode scroll_node;
diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc index e32fa24..9c1bec65 100644 --- a/cc/trees/draw_property_utils.cc +++ b/cc/trees/draw_property_utils.cc
@@ -49,7 +49,7 @@ // doesn't set effect ids on clip nodes. return; } - DCHECK(effect_node->has_render_surface); + DCHECK(effect_node->HasRenderSurface()); transform->matrix().postScale(effect_node->surface_contents_scale.x(), effect_node->surface_contents_scale.y(), 1.f); } @@ -458,7 +458,7 @@ const TransformTree& transform_tree, const EffectTree& effect_tree) { const EffectNode* effect_node = effect_tree.Node(layer->effect_tree_index()); - if (effect_node->has_render_surface && effect_node->subtree_has_copy_request) + if (effect_node->HasRenderSurface() && effect_node->subtree_has_copy_request) return false; // If the layer transform is not invertible, it should be skipped. In case the @@ -509,7 +509,7 @@ // (included) and its target surface (excluded). const EffectNode* node = tree.Node(render_surface->EffectTreeIndex()); float draw_opacity = tree.EffectiveOpacity(node); - for (node = tree.parent(node); node && !node->has_render_surface; + for (node = tree.parent(node); node && !node->HasRenderSurface(); node = tree.parent(node)) { draw_opacity *= tree.EffectiveOpacity(node); } @@ -656,7 +656,7 @@ const EffectTree* effect_tree = &property_trees->effect_tree; const EffectNode* effect_node = effect_tree->Node(layer->effect_tree_index()); const EffectNode* target_node = - effect_node->has_render_surface + effect_node->HasRenderSurface() ? effect_node : effect_tree->Node(effect_node->target_id); bool include_expanding_clips = false; @@ -676,7 +676,7 @@ // Return empty rrect if this node has a render surface but the function call // was made for a non render surface. - if (effect_node->has_render_surface && !for_render_surface) + if (effect_node->HasRenderSurface() && !for_render_surface) return kEmptyRoundedCornerInfo; // Traverse the parent chain up to the render target to find a node which has @@ -691,7 +691,7 @@ // Simply break if we reached a node that has a render surface or is the // render target. - if (node->has_render_surface || node->id == target_id) + if (node->HasRenderSurface() || node->id == target_id) break; node = effect_tree->parent(node); @@ -723,7 +723,7 @@ if (i == EffectTree::kContentsRootNodeId) { // Render target of the node corresponding to root is itself. node->target_id = EffectTree::kContentsRootNodeId; - } else if (effect_tree->parent(node)->has_render_surface) { + } else if (effect_tree->parent(node)->HasRenderSurface()) { node->target_id = node->parent_id; } else { node->target_id = effect_tree->parent(node)->target_id; @@ -765,7 +765,7 @@ void ConcatInverseSurfaceContentsScale(const EffectNode* effect_node, gfx::Transform* transform) { - DCHECK(effect_node->has_render_surface); + DCHECK(effect_node->HasRenderSurface()); if (effect_node->surface_contents_scale.x() != 0.0 && effect_node->surface_contents_scale.y() != 0.0) transform->Scale(1.0 / effect_node->surface_contents_scale.x(),
diff --git a/cc/trees/effect_node.cc b/cc/trees/effect_node.cc index 3701e4c..acf8f94 100644 --- a/cc/trees/effect_node.cc +++ b/cc/trees/effect_node.cc
@@ -17,7 +17,6 @@ screen_space_opacity(1.f), backdrop_filter_quality(1.f), blend_mode(SkBlendMode::kSrcOver), - has_render_surface(false), cache_render_surface(false), has_copy_request(false), hidden_by_backface_visibility(false), @@ -34,6 +33,7 @@ effect_changed(false), subtree_has_copy_request(false), is_fast_rounded_corner(false), + render_surface_reason(RenderSurfaceReason::kNone), transform_id(0), clip_id(0), target_id(1), @@ -48,7 +48,6 @@ stable_id == other.stable_id && opacity == other.opacity && screen_space_opacity == other.screen_space_opacity && backdrop_filter_quality == other.backdrop_filter_quality && - has_render_surface == other.has_render_surface && cache_render_surface == other.cache_render_surface && has_copy_request == other.has_copy_request && filters == other.filters && @@ -57,6 +56,9 @@ filters_origin == other.filters_origin && rounded_corner_bounds == other.rounded_corner_bounds && is_fast_rounded_corner == other.is_fast_rounded_corner && + // The specific reason is just for tracing/testing/debugging, so just + // check whether a render surface is needed. + HasRenderSurface() == other.HasRenderSurface() && blend_mode == other.blend_mode && surface_contents_scale == other.surface_contents_scale && unscaled_mask_target_size == other.unscaled_mask_target_size && @@ -83,6 +85,54 @@ other.closest_ancestor_with_copy_request_id; } +const char* RenderSurfaceReasonToString(RenderSurfaceReason reason) { + switch (reason) { + case RenderSurfaceReason::kNone: + return "none"; + case RenderSurfaceReason::kRoot: + return "root"; + case RenderSurfaceReason::k3dTransformFlattening: + return "3d transform flattening"; + case RenderSurfaceReason::kBlendMode: + return "blend mode"; + case RenderSurfaceReason::kBlendModeDstIn: + return "blend mode kDstIn"; + case RenderSurfaceReason::kOpacity: + return "opacity"; + case RenderSurfaceReason::kOpacityAnimation: + return "opacity animation"; + case RenderSurfaceReason::kFilter: + return "filter"; + case RenderSurfaceReason::kFilterAnimation: + return "filter animation"; + case RenderSurfaceReason::kBackdropFilter: + return "backdrop filter"; + case RenderSurfaceReason::kBackdropFilterAnimation: + return "backdrop filter animation"; + case RenderSurfaceReason::kRoundedCorner: + return "rounded corner"; + case RenderSurfaceReason::kClipPath: + return "clip path"; + case RenderSurfaceReason::kClipAxisAlignment: + return "clip axis alignment"; + case RenderSurfaceReason::kMask: + return "mask"; + case RenderSurfaceReason::kRootOrIsolatedGroup: + return "root or isolated group"; + case RenderSurfaceReason::kTrilinearFiltering: + return "trilinear filtering"; + case RenderSurfaceReason::kCache: + return "cache"; + case RenderSurfaceReason::kCopyRequest: + return "copy request"; + case RenderSurfaceReason::kTest: + return "test"; + default: + NOTREACHED() << static_cast<int>(reason); + return ""; + } +} + void EffectNode::AsValueInto(base::trace_event::TracedValue* value) const { value->SetInteger("id", id); value->SetInteger("parent_id", parent_id); @@ -91,7 +141,6 @@ value->SetDouble("backdrop_filter_quality", backdrop_filter_quality); value->SetBoolean("is_fast_rounded_corner", is_fast_rounded_corner); value->SetString("blend_mode", SkBlendMode_Name(blend_mode)); - value->SetBoolean("has_render_surface", has_render_surface); value->SetBoolean("cache_render_surface", cache_render_surface); value->SetBoolean("has_copy_request", has_copy_request); value->SetBoolean("double_sided", double_sided); @@ -104,7 +153,10 @@ value->SetBoolean("has_masking_child", has_masking_child); value->SetBoolean("is_masked", is_masked); value->SetBoolean("effect_changed", effect_changed); - value->SetInteger("subtree_has_copy_request", subtree_has_copy_request); + value->SetBoolean("subtree_has_copy_request", subtree_has_copy_request); + value->SetBoolean("is_fast_rounded_corner", is_fast_rounded_corner); + value->SetString("render_surface_reason", + RenderSurfaceReasonToString(render_surface_reason)); value->SetInteger("transform_id", transform_id); value->SetInteger("clip_id", clip_id); value->SetInteger("target_id", target_id);
diff --git a/cc/trees/effect_node.h b/cc/trees/effect_node.h index f9ad9713..46d285e9 100644 --- a/cc/trees/effect_node.h +++ b/cc/trees/effect_node.h
@@ -20,6 +20,33 @@ namespace cc { +enum class RenderSurfaceReason : uint8_t { + kNone, + kRoot, + k3dTransformFlattening, + kBlendMode, + kBlendModeDstIn, + kOpacity, + kOpacityAnimation, + kFilter, + kFilterAnimation, + kBackdropFilter, + kBackdropFilterAnimation, + kRoundedCorner, + kClipPath, + kClipAxisAlignment, + kMask, + kRootOrIsolatedGroup, + kTrilinearFiltering, + kCache, + kCopyRequest, + // This must be the last value because it's used in tracing code to know the + // number of reasons. + kTest, +}; + +CC_EXPORT const char* RenderSurfaceReasonToString(RenderSurfaceReason); + struct CC_EXPORT EffectNode { EffectNode(); EffectNode(const EffectNode& other); @@ -55,7 +82,6 @@ gfx::Size unscaled_mask_target_size; - bool has_render_surface : 1; bool cache_render_surface : 1; bool has_copy_request : 1; bool hidden_by_backface_visibility : 1; @@ -85,7 +111,10 @@ bool subtree_has_copy_request : 1; // If set, the effect node tries to not trigger a render surface due to it // having a rounded corner. - bool is_fast_rounded_corner; + bool is_fast_rounded_corner : 1; + // RenderSurfaceReason::kNone if this effect node should not create a render + // surface, or the reason that this effect node should create one. + RenderSurfaceReason render_surface_reason; // The transform node index of the transform to apply to this effect // node's content when rendering to a surface. int transform_id; @@ -102,6 +131,10 @@ int closest_ancestor_with_cached_render_surface_id; int closest_ancestor_with_copy_request_id; + bool HasRenderSurface() const { + return render_surface_reason != RenderSurfaceReason::kNone; + } + bool operator==(const EffectNode& other) const; void AsValueInto(base::trace_event::TracedValue* value) const;
diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc index e46a166..6eb7ec0 100644 --- a/cc/trees/layer_tree_host_common.cc +++ b/cc/trees/layer_tree_host_common.cc
@@ -489,6 +489,38 @@ render_surface_list); } +static void RecordRenderSurfaceReasonsForTracing( + const PropertyTrees* property_trees, + const RenderSurfaceList* render_surface_list) { + static const auto* tracing_enabled = + TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("cc"); + if (!*tracing_enabled || + // Don't output single root render surface. + render_surface_list->size() <= 1) + return; + + TRACE_EVENT_INSTANT1("cc", "RenderSurfaceReasonCount", + TRACE_EVENT_SCOPE_THREAD, "total", + render_surface_list->size()); + + // kTest is the last value which is not included for tracing. + constexpr auto kNumReasons = static_cast<size_t>(RenderSurfaceReason::kTest); + int reason_counts[kNumReasons] = {0}; + for (const auto* render_surface : *render_surface_list) { + const auto* effect_node = + property_trees->effect_tree.Node(render_surface->EffectTreeIndex()); + reason_counts[static_cast<size_t>(effect_node->render_surface_reason)]++; + } + for (size_t i = 0; i < kNumReasons; i++) { + if (!reason_counts[i]) + continue; + TRACE_EVENT_INSTANT1( + "cc", "RenderSurfaceReasonCount", TRACE_EVENT_SCOPE_THREAD, + RenderSurfaceReasonToString(static_cast<RenderSurfaceReason>(i)), + reason_counts[i]); + } +} + void CalculateDrawPropertiesInternal( LayerTreeHostCommon::CalcDrawPropsImplInputs* inputs, PropertyTreeOption property_tree_option) { @@ -604,6 +636,8 @@ inputs->root_layer->layer_tree_impl(), inputs->property_trees, inputs->render_surface_list, inputs->max_texture_size); } + RecordRenderSurfaceReasonsForTracing(inputs->property_trees, + inputs->render_surface_list); // A root layer render_surface should always exist after // CalculateDrawProperties.
diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc index 37d6f1c..93750755 100644 --- a/cc/trees/layer_tree_host_common_unittest.cc +++ b/cc/trees/layer_tree_host_common_unittest.cc
@@ -1329,7 +1329,7 @@ EffectTree& effect_tree = root->layer_tree_impl()->property_trees()->effect_tree; EffectNode* node = effect_tree.Node(clips_subtree->effect_tree_index()); - EXPECT_TRUE(node->has_render_surface); + EXPECT_TRUE(node->HasRenderSurface()); } TEST_F(LayerTreeHostCommonTest, EffectNodesForNonAxisAlignedClips) { @@ -3852,7 +3852,7 @@ EXPECT_EQ(GetRenderSurface(front_facing_child), GetRenderSurface(root)); EXPECT_EQ(GetRenderSurface(back_facing_child), GetRenderSurface(root)); EXPECT_NE(GetRenderSurface(front_facing_surface), GetRenderSurface(root)); - // We expect that a has_render_surface was created but not used. + // We expect that a render surface was created but not used. EXPECT_NE(GetRenderSurface(back_facing_surface), GetRenderSurface(root)); EXPECT_NE(GetRenderSurface(back_facing_surface), GetRenderSurface(front_facing_surface)); @@ -10696,7 +10696,7 @@ const EffectNode* effect_node = effect_tree.Node(rounded_corner_layer_1->effect_tree_index()); gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds; - EXPECT_FALSE(effect_node->has_render_surface); + EXPECT_FALSE(effect_node->HasRenderSurface()); EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), kRoundedCorner1Radius); EXPECT_EQ(rounded_corner_bounds_1.rect(), @@ -10706,7 +10706,7 @@ // surface. It also has 2 descendants that draw. effect_node = effect_tree.Node(rounded_corner_layer_2->effect_tree_index()); gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds; - EXPECT_TRUE(effect_node->has_render_surface); + EXPECT_TRUE(effect_node->HasRenderSurface()); EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), kRoundedCorner2Radius); EXPECT_EQ(rounded_corner_bounds_2.rect(), @@ -10716,7 +10716,7 @@ // the creation of a render surface. effect_node = effect_tree.Node(rounded_corner_layer_3->effect_tree_index()); gfx::RRectF rounded_corner_bounds_3 = effect_node->rounded_corner_bounds; - EXPECT_TRUE(effect_node->has_render_surface); + EXPECT_TRUE(effect_node->HasRenderSurface()); EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(), kRoundedCorner3Radius); EXPECT_EQ(rounded_corner_bounds_3.rect(), @@ -10726,7 +10726,7 @@ // rounded corner, it does not need a render surface. effect_node = effect_tree.Node(rounded_corner_layer_4->effect_tree_index()); gfx::RRectF rounded_corner_bounds_4 = effect_node->rounded_corner_bounds; - EXPECT_FALSE(effect_node->has_render_surface); + EXPECT_FALSE(effect_node->HasRenderSurface()); EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(), kRoundedCorner4Radius); EXPECT_EQ(rounded_corner_bounds_4.rect(), @@ -10891,7 +10891,7 @@ const EffectNode* effect_node = effect_tree.Node(rounded_corner_layer_1->effect_tree_index()); gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds; - EXPECT_FALSE(effect_node->has_render_surface); + EXPECT_FALSE(effect_node->HasRenderSurface()); EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), kRoundedCorner1Radius); EXPECT_EQ(rounded_corner_bounds_1.rect(), @@ -10901,7 +10901,7 @@ // has a rounded corner, it does not need a render surface. effect_node = effect_tree.Node(rounded_corner_layer_2->effect_tree_index()); gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds; - EXPECT_FALSE(effect_node->has_render_surface); + EXPECT_FALSE(effect_node->HasRenderSurface()); EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), kRoundedCorner2Radius); EXPECT_EQ(rounded_corner_bounds_2.rect(), @@ -11018,7 +11018,7 @@ const EffectNode* effect_node = effect_tree.Node(rounded_corner_layer_1->effect_tree_index()); gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds; - EXPECT_TRUE(effect_node->has_render_surface); + EXPECT_TRUE(effect_node->HasRenderSurface()); EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), kRoundedCorner1Radius); EXPECT_EQ(rounded_corner_bounds_1.rect(), @@ -11028,7 +11028,7 @@ // has a rounded corner, it does not need a render surface. effect_node = effect_tree.Node(rounded_corner_layer_2->effect_tree_index()); gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds; - EXPECT_FALSE(effect_node->has_render_surface); + EXPECT_FALSE(effect_node->HasRenderSurface()); EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), kRoundedCorner2Radius); EXPECT_EQ(rounded_corner_bounds_2.rect(), @@ -11169,7 +11169,7 @@ const EffectNode* effect_node = effect_tree.Node(fast_rounded_corner_layer->effect_tree_index()); gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds; - EXPECT_FALSE(effect_node->has_render_surface); + EXPECT_FALSE(effect_node->HasRenderSurface()); EXPECT_TRUE(effect_node->is_fast_rounded_corner); EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), kRoundedCorner1Radius); @@ -11179,7 +11179,7 @@ // Since this node has 2 descendants that draw, it will have a rounded corner. effect_node = effect_tree.Node(rounded_corner_layer->effect_tree_index()); gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds; - EXPECT_TRUE(effect_node->has_render_surface); + EXPECT_TRUE(effect_node->HasRenderSurface()); EXPECT_FALSE(effect_node->is_fast_rounded_corner); EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), kRoundedCorner2Radius); @@ -11346,7 +11346,7 @@ const EffectNode* effect_node = effect_tree.Node(rounded_corner_layer_1->effect_tree_index()); gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds; - EXPECT_TRUE(effect_node->has_render_surface); + EXPECT_TRUE(effect_node->HasRenderSurface()); EXPECT_FALSE(effect_node->is_fast_rounded_corner); EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), kRoundedCorner1Radius); @@ -11358,7 +11358,7 @@ effect_node = effect_tree.Node(fast_rounded_corner_layer_2->effect_tree_index()); gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds; - EXPECT_FALSE(effect_node->has_render_surface); + EXPECT_FALSE(effect_node->HasRenderSurface()); EXPECT_TRUE(effect_node->is_fast_rounded_corner); EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), kRoundedCorner2Radius); @@ -11369,7 +11369,7 @@ // render surface. effect_node = effect_tree.Node(rounded_corner_layer_3->effect_tree_index()); gfx::RRectF rounded_corner_bounds_3 = effect_node->rounded_corner_bounds; - EXPECT_TRUE(effect_node->has_render_surface); + EXPECT_TRUE(effect_node->HasRenderSurface()); EXPECT_FALSE(effect_node->is_fast_rounded_corner); EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(), kRoundedCorner3Radius); @@ -11379,7 +11379,7 @@ // Since this layer no descendants, it would no thave a render pass. effect_node = effect_tree.Node(rounded_corner_layer_4->effect_tree_index()); gfx::RRectF rounded_corner_bounds_4 = effect_node->rounded_corner_bounds; - EXPECT_FALSE(effect_node->has_render_surface); + EXPECT_FALSE(effect_node->HasRenderSurface()); EXPECT_FALSE(effect_node->is_fast_rounded_corner); EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(), kRoundedCorner4Radius); @@ -11554,7 +11554,7 @@ const EffectNode* effect_node = effect_tree.Node(fast_rounded_corner_layer_1->effect_tree_index()); gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds; - EXPECT_TRUE(effect_node->has_render_surface); + EXPECT_TRUE(effect_node->HasRenderSurface()); EXPECT_TRUE(effect_node->is_fast_rounded_corner); EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), kRoundedCorner1Radius); @@ -11565,7 +11565,7 @@ // not have a render surface. effect_node = effect_tree.Node(rounded_corner_layer_1->effect_tree_index()); gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds; - EXPECT_FALSE(effect_node->has_render_surface); + EXPECT_FALSE(effect_node->HasRenderSurface()); EXPECT_FALSE(effect_node->is_fast_rounded_corner); EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), kRoundedCorner2Radius); @@ -11576,7 +11576,7 @@ // render surface. effect_node = effect_tree.Node(rounded_corner_layer_2->effect_tree_index()); gfx::RRectF rounded_corner_bounds_3 = effect_node->rounded_corner_bounds; - EXPECT_TRUE(effect_node->has_render_surface); + EXPECT_TRUE(effect_node->HasRenderSurface()); EXPECT_FALSE(effect_node->is_fast_rounded_corner); EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(), kRoundedCorner3Radius); @@ -11586,7 +11586,7 @@ // Since this layer has no descendant, it does not need a render surface. effect_node = effect_tree.Node(rounded_corner_layer_3->effect_tree_index()); gfx::RRectF rounded_corner_bounds_4 = effect_node->rounded_corner_bounds; - EXPECT_FALSE(effect_node->has_render_surface); + EXPECT_FALSE(effect_node->HasRenderSurface()); EXPECT_FALSE(effect_node->is_fast_rounded_corner); EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(), kRoundedCorner4Radius);
diff --git a/cc/trees/layer_tree_host_pixeltest_masks.cc b/cc/trees/layer_tree_host_pixeltest_masks.cc index dc63c71..aac9b10 100644 --- a/cc/trees/layer_tree_host_pixeltest_masks.cc +++ b/cc/trees/layer_tree_host_pixeltest_masks.cc
@@ -109,7 +109,7 @@ EffectNode isolation_effect; isolation_effect.clip_id = 1; isolation_effect.stable_id = 2; - isolation_effect.has_render_surface = true; + isolation_effect.render_surface_reason = RenderSurfaceReason::kTest; isolation_effect.transform_id = 1; property_trees.effect_tree.Insert(isolation_effect, 1); @@ -167,7 +167,7 @@ EffectNode isolation_effect; isolation_effect.clip_id = 1; isolation_effect.stable_id = 2; - isolation_effect.has_render_surface = true; + isolation_effect.render_surface_reason = RenderSurfaceReason::kTest; isolation_effect.transform_id = 1; property_trees.effect_tree.Insert(isolation_effect, 1); @@ -250,7 +250,7 @@ EffectNode isolation_effect; isolation_effect.clip_id = 1; isolation_effect.stable_id = 2; - isolation_effect.has_render_surface = true; + isolation_effect.render_surface_reason = RenderSurfaceReason::kTest; isolation_effect.transform_id = 1; property_trees.effect_tree.Insert(isolation_effect, 1); @@ -313,7 +313,7 @@ EffectNode isolation_effect; isolation_effect.clip_id = 1; isolation_effect.stable_id = 2; - isolation_effect.has_render_surface = true; + isolation_effect.render_surface_reason = RenderSurfaceReason::kTest; isolation_effect.transform_id = 1; property_trees.effect_tree.Insert(isolation_effect, 1); @@ -322,7 +322,7 @@ mask_effect.stable_id = 3; mask_effect.transform_id = 1; mask_effect.blend_mode = SkBlendMode::kDstIn; - mask_effect.has_render_surface = true; + mask_effect.render_surface_reason = RenderSurfaceReason::kTest; property_trees.effect_tree.Insert(mask_effect, 2); scoped_refptr<SolidColorLayer> background = @@ -378,7 +378,7 @@ EffectNode isolation_effect; isolation_effect.clip_id = 1; isolation_effect.stable_id = 2; - isolation_effect.has_render_surface = true; + isolation_effect.render_surface_reason = RenderSurfaceReason::kTest; isolation_effect.transform_id = 1; property_trees.effect_tree.Insert(isolation_effect, 1); @@ -426,7 +426,7 @@ EffectNode isolation_effect; isolation_effect.clip_id = 1; isolation_effect.stable_id = 2; - isolation_effect.has_render_surface = true; + isolation_effect.render_surface_reason = RenderSurfaceReason::kTest; isolation_effect.transform_id = 1; property_trees.effect_tree.Insert(isolation_effect, 1); @@ -502,7 +502,7 @@ EffectNode isolation_effect; isolation_effect.clip_id = 1; isolation_effect.stable_id = 2; - isolation_effect.has_render_surface = true; + isolation_effect.render_surface_reason = RenderSurfaceReason::kTest; isolation_effect.transform_id = 1; property_trees.effect_tree.Insert(isolation_effect, 1); @@ -562,7 +562,7 @@ EffectNode isolation_effect; isolation_effect.clip_id = 1; isolation_effect.stable_id = 2; - isolation_effect.has_render_surface = true; + isolation_effect.render_surface_reason = RenderSurfaceReason::kTest; isolation_effect.transform_id = 1; property_trees.effect_tree.Insert(isolation_effect, 1);
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index 25d93cc..8bab2e6 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc
@@ -1581,7 +1581,8 @@ // This behavior is specific to Android WebView, which only uses // multi-threaded compositor. -MULTI_THREAD_TEST_F(LayerTreeHostTestPrepareTilesWithoutDraw); +// Flaky: https://crbug.com/947673 +// MULTI_THREAD_TEST_F(LayerTreeHostTestPrepareTilesWithoutDraw); // Verify CanDraw() is false until first commit. class LayerTreeHostTestCantDrawBeforeCommit : public LayerTreeHostTest {
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc index 9801773..d75cf91d 100644 --- a/cc/trees/property_tree.cc +++ b/cc/trees/property_tree.cc
@@ -831,13 +831,13 @@ // when we actually encounter a masking child. node->has_masking_child = false; if (node->blend_mode == SkBlendMode::kDstIn) { - DCHECK(parent_node->has_render_surface); + DCHECK(parent_node->HasRenderSurface()); parent_node->has_masking_child = true; } } void EffectTree::UpdateSurfaceContentsScale(EffectNode* effect_node) { - if (!effect_node->has_render_surface) { + if (!effect_node->HasRenderSurface()) { effect_node->surface_contents_scale = gfx::Vector2dF(1.0f, 1.0f); return; } @@ -951,7 +951,7 @@ int node_id, std::vector<std::unique_ptr<viz::CopyOutputRequest>>* requests) { EffectNode* effect_node = Node(node_id); - DCHECK(effect_node->has_render_surface); + DCHECK(effect_node->HasRenderSurface()); DCHECK(effect_node->has_copy_request); // The area needs to be transformed from the space of content that draws to @@ -1071,7 +1071,7 @@ for (int id = kContentsRootNodeId; id < static_cast<int>(size()); ++id) { EffectNode* effect_node = Node(id); bool needs_render_surface = - id == kContentsRootNodeId || effect_node->has_render_surface; + id == kContentsRootNodeId || effect_node->HasRenderSurface(); if (needs_render_surface == !!render_surfaces_[id]) continue; @@ -1120,7 +1120,7 @@ std::vector<std::pair<uint64_t, int>> stable_id_node_id_list; for (int id = kContentsRootNodeId; id < static_cast<int>(size()); ++id) { EffectNode* node = Node(id); - if (node->has_render_surface) { + if (node->HasRenderSurface()) { stable_id_node_id_list.push_back( std::make_pair(node->stable_id, node->id)); }
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc index bf78d54..e5f34003 100644 --- a/cc/trees/property_tree_builder.cc +++ b/cc/trees/property_tree_builder.cc
@@ -708,11 +708,11 @@ return layer->test_properties()->cache_render_surface; } -static inline bool ForceRenderSurface(Layer* layer) { +static inline bool ForceRenderSurfaceForTesting(Layer* layer) { return layer->force_render_surface_for_testing(); } -static inline bool ForceRenderSurface(LayerImpl* layer) { +static inline bool ForceRenderSurfaceForTesting(LayerImpl* layer) { return layer->test_properties()->force_render_surface; } @@ -838,40 +838,40 @@ } template <typename LayerType> -bool ShouldCreateRenderSurface(const MutatorHost& mutator_host, - LayerType* layer, - gfx::Transform current_transform, - bool animation_axis_aligned) { +RenderSurfaceReason ComputeRenderSurfaceReason(const MutatorHost& mutator_host, + LayerType* layer, + gfx::Transform current_transform, + bool animation_axis_aligned) { const bool preserves_2d_axis_alignment = current_transform.Preserves2dAxisAlignment() && animation_axis_aligned; const bool is_root = !LayerParent(layer); if (is_root) - return true; + return RenderSurfaceReason::kRoot; // If the layer uses a mask. if (MaskLayer(layer)) { - return true; + return RenderSurfaceReason::kMask; } // If the layer uses trilinear filtering. if (TrilinearFiltering(layer)) { - return true; + return RenderSurfaceReason::kTrilinearFiltering; } // If the layer uses a CSS filter. if (!Filters(layer).IsEmpty()) { - return true; + return RenderSurfaceReason::kFilter; } // If the layer uses a CSS backdrop-filter. if (!BackdropFilters(layer).IsEmpty()) { - return true; + return RenderSurfaceReason::kBackdropFilter; } // If the layer will use a CSS filter. In this case, the animation // will start and add a filter to this layer, so it needs a surface. if (HasPotentiallyRunningFilterAnimation(mutator_host, layer)) { - return true; + return RenderSurfaceReason::kFilterAnimation; } int num_descendants_that_draw_content = NumDescendantsThatDrawContent(layer); @@ -880,15 +880,12 @@ // parent (i.e. parent participates in a 3D rendering context). if (LayerIsInExisting3DRenderingContext(layer) && ShouldFlattenTransform(layer) && num_descendants_that_draw_content > 0) { - TRACE_EVENT_INSTANT0( - "cc", "PropertyTreeBuilder::ShouldCreateRenderSurface flattening", - TRACE_EVENT_SCOPE_THREAD); - return true; + return RenderSurfaceReason::k3dTransformFlattening; } if (!IsFastRoundedCorner(layer) && HasRoundedCorner(layer) && num_descendants_that_draw_content > 1) { - return true; + return RenderSurfaceReason::kRoundedCorner; } // If the layer has blending. @@ -896,20 +893,14 @@ // types of quads than viz::RenderPassDrawQuad. Layers having descendants that // draw content will still create a separate rendering surface. if (BlendMode(layer) != SkBlendMode::kSrcOver) { - TRACE_EVENT_INSTANT0( - "cc", "PropertyTreeBuilder::ShouldCreateRenderSurface blending", - TRACE_EVENT_SCOPE_THREAD); - return true; + return RenderSurfaceReason::kBlendMode; } // If the layer clips its descendants but it is not axis-aligned with respect // to its parent. bool layer_clips_external_content = LayerClipsSubtree(layer); if (layer_clips_external_content && !preserves_2d_axis_alignment && num_descendants_that_draw_content > 0) { - TRACE_EVENT_INSTANT0( - "cc", "PropertyTreeBuilder::ShouldCreateRenderSurface clipping", - TRACE_EVENT_SCOPE_THREAD); - return true; + return RenderSurfaceReason::kClipAxisAlignment; } // If the layer has some translucency and does not have a preserves-3d @@ -926,11 +917,8 @@ HasPotentiallyRunningOpacityAnimation(mutator_host, layer); if (may_have_transparency && ShouldFlattenTransform(layer) && at_least_two_layers_in_subtree_draw_content) { - TRACE_EVENT_INSTANT0( - "cc", "PropertyTreeBuilder::ShouldCreateRenderSurface opacity", - TRACE_EVENT_SCOPE_THREAD); DCHECK(!is_root); - return true; + return RenderSurfaceReason::kOpacity; } // If the layer has isolation. // TODO(rosca): to be optimized - create separate rendering surface only when @@ -938,25 +926,22 @@ // (layer has transparent background or descendants overflow). // https://code.google.com/p/chromium/issues/detail?id=301738 if (IsRootForIsolatedGroup(layer)) { - TRACE_EVENT_INSTANT0( - "cc", "PropertyTreeBuilder::ShouldCreateRenderSurface isolation", - TRACE_EVENT_SCOPE_THREAD); - return true; + return RenderSurfaceReason::kRootOrIsolatedGroup; } // If we force it. - if (ForceRenderSurface(layer)) - return true; + if (ForceRenderSurfaceForTesting(layer)) + return RenderSurfaceReason::kTest; // If we cache it. if (CacheRenderSurface(layer)) - return true; + return RenderSurfaceReason::kCache; // If we'll make a copy of the layer's contents. if (HasCopyRequest(layer)) - return true; + return RenderSurfaceReason::kCopyRequest; - return false; + return RenderSurfaceReason::kNone; } static void TakeCopyRequests( @@ -1020,10 +1005,12 @@ data_for_children->animation_axis_aligned_since_render_target &= AnimationsPreserveAxisAlignment(mutator_host_, layer); data_for_children->compound_transform_since_render_target *= Transform(layer); - const bool should_create_render_surface = ShouldCreateRenderSurface( + auto render_surface_reason = ComputeRenderSurfaceReason( mutator_host_, layer, data_for_children->compound_transform_since_render_target, data_for_children->animation_axis_aligned_since_render_target); + bool should_create_render_surface = + render_surface_reason != RenderSurfaceReason::kNone; bool not_axis_aligned_since_last_clip = data_from_ancestor.not_axis_aligned_since_last_clip @@ -1055,7 +1042,6 @@ node->opacity = Opacity(layer); node->blend_mode = BlendMode(layer); node->unscaled_mask_target_size = layer->bounds(); - node->has_render_surface = should_create_render_surface; node->cache_render_surface = CacheRenderSurface(layer); node->has_copy_request = HasCopyRequest(layer); node->filters = Filters(layer); @@ -1073,6 +1059,7 @@ node->is_currently_animating_filter = FilterIsAnimating(mutator_host_, layer); node->effect_changed = PropertyChanged(layer); node->subtree_has_copy_request = SubtreeHasCopyRequest(layer); + node->render_surface_reason = render_surface_reason; node->closest_ancestor_with_cached_render_surface_id = CacheRenderSurface(layer) ? node_id @@ -1172,7 +1159,7 @@ // single rrect per quad at draw time, it would be unable to handle // intersections thus resulting in artifacts. if (subtree_has_rounded_corner && has_rounded_corner) - effect_node->has_render_surface = true; + effect_node->render_surface_reason = RenderSurfaceReason::kRoundedCorner; // Inform the parent that its subtree has rounded corners if one of the two // scenario is true: @@ -1183,9 +1170,9 @@ // surface of its own to prevent blending artifacts due to intersecting // rounded corners. *data_for_children->subtree_has_rounded_corner = - (subtree_has_rounded_corner && !effect_node->has_render_surface) || + (subtree_has_rounded_corner && !effect_node->HasRenderSurface()) || has_rounded_corner; - return effect_node->has_render_surface; + return effect_node->HasRenderSurface(); } static inline bool UserScrollableHorizontal(Layer* layer) {
diff --git a/cc/trees/property_tree_unittest.cc b/cc/trees/property_tree_unittest.cc index 1e0a02a..848d6e0 100644 --- a/cc/trees/property_tree_unittest.cc +++ b/cc/trees/property_tree_unittest.cc
@@ -195,7 +195,8 @@ int grand_parent = tree.Insert(TransformNode(), 0); int effect_grand_parent = effect_tree.Insert(EffectNode(), 0); - effect_tree.Node(effect_grand_parent)->has_render_surface = true; + effect_tree.Node(effect_grand_parent)->render_surface_reason = + RenderSurfaceReason::kTest; effect_tree.Node(effect_grand_parent)->transform_id = grand_parent; effect_tree.Node(effect_grand_parent)->surface_contents_scale = gfx::Vector2dF(1.f, 1.f); @@ -207,7 +208,8 @@ int parent = tree.Insert(TransformNode(), grand_parent); int effect_parent = effect_tree.Insert(EffectNode(), effect_grand_parent); effect_tree.Node(effect_parent)->transform_id = parent; - effect_tree.Node(effect_parent)->has_render_surface = true; + effect_tree.Node(effect_parent)->render_surface_reason = + RenderSurfaceReason::kTest; effect_tree.Node(effect_parent)->surface_contents_scale = gfx::Vector2dF(1.f, 1.f); tree.Node(parent)->source_node_id = grand_parent; @@ -493,7 +495,8 @@ int parent = tree.Insert(TransformNode(), 0); int effect_parent = effect_tree.Insert(EffectNode(), 0); - effect_tree.Node(effect_parent)->has_render_surface = true; + effect_tree.Node(effect_parent)->render_surface_reason = + RenderSurfaceReason::kTest; effect_tree.Node(effect_parent)->surface_contents_scale = gfx::Vector2dF(1.f, 1.f); tree.Node(parent)->scrolls = true; @@ -547,7 +550,7 @@ EffectTree& effect_tree = property_trees.effect_tree; EffectNode effect_node; - effect_node.has_render_surface = true; + effect_node.render_surface_reason = RenderSurfaceReason::kTest; effect_node.has_copy_request = true; effect_node.transform_id = contents_root.id; effect_node.id = effect_tree.Insert(effect_node, 0); @@ -646,7 +649,7 @@ EffectTree& effect_tree = property_trees.effect_tree; EffectNode effect_node; - effect_node.has_render_surface = true; + effect_node.render_surface_reason = RenderSurfaceReason::kTest; effect_node.has_copy_request = true; effect_node.transform_id = contents_root.id; effect_node.id = effect_tree.Insert(effect_node, 0);
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 8a553a2..99c043b 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -475,6 +475,8 @@ java_group("chrome_all_java") { deps = [ ":chrome_java", + "//chrome/android/features/autofill_assistant:animated_poodle_java", + "//chrome/android/features/autofill_assistant:java", "//chrome/android/features/keyboard_accessory:internal_java", "//chrome/android/features/media_router:java", ] @@ -1763,7 +1765,6 @@ "module_name", "verify_android_configuration", "proguard_jar_path", - "resource_ids_provider_dep", "static_library_provider", "target_type", "use_trichrome_library", @@ -1815,13 +1816,18 @@ android_manifest_dep = ":trichrome_library_android_manifest" if (trichrome_synchronized_proguard) { static_library_dependent_targets = [ - "//android_webview:trichrome_webview_apk", - - # Webview must be listed first WebView's R classes take precedence. - # TODO(http://crbug.com/901465): Make this less subtle by handling - # order in writing_build_config.py by prioritizing the entry that - # matches resource_ids_provider_dep. - ":trichrome_chrome_apk", + { + name = "//android_webview:trichrome_webview_apk" + is_resource_ids_provider = true + is_compressed_locales_provider = false + is_uncompressed_locales_provider = true + }, + { + name = ":trichrome_chrome_apk" + is_resource_ids_provider = false + is_compressed_locales_provider = true + is_uncompressed_locales_provider = false + }, ] } } @@ -1834,13 +1840,18 @@ android_manifest_dep = ":trichrome_library_android_manifest" if (trichrome_synchronized_proguard) { static_library_dependent_targets = [ - "//android_webview:trichrome_webview_for_bundle_apk", - - # Webview must be listed first WebView's R classes take precedence. - # TODO(http://crbug.com/901465): Make this less subtle by handling - # order in write_build_config.py by prioritizing the entry that - # matches resource_ids_provider_dep. - ":trichrome_chrome_bundle", + { + name = "//android_webview:trichrome_webview_for_bundle_apk" + is_resource_ids_provider = true + is_compressed_locales_provider = false + is_uncompressed_locales_provider = true + }, + { + name = ":trichrome_chrome_bundle" + is_resource_ids_provider = false + is_compressed_locales_provider = true + is_uncompressed_locales_provider = false + }, ] } } @@ -1861,7 +1872,6 @@ use_trichrome_library = true if (trichrome_synchronized_proguard) { static_library_provider = ":trichrome_library_apk" - resource_ids_provider_dep = "//android_webview:trichrome_webview_apk" } } @@ -2173,10 +2183,6 @@ !_is_trichrome) { verify_android_configuration = true } - if (trichrome_synchronized_proguard) { - resource_ids_provider_dep = - "//android_webview:trichrome_webview_for_bundle_apk" - } } if (enable_arcore || enable_vr) { @@ -2266,12 +2272,6 @@ }, ] } - extra_modules += [ - { - name = "autofill_assistant" - module_target = ":${target_name}__autofill_assistant_bundle_module" - }, - ] } }
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index d4a8d4e..6917ac9a 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -1112,6 +1112,10 @@ "java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProperties.java", "java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionViewBinder.java", + "java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessor.java", + "java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionView.java", + "java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewBinder.java", + "java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewProperties.java", "java/src/org/chromium/chrome/browser/page_info/CertificateChainHelper.java", "java/src/org/chromium/chrome/browser/page_info/CertificateViewer.java", "java/src/org/chromium/chrome/browser/page_info/ConnectionInfoPopup.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni index d316c192..a20f4d1 100644 --- a/chrome/android/chrome_test_java_sources.gni +++ b/chrome/android/chrome_test_java_sources.gni
@@ -84,7 +84,6 @@ "javatests/src/org/chromium/chrome/browser/contacts_picker/ContactsPickerDialogTest.java", "javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java", "javatests/src/org/chromium/chrome/browser/contextmenu/TabularContextMenuUiTest.java", - "javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java", "javatests/src/org/chromium/chrome/browser/contextual_suggestions/EmptyEnabledStateMonitor.java", "javatests/src/org/chromium/chrome/browser/contextual_suggestions/EnabledStateMonitorTest.java", "javatests/src/org/chromium/chrome/browser/contextual_suggestions/FakeContextualSuggestionsSource.java",
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn index 8e21108..4dea80a 100644 --- a/chrome/android/features/tab_ui/BUILD.gn +++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -20,6 +20,9 @@ "java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinder.java", + "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java", + "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java", + "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetMediator.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetContent.java", @@ -34,6 +37,7 @@ "java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarViewBinder.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerProperties.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java", + "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java", @@ -43,7 +47,6 @@ "java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripToolbarViewProperties.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewBinder.java", "java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripViewHolder.java", - "java/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProvider.java", ] deps = [
diff --git a/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml b/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml index e9314d1..5c4d46a 100644 --- a/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml +++ b/chrome/android/features/tab_ui/java/res/layout/bottom_tab_grid_toolbar.xml
@@ -14,7 +14,8 @@ android:layout_width="match_parent" android:layout_height="@dimen/bottom_sheet_peek_height" android:orientation="horizontal" - android:gravity="center_vertical"> + android:gravity="center_vertical" + android:clickable="true"> <org.chromium.ui.widget.ChromeImageView android:id="@+id/toolbar_left_button" style="@style/BottomToolbarButton"
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml index 4578919..d31dd44 100644 --- a/chrome/android/features/tab_ui/java/res/values/dimens.xml +++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -10,6 +10,8 @@ <dimen name="tab_list_mini_card_radius">4dp</dimen> <dimen name="tab_list_mini_card_frame_size">1dp</dimen> <dimen name="tab_grid_close_button_size">18dp</dimen> + <dimen name="tab_grid_dialog_side_margin">16dp</dimen> + <dimen name="tab_grid_dialog_top_margin">85dp</dimen> <dimen name="tab_grid_thumbnail_card_default_size">152dp</dimen> <dimen name="tab_grid_thumbnail_favicon_frame_padding">16dp</dimen> <dimen name="tab_grid_thumbnail_favicon_padding">24dp</dimen>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java index b7641780..474a57e4 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherCoordinator.java
@@ -14,10 +14,12 @@ import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; import org.chromium.chrome.browser.lifecycle.Destroyable; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tabmodel.TabCreatorManager; import org.chromium.chrome.browser.tabmodel.TabList; import org.chromium.chrome.browser.tabmodel.TabModel; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.toolbar.ToolbarManager; +import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor; @@ -35,15 +37,32 @@ private final TabListCoordinator mTabGridCoordinator; private final GridTabSwitcherMediator mMediator; private final MultiThumbnailCardProvider mMultiThumbnailCardProvider; + private final TabGridDialogCoordinator mTabGridDialogCoordinator; public GridTabSwitcherCoordinator(Context context, ActivityLifecycleDispatcher lifecycleDispatcher, ToolbarManager toolbarManager, TabModelSelector tabModelSelector, TabContentManager tabContentManager, - CompositorViewHolder compositorViewHolder, ChromeFullscreenManager fullscreenManager) { + CompositorViewHolder compositorViewHolder, ChromeFullscreenManager fullscreenManager, + TabCreatorManager tabCreatorManager) { PropertyModel containerViewModel = new PropertyModel(TabListContainerProperties.ALL_KEYS); + TabListMediator.GridCardOnClickListenerProvider gridCardOnClickListenerProvider; + if (FeatureUtilities.isTabGroupsAndroidUiImprovementsEnabled()) { + mTabGridDialogCoordinator = new TabGridDialogCoordinator(context, tabModelSelector, + tabContentManager, tabCreatorManager, new CompositorViewHolder(context), this); - mMediator = new GridTabSwitcherMediator(this, containerViewModel, tabModelSelector, - fullscreenManager, compositorViewHolder); + mMediator = new GridTabSwitcherMediator(this, containerViewModel, tabModelSelector, + fullscreenManager, compositorViewHolder, + mTabGridDialogCoordinator.getResetHandler()); + + gridCardOnClickListenerProvider = mMediator::getGridCardOnClickListener; + } else { + mTabGridDialogCoordinator = null; + + mMediator = new GridTabSwitcherMediator(this, containerViewModel, tabModelSelector, + fullscreenManager, compositorViewHolder, null); + + gridCardOnClickListenerProvider = null; + } mMultiThumbnailCardProvider = new MultiThumbnailCardProvider(context, tabContentManager, tabModelSelector); @@ -60,8 +79,8 @@ mTabGridCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.GRID, context, tabModelSelector, mMultiThumbnailCardProvider, titleProvider, true, - mMediator::getCreateGroupButtonOnClickListener, compositorViewHolder, true, - COMPONENT_NAME); + mMediator::getCreateGroupButtonOnClickListener, gridCardOnClickListenerProvider, + compositorViewHolder, true, COMPONENT_NAME); mContainerViewChangeProcessor = PropertyModelChangeProcessor.create(containerViewModel, mTabGridCoordinator.getContainerView(), TabGridContainerViewBinder::bind);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java index 31c7bfc..1c3d82a9 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediator.java
@@ -38,6 +38,8 @@ import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.ui.modelutil.PropertyModel; +import java.util.List; + /** * The Mediator that is responsible for resetting the tab grid based on visibility and model * changes. @@ -55,6 +57,7 @@ private final TabModelSelectorObserver mTabModelSelectorObserver; private final ObserverList<OverviewModeObserver> mObservers = new ObserverList<>(); private final ChromeFullscreenManager mFullscreenManager; + private final TabGridDialogMediator.ResetHandler mTabGridDialogResetHandler; private final ChromeFullscreenManager.FullscreenListener mFullscreenListener = new ChromeFullscreenManager.FullscreenListener() { @Override @@ -99,7 +102,8 @@ */ GridTabSwitcherMediator(ResetHandler resetHandler, PropertyModel containerViewModel, TabModelSelector tabModelSelector, ChromeFullscreenManager fullscreenManager, - CompositorViewHolder compositorViewHolder) { + CompositorViewHolder compositorViewHolder, + TabGridDialogMediator.ResetHandler tabGridDialogResetHandler) { mResetHandler = resetHandler; mContainerViewModel = containerViewModel; mTabModelSelector = tabModelSelector; @@ -150,6 +154,7 @@ BOTTOM_CONTROLS_HEIGHT, fullscreenManager.getBottomControlsHeight()); mCompositorViewHolder = compositorViewHolder; + mTabGridDialogResetHandler = tabGridDialogResetHandler; } private void setVisibility(boolean isVisible) { @@ -245,6 +250,14 @@ } @Nullable + TabListMediator.TabActionListener getGridCardOnClickListener(Tab tab) { + if (!ableToOpenDialog(tab)) return null; + return tabId -> { + mTabGridDialogResetHandler.resetWithListOfTabs(getRelatedTabs(tabId)); + }; + } + + @Nullable TabListMediator.TabActionListener getCreateGroupButtonOnClickListener(Tab tab) { if (!ableToCreateGroup(tab)) return null; @@ -261,10 +274,18 @@ private boolean ableToCreateGroup(Tab tab) { return FeatureUtilities.isTabGroupsAndroidEnabled() && mTabModelSelector.isIncognitoSelected() == tab.isIncognito() - && mTabModelSelector.getTabModelFilterProvider() - .getCurrentTabModelFilter() - .getRelatedTabList(tab.getId()) - .size() - == 1; + && getRelatedTabs(tab.getId()).size() == 1; + } + + private boolean ableToOpenDialog(Tab tab) { + return FeatureUtilities.isTabGroupsAndroidEnabled() + && mTabModelSelector.isIncognitoSelected() == tab.isIncognito() + && getRelatedTabs(tab.getId()).size() != 1; + } + + private List<Tab> getRelatedTabs(int tabId) { + return mTabModelSelector.getTabModelFilterProvider() + .getCurrentTabModelFilter() + .getRelatedTabList(tabId); } }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java new file mode 100644 index 0000000..5b9ecf11 --- /dev/null +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
@@ -0,0 +1,84 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tasks.tab_management; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.view.ViewGroup; + +import org.chromium.chrome.browser.compositor.CompositorViewHolder; +import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tabmodel.TabCreatorManager; +import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.ui.modelutil.PropertyModel; + +import java.util.List; + +/** + * A coordinator for TabGridDialog component. Manages the communication with + * {@link TabListCoordinator} as well as the life-cycle of shared component + * objects. + */ +public class TabGridDialogCoordinator { + final static String COMPONENT_NAME = "TabGridDialog"; + private final Context mContext; + private final TabListCoordinator mTabListCoordinator; + private final TabGridDialogMediator mMediator; + private final PropertyModel mToolbarPropertyModel; + private TabGridSheetToolbarCoordinator mToolbarCoordinator; + private ViewGroup mParentView; + private TabGridDialogParent mParentLayout; + + TabGridDialogCoordinator(Context context, TabModelSelector tabModelSelector, + TabContentManager tabContentManager, TabCreatorManager tabCreatorManager, + CompositorViewHolder compositorViewHolder, + GridTabSwitcherMediator.ResetHandler resetHandler) { + mContext = context; + + mToolbarPropertyModel = new PropertyModel(TabGridSheetProperties.ALL_KEYS); + + mTabListCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.GRID, context, + tabModelSelector, tabContentManager::getTabThumbnailWithCallback, null, false, null, + null, compositorViewHolder, false, COMPONENT_NAME); + + mMediator = new TabGridDialogMediator(context, this::resetWithListOfTabs, + mToolbarPropertyModel, tabModelSelector, tabCreatorManager, resetHandler); + + mParentView = compositorViewHolder; + + mParentLayout = new TabGridDialogParent(context); + } + + /** + * Destroy any members that needs clean up. + */ + public void destroy() { + mTabListCoordinator.destroy(); + mMediator.destroy(); + } + + private void updateDialogContent(List<Tab> tabs) { + if (tabs != null) { + TabListRecyclerView recyclerView = mTabListCoordinator.getContainerView(); + mToolbarCoordinator = new TabGridSheetToolbarCoordinator( + mContext, recyclerView, mToolbarPropertyModel, mParentView, mParentLayout); + } else { + if (mToolbarCoordinator != null) { + mToolbarCoordinator.destroy(); + } + } + } + + TabGridDialogMediator.ResetHandler getResetHandler() { + return this::resetWithListOfTabs; + } + + public void resetWithListOfTabs(@Nullable List<Tab> tabs) { + mTabListCoordinator.resetWithListOfTabs(tabs); + updateDialogContent(tabs); + mMediator.onReset(tabs == null ? null : tabs.get(0).getId()); + } +}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java new file mode 100644 index 0000000..a777fa501 --- /dev/null +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -0,0 +1,192 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tasks.tab_management; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.view.View; + +import org.chromium.base.metrics.RecordUserAction; +import org.chromium.chrome.browser.UrlConstants; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver; +import org.chromium.chrome.browser.tabmodel.TabCreatorManager; +import org.chromium.chrome.browser.tabmodel.TabLaunchType; +import org.chromium.chrome.browser.tabmodel.TabModelObserver; +import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.chrome.browser.tabmodel.TabSelectionType; +import org.chromium.chrome.browser.widget.ScrimView; +import org.chromium.content_public.browser.LoadUrlParams; +import org.chromium.ui.modelutil.PropertyModel; + +import java.util.List; + +/** + * A mediator for the TabGridDialog component, responsible for communicating + * with the components' coordinator as well as managing the business logic + * for dialog show/hide. + */ +public class TabGridDialogMediator { + /** + * Defines an interface for a {@link TabGridDialogMediator} reset event handler. + */ + interface ResetHandler { + /** + * Handles a reset event originated from {@link TabGridDialogMediator} and {@link + * GridTabSwitcherMediator}. + * + * @param tabs List of Tabs to reset. + */ + void resetWithListOfTabs(@Nullable List<Tab> tabs); + } + + private final Context mContext; + private final PropertyModel mModel; + private final TabModelSelector mTabModelSelector; + private final TabModelObserver mTabModelObserver; + private final TabCreatorManager mTabCreatorManager; + private final TabGridDialogMediator.ResetHandler mDialogResetHandler; + private final GridTabSwitcherMediator.ResetHandler mGridTabSwitcherResetHandler; + private int mCurrentTabId = Tab.INVALID_TAB_ID; + + TabGridDialogMediator(Context context, TabGridDialogMediator.ResetHandler dialogResetHandler, + PropertyModel model, TabModelSelector tabModelSelector, + TabCreatorManager tabCreatorManager, + GridTabSwitcherMediator.ResetHandler gridTabSwitcherResetHandler) { + mContext = context; + mModel = model; + mTabModelSelector = tabModelSelector; + mTabCreatorManager = tabCreatorManager; + mDialogResetHandler = dialogResetHandler; + mGridTabSwitcherResetHandler = gridTabSwitcherResetHandler; + + // Register for tab model. + mTabModelObserver = new EmptyTabModelObserver() { + @Override + public void didAddTab(Tab tab, @TabLaunchType int type) { + updateDialog(); + updateGridTabSwitcher(); + } + + @Override + public void tabClosureUndone(Tab tab) { + updateDialog(); + updateGridTabSwitcher(); + } + + @Override + public void didSelectTab(Tab tab, int type, int lastId) { + if (type == TabSelectionType.FROM_USER) + mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, false); + } + + @Override + public void willCloseTab(Tab tab, boolean animate) { + updateDialog(); + updateGridTabSwitcher(); + List<Tab> relatedTabs = getRelatedTabs(tab.getId()); + // If current tab is closed and tab group is not empty, hand over ID of the next + // tab in the group to mCurrentTabId. + if (relatedTabs.size() == 0) return; + if (tab.getId() == mCurrentTabId) { + mCurrentTabId = relatedTabs.get(0).getId(); + } + } + }; + mTabModelSelector.getTabModelFilterProvider().addTabModelFilterObserver(mTabModelObserver); + + // Setup toolbar property model. + setupToolbarClickHandlers(); + + // Setup ScrimView observer. + setupScrimViewObserver(); + } + + void onReset(Integer tabId) { + if (tabId != null) { + mCurrentTabId = tabId; + updateDialog(); + mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, true); + } else { + mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, false); + } + } + + /** + * Destroy any members that needs clean up. + */ + public void destroy() { + if (mTabModelObserver != null) { + mTabModelSelector.getTabModelFilterProvider().removeTabModelFilterObserver( + mTabModelObserver); + } + } + + private void updateGridTabSwitcher() { + mGridTabSwitcherResetHandler.resetWithTabList( + mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter()); + } + + private void updateDialog() { + if (mCurrentTabId == Tab.INVALID_TAB_ID) return; + List<Tab> relatedTabs = getRelatedTabs(mCurrentTabId); + int tabsCount = relatedTabs.size(); + if (tabsCount == 0) { + mDialogResetHandler.resetWithListOfTabs(null); + return; + } + mModel.set(TabGridSheetProperties.HEADER_TITLE, + mContext.getResources().getQuantityString( + org.chromium.chrome.R.plurals.bottom_tab_grid_title_placeholder, tabsCount, + tabsCount)); + } + + private void setupToolbarClickHandlers() { + mModel.set( + TabGridSheetProperties.COLLAPSE_CLICK_LISTENER, getCollapseButtonClickListener()); + mModel.set(TabGridSheetProperties.ADD_CLICK_LISTENER, getAddButtonClickListener()); + } + + private void setupScrimViewObserver() { + ScrimView.ScrimObserver scrimObserver = new ScrimView.ScrimObserver() { + @Override + public void onScrimClick() { + mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, false); + } + @Override + public void onScrimVisibilityChanged(boolean visible) {} + }; + mModel.set(TabGridSheetProperties.SCRIMVIEW_OBSERVER, scrimObserver); + } + + private View.OnClickListener getCollapseButtonClickListener() { + return view -> { + RecordUserAction.record("TabGroup.DialogMinimizedFromGrid"); + mModel.set(TabGridSheetProperties.IS_DIALOG_VISIBLE, false); + }; + } + + private View.OnClickListener getAddButtonClickListener() { + return view -> { + Tab currentTab = mTabModelSelector.getTabById(mCurrentTabId); + List<Tab> relatedTabs = getRelatedTabs(currentTab.getId()); + + assert relatedTabs.size() > 0; + + Tab parentTabToAttach = relatedTabs.get(relatedTabs.size() - 1); + mTabCreatorManager.getTabCreator(currentTab.isIncognito()) + .createNewTab(new LoadUrlParams(UrlConstants.NTP_URL), + TabLaunchType.FROM_CHROME_UI, parentTabToAttach); + RecordUserAction.record( + "MobileNewTabOpened." + TabGridDialogCoordinator.COMPONENT_NAME); + }; + } + + private List<Tab> getRelatedTabs(int tabId) { + return mTabModelSelector.getTabModelFilterProvider() + .getCurrentTabModelFilter() + .getRelatedTabList(tabId); + } +}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java new file mode 100644 index 0000000..632f0200 --- /dev/null +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogParent.java
@@ -0,0 +1,130 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tasks.tab_management; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Color; +import android.util.DisplayMetrics; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.PopupWindow; + +import org.chromium.chrome.browser.widget.ScrimView; +import org.chromium.chrome.tab_ui.R; +import org.chromium.ui.interpolators.BakedBezierInterpolator; + +/** + * Parent for TabGridDialog component. + * TODO(yuezhanggg): Add animations of card scales up to dialog and dialog scales down to card when + * show/hide dialog. + */ +public class TabGridDialogParent { + private PopupWindow mPopupWindow; + private LinearLayout mDialogContainerView; + private ScrimView mScrimView; + private ScrimView.ScrimParams mScrimParams; + private ValueAnimator mDialogFadeIn; + private ValueAnimator mDialogFadeOut; + private Animator mCurrentAnimator; + + TabGridDialogParent(Context context) { + setUpDialog(context); + } + + private void setUpDialog(Context context) { + FrameLayout backgroundView = new FrameLayout(context); + mDialogContainerView = new LinearLayout(context); + FrameLayout.LayoutParams containerParams = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + int sideMargin = + (int) context.getResources().getDimension(R.dimen.tab_grid_dialog_side_margin); + int topMargin = + (int) context.getResources().getDimension(R.dimen.tab_grid_dialog_top_margin); + containerParams.setMargins(sideMargin, topMargin, sideMargin, topMargin); + mDialogContainerView.setLayoutParams(containerParams); + mDialogContainerView.setBackgroundColor(Color.WHITE); + mDialogContainerView.setOrientation(LinearLayout.VERTICAL); + backgroundView.addView(mDialogContainerView); + + DisplayMetrics displayMetrics = new DisplayMetrics(); + ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay() + .getMetrics(displayMetrics); + mPopupWindow = new PopupWindow( + backgroundView, displayMetrics.widthPixels, displayMetrics.heightPixels); + mScrimView = new ScrimView(context, null, backgroundView); + + mDialogFadeIn = ObjectAnimator.ofFloat(mDialogContainerView, View.ALPHA, 0f, 1f); + mDialogFadeIn.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE); + mDialogFadeIn.setDuration(TabListRecyclerView.BASE_ANIMATION_DURATION_MS); + mDialogFadeIn.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mCurrentAnimator = null; + } + + @Override + public void onAnimationCancel(Animator animation) { + mCurrentAnimator = null; + } + }); + + mDialogFadeOut = ObjectAnimator.ofFloat(mDialogContainerView, View.ALPHA, 1f, 0f); + mDialogFadeOut.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE); + mDialogFadeOut.setDuration(TabListRecyclerView.BASE_ANIMATION_DURATION_MS); + mDialogFadeOut.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mPopupWindow.dismiss(); + mCurrentAnimator = null; + } + + @Override + public void onAnimationCancel(Animator animation) { + mPopupWindow.dismiss(); + mCurrentAnimator = null; + } + }); + } + + void setScrimViewObserver(ScrimView.ScrimObserver scrimViewObserver) { + mScrimParams = + new ScrimView.ScrimParams(mDialogContainerView, false, true, 0, scrimViewObserver); + } + + void updateDialog(View toolbarView, View recyclerView) { + mDialogContainerView.removeAllViews(); + mDialogContainerView.addView(toolbarView); + mDialogContainerView.addView(recyclerView); + recyclerView.setVisibility(View.VISIBLE); + } + + void showDialog(View parent) { + if (mCurrentAnimator != null && mCurrentAnimator != mDialogFadeIn) { + mCurrentAnimator.cancel(); + } + mPopupWindow.showAtLocation(parent, Gravity.CENTER, 0, 0); + mScrimView.showScrim(mScrimParams); + mDialogFadeIn.start(); + mCurrentAnimator = mDialogFadeIn; + } + + void hideDialog() { + if (mCurrentAnimator != null && mCurrentAnimator != mDialogFadeOut) { + mCurrentAnimator.cancel(); + } + mScrimView.hideScrim(true); + mDialogFadeOut.start(); + mCurrentAnimator = mDialogFadeOut; + } +}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java index bf65a036..504d183 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetCoordinator.java
@@ -44,7 +44,7 @@ mTabGridCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.GRID, context, tabModelSelector, tabContentManager::getTabThumbnailWithCallback, null, false, null, - bottomSheetController.getBottomSheet(), false, COMPONENT_NAME); + null, bottomSheetController.getBottomSheet(), false, COMPONENT_NAME); mMediator = new TabGridSheetMediator(mContext, bottomSheetController, this::resetWithListOfTabs, mToolbarPropertyModel, tabModelSelector,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetMediator.java index 36d84f28..a98353a 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetMediator.java
@@ -30,7 +30,7 @@ import java.util.List; /** - * A mediator for the TabGridSheet component, respoonsible for communicating + * A mediator for the TabGridSheet component, responsible for communicating * with the components' coordinator as well as managing the state of the bottom * sheet. */
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetProperties.java index d8897903..6f94201 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetProperties.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetProperties.java
@@ -7,6 +7,7 @@ import android.content.res.ColorStateList; import android.view.View.OnClickListener; +import org.chromium.chrome.browser.widget.ScrimView; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel; @@ -25,7 +26,13 @@ new PropertyModel.WritableIntPropertyKey(); public static final PropertyModel.WritableObjectPropertyKey<ColorStateList> TINT = new PropertyModel.WritableObjectPropertyKey<>(); + public static final PropertyModel.WritableBooleanPropertyKey IS_DIALOG_VISIBLE = + new PropertyModel.WritableBooleanPropertyKey(); + public static final PropertyModel + .WritableObjectPropertyKey<ScrimView.ScrimObserver> SCRIMVIEW_OBSERVER = + new PropertyModel.WritableObjectPropertyKey<>(); - public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {COLLAPSE_CLICK_LISTENER, - ADD_CLICK_LISTENER, HEADER_TITLE, CONTENT_TOP_MARGIN, PRIMARY_COLOR, TINT}; + public static final PropertyKey[] ALL_KEYS = + new PropertyKey[] {COLLAPSE_CLICK_LISTENER, ADD_CLICK_LISTENER, HEADER_TITLE, + CONTENT_TOP_MARGIN, PRIMARY_COLOR, TINT, IS_DIALOG_VISIBLE, SCRIMVIEW_OBSERVER}; }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java index 557c080..3aada02 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetToolbarCoordinator.java
@@ -33,10 +33,16 @@ */ TabGridSheetToolbarCoordinator( Context context, ViewGroup contentView, PropertyModel toolbarPropertyModel) { + this(context, contentView, toolbarPropertyModel, null, null); + } + + TabGridSheetToolbarCoordinator(Context context, ViewGroup contentView, + PropertyModel toolbarPropertyModel, ViewGroup parentView, TabGridDialogParent dialog) { mToolbarView = (TabGroupUiToolbarView) LayoutInflater.from(context).inflate( R.layout.bottom_tab_grid_toolbar, contentView, false); mModelChangeProcessor = PropertyModelChangeProcessor.create(toolbarPropertyModel, - new TabGridSheetViewBinder.ViewHolder(mToolbarView, contentView), + new TabGridSheetViewBinder.ViewHolder( + mToolbarView, contentView, parentView, dialog), TabGridSheetViewBinder::bind); }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetViewBinder.java index 6c39e67d..458f9c8 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetViewBinder.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridSheetViewBinder.java
@@ -8,10 +8,14 @@ import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.COLLAPSE_CLICK_LISTENER; import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.CONTENT_TOP_MARGIN; import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.HEADER_TITLE; +import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.IS_DIALOG_VISIBLE; import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.PRIMARY_COLOR; +import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.SCRIMVIEW_OBSERVER; import static org.chromium.chrome.browser.tasks.tab_management.TabGridSheetProperties.TINT; +import android.support.annotation.Nullable; import android.view.View; +import android.view.ViewGroup; import android.widget.FrameLayout; import org.chromium.ui.modelutil.PropertyKey; @@ -27,10 +31,17 @@ public static class ViewHolder { public final TabGroupUiToolbarView toolbarView; public final View contentView; + @Nullable + public ViewGroup parentView; + @Nullable + public TabGridDialogParent dialogView; - ViewHolder(TabGroupUiToolbarView toolbarView, View contentView) { + ViewHolder(TabGroupUiToolbarView toolbarView, View contentView, + @Nullable ViewGroup parentView, @Nullable TabGridDialogParent dialogView) { this.toolbarView = toolbarView; this.contentView = contentView; + this.parentView = parentView; + this.dialogView = dialogView; } } @@ -56,6 +67,15 @@ viewHolder.contentView.setBackgroundColor(model.get(PRIMARY_COLOR)); } else if (TINT == propertyKey) { viewHolder.toolbarView.setTint(model.get(TINT)); + } else if (SCRIMVIEW_OBSERVER == propertyKey) { + viewHolder.dialogView.setScrimViewObserver(model.get(SCRIMVIEW_OBSERVER)); + } else if (IS_DIALOG_VISIBLE == propertyKey) { + if (model.get(IS_DIALOG_VISIBLE)) { + viewHolder.dialogView.updateDialog(viewHolder.toolbarView, viewHolder.contentView); + viewHolder.dialogView.showDialog(viewHolder.parentView); + } else { + viewHolder.dialogView.hideDialog(); + } } } }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java index 0ddf740..32bf5b1 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
@@ -74,7 +74,7 @@ TabContentManager tabContentManager = activity.getTabContentManager(); mTabStripCoordinator = new TabListCoordinator(TabListCoordinator.TabListMode.STRIP, - mContext, tabModelSelector, null, null, false, null, + mContext, tabModelSelector, null, null, false, null, null, mTabStripToolbarCoordinator.getTabListContainerView(), true, COMPONENT_NAME); mTabGridSheetCoordinator =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java index 87657a0..78f9939 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiToolbarView.java
@@ -50,6 +50,10 @@ mRightButton.setOnClickListener(listener); } + void setTitleOnClickListener(OnClickListener listener) { + mTitleTextView.setOnClickListener(listener); + } + ViewGroup getViewContainer() { return mContainerView; }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java index 12fcb47..5824872 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -71,6 +71,8 @@ TabListMediator.ThumbnailProvider thumbnailProvider, TabListMediator.TitleProvider titleProvider, boolean closeRelatedTabs, @Nullable TabListMediator.CreateGroupButtonProvider createGroupButtonProvider, + @Nullable TabListMediator + .GridCardOnClickListenerProvider gridCardOnClickListenerProvider, @NonNull ViewGroup parentView, boolean attachToParent, String componentName) { TabListModel tabListModel = new TabListModel(); mMode = mode; @@ -122,7 +124,7 @@ mMediator = new TabListMediator(tabListModel, tabModelSelector, thumbnailProvider, titleProvider, tabListFaviconProvider, closeRelatedTabs, createGroupButtonProvider, - componentName); + gridCardOnClickListenerProvider, componentName); if (mMode == TabListMode.GRID) { ItemTouchHelper touchHelper = new ItemTouchHelper(mMediator.getItemTouchHelperCallback(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java index c72995a..92768311c 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -120,6 +120,18 @@ TabActionListener getCreateGroupButtonOnClickListener(Tab tab); } + /** + * An interface to get the onClickListener for opening dialog when click on a grid card. + */ + public interface GridCardOnClickListenerProvider { + /** + * @return {@link TabActionListener} to open tabgrid dialog. If the given {@link Tab} is not + * able to create group, return null; + */ + @Nullable + TabActionListener getGridCardOnClickListener(Tab tab); + } + @IntDef({TabClosedFrom.TAB_STRIP, TabClosedFrom.TAB_GRID_SHEET, TabClosedFrom.GRID_TAB_SWITCHER, TabClosedFrom.GRID_TAB_SWITCHER_GROUP}) @Retention(RetentionPolicy.SOURCE) @@ -141,6 +153,7 @@ private final TabActionListener mTabClosedListener; private final TitleProvider mTitleProvider; private final CreateGroupButtonProvider mCreateGroupButtonProvider; + private final GridCardOnClickListenerProvider mGridCardOnClickListenerProvider; private final String mComponentName; private boolean mCloseAllRelatedTabs; private ComponentCallbacks mComponentCallbacks; @@ -239,7 +252,9 @@ public TabListMediator(TabListModel model, TabModelSelector tabModelSelector, @Nullable ThumbnailProvider thumbnailProvider, @Nullable TitleProvider titleProvider, TabListFaviconProvider tabListFaviconProvider, boolean closeRelatedTabs, - @Nullable CreateGroupButtonProvider createGroupButtonProvider, String componentName) { + @Nullable CreateGroupButtonProvider createGroupButtonProvider, + @Nullable GridCardOnClickListenerProvider gridCardOnClickListenerProvider, + String componentName) { mTabModelSelector = tabModelSelector; mThumbnailProvider = thumbnailProvider; mModel = model; @@ -247,6 +262,7 @@ mComponentName = componentName; mTitleProvider = titleProvider != null ? titleProvider : Tab::getTitle; mCreateGroupButtonProvider = createGroupButtonProvider; + mGridCardOnClickListenerProvider = gridCardOnClickListenerProvider; mCloseAllRelatedTabs = closeRelatedTabs; mTabModelObserver = new EmptyTabModelObserver() { @@ -502,6 +518,13 @@ if (mCloseAllRelatedTabs && !mShownIPH) { showIPH = getRelatedTabsForId(tab.getId()).size() > 1; } + TabActionListener tabSelectedListener; + if (mGridCardOnClickListenerProvider == null + || getRelatedTabsForId(tab.getId()).size() == 1) { + tabSelectedListener = mTabSelectedListener; + } else { + tabSelectedListener = mGridCardOnClickListenerProvider.getGridCardOnClickListener(tab); + } PropertyModel tabInfo = new PropertyModel.Builder(TabProperties.ALL_KEYS_TAB_GRID) @@ -511,7 +534,7 @@ mTabListFaviconProvider.getDefaultFaviconDrawable()) .with(TabProperties.IS_SELECTED, isSelected) .with(TabProperties.IPH_PROVIDER, showIPH ? mIphProvider : null) - .with(TabProperties.TAB_SELECTED_LISTENER, mTabSelectedListener) + .with(TabProperties.TAB_SELECTED_LISTENER, tabSelectedListener) .with(TabProperties.TAB_CLOSED_LISTENER, mTabClosedListener) .with(TabProperties.CREATE_GROUP_LISTENER, createGroupButtonOnClickListener) .with(TabProperties.ALPHA, 1f)
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementModuleImpl.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementModuleImpl.java index 3c94a78..5879a14 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementModuleImpl.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementModuleImpl.java
@@ -29,7 +29,7 @@ return new GridTabSwitcherCoordinator(activity, activity.getLifecycleDispatcher(), activity.getToolbarManager(), activity.getTabModelSelector(), activity.getTabContentManager(), activity.getCompositorViewHolder(), - activity.getFullscreenManager()); + activity.getFullscreenManager(), activity); } @Override
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java index 97fb1ae7..f0d4773f 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/GridTabSwitcherMediatorUnitTest.java
@@ -156,7 +156,7 @@ mModel = new PropertyModel(TabListContainerProperties.ALL_KEYS); mModel.addObserver(mPropertyObserver); mMediator = new GridTabSwitcherMediator(mResetHandler, mModel, mTabModelSelector, - mFullscreenManager, mCompositorViewHolder); + mFullscreenManager, mCompositorViewHolder, null); mMediator.addOverviewModeObserver(mOverviewModeObserver); }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java index 6bdf32a1..ff94d4d 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -131,7 +131,7 @@ mModel = new TabListModel(); mMediator = new TabListMediator(mModel, mTabModelSelector, mTabContentManager::getTabThumbnailWithCallback, null, mTabListFaviconProvider, - false, null, getClass().getSimpleName()); + false, null, null, getClass().getSimpleName()); } @After
diff --git a/chrome/android/java/res/layout/omnibox_answer_suggestion.xml b/chrome/android/java/res/layout/omnibox_answer_suggestion.xml index ee2b403b..72d5bb7 100644 --- a/chrome/android/java/res/layout/omnibox_answer_suggestion.xml +++ b/chrome/android/java/res/layout/omnibox_answer_suggestion.xml
@@ -28,7 +28,7 @@ android:layout_centerVertical="true" android:layout_height="36dp" android:layout_marginEnd="10dp" - android:layout_marginStart="@dimen/omnibox_answer_suggestion_icon_margin_start" + android:layout_marginStart="@dimen/omnibox_suggestion_icon_margin_start" android:layout_width="36dp" android:scaleType="fitCenter" />
diff --git a/chrome/android/java/res/layout/omnibox_entity_suggestion.xml b/chrome/android/java/res/layout/omnibox_entity_suggestion.xml new file mode 100644 index 0000000..2f76cf0 --- /dev/null +++ b/chrome/android/java/res/layout/omnibox_entity_suggestion.xml
@@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> +<org.chromium.chrome.browser.omnibox.suggestions.entity.EntitySuggestionView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_height="@dimen/omnibox_suggestion_height" + android:layout_width="match_parent"> + + <view class="org.chromium.chrome.browser.omnibox.suggestions.entity.EntitySuggestionView$FocusableView" + android:id="@+id/omnibox_entity" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_centerVertical="true" + android:layout_toStartOf="@+id/omnibox_entity_refine_icon" + android:background="?attr/selectableItemBackground" + android:clickable="true" + android:focusable="true" + android:paddingVertical="10dp"> + + <ImageView + android:id="@+id/omnibox_entity_image" + android:layout_width="@dimen/omnibox_suggestion_entity_icon_size" + android:layout_height="@dimen/omnibox_suggestion_entity_icon_size" + android:layout_centerVertical="true" + android:layout_marginEnd="8dp" + android:layout_marginStart="@dimen/omnibox_suggestion_icon_margin_start" + android:contentDescription="@null" /> + + <TextView + android:id="@+id/omnibox_entity_subject_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:layout_toEndOf="@id/omnibox_entity_image" + android:ellipsize="end" + android:textAppearance="@style/TextAppearance.BlackTitle1" + android:maxLines="1" + android:singleLine="true" + android:textAlignment="viewStart" /> + + <TextView + android:id="@+id/omnibox_entity_description_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_alignEnd="@id/omnibox_entity_subject_text" + android:layout_alignStart="@id/omnibox_entity_subject_text" + android:layout_below="@id/omnibox_entity_subject_text" + android:ellipsize="end" + android:textAppearance="@style/TextAppearance.BlackHint2" + android:maxLines="3" + android:singleLine="false" + android:textAlignment="viewStart" /> + + </view> + + <ImageView android:background="?attr/selectableItemBackground" + android:id="@id/omnibox_entity_refine_icon" + android:layout_width="@dimen/omnibox_suggestion_refine_width" + android:layout_height="match_parent" + android:layout_alignBottom="@id/omnibox_entity" + android:layout_alignParentEnd="true" + android:layout_alignTop="@id/omnibox_entity" + android:layout_centerVertical="true" + android:layout_marginEnd="@dimen/omnibox_suggestion_refine_view_modern_end_padding" + android:clickable="true" + android:contentDescription="@string/accessibility_omnibox_btn_refine" + android:focusable="true" + android:scaleType="center" + android:src="@drawable/btn_suggestion_refine" /> + +</org.chromium.chrome.browser.omnibox.suggestions.entity.EntitySuggestionView>
diff --git a/chrome/android/java/res/values-sw600dp/dimens.xml b/chrome/android/java/res/values-sw600dp/dimens.xml index 49c5003..dbdb8a98 100644 --- a/chrome/android/java/res/values-sw600dp/dimens.xml +++ b/chrome/android/java/res/values-sw600dp/dimens.xml
@@ -34,7 +34,7 @@ <dimen name="omnibox_suggestion_start_offset_without_icon">@dimen/location_bar_icon_width</dimen> <dimen name="omnibox_suggestion_start_offset_with_icon">@dimen/omnibox_suggestion_start_offset_without_icon</dimen> - <dimen name="omnibox_answer_suggestion_icon_margin_start">0dp</dimen> + <dimen name="omnibox_suggestion_icon_margin_start">0dp</dimen> <!-- NTP dimensions --> <dimen name="ntp_search_box_transition_length">60dp</dimen>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml index 16544e7..fa90ac3d 100644 --- a/chrome/android/java/res/values/dimens.xml +++ b/chrome/android/java/res/values/dimens.xml
@@ -296,13 +296,14 @@ <dimen name="omnibox_suggestion_start_offset_without_icon">18dp</dimen> <dimen name="omnibox_suggestion_start_offset_with_icon">56dp</dimen> + <dimen name="omnibox_suggestion_icon_margin_start">10dp</dimen> <dimen name="omnibox_suggestion_favicon_size">24dp</dimen> + <dimen name="omnibox_suggestion_entity_icon_size">36dp</dimen> <dimen name="omnibox_suggestion_refine_width">48dp</dimen> <dimen name="omnibox_suggestion_text_vertical_padding">5dp</dimen> <dimen name="omnibox_suggestion_multiline_text_vertical_padding">10dp</dimen> <dimen name="omnibox_suggestion_refine_view_modern_end_padding">4dp</dimen> - <dimen name="omnibox_answer_suggestion_icon_margin_start">10dp</dimen> <!-- NTP dimensions --> <dimen name="tile_grid_layout_max_width">504dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java index eb4d052..c40d8bf 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1470,7 +1470,6 @@ // TODO(yusufo): Unify initialization. initializeBottomSheet( !ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON)); - getComponent().resolveContextualSuggestionsCoordinator(); } AppHooks.get().startMonitoringNetworkQuality(); AppHooks.get().startSystemSettingsObserver();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java index ed32631..2a64da80 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -313,6 +313,8 @@ public static final String QUERY_IN_OMNIBOX = "QueryInOmnibox"; public static final String TAB_ENGAGEMENT_REPORTING_ANDROID = "TabEngagementReportingAndroid"; public static final String TAB_GROUPS_ANDROID = "TabGroupsAndroid"; + public static final String TAB_GROUPS_UI_IMPROVEMENTS_ANDROID = + "TabGroupsUiImprovementsAndroid"; public static final String TAB_GRID_LAYOUT_ANDROID = "TabGridLayoutAndroid"; public static final String TAB_PERSISTENT_STORE_TASK_RUNNER = "TabPersistentStoreTaskRunner"; public static final String TAB_REPARENTING = "TabReparenting";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityComponent.java b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityComponent.java index 256fc347..2f96c8eb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityComponent.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityComponent.java
@@ -4,7 +4,6 @@ package org.chromium.chrome.browser.dependency_injection; -import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsCoordinator; import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModule; import dagger.Subcomponent; @@ -16,7 +15,4 @@ @ActivityScope public interface ChromeActivityComponent { ChromeAppComponent getParent(); - - // Temporary getters for DI migration process. - ContextualSuggestionsCoordinator resolveContextualSuggestionsCoordinator(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBar.java index 3e8a14640..9c565b9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBar.java
@@ -6,9 +6,15 @@ import android.graphics.Bitmap; import android.support.annotation.ColorRes; +import android.text.TextUtils; import org.chromium.base.annotations.CalledByNative; import org.chromium.chrome.browser.ResourceId; +import org.chromium.chrome.browser.touchless.dialog.TouchlessDialogProperties; +import org.chromium.chrome.browser.touchless.dialog.TouchlessDialogProperties.DialogListItemProperties; +import org.chromium.ui.modelutil.PropertyModel; + +import java.util.ArrayList; /** * An infobar that presents the user with several buttons. @@ -59,6 +65,40 @@ onButtonClicked(action); } + @Override + public boolean supportsTouchlessMode() { + // Only allow whitelisted implementations of the confirm infobar. + return getInfoBarIdentifier() == InfoBarIdentifier.POPUP_BLOCKED_INFOBAR_DELEGATE_MOBILE; + } + + @Override + public PropertyModel createModel() { + PropertyModel model = super.createModel(); + + ArrayList<PropertyModel> options = new ArrayList<>(); + if (!TextUtils.isEmpty(mPrimaryButtonText)) { + options.add(new PropertyModel.Builder(DialogListItemProperties.ALL_KEYS) + .with(DialogListItemProperties.TEXT, mPrimaryButtonText) + .with(DialogListItemProperties.CLICK_LISTENER, + (v) -> onButtonClicked(true)) + .build()); + } + + if (!TextUtils.isEmpty(mSecondaryButtonText)) { + options.add(new PropertyModel.Builder(DialogListItemProperties.ALL_KEYS) + .with(DialogListItemProperties.TEXT, mSecondaryButtonText) + .with(DialogListItemProperties.CLICK_LISTENER, + (v) -> onButtonClicked(false)) + .build()); + } + + PropertyModel[] optionModels = new PropertyModel[options.size()]; + options.toArray(optionModels); + model.set(TouchlessDialogProperties.LIST_MODELS, optionModels); + + return model; + } + /** * Creates and begins the process for showing a ConfirmInfoBar. * @param enumeratedIconId ID corresponding to the icon that will be shown for the infobar.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java index 04c05d8..2354fc1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBar.java
@@ -323,7 +323,9 @@ @Override public void onCloseButtonClicked() { - if (mNativeInfoBarPtr != 0) nativeOnCloseButtonClicked(mNativeInfoBarPtr); + if (mNativeInfoBarPtr != 0 && !mIsDismissed) { + nativeOnCloseButtonClicked(mNativeInfoBarPtr); + } } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainerView.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainerView.java index 7221fd8..9953cd2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainerView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainerView.java
@@ -190,8 +190,8 @@ */ void setParentView(ViewGroup parent) { mParentView = parent; - removeFromParentView(); - addToParentView(); + // Don't attach the container to the new parent if it is not previously attached. + if (removeFromParentView()) addToParentView(); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java index a77cf3d..839799a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
@@ -32,6 +32,8 @@ import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewViewBinder; import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionProcessor; import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionViewBinder; +import org.chromium.chrome.browser.omnibox.suggestions.entity.EntitySuggestionView; +import org.chromium.chrome.browser.omnibox.suggestions.entity.EntitySuggestionViewBinder; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.toolbar.ToolbarDataProvider; import org.chromium.chrome.browser.util.KeyNavigationUtil; @@ -210,6 +212,12 @@ () -> (AnswerSuggestionView) LayoutInflater.from(mListView.getContext()) .inflate(R.layout.omnibox_answer_suggestion, null), AnswerSuggestionViewBinder::bind); + + adapter.registerType( + OmniboxSuggestionUiType.ENTITY_SUGGESTION, + () -> (EntitySuggestionView) LayoutInflater.from(mListView.getContext()) + .inflate(R.layout.omnibox_entity_suggestion, null), + EntitySuggestionViewBinder::bind); // clang-format on mHolder = new SuggestionListViewHolder(container, list, adapter);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java index bd42d6ef..f05dbdd6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -37,6 +37,7 @@ import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost; import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate; import org.chromium.chrome.browser.omnibox.suggestions.editurl.EditUrlSuggestionProcessor; +import org.chromium.chrome.browser.omnibox.suggestions.entity.EntitySuggestionProcessor; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.toolbar.ToolbarDataProvider; import org.chromium.components.omnibox.AnswerType; @@ -93,7 +94,8 @@ private final Handler mHandler; private final BasicSuggestionProcessor mBasicSuggestionProcessor; private EditUrlSuggestionProcessor mEditUrlProcessor; - private final AnswerSuggestionProcessor mAnswerSuggestionProcessor; + private AnswerSuggestionProcessor mAnswerSuggestionProcessor; + private final EntitySuggestionProcessor mEntitySuggestionProcessor; private ToolbarDataProvider mDataProvider; private boolean mNativeInitialized; @@ -155,6 +157,7 @@ mAnswerSuggestionProcessor = new AnswerSuggestionProcessor(mContext, this, textProvider); mEditUrlProcessor = new EditUrlSuggestionProcessor( delegate, (suggestion) -> onSelection(suggestion, 0)); + mEntitySuggestionProcessor = new EntitySuggestionProcessor(mContext, this); } @Override @@ -317,6 +320,7 @@ mDeferredNativeRunnables.clear(); mAnswerSuggestionProcessor.onNativeInitialized(); mBasicSuggestionProcessor.onNativeInitialized(); + mEntitySuggestionProcessor.onNativeInitialized(); if (mEditUrlProcessor != null) mEditUrlProcessor.onNativeInitialized(); } @@ -355,6 +359,7 @@ if (mEditUrlProcessor != null) mEditUrlProcessor.onUrlFocusChange(hasFocus); mAnswerSuggestionProcessor.onUrlFocusChange(hasFocus); mBasicSuggestionProcessor.onUrlFocusChange(hasFocus); + mEntitySuggestionProcessor.onUrlFocusChange(hasFocus); } /** @@ -705,6 +710,8 @@ private SuggestionProcessor getProcessorForSuggestion(OmniboxSuggestion suggestion) { if (mAnswerSuggestionProcessor.doesProcessSuggestion(suggestion)) { return mAnswerSuggestionProcessor; + } else if (mEntitySuggestionProcessor.doesProcessSuggestion(suggestion)) { + return mEntitySuggestionProcessor; } else if (mEditUrlProcessor != null && mEditUrlProcessor.doesProcessSuggestion(suggestion)) { return mEditUrlProcessor;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionUiType.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionUiType.java index c13051e..22a2a7a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionUiType.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionUiType.java
@@ -11,10 +11,11 @@ /** The different types of view that a suggestion can be. */ @IntDef({OmniboxSuggestionUiType.DEFAULT, OmniboxSuggestionUiType.EDIT_URL_SUGGESTION, - OmniboxSuggestionUiType.ANSWER_SUGGESTION}) + OmniboxSuggestionUiType.ANSWER_SUGGESTION, OmniboxSuggestionUiType.ENTITY_SUGGESTION}) @Retention(RetentionPolicy.SOURCE) public @interface OmniboxSuggestionUiType { int DEFAULT = 0; int EDIT_URL_SUGGESTION = 1; int ANSWER_SUGGESTION = 2; + int ENTITY_SUGGESTION = 3; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessor.java new file mode 100644 index 0000000..7aedbee9 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionProcessor.java
@@ -0,0 +1,62 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.omnibox.suggestions.entity; + +import android.content.Context; + +import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType; +import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator.SuggestionProcessor; +import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion; +import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType; +import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionHost; +import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate; +import org.chromium.ui.modelutil.PropertyModel; + +/** A class that handles model and view creation for the Entity suggestions. */ +public class EntitySuggestionProcessor implements SuggestionProcessor { + private final Context mContext; + private final SuggestionHost mSuggestionHost; + + /** + * @param context An Android context. + * @param suggestionHost A handle to the object using the suggestions. + */ + public EntitySuggestionProcessor(Context context, SuggestionHost suggestionHost) { + mContext = context; + mSuggestionHost = suggestionHost; + } + + @Override + public boolean doesProcessSuggestion(OmniboxSuggestion suggestion) { + return suggestion.getType() == OmniboxSuggestionType.SEARCH_SUGGEST_ENTITY; + } + + @Override + public int getViewTypeId() { + return OmniboxSuggestionUiType.ENTITY_SUGGESTION; + } + + @Override + public PropertyModel createModelForSuggestion(OmniboxSuggestion suggestion) { + return new PropertyModel(EntitySuggestionViewProperties.ALL_KEYS); + } + + @Override + public void onNativeInitialized() {} + + @Override + public void onUrlFocusChange(boolean hasFocus) {} + + @Override + public void populateModel(OmniboxSuggestion suggestion, PropertyModel model, int position) { + // TODO(ender): Fetch entity icon URL. + SuggestionViewDelegate delegate = + mSuggestionHost.createSuggestionViewDelegate(suggestion, position); + + model.set(EntitySuggestionViewProperties.SUBJECT_TEXT, suggestion.getDisplayText()); + model.set(EntitySuggestionViewProperties.DESCRIPTION_TEXT, suggestion.getDescription()); + model.set(EntitySuggestionViewProperties.DELEGATE, delegate); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionView.java new file mode 100644 index 0000000..0071cdf --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionView.java
@@ -0,0 +1,124 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.omnibox.suggestions.entity; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import org.chromium.chrome.R; + +/** + * Container view for omnibox entity suggestions. + */ +public class EntitySuggestionView extends RelativeLayout { + /** + * EventListener is a class receiving all untranslated input events. + */ + interface EventListener { + /** + * Process gesture event described by supplied MotionEvent. + * @param event Gesture motion event. + */ + void onMotionEvent(MotionEvent event); + + /** Process click event. */ + void onClick(); + + /** Process long click event. */ + void onLongClick(); + + /** Process select/highlight event. */ + void onSelected(); + + /** Process refine event. */ + void onRefine(); + } + + private EventListener mEventListener; + private View mEntityView; + private TextView mSubjectText; + private TextView mDescriptionText; + private ImageView mEntityImageView; + private ImageView mRefineView; + + /** + * Container view for omnibox suggestions allowing soft focus from keyboard. + */ + public static class FocusableView extends RelativeLayout { + /** Creates new instance of FocusableView. */ + public FocusableView(Context context, AttributeSet attributes) { + super(context, attributes); + } + + @Override + public boolean isFocused() { + return super.isFocused() || (isSelected() && !isInTouchMode()); + } + } + + /** Creates new instance of EntitySuggestionView. */ + public EntitySuggestionView(Context context, AttributeSet attributes) { + super(context, attributes); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mSubjectText = findViewById(R.id.omnibox_entity_subject_text); + mDescriptionText = findViewById(R.id.omnibox_entity_description_text); + mEntityImageView = findViewById(R.id.omnibox_entity_image); + mEntityView = findViewById(R.id.omnibox_entity); + mRefineView = findViewById(R.id.omnibox_entity_refine_icon); + } + + @Override + public void setSelected(boolean selected) { + super.setSelected(selected); + mEntityView.setSelected(selected); + if (mEventListener != null && selected && !isInTouchMode()) { + mEventListener.onSelected(); + } + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (mEventListener != null) { + mEventListener.onMotionEvent(ev); + } + return super.dispatchTouchEvent(ev); + } + + /** Specify delegate receiving click/refine events. */ + void setDelegate(EventListener delegate) { + mEventListener = delegate; + mEntityView.setOnClickListener((View v) -> mEventListener.onClick()); + mEntityView.setOnLongClickListener((View v) -> { + mEventListener.onLongClick(); + return true; + }); + mRefineView.setOnClickListener((View v) -> mEventListener.onRefine()); + } + + /** + * Specifies the text to be displayed as subject name. + * @param text Text to be displayed. + */ + void setSubjectText(String text) { + mSubjectText.setText(text); + } + + /** + * Specifies the text to be displayed as description. + * @param text Text to be displayed. + */ + void setDescriptionText(String text) { + mDescriptionText.setText(text); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewBinder.java new file mode 100644 index 0000000..a6130c4a --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewBinder.java
@@ -0,0 +1,80 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.omnibox.suggestions.entity; + +import android.os.Handler; +import android.view.MotionEvent; + +import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate; +import org.chromium.ui.modelutil.PropertyKey; +import org.chromium.ui.modelutil.PropertyModel; + +/** A mechanism binding EntitySuggestion properties to its view. */ +public class EntitySuggestionViewBinder { + private static final class EventListener implements EntitySuggestionView.EventListener { + final Handler mHandler; + final SuggestionViewDelegate mDelegate; + + EventListener(EntitySuggestionView view, SuggestionViewDelegate delegate) { + mHandler = new Handler(); + mDelegate = delegate; + } + + @Override + public void onMotionEvent(MotionEvent ev) { + // Whenever the suggestion dropdown is touched, we dispatch onGestureDown which is + // used to let autocomplete controller know that it should stop updating suggestions. + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mDelegate.onGestureDown(); + break; + case MotionEvent.ACTION_UP: + mDelegate.onGestureUp(ev.getEventTime()); + break; + } + } + + @Override + public void onClick() { + postAction(() -> mDelegate.onSelection()); + } + + @Override + public void onLongClick() { + postAction(() -> mDelegate.onLongPress()); + } + + @Override + public void onSelected() { + postAction(() -> mDelegate.onSetUrlToSuggestion()); + } + + @Override + public void onRefine() { + postAction(() -> mDelegate.onRefineSuggestion()); + } + + /** + * Post delegate action to main thread. + * @param action Delegate action to invoke on the UI thread. + */ + private void postAction(Runnable action) { + if (!mHandler.post(action)) action.run(); + } + } + + /** @see PropertyModelChangeProcessor.ViewBinder#bind(Object, Object, Object) */ + public static void bind( + PropertyModel model, EntitySuggestionView view, PropertyKey propertyKey) { + if (EntitySuggestionViewProperties.DELEGATE.equals(propertyKey)) { + view.setDelegate( + new EventListener(view, model.get(EntitySuggestionViewProperties.DELEGATE))); + } else if (EntitySuggestionViewProperties.SUBJECT_TEXT.equals(propertyKey)) { + view.setSubjectText(model.get(EntitySuggestionViewProperties.SUBJECT_TEXT)); + } else if (EntitySuggestionViewProperties.DESCRIPTION_TEXT.equals(propertyKey)) { + view.setDescriptionText(model.get(EntitySuggestionViewProperties.DESCRIPTION_TEXT)); + } + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewProperties.java new file mode 100644 index 0000000..3735a88 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/entity/EntitySuggestionViewProperties.java
@@ -0,0 +1,33 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.omnibox.suggestions.entity; + +import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties; +import org.chromium.chrome.browser.omnibox.suggestions.basic.SuggestionViewDelegate; +import org.chromium.ui.modelutil.PropertyKey; +import org.chromium.ui.modelutil.PropertyModel; +import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey; + +/** + * The properties associated with rendering the entity suggestion view. + */ +class EntitySuggestionViewProperties { + /** The delegate to handle actions on the suggestion view. */ + public static final WritableObjectPropertyKey<SuggestionViewDelegate> DELEGATE = + new WritableObjectPropertyKey<>(); + + /** Text content for the first line of text (subject). */ + public static final WritableObjectPropertyKey<String> SUBJECT_TEXT = + new WritableObjectPropertyKey<>(); + /** Text content for the second line of text (description). */ + public static final WritableObjectPropertyKey<String> DESCRIPTION_TEXT = + new WritableObjectPropertyKey<>(); + + public static final PropertyKey[] ALL_UNIQUE_KEYS = + new PropertyKey[] {DELEGATE, SUBJECT_TEXT, DESCRIPTION_TEXT}; + + public static final PropertyKey[] ALL_KEYS = + PropertyModel.concatKeys(ALL_UNIQUE_KEYS, SuggestionCommonProperties.ALL_KEYS); +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java index 24f6322..0313ce6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
@@ -316,6 +316,13 @@ public static final String TAB_GROUPS_ANDROID_ENABLED_KEY = "tab_group_android_enabled"; /** + * Whether or not the tab group UI improvement is enabled. + * Default value is false. + */ + public static final String TAB_GROUPS_UI_IMPROVEMENTS_ANDROID_ENABLED_KEY = + "tab_group_ui_improvements_android_enabled"; + + /** * Key for whether PrefetchBackgroundTask should load native in service manager only mode. * Default value is false. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java index f9a1fcf..483799e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
@@ -4,7 +4,6 @@ package org.chromium.chrome.browser.suggestions; -import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; @@ -28,8 +27,6 @@ import org.chromium.base.SysUtils; import org.chromium.chrome.R; import org.chromium.chrome.browser.compositor.animation.CompositorAnimationHandler; -import org.chromium.chrome.browser.download.DownloadUtils; -import org.chromium.chrome.browser.download.ui.DownloadFilter; import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder; import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; import org.chromium.chrome.browser.util.ViewUtils; @@ -220,7 +217,6 @@ mThumbnailView.setImageDrawable(colorDrawable); } - if (!mIsContextual) ApiCompatibilityUtils.setImageTintList(mThumbnailView, null); // Fetch thumbnail for the current article. mImageFetcher.makeArticleThumbnailRequest( @@ -233,21 +229,6 @@ mThumbnailView.setScaleType(ImageView.ScaleType.CENTER_CROP); mThumbnailView.setBackground(null); mThumbnailView.setImageDrawable(thumbnail); - if (!mIsContextual) ApiCompatibilityUtils.setImageTintList(mThumbnailView, null); - } - - private void setThumbnailFromFileType(@DownloadFilter.Type int fileType) { - int iconBackgroundColor = DownloadUtils.getIconBackgroundColor(mThumbnailView.getContext()); - ColorStateList iconForegroundColorList = - DownloadUtils.getIconForegroundColorList(mThumbnailView.getContext()); - - mThumbnailView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - mThumbnailView.setBackgroundColor(iconBackgroundColor); - mThumbnailView.setImageResource( - DownloadUtils.getIconResId(fileType, DownloadUtils.IconSize.DP_36)); - if (!mIsContextual) { - ApiCompatibilityUtils.setImageTintList(mThumbnailView, iconForegroundColorList); - } } private void setDefaultFaviconOnView(int faviconSizePx) { @@ -268,7 +249,6 @@ mThumbnailView.setScaleType(ImageView.ScaleType.CENTER_CROP); mThumbnailView.setBackground(null); - if (!mIsContextual) ApiCompatibilityUtils.setImageTintList(mThumbnailView, null); int duration = FADE_IN_ANIMATION_TIME_MS; if (CompositorAnimationHandler.isInTestingMode()) { mThumbnailView.setImageDrawable(thumbnail);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java index fb785eb..4220046d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
@@ -135,6 +135,7 @@ @Override public boolean canAutoHideBrowserControls() { if (ChromeFeatureList.isEnabled(ChromeFeatureList.DONT_AUTO_HIDE_BROWSER_CONTROLS) + && mTab.getActivity().getToolbarManager() != null && mTab.getActivity().getToolbarManager().getBottomToolbarCoordinator() != null) { return false; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java index 98f78a3..8becdfde 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/SuspendedTab.java
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.usage_stats; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.view.LayoutInflater; @@ -14,6 +15,8 @@ import android.widget.LinearLayout.LayoutParams; import android.widget.TextView; +import org.chromium.base.ContextUtils; +import org.chromium.base.Log; import org.chromium.base.UserData; import org.chromium.base.VisibleForTesting; import org.chromium.chrome.R; @@ -26,8 +29,11 @@ * domain name (FQDN) has been suspended via Digital Wellbeing. */ public class SuspendedTab extends EmptyTabObserver implements UserData { - private static final String DIGITAL_WELLBEING_DASHBOARD_ACTION = - "com.google.android.apps.wellbeing.action.APP_USAGE_DASHBOARD"; + private static final String DIGITAL_WELLBEING_SITE_DETAILS_ACTION = + "org.chromium.chrome.browser.usage_stats.action.SHOW_WEBSITE_DETAILS"; + private static final String EXTRA_FQDN_NAME = + "org.chromium.chrome.browser.usage_stats.extra.FULLY_QUALIFIED_DOMAIN_NAME"; + private static final String TAG = "SuspendedTab"; private static final Class<SuspendedTab> USER_DATA_KEY = SuspendedTab.class; public static SuspendedTab from(Tab tab) { @@ -105,21 +111,6 @@ LayoutInflater inflater = LayoutInflater.from(context); View suspendedTabView = inflater.inflate(R.layout.suspended_tab, null); - TextView explanationText = - (TextView) suspendedTabView.findViewById(R.id.suspended_tab_explanation); - explanationText.setText( - context.getString(R.string.usage_stats_site_paused_explanation, mFqdn)); - - View settingsLink = suspendedTabView.findViewById(R.id.suspended_tab_settings_button); - settingsLink.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(DIGITAL_WELLBEING_DASHBOARD_ACTION); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } - }); - return suspendedTabView; } @@ -134,6 +125,7 @@ parent.addView(mView, new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + updateFqdnText(); } private void updateFqdnText() { @@ -141,6 +133,27 @@ TextView explanationText = (TextView) mView.findViewById(R.id.suspended_tab_explanation); explanationText.setText( context.getString(R.string.usage_stats_site_paused_explanation, mFqdn)); + setSettingsLinkClickListener(); + } + + private void setSettingsLinkClickListener() { + Context context = mTab.getContext(); + View settingsLink = mView.findViewById(R.id.suspended_tab_settings_button); + settingsLink.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(DIGITAL_WELLBEING_SITE_DETAILS_ACTION); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRA_FQDN_NAME, mFqdn); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, + ContextUtils.getApplicationContext().getPackageName()); + try { + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.e(TAG, "No activity found for site details intent", e); + } + } + }); } private void removeViewIfPresent() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java index 477b1887..67e707d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
@@ -83,6 +83,7 @@ private static Boolean sShouldPrioritizeBootstrapTasks; private static Boolean sIsGridTabSwitcherEnabled; private static Boolean sIsTabGroupsAndroidEnabled; + private static Boolean sIsTabGroupUiImprovementsAndroidEnabled; private static Boolean sFeedEnabled; private static Boolean sServiceManagerForBackgroundPrefetch; private static Boolean sIsNetworkServiceWarmUpEnabled; @@ -213,6 +214,7 @@ if (isHighEndPhone()) cacheGridTabSwitcherEnabled(); if (isHighEndPhone()) cacheTabGroupsAndroidEnabled(); + if (isHighEndPhone()) cacheTabGroupsAndroidUiImprovementsEnabled(); // Propagate REACHED_CODE_PROFILER feature value to LibraryLoader. This can't be done in // LibraryLoader itself because it lives in //base and can't depend on ChromeFeatureList. @@ -639,6 +641,34 @@ ContextUtils.getApplicationContext()); } + private static void cacheTabGroupsAndroidUiImprovementsEnabled() { + ChromePreferenceManager.getInstance().writeBoolean( + ChromePreferenceManager.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID_ENABLED_KEY, + !DeviceClassManager.enableAccessibilityLayout() + && (ChromeFeatureList.isEnabled( + ChromeFeatureList.DOWNLOAD_TAB_MANAGEMENT_MODULE) + || ChromeFeatureList.isEnabled( + ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID)) + && TabManagementModuleProvider.getTabManagementModule() != null + && ChromeFeatureList.isEnabled( + ChromeFeatureList.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID)); + } + + /** + * @return Whether the tab group ui improvement feature is enabled and available for use. + */ + public static boolean isTabGroupsAndroidUiImprovementsEnabled() { + if (!isTabGroupsAndroidEnabled()) return false; + + if (sIsTabGroupUiImprovementsAndroidEnabled == null) { + ChromePreferenceManager preferenceManager = ChromePreferenceManager.getInstance(); + + sIsTabGroupUiImprovementsAndroidEnabled = preferenceManager.readBoolean( + ChromePreferenceManager.TAB_GROUPS_UI_IMPROVEMENTS_ANDROID_ENABLED_KEY, false); + } + return sIsTabGroupUiImprovementsAndroidEnabled; + } + /** * @return Whether this device is running Android Go. This is assumed when we're running Android * O or later and we're on a low-end device.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/ArConsentDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/ArConsentDialog.java new file mode 100644 index 0000000..aec9936 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/ArConsentDialog.java
@@ -0,0 +1,139 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.vr; + +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.support.annotation.NonNull; + +import org.chromium.base.Log; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.ui.base.PermissionCallback; +import org.chromium.ui.base.WindowAndroid; +import org.chromium.ui.modaldialog.DialogDismissalCause; +import org.chromium.ui.modaldialog.ModalDialogManager; +import org.chromium.ui.modaldialog.ModalDialogProperties; +import org.chromium.ui.modelutil.PropertyModel; + +/** + * Provides a consent dialog shown before entering an immersive AR session. + * + * <p>For the duration of the session, the site will get ARCore world understanding + * data such as hit tests or plane detection, and also camera movement tracking via + * 6DoF poses. The browser process separately receives the camera image and composites + * that image with the application-drawn AR image.</p> + * + * <p>This is different from typical camera permission usage since the web page + * will NOT get access to camera images. The user consent is only valid for + * the duration of one session and is not persistent.</p> + * + * <p>The browser needs Android-level camera access for using ARCore, this + * is requested if needed after the user has granted consent for the AR session.</p> + */ +public class ArConsentDialog implements ModalDialogProperties.Controller { + private static final String TAG = "ArConsentDialog"; + private static final boolean DEBUG_LOGS = false; + + private ModalDialogManager mModalDialogManager; + private ArCoreJavaUtils mArCoreJavaUtils; + private ChromeActivity mActivity; + + public static void showDialog(ChromeActivity activity, ArCoreJavaUtils caller) { + ArConsentDialog dialog = new ArConsentDialog(); + dialog.show(activity, caller); + } + + public void show(@NonNull ChromeActivity activity, @NonNull ArCoreJavaUtils caller) { + mArCoreJavaUtils = caller; + mActivity = activity; + + Resources resources = activity.getResources(); + PropertyModel model = new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS) + .with(ModalDialogProperties.CONTROLLER, this) + .with(ModalDialogProperties.TITLE, resources, + R.string.ar_immersive_mode_consent_title) + .with(ModalDialogProperties.MESSAGE, resources, + R.string.ar_immersive_mode_consent_message) + .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources, + R.string.ar_immersive_mode_consent_button) + .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources, + R.string.cancel) + .with(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE, true) + .build(); + mModalDialogManager = activity.getModalDialogManager(); + mModalDialogManager.showDialog(model, ModalDialogManager.ModalDialogType.APP); + } + + @Override + public void onClick(PropertyModel model, int buttonType) { + if (buttonType == ModalDialogProperties.ButtonType.NEGATIVE) { + mModalDialogManager.dismissDialog(model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED); + } else { + mModalDialogManager.dismissDialog(model, DialogDismissalCause.POSITIVE_BUTTON_CLICKED); + } + } + + @Override + public void onDismiss(PropertyModel model, int dismissalCause) { + if (dismissalCause == DialogDismissalCause.POSITIVE_BUTTON_CLICKED) { + onConsentGranted(); + } else { + onConsentDenied(); + } + } + + private void onConsentGranted() { + if (DEBUG_LOGS) Log.i(TAG, "onConsentGranted"); + + WindowAndroid window = mActivity.getWindowAndroid(); + if (!window.hasPermission(android.Manifest.permission.CAMERA)) { + // The user has agreed to proceed with the AR session, but the browser + // application doesn't have the prerequisite Android-level camera permission + // needed for using ARCore internally. Show the system permission prompt. + requestCameraPermission(); + return; + } + startSession(); + } + + private void requestCameraPermission() { + PermissionCallback callback = new PermissionCallback() { + @Override + public void onRequestPermissionsResult(String[] permissions, int[] grantResults) { + if (DEBUG_LOGS) Log.i(TAG, "onRequestPermissionsResult"); + // If request is cancelled, the result arrays are empty. + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if (DEBUG_LOGS) Log.i(TAG, "onRequestPermissionsResult=granted"); + startSession(); + } else { + // Didn't get permission :-( + if (DEBUG_LOGS) Log.i(TAG, "onRequestPermissionsResult=denied"); + endSession(); + } + } + }; + + WindowAndroid window = mActivity.getWindowAndroid(); + window.requestPermissions(new String[] {android.Manifest.permission.CAMERA}, callback); + } + + private void onConsentDenied() { + if (DEBUG_LOGS) Log.i(TAG, "onConsentDenied"); + endSession(); + } + + private void startSession() { + if (DEBUG_LOGS) Log.i(TAG, "startSession"); + // We have user consent to start the session. + mArCoreJavaUtils.onStartSession(mActivity); + } + + private void endSession() { + if (DEBUG_LOGS) Log.i(TAG, "endSession"); + mArCoreJavaUtils.onDrawingSurfaceDestroyed(); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/ArCoreJavaUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/ArCoreJavaUtils.java index 16ed2fd..a8f7cef1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr/ArCoreJavaUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/ArCoreJavaUtils.java
@@ -6,6 +6,7 @@ import android.app.Activity; import android.content.Context; +import android.view.Surface; import dalvik.system.BaseDexClassLoader; @@ -17,6 +18,7 @@ import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.infobar.InfoBarIdentifier; import org.chromium.chrome.browser.infobar.SimpleConfirmInfoBarBuilder; import org.chromium.chrome.browser.modules.ModuleInstallUi; @@ -29,11 +31,14 @@ @JNINamespace("vr") public class ArCoreJavaUtils implements ModuleInstallUi.FailureUiListener { private static final String TAG = "ArCoreJavaUtils"; + private static final boolean DEBUG_LOGS = false; private long mNativeArCoreJavaUtils; private boolean mAppInfoInitialized; private Tab mTab; + private ArImmersiveOverlay mArImmersiveOverlay; + // Instance that requested installation of ARCore. // Should be non-null only if there is a pending request to install ARCore. private static ArCoreJavaUtils sRequestInstallInstance; @@ -102,11 +107,53 @@ } private ArCoreJavaUtils(long nativeArCoreJavaUtils) { + if (DEBUG_LOGS) Log.i(TAG, "constructor, nativeArCoreJavaUtils=" + nativeArCoreJavaUtils); mNativeArCoreJavaUtils = nativeArCoreJavaUtils; initializeAppInfo(); } @CalledByNative + private void launchArConsentDialog(final Tab tab) { + if (DEBUG_LOGS) Log.i(TAG, "launchArConsentDialog"); + final ChromeActivity activity = tab.getActivity(); + ArConsentDialog.showDialog(activity, this); + } + + @CalledByNative + private void destroyArImmersiveOverlay() { + if (DEBUG_LOGS) Log.i(TAG, "destroyArImmersiveOverlay"); + if (mArImmersiveOverlay != null) { + mArImmersiveOverlay.destroyDialog(); + mArImmersiveOverlay = null; + } + } + + public void onStartSession(ChromeActivity activity) { + if (DEBUG_LOGS) Log.i(TAG, "onSessionStarted"); + mArImmersiveOverlay = new ArImmersiveOverlay(); + mArImmersiveOverlay.show(activity, this); + } + + public void onDrawingSurfaceReady(Surface surface, int rotation, int width, int height) { + if (DEBUG_LOGS) Log.i(TAG, "onDrawingSurfaceReady"); + if (mNativeArCoreJavaUtils == 0) return; + nativeOnDrawingSurfaceReady(mNativeArCoreJavaUtils, surface, rotation, width, height); + } + + public void onDrawingSurfaceTouch(boolean isTouching, float x, float y) { + if (DEBUG_LOGS) Log.i(TAG, "onDrawingSurfaceTouch"); + if (mNativeArCoreJavaUtils == 0) return; + nativeOnDrawingSurfaceTouch(mNativeArCoreJavaUtils, isTouching, x, y); + } + + public void onDrawingSurfaceDestroyed() { + if (DEBUG_LOGS) Log.i(TAG, "onDrawingSurfaceDestroyed"); + if (mNativeArCoreJavaUtils == 0) return; + mArImmersiveOverlay = null; + nativeOnDrawingSurfaceDestroyed(mNativeArCoreJavaUtils); + } + + @CalledByNative private void onNativeDestroy() { mNativeArCoreJavaUtils = 0; } @@ -298,4 +345,10 @@ long nativeArCoreJavaUtils, boolean success); private native void nativeOnRequestInstallSupportedArCoreResult( long nativeArCoreJavaUtils, boolean success); + + private native void nativeOnDrawingSurfaceReady( + long nativeArCoreJavaUtils, Surface surface, int rotation, int width, int height); + private native void nativeOnDrawingSurfaceTouch( + long nativeArCoreJavaUtils, boolean touching, float x, float y); + private native void nativeOnDrawingSurfaceDestroyed(long nativeArCoreJavaUtils); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr/ArImmersiveOverlay.java b/chrome/android/java/src/org/chromium/chrome/browser/vr/ArImmersiveOverlay.java new file mode 100644 index 0000000..4ee8082 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr/ArImmersiveOverlay.java
@@ -0,0 +1,174 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.vr; + +import android.app.Activity; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.pm.ActivityInfo; +import android.support.annotation.NonNull; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; + +import org.chromium.base.Log; +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.content_public.browser.ScreenOrientationDelegate; +import org.chromium.content_public.browser.ScreenOrientationProvider; + +/** + * Provides a fullscreen overlay for immersive AR mode. + */ +public class ArImmersiveOverlay implements SurfaceHolder.Callback2, + DialogInterface.OnCancelListener, View.OnTouchListener, + ScreenOrientationDelegate { + private static final String TAG = "ArImmersiveOverlay"; + private static final boolean DEBUG_LOGS = false; + + // Android supports multiple variants of fullscreen applications. Currently, we use a fullscreen + // layout with translucent navigation bar, where the content shows behind the navigation bar. + // Alternatively, we could add FLAG_HIDE_NAVIGATION and FLAG_IMMERSIVE_STICKY to hide the + // navigation bar, but then we'd need to show a "pull from top and press back button to exit" + // prompt. + private static final int VISIBILITY_FLAGS_IMMERSIVE = View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + + private ArCoreJavaUtils mArCoreJavaUtils; + private ChromeActivity mActivity; + private boolean mSurfaceReportedReady; + private Dialog mDialog; + private Integer mRestoreOrientation; + private boolean mCleanupInProgress; + + public void show(@NonNull ChromeActivity activity, @NonNull ArCoreJavaUtils caller) { + if (DEBUG_LOGS) Log.i(TAG, "constructor"); + mArCoreJavaUtils = caller; + mActivity = activity; + + // Create a fullscreen dialog and set the system / navigation bars translucent. + mDialog = new Dialog(activity, android.R.style.Theme_NoTitleBar_Fullscreen); + mDialog.getWindow().setBackgroundDrawable(null); + int wmFlags = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS + | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; + mDialog.getWindow().addFlags(wmFlags); + + mDialog.getWindow().takeSurface(this); + + View view = mDialog.getWindow().getDecorView(); + view.setSystemUiVisibility(VISIBILITY_FLAGS_IMMERSIVE); + view.setOnTouchListener(this); + mDialog.setOnCancelListener(this); + + mDialog.getWindow().setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + mDialog.show(); + } + + public void destroyDialog() { + if (DEBUG_LOGS) Log.i(TAG, "destroyDialog"); + cleanupAndExit(); + } + + @Override // View.OnTouchListener + public boolean onTouch(View v, MotionEvent ev) { + // Only forward primary actions, ignore more complex events such as secondary pointer + // touches. Ignore batching since we're only sending one ray pose per frame. + if (ev.getAction() == MotionEvent.ACTION_DOWN || ev.getAction() == MotionEvent.ACTION_MOVE + || ev.getAction() == MotionEvent.ACTION_UP) { + boolean touching = ev.getAction() != MotionEvent.ACTION_UP; + if (DEBUG_LOGS) Log.i(TAG, "onTouch touching=" + touching); + mArCoreJavaUtils.onDrawingSurfaceTouch(touching, ev.getX(0), ev.getY(0)); + } + return true; + } + + @Override // ScreenOrientationDelegate + public boolean canUnlockOrientation(Activity activity, int defaultOrientation) { + if (mActivity == activity && mRestoreOrientation != null) { + mRestoreOrientation = defaultOrientation; + return false; + } + return true; + } + + @Override // ScreenOrientationDelegate + public boolean canLockOrientation() { + return false; + } + + @Override // SurfaceHolder.Callback2 + public void surfaceCreated(SurfaceHolder holder) { + if (DEBUG_LOGS) Log.i(TAG, "surfaceCreated"); + // Do nothing here, we'll handle setup on the following surfaceChanged. + } + + @Override // SurfaceHolder.Callback2 + public void surfaceRedrawNeeded(SurfaceHolder holder) { + if (DEBUG_LOGS) Log.i(TAG, "surfaceRedrawNeeded"); + } + + @Override // SurfaceHolder.Callback2 + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + // WebXR immersive sessions don't support resize, so use the first reported size. + // We shouldn't get resize events since we're using FLAG_LAYOUT_STABLE and are + // locking screen orientation. + if (mSurfaceReportedReady) { + int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); + if (DEBUG_LOGS) + Log.i(TAG, + "surfaceChanged ignoring change to width=" + width + " height=" + height + + " rotation=" + rotation); + return; + } + + // Save current orientation mode, and then lock current orientation. + ScreenOrientationProvider.setOrientationDelegate(this); + if (mRestoreOrientation == null) { + mRestoreOrientation = mActivity.getRequestedOrientation(); + } + mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); + + int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); + if (DEBUG_LOGS) + Log.i(TAG, "surfaceChanged size=" + width + "x" + height + " rotation=" + rotation); + mArCoreJavaUtils.onDrawingSurfaceReady(holder.getSurface(), rotation, width, height); + mSurfaceReportedReady = true; + } + + @Override // SurfaceHolder.Callback2 + public void surfaceDestroyed(SurfaceHolder holder) { + if (DEBUG_LOGS) Log.i(TAG, "surfaceDestroyed"); + cleanupAndExit(); + } + + @Override // DialogInterface.OnCancelListener + public void onCancel(DialogInterface dialog) { + if (DEBUG_LOGS) Log.i(TAG, "onCancel"); + cleanupAndExit(); + } + + public void cleanupAndExit() { + if (DEBUG_LOGS) Log.i(TAG, "cleanupAndExit"); + + // Avoid duplicate cleanup if we're exiting via destroyDialog, that + // triggers cleanupAndExit -> dismiss -> surfaceDestroyed -> cleanupAndExit. + if (mCleanupInProgress) return; + mCleanupInProgress = true; + + // Restore orientation. + ScreenOrientationProvider.setOrientationDelegate(null); + if (mRestoreOrientation != null) mActivity.setRequestedOrientation(mRestoreOrientation); + mRestoreOrientation = null; + + // The surface is destroyed when exiting via "back" button, but also in other lifecycle + // situations such as switching apps or toggling the phone's power button. Treat each of + // these as exiting the immersive session. We need to dismiss the dialog to ensure + // consistent state after non-exiting lifecycle events. + mDialog.dismiss(); + mArCoreJavaUtils.onDrawingSurfaceDestroyed(); + } +}
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd index 36977577..834170c 100644 --- a/chrome/android/java/strings/android_chrome_strings.grd +++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -3880,6 +3880,19 @@ <message name="IDS_AR_MODULE_TITLE" desc="Text shown when the AR module is referenced in install start, success, failure UI (e.g. in IDS_MODULE_INSTALL_START_TEXT, which will expand to 'Installing Augmented Reality for Chrome…')."> Augmented Reality </message> + <message name="IDS_AR_IMMERSIVE_MODE_CONSENT_TITLE" desc="Title for dialog shown when a site requests consent for starting an augmented reality session."> + Start Augmented Reality session? + </message> + <message name="IDS_AR_IMMERSIVE_MODE_CONSENT_MESSAGE" desc="Message for dialog shown when a site requests consent for starting an augmented reality session."> + For the duration of this session, the site will be able to: +• create a 3D map of your environment +• track camera motion + +The site does NOT gain access to the camera. The camera images are only visible to you. + </message> + <message name="IDS_AR_IMMERSIVE_MODE_CONSENT_BUTTON" desc="Confirm button for dialog shown when a site requests consent for starting an augmented reality session."> + Enter AR + </message> </if> <!-- Dynamic feature modules -->
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_AR_IMMERSIVE_MODE_CONSENT_BUTTON.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_AR_IMMERSIVE_MODE_CONSENT_BUTTON.png.sha1 new file mode 100644 index 0000000..40f2a43 --- /dev/null +++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_AR_IMMERSIVE_MODE_CONSENT_BUTTON.png.sha1
@@ -0,0 +1 @@ +e74fa94d9cdb44a1e0fd6775706a1ec6027c7bae \ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_AR_IMMERSIVE_MODE_CONSENT_MESSAGE.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_AR_IMMERSIVE_MODE_CONSENT_MESSAGE.png.sha1 new file mode 100644 index 0000000..40f2a43 --- /dev/null +++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_AR_IMMERSIVE_MODE_CONSENT_MESSAGE.png.sha1
@@ -0,0 +1 @@ +e74fa94d9cdb44a1e0fd6775706a1ec6027c7bae \ No newline at end of file
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_AR_IMMERSIVE_MODE_CONSENT_TITLE.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_AR_IMMERSIVE_MODE_CONSENT_TITLE.png.sha1 new file mode 100644 index 0000000..40f2a43 --- /dev/null +++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_AR_IMMERSIVE_MODE_CONSENT_TITLE.png.sha1
@@ -0,0 +1 @@ +e74fa94d9cdb44a1e0fd6775706a1ec6027c7bae \ No newline at end of file
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index 173a9b3..8aedf50 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -44,9 +44,11 @@ if (enable_arcore) { chrome_java_sources += [ + "java/src/org/chromium/chrome/browser/vr/ArConsentDialog.java", "java/src/org/chromium/chrome/browser/vr/ArCoreJavaUtils.java", "java/src/org/chromium/chrome/browser/vr/ArDelegateImpl.java", "java/src/org/chromium/chrome/browser/vr/ArCoreShim.java", + "java/src/org/chromium/chrome/browser/vr/ArImmersiveOverlay.java", ] }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java deleted file mode 100644 index a6951b8..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsTest.java +++ /dev/null
@@ -1,787 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.contextual_suggestions; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.LargeTest; -import android.support.test.filters.MediumTest; -import android.support.v7.widget.RecyclerView; -import android.view.View; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.RuleChain; -import org.junit.rules.TestRule; -import org.junit.runner.RunWith; - -import org.chromium.base.ThreadUtils; -import org.chromium.base.test.util.CallbackHelper; -import org.chromium.base.test.util.CommandLineFlags; -import org.chromium.base.test.util.DisabledTest; -import org.chromium.base.test.util.Feature; -import org.chromium.base.test.util.Restriction; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.ChromeActivity; -import org.chromium.chrome.browser.ChromeFeatureList; -import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.ChromeTabbedActivity; -import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModel.PropertyKey; -import org.chromium.chrome.browser.dependency_injection.ChromeAppModule; -import org.chromium.chrome.browser.dependency_injection.ModuleOverridesRule; -import org.chromium.chrome.browser.feature_engagement.TrackerFactory; -import org.chromium.chrome.browser.fullscreen.FullscreenManagerTestUtils; -import org.chromium.chrome.browser.multiwindow.MultiWindowTestHelper; -import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; -import org.chromium.chrome.browser.native_page.ContextMenuManager; -import org.chromium.chrome.browser.ntp.cards.NewTabPageViewHolder.PartialBindCallback; -import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; -import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder; -import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.tabmodel.TabModel; -import org.chromium.chrome.browser.tabmodel.TabSelectionType; -import org.chromium.chrome.browser.test.ScreenShooter; -import org.chromium.chrome.browser.toolbar.top.ToolbarPhone; -import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet; -import org.chromium.chrome.test.BottomSheetTestRule; -import org.chromium.chrome.test.ChromeActivityTestRule; -import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.chrome.test.ChromeTabbedActivityTestRule; -import org.chromium.chrome.test.util.ChromeTabUtils; -import org.chromium.chrome.test.util.RenderTestRule; -import org.chromium.chrome.test.util.browser.Features.DisableFeatures; -import org.chromium.chrome.test.util.browser.Features.EnableFeatures; -import org.chromium.chrome.test.util.browser.RecyclerViewTestUtils; -import org.chromium.chrome.test.util.browser.compositor.layouts.DisableChromeAnimations; -import org.chromium.components.feature_engagement.FeatureConstants; -import org.chromium.content_public.browser.LoadUrlParams; -import org.chromium.content_public.browser.test.util.Criteria; -import org.chromium.content_public.browser.test.util.CriteriaHelper; -import org.chromium.content_public.browser.test.util.TestWebContentsObserver; -import org.chromium.net.test.EmbeddedTestServer; -import org.chromium.ui.modelutil.ListObservable; -import org.chromium.ui.modelutil.ListObservable.ListObserver; -import org.chromium.ui.test.util.UiRestriction; - -import java.util.Locale; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -/** - * Tests related to displaying contextual suggestions in a bottom sheet. - */ -@RunWith(ChromeJUnit4ClassRunner.class) -@Restriction(UiRestriction.RESTRICTION_TYPE_PHONE) -@CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE) -public class ContextualSuggestionsTest { - private final TestRule mModuleOverridesRule = new ModuleOverridesRule() - .setOverride(ContextualSuggestionsModuleForTest.Factory.class, - ContextualSuggestionsModuleForTest::new) - .setOverride(ChromeAppModule.Factory.class, ChromeAppModuleForTest::new); - - private final ChromeTabbedActivityTestRule mActivityTestRule = - new ChromeTabbedActivityTestRule(); - - @Rule - public TestRule mOverrideModulesThenLaunchRule = - RuleChain.outerRule(mModuleOverridesRule).around(mActivityTestRule); - @Rule - public ScreenShooter mScreenShooter = new ScreenShooter(); - @Rule - public TestRule mDisableChromeAnimations = new DisableChromeAnimations(); - @Rule - public RenderTestRule mRenderTestRule = new RenderTestRule(); - - private static final String TEST_PAGE = - "/chrome/test/data/android/contextual_suggestions/contextual_suggestions_test.html"; - - private final FakeContextualSuggestionsSource mFakeSource = - new FakeContextualSuggestionsSource(); - - private EmbeddedTestServer mTestServer; - private ContextualSuggestionsCoordinator mCoordinator; - private ContextualSuggestionsMediator mMediator; - private ContextualSuggestionsModel mModel; - private BottomSheet mBottomSheet; - private FakeTracker mFakeTracker; - - // Used in multi-instance test. - private ContextualSuggestionsCoordinator mCoordinator2; - private ContextualSuggestionsMediator mMediator2; - private ContextualSuggestionsModel mModel2; - private BottomSheet mBottomSheet2; - - private int mNumberOfSourcesCreated; - - private class ContextualSuggestionsModuleForTest extends ContextualSuggestionsModule { - @Override - public ContextualSuggestionsSource provideContextualSuggestionsSource(Profile profile) { - mNumberOfSourcesCreated++; - return mFakeSource; - } - } - - private class ChromeAppModuleForTest extends ChromeAppModule { - @Override - public EnabledStateMonitor provideEnabledStateMonitor() { - return new EmptyEnabledStateMonitor() { - @Override - public boolean getSettingsEnabled() { - return true; - } - - @Override - public boolean getEnabledState() { - return true; - } - }; - } - } - - @Before - public void setUp() throws Exception { - FetchHelper.setDisableDelayForTesting(true); - ContextualSuggestionsMediator.setOverrideIPHTimeoutForTesting(true); - - mFakeTracker = new FakeTracker(FeatureConstants.CONTEXTUAL_SUGGESTIONS_FEATURE); - TrackerFactory.setTrackerForTests(mFakeTracker); - - mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext()); - mActivityTestRule.startMainActivityWithURL(mTestServer.getURL(TEST_PAGE)); - final CallbackHelper waitForSuggestionsHelper = new CallbackHelper(); - - ThreadUtils.runOnUiThreadBlocking(() -> { - mCoordinator = mActivityTestRule.getActivity() - .getComponent() - .resolveContextualSuggestionsCoordinator(); - mMediator = mCoordinator.getMediatorForTesting(); - mModel = mCoordinator.getModelForTesting(); - - if (mModel.getTitle() != null) { - waitForSuggestionsHelper.notifyCalled(); - } else { - mModel.addObserver((source, propertyKey) -> { - if (propertyKey == PropertyKey.TITLE && mModel.getTitle() != null) { - waitForSuggestionsHelper.notifyCalled(); - } - }); - } - }); - - waitForSuggestionsHelper.waitForCallback(0); - mBottomSheet = mActivityTestRule.getActivity().getBottomSheet(); - } - - @After - public void tearDown() { - mTestServer.stopAndDestroyServer(); - FetchHelper.setDisableDelayForTesting(false); - ContextualSuggestionsMediator.setOverrideIPHTimeoutForTesting(false); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - public void testRepeatedOpen() throws Exception { - View toolbarButton = getToolbarButton(); - assertEquals( - "Toolbar button should be visible", View.VISIBLE, toolbarButton.getVisibility()); - - clickToolbarButton(); - simulateClickOnCloseButton(); - - assertEquals( - "Toolbar button should be visible", View.VISIBLE, toolbarButton.getVisibility()); - - clickToolbarButton(); - testOpenFirstSuggestion(); - - assertEquals("Toolbar button should be visible", View.GONE, toolbarButton.getVisibility()); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - public void testOpenSuggestion() throws Exception { - clickToolbarButton(); - testOpenFirstSuggestion(); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - public void testOpenArticleInNewTab() throws Exception { - clickToolbarButton(); - - SnippetArticleViewHolder holder = getFirstSuggestionViewHolder(); - String expectedUrl = holder.getUrl(); - - ChromeTabUtils.invokeContextMenuAndOpenInANewTab(mActivityTestRule, holder.itemView, - ContextMenuManager.ContextMenuItemId.OPEN_IN_NEW_TAB, false, expectedUrl); - - assertEquals("Sheet should still be opened.", BottomSheet.SheetState.HALF, - mBottomSheet.getSheetState()); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - public void testOpenSuggestionInNewTabIncognito() throws Exception { - clickToolbarButton(); - - SnippetArticleViewHolder holder = getFirstSuggestionViewHolder(); - String expectedUrl = holder.getUrl(); - - ChromeTabUtils.invokeContextMenuAndOpenInANewTab(mActivityTestRule, holder.itemView, - ContextMenuManager.ContextMenuItemId.OPEN_IN_INCOGNITO_TAB, true, expectedUrl); - - ThreadUtils.runOnUiThreadBlocking(() -> mBottomSheet.endAnimations()); - - assertFalse("Sheet should be closed.", mBottomSheet.isSheetOpen()); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - public void testShadowVisibleOnScroll() throws Exception { - clickToolbarButton(); - - assertFalse("Shadow shouldn't be visible.", mModel.getToolbarShadowVisibility()); - - CallbackHelper shadowVisibilityCallback = new CallbackHelper(); - - mModel.addObserver((source, propertyKey) -> { - if (propertyKey == PropertyKey.TOOLBAR_SHADOW_VISIBILITY - && mModel.getToolbarShadowVisibility()) { - shadowVisibilityCallback.notifyCalled(); - } - }); - - ThreadUtils.runOnUiThreadBlocking(() -> { - RecyclerView view = - (RecyclerView) mBottomSheet.getCurrentSheetContent().getContentView(); - view.smoothScrollToPosition(5); - }); - - shadowVisibilityCallback.waitForCallback("Shadow should be visible"); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - public void testCanShowInProductHelp_DefaultConfidenceThreshold() { - // Check fieldtrial setup. - Assert.assertEquals(0.d, - ChromeFeatureList.getFieldTrialParamByFeatureAsDouble( - ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON, - ContextualSuggestionsMediator.IPH_CONFIDENCE_THRESHOLD_PARAM, 0.d), - 0.d); - assertTrue(mMediator.getCanShowIphForCurrentResults()); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @CommandLineFlags.Add({"enable-features=" + ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON - + "<FakeEoCStudyName", "force-fieldtrials=FakeEoCStudyName/Enabled", - "force-fieldtrial-params=FakeEoCStudyName.Enabled:" - + ContextualSuggestionsMediator.IPH_CONFIDENCE_THRESHOLD_PARAM + "/0.5"}) - public void testCanShowInProductHelp_ResultsConfidenceAboveThreshold() { - // Check fieldtrial setup. - Assert.assertEquals(0.5d, - ChromeFeatureList.getFieldTrialParamByFeatureAsDouble( - ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON, - ContextualSuggestionsMediator.IPH_CONFIDENCE_THRESHOLD_PARAM, 0.d), - 0.d); - assertTrue(mMediator.getCanShowIphForCurrentResults()); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @CommandLineFlags.Add({"enable-features=" + ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON - + "<FakeEoCStudyName", "force-fieldtrials=FakeEoCStudyName/Enabled", - "force-fieldtrial-params=FakeEoCStudyName.Enabled:" - + ContextualSuggestionsMediator.IPH_CONFIDENCE_THRESHOLD_PARAM + "/0.75"}) - public void testCanShowInProductHelp_ResultsConfidenceAtThreshold() { - // Check fieldtrial setup. - Assert.assertEquals(FakeContextualSuggestionsSource.TEST_PEEK_CONFIDENCE, - ChromeFeatureList.getFieldTrialParamByFeatureAsDouble( - ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON, - ContextualSuggestionsMediator.IPH_CONFIDENCE_THRESHOLD_PARAM, 0.d), - 0.d); - assertTrue(mMediator.getCanShowIphForCurrentResults()); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @CommandLineFlags.Add({"enable-features=" + ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON - + "<FakeEoCStudyName", "force-fieldtrials=FakeEoCStudyName/Enabled", - "force-fieldtrial-params=FakeEoCStudyName.Enabled:" - + ContextualSuggestionsMediator.IPH_CONFIDENCE_THRESHOLD_PARAM + "/0.8"}) - public void testCanShowInProductHelp_ResultsConfidenceBelowThreshold() { - // Check fieldtrial setup. - Assert.assertEquals(0.8d, - ChromeFeatureList.getFieldTrialParamByFeatureAsDouble( - ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON, - ContextualSuggestionsMediator.IPH_CONFIDENCE_THRESHOLD_PARAM, 0.d), - 0.d); - assertFalse(mMediator.getCanShowIphForCurrentResults()); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - @DisableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_IPH_REVERSE_SCROLL) - public void testInProductHelp_DontRequireReverseScroll() throws Exception { - // IPH can only be shown after the animation completes. - ThreadUtils.runOnUiThreadBlocking( - () -> getToolbarPhone().endExperimentalButtonAnimationForTesting()); - - CriteriaHelper.pollUiThread(() -> mMediator.getHelpBubbleForTesting() != null && - mMediator.getHelpBubbleForTesting().isShowing(), - "Help bubble never shown."); - - ThreadUtils.runOnUiThreadBlocking(() -> mMediator.getHelpBubbleForTesting().dismiss()); - - Assert.assertEquals("Help bubble should be dimissed.", 1, - mFakeTracker.mDimissedCallbackHelper.getCallCount()); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @EnableFeatures({ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON, - ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_IPH_REVERSE_SCROLL}) - @DisabledTest(message = "https://crbug.com/890947") - public void testInProductHelp_RequireReverseScroll() throws Exception { - // IPH can only be shown after the animation to show the toolbar button completes. - ThreadUtils.runOnUiThreadBlocking( - () -> getToolbarPhone().endExperimentalButtonAnimationForTesting()); - - Assert.assertNull("Help bubble should not be shown yet.", - mMediator.getHelpBubbleForTesting()); - - // Scroll the base page, hiding then reshowing the browser controls. - FullscreenManagerTestUtils.disableBrowserOverrides(); - FullscreenManagerTestUtils.waitForBrowserControlsToBeMoveable( - mActivityTestRule, mActivityTestRule.getActivity().getActivityTab()); - - CriteriaHelper.pollUiThread(() -> mMediator.getHelpBubbleForTesting() != null && - mMediator.getHelpBubbleForTesting().isShowing(), - "Help bubble never shown."); - - ThreadUtils.runOnUiThreadBlocking(() -> mMediator.getHelpBubbleForTesting().dismiss()); - - Assert.assertEquals("Help bubble should be dimissed.", 1, - mFakeTracker.mDimissedCallbackHelper.getCallCount()); - } - - @Test - @LargeTest - @Feature({"ContextualSuggestions"}) - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - public void testMultiInstanceMode() throws Exception { - ChromeTabbedActivity activity1 = mActivityTestRule.getActivity(); - clickToolbarButton(); - - MultiWindowUtils.getInstance().setIsInMultiWindowModeForTesting(true); - ChromeTabbedActivity activity2 = MultiWindowTestHelper.createSecondChromeTabbedActivity( - activity1, new LoadUrlParams(mTestServer.getURL(TEST_PAGE))); - ChromeActivityTestRule.waitForActivityNativeInitializationComplete(activity2); - - CallbackHelper allItemsInsertedCallback = new CallbackHelper(); - ThreadUtils.runOnUiThreadBlocking(() -> { - mCoordinator2 = activity2.getComponent().resolveContextualSuggestionsCoordinator(); - mMediator2 = mCoordinator2.getMediatorForTesting(); - mModel2 = mCoordinator2.getModelForTesting(); - mBottomSheet2 = activity2.getBottomSheet(); - - mModel2.getClusterList().addObserver(new ListObserver<PartialBindCallback>() { - @Override - public void onItemRangeInserted(ListObservable source, int index, int count) { - // There will be two calls to this method, one for each cluster that is added - // to the list. Wait for the expected number of items to ensure the model - // is finished updating. - if (mModel2.getClusterList().getItemCount() - == FakeContextualSuggestionsSource.TOTAL_ITEM_COUNT) { - allItemsInsertedCallback.notifyCalled(); - } - } - }); - }); - - assertNotEquals("There should be two coordinators.", mCoordinator, mCoordinator2); - assertNotEquals("There should be two mediators.", mMediator, mMediator2); - assertNotEquals("There should be two models.", mModel, mModel2); - assertEquals("There should have been two requests to create a ContextualSuggestionsSource", - 2, mNumberOfSourcesCreated); - - allItemsInsertedCallback.waitForCallback(0); - - int itemCount = ThreadUtils.runOnUiThreadBlocking( - () -> { return mModel.getClusterList().getItemCount(); }); - assertEquals("Second model has incorrect number of items.", - (int) FakeContextualSuggestionsSource.TOTAL_ITEM_COUNT, itemCount); - - clickToolbarButton(activity2); - BottomSheetTestRule.waitForWindowUpdates(); - ThreadUtils.runOnUiThreadBlocking(() -> { - ContextualSuggestionsBottomSheetContent content1 = - (ContextualSuggestionsBottomSheetContent) mBottomSheet.getCurrentSheetContent(); - ContextualSuggestionsBottomSheetContent content2 = - (ContextualSuggestionsBottomSheetContent) - mBottomSheet2.getCurrentSheetContent(); - assertNotEquals("There should be two bottom sheet contents", content1, content2); - }); - - assertEquals("Sheet in the second activity should be peeked.", BottomSheet.SheetState.HALF, - mBottomSheet2.getSheetState()); - assertEquals("Sheet in the first activity should be open.", BottomSheet.SheetState.HALF, - mBottomSheet.getSheetState()); - - ThreadUtils.runOnUiThreadBlocking( - () -> mBottomSheet2.setSheetState(BottomSheet.SheetState.FULL, false)); - - SnippetArticleViewHolder holder = getFirstSuggestionViewHolder(mBottomSheet2); - String expectedUrl = holder.getUrl(); - ChromeTabUtils.invokeContextMenuAndOpenInOtherWindow(activity2, activity1, holder.itemView, - ContextMenuManager.ContextMenuItemId.OPEN_IN_NEW_WINDOW, false, expectedUrl); - - ThreadUtils.runOnUiThreadBlocking(() -> { - mBottomSheet.endAnimations(); - mBottomSheet2.endAnimations(); - }); - - assertTrue("Sheet in second activity should be opened.", mBottomSheet2.isSheetOpen()); - assertFalse("Sheet in first activity should be closed.", mBottomSheet.isSheetOpen()); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions", "UiCatalogue"}) - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - public void testCaptureContextualSuggestionsBottomSheet() throws Exception { - dismissHelpBubble(); - - mScreenShooter.shoot("Contextual suggestions: toolbar button"); - - clickToolbarButton(); - BottomSheetTestRule.waitForWindowUpdates(); - - ThreadUtils.runOnUiThreadBlocking( - () -> mBottomSheet.setSheetState(BottomSheet.SheetState.HALF, false)); - BottomSheetTestRule.waitForWindowUpdates(); - mScreenShooter.shoot("Contextual suggestions: half height, images loading"); - - ThreadUtils.runOnUiThreadBlocking(() -> mFakeSource.runImageFetchCallbacks()); - BottomSheetTestRule.waitForWindowUpdates(); - mScreenShooter.shoot("Contextual suggestions: half height, images loaded"); - - ThreadUtils.runOnUiThreadBlocking( - () -> mBottomSheet.setSheetState(BottomSheet.SheetState.FULL, false)); - BottomSheetTestRule.waitForWindowUpdates(); - mScreenShooter.shoot("Contextual suggestions: full height"); - - ThreadUtils.runOnUiThreadBlocking(() -> { - RecyclerView view = - (RecyclerView) mBottomSheet.getCurrentSheetContent().getContentView(); - view.scrollToPosition(5); - }); - BottomSheetTestRule.waitForWindowUpdates(); - mScreenShooter.shoot("Contextual suggestions: scrolled"); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions", "RenderTest"}) - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - public void testRender() throws Exception { - dismissHelpBubble(); - - // Open the sheet to cause the suggestions to be bound in the RecyclerView, then capture - // a suggestion with its thumbnail loading. - clickToolbarButton(); - ThreadUtils.runOnUiThreadBlocking( - () -> mBottomSheet.setSheetState(BottomSheet.SheetState.FULL, false)); - - BottomSheetTestRule.waitForWindowUpdates(); - mRenderTestRule.render(getFirstSuggestionViewHolder().itemView, "suggestion_image_loading"); - - // Run the image fetch callback so images load, then capture a suggestion with its - // thumbnail loaded. - ThreadUtils.runOnUiThreadBlocking(() -> mFakeSource.runImageFetchCallbacks()); - BottomSheetTestRule.waitForWindowUpdates(); - mRenderTestRule.render(getFirstSuggestionViewHolder().itemView, "suggestion_image_loaded"); - - // Render a thumbnail with an offline badge. - ThreadUtils.runOnUiThreadBlocking( - () -> getSuggestionViewHolder(2).setOfflineBadgeVisibilityForTesting(true)); - mRenderTestRule.render(getSuggestionViewHolder(2).itemView, "suggestion_offline"); - - // Render the full suggestions sheet. - mRenderTestRule.render(mBottomSheet, "full_height"); - - // Scroll the suggestions and render the full suggestions sheet. - ThreadUtils.runOnUiThreadBlocking(() -> { - RecyclerView view = - (RecyclerView) mBottomSheet.getCurrentSheetContent().getContentView(); - view.scrollToPosition(5); - }); - BottomSheetTestRule.waitForWindowUpdates(); - mRenderTestRule.render(mBottomSheet, "full_height_scrolled"); - } - - // Re-enable if peek delay condition is hooked up to toolbar button. - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @DisabledTest - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - public void testPeekDelay() throws Exception { - // Close the suggestions from setUp(). - ThreadUtils.runOnUiThreadBlocking(() -> { - mMediator.clearState(); - mBottomSheet.endAnimations(); - }); - - // Request suggestions with fetch time baseline set for testing. - long startTime = SystemClock.uptimeMillis(); - FetchHelper.setFetchTimeBaselineMillisForTesting(startTime); - ThreadUtils.runOnUiThreadBlocking( - () -> mMediator.requestSuggestions("http://www.testurl.com")); - assertEquals("Bottom sheet should be hidden before delay.", BottomSheet.SheetState.HIDDEN, - mBottomSheet.getSheetState()); - - // Simulate user scroll by calling showContentInSheet until the sheet is peeked. - CriteriaHelper.pollUiThread(() -> { - mMediator.showContentInSheetForTesting(true); - mBottomSheet.endAnimations(); - return mBottomSheet.getSheetState() == BottomSheet.SheetState.PEEK; - }); - - // Verify that suggestions is shown after the expected delay. - long duration = SystemClock.uptimeMillis() - startTime; - long expected = FakeContextualSuggestionsSource.TEST_PEEK_DELAY_SECONDS * 1000; - assertTrue(String.format(Locale.US, - "The peek delay should be greater than %d ms, but was %d ms.", - expected, duration), - duration >= expected); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - public void testToolbarButton_SwitchTabs() throws Exception { - View toolbarButton = getToolbarButton(); - - assertEquals( - "Toolbar button should be visible", View.VISIBLE, toolbarButton.getVisibility()); - - final TabModel currentModel = - mActivityTestRule.getActivity().getTabModelSelector().getCurrentModel(); - int currentIndex = currentModel.index(); - ChromeTabUtils.newTabFromMenu( - InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity()); - - assertEquals("Toolbar button should be gone", View.GONE, toolbarButton.getVisibility()); - - ThreadUtils.runOnUiThreadBlocking( - () -> currentModel.setIndex(currentIndex, TabSelectionType.FROM_USER)); - - CriteriaHelper.pollUiThread( - () -> { return toolbarButton.getVisibility() == View.VISIBLE; }); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - public void testToolbarButton_ResponseInTabSwitcher() throws Exception { - View toolbarButton = getToolbarButton(); - - assertEquals("Toolbar button should be visible before resetting suggestions", View.VISIBLE, - toolbarButton.getVisibility()); - - // Simulate suggestions being cleared. - ThreadUtils.runOnUiThreadBlocking(() -> { - mMediator.clearState(); - getToolbarPhone().endExperimentalButtonAnimationForTesting(); - }); - assertEquals("Toolbar button should be gone", View.GONE, toolbarButton.getVisibility()); - assertEquals("Suggestions should be cleared", 0, mModel.getClusterList().getItemCount()); - - // Enter tab switcher. - ThreadUtils.runOnUiThreadBlocking( - () -> { mActivityTestRule.getActivity().getLayoutManager().showOverview(false); }); - - // Simulate a new suggestions request. - ThreadUtils.runOnUiThreadBlocking( - () -> mMediator.requestSuggestions("https://www.google.com")); - CriteriaHelper.pollUiThread(new Criteria() { - @Override - public boolean isSatisfied() { - return mModel.getClusterList().getItemCount() - == FakeContextualSuggestionsSource.TOTAL_ITEM_COUNT; - } - }); - - assertEquals("Toolbar button should be visible after response received", View.VISIBLE, - toolbarButton.getVisibility()); - - ThreadUtils.runOnUiThreadBlocking( - () -> { mActivityTestRule.getActivity().getLayoutManager().hideOverview(false); }); - - assertEquals("Toolbar button should still be visible after exiting tab switcher", - View.VISIBLE, toolbarButton.getVisibility()); - } - - @Test - @MediumTest - @Feature({"ContextualSuggestions"}) - @EnableFeatures(ChromeFeatureList.CONTEXTUAL_SUGGESTIONS_BUTTON) - public void testSuggestionRanking() throws Exception { - ClusterList clusters = mModel.getClusterList(); - - ContextualSuggestionsCluster cluster1 = clusters.getClusterForTesting(0); - for (int i = 0; i < cluster1.getSuggestions().size(); i++) { - SnippetArticle article = cluster1.getSuggestions().get(i); - assertEquals("Cluster rank incorrect for item " + i + " in cluster1", i, - article.getPerSectionRank()); - assertEquals("Global rank incorrect for item " + i + " in cluster1", i, - article.getGlobalRank()); - } - - ContextualSuggestionsCluster cluster2 = clusters.getClusterForTesting(1); - for (int i = 0; i < cluster2.getSuggestions().size(); i++) { - SnippetArticle article = cluster2.getSuggestions().get(i); - assertEquals("Cluster rank incorrect for item " + i + " in cluster2", i, - article.getPerSectionRank()); - assertEquals("Global rank incorrect for item " + i + " in cluster2", - i + cluster1.getSuggestions().size(), article.getGlobalRank()); - } - } - - private void simulateClickOnCloseButton() { - ThreadUtils.runOnUiThreadBlocking(() -> { - mBottomSheet.getCurrentSheetContent() - .getToolbarView() - .findViewById(R.id.close_button) - .performClick(); - mBottomSheet.endAnimations(); - }); - - assertEquals("Sheet should be hidden.", BottomSheet.SheetState.HIDDEN, - mBottomSheet.getSheetState()); - assertNull("Bottom sheet contents should be null.", mBottomSheet.getCurrentSheetContent()); - } - - private SnippetArticleViewHolder getFirstSuggestionViewHolder() { - return getFirstSuggestionViewHolder(mBottomSheet); - } - - private SnippetArticleViewHolder getFirstSuggestionViewHolder(BottomSheet bottomSheet) { - return getSuggestionViewHolder(bottomSheet, 0); - } - - private SnippetArticleViewHolder getSuggestionViewHolder(int index) { - return getSuggestionViewHolder(mBottomSheet, index); - } - - private SnippetArticleViewHolder getSuggestionViewHolder(BottomSheet bottomSheet, int index) { - ContextualSuggestionsBottomSheetContent content = - (ContextualSuggestionsBottomSheetContent) bottomSheet.getCurrentSheetContent(); - RecyclerView recyclerView = (RecyclerView) content.getContentView(); - - RecyclerViewTestUtils.waitForStableRecyclerView(recyclerView); - - return (SnippetArticleViewHolder) recyclerView.findViewHolderForAdapterPosition(index); - } - - private View getToolbarButton() throws ExecutionException { - return getToolbarButton(mActivityTestRule.getActivity()); - } - - private View getToolbarButton(ChromeActivity activity) throws ExecutionException { - return ThreadUtils.runOnUiThreadBlocking( - () -> { return getToolbarPhone(activity).getExperimentalButtonForTesting(); }); - } - - private void clickToolbarButton() throws ExecutionException { - clickToolbarButton(mActivityTestRule.getActivity()); - } - - private void clickToolbarButton(ChromeActivity activity) throws ExecutionException { - View toolbarButton = getToolbarButton(activity); - assertEquals( - "Toolbar button should be visible", View.VISIBLE, toolbarButton.getVisibility()); - - ThreadUtils.runOnUiThreadBlocking(() -> { - toolbarButton.performClick(); - mBottomSheet.endAnimations(); - }); - assertTrue("Sheet should be open.", mBottomSheet.isSheetOpen()); - } - - private void testOpenFirstSuggestion() throws InterruptedException, TimeoutException { - SnippetArticleViewHolder holder = getFirstSuggestionViewHolder(); - String expectedUrl = holder.getUrl(); - - TestWebContentsObserver webContentsObserver = new TestWebContentsObserver( - mActivityTestRule.getActivity().getActivityTab().getWebContents()); - - int callCount = webContentsObserver.getOnPageStartedHelper().getCallCount(); - - ThreadUtils.runOnUiThreadBlocking(() -> { holder.itemView.performClick(); }); - - webContentsObserver.getOnPageStartedHelper().waitForCallback(callCount); - - ThreadUtils.runOnUiThreadBlocking(() -> mBottomSheet.endAnimations()); - - assertFalse("Sheet should be closed.", mBottomSheet.isSheetOpen()); - - // URL may not have been updated yet when WebContentsObserver#didStartLoading is called. - CriteriaHelper.pollUiThread(() -> { - return mActivityTestRule.getActivity().getActivityTab().getUrl().equals(expectedUrl); - }); - - ThreadUtils.runOnUiThreadBlocking( - () -> getToolbarPhone().endExperimentalButtonAnimationForTesting()); - } - - private void dismissHelpBubble() { - ThreadUtils.runOnUiThreadBlocking(() -> { - if (mMediator.getHelpBubbleForTesting() != null) { - mMediator.getHelpBubbleForTesting().dismiss(); - } - }); - } - - private ToolbarPhone getToolbarPhone() { - return getToolbarPhone(mActivityTestRule.getActivity()); - } - - private ToolbarPhone getToolbarPhone(ChromeActivity activity) { - return (ToolbarPhone) activity.getToolbarManager().getToolbarLayoutForTesting(); - } -}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java index 017d51f..1c3577e 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArSessionTest.java
@@ -5,12 +5,10 @@ package org.chromium.chrome.browser.vr; import static org.chromium.chrome.browser.vr.WebXrArTestFramework.PAGE_LOAD_TIMEOUT_S; -import static org.chromium.chrome.browser.vr.WebXrArTestFramework.POLL_TIMEOUT_LONG_MS; import android.os.Build; import android.support.test.filters.MediumTest; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -28,6 +26,7 @@ import org.chromium.chrome.browser.vr.util.XrTestRuleUtils; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; +import org.chromium.content_public.browser.WebContents; import java.util.List; import java.util.concurrent.Callable; @@ -76,6 +75,33 @@ } /** + * Tests that AR session consent can be declined or granted per session. + */ + @Test + @MediumTest + @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) + public void testArVaryingPerSessionConsent() throws InterruptedException { + mWebXrArTestFramework.loadUrlAndAwaitInitialization( + mWebXrArTestFramework.getEmbeddedServerUrlForHtmlTestFile( + "test_ar_request_session_succeeds"), + PAGE_LOAD_TIMEOUT_S); + WebContents contents = mWebXrArTestFramework.getCurrentWebContents(); + + // Start session, decline consent prompt. + mWebXrArTestFramework.enterSessionWithUserGestureAndDeclineConsentOrFail(contents); + mWebXrArTestFramework.assertNoJavaScriptErrors(); + + // Start new session, accept consent prompt this time. + mWebXrArTestFramework.enterSessionWithUserGestureOrFail(contents); + mWebXrArTestFramework.endSession(); + mWebXrArTestFramework.assertNoJavaScriptErrors(); + + // Start yet another session, decline consent prompt again. + mWebXrArTestFramework.enterSessionWithUserGestureAndDeclineConsentOrFail(contents); + mWebXrArTestFramework.assertNoJavaScriptErrors(); + } + + /** * Tests that repeatedly starting and stopping AR sessions does not cause any unexpected * behavior. Regression test for https://crbug.com/837894. */ @@ -93,30 +119,4 @@ } mWebXrArTestFramework.assertNoJavaScriptErrors(); } - - /** - * Tests that repeated calls to requestSession on the same page only prompts the user for - * camera permissions once. - */ - @Test - @MediumTest - @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) - public void testRepeatedArSessionsOnlyPromptPermissionsOnce() throws InterruptedException { - mWebXrArTestFramework.loadUrlAndAwaitInitialization( - mWebXrArTestFramework.getEmbeddedServerUrlForHtmlTestFile( - "test_ar_request_session_succeeds"), - PAGE_LOAD_TIMEOUT_S); - Assert.assertTrue("First AR session request did not trigger permission prompt", - mWebXrArTestFramework.permissionRequestWouldTriggerPrompt("camera")); - mWebXrArTestFramework.enterSessionWithUserGestureOrFail(); - mWebXrArTestFramework.endSession(); - // Manually run through the same steps as enterArSessionOrFail so that we don't trigger - // its automatic permission acceptance. - Assert.assertFalse("Second AR session request triggered permission prompt", - mWebXrArTestFramework.permissionRequestWouldTriggerPrompt("camera")); - mWebXrArTestFramework.enterSessionWithUserGesture(); - mWebXrArTestFramework.pollJavaScriptBooleanOrFail( - "sessionInfos[sessionTypes.AR].currentSession != null", POLL_TIMEOUT_LONG_MS); - mWebXrArTestFramework.assertNoJavaScriptErrors(); - } -} \ No newline at end of file +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java index c790d59..cb9b20e 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java
@@ -21,8 +21,8 @@ } /** - * Requests an AR session, automatically accepting the Camera permission prompt if necessary. - * Causes a test failure if it is unable to do so. + * Requests an AR session, automatically giving consent when prompted. + * Causes a test failure if it is unable to do so, or if the consent prompt is missing. * * @param webContents The Webcontents to start the AR session in. */ @@ -30,21 +30,42 @@ public void enterSessionWithUserGestureOrFail(WebContents webContents) { runJavaScriptOrFail( "sessionTypeToRequest = sessionTypes.AR", POLL_TIMEOUT_LONG_MS, webContents); - // Requesting an AR session for the first time on a page will always prompt for camera - // permissions, but not on subsequent requests, so check to see if we'll need to accept it - // after requesting the session. - boolean expectPermissionPrompt = permissionRequestWouldTriggerPrompt("camera", webContents); - // TODO(bsheedy): Rename enterPresentation since it's used for both presentation and AR? + enterSessionWithUserGesture(webContents); - if (expectPermissionPrompt) { - PermissionUtils.waitForPermissionPrompt(); - PermissionUtils.acceptPermissionPrompt(); - } + + // We expect the AR-specific AR session consent prompt but should not get + // prompted for page camera permission. + PermissionUtils.waitForArConsentPrompt(getRule().getActivity()); + PermissionUtils.acceptArConsentPrompt(getRule().getActivity()); + pollJavaScriptBooleanOrFail("sessionInfos[sessionTypes.AR].currentSession != null", POLL_TIMEOUT_LONG_MS, webContents); } /** + * Requests an AR session, then declines consent when prompted. + * Causes a test failure if there was no prompt, or if the session started without consent. + * + * @param webContents The Webcontents to start the AR session in. + */ + public void enterSessionWithUserGestureAndDeclineConsentOrFail(WebContents webContents) { + runJavaScriptOrFail( + "sessionTypeToRequest = sessionTypes.AR", POLL_TIMEOUT_LONG_MS, webContents); + + enterSessionWithUserGesture(webContents); + + // We expect the AR-specific AR session consent prompt but should not get + // prompted for page camera permission. + PermissionUtils.waitForArConsentPrompt(getRule().getActivity()); + PermissionUtils.declineArConsentPrompt(getRule().getActivity()); + + pollJavaScriptBooleanOrFail( + "sessionInfos[sessionTypes.AR].error != null", POLL_TIMEOUT_LONG_MS, webContents); + pollJavaScriptBooleanOrFail("sessionInfos[sessionTypes.AR].currentSession == null", + POLL_TIMEOUT_LONG_MS, webContents); + } + + /** * Exits a WebXR AR session. * * @param webcontents The WebContents to exit the AR session in @@ -54,4 +75,4 @@ runJavaScriptOrFail("sessionInfos[sessionTypes.AR].currentSession.end()", POLL_TIMEOUT_SHORT_MS, webContents); } -} \ No newline at end of file +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java index 229132d..a620ef6 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrVrDeviceTest.java
@@ -25,6 +25,7 @@ import org.chromium.base.test.params.ParameterSet; import org.chromium.base.test.params.ParameterizedRunner; import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.MinAndroidSdkLevel; import org.chromium.base.test.util.Restriction; import org.chromium.chrome.browser.ChromeSwitches; @@ -176,6 +177,7 @@ .Remove({"enable-webvr"}) @CommandLineFlags.Add({"enable-features=WebXR"}) @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM) + @DisabledTest(message = "crbug.com/958432") public void testForNullPosesInInlineVrFromNfc() throws InterruptedException { mWebXrVrTestFramework.loadUrlAndAwaitInitialization( WebXrVrTestFramework.getFileUrlForHtmlTestFile("test_inline_vr_poses"),
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/PermissionUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/PermissionUtils.java index 8f08242c..3c1afeb 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/PermissionUtils.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/PermissionUtils.java
@@ -4,10 +4,14 @@ package org.chromium.chrome.browser.vr.util; +import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.permissions.PermissionDialogController; +import org.chromium.chrome.browser.vr.ArConsentDialog; import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.ui.modaldialog.ModalDialogManager; import org.chromium.ui.modaldialog.ModalDialogProperties; +import org.chromium.ui.modelutil.PropertyModel; /** * Utility class for interacting with permission prompts outside of the VR Browser. For interaction @@ -42,4 +46,51 @@ ModalDialogProperties.ButtonType.NEGATIVE); }); } + + /** + * Blocks until the AR session consent prompt appears. + */ + public static void waitForArConsentPrompt(ChromeActivity activity) { + CriteriaHelper.pollUiThread(() -> { + return isArConsentDialogShown(activity); + }, "AR consent prompt did not appear in allotted time"); + } + + /** + * Accepts the currently displayed AR session consent prompt. + */ + public static void acceptArConsentPrompt(ChromeActivity activity) { + TestThreadUtils.runOnUiThreadBlocking(() -> { + clickArConsentDialogButton(activity, ModalDialogProperties.ButtonType.POSITIVE); + }); + } + + /** + * Declines the currently displayed AR session consent prompt. + */ + public static void declineArConsentPrompt(ChromeActivity activity) { + TestThreadUtils.runOnUiThreadBlocking(() -> { + clickArConsentDialogButton(activity, ModalDialogProperties.ButtonType.NEGATIVE); + }); + } + + /** + * Helper function to check if the AR consent dialog is being shown. + */ + public static boolean isArConsentDialogShown(ChromeActivity activity) { + ModalDialogManager manager = activity.getModalDialogManager(); + PropertyModel model = manager.getCurrentDialogForTest(); + if (model == null) return false; + return model.get(ModalDialogProperties.CONTROLLER) instanceof ArConsentDialog; + } + + /** + * Helper function to click a button in the AR consent dialog. + */ + public static void clickArConsentDialogButton(ChromeActivity activity, int buttonType) { + ModalDialogManager manager = activity.getModalDialogManager(); + PropertyModel model = manager.getCurrentDialogForTest(); + ArConsentDialog dialog = (ArConsentDialog) (model.get(ModalDialogProperties.CONTROLLER)); + dialog.onClick(model, buttonType); + } }
diff --git a/chrome/android/touchless/java/res/drawable-hdpi/progress_bar_shadow.9.png b/chrome/android/touchless/java/res/drawable-hdpi/progress_bar_shadow.9.png new file mode 100644 index 0000000..427ff726 --- /dev/null +++ b/chrome/android/touchless/java/res/drawable-hdpi/progress_bar_shadow.9.png Binary files differ
diff --git a/chrome/android/touchless/java/res/drawable-mdpi/progress_bar_shadow.9.png b/chrome/android/touchless/java/res/drawable-mdpi/progress_bar_shadow.9.png new file mode 100644 index 0000000..f6bf182 --- /dev/null +++ b/chrome/android/touchless/java/res/drawable-mdpi/progress_bar_shadow.9.png Binary files differ
diff --git a/chrome/android/touchless/java/res/drawable-xhdpi/progress_bar_shadow.9.png b/chrome/android/touchless/java/res/drawable-xhdpi/progress_bar_shadow.9.png new file mode 100644 index 0000000..4d611a10 --- /dev/null +++ b/chrome/android/touchless/java/res/drawable-xhdpi/progress_bar_shadow.9.png Binary files differ
diff --git a/chrome/android/touchless/java/res/drawable-xxhdpi/progress_bar_shadow.9.png b/chrome/android/touchless/java/res/drawable-xxhdpi/progress_bar_shadow.9.png new file mode 100644 index 0000000..e7e2884 --- /dev/null +++ b/chrome/android/touchless/java/res/drawable-xxhdpi/progress_bar_shadow.9.png Binary files differ
diff --git a/chrome/android/touchless/java/res/drawable-xxxhdpi/progress_bar_shadow.9.png b/chrome/android/touchless/java/res/drawable-xxxhdpi/progress_bar_shadow.9.png new file mode 100644 index 0000000..fcb1c69 --- /dev/null +++ b/chrome/android/touchless/java/res/drawable-xxxhdpi/progress_bar_shadow.9.png Binary files differ
diff --git a/chrome/android/touchless/java/res/drawable/notouch_progress_bar_drawable.xml b/chrome/android/touchless/java/res/drawable/notouch_progress_bar_drawable.xml index 200cd437..66a6797 100644 --- a/chrome/android/touchless/java/res/drawable/notouch_progress_bar_drawable.xml +++ b/chrome/android/touchless/java/res/drawable/notouch_progress_bar_drawable.xml
@@ -1,9 +1,13 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background"> <shape> <corners android:radius="16dp" /> - <solid android:color="@android:color/white" /> + <solid android:color="@android:color/transparent" /> </shape> </item>
diff --git a/chrome/android/touchless/java/res/layout/notouch_progress_bar_view.xml b/chrome/android/touchless/java/res/layout/notouch_progress_bar_view.xml index 38983ef..250a4442 100644 --- a/chrome/android/touchless/java/res/layout/notouch_progress_bar_view.xml +++ b/chrome/android/touchless/java/res/layout/notouch_progress_bar_view.xml
@@ -1,14 +1,20 @@ <?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="180dp" - android:layout_height="32dp" - android:layout_marginTop="11dp"> +<!-- 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. --> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:background="@drawable/progress_bar_shadow"> <ProgressBar android:id="@+id/notouch_progress_bar_view" style="@android:style/Widget.ProgressBar.Horizontal" - android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_width="192dp" + android:layout_height="36dp" android:progressDrawable="@drawable/notouch_progress_bar_drawable" /> <TextView @@ -16,10 +22,9 @@ style="@style/TextAppearance.NoTouchProgressBar" android:layout_width="match_parent" android:layout_height="match_parent" - android:ellipsize="start" + android:layout_gravity="center" android:gravity="center" - android:paddingEnd="16dp" - android:paddingStart="16dp" + android:ellipsize="start" android:singleLine="true" /> -</RelativeLayout> \ No newline at end of file +</FrameLayout> \ No newline at end of file
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java index f303a2d..1bf5522 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/NoTouchActivity.java
@@ -27,10 +27,12 @@ import org.chromium.chrome.browser.compositor.layouts.LayoutManager; import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager; import org.chromium.chrome.browser.preferences.ChromePreferenceManager; +import org.chromium.chrome.browser.snackbar.SnackbarManager; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabRedirectHandler; import org.chromium.chrome.browser.tab.TabState; import org.chromium.chrome.browser.touchless.dialog.TouchlessDialogPresenter; +import org.chromium.chrome.browser.touchless.snackbar.BlackHoleSnackbarManager; import org.chromium.chrome.browser.touchless.ui.iph.KeyFunctionsIPHCoordinator; import org.chromium.chrome.browser.touchless.ui.progressbar.ProgressBarCoordinator; import org.chromium.chrome.browser.touchless.ui.progressbar.ProgressBarView; @@ -71,6 +73,9 @@ /** Tab observer that tracks media state. */ private TouchlessTabObserver mTabObserver; + /** The snackbar manager for this activity that drops all snackbar requests. */ + private BlackHoleSnackbarManager mSnackbarManager; + /** * Internal class which performs the intent handling operations delegated by IntentHandler. */ @@ -156,6 +161,7 @@ if (launchNtpDueToInactivity) resetSavedInstanceState(); super.initializeState(); + mSnackbarManager = new BlackHoleSnackbarManager(this); mKeyFunctionsIPHCoordinator = new KeyFunctionsIPHCoordinator(mTooltipView, getActivityTabProvider()); mProgressBarCoordinator = @@ -315,4 +321,9 @@ public TouchlessUiController getTouchlessUiController() { return mUiController; } + + @Override + public SnackbarManager getSnackbarManager() { + return mSnackbarManager; + } }
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java index 99b35444..c4eada2 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java
@@ -4,7 +4,6 @@ package org.chromium.chrome.browser.touchless.dialog; -import android.app.Activity; import android.app.Dialog; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; @@ -39,7 +38,7 @@ /** A modal dialog presenter that is specific to touchless dialogs. */ public class TouchlessDialogPresenter extends Presenter { /** An activity to attach dialogs to. */ - private final Activity mActivity; + private final ChromeActivity mActivity; /** The dialog this class abstracts. */ private Dialog mDialog; @@ -48,7 +47,7 @@ private PropertyModelChangeProcessor<PropertyModel, Pair<ViewGroup, ModelListAdapter>, PropertyKey> mModelChangeProcessor; - public TouchlessDialogPresenter(Activity activity) { + public TouchlessDialogPresenter(ChromeActivity activity) { mActivity = activity; } @@ -76,17 +75,19 @@ mDialog.setOnCancelListener(dialogInterface -> dismissCurrentDialog(DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE)); mDialog.setOnShowListener(dialog - -> AppHooks.get().getTouchlessUiControllerForActivity((ChromeActivity) mActivity) - .addModelToQueue(model)); + -> AppHooks.get().getTouchlessUiControllerForActivity(mActivity).addModelToQueue( + model)); mDialog.setOnDismissListener(dialog - -> AppHooks.get().getTouchlessUiControllerForActivity((ChromeActivity) mActivity) - .removeModelFromQueue(model)); + -> AppHooks.get() + .getTouchlessUiControllerForActivity(mActivity) + .removeModelFromQueue(model)); // Cancel on touch outside should be disabled by default. The ModelChangeProcessor wouldn't // notify change if the property is not set during initialization. mDialog.setCanceledOnTouchOutside(false); - mDialog.setOnKeyListener((dialog, keyCode, event) -> - AppHooks.get().getTouchlessUiControllerForActivity((ChromeActivity) mActivity) - .onKeyEvent(event)); + mDialog.setOnKeyListener( + (dialog, keyCode, event) + -> AppHooks.get().getTouchlessUiControllerForActivity(mActivity).onKeyEvent( + event)); ViewGroup dialogView = (ViewGroup) LayoutInflater.from(mDialog.getContext()) .inflate(R.layout.touchless_dialog_view, null); ModelListAdapter adapter = new ModelListAdapter(mActivity);
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/snackbar/BlackHoleSnackbarManager.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/snackbar/BlackHoleSnackbarManager.java new file mode 100644 index 0000000..fb4c55d --- /dev/null +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/snackbar/BlackHoleSnackbarManager.java
@@ -0,0 +1,29 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.touchless.snackbar; + +import android.app.Activity; +import android.view.ViewGroup; + +import org.chromium.chrome.browser.snackbar.Snackbar; +import org.chromium.chrome.browser.snackbar.SnackbarManager; + +/** A snackbar manager that consumes all incoming requests. */ +public class BlackHoleSnackbarManager extends SnackbarManager { + /** + * @param activity The embedding activity. + */ + public BlackHoleSnackbarManager(Activity activity) { + super(activity, new ViewGroup(activity) { + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) {} + }); + } + + @Override + public void showSnackbar(Snackbar snackbar) { + // Intentional noop. + } +}
diff --git a/chrome/android/touchless/touchless_java_sources.gni b/chrome/android/touchless/touchless_java_sources.gni index 9074b299..69640629 100644 --- a/chrome/android/touchless/touchless_java_sources.gni +++ b/chrome/android/touchless/touchless_java_sources.gni
@@ -41,6 +41,7 @@ "touchless/java/src/org/chromium/chrome/browser/touchless/TouchlessZoomHelper.java", "touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java", "touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogProperties.java", + "touchless/java/src/org/chromium/chrome/browser/touchless/snackbar/BlackHoleSnackbarManager.java", "touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHCoordinator.java", "touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHMediator.java", "touchless/java/src/org/chromium/chrome/browser/touchless/ui/iph/KeyFunctionsIPHProperties.java",
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp index 7d02b8b..89da853 100644 --- a/chrome/app/settings_strings.grdp +++ b/chrome/app/settings_strings.grdp
@@ -549,7 +549,7 @@ Available USB devices will appear here. </message> <message name="IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_DESCRIPTION" desc="Description for managing shared USB devices."> - Give Linux permission to access USB devices. Linux won't remember a USB device after it's removed. + Give Linux apps permission to access USB devices. Linux won't remember a USB device after it's removed. </message> <message name="IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_EXTRA_DESCRIPTION" desc="Extra description for managing shared USB devices."> Only Android devices are currently supported.
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 9663be8..e727a763 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2603,6 +2603,13 @@ kOmniboxUIVerticalMarginVariations, "OmniboxUIVerticalMarginVariations")}, + {"omnibox-ui-vertical-margin-limit-to-non-touch-only", + flag_descriptions::kOmniboxUIVerticalMarginLimitToNonTouchOnlyName, + flag_descriptions::kOmniboxUIVerticalMarginLimitToNonTouchOnlyDescription, + kOsDesktop, + FEATURE_VALUE_TYPE( + omnibox::kUIExperimentVerticalMarginLimitToNonTouchOnly)}, + {"omnibox-ui-show-suggestion-favicons", flag_descriptions::kOmniboxUIShowSuggestionFaviconsName, flag_descriptions::kOmniboxUIShowSuggestionFaviconsDescription, @@ -3067,6 +3074,11 @@ flag_descriptions::kTabGroupsAndroidDescription, kOsAndroid, FEATURE_VALUE_TYPE(chrome::android::kTabGroupsAndroid)}, + {"enable-tab-groups-ui-improvements", + flag_descriptions::kTabGroupsUiImprovementsAndroidName, + flag_descriptions::kTabGroupsUiImprovementsAndroidDescription, kOsAndroid, + FEATURE_VALUE_TYPE(chrome::android::kTabGroupsUiImprovementsAndroid)}, + {"enable-tab-switcher-on-return", flag_descriptions::kTabSwitcherOnReturnName, flag_descriptions::kTabSwitcherOnReturnDescription, kOsAndroid,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc index c4269ed..5c84557 100644 --- a/chrome/browser/android/chrome_feature_list.cc +++ b/chrome/browser/android/chrome_feature_list.cc
@@ -170,6 +170,7 @@ &kSpecialUserDecision, &kTabEngagementReportingAndroid, &kTabGroupsAndroid, + &kTabGroupsUiImprovementsAndroid, &kTabGridLayoutAndroid, &kTabPersistentStoreTaskRunner, &kTabReparenting, @@ -499,6 +500,9 @@ const base::Feature kTabGroupsAndroid{"TabGroupsAndroid", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kTabGroupsUiImprovementsAndroid{ + "TabGroupsUiImprovementsAndroid", base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kTabGridLayoutAndroid{"TabGridLayoutAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h index 4296287..0d6c4d1 100644 --- a/chrome/browser/android/chrome_feature_list.h +++ b/chrome/browser/android/chrome_feature_list.h
@@ -101,6 +101,7 @@ extern const base::Feature kSpecialUserDecision; extern const base::Feature kTabEngagementReportingAndroid; extern const base::Feature kTabGroupsAndroid; +extern const base::Feature kTabGroupsUiImprovementsAndroid; extern const base::Feature kTabGridLayoutAndroid; extern const base::Feature kTabPersistentStoreTaskRunner; extern const base::Feature kTabReparenting;
diff --git a/chrome/browser/android/service_tab_launcher.cc b/chrome/browser/android/service_tab_launcher.cc index 12b9e5ed..4c39017 100644 --- a/chrome/browser/android/service_tab_launcher.cc +++ b/chrome/browser/android/service_tab_launcher.cc
@@ -40,7 +40,7 @@ void ServiceTabLauncher::LaunchTab(content::BrowserContext* browser_context, const content::OpenURLParams& params, - const TabLaunchedCallback& callback) { + TabLaunchedCallback callback) { WindowOpenDisposition disposition = params.disposition; if (disposition != WindowOpenDisposition::NEW_WINDOW && disposition != WindowOpenDisposition::NEW_POPUP && @@ -61,8 +61,9 @@ ScopedJavaLocalRef<jobject> post_data; + // IDMap requires a pointer, so we move |callback| into a heap pointer. int request_id = tab_launched_callbacks_.Add( - std::make_unique<TabLaunchedCallback>(callback)); + std::make_unique<TabLaunchedCallback>(std::move(callback))); DCHECK_GE(request_id, 1); Java_ServiceTabLauncher_launchTab( @@ -75,9 +76,6 @@ content::WebContents* web_contents) { TabLaunchedCallback* callback = tab_launched_callbacks_.Lookup(request_id); DCHECK(callback); - - if (callback) - callback->Run(web_contents); - + std::move(*callback).Run(web_contents); tab_launched_callbacks_.Remove(request_id); }
diff --git a/chrome/browser/android/service_tab_launcher.h b/chrome/browser/android/service_tab_launcher.h index 888f417..e42cb29 100644 --- a/chrome/browser/android/service_tab_launcher.h +++ b/chrome/browser/android/service_tab_launcher.h
@@ -22,7 +22,7 @@ // tab has been launched, the user of this class will be informed with the // content::WebContents instance associated with the tab. class ServiceTabLauncher { - using TabLaunchedCallback = base::Callback<void(content::WebContents*)>; + using TabLaunchedCallback = base::OnceCallback<void(content::WebContents*)>; public: // Returns the singleton instance of the service tab launcher. @@ -33,7 +33,7 @@ // the tab is avialable. This method must only be called from the UI thread. void LaunchTab(content::BrowserContext* browser_context, const content::OpenURLParams& params, - const TabLaunchedCallback& callback); + TabLaunchedCallback callback); // To be called when the tab for |request_id| has launched, with the // associated |web_contents|. The WebContents must not yet have started
diff --git a/chrome/browser/android/vr/BUILD.gn b/chrome/browser/android/vr/BUILD.gn index dcb6b17..8b87c02 100644 --- a/chrome/browser/android/vr/BUILD.gn +++ b/chrome/browser/android/vr/BUILD.gn
@@ -82,8 +82,6 @@ "arcore_device/arcore_install_utils.h", "arcore_device/arcore_java_utils.cc", "arcore_device/arcore_java_utils.h", - "arcore_device/arcore_permission_helper.cc", - "arcore_device/arcore_permission_helper.h", "arcore_device/arcore_shim.cc", "arcore_device/arcore_shim.h", ]
diff --git a/chrome/browser/android/vr/arcore_device/ar_image_transport.cc b/chrome/browser/android/vr/arcore_device/ar_image_transport.cc index 53f6538f..f7708ca 100644 --- a/chrome/browser/android/vr/arcore_device/ar_image_transport.cc +++ b/chrome/browser/android/vr/arcore_device/ar_image_transport.cc
@@ -9,6 +9,7 @@ #include "base/containers/queue.h" #include "base/trace_event/traced_value.h" #include "chrome/browser/android/vr/mailbox_to_surface_bridge.h" +#include "chrome/browser/android/vr/web_xr_presentation_state.h" #include "gpu/command_buffer/common/shared_image_usage.h" #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h" #include "ui/gfx/gpu_fence.h" @@ -21,69 +22,36 @@ namespace device { -namespace { - -// Number of shared buffers to use in rotation. Two would be sufficient if -// strictly sequenced, but use an extra one since we currently don't know -// exactly when the Renderer is done with it. -constexpr int kSharedBufferSwapChainSize = 3; - -} // namespace - -// TODO(klausw): share this with WebXrPresentationState. -struct SharedFrameBuffer { - SharedFrameBuffer() = default; - ~SharedFrameBuffer() = default; - - gfx::Size size; - - std::unique_ptr<gpu::GpuMemoryBufferImplAndroidHardwareBuffer> - shared_gpu_memory_buffer; - - // Resources in the remote GPU process command buffer context - gpu::MailboxHolder mailbox_holder; - - // Resources in the local GL context - GLuint local_texture_id = 0; - // This refptr keeps the image alive while processing a frame. That's - // required because it owns underlying resources and must still be - // alive when the mailbox texture backed by this image is used. - scoped_refptr<gl::GLImageEGL> local_glimage; -}; - -struct SharedFrameBufferSwapChain { - SharedFrameBufferSwapChain() = default; - ~SharedFrameBufferSwapChain() = default; - - base::queue<std::unique_ptr<SharedFrameBuffer>> buffers; - int next_memory_buffer_id = 0; -}; - ArImageTransport::ArImageTransport( std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge) : gl_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), - mailbox_bridge_(std::move(mailbox_bridge)), - swap_chain_(std::make_unique<SharedFrameBufferSwapChain>()) {} + mailbox_bridge_(std::move(mailbox_bridge)) {} ArImageTransport::~ArImageTransport() { DCHECK(IsOnGlThread()); - while (!swap_chain_->buffers.empty()) { - std::unique_ptr<SharedFrameBuffer> buffer = - std::move(swap_chain_->buffers.front()); - swap_chain_->buffers.pop(); - if (!buffer->mailbox_holder.mailbox.IsZero()) { - DVLOG(2) << ": DestroySharedImage, mailbox=" - << buffer->mailbox_holder.mailbox.ToDebugString(); - // Note: the sync token in mailbox_holder may not be accurate. See comment - // in TransferFrame below. - mailbox_bridge_->DestroySharedImage(buffer->mailbox_holder); + + if (webxr_) { + std::vector<std::unique_ptr<vr::WebXrSharedBuffer>> buffers = + webxr_->TakeSharedBuffers(); + for (auto& buffer : buffers) { + if (!buffer->mailbox_holder.mailbox.IsZero()) { + DCHECK(mailbox_bridge_); + DVLOG(2) << ": DestroySharedImage, mailbox=" + << buffer->mailbox_holder.mailbox.ToDebugString(); + // Note: the sync token in mailbox_holder may not be accurate. See + // comment in TransferFrame below. + mailbox_bridge_->DestroySharedImage(buffer->mailbox_holder); + } } } } -bool ArImageTransport::Initialize() { +bool ArImageTransport::Initialize(vr::WebXrPresentationState* webxr) { + DVLOG(1) << __func__; DCHECK(IsOnGlThread()); + webxr_ = webxr; + mailbox_bridge_->BindContextProviderToCurrentThread(); glDisable(GL_DEPTH_TEST); @@ -91,7 +59,7 @@ ar_renderer_ = std::make_unique<ArRenderer>(); glGenTextures(1, &camera_texture_id_arcore_); - SetupHardwareBuffers(); + glGenFramebuffersEXT(1, &camera_fbo_); return true; } @@ -101,7 +69,7 @@ } void ArImageTransport::ResizeSharedBuffer(const gfx::Size& size, - SharedFrameBuffer* buffer) { + vr::WebXrSharedBuffer* buffer) { DCHECK(IsOnGlThread()); if (buffer->size == size) @@ -125,31 +93,29 @@ static constexpr gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888; static constexpr gfx::BufferUsage usage = gfx::BufferUsage::SCANOUT; - gfx::GpuMemoryBufferId kBufferId(swap_chain_->next_memory_buffer_id++); - buffer->shared_gpu_memory_buffer = - gpu::GpuMemoryBufferImplAndroidHardwareBuffer::Create( - kBufferId, size, format, usage, - gpu::GpuMemoryBufferImpl::DestructionCallback()); + gfx::GpuMemoryBufferId kBufferId(webxr_->next_memory_buffer_id++); + buffer->gmb = gpu::GpuMemoryBufferImplAndroidHardwareBuffer::Create( + kBufferId, size, format, usage, + gpu::GpuMemoryBufferImpl::DestructionCallback()); uint32_t shared_image_usage = gpu::SHARED_IMAGE_USAGE_SCANOUT | gpu::SHARED_IMAGE_USAGE_DISPLAY | gpu::SHARED_IMAGE_USAGE_GLES2; - buffer->mailbox_holder = - mailbox_bridge_->CreateSharedImage(buffer->shared_gpu_memory_buffer.get(), - gfx::ColorSpace(), shared_image_usage); + buffer->mailbox_holder = mailbox_bridge_->CreateSharedImage( + buffer->gmb.get(), gfx::ColorSpace(), shared_image_usage); DVLOG(2) << ": CreateSharedImage, mailbox=" << buffer->mailbox_holder.mailbox.ToDebugString(); auto img = base::MakeRefCounted<gl::GLImageAHardwareBuffer>(size); base::android::ScopedHardwareBufferHandle ahb = - buffer->shared_gpu_memory_buffer->CloneHandle().android_hardware_buffer; + buffer->gmb->CloneHandle().android_hardware_buffer; bool ret = img->Initialize(ahb.get(), false /* preserved */); if (!ret) { DLOG(WARNING) << __FUNCTION__ << ": ERROR: failed to initialize image!"; return; } - glBindTexture(GL_TEXTURE_EXTERNAL_OES, buffer->local_texture_id); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, buffer->local_texture); img->BindTexImage(GL_TEXTURE_EXTERNAL_OES); buffer->local_glimage = std::move(img); @@ -159,87 +125,81 @@ buffer->size = size; } -void ArImageTransport::SetupHardwareBuffers() { - DCHECK(IsOnGlThread()); - - glGenFramebuffersEXT(1, &camera_fbo_); - - for (int i = 0; i < kSharedBufferSwapChainSize; ++i) { - std::unique_ptr<SharedFrameBuffer> buffer = - std::make_unique<SharedFrameBuffer>(); - - // Local resources - glGenTextures(1, &buffer->local_texture_id); - - // Add to swap chain - swap_chain_->buffers.push(std::move(buffer)); - } - - glGenFramebuffersEXT(1, &transfer_fbo_); +std::unique_ptr<vr::WebXrSharedBuffer> ArImageTransport::CreateBuffer() { + std::unique_ptr<vr::WebXrSharedBuffer> buffer = + std::make_unique<vr::WebXrSharedBuffer>(); + // Local resources + glGenTextures(1, &buffer->local_texture); + return buffer; } gpu::MailboxHolder ArImageTransport::TransferFrame( const gfx::Size& frame_size, const gfx::Transform& uv_transform) { DCHECK(IsOnGlThread()); - // TODO(klausw): find out when a buffer is actually done being used - // including by GL so we can know if we are overwriting one. - // A sync token needs to be returned by the client and stashed into - // shared_buffer->mailbox_holder.sync_token, then waited upon before reusing - // the buffer. - DCHECK(swap_chain_->buffers.size() > 0); - glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, transfer_fbo_); - - std::unique_ptr<SharedFrameBuffer> shared_buffer = - std::move(swap_chain_->buffers.front()); - swap_chain_->buffers.pop(); - ResizeSharedBuffer(frame_size, shared_buffer.get()); - - glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_EXTERNAL_OES, - shared_buffer->local_texture_id, 0); - - if (!transfer_fbo_completeness_checked_) { - auto status = glCheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER); - DVLOG(1) << __FUNCTION__ << ": framebuffer status=" << std::hex << status; - DCHECK(status == GL_FRAMEBUFFER_COMPLETE); - transfer_fbo_completeness_checked_ = true; + if (!webxr_->GetAnimatingFrame()->shared_buffer) { + webxr_->GetAnimatingFrame()->shared_buffer = CreateBuffer(); } + vr::WebXrSharedBuffer* shared_buffer = + webxr_->GetAnimatingFrame()->shared_buffer.get(); + ResizeSharedBuffer(frame_size, shared_buffer); + mailbox_bridge_->GenSyncToken(&shared_buffer->mailbox_holder.sync_token); + return shared_buffer->mailbox_holder; +} + +void ArImageTransport::CreateGpuFenceForSyncToken( + const gpu::SyncToken& sync_token, + base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) { + DVLOG(2) << __func__; + mailbox_bridge_->CreateGpuFence(sync_token, std::move(callback)); +} + +void ArImageTransport::WaitSyncToken(const gpu::SyncToken& sync_token) { + mailbox_bridge_->WaitSyncToken(sync_token); +} + +void ArImageTransport::CopyCameraImageToFramebuffer( + const gfx::Size& frame_size, + const gfx::Transform& uv_transform) { + glDisable(GL_BLEND); + CopyTextureToFramebuffer(camera_texture_id_arcore_, frame_size, uv_transform); +} + +void ArImageTransport::CopyDrawnImageToFramebuffer( + const gfx::Size& frame_size, + const gfx::Transform& uv_transform) { + DVLOG(2) << __func__; + + vr::WebXrSharedBuffer* shared_buffer = + webxr_->GetRenderingFrame()->shared_buffer.get(); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); + CopyTextureToFramebuffer(shared_buffer->local_texture, frame_size, + uv_transform); +} + +void ArImageTransport::CopyTextureToFramebuffer( + GLuint texture, + const gfx::Size& frame_size, + const gfx::Transform& uv_transform) { + DVLOG(2) << __func__; // Don't need face culling, depth testing, blending, etc. Turn it all off. // TODO(klausw): see if we can do this one time on initialization. That would // be a tiny bit more efficient, but is only safe if ARCore and ArRenderer // don't modify these states. glDisable(GL_CULL_FACE); glDisable(GL_SCISSOR_TEST); - glDisable(GL_BLEND); glDisable(GL_POLYGON_OFFSET_FILL); glViewport(0, 0, frame_size.width(), frame_size.height()); // Draw the ARCore texture! float uv_transform_floats[16]; uv_transform.matrix().asColMajorf(uv_transform_floats); - ar_renderer_->Draw(camera_texture_id_arcore_, uv_transform_floats, 0, 0); - - glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); - - // Make a GpuFence and place it in the GPU stream for sequencing. - std::unique_ptr<gl::GLFence> gl_fence = gl::GLFence::CreateForGpuFence(); - std::unique_ptr<gfx::GpuFence> gpu_fence = gl_fence->GetGpuFence(); - - // Have GL wait on both the client fence and the creation/return sync token. - // TODO(piman): this should probably do an UpdateSharedImage. - mailbox_bridge_->WaitSyncToken(shared_buffer->mailbox_holder.sync_token); - mailbox_bridge_->WaitForClientGpuFence(gpu_fence.get()); - mailbox_bridge_->GenSyncToken(&shared_buffer->mailbox_holder.sync_token); - - gpu::MailboxHolder rendered_frame_holder = shared_buffer->mailbox_holder; - - // Done with the shared buffer. - swap_chain_->buffers.push(std::move(shared_buffer)); - - return rendered_frame_holder; + ar_renderer_->Draw(texture, uv_transform_floats, 0, 0); } bool ArImageTransport::IsOnGlThread() const {
diff --git a/chrome/browser/android/vr/arcore_device/ar_image_transport.h b/chrome/browser/android/vr/arcore_device/ar_image_transport.h index 6703c43..747e2f9 100644 --- a/chrome/browser/android/vr/arcore_device/ar_image_transport.h +++ b/chrome/browser/android/vr/arcore_device/ar_image_transport.h
@@ -12,19 +12,23 @@ #include "device/vr/public/mojom/vr_service.mojom.h" #include "ui/gfx/geometry/size_f.h" +namespace gfx { +class GpuFence; +} // namespace gfx + namespace gpu { struct MailboxHolder; +struct SyncToken; } // namespace gpu namespace vr { class MailboxToSurfaceBridge; +class WebXrPresentationState; +struct WebXrSharedBuffer; } // namespace vr namespace device { -struct SharedFrameBuffer; -struct SharedFrameBufferSwapChain; - // This class copies the camera texture to a shared image and returns a mailbox // holder which is suitable for mojo transport to the Renderer. class ArImageTransport { @@ -34,7 +38,7 @@ virtual ~ArImageTransport(); // Initialize() must be called on a valid GL thread. - virtual bool Initialize(); + virtual bool Initialize(vr::WebXrPresentationState* webxr); virtual GLuint GetCameraTextureId(); @@ -43,22 +47,32 @@ // a gpu::MailboxHolder with that texture copied to a shared buffer. virtual gpu::MailboxHolder TransferFrame(const gfx::Size& frame_size, const gfx::Transform& uv_transform); + virtual void CreateGpuFenceForSyncToken( + const gpu::SyncToken& sync_token, + base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)>); + virtual void CopyCameraImageToFramebuffer(const gfx::Size& frame_size, + const gfx::Transform& uv_transform); + virtual void CopyDrawnImageToFramebuffer(const gfx::Size& frame_size, + const gfx::Transform& uv_transform); + virtual void CopyTextureToFramebuffer(GLuint texture, + const gfx::Size& frame_size, + const gfx::Transform& uv_transform); + virtual void WaitSyncToken(const gpu::SyncToken& sync_token); private: - void SetupHardwareBuffers(); - void ResizeSharedBuffer(const gfx::Size& size, SharedFrameBuffer* buffer); + std::unique_ptr<vr::WebXrSharedBuffer> CreateBuffer(); + void ResizeSharedBuffer(const gfx::Size& size, vr::WebXrSharedBuffer* buffer); bool IsOnGlThread() const; std::unique_ptr<ArRenderer> ar_renderer_; // samplerExternalOES texture data for WebXR content image. GLuint camera_texture_id_arcore_ = 0; GLuint camera_fbo_ = 0; - GLuint transfer_fbo_ = 0; - bool transfer_fbo_completeness_checked_ = false; scoped_refptr<base::SingleThreadTaskRunner> gl_thread_task_runner_; std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge_; - std::unique_ptr<SharedFrameBufferSwapChain> swap_chain_; + + vr::WebXrPresentationState* webxr_ = nullptr; DISALLOW_COPY_AND_ASSIGN(ArImageTransport); };
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device.cc b/chrome/browser/android/vr/arcore_device/arcore_device.cc index fdc03ed..796129fc 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_device.cc +++ b/chrome/browser/android/vr/arcore_device/arcore_device.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/android/vr/arcore_device/arcore_impl.h" #include "chrome/browser/android/vr/arcore_device/arcore_install_utils.h" #include "chrome/browser/android/vr/arcore_device/arcore_java_utils.h" -#include "chrome/browser/android/vr/arcore_device/arcore_permission_helper.h" #include "chrome/browser/android/vr/mailbox_to_surface_bridge.h" #include "chrome/browser/permissions/permission_manager.h" #include "chrome/browser/permissions/permission_result.h" @@ -34,10 +33,12 @@ namespace { -mojom::VRDisplayInfoPtr CreateVRDisplayInfo(mojom::XRDeviceId device_id) { +mojom::VRDisplayInfoPtr CreateVRDisplayInfo(mojom::XRDeviceId device_id, + const gfx::Size& frame_size) { mojom::VRDisplayInfoPtr device = mojom::VRDisplayInfo::New(); device->id = device_id; device->displayName = "ARCore VR Device"; + device->webxr_default_framebuffer_scale = 1.0; device->capabilities = mojom::VRDisplayCapabilities::New(); device->capabilities->hasPosition = true; device->capabilities->hasExternalDisplay = false; @@ -48,11 +49,11 @@ mojom::VREyeParametersPtr& left_eye = device->leftEye; left_eye->fieldOfView = mojom::VRFieldOfView::New(); // TODO(lincolnfrog): get these values for real (see gvr device). - uint width = 1080; - uint height = 1795; double fov_x = 1437.387; double fov_y = 1438.074; // TODO(lincolnfrog): get real camera intrinsics. + int width = frame_size.width(); + int height = frame_size.height(); float horizontal_degrees = atan(width / (2.0 * fov_x)) * kDegreesPerRadian; float vertical_degrees = atan(height / (2.0 * fov_y)) * kDegreesPerRadian; left_eye->fieldOfView->leftDegrees = horizontal_degrees; @@ -67,21 +68,27 @@ } // namespace +ArCoreDevice::SessionState::SessionState() = default; +ArCoreDevice::SessionState::~SessionState() = default; + ArCoreDevice::ArCoreDevice( std::unique_ptr<ArCoreFactory> arcore_factory, std::unique_ptr<ArImageTransportFactory> ar_image_transport_factory, std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_to_surface_bridge, - std::unique_ptr<vr::ArCoreInstallUtils> arcore_install_utils, - std::unique_ptr<ArCorePermissionHelper> arcore_permission_helper) + std::unique_ptr<vr::ArCoreInstallUtils> arcore_install_utils) : VRDeviceBase(mojom::XRDeviceId::ARCORE_DEVICE_ID), main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), arcore_factory_(std::move(arcore_factory)), ar_image_transport_factory_(std::move(ar_image_transport_factory)), mailbox_bridge_(std::move(mailbox_to_surface_bridge)), arcore_install_utils_(std::move(arcore_install_utils)), - arcore_permission_helper_(std::move(arcore_permission_helper)), + session_state_(std::make_unique<ArCoreDevice::SessionState>()), weak_ptr_factory_(this) { - SetVRDisplayInfo(CreateVRDisplayInfo(GetId())); + // Ensure display_info_ is set to avoid crash in CallDeferredSessionCallback + // if initialization fails. Use an arbitrary but really low resolution to make + // it obvious if we're using this data instead of the actual values we get + // from the output drawing surface. + SetVRDisplayInfo(CreateVRDisplayInfo(GetId(), {16, 16})); // TODO(https://crbug.com/836524) clean up usage of mailbox bridge // and extract the methods in this class that interact with ARCore API @@ -91,178 +98,253 @@ } ArCoreDevice::ArCoreDevice() - : ArCoreDevice(std::make_unique<ArCoreImplFactory>(), - std::make_unique<ArImageTransportFactory>(), - std::make_unique<vr::MailboxToSurfaceBridge>(), - std::make_unique<vr::ArCoreJavaUtils>( - base::BindRepeating( - &ArCoreDevice::OnRequestInstallArModuleResult, - base::Unretained( - this)), // unretained is fine since ArCoreDevice - // owns the ArCoreJavaUtils instance - base::BindRepeating( - &ArCoreDevice::OnRequestInstallSupportedArCoreResult, - base::Unretained(this))), // ditto - std::make_unique<ArCorePermissionHelper>()) {} + : ArCoreDevice( + std::make_unique<ArCoreImplFactory>(), + std::make_unique<ArImageTransportFactory>(), + std::make_unique<vr::MailboxToSurfaceBridge>(), + std::make_unique<vr::ArCoreJavaUtils>( + base::BindRepeating( + &ArCoreDevice::OnRequestInstallArModuleResult, + base::Unretained(this)), // unretained is fine for callbacks + // since ArCoreDevice owns the + // ArCoreJavaUtils instance + base::BindRepeating( + &ArCoreDevice::OnRequestInstallSupportedArCoreResult, + base::Unretained(this)))) {} ArCoreDevice::~ArCoreDevice() { - CallDeferredRequestSessionCallbacks(/*success=*/false); + CallDeferredRequestSessionCallback(/*success=*/false); // The GL thread must be terminated since it uses our members. For example, // there might still be a posted Initialize() call in flight that uses // arcore_install_utils_ and arcore_factory_. Ensure that the thread is // stopped before other members get destructed. Don't call Stop() here, // destruction calls Stop() and doing so twice is illegal (null pointer // dereference). - arcore_gl_thread_ = nullptr; + session_state_->arcore_gl_thread_ = nullptr; } void ArCoreDevice::OnMailboxBridgeReady() { + DVLOG(1) << __func__; DCHECK(IsOnMainThread()); - DCHECK(!arcore_gl_thread_); + DCHECK(!session_state_->arcore_gl_thread_); // MailboxToSurfaceBridge's destructor's call to DestroyContext must // happen on the GL thread, so transferring it to that thread is appropriate. // TODO(https://crbug.com/836553): use same GL thread as GVR. - arcore_gl_thread_ = std::make_unique<ArCoreGlThread>( + session_state_->arcore_gl_thread_ = std::make_unique<ArCoreGlThread>( std::move(ar_image_transport_factory_), std::move(mailbox_bridge_), CreateMainThreadCallback(base::BindOnce( &ArCoreDevice::OnArCoreGlThreadInitialized, GetWeakPtr()))); - arcore_gl_thread_->Start(); + session_state_->arcore_gl_thread_->Start(); } void ArCoreDevice::OnArCoreGlThreadInitialized() { + DVLOG(1) << __func__; DCHECK(IsOnMainThread()); - is_arcore_gl_thread_initialized_ = true; + session_state_->is_arcore_gl_thread_initialized_ = true; - if (pending_request_ar_module_callback_) { - std::move(pending_request_ar_module_callback_).Run(); + if (session_state_->pending_request_session_after_gl_thread_initialized_) { + std::move( + session_state_->pending_request_session_after_gl_thread_initialized_) + .Run(); } } void ArCoreDevice::RequestSession( mojom::XRRuntimeSessionOptionsPtr options, mojom::XRRuntime::RequestSessionCallback callback) { + DVLOG(1) << __func__; DCHECK(IsOnMainThread()); - // If we are currently handling another request defer this request. All - // deferred requests will be processed once handling is complete. - deferred_request_session_callbacks_.push_back(std::move(callback)); - if (deferred_request_session_callbacks_.size() > 1) { + if (session_state_->pending_request_session_callback_) { + DVLOG(1) << __func__ << ": Rejecting additional session request"; + std::move(callback).Run(nullptr, nullptr); return; } + session_state_->pending_request_session_callback_ = std::move(callback); + + if (session_state_->is_arcore_gl_thread_initialized_) { + // First session on a new ArCoreDevice, and it's ready to proceed now. + RequestSessionAfterInitialization(options->render_process_id, + options->render_frame_id); + } else { + if (mailbox_bridge_) { + // This is a new ArCoreDevice, but its mailbox_bridge_ hasn't finished + // initialization yet. + } else { + // We're reusing a previously constructed ArCoreDevice for a new session. + // Restart initialization. + mailbox_bridge_ = std::make_unique<vr::MailboxToSurfaceBridge>(); + mailbox_bridge_->CreateUnboundContextProvider( + base::BindOnce(&ArCoreDevice::OnMailboxBridgeReady, GetWeakPtr())); + } + + // We're now expecting a call to OnMailboxBridgeReady() which will create + // a new GL thread, and at some point after that GL thread initialization + // will complete which calls OnArCoreGlThreadInitialized(). + session_state_->pending_request_session_after_gl_thread_initialized_ = + base::BindOnce(&ArCoreDevice::RequestSessionAfterInitialization, + GetWeakPtr(), options->render_process_id, + options->render_frame_id); + } +} + +void ArCoreDevice::RequestSessionAfterInitialization(int render_process_id, + int render_frame_id) { + session_state_->start_immersive_activity_callback_ = + base::BindOnce(&ArCoreDevice::RequestArSessionConsent, GetWeakPtr(), + render_process_id, render_frame_id); + + RequestArModule(render_process_id, render_frame_id); +} + +void ArCoreDevice::RequestArSessionConsent(int render_process_id, + int render_frame_id) { + auto ready_callback = + base::BindRepeating(&ArCoreDevice::OnDrawingSurfaceReady, GetWeakPtr()); + auto touch_callback = + base::BindRepeating(&ArCoreDevice::OnDrawingSurfaceTouch, GetWeakPtr()); + auto destroyed_callback = + base::BindOnce(&ArCoreDevice::OnDrawingSurfaceDestroyed, GetWeakPtr()); + + arcore_install_utils_->RequestArSession( + render_process_id, render_frame_id, std::move(ready_callback), + std::move(touch_callback), std::move(destroyed_callback)); +} + +void ArCoreDevice::OnDrawingSurfaceReady(gfx::AcceleratedWidget window, + display::Display::Rotation rotation, + const gfx::Size& frame_size) { + DVLOG(1) << __func__ << ": size=" << frame_size.width() << "x" + << frame_size.height() << " rotation=" << static_cast<int>(rotation); + DCHECK(!session_state_->is_arcore_gl_initialized_); + + auto display_info = CreateVRDisplayInfo(GetId(), frame_size); + SetVRDisplayInfo(std::move(display_info)); + + RequestArCoreGlInitialization(window, rotation, frame_size); +} + +void ArCoreDevice::OnDrawingSurfaceTouch(bool touching, + const gfx::PointF& location) { + DVLOG(2) << __func__ << ": touching=" << touching; + + if (!session_state_->is_arcore_gl_initialized_ || + !session_state_->arcore_gl_thread_) + return; + + PostTaskToGlThread(base::BindOnce( + &ArCoreGl::OnScreenTouch, + session_state_->arcore_gl_thread_->GetArCoreGl()->GetWeakPtr(), touching, + location)); +} + +void ArCoreDevice::OnDrawingSurfaceDestroyed() { + DVLOG(1) << __func__; + + CallDeferredRequestSessionCallback(/*success=*/false); + + OnSessionEnded(); +} + +void ArCoreDevice::OnSessionEnded() { + DVLOG(1) << __func__; + + // This may be a no-op in case it's destroyed already. + arcore_install_utils_->DestroyDrawingSurface(); + + // The GL thread had initialized its context with a drawing_widget based on + // the ArImmersiveOverlay's Surface, and the one it has is no longer valid. + // For now, just destroy the GL thread so that it is recreated for the next + // session with fresh associated resources. Also go through these steps in + // case the GL thread hadn't completed, or had initialized partially, to + // ensure consistent state. // TODO(https://crbug.com/849568): Instead of splitting the initialization // of this class between construction and RequestSession, perform all the // initialization at once on the first successful RequestSession call. - if (!is_arcore_gl_thread_initialized_) { - pending_request_ar_module_callback_ = - base::BindOnce(&ArCoreDevice::RequestArModule, GetWeakPtr(), - options->render_process_id, options->render_frame_id, - options->has_user_activation); - return; - } - RequestArModule(options->render_process_id, options->render_frame_id, - options->has_user_activation); + // Reset per-session members to initial values. + session_state_ = std::make_unique<ArCoreDevice::SessionState>(); + + // The image transport factory should be reusable, but we've std::moved it + // to the GL thread. Make a new one for next time. (This is cheap, it's + // just a factory.) + ar_image_transport_factory_ = std::make_unique<ArImageTransportFactory>(); + + // Shut down the mailbox bridge, this has the side effect of also destroying + // GL resources in the GPU process. + mailbox_bridge_ = nullptr; } -void ArCoreDevice::RequestArModule(int render_process_id, - int render_frame_id, - bool has_user_activation) { +void ArCoreDevice::RequestArModule(int render_process_id, int render_frame_id) { + DVLOG(1) << __func__; if (arcore_install_utils_->ShouldRequestInstallArModule()) { if (!arcore_install_utils_->CanRequestInstallArModule()) { - OnRequestArModuleResult(render_process_id, render_frame_id, - has_user_activation, false); + OnRequestArModuleResult(render_process_id, render_frame_id, false); return; } on_request_ar_module_result_callback_ = base::BindOnce(&ArCoreDevice::OnRequestArModuleResult, GetWeakPtr(), - render_process_id, render_frame_id, has_user_activation); + render_process_id, render_frame_id); arcore_install_utils_->RequestInstallArModule(render_process_id, render_frame_id); return; } - OnRequestArModuleResult(render_process_id, render_frame_id, - has_user_activation, true); + OnRequestArModuleResult(render_process_id, render_frame_id, true); } void ArCoreDevice::OnRequestArModuleResult(int render_process_id, int render_frame_id, - bool has_user_activation, bool success) { - DVLOG(3) << __func__ << ": has_user_activation=" << has_user_activation - << ", success=" << success; + DVLOG(3) << __func__ << ": success=" << success; if (!success) { - CallDeferredRequestSessionCallbacks(/*success=*/false); + CallDeferredRequestSessionCallback(/*success=*/false); return; } - RequestArCoreInstallOrUpdate(render_process_id, render_frame_id, - has_user_activation); + RequestArCoreInstallOrUpdate(render_process_id, render_frame_id); } void ArCoreDevice::RequestArCoreInstallOrUpdate(int render_process_id, - int render_frame_id, - bool has_user_activation) { + int render_frame_id) { + DVLOG(1) << __func__; DCHECK(IsOnMainThread()); - DCHECK(is_arcore_gl_thread_initialized_); DCHECK(!on_request_arcore_install_or_update_result_callback_); if (arcore_install_utils_->ShouldRequestInstallSupportedArCore()) { // ARCore is not installed or requires an update. Store the callback to be // processed later once installation/update is complete or got cancelled. on_request_arcore_install_or_update_result_callback_ = base::BindOnce( - &ArCoreDevice::OnRequestArCoreInstallOrUpdateResult, GetWeakPtr(), - render_process_id, render_frame_id, has_user_activation); + &ArCoreDevice::OnRequestArCoreInstallOrUpdateResult, GetWeakPtr()); arcore_install_utils_->RequestInstallSupportedArCore(render_process_id, render_frame_id); return; } - OnRequestArCoreInstallOrUpdateResult(render_process_id, render_frame_id, - has_user_activation, true); + OnRequestArCoreInstallOrUpdateResult(true); } -void ArCoreDevice::OnRequestArCoreInstallOrUpdateResult( - int render_process_id, - int render_frame_id, - bool has_user_activation, - bool success) { +void ArCoreDevice::OnRequestArCoreInstallOrUpdateResult(bool success) { + DVLOG(1) << __func__; DCHECK(IsOnMainThread()); - DCHECK(is_arcore_gl_thread_initialized_); if (!success) { - CallDeferredRequestSessionCallbacks(/*success=*/false); + CallDeferredRequestSessionCallback(/*success=*/false); return; } - // TODO(https://crbug.com/845792): Consider calling a method to ask for the - // appropriate permissions. - // ARCore sessions require camera permission. - arcore_permission_helper_->RequestCameraPermission( - render_process_id, render_frame_id, has_user_activation, - base::BindOnce(&ArCoreDevice::OnRequestCameraPermissionComplete, - GetWeakPtr())); -} - -void ArCoreDevice::OnRequestCameraPermissionComplete(bool success) { - DCHECK(IsOnMainThread()); - DCHECK(is_arcore_gl_thread_initialized_); - - if (!success) { - CallDeferredRequestSessionCallbacks(/*success=*/false); - return; - } - - // By this point ARCore has already been set up, so continue handling request. - RequestArCoreGlInitialization(); + DCHECK(session_state_->start_immersive_activity_callback_); + base::ResetAndReturn(&session_state_->start_immersive_activity_callback_) + .Run(); } void ArCoreDevice::OnRequestInstallArModuleResult(bool success) { + DVLOG(1) << __func__; DCHECK(IsOnMainThread()); if (on_request_ar_module_result_callback_) { @@ -271,48 +353,60 @@ } void ArCoreDevice::OnRequestInstallSupportedArCoreResult(bool success) { + DVLOG(1) << __func__; DCHECK(IsOnMainThread()); - DCHECK(is_arcore_gl_thread_initialized_); DCHECK(on_request_arcore_install_or_update_result_callback_); std::move(on_request_arcore_install_or_update_result_callback_).Run(success); } -void ArCoreDevice::CallDeferredRequestSessionCallbacks(bool success) { +void ArCoreDevice::CallDeferredRequestSessionCallback(bool success) { + DVLOG(1) << __func__ << " success=" << success; DCHECK(IsOnMainThread()); - DCHECK(!success || is_arcore_gl_thread_initialized_); - for (auto& deferred_callback : deferred_request_session_callbacks_) { - // We don't expect this call to alter deferred_request_session_callbacks_. - // The call may request another session, which should be handled right here - // in this loop as well. + // We might not have any pending session requests, i.e. if destroyed + // immediately after construction. + if (!session_state_->pending_request_session_callback_) + return; - if (!success) { - std::move(deferred_callback).Run(nullptr, nullptr); - continue; - } + mojom::XRRuntime::RequestSessionCallback deferred_callback = + base::ResetAndReturn(&session_state_->pending_request_session_callback_); - auto callback = base::BindOnce(&ArCoreDevice::OnCreateSessionCallback, - GetWeakPtr(), std::move(deferred_callback)); - - PostTaskToGlThread(base::BindOnce( - &ArCoreGl::CreateSession, - arcore_gl_thread_->GetArCoreGl()->GetWeakPtr(), display_info_->Clone(), - CreateMainThreadCallback(std::move(callback)))); + if (!success) { + std::move(deferred_callback).Run(nullptr, nullptr); + return; } - deferred_request_session_callbacks_.clear(); + + // Success case should only happen after GL thread is ready. + DCHECK(session_state_->is_arcore_gl_thread_initialized_); + auto create_callback = + base::BindOnce(&ArCoreDevice::OnCreateSessionCallback, GetWeakPtr(), + std::move(deferred_callback)); + + auto shutdown_callback = + base::BindOnce(&ArCoreDevice::OnSessionEnded, GetWeakPtr()); + + PostTaskToGlThread(base::BindOnce( + &ArCoreGl::CreateSession, + session_state_->arcore_gl_thread_->GetArCoreGl()->GetWeakPtr(), + display_info_->Clone(), + CreateMainThreadCallback(std::move(create_callback)), + CreateMainThreadCallback(std::move(shutdown_callback)))); } void ArCoreDevice::OnCreateSessionCallback( mojom::XRRuntime::RequestSessionCallback deferred_callback, mojom::XRFrameDataProviderPtrInfo frame_data_provider_info, mojom::VRDisplayInfoPtr display_info, - mojom::XRSessionControllerPtrInfo session_controller_info) { + mojom::XRSessionControllerPtrInfo session_controller_info, + mojom::XRPresentationConnectionPtr presentation_connection) { + DVLOG(2) << __func__; DCHECK(IsOnMainThread()); mojom::XRSessionPtr session = mojom::XRSession::New(); session->data_provider = std::move(frame_data_provider_info); session->display_info = std::move(display_info); + session->submit_frame_sink = std::move(presentation_connection); mojom::XRSessionControllerPtr controller(std::move(session_controller_info)); @@ -321,17 +415,22 @@ void ArCoreDevice::PostTaskToGlThread(base::OnceClosure task) { DCHECK(IsOnMainThread()); - arcore_gl_thread_->GetArCoreGl()->GetGlThreadTaskRunner()->PostTask( - FROM_HERE, std::move(task)); + session_state_->arcore_gl_thread_->GetArCoreGl() + ->GetGlThreadTaskRunner() + ->PostTask(FROM_HERE, std::move(task)); } bool ArCoreDevice::IsOnMainThread() { return main_thread_task_runner_->BelongsToCurrentThread(); } -void ArCoreDevice::RequestArCoreGlInitialization() { +void ArCoreDevice::RequestArCoreGlInitialization( + gfx::AcceleratedWidget drawing_widget, + int drawing_rotation, + const gfx::Size& frame_size) { + DVLOG(1) << __func__; DCHECK(IsOnMainThread()); - DCHECK(is_arcore_gl_thread_initialized_); + DCHECK(session_state_->is_arcore_gl_thread_initialized_); if (!arcore_install_utils_->EnsureLoaded()) { DLOG(ERROR) << "ARCore was not loaded properly."; @@ -339,14 +438,17 @@ return; } - if (!is_arcore_gl_initialized_) { + if (!session_state_->is_arcore_gl_initialized_) { // We will only try to initialize ArCoreGl once, at the end of the // permission sequence, and will resolve pending requests that have queued // up once that initialization completes. We set is_arcore_gl_initialized_ // in the callback to block operations that require it to be ready. + auto rotation = static_cast<display::Display::Rotation>(drawing_rotation); PostTaskToGlThread(base::BindOnce( - &ArCoreGl::Initialize, arcore_gl_thread_->GetArCoreGl()->GetWeakPtr(), - arcore_install_utils_.get(), arcore_factory_.get(), + &ArCoreGl::Initialize, + session_state_->arcore_gl_thread_->GetArCoreGl()->GetWeakPtr(), + arcore_install_utils_.get(), arcore_factory_.get(), drawing_widget, + frame_size, rotation, CreateMainThreadCallback(base::BindOnce( &ArCoreDevice::OnArCoreGlInitializationComplete, GetWeakPtr())))); return; @@ -356,17 +458,20 @@ } void ArCoreDevice::OnArCoreGlInitializationComplete(bool success) { + DVLOG(1) << __func__; DCHECK(IsOnMainThread()); - DCHECK(is_arcore_gl_thread_initialized_); + DCHECK(session_state_->is_arcore_gl_thread_initialized_); if (!success) { - CallDeferredRequestSessionCallbacks(/*success=*/false); + CallDeferredRequestSessionCallback(/*success=*/false); return; } - is_arcore_gl_initialized_ = true; + session_state_->is_arcore_gl_initialized_ = true; - CallDeferredRequestSessionCallbacks(/*success=*/true); + // We only start GL initialization after the user has granted consent, so we + // can now start the session. + CallDeferredRequestSessionCallback(/*success=*/true); } } // namespace device
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device.h b/chrome/browser/android/vr/arcore_device/arcore_device.h index 583e74a6..fc2560b 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_device.h +++ b/chrome/browser/android/vr/arcore_device/arcore_device.h
@@ -12,10 +12,13 @@ #include "base/android/jni_android.h" #include "base/bind.h" +#include "base/callback.h" #include "base/macros.h" #include "base/optional.h" #include "device/vr/vr_device.h" #include "device/vr/vr_device_base.h" +#include "ui/gfx/geometry/size_f.h" +#include "ui/gfx/native_widget_types.h" namespace vr { class MailboxToSurfaceBridge; @@ -27,7 +30,6 @@ class ArImageTransportFactory; class ArCoreFactory; class ArCoreGlThread; -class ArCorePermissionHelper; class ArCoreDevice : public VRDeviceBase { public: @@ -35,8 +37,7 @@ std::unique_ptr<ArCoreFactory> arcore_factory, std::unique_ptr<ArImageTransportFactory> ar_image_transport_factory, std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_to_surface_bridge, - std::unique_ptr<vr::ArCoreInstallUtils> arcore_install_utils, - std::unique_ptr<ArCorePermissionHelper> arcore_permission_helper); + std::unique_ptr<vr::ArCoreInstallUtils> arcore_install_utils); ArCoreDevice(); ~ArCoreDevice() override; @@ -58,6 +59,13 @@ void OnArCoreGlThreadInitialized(); void OnRequestCameraPermissionComplete(bool success); + void OnDrawingSurfaceReady(gfx::AcceleratedWidget window, + display::Display::Rotation rotation, + const gfx::Size& frame_size); + void OnDrawingSurfaceTouch(bool touching, const gfx::PointF& location); + void OnDrawingSurfaceDestroyed(); + void OnSessionEnded(); + template <typename... Args> static void RunCallbackOnTaskRunner( const scoped_refptr<base::TaskRunner>& task_runner, @@ -78,51 +86,59 @@ bool IsOnMainThread(); - void RequestArModule(int render_process_id, - int render_frame_id, - bool has_user_activation); + void RequestSessionAfterInitialization(int render_process_id, + int render_frame_id); + void RequestArModule(int render_process_id, int render_frame_id); void OnRequestArModuleResult(int render_process_id, int render_frame_id, - bool has_user_activation, bool success); - void RequestArCoreInstallOrUpdate(int render_process_id, - int render_frame_id, - bool has_user_activation); - void OnRequestArCoreInstallOrUpdateResult(int render_process_id, - int render_frame_id, - bool has_user_activation, - bool success); - void CallDeferredRequestSessionCallbacks(bool success); + void RequestArCoreInstallOrUpdate(int render_process_id, int render_frame_id); + void OnRequestArCoreInstallOrUpdateResult(bool success); + void CallDeferredRequestSessionCallback(bool success); void OnRequestAndroidCameraPermissionResult( base::OnceCallback<void(bool)> callback, bool was_android_camera_permission_granted); - void RequestArCoreGlInitialization(); + void RequestArCoreGlInitialization(gfx::AcceleratedWidget window, + int rotation, + const gfx::Size& size); void OnArCoreGlInitializationComplete(bool success); + void RequestArSessionConsent(int render_process_id, int render_frame_id); void OnCreateSessionCallback( mojom::XRRuntime::RequestSessionCallback deferred_callback, mojom::XRFrameDataProviderPtrInfo frame_data_provider_info, mojom::VRDisplayInfoPtr display_info, - mojom::XRSessionControllerPtrInfo session_controller_info); + mojom::XRSessionControllerPtrInfo session_controller_info, + mojom::XRPresentationConnectionPtr presentation_connection); scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_; std::unique_ptr<ArCoreFactory> arcore_factory_; std::unique_ptr<ArImageTransportFactory> ar_image_transport_factory_; std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge_; - std::unique_ptr<ArCoreGlThread> arcore_gl_thread_; std::unique_ptr<vr::ArCoreInstallUtils> arcore_install_utils_; - std::unique_ptr<ArCorePermissionHelper> arcore_permission_helper_; - bool is_arcore_gl_thread_initialized_ = false; - bool is_arcore_gl_initialized_ = false; + // Encapsulates data with session lifetime. + struct SessionState { + SessionState(); + ~SessionState(); - // If we get a requestSession before we are completely initialized, store a - // callback to requesting the AR module since that is the next step that needs - // to be taken. - base::OnceClosure pending_request_ar_module_callback_; + std::unique_ptr<ArCoreGlThread> arcore_gl_thread_; + bool is_arcore_gl_thread_initialized_ = false; + bool is_arcore_gl_initialized_ = false; - std::vector<mojom::XRRuntime::RequestSessionCallback> - deferred_request_session_callbacks_; + base::OnceClosure start_immersive_activity_callback_; + + // The initial requestSession triggers the initialization sequence, store + // the callback for replying once that initialization completes. Only one + // concurrent session is supported, other requests are rejected. + mojom::XRRuntime::RequestSessionCallback pending_request_session_callback_; + + base::OnceClosure pending_request_session_after_gl_thread_initialized_; + }; + + // This object is reset to initial values when ending a session. This helps + // ensure that each session has consistent per-session state. + std::unique_ptr<SessionState> session_state_; base::OnceCallback<void(bool)> on_request_arcore_install_or_update_result_callback_;
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc b/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc index 0aacf466..cc514c7 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc +++ b/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc
@@ -14,7 +14,6 @@ #include "chrome/browser/android/vr/arcore_device/arcore_device.h" #include "chrome/browser/android/vr/arcore_device/arcore_gl.h" #include "chrome/browser/android/vr/arcore_device/arcore_install_utils.h" -#include "chrome/browser/android/vr/arcore_device/arcore_permission_helper.h" #include "chrome/browser/android/vr/arcore_device/fake_arcore.h" #include "chrome/browser/android/vr/mailbox_to_surface_bridge.h" #include "device/vr/public/mojom/vr_service.mojom.h" @@ -33,7 +32,7 @@ // TODO(lincolnfrog): verify this gets called on GL thread. // TODO(lincolnfrog): test what happens if this returns false. - bool Initialize() override { return true; } + bool Initialize(vr::WebXrPresentationState*) override { return true; } // TODO(lincolnfrog): test verify this somehow. GLuint GetCameraTextureId() override { return CAMERA_TEXTURE_ID; } @@ -95,6 +94,19 @@ bool ShouldRequestInstallSupportedArCore() override { return false; } void RequestInstallSupportedArCore(int render_process_id, int render_frame_id) override {} + void RequestArSession( + int render_process_id, + int render_frame_id, + vr::SurfaceReadyCallback ready_callback, + vr::SurfaceTouchCallback touch_callback, + vr::SurfaceDestroyedCallback destroyed_callback) override { + // Return arbitrary screen geometry as stand-in for the expected + // drawing surface. It's not actually a surface, hence the nullptr + // instead of a WindowAndroid. + std::move(ready_callback) + .Run(nullptr, display::Display::Rotation::ROTATE_0, {1024, 512}); + } + void DestroyDrawingSurface() override {} bool EnsureLoaded() override { return true; } @@ -113,43 +125,14 @@ } }; -class StubArCorePermissionHelper : public ArCorePermissionHelper { - public: - StubArCorePermissionHelper() = default; - - MOCK_METHOD4(DoRequestCameraPermission, - void(int render_process_id, - int render_frame_id, - bool has_user_activation, - base::OnceCallback<void(bool)> callback)); - void RequestCameraPermission( - int render_process_id, - int render_frame_id, - bool has_user_activation, - base::OnceCallback<void(bool)> callback) override { - callback_ = std::move(callback); - if (request_camera_permission_quit_closure) { - std::move(request_camera_permission_quit_closure).Run(); - } - } - - void CallCallback(bool result) { std::move(callback_).Run(result); } - - base::OnceClosure request_camera_permission_quit_closure; - - private: - base::OnceCallback<void(bool)> callback_; -}; - class ArCoreDeviceTest : public testing::Test { public: ArCoreDeviceTest() {} ~ArCoreDeviceTest() override {} - static const gfx::Size kTestFrameSize; - void OnSessionCreated(mojom::XRSessionPtr session, mojom::XRSessionControllerPtr controller) { + DVLOG(1) << __func__; session_ = std::move(session); controller_ = std::move(controller); // TODO(crbug.com/837834): verify that things fail if restricted. @@ -166,7 +149,6 @@ StubMailboxToSurfaceBridge* bridge; StubArCoreInstallUtils* install_utils; - StubArCorePermissionHelper* permission_helper; mojom::XRFrameDataProviderPtr frame_provider; mojom::XREnvironmentIntegrationProviderAssociatedPtr environment_provider; std::unique_ptr<base::RunLoop> run_loop; @@ -180,21 +162,17 @@ std::unique_ptr<StubArCoreInstallUtils> install_utils_ptr = std::make_unique<StubArCoreInstallUtils>(); install_utils = install_utils_ptr.get(); - std::unique_ptr<StubArCorePermissionHelper> permission_helper_ptr = - std::make_unique<StubArCorePermissionHelper>(); - permission_helper = permission_helper_ptr.get(); device_ = std::make_unique<ArCoreDevice>( std::make_unique<FakeArCoreFactory>(), std::make_unique<StubArImageTransportFactory>(), std::move(bridge_ptr), - std::move(install_utils_ptr), std::move(permission_helper_ptr)); + std::move(install_utils_ptr)); } void CreateSession() { mojom::XRRuntimeSessionOptionsPtr options = mojom::XRRuntimeSessionOptions::New(); - options->immersive = false; - // TODO(crbug.com/837834): ensure request fails without user activation? - options->has_user_activation = true; + options->environment_integration = true; + options->immersive = true; device()->RequestSession(std::move(options), base::BindOnce(&ArCoreDeviceTest::OnSessionCreated, base::Unretained(this))); @@ -204,25 +182,12 @@ // DoCreateUnboundContextProvider(testing::_)).Times(1); run_loop = std::make_unique<base::RunLoop>(); - permission_helper->request_camera_permission_quit_closure = - run_loop->QuitClosure(); - bridge->CallCallback(); - run_loop->Run(); - - // TODO(https://crbug.com/837834): figure out how to make this work. - // EXPECT_CALL(*permission_helper, DoRequestCameraPermission(testing::_, - // testing::_, testing::_, testing::_)).Times(1); - - run_loop = std::make_unique<base::RunLoop>(); quit_closure = run_loop->QuitClosure(); - permission_helper->CallCallback(true); + bridge->CallCallback(); run_loop->Run(); EXPECT_TRUE(environment_provider); EXPECT_TRUE(session_); - - environment_provider->UpdateSessionGeometry(kTestFrameSize, - display::Display::ROTATE_0); } mojom::XRFrameDataPtr GetFrameData() { @@ -256,9 +221,6 @@ mojom::XRSessionControllerPtr controller_; }; -// The default screen size for portrait mode on the Pixel Android phone. -const gfx::Size ArCoreDeviceTest::kTestFrameSize = {1080, 1795}; - TEST_F(ArCoreDeviceTest, RequestSession) { CreateSession(); } @@ -268,20 +230,6 @@ GetFrameData(); } -TEST_F(ArCoreDeviceTest, SetDisplayGeometry) { - CreateSession(); - - auto frame_data = GetFrameData(); - EXPECT_TRUE(frame_data->buffer_size.value() == kTestFrameSize); - - gfx::Size new_size = {100, 200}; - environment_provider->UpdateSessionGeometry(new_size, - display::Display::ROTATE_90); - - frame_data = GetFrameData(); - EXPECT_TRUE(frame_data->buffer_size.value() == new_size); -} - TEST_F(ArCoreDeviceTest, RequestHitTest) { CreateSession();
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.cc b/chrome/browser/android/vr/arcore_device/arcore_gl.cc index a05b998..2357e987 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_gl.cc +++ b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
@@ -5,11 +5,13 @@ #include "chrome/browser/android/vr/arcore_device/arcore_gl.h" #include <algorithm> +#include <iomanip> #include <limits> #include <utility> #include "base/android/android_hardware_buffer_compat.h" #include "base/android/jni_android.h" #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/callback_helpers.h" #include "base/containers/queue.h" #include "base/memory/ptr_util.h" @@ -20,7 +22,7 @@ #include "chrome/browser/android/vr/arcore_device/ar_image_transport.h" #include "chrome/browser/android/vr/arcore_device/arcore_impl.h" #include "chrome/browser/android/vr/arcore_device/arcore_install_utils.h" -#include "chrome/browser/android/vr/mailbox_to_surface_bridge.h" +#include "chrome/browser/android/vr/web_xr_presentation_state.h" #include "device/vr/public/mojom/vr_service.mojom.h" #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h" #include "ui/display/display.h" @@ -76,6 +78,11 @@ return result; } +gfx::Transform WebXRImageTransformMatrix() { + gfx::Transform result; + return result; +} + const gfx::Size kDefaultFrameSize = {1, 1}; const display::Display::Rotation kDefaultRotation = display::Display::ROTATE_0; @@ -96,34 +103,39 @@ ArCoreGl::ArCoreGl(std::unique_ptr<ArImageTransport> ar_image_transport) : gl_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), ar_image_transport_(std::move(ar_image_transport)), + webxr_(std::make_unique<vr::WebXrPresentationState>()), frame_data_binding_(this), session_controller_binding_(this), environment_binding_(this), - weak_ptr_factory_(this) {} + presentation_binding_(this), + weak_ptr_factory_(this) { + DVLOG(1) << __func__; + webxr_transform_ = WebXRImageTransformMatrix(); +} ArCoreGl::~ArCoreGl() { + DVLOG(1) << __func__; DCHECK(IsOnGlThread()); ar_image_transport_.reset(); + CloseBindingsIfOpen(); } void ArCoreGl::Initialize(vr::ArCoreInstallUtils* install_utils, ArCoreFactory* arcore_factory, + gfx::AcceleratedWidget drawing_widget, + const gfx::Size& frame_size, + display::Display::Rotation display_rotation, base::OnceCallback<void(bool)> callback) { DVLOG(3) << __func__; DCHECK(IsOnGlThread()); + DCHECK(!is_initialized_); - // Do not DCHECK !is_initialized to allow multiple calls to correctly - // proceed. This method may be called multiple times if a subsequent session - // request occurs before the first one completes and the callback is called. - // TODO(https://crbug.com/849568): This may not be necessary after - // addressing this issue. - if (is_initialized_) { - std::move(callback).Run(true); - return; - } + transfer_size_ = frame_size; + display_rotation_ = display_rotation; + should_update_display_geometry_ = true; - if (!InitializeGl()) { + if (!InitializeGl(drawing_widget)) { std::move(callback).Run(false); return; } @@ -155,12 +167,15 @@ } void ArCoreGl::CreateSession(mojom::VRDisplayInfoPtr display_info, - ArCoreGlCreateSessionCallback callback) { + ArCoreGlCreateSessionCallback create_callback, + base::OnceClosure shutdown_callback) { DVLOG(3) << __func__; DCHECK(IsOnGlThread()); DCHECK(is_initialized_); + session_shutdown_callback_ = std::move(shutdown_callback); + CloseBindingsIfOpen(); mojom::XRFrameDataProviderPtrInfo frame_data_provider_info; @@ -173,11 +188,33 @@ session_controller_binding_.set_connection_error_handler(base::BindOnce( &ArCoreGl::OnBindingDisconnect, weak_ptr_factory_.GetWeakPtr())); - std::move(callback).Run(std::move(frame_data_provider_info), - std::move(display_info), std::move(controller_info)); + device::mojom::XRPresentationProviderPtr presentation_provider; + presentation_binding_.Bind(mojo::MakeRequest(&presentation_provider)); + + device::mojom::XRPresentationTransportOptionsPtr transport_options = + device::mojom::XRPresentationTransportOptions::New(); + transport_options->wait_for_gpu_fence = true; + + // Currently, AR mode only supports Android O+ due to requiring + // AHardwareBuffer-backed GpuMemoryBuffer shared images. This could be + // extended back to Android N by using the SUBMIT_AS_MAILBOX_HOLDER method + // that uses Surface/SurfaceTexture. + transport_options->transport_method = + device::mojom::XRPresentationTransportMethod::DRAW_INTO_TEXTURE_MAILBOX; + + auto submit_frame_sink = device::mojom::XRPresentationConnection::New(); + submit_frame_sink->client_request = mojo::MakeRequest(&submit_client_); + submit_frame_sink->provider = presentation_provider.PassInterface(); + submit_frame_sink->transport_options = std::move(transport_options); + + display_info_ = std::move(display_info); + + std::move(create_callback) + .Run(std::move(frame_data_provider_info), display_info_->Clone(), + std::move(controller_info), std::move(submit_frame_sink)); } -bool ArCoreGl::InitializeGl() { +bool ArCoreGl::InitializeGl(gfx::AcceleratedWidget drawing_widget) { DVLOG(3) << __func__; DCHECK(IsOnGlThread()); @@ -190,9 +227,10 @@ } scoped_refptr<gl::GLSurface> surface = - gl::init::CreateOffscreenGLSurface(gfx::Size()); + gl::init::CreateViewGLSurface(drawing_widget); + DVLOG(3) << "surface=" << surface.get(); if (!surface.get()) { - DLOG(ERROR) << "gl::init::CreateOffscreenGLSurface failed"; + DLOG(ERROR) << "gl::init::CreateViewGLSurface failed"; return false; } @@ -207,7 +245,8 @@ return false; } - if (!ar_image_transport_->Initialize()) { + DVLOG(3) << "ar_image_transport_->Initialize()..."; + if (!ar_image_transport_->Initialize(webxr_.get())) { DLOG(ERROR) << "ARImageTransport failed to initialize"; return false; } @@ -217,6 +256,7 @@ surface_ = std::move(surface); context_ = std::move(context); + DVLOG(3) << "done"; return true; } @@ -225,6 +265,14 @@ mojom::XRFrameDataProvider::GetFrameDataCallback callback) { TRACE_EVENT0("gpu", __FUNCTION__); + if (webxr_->HaveAnimatingFrame()) { + DVLOG(3) << __func__ << ": deferring, HaveAnimatingFrame"; + pending_getframedata_ = + base::BindOnce(&ArCoreGl::GetFrameData, GetWeakPtr(), + std::move(options), std::move(callback)); + return; + } + DVLOG(3) << __func__ << ": should_update_display_geometry_=" << should_update_display_geometry_ << ", transfer_size_=" << transfer_size_.ToString() @@ -238,6 +286,11 @@ return; } + if (is_paused_) { + DVLOG(2) << __func__ << ": paused but frame data not restricted. Resuming."; + Resume(); + } + // Check if the frame_size and display_rotation updated last frame. If yes, // apply the update for this frame. if (should_recalculate_uvs_) { @@ -252,6 +305,26 @@ constexpr float depth_near = 0.1f; constexpr float depth_far = 1000.f; projection_ = arcore_->GetProjectionMatrix(depth_near, depth_far); + auto m = projection_.matrix(); + float left = depth_near * (m.get(2, 0) - 1.f) / m.get(0, 0); + float right = depth_near * (m.get(2, 0) + 1.f) / m.get(0, 0); + float bottom = depth_near * (m.get(2, 1) - 1.f) / m.get(1, 1); + float top = depth_near * (m.get(2, 1) + 1.f) / m.get(1, 1); + + // VRFieldOfView wants positive angles. + mojom::VRFieldOfViewPtr field_of_view = mojom::VRFieldOfView::New(); + field_of_view->leftDegrees = gfx::RadToDeg(atanf(-left / depth_near)); + field_of_view->rightDegrees = gfx::RadToDeg(atanf(right / depth_near)); + field_of_view->downDegrees = gfx::RadToDeg(atanf(-bottom / depth_near)); + field_of_view->upDegrees = gfx::RadToDeg(atanf(top / depth_near)); + DVLOG(3) << " fov degrees up=" << field_of_view->upDegrees + << " down=" << field_of_view->downDegrees + << " left=" << field_of_view->leftDegrees + << " right=" << field_of_view->rightDegrees; + + display_info_->leftEye->fieldOfView = std::move(field_of_view); + display_info_changed_ = true; + should_recalculate_uvs_ = false; } @@ -278,6 +351,7 @@ if (!camera_updated) { DVLOG(1) << "arcore_->Update() failed"; std::move(callback).Run(nullptr); + have_camera_image_ = false; return; } @@ -286,23 +360,37 @@ if (transfer_size_.IsEmpty()) { DLOG(ERROR) << "No valid AR frame size provided!"; std::move(callback).Run(nullptr); + have_camera_image_ = false; return; } - // Transfer the camera image texture to a MailboxHolder for transport to - // the renderer process. + have_camera_image_ = true; + mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New(); + + frame_data->frame_id = webxr_->StartFrameAnimating(); + DVLOG(2) << __func__ << " frame=" << frame_data->frame_id; + + if (display_info_changed_) { + frame_data->left_eye = display_info_->leftEye.Clone(); + display_info_changed_ = false; + } + // Set up a shared buffer for the renderer to draw into, it'll be sent + // alongside the frame pose. gpu::MailboxHolder buffer_holder = ar_image_transport_->TransferFrame(transfer_size_, uv_transform_); + if (pose) { + mojom::XRInputSourceStatePtr input_state = GetInputSourceState(); + if (input_state) { + input_states_.push_back(std::move(input_state)); + pose->input_state = std::move(input_states_); + } + } + // Create the frame data to return to the renderer. - mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New(); frame_data->pose = std::move(pose); frame_data->buffer_holder = buffer_holder; - frame_data->buffer_size = transfer_size_; frame_data->time_delta = base::TimeTicks::Now() - base::TimeTicks(); - // Convert the Transform's 4x4 matrix to 16 floats in column-major order. - frame_data->projection_matrix.emplace(16); - projection_.matrix().asColMajorf(frame_data->projection_matrix->data()); fps_meter_.AddFrame(base::TimeTicks::Now()); TRACE_COUNTER1("gpu", "WebXR FPS", fps_meter_.GetFPS()); @@ -316,6 +404,133 @@ base::Passed(&frame_data), base::Passed(&callback))); } +bool ArCoreGl::IsSubmitFrameExpected(int16_t frame_index) { + // submit_client_ could be null when we exit presentation, if there were + // pending SubmitFrame messages queued. XRSessionClient::OnExitPresent + // will clean up state in blink, so it doesn't wait for + // OnSubmitFrameTransferred or OnSubmitFrameRendered. Similarly, + // the animating frame state is cleared when exiting presentation, + // and we should ignore a leftover queued SubmitFrame. + if (!submit_client_.get() || !webxr_->HaveAnimatingFrame()) + return false; + + vr::WebXrFrame* animating_frame = webxr_->GetAnimatingFrame(); + + if (animating_frame->index != frame_index) { + DVLOG(1) << __func__ << ": wrong frame index, got " << frame_index + << ", expected " << animating_frame->index; + mojo::ReportBadMessage("SubmitFrame called with wrong frame index"); + CloseBindingsIfOpen(); + return false; + } + + // Frame looks valid. + return true; +} + +void ArCoreGl::SubmitFrameMissing(int16_t frame_index, + const gpu::SyncToken& sync_token) { + DVLOG(2) << __func__; + + if (!IsSubmitFrameExpected(frame_index)) + return; + + webxr_->RecycleUnusedAnimatingFrame(); + ar_image_transport_->WaitSyncToken(sync_token); + + // Draw the current camera texture to the output default framebuffer now. + if (have_camera_image_) { + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); + ar_image_transport_->CopyCameraImageToFramebuffer(transfer_size_, + uv_transform_); + have_camera_image_ = false; + } + + // We're done with the camera image for this frame, start the next ARCore + // update if we had deferred it. This will get the next frame's camera image + // and pose in parallel while we're waiting for this frame's rendered image. + if (pending_getframedata_) { + base::ResetAndReturn(&pending_getframedata_).Run(); + } + + surface_->SwapBuffers(base::DoNothing()); + DVLOG(3) << __func__ << ": frame=" << frame_index << " SwapBuffers"; +} + +void ArCoreGl::SubmitFrame(int16_t frame_index, + const gpu::MailboxHolder& mailbox, + base::TimeDelta time_waited) { + NOTIMPLEMENTED(); +} + +void ArCoreGl::SubmitFrameWithTextureHandle(int16_t frame_index, + mojo::ScopedHandle texture_handle) { + NOTIMPLEMENTED(); +} + +void ArCoreGl::SubmitFrameDrawnIntoTexture(int16_t frame_index, + const gpu::SyncToken& sync_token, + base::TimeDelta time_waited) { + DVLOG(2) << __func__ << ": frame=" << frame_index; + + if (!IsSubmitFrameExpected(frame_index)) + return; + + webxr_->TransitionFrameAnimatingToProcessing(); + + TRACE_EVENT0("gpu", "ArCore SubmitFrame"); + + // Draw the current camera texture to the output default framebuffer now. + if (have_camera_image_) { + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); + ar_image_transport_->CopyCameraImageToFramebuffer(transfer_size_, + uv_transform_); + have_camera_image_ = false; + } + + // We're done with the camera image for this frame, start the next ARCore + // update if we had deferred it. This will get the next frame's camera image + // and pose in parallel while we're waiting for this frame's rendered image. + if (pending_getframedata_) { + base::ResetAndReturn(&pending_getframedata_).Run(); + } + + ar_image_transport_->CreateGpuFenceForSyncToken( + sync_token, base::BindOnce(&ArCoreGl::OnWebXrTokenSignaled, GetWeakPtr(), + frame_index)); +} + +void ArCoreGl::OnWebXrTokenSignaled(int16_t frame_index, + std::unique_ptr<gfx::GpuFence> gpu_fence) { + DVLOG(3) << __func__ << ": frame=" << frame_index; + + webxr_->TransitionFrameProcessingToRendering(); + + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); + ar_image_transport_->CopyDrawnImageToFramebuffer(transfer_size_, + webxr_transform_); + surface_->SwapBuffers(base::DoNothing()); + DVLOG(3) << __func__ << ": frame=" << frame_index << " SwapBuffers"; + + webxr_->EndFrameRendering(); + + if (submit_client_) { + // Create a local GpuFence and pass it to the Renderer via IPC. + std::unique_ptr<gl::GLFence> gl_fence = gl::GLFence::CreateForGpuFence(); + std::unique_ptr<gfx::GpuFence> gpu_fence2 = gl_fence->GetGpuFence(); + submit_client_->OnSubmitFrameGpuFence( + gfx::CloneHandleForIPC(gpu_fence2->GetGpuFenceHandle())); + } +} + +void ArCoreGl::UpdateLayerBounds(int16_t frame_index, + const gfx::RectF& left_bounds, + const gfx::RectF& right_bounds, + const gfx::Size& source_size) { + DVLOG(2) << __func__; + // Nothing to do +} + void ArCoreGl::GetEnvironmentIntegrationProvider( device::mojom::XREnvironmentIntegrationProviderAssociatedRequest environment_request) { @@ -329,21 +544,6 @@ &ArCoreGl::OnBindingDisconnect, weak_ptr_factory_.GetWeakPtr())); } -void ArCoreGl::UpdateSessionGeometry( - const gfx::Size& frame_size, - display::Display::Rotation display_rotation) { - DVLOG(3) << __func__ << ": frame_size=" << frame_size.ToString() - << ", display_rotation=" << display_rotation; - - DCHECK(IsOnGlThread()); - DCHECK(is_initialized_); - - transfer_size_ = frame_size; - display_rotation_ = display_rotation; - - should_update_display_geometry_ = true; -} - void ArCoreGl::RequestHitTest( mojom::XRRayPtr ray, mojom::XREnvironmentIntegrationProvider::RequestHitTestCallback callback) { @@ -369,6 +569,7 @@ DCHECK(IsOnGlThread()); DCHECK(is_initialized_); + DVLOG(3) << __func__ << ": frame_data_restricted=" << frame_data_restricted; restrict_frame_data_ = frame_data_restricted; if (restrict_frame_data_) { Pause(); @@ -380,7 +581,7 @@ void ArCoreGl::ProcessFrame( mojom::XRFrameDataPtr frame_data, mojom::XRFrameDataProvider::GetFrameDataCallback callback) { - DVLOG(3) << __func__; + DVLOG(3) << __func__ << " frame=" << frame_data->frame_id; DCHECK(IsOnGlThread()); DCHECK(is_initialized_); @@ -416,24 +617,73 @@ std::move(callback).Run(std::move(frame_data)); } +void ArCoreGl::OnScreenTouch(bool touching, const gfx::PointF& touch_point) { + DVLOG(2) << __func__ << ": touching=" << touching; + screen_last_touch_ = touch_point; + screen_touch_active_ = touching; + if (touching) + screen_touch_pending_ = true; +} + +mojom::XRInputSourceStatePtr ArCoreGl::GetInputSourceState() { + DVLOG(3) << __func__; + + // If there's no active screen touch, and no unreported past click event, + // don't report a device. + if (!screen_touch_pending_ && !screen_touch_active_) + return nullptr; + + device::mojom::XRInputSourceStatePtr state = + device::mojom::XRInputSourceState::New(); + + // Only one controller is supported, so the source id can be static. + state->source_id = 1; + + state->primary_input_pressed = screen_touch_active_; + if (!screen_touch_active_ && screen_touch_pending_) { + state->primary_input_clicked = true; + screen_touch_pending_ = false; + } + + state->description = device::mojom::XRInputSourceDescription::New(); + + state->description->handedness = device::mojom::XRHandedness::NONE; + + state->description->target_ray_mode = device::mojom::XRTargetRayMode::TAPPING; + + // Controller doesn't have a measured position. + state->description->emulated_position = true; + + // TODO(klausw): fill in state->grip and state->description->pointer_offset + // by unprojecting the screen coordinates into a world space ray. + + return state; +} + void ArCoreGl::Pause() { DCHECK(IsOnGlThread()); DCHECK(is_initialized_); + DVLOG(1) << __func__; arcore_->Pause(); + is_paused_ = true; } void ArCoreGl::Resume() { DCHECK(IsOnGlThread()); DCHECK(is_initialized_); + DVLOG(1) << __func__; arcore_->Resume(); + is_paused_ = false; } void ArCoreGl::OnBindingDisconnect() { DVLOG(3) << __func__; CloseBindingsIfOpen(); + + base::ResetAndReturn(&session_shutdown_callback_).Run(); } void ArCoreGl::CloseBindingsIfOpen() { @@ -442,6 +692,7 @@ environment_binding_.Close(); frame_data_binding_.Close(); session_controller_binding_.Close(); + presentation_binding_.Close(); } bool ArCoreGl::IsOnGlThread() const {
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.h b/chrome/browser/android/vr/arcore_device/arcore_gl.h index ae3b85c..5a3756c1 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_gl.h +++ b/chrome/browser/android/vr/arcore_device/arcore_gl.h
@@ -20,11 +20,16 @@ #include "mojo/public/cpp/bindings/associated_binding.h" #include "mojo/public/cpp/bindings/binding.h" #include "ui/display/display.h" +#include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/quaternion.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/size_f.h" #include "ui/gfx/native_widget_types.h" +namespace gfx { +class GpuFence; +} // namespace gfx + namespace gl { class GLContext; class GLSurface; @@ -32,6 +37,7 @@ namespace vr { class ArCoreInstallUtils; +class WebXrPresentationState; } // namespace vr namespace device { @@ -44,11 +50,13 @@ using ArCoreGlCreateSessionCallback = base::OnceCallback<void( mojom::XRFrameDataProviderPtrInfo frame_data_provider_info, mojom::VRDisplayInfoPtr display_info, - mojom::XRSessionControllerPtrInfo session_controller_info)>; + mojom::XRSessionControllerPtrInfo session_controller_info, + mojom::XRPresentationConnectionPtr presentation_connection)>; // All of this class's methods must be called on the same valid GL thread with // the exception of GetGlThreadTaskRunner() and GetWeakPtr(). class ArCoreGl : public mojom::XRFrameDataProvider, + public mojom::XRPresentationProvider, public mojom::XREnvironmentIntegrationProvider, public mojom::XRSessionController { public: @@ -57,10 +65,14 @@ void Initialize(vr::ArCoreInstallUtils* install_utils, ArCoreFactory* arcore_factory, + gfx::AcceleratedWidget drawing_widget, + const gfx::Size& frame_size, + display::Display::Rotation display_rotation, base::OnceCallback<void(bool)> callback); void CreateSession(mojom::VRDisplayInfoPtr display_info, - ArCoreGlCreateSessionCallback callback); + ArCoreGlCreateSessionCallback create_callback, + base::OnceClosure shutdown_callback); const scoped_refptr<base::SingleThreadTaskRunner>& GetGlThreadTaskRunner() { return gl_thread_task_runner_; @@ -74,10 +86,20 @@ mojom::XREnvironmentIntegrationProviderAssociatedRequest environment_provider) override; - // mojom::XREnvironmentIntegrationProvider - void UpdateSessionGeometry( - const gfx::Size& frame_size, - display::Display::Rotation display_rotation) override; + // XRPresentationProvider + void SubmitFrameMissing(int16_t frame_index, const gpu::SyncToken&) override; + void SubmitFrame(int16_t frame_index, + const gpu::MailboxHolder& mailbox, + base::TimeDelta time_waited) override; + void SubmitFrameWithTextureHandle(int16_t frame_index, + mojo::ScopedHandle texture_handle) override; + void SubmitFrameDrawnIntoTexture(int16_t frame_index, + const gpu::SyncToken&, + base::TimeDelta time_waited) override; + void UpdateLayerBounds(int16_t frame_index, + const gfx::RectF& left_bounds, + const gfx::RectF& right_bounds, + const gfx::Size& source_size) override; void RequestHitTest( mojom::XRRayPtr, @@ -86,18 +108,27 @@ // mojom::XRSessionController void SetFrameDataRestricted(bool restricted) override; + void OnWebXrTokenSignaled(int16_t frame_index, + std::unique_ptr<gfx::GpuFence> gpu_fence); + + void OnScreenTouch(bool touching, const gfx::PointF& touch_point); + mojom::XRInputSourceStatePtr GetInputSourceState(); + base::WeakPtr<ArCoreGl> GetWeakPtr(); private: void Pause(); void Resume(); + bool IsSubmitFrameExpected(int16_t frame_index); void ProcessFrame(mojom::XRFrameDataPtr frame_data, mojom::XRFrameDataProvider::GetFrameDataCallback callback); - bool InitializeGl(); + bool InitializeGl(gfx::AcceleratedWidget drawing_widget); bool IsOnGlThread() const; + base::OnceClosure session_shutdown_callback_; + scoped_refptr<gl::GLSurface> surface_; scoped_refptr<gl::GLContext> context_; scoped_refptr<base::SingleThreadTaskRunner> gl_thread_task_runner_; @@ -106,18 +137,41 @@ std::unique_ptr<ArCore> arcore_; std::unique_ptr<ArImageTransport> ar_image_transport_; + // This class uses the same overall presentation state logic + // as GvrGraphicsDelegate, with some difference due to drawing + // camera images even on frames with no pose and therefore + // no blink-generated rendered image. + // + // Rough sequence is: + // + // SubmitFrame N N animating->processing + // draw camera N + // waitForToken + // GetFrameData N+1 N+1 start animating + // update ARCore N to N+1 + // OnToken N N processing->rendering + // draw rendered N + // swap N rendering done + // SubmitFrame N+1 N+1 animating->processing + // draw camera N+1 + // waitForToken + std::unique_ptr<vr::WebXrPresentationState> webxr_; + // Default dummy values to ensure consistent behaviour. gfx::Size transfer_size_ = gfx::Size(0, 0); display::Display::Rotation display_rotation_ = display::Display::ROTATE_0; bool should_update_display_geometry_ = true; gfx::Transform uv_transform_; + gfx::Transform webxr_transform_; gfx::Transform projection_; // The first run of ProduceFrame should set uv_transform_ and projection_ // using the default settings in ArCore. bool should_recalculate_uvs_ = true; + bool have_camera_image_ = false; bool is_initialized_ = false; + bool is_paused_ = true; bool restrict_frame_data_ = false; @@ -133,6 +187,37 @@ void OnBindingDisconnect(); void CloseBindingsIfOpen(); + mojo::Binding<device::mojom::XRPresentationProvider> presentation_binding_; + device::mojom::XRPresentationClientPtr submit_client_; + + base::OnceClosure pending_getframedata_; + + mojom::VRDisplayInfoPtr display_info_; + bool display_info_changed_ = false; + + std::vector<device::mojom::XRInputSourceStatePtr> input_states_; + gfx::PointF screen_last_touch_; + + // Screen touch start/end events get reported asynchronously. We want to + // report at least one "clicked" event even if start and end happen within a + // single frame. The "active" state corresponds to the current state and is + // updated asynchronously. The "pending" state is set to true whenever the + // screen is touched, but only gets cleared by the input source handler. + // + // active pending event + // 0 0 + // 1 1 + // 1 1 pressed=true (selectstart) + // 1 1 pressed=true + // 0 1->0 pressed=false clicked=true (selectend, click) + // + // 0 0 + // 1 1 + // 0 1 + // 0 1->0 pressed=false clicked=true (selectend, click) + float screen_touch_pending_ = false; + float screen_touch_active_ = false; + // Must be last. base::WeakPtrFactory<ArCoreGl> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(ArCoreGl);
diff --git a/chrome/browser/android/vr/arcore_device/arcore_install_utils.h b/chrome/browser/android/vr/arcore_device/arcore_install_utils.h index 58be55ff4..ae16038 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_install_utils.h +++ b/chrome/browser/android/vr/arcore_device/arcore_install_utils.h
@@ -7,9 +7,34 @@ #include "base/android/scoped_java_ref.h" #include "base/memory/weak_ptr.h" +#include "ui/display/display.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/native_widget_types.h" namespace vr { +// Immersive AR sessions use callbacks in the following sequence: +// +// RequestArSession +// [show consent prompt] +// if consent declined, or if camera permission refused after consent: +// DestroyedCallback +// +// if accepted: +// SurfaceReadyCallback +// SurfaceTouchCallback (repeated for each touch) +// [exit session via "back" button, or via JS session exit] +// DestroyedCallback +// +using SurfaceReadyCallback = + base::RepeatingCallback<void(gfx::AcceleratedWidget window, + display::Display::Rotation rotation, + const gfx::Size& size)>; +using SurfaceTouchCallback = + base::RepeatingCallback<void(bool touching, const gfx::PointF& location)>; +using SurfaceDestroyedCallback = base::OnceClosure; + class ArCoreInstallUtils { public: virtual ~ArCoreInstallUtils() = default; @@ -25,6 +50,13 @@ virtual bool EnsureLoaded() = 0; virtual base::android::ScopedJavaLocalRef<jobject> GetApplicationContext() = 0; + virtual void RequestArSession( + int render_process_id, + int render_frame_id, + SurfaceReadyCallback ready_callback, + SurfaceTouchCallback touch_callback, + SurfaceDestroyedCallback destroyed_callback) = 0; + virtual void DestroyDrawingSurface() = 0; }; } // namespace vr
diff --git a/chrome/browser/android/vr/arcore_device/arcore_java_utils.cc b/chrome/browser/android/vr/arcore_device/arcore_java_utils.cc index c461e2d..eb9d09d 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_java_utils.cc +++ b/chrome/browser/android/vr/arcore_device/arcore_java_utils.cc
@@ -98,6 +98,64 @@ getTabFromRenderer(render_process_id, render_frame_id)); } +void ArCoreJavaUtils::RequestArSession( + int render_process_id, + int render_frame_id, + SurfaceReadyCallback ready_callback, + SurfaceTouchCallback touch_callback, + SurfaceDestroyedCallback destroyed_callback) { + DVLOG(1) << __func__; + JNIEnv* env = AttachCurrentThread(); + + Java_ArCoreJavaUtils_launchArConsentDialog( + env, j_arcore_java_utils_, + getTabFromRenderer(render_process_id, render_frame_id)); + + surface_ready_callback_ = std::move(ready_callback); + surface_touch_callback_ = std::move(touch_callback); + surface_destroyed_callback_ = std::move(destroyed_callback); +} + +void ArCoreJavaUtils::DestroyDrawingSurface() { + DVLOG(1) << __func__; + JNIEnv* env = AttachCurrentThread(); + + Java_ArCoreJavaUtils_destroyArImmersiveOverlay(env, j_arcore_java_utils_); +} + +void ArCoreJavaUtils::OnDrawingSurfaceReady( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj, + const base::android::JavaParamRef<jobject>& surface, + int rotation, + int width, + int height) { + DVLOG(1) << __func__ << ": width=" << width << " height=" << height + << " rotation=" << rotation; + gfx::AcceleratedWidget window = + ANativeWindow_fromSurface(base::android::AttachCurrentThread(), surface); + display::Display::Rotation display_rotation = + static_cast<display::Display::Rotation>(rotation); + surface_ready_callback_.Run(window, display_rotation, {width, height}); +} + +void ArCoreJavaUtils::OnDrawingSurfaceTouch( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj, + bool touching, + float x, + float y) { + DVLOG(3) << __func__ << ": touching=" << touching; + surface_touch_callback_.Run(touching, {x, y}); +} + +void ArCoreJavaUtils::OnDrawingSurfaceDestroyed( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj) { + DVLOG(1) << __func__ << ":::"; + base::ResetAndReturn(&surface_destroyed_callback_).Run(); +} + void ArCoreJavaUtils::OnRequestInstallArModuleResult( JNIEnv* env, const base::android::JavaParamRef<jobject>& obj,
diff --git a/chrome/browser/android/vr/arcore_device/arcore_java_utils.h b/chrome/browser/android/vr/arcore_device/arcore_java_utils.h index 5920f64..5a5afe5 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_java_utils.h +++ b/chrome/browser/android/vr/arcore_device/arcore_java_utils.h
@@ -5,7 +5,9 @@ #ifndef CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_JAVA_UTILS_H_ #define CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_JAVA_UTILS_H_ +#include <android/native_window_jni.h> #include <jni.h> + #include "base/android/scoped_java_ref.h" #include "base/callback.h" #include "base/memory/weak_ptr.h" @@ -26,6 +28,12 @@ bool ShouldRequestInstallSupportedArCore() override; void RequestInstallSupportedArCore(int render_process_id, int render_frame_id) override; + void RequestArSession(int render_process_id, + int render_frame_id, + SurfaceReadyCallback ready_callback, + SurfaceTouchCallback touch_callback, + SurfaceDestroyedCallback destroyed_callback) override; + void DestroyDrawingSurface() override; // Methods called from the Java side. void OnRequestInstallArModuleResult( @@ -36,6 +44,21 @@ JNIEnv* env, const base::android::JavaParamRef<jobject>& obj, bool success); + void OnDrawingSurfaceReady( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj, + const base::android::JavaParamRef<jobject>& surface, + int rotation, + int width, + int height); + void OnDrawingSurfaceTouch(JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj, + bool touching, + float x, + float y); + void OnDrawingSurfaceDestroyed( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj); bool EnsureLoaded() override; base::android::ScopedJavaLocalRef<jobject> GetApplicationContext() override; @@ -49,6 +72,10 @@ base::RepeatingCallback<void(bool)> ar_core_installation_callback_; base::android::ScopedJavaGlobalRef<jobject> j_arcore_java_utils_; + + SurfaceReadyCallback surface_ready_callback_; + SurfaceTouchCallback surface_touch_callback_; + SurfaceDestroyedCallback surface_destroyed_callback_; }; } // namespace vr
diff --git a/chrome/browser/android/vr/arcore_device/arcore_permission_helper.cc b/chrome/browser/android/vr/arcore_device/arcore_permission_helper.cc deleted file mode 100644 index 5791ed11..0000000 --- a/chrome/browser/android/vr/arcore_device/arcore_permission_helper.cc +++ /dev/null
@@ -1,100 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/android/vr/arcore_device/arcore_permission_helper.h" - -#include "base/bind.h" -#include "chrome/browser/permissions/permission_manager.h" -#include "chrome/browser/permissions/permission_result.h" -#include "chrome/browser/permissions/permission_update_infobar_delegate_android.h" -#include "chrome/browser/profiles/profile.h" -#include "components/content_settings/core/common/content_settings_types.h" -#include "content/public/browser/render_frame_host.h" -#include "content/public/browser/web_contents.h" - -namespace device { - -ArCorePermissionHelper::ArCorePermissionHelper() : weak_ptr_factory_(this) {} - -ArCorePermissionHelper::~ArCorePermissionHelper() {} - -void ArCorePermissionHelper::RequestCameraPermission( - int render_process_id, - int render_frame_id, - bool has_user_activation, - base::OnceCallback<void(bool)> callback) { - content::RenderFrameHost* rfh = - content::RenderFrameHost::FromID(render_process_id, render_frame_id); - - DCHECK(rfh); - // The RFH may have been destroyed by the time the request is processed. - // We have to do a runtime check in addition to the DCHECK as it doesn't - // trigger in release. - if (!rfh) { - DLOG(ERROR) << "The RenderFrameHost was destroyed prior to permission"; - std::move(callback).Run(false); - } - - content::WebContents* web_contents = - content::WebContents::FromRenderFrameHost(rfh); - DCHECK(web_contents); - - Profile* profile = - Profile::FromBrowserContext(web_contents->GetBrowserContext()); - - PermissionManager* permission_manager = PermissionManager::Get(profile); - - permission_manager->RequestPermission( - CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, rfh, web_contents->GetURL(), - has_user_activation, - base::BindRepeating( - &ArCorePermissionHelper::OnRequestCameraPermissionResult, - GetWeakPtr(), web_contents, base::Passed(&callback))); -} - -void ArCorePermissionHelper::OnRequestCameraPermissionResult( - content::WebContents* web_contents, - base::OnceCallback<void(bool)> callback, - ContentSetting content_setting) { - // If the camera permission is not allowed, abort the request. - if (content_setting != CONTENT_SETTING_ALLOW) { - std::move(callback).Run(false); - return; - } - - // Even if the content setting stated that the camera access is allowed, - // the Android camera permission might still need to be requested, so check - // if the OS level permission infobar should be shown. - std::vector<ContentSettingsType> content_settings_types; - content_settings_types.push_back(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA); - ShowPermissionInfoBarState show_permission_info_bar_state = - PermissionUpdateInfoBarDelegate::ShouldShowPermissionInfoBar( - web_contents, content_settings_types); - switch (show_permission_info_bar_state) { - case ShowPermissionInfoBarState::NO_NEED_TO_SHOW_PERMISSION_INFOBAR: - std::move(callback).Run(true); - return; - case ShowPermissionInfoBarState::SHOW_PERMISSION_INFOBAR: - // Show the Android camera permission info bar. - PermissionUpdateInfoBarDelegate::Create( - web_contents, content_settings_types, - base::BindOnce( - &ArCorePermissionHelper::OnRequestAndroidCameraPermissionResult, - GetWeakPtr(), base::Passed(&callback))); - return; - case ShowPermissionInfoBarState::CANNOT_SHOW_PERMISSION_INFOBAR: - std::move(callback).Run(false); - return; - } - - NOTREACHED() << "Unknown show permission infobar state."; -} - -void ArCorePermissionHelper::OnRequestAndroidCameraPermissionResult( - base::OnceCallback<void(bool)> callback, - bool was_android_camera_permission_granted) { - std::move(callback).Run(was_android_camera_permission_granted); -} - -} // namespace device
diff --git a/chrome/browser/android/vr/arcore_device/arcore_permission_helper.h b/chrome/browser/android/vr/arcore_device/arcore_permission_helper.h deleted file mode 100644 index 20336de..0000000 --- a/chrome/browser/android/vr/arcore_device/arcore_permission_helper.h +++ /dev/null
@@ -1,53 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_PERMISSION_HELPER_H_ -#define CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_PERMISSION_HELPER_H_ - -#include <memory> -#include "base/android/java_handler_thread.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/single_thread_task_runner.h" -#include "chrome/browser/android/vr/mailbox_to_surface_bridge.h" -#include "components/content_settings/core/common/content_settings.h" - -namespace content { -class WebContents; -} // namespace content - -namespace device { - -class ArCorePermissionHelper { - public: - ArCorePermissionHelper(); - virtual ~ArCorePermissionHelper(); - - virtual void RequestCameraPermission(int render_process_id, - int render_frame_id, - bool has_user_activation, - base::OnceCallback<void(bool)> callback); - - virtual void OnRequestCameraPermissionResult( - content::WebContents* web_contents, - base::OnceCallback<void(bool)> callback, - ContentSetting content_setting); - - virtual void OnRequestAndroidCameraPermissionResult( - base::OnceCallback<void(bool)> callback, - bool was_android_camera_permission_granted); - - private: - base::WeakPtr<ArCorePermissionHelper> GetWeakPtr() { - return weak_ptr_factory_.GetWeakPtr(); - } - - // Must be last. - base::WeakPtrFactory<ArCorePermissionHelper> weak_ptr_factory_; - DISALLOW_COPY_AND_ASSIGN(ArCorePermissionHelper); -}; - -} // namespace device - -#endif // CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_PERMISSION_HELPER_H_
diff --git a/chrome/browser/autofill/captured_sites_test_utils.cc b/chrome/browser/autofill/captured_sites_test_utils.cc index a98694a..9068a74 100644 --- a/chrome/browser/autofill/captured_sites_test_utils.cc +++ b/chrome/browser/autofill/captured_sites_test_utils.cc
@@ -426,7 +426,6 @@ .AppendASCII("catapult") .AppendASCII("telemetry") .AppendASCII("telemetry") - .AppendASCII("internal") .AppendASCII("bin"); options.current_directory = web_page_replay_binary_dir;
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index c45606c..f07e481d 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4155,7 +4155,7 @@ void ChromeContentBrowserClient::OpenURL( content::SiteInstance* site_instance, const content::OpenURLParams& params, - const base::RepeatingCallback<void(content::WebContents*)>& callback) { + base::OnceCallback<void(content::WebContents*)> callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(ShouldAllowOpenURL(site_instance, params.url)); @@ -4163,7 +4163,7 @@ #if defined(OS_ANDROID) ServiceTabLauncher::GetInstance()->LaunchTab(browser_context, params, - callback); + std::move(callback)); #else NavigateParams nav_params(Profile::FromBrowserContext(browser_context), params.url, params.transition); @@ -4171,7 +4171,7 @@ nav_params.user_gesture = params.user_gesture; Navigate(&nav_params); - callback.Run(nav_params.navigated_or_inserted_contents); + std::move(callback).Run(nav_params.navigated_or_inserted_contents); #endif }
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h index 7926733..afc5f97 100644 --- a/chrome/browser/chrome_content_browser_client.h +++ b/chrome/browser/chrome_content_browser_client.h
@@ -429,10 +429,10 @@ base::StringPiece name) override; std::vector<service_manager::Manifest> GetExtraServiceManifests() override; std::vector<std::string> GetStartupServices() override; - void OpenURL(content::SiteInstance* site_instance, - const content::OpenURLParams& params, - const base::RepeatingCallback<void(content::WebContents*)>& - callback) override; + void OpenURL( + content::SiteInstance* site_instance, + const content::OpenURLParams& params, + base::OnceCallback<void(content::WebContents*)> callback) override; content::ControllerPresentationServiceDelegate* GetControllerPresentationServiceDelegate( content::WebContents* web_contents) override;
diff --git a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage_unittest.cc b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage_unittest.cc index 89d9d59..8f52880 100644 --- a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage_unittest.cc +++ b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage_unittest.cc
@@ -113,7 +113,7 @@ PrefService* pref_service = profile_->GetPrefs(); QuickUnlockStorageTestApi test_api(quick_unlock_storage); - // The default is one day, so verify moving the last strong auth time back 12 + // The default is two days, so verify moving the last strong auth time back 24 // hours(half of the expiration time) should not request strong auth. quick_unlock_storage->MarkStrongAuth(); base::TimeDelta expiration_time = GetExpirationTime(pref_service);
diff --git a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc index 2766e73..85afc3b 100644 --- a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc +++ b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc
@@ -63,8 +63,8 @@ return base::TimeDelta::FromHours(6); case PasswordConfirmationFrequency::TWELVE_HOURS: return base::TimeDelta::FromHours(12); - case PasswordConfirmationFrequency::DAY: - return base::TimeDelta::FromDays(1); + case PasswordConfirmationFrequency::TWO_DAYS: + return base::TimeDelta::FromDays(2); case PasswordConfirmationFrequency::WEEK: return base::TimeDelta::FromDays(7); } @@ -80,7 +80,7 @@ base::Value(std::move(quick_unlock_whitelist_default))); registry->RegisterIntegerPref( prefs::kQuickUnlockTimeout, - static_cast<int>(PasswordConfirmationFrequency::DAY)); + static_cast<int>(PasswordConfirmationFrequency::TWO_DAYS)); // Preferences related the lock screen pin unlock. registry->RegisterIntegerPref(prefs::kPinUnlockMinimumLength,
diff --git a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h index ffa95296..9c2581a 100644 --- a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h +++ b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h
@@ -23,7 +23,7 @@ enum class PasswordConfirmationFrequency { SIX_HOURS = 0, TWELVE_HOURS = 1, - DAY = 2, + TWO_DAYS = 2, WEEK = 3 };
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc index c0a62d6..38f0461 100644 --- a/chrome/browser/chromeos/login/session/user_session_manager.cc +++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -102,6 +102,7 @@ #include "chrome/browser/ui/startup/startup_browser_creator.h" #include "chrome/browser/ui/webui/chromeos/login/discover/discover_manager.h" #include "chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.h" +#include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h" #include "chrome/common/channel_info.h" #include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" @@ -146,6 +147,7 @@ #include "content/public/browser/notification_service.h" #include "content/public/browser/storage_partition.h" #include "content/public/common/content_switches.h" +#include "content/public/common/page_zoom.h" #include "extensions/common/features/feature_session_type.h" #include "rlz/buildflags/buildflags.h" #include "services/identity/public/cpp/accounts_mutator.h" @@ -1485,6 +1487,14 @@ } void UserSessionManager::FinalizePrepareProfile(Profile* profile) { + // Record each user's "Page zoom" setting for https://crbug.com/955071. + // This can be removed after M79. + double zoom_level = profile->GetZoomLevelPrefs()->GetDefaultZoomLevelPref(); + double zoom_factor = content::ZoomLevelToZoomFactor(zoom_level); + int zoom_percent = std::floor(zoom_factor * 100); + // Zoom can be greater than 100%. + UMA_HISTOGRAM_COUNTS_1000("Login.DefaultPageZoom", zoom_percent); + BootTimesRecorder::Get()->AddLoginTimeMarker("TPMOwn-End", false); user_manager::UserManager* user_manager = user_manager::UserManager::Get();
diff --git a/chrome/browser/extensions/extension_unload_browsertest.cc b/chrome/browser/extensions/extension_unload_browsertest.cc index 45dfa255..687a51d 100644 --- a/chrome/browser/extensions/extension_unload_browsertest.cc +++ b/chrome/browser/extensions/extension_unload_browsertest.cc
@@ -49,10 +49,6 @@ // After an extension is uninstalled, network requests from its content scripts // should fail but not kill the renderer process. IN_PROC_BROWSER_TEST_F(ExtensionUnloadBrowserTest, UnloadWithContentScripts) { - // https://crbug.com/862176 - if (base::FeatureList::IsEnabled(network::features::kNetworkService)) - return; - ASSERT_TRUE(embedded_test_server()->Start()); // Load an extension with a content script that has a button to send XHRs.
diff --git a/chrome/browser/extensions/service_worker_apitest.cc b/chrome/browser/extensions/service_worker_apitest.cc index e3d7ca4..9391ad5e 100644 --- a/chrome/browser/extensions/service_worker_apitest.cc +++ b/chrome/browser/extensions/service_worker_apitest.cc
@@ -15,6 +15,7 @@ #include "base/test/bind_test_util.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" +#include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/lazy_background_page_test_util.h" @@ -58,6 +59,7 @@ #include "extensions/browser/service_worker_task_queue.h" #include "extensions/common/api/test.h" #include "extensions/common/value_builder.h" +#include "extensions/common/verifier_formats.h" #include "extensions/test/background_page_watcher.h" #include "extensions/test/extension_test_message_listener.h" #include "extensions/test/result_catcher.h" @@ -1135,6 +1137,76 @@ EXPECT_EQ(starting_tab_count, browser()->tab_strip_model()->count()); } +// Tests that updating a packed extension with modified scripts works +// properly -- we expect that the new script will execute, rather than the +// previous one. +IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, UpdatePackedExtension) { + // Extensions APIs from SW are only enabled on trunk. + ScopedCurrentChannel current_channel_override(version_info::Channel::UNKNOWN); + constexpr char kManifest1[] = + R"({ + "name": "Test Extension", + "manifest_version": 2, + "version": "0.1", + "background": {"service_worker": "script.js"} + })"; + // This script installs an event listener for updates to the extension with + // a callback that forces itself to reload. + constexpr char kScript[] = + R"( + chrome.runtime.onUpdateAvailable.addListener(function(details) { + chrome.runtime.reload(); + }); + chrome.test.sendMessage('ready1'); + )"; + + std::string id; + TestExtensionDir test_dir; + + // Write the manifest and script files and load the extension. + test_dir.WriteManifest(kManifest1); + test_dir.WriteFile(FILE_PATH_LITERAL("script.js"), kScript); + + { + ExtensionTestMessageListener ready_listener("ready1", false); + base::FilePath path = test_dir.Pack(); + const Extension* extension = LoadExtension(path); + ASSERT_TRUE(extension); + + EXPECT_TRUE(ready_listener.WaitUntilSatisfied()); + id = extension->id(); + } + + constexpr char kManifest2[] = + R"({ + "name": "Test Extension", + "manifest_version": 2, + "version": "0.2", + "background": {"service_worker": "script.js"} + })"; + // Rewrite the manifest and script files with a version change in the manifest + // file. After reloading the extension, the old version of the extension + // should detect the update, force the reload, and the new script should + // execute. + test_dir.WriteManifest(kManifest2); + test_dir.WriteFile(FILE_PATH_LITERAL("script.js"), + "chrome.test.sendMessage('ready2');"); + { + ExtensionTestMessageListener ready_listener("ready2", false); + base::FilePath path = test_dir.Pack(); + ExtensionService* const extension_service = + ExtensionSystem::Get(profile())->extension_service(); + EXPECT_TRUE(extension_service->UpdateExtension( + CRXFileInfo(id, GetTestVerifierFormat(), path), true, nullptr)); + EXPECT_TRUE(ready_listener.WaitUntilSatisfied()); + EXPECT_EQ("0.2", ExtensionRegistry::Get(profile()) + ->enabled_extensions() + .GetByID(id) + ->version() + .GetString()); + } +} + // Tests that updating an unpacked extension with modified scripts works // properly -- we expect that the new script will execute, rather than the // previous one.
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index c95018233..c19ce9b 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -1716,6 +1716,11 @@ "expiry_milestone": 80 }, { + "name": "enable-tab-groups-ui-improvements", + "owners": [ "memex-team@google.com" ], + "expiry_milestone": 80 + }, + { "name": "enable-tab-switcher-on-return", "owners": [ "memex-team@google.com" ], "expiry_milestone": 76 @@ -2590,7 +2595,12 @@ }, { "name": "omnibox-ui-vertical-margin", - "owners": [ "chrome-omnibox-team@google.com" ], + "owners": [ "tommycli", "chrome-omnibox-team@google.com" ], + "expiry_milestone": 80 + }, + { + "name": "omnibox-ui-vertical-margin-limit-to-non-touch-only", + "owners": [ "tommycli", "chrome-omnibox-team@google.com" ], "expiry_milestone": 80 }, {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index d8197e9..e935e0f 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -1423,6 +1423,12 @@ const char kOmniboxUIVerticalMarginDescription[] = "Changes the vertical margin in the Omnibox UI."; +const char kOmniboxUIVerticalMarginLimitToNonTouchOnlyName[] = + "Omnibox UI Vertical Margin - Limit to Non-Touch Only"; +const char kOmniboxUIVerticalMarginLimitToNonTouchOnlyDescription[] = + "Limits the vertical margin UI experiment to non-touch devices only. Has " + "no effect if the Omnibox Vertical Margin experiment is not enabled."; + const char kOmniboxUIWhiteBackgroundOnBlurName[] = "Omnibox UI White Background On Blur"; const char kOmniboxUIWhiteBackgroundOnBlurDescription[] = @@ -1845,6 +1851,10 @@ const char kTabGroupsAndroidDescription[] = "Allows users to create groups to better organize their tabs."; +const char kTabGroupsUiImprovementsAndroidName[] = "Tab Groups UI Improvements"; +const char kTabGroupsUiImprovementsAndroidDescription[] = + "Allows users to access new features in Tab Group UI."; + const char kTabGroupsName[] = "Tab Groups"; const char kTabGroupsDescription[] = "Allows users to organize tabs into visually distinct groups, e.g. to "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 84c8694..8450a5b7 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -866,6 +866,9 @@ extern const char kOmniboxUIVerticalMarginName[]; extern const char kOmniboxUIVerticalMarginDescription[]; +extern const char kOmniboxUIVerticalMarginLimitToNonTouchOnlyName[]; +extern const char kOmniboxUIVerticalMarginLimitToNonTouchOnlyDescription[]; + extern const char kOmniboxUIWhiteBackgroundOnBlurName[]; extern const char kOmniboxUIWhiteBackgroundOnBlurDescription[]; @@ -1101,6 +1104,9 @@ extern const char kTabGroupsAndroidName[]; extern const char kTabGroupsAndroidDescription[]; +extern const char kTabGroupsUiImprovementsAndroidName[]; +extern const char kTabGroupsUiImprovementsAndroidDescription[]; + extern const char kTabGroupsName[]; extern const char kTabGroupsDescription[];
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc index d9984262..f28fd867 100644 --- a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc +++ b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
@@ -775,9 +775,10 @@ } // Test is flaky on chromeos and linux. https://crbug.com/938054. -// Test is flaky on mac: https://crbug.com/948674. -#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \ - defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) +// Test is flaky on mac and win: https://crbug.com/948674. +#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \ + defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || \ + defined(OS_WIN) #define MAYBE_ForegroundAndBackgroundPages DISABLED_ForegroundAndBackgroundPages #else #define MAYBE_ForegroundAndBackgroundPages ForegroundAndBackgroundPages
diff --git a/chrome/browser/resources/chromeos/switch_access/BUILD.gn b/chrome/browser/resources/chromeos/switch_access/BUILD.gn index 911970a..f14456fb 100644 --- a/chrome/browser/resources/chromeos/switch_access/BUILD.gn +++ b/chrome/browser/resources/chromeos/switch_access/BUILD.gn
@@ -59,6 +59,7 @@ "options.html", "options.js", "preferences.js", + "rect_helper.js", "switch_access.js", "switch_access_constants.js", "switch_access_predicate.js", @@ -168,6 +169,7 @@ ":navigation_manager", ":options", ":preferences", + ":rect_helper", ":switch_access", ":switch_access_constants", ":switch_access_interface", @@ -190,6 +192,7 @@ ":back_button_manager", ":menu_manager", ":menu_panel_interface", + ":rect_helper", ":switch_access_constants", ":switch_access_predicate", ":text_input_manager", @@ -263,6 +266,10 @@ externs_list = [ "$externs_path/chrome_extensions.js" ] } +js_library("rect_helper") { + externs_list = [ "$externs_path/accessibility_private.js" ] +} + js_library("switch_access") { deps = [ ":auto_scan_manager",
diff --git a/chrome/browser/resources/chromeos/switch_access/back_button_manager.js b/chrome/browser/resources/chromeos/switch_access/back_button_manager.js index ef3fc82..6647e02b 100644 --- a/chrome/browser/resources/chromeos/switch_access/back_button_manager.js +++ b/chrome/browser/resources/chromeos/switch_access/back_button_manager.js
@@ -30,14 +30,14 @@ /** * Shows the menu as just a back button in the upper right corner of the * node. - * @param {!chrome.automation.AutomationNode} node + * @param {chrome.accessibilityPrivate.ScreenRect=} nodeLocation */ - show(node) { - if (!node.location) + show(nodeLocation) { + if (!nodeLocation) return; this.backButtonOpen_ = true; chrome.accessibilityPrivate.setSwitchAccessMenuState( - true, node.location, 0 /* num_actions */); + true, nodeLocation, 0 /* num_actions */); this.menuPanel_.setFocusRing(SAConstants.BACK_ID, true); }
diff --git a/chrome/browser/resources/chromeos/switch_access/manifest.json.jinja2 b/chrome/browser/resources/chromeos/switch_access/manifest.json.jinja2 index 5301ca3..418712a8 100644 --- a/chrome/browser/resources/chromeos/switch_access/manifest.json.jinja2 +++ b/chrome/browser/resources/chromeos/switch_access/manifest.json.jinja2
@@ -20,6 +20,7 @@ "menu_manager.js", "navigation_manager.js", "preferences.js", + "rect_helper.js", "switch_access.js", "switch_access_constants.js", "switch_access_predicate.js",
diff --git a/chrome/browser/resources/chromeos/switch_access/menu_panel.css b/chrome/browser/resources/chromeos/switch_access/menu_panel.css index b16ae4b0..cceefe8 100644 --- a/chrome/browser/resources/chromeos/switch_access/menu_panel.css +++ b/chrome/browser/resources/chromeos/switch_access/menu_panel.css
@@ -37,7 +37,7 @@ } .focus, #back.focus { - outline: solid 3px rgb(138, 180, 248); /** GB300 from SAConstants */ + outline: solid 3px rgb(26, 115, 232); /** Focus color from SAConstants */ } img { display: block;
diff --git a/chrome/browser/resources/chromeos/switch_access/navigation_manager.js b/chrome/browser/resources/chromeos/switch_access/navigation_manager.js index 48f8fad..835beb22 100644 --- a/chrome/browser/resources/chromeos/switch_access/navigation_manager.js +++ b/chrome/browser/resources/chromeos/switch_access/navigation_manager.js
@@ -88,6 +88,12 @@ secondaryColor: SAConstants.Focus.SECONDARY_COLOR }; + /** + * The currently highlighted scope object. Tracked for comparison purposes. + * @private {chrome.automation.AutomationNode} + */ + this.focusedScope_; + this.init_(); } @@ -524,16 +530,23 @@ /** * Set the focus ring for the current node and scope. - * @param {chrome.accessibilityPrivate.ScreenRect=} opt_focusRect Optionally - * set where the focus should be. Prevents back button from being shown. * @private */ - updateFocusRings_(opt_focusRect) { + updateFocusRings_() { + const focusRect = this.node_.location; + // If the scope element has not changed, we want to use the previously + // calculated rect as the current scope rect. + let scopeRect = this.scope_ === this.focusedScope_ ? + this.scopeFocusRing_.rects[0] : + this.scope_.location; + this.focusedScope_ = this.scope_; + if (this.node_ === this.backButtonManager_.buttonNode()) { - this.backButtonManager_.show(this.scope_); + this.backButtonManager_.show(scopeRect); this.primaryFocusRing_.rects = []; - this.scopeFocusRing_.rects = [this.scope_.location]; + this.scopeFocusRing_.rects = [scopeRect]; + chrome.accessibilityPrivate.setFocusRings( [this.primaryFocusRing_, this.scopeFocusRing_]); @@ -541,11 +554,10 @@ } this.backButtonManager_.hide(); - const focusRect = opt_focusRect || this.node_.location; - const scopeRect = this.scope_.location; - - // TODO(anastasi): Make adjustments to scope rect so it draws entirely - // outside the focus rect. + // If the current element is not the back button, the scope rect should + // expand to contain the focus rect. + scopeRect = RectHelper.expandToFitWithPadding( + SAConstants.Focus.SCOPE_BUFFER, scopeRect, focusRect); this.primaryFocusRing_.rects = [focusRect]; this.scopeFocusRing_.rects = [scopeRect];
diff --git a/chrome/browser/resources/chromeos/switch_access/rect_helper.js b/chrome/browser/resources/chromeos/switch_access/rect_helper.js new file mode 100644 index 0000000..0243165 --- /dev/null +++ b/chrome/browser/resources/chromeos/switch_access/rect_helper.js
@@ -0,0 +1,64 @@ +// 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. + +/** A collection of helper functions when dealing with rects. */ +const RectHelper = { + /** + * Finds the bottom of a rect. + * @param {!chrome.accessibilityPrivate.ScreenRect} rect + * @return {number} + */ + bottom: (rect) => rect.top + rect.height, + + /** + * Finds the right of a rect. + * @param {!chrome.accessibilityPrivate.ScreenRect} rect + * @return {number} + */ + right: (rect) => rect.left + rect.width, + + /** + * Increases the size of |outer| to entirely enclose |inner|, with |padding| + * buffer on each side. + * @param {number} padding + * @param {chrome.accessibilityPrivate.ScreenRect=} outer + * @param {chrome.accessibilityPrivate.ScreenRect=} inner + * @return {chrome.accessibilityPrivate.ScreenRect|undefined} + */ + expandToFitWithPadding: (padding, outer, inner) => { + if (!outer || !inner) + return outer; + + let newOuter = RectHelper.deepCopy(outer); + + if (newOuter.top > inner.top - padding) { + newOuter.top = inner.top - padding; + // The height should be the original bottom point less the new top point. + newOuter.height = RectHelper.bottom(outer) - newOuter.top; + } + if (newOuter.left > inner.left - padding) { + newOuter.left = inner.left - padding; + // The new width should be the original right point less the new left. + newOuter.width = RectHelper.right(outer) - newOuter.left; + } + if (RectHelper.bottom(newOuter) < RectHelper.bottom(inner) + padding) { + newOuter.height = RectHelper.bottom(inner) + padding - newOuter.top; + } + if (RectHelper.right(newOuter) < RectHelper.right(inner) + padding) { + newOuter.width = RectHelper.right(inner) + padding - newOuter.left; + } + + return newOuter; + }, + + /** + * @param {!chrome.accessibilityPrivate.ScreenRect} rect + * @return {!chrome.accessibilityPrivate.ScreenRect} + */ + deepCopy: (rect) => { + const copy = (Object.assign({}, rect)); + return /** @type {!chrome.accessibilityPrivate.ScreenRect} */ (copy); + } + +};
diff --git a/chrome/browser/resources/chromeos/switch_access/rect_helper_unittest.gtestjs b/chrome/browser/resources/chromeos/switch_access/rect_helper_unittest.gtestjs new file mode 100644 index 0000000..5346efe --- /dev/null +++ b/chrome/browser/resources/chromeos/switch_access/rect_helper_unittest.gtestjs
@@ -0,0 +1,81 @@ +// 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. + +/** + * Test fixture for rect_helper.js. + * @constructor + * @extends {testing.Test} + */ +function SwitchAccessRectHelperUnitTest() { + testing.Test.call(this); +} + +SwitchAccessRectHelperUnitTest.prototype = { + __proto__: testing.Test.prototype, + + /** @override */ + extraLibraries: [ + 'rect_helper.js', + ], + + equal: (rect1, rect2) => rect1.left === rect2.left && + rect1.top === rect2.top && + rect1.width === rect2.width && + rect1.height === rect2.height, +}; + +TEST_F('SwitchAccessRectHelperUnitTest', + 'ExpandToFitWithPadding_OuterContainedInInner', function() { + const padding = 5; + const inner = {left: 100, top: 100, width: 100, height: 100}; + + const outer = {left: 120, top: 120, width: 20, height: 20}; + let expected = {left: 95, top: 95, width: 110, height: 110}; + + assertTrue(this.equal(expected, + RectHelper.expandToFitWithPadding(padding, outer, inner))); +}); + +TEST_F('SwitchAccessRectHelperUnitTest', + 'ExpandToFitWithPadding_OuterContainsInner', function() { + const padding = 5; + const inner = {left: 100, top: 100, width: 100, height: 100}; + const outer = {left: 50, top: 50, width: 200, height: 200}; + + assertTrue(this.equal(outer, + RectHelper.expandToFitWithPadding(padding, outer, inner))); +}); + +TEST_F('SwitchAccessRectHelperUnitTest', 'ExpandToFitWithPadding_NoOverlap', + function() { + const padding = 5; + const inner = {left: 100, top: 100, width: 100, height: 100}; + const outer = {left: 10, top: 10, width: 10, height: 10}; + expected = {left: 10, top: 10, width: 195, height: 195}; + + assertTrue(this.equal(expected, + RectHelper.expandToFitWithPadding(padding, outer, inner))); +}); + +TEST_F('SwitchAccessRectHelperUnitTest', 'ExpandToFitWithPadding_Overlap', + function() { + const padding = 5; + const inner = {left: 100, top: 100, width: 100, height: 100}; + const outer = {left: 120, top: 50, width: 200, height: 200}; + expected = {left: 95, top: 50, width: 225, height: 200}; + + assertTrue(this.equal(expected, + RectHelper.expandToFitWithPadding(padding, outer, inner))); +}); + +TEST_F('SwitchAccessRectHelperUnitTest', 'ExpandToFitWithPadding_WithinPadding', + function() { + const padding = 5; + const inner = {left: 100, top: 100, width: 100, height: 100}; + const outer = {left: 97, top: 95, width: 108, height: 110}; + expected = {left: 95, top: 95, width: 110, height: 110}; + + assertTrue(this.equal(expected, + RectHelper.expandToFitWithPadding(padding, outer, inner))); +});
diff --git a/chrome/browser/resources/chromeos/switch_access/switch_access_constants.js b/chrome/browser/resources/chromeos/switch_access/switch_access_constants.js index 09571c0..1420d6d9 100644 --- a/chrome/browser/resources/chromeos/switch_access/switch_access_constants.js +++ b/chrome/browser/resources/chromeos/switch_access/switch_access_constants.js
@@ -46,6 +46,13 @@ SAConstants.Focus.SCOPE_ID = 'scope'; /** + * The buffer (in dip) between the primary focus ring and the scope focus ring. + * @type {number} + * @const + */ +SAConstants.Focus.SCOPE_BUFFER = 2; + +/** * The ID used for the focus ring around the active text input. * @type {string} * @const @@ -57,14 +64,14 @@ * @type {string} * @const */ -SAConstants.Focus.PRIMARY_COLOR = '#8ab4f8b8'; +SAConstants.Focus.PRIMARY_COLOR = '#1A73E8FF'; /** * The outer color of the focus rings. * @type {string} * @const */ -SAConstants.Focus.SECONDARY_COLOR = '#0003'; +SAConstants.Focus.SECONDARY_COLOR = '#0006'; /** * The amount of space (in px) needed to fit a focus ring around an element.
diff --git a/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js b/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js index e0a8e97..a17d2c0 100644 --- a/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js +++ b/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js
@@ -60,7 +60,7 @@ } // Check various indicators that the node is actionable. - if (role === RoleType.BUTTON) + if (role === RoleType.BUTTON || role === RoleType.SLIDER) return true; if (SwitchAccessPredicate.isTextInput(node))
diff --git a/chrome/browser/resources/chromeos/switch_access/switch_access_predicate_test.extjs b/chrome/browser/resources/chromeos/switch_access/switch_access_predicate_test.extjs index 4b0d0bfa..73ceedc 100644 --- a/chrome/browser/resources/chromeos/switch_access/switch_access_predicate_test.extjs +++ b/chrome/browser/resources/chromeos/switch_access/switch_access_predicate_test.extjs
@@ -169,6 +169,7 @@ '<a href="https://www.google.com/" aria-label="link1">link1</a>' + '<input type="text" aria-label="input1">input1</input>' + '<button>button3</button>' + + '<input type="range" aria-label="slider" value="5" min="0" max="10">' + '<div aria-label="listitem" role="listitem" onclick="2+2"></div>' + '<div aria-label="div1"><p>p1</p></div>'; this.runWithLoadedTree('data:text/html;charset=utf-8,' + treeString, @@ -202,6 +203,10 @@ const button3 = this.getNodeByName('button3'); assertTrue(SwitchAccessPredicate.isActionable(button3)); + // Sliders are generally actionable. + const slider = this.getNodeByName('slider'); + assertTrue(SwitchAccessPredicate.isActionable(slider)); + // List items with a default action of click are actionable. const listitem = this.getNodeByName('listitem'); assertTrue(SwitchAccessPredicate.isActionable(listitem));
diff --git a/chrome/browser/resources/discards/graph_doc.js b/chrome/browser/resources/discards/graph_doc.js index 1ff2ae5..1cfcfe5 100644 --- a/chrome/browser/resources/discards/graph_doc.js +++ b/chrome/browser/resources/discards/graph_doc.js
@@ -43,11 +43,25 @@ } /** + * Sets the initial x and y position of this node, also resets + * vx and vy. + * @param {number} graph_width: Width of the graph view (svg). + * @param {number} graph_height: Height of the graph view (svg). + */ + setInitialPosition(graph_width, graph_height) { + this.x = graph_width / 2; + this.y = this.targetYPosition(graph_height); + this.vx = 0; + this.vy = 0; + } + + /** * @param {number} graph_height: Height of the graph view (svg). * @return {number} */ targetYPosition(graph_height) { - return 0; + const bounds = this.allowedYRange(graph_height); + return (bounds[0] + bounds[1]) / 2; } /** @@ -63,8 +77,8 @@ * @return {!Array<number>} */ allowedYRange(graph_height) { - // By default, there is no hard constraint on the y position of a node. - return [-Infinity, Infinity]; + // By default, nodes just need to be in bounds of the graph. + return [0, graph_height]; } /** @return {number}: The strength of the repulsion force with other nodes. */ @@ -101,11 +115,6 @@ return this.page.mainFrameUrl.length > 0 ? this.page.mainFrameUrl : 'Page'; } - /** override */ - targetYPosition(graph_height) { - return kPageNodesTargetY; - } - /** @override */ targetYPositionStrength() { return 10; @@ -172,11 +181,6 @@ return `PID: ${this.process.pid.pid}`; } - /** override */ - targetYPosition(graph_height) { - return graph_height - (kProcessNodesYRange / 2); - } - /** @return {number} */ targetYPositionStrength() { return 10; @@ -193,6 +197,40 @@ } } +/** + * A force that bounds GraphNodes |allowedYRange| in Y. + * @param {number} graph_height + */ +function bounding_force(graph_height) { + /** @type {!Array<!GraphNode>} */ + let nodes = []; + /** @type {!Array<!Array>} */ + let bounds = []; + + /** @param {number} alpha */ + function force(alpha) { + const n = nodes.length; + for (let i = 0; i < n; ++i) { + const bound = bounds[i]; + const node = nodes[i]; + const yOld = node.y; + const yNew = Math.max(bound[0], Math.min(yOld, bound[1])); + if (yOld != yNew) { + node.y = yNew; + // Zero the velocity of clamped nodes. + node.vy = 0; + } + } + } + + /** @param {!Array<!GraphNode>} n */ + force.initialize = function(n) { + nodes = n; + bounds = nodes.map(node => node.allowedYRange(graph_height)); + }; + + return force; +} class Graph { /** @@ -207,10 +245,13 @@ */ this.svg_ = svg; + /** @private {boolean} */ + this.wasResized_ = false; + /** @private {number} */ - this.width_ = 100; + this.width_ = 0; /** @private {number} */ - this.height_ = 100; + this.height_ = 0; /** @private {d3.ForceSimulation} */ this.simulation_ = null; @@ -339,8 +380,7 @@ /** @private */ onTick_() { const circles = this.nodeGroup_.selectAll('circle'); - circles.attr('cx', this.getClampedXPosition_.bind(this)) - .attr('cy', this.getClampedYPosition_.bind(this)); + circles.attr('cx', d => d.x).attr('cy', d => d.y); const lines = this.linkGroup_.selectAll('line'); lines.attr('x1', d => d.source.x) @@ -363,6 +403,7 @@ node.page = page; } else { node = new PageNode(page); + node.setInitialPosition(this.width_, this.height_); } this.nodes_.set(page.id, node); @@ -382,6 +423,7 @@ node.frame = frame; } else { node = new FrameNode(frame); + node.setInitialPosition(this.width_, this.height_); } this.nodes_.set(frame.id, node); @@ -401,6 +443,7 @@ node.process = process; } else { node = new ProcessNode(process); + node.setInitialPosition(this.width_, this.height_); } this.nodes_.set(process.id, node); @@ -501,25 +544,6 @@ * @param {!d3.ForceNode} d The node to position. * @private */ - getClampedYPosition_(d) { - const range = d.allowedYRange(this.height_); - d.y = Math.max(range[0], Math.min(d.y, range[1])); - return d.y; - } - - /** - * @param {!d3.ForceNode} d The node to position. - * @private - */ - getClampedXPosition_(d) { - d.x = Math.max(10, Math.min(d.x, this.width_ - 10)); - return d.x; - } - - /** - * @param {!d3.ForceNode} d The node to position. - * @private - */ getTargetYPositionStrength_(d) { return d.targetYPositionStrength(); } @@ -553,6 +577,20 @@ .strength(this.getTargetYPositionStrength_.bind(this)); this.simulation_.force('x_pos', xForce); this.simulation_.force('y_pos', yForce); + this.simulation_.force('y_bound', bounding_force(this.height_)); + + if (!this.wasResized_) { + this.wasResized_ = true; + + // Reinitialize all node positions on first resize. + this.nodes_.forEach( + node => node.setInitialPosition(this.width_, this.height_)); + + // Allow the simulation to settle by running it for a bit. + for (let i = 0; i < 200; ++i) { + this.simulation_.tick(); + } + } this.restartSimulation_(); }
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc index b9acd99..03204f05 100644 --- a/chrome/browser/themes/theme_service.cc +++ b/chrome/browser/themes/theme_service.cc
@@ -314,6 +314,8 @@ // the "Use Classic" button. const char ThemeService::kDefaultThemeID[] = ""; +const char ThemeService::kAutogeneratedThemeID[] = "autogenerated_theme_id"; + ThemeService::ThemeService() : ready_(false), rb_(ui::ResourceBundle::GetSharedInstance()), @@ -450,14 +452,18 @@ bool ThemeService::UsingDefaultTheme() const { std::string id = GetThemeID(); - return id == ThemeService::kDefaultThemeID || - id == kDefaultThemeGalleryID; + return id == kDefaultThemeID || id == kDefaultThemeGalleryID; } bool ThemeService::UsingSystemTheme() const { return UsingDefaultTheme(); } +bool ThemeService::UsingExtensionTheme() const { + return get_theme_supplier() && get_theme_supplier()->get_theme_type() == + CustomThemeSupplier::ThemeType::EXTENSION; +} + std::string ThemeService::GetThemeID() const { return profile_->GetPrefs()->GetString(prefs::kCurrentThemeID); } @@ -553,7 +559,12 @@ } bool ThemeService::UsingAutogenerated() const { - return profile_->GetPrefs()->HasPrefPath(prefs::kAutogeneratedThemeColor); + bool autogenerated = + get_theme_supplier() && get_theme_supplier()->get_theme_type() == + CustomThemeSupplier::ThemeType::AUTOGENERATED; + DCHECK_EQ(autogenerated, + profile_->GetPrefs()->HasPrefPath(prefs::kAutogeneratedThemeColor)); + return autogenerated; } SkColor ThemeService::GetThemeColor() const { @@ -682,26 +693,28 @@ void ThemeService::InitFromPrefs() { FixInconsistentPreferencesIfNeeded(); - PrefService* prefs = profile_->GetPrefs(); std::string current_id = GetThemeID(); if (current_id == kDefaultThemeID) { - if (UsingAutogenerated()) { - BuildFromColor(GetThemeColor()); - } else if (ShouldInitWithSystemTheme()) { + if (ShouldInitWithSystemTheme()) UseSystemTheme(); - } else { + else UseDefaultTheme(); - } + set_ready(); + return; + } + if (current_id == kAutogeneratedThemeID) { + BuildFromColor(GetThemeColor()); set_ready(); return; } bool loaded_pack = false; - // If we don't have a file pack, we're updating from an old version. + PrefService* prefs = profile_->GetPrefs(); base::FilePath path = prefs->GetFilePath(prefs::kCurrentThemePackFilename); + // If we don't have a file pack, we're updating from an old version. if (!path.empty()) { path = path.Append(chrome::kThemePackFilename); SwapThemeSupplier(BrowserThemePack::BuildFromDataPack(path, current_id)); @@ -1051,4 +1064,6 @@ void ThemeService::SetThemePrefsForColor(SkColor color) { ClearThemePrefs(); profile_->GetPrefs()->SetInteger(prefs::kAutogeneratedThemeColor, color); + profile_->GetPrefs()->SetString(prefs::kCurrentThemeID, + kAutogeneratedThemeID); }
diff --git a/chrome/browser/themes/theme_service.h b/chrome/browser/themes/theme_service.h index f3bc8749..6c79c72 100644 --- a/chrome/browser/themes/theme_service.h +++ b/chrome/browser/themes/theme_service.h
@@ -59,6 +59,9 @@ // Public constants used in ThemeService and its subclasses: static const char kDefaultThemeID[]; + // Constant ID to use for all autogenerated themes. + static const char kAutogeneratedThemeID[]; + ThemeService(); ~ThemeService() override; @@ -102,6 +105,12 @@ // theme, not the "Classic" theme. virtual bool UsingSystemTheme() const; + // Whether we are using theme installed through extensions. + // |UsingExtensionTheme| and |UsingDefaultTheme| are not mutually exclusive as + // default theme can be installed through extensions. Cannot be called before + // theme is loaded. + virtual bool UsingExtensionTheme() const; + // Gets the id of the last installed theme. (The theme may have been further // locally customized.) virtual std::string GetThemeID() const; @@ -136,6 +145,8 @@ // Builds a theme from a given |color|. virtual void BuildFromColor(SkColor color); + // Whether using autogenerated theme. Cannot be called before theme is + // loaded. virtual bool UsingAutogenerated() const; virtual SkColor GetThemeColor() const;
diff --git a/chrome/browser/themes/theme_service_factory.cc b/chrome/browser/themes/theme_service_factory.cc index 6ea6811..feb337b 100644 --- a/chrome/browser/themes/theme_service_factory.cc +++ b/chrome/browser/themes/theme_service_factory.cc
@@ -32,12 +32,13 @@ // static const extensions::Extension* ThemeServiceFactory::GetThemeForProfile( Profile* profile) { - std::string id = GetForProfile(profile)->GetThemeID(); - if (id == ThemeService::kDefaultThemeID) - return NULL; + ThemeService* theme_service = GetForProfile(profile); + if (!theme_service->UsingExtensionTheme()) + return nullptr; - return extensions::ExtensionRegistry::Get( - profile)->enabled_extensions().GetByID(id); + return extensions::ExtensionRegistry::Get(profile) + ->enabled_extensions() + .GetByID(theme_service->GetThemeID()); } // static
diff --git a/chrome/browser/themes/theme_service_unittest.cc b/chrome/browser/themes/theme_service_unittest.cc index dbd6e01..de97d59 100644 --- a/chrome/browser/themes/theme_service_unittest.cc +++ b/chrome/browser/themes/theme_service_unittest.cc
@@ -146,6 +146,7 @@ const std::string& extension_id = LoadUnpackedMinimalThemeAt(temp_dir.GetPath()); EXPECT_FALSE(theme_service->UsingDefaultTheme()); + EXPECT_TRUE(theme_service->UsingExtensionTheme()); EXPECT_EQ(extension_id, theme_service->GetThemeID()); // Now uninstall the extension, should revert to the default theme. @@ -153,6 +154,7 @@ extensions::UNINSTALL_REASON_FOR_TESTING, NULL); EXPECT_TRUE(theme_service->UsingDefaultTheme()); + EXPECT_FALSE(theme_service->UsingExtensionTheme()); } // Test that a theme extension is disabled when not in use. A theme may be @@ -428,8 +430,10 @@ ThemeService* theme_service = ThemeServiceFactory::GetForProfile(profile_.get()); theme_service->UseDefaultTheme(); + EXPECT_TRUE(theme_service->UsingDefaultTheme()); EXPECT_FALSE(theme_service->UsingAutogenerated()); theme_service->BuildFromColor(SkColorSetRGB(100, 100, 100)); + EXPECT_FALSE(theme_service->UsingDefaultTheme()); EXPECT_TRUE(theme_service->UsingAutogenerated()); // Set theme from data pack and then override it with theme from color. @@ -438,14 +442,16 @@ const std::string& extension1_id = LoadUnpackedMinimalThemeAt(temp_dir1.GetPath()); EXPECT_EQ(extension1_id, theme_service->GetThemeID()); + EXPECT_FALSE(theme_service->UsingDefaultTheme()); EXPECT_FALSE(theme_service->UsingAutogenerated()); base::FilePath path = profile_->GetPrefs()->GetFilePath(prefs::kCurrentThemePackFilename); EXPECT_FALSE(path.empty()); theme_service->BuildFromColor(SkColorSetRGB(100, 100, 100)); + EXPECT_FALSE(theme_service->UsingDefaultTheme()); EXPECT_TRUE(theme_service->UsingAutogenerated()); - EXPECT_EQ(ThemeService::kDefaultThemeID, theme_service->GetThemeID()); + EXPECT_EQ(ThemeService::kAutogeneratedThemeID, theme_service->GetThemeID()); path = profile_->GetPrefs()->GetFilePath(prefs::kCurrentThemePackFilename); EXPECT_TRUE(path.empty()); }
diff --git a/chrome/browser/themes/theme_syncable_service.cc b/chrome/browser/themes/theme_syncable_service.cc index ea065fe..cf65501 100644 --- a/chrome/browser/themes/theme_syncable_service.cc +++ b/chrome/browser/themes/theme_syncable_service.cc
@@ -265,28 +265,33 @@ bool ThemeSyncableService::GetThemeSpecificsFromCurrentTheme( sync_pb::ThemeSpecifics* theme_specifics) const { - const extensions::Extension* current_theme = - theme_service_->UsingDefaultTheme() ? - NULL : - extensions::ExtensionSystem::Get(profile_)->extension_service()-> - GetExtensionById(theme_service_->GetThemeID(), false); - if (current_theme && !extensions::sync_helper::IsSyncable(current_theme)) { - DVLOG(1) << "Ignoring non-syncable extension: " << current_theme->id(); + const extensions::Extension* current_extension = + theme_service_->UsingExtensionTheme() && + !theme_service_->UsingDefaultTheme() + ? extensions::ExtensionSystem::Get(profile_) + ->extension_service() + ->GetExtensionById(theme_service_->GetThemeID(), false) + : nullptr; + if (current_extension && + !extensions::sync_helper::IsSyncable(current_extension)) { + DVLOG(1) << "Ignoring non-syncable extension: " << current_extension->id(); return false; } theme_specifics->Clear(); theme_specifics->set_use_custom_theme(false); - if (current_theme) { + if (current_extension) { // Using custom theme and it's an extension. - DCHECK(current_theme->is_theme()); + DCHECK(current_extension->is_theme()); theme_specifics->set_use_custom_theme(true); - theme_specifics->set_custom_theme_name(current_theme->name()); - theme_specifics->set_custom_theme_id(current_theme->id()); + theme_specifics->set_custom_theme_name(current_extension->name()); + theme_specifics->set_custom_theme_id(current_extension->id()); theme_specifics->set_custom_theme_update_url( - extensions::ManifestURL::GetUpdateURL(current_theme).spec()); - } else if (theme_service_->UsingAutogenerated()) { + extensions::ManifestURL::GetUpdateURL(current_extension).spec()); + } + + if (theme_service_->UsingAutogenerated()) { // Using custom theme and it's autogenerated from color. theme_specifics->set_use_custom_theme(false); theme_specifics->mutable_autogenerated_theme()->set_color(
diff --git a/chrome/browser/themes/theme_syncable_service_unittest.cc b/chrome/browser/themes/theme_syncable_service_unittest.cc index 3bcb144..74543546f 100644 --- a/chrome/browser/themes/theme_syncable_service_unittest.cc +++ b/chrome/browser/themes/theme_syncable_service_unittest.cc
@@ -110,6 +110,8 @@ bool UsingAutogenerated() const override { return color_ != 0; } + bool UsingExtensionTheme() const override { return !!theme_extension_; } + string GetThemeID() const override { if (theme_extension_.get()) return theme_extension_->id();
diff --git a/chrome/browser/ui/views/global_error_bubble_view_unittest.cc b/chrome/browser/ui/views/global_error_bubble_view_unittest.cc index d7af629..b1030197 100644 --- a/chrome/browser/ui/views/global_error_bubble_view_unittest.cc +++ b/chrome/browser/ui/views/global_error_bubble_view_unittest.cc
@@ -70,8 +70,8 @@ button_(nullptr, base::string16()), view_(std::make_unique<GlobalErrorBubbleView>( &arg_view_, - gfx::Rect(anchor_point_, gfx::Size()), - arrow_, + gfx::Rect(gfx::Point(), gfx::Size()), + views::BubbleBorder::NONE, nullptr, mock_global_error_with_standard_bubble_->AsWeakPtr())) {} @@ -80,8 +80,6 @@ std::unique_ptr<StrictMock<MockGlobalErrorWithStandardBubble>> mock_global_error_with_standard_bubble_; views::View arg_view_; - const gfx::Point anchor_point_; - views::BubbleBorder::Arrow arrow_; views::LabelButton button_; std::unique_ptr<GlobalErrorBubbleView> view_;
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc index 73bdc3c..efa1147 100644 --- a/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_browsertest.cc
@@ -26,6 +26,7 @@ #include "components/content_settings/core/common/content_settings_types.h" #include "components/safe_browsing/features.h" #include "components/safe_browsing/password_protection/metrics_util.h" +#include "components/strings/grit/components_strings.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" @@ -40,16 +41,11 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/accessibility/ax_action_data.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/events/event_constants.h" namespace { -const auto kPageInfoNotSecureTitle = - base::ASCIIToUTF16("Your connection to this site is not secure"); -const auto kPageInfoMixedTitle = - base::ASCIIToUTF16("Your connection to this site is not fully secure"); -const auto kPageInfoSecureTitle = base::ASCIIToUTF16("Connection is secure"); - class ClickEvent : public ui::Event { public: ClickEvent() : ui::Event(ui::ET_UNKNOWN, base::TimeTicks(), 0) {} @@ -627,7 +623,7 @@ PageInfoBubbleView::GetShownBubbleType()); } -// Ensure that changes to security state are reflected in open PageInfo bubble. +// Ensure changes to security state are reflected in an open PageInfo bubble. IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, UpdatesOnSecurityStateChange) { net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); @@ -642,8 +638,10 @@ views::BubbleDialogDelegateView* page_info = PageInfoBubbleView::GetPageInfoBubble(); - EXPECT_EQ(page_info->GetWindowTitle(), kPageInfoSecureTitle); + EXPECT_EQ(page_info->GetWindowTitle(), + l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURE_SUMMARY)); ExecuteJavaScriptForTests("load_mixed();"); - EXPECT_EQ(page_info->GetWindowTitle(), kPageInfoMixedTitle); + EXPECT_EQ(page_info->GetWindowTitle(), + l10n_util::GetStringUTF16(IDS_PAGE_INFO_MIXED_CONTENT_SUMMARY)); }
diff --git a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc index d32ab86..75c7e63 100644 --- a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc +++ b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
@@ -273,6 +273,11 @@ return button; } +bool CvcUnmaskViewController::ShouldShowSecondaryButton() { + // Do not show the "Cancel Payment" button. + return false; +} + void CvcUnmaskViewController::ButtonPressed(views::Button* sender, const ui::Event& event) { if (!dialog()->IsInteractive())
diff --git a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.h b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.h index af2c08f..2530fb0 100644 --- a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.h +++ b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.h
@@ -70,6 +70,7 @@ base::string16 GetSheetTitle() override; void FillContentView(views::View* content_view) override; std::unique_ptr<views::Button> CreatePrimaryButton() override; + bool ShouldShowSecondaryButton() override; void ButtonPressed(views::Button* sender, const ui::Event& event) override; private:
diff --git a/chrome/browser/ui/views/payments/editor_view_controller.cc b/chrome/browser/ui/views/payments/editor_view_controller.cc index 5730a15c..a90a3bba 100644 --- a/chrome/browser/ui/views/payments/editor_view_controller.cc +++ b/chrome/browser/ui/views/payments/editor_view_controller.cc
@@ -138,6 +138,11 @@ return button; } +bool EditorViewController::ShouldShowSecondaryButton() { + // Do not show the "Cancel Payment" button. + return false; +} + void EditorViewController::FillContentView(views::View* content_view) { auto layout = std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical); layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
diff --git a/chrome/browser/ui/views/payments/editor_view_controller.h b/chrome/browser/ui/views/payments/editor_view_controller.h index f3aba89..0bcb91a 100644 --- a/chrome/browser/ui/views/payments/editor_view_controller.h +++ b/chrome/browser/ui/views/payments/editor_view_controller.h
@@ -149,6 +149,7 @@ // PaymentRequestSheetController; std::unique_ptr<views::Button> CreatePrimaryButton() override; + bool ShouldShowSecondaryButton() override; void FillContentView(views::View* content_view) override; // views::ComboboxListener:
diff --git a/chrome/browser/ui/views/payments/profile_list_view_controller.cc b/chrome/browser/ui/views/payments/profile_list_view_controller.cc index 59475b1..86aa87f 100644 --- a/chrome/browser/ui/views/payments/profile_list_view_controller.cc +++ b/chrome/browser/ui/views/payments/profile_list_view_controller.cc
@@ -188,16 +188,16 @@ return GetShippingAddressSectionString(spec()->shipping_type()); } - int GetExtraFooterViewButtonTextId() override { - return IDS_PAYMENTS_ADD_ADDRESS; + base::string16 GetSecondaryButtonLabel() override { + return l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_ADDRESS); } - int GetExtraFooterViewButtonTag() override { + int GetSecondaryButtonTag() override { return static_cast<int>( ProfileListViewControllerTags::ADD_SHIPPING_ADDRESS_BUTTON); } - int GetExtraFooterViewButtonViewId() override { + int GetSecondaryButtonId() override { return static_cast<int>(DialogViewID::PAYMENT_METHOD_ADD_SHIPPING_BUTTON); } @@ -288,15 +288,15 @@ IDS_PAYMENT_REQUEST_CONTACT_INFO_SECTION_NAME); } - int GetExtraFooterViewButtonTextId() override { - return IDS_PAYMENTS_ADD_CONTACT; + base::string16 GetSecondaryButtonLabel() override { + return l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_CONTACT); } - int GetExtraFooterViewButtonTag() override { + int GetSecondaryButtonTag() override { return static_cast<int>(ProfileListViewControllerTags::ADD_CONTACT_BUTTON); } - int GetExtraFooterViewButtonViewId() override { + int GetSecondaryButtonId() override { return static_cast<int>(DialogViewID::PAYMENT_METHOD_ADD_CONTACT_BUTTON); } @@ -366,27 +366,9 @@ content_view->AddChildView(list_view.release()); } -std::unique_ptr<views::View> -ProfileListViewController::CreateExtraFooterView() { - auto extra_view = std::make_unique<views::View>(); - - extra_view->SetLayoutManager(std::make_unique<views::BoxLayout>( - views::BoxLayout::kHorizontal, gfx::Insets(), - kPaymentRequestButtonSpacing)); - - views::LabelButton* button = views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16(GetExtraFooterViewButtonTextId())); - button->set_tag(GetExtraFooterViewButtonTag()); - button->set_id(GetExtraFooterViewButtonViewId()); - button->SetFocusBehavior(views::View::FocusBehavior::ALWAYS); - extra_view->AddChildView(button); - - return extra_view; -} - void ProfileListViewController::ButtonPressed(views::Button* sender, const ui::Event& event) { - if (sender->tag() == GetExtraFooterViewButtonTag()) + if (sender->tag() == GetSecondaryButtonTag()) ShowEditor(nullptr); else PaymentRequestSheetController::ButtonPressed(sender, event);
diff --git a/chrome/browser/ui/views/payments/profile_list_view_controller.h b/chrome/browser/ui/views/payments/profile_list_view_controller.h index fe4e44a..1ced64aa 100644 --- a/chrome/browser/ui/views/payments/profile_list_view_controller.h +++ b/chrome/browser/ui/views/payments/profile_list_view_controller.h
@@ -49,7 +49,6 @@ PaymentRequestDialogView* dialog); // PaymentRequestSheetController: - std::unique_ptr<views::View> CreateExtraFooterView() override; void ButtonPressed(views::Button* sender, const ui::Event& event) override; // Returns a representation of the given profile appropriate for display @@ -93,15 +92,6 @@ // PaymentRequestSheetController: void FillContentView(views::View* content_view) override; - // Settings and events related to the button in the extra view area of the - // footer. - // +------------------------------------------------------------+ - // | EXTRA VIEW | PAY(primary button)| CANCEL(secondary button) | - // +------------------------------------------------------------+ - virtual int GetExtraFooterViewButtonTextId() = 0; - virtual int GetExtraFooterViewButtonTag() = 0; - virtual int GetExtraFooterViewButtonViewId() = 0; - private: std::unique_ptr<views::Button> CreateRow(autofill::AutofillProfile* profile); PaymentRequestItemList list_;
diff --git a/chrome/browser/ui/views/payments/shipping_option_view_controller.cc b/chrome/browser/ui/views/payments/shipping_option_view_controller.cc index 3ca2b47..3faccb6 100644 --- a/chrome/browser/ui/views/payments/shipping_option_view_controller.cc +++ b/chrome/browser/ui/views/payments/shipping_option_view_controller.cc
@@ -121,4 +121,9 @@ return nullptr; } +bool ShippingOptionViewController::ShouldShowSecondaryButton() { + // Do not show the "Cancel Payment" button. + return false; +} + } // namespace payments
diff --git a/chrome/browser/ui/views/payments/shipping_option_view_controller.h b/chrome/browser/ui/views/payments/shipping_option_view_controller.h index 2cfe104..5c7d09e 100644 --- a/chrome/browser/ui/views/payments/shipping_option_view_controller.h +++ b/chrome/browser/ui/views/payments/shipping_option_view_controller.h
@@ -30,6 +30,7 @@ base::string16 GetSheetTitle() override; void FillContentView(views::View* content_view) override; std::unique_ptr<views::View> CreateExtraFooterView() override; + bool ShouldShowSecondaryButton() override; PaymentRequestItemList shipping_option_list_;
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc index 270ec2c..0e44541 100644 --- a/chrome/browser/ui/webauthn/sheet_models.cc +++ b/chrome/browser/ui/webauthn/sheet_models.cc
@@ -1028,11 +1028,11 @@ } bool AuthenticatorSelectAccountSheetModel::IsAcceptButtonVisible() const { - return true; + return false; } bool AuthenticatorSelectAccountSheetModel::IsAcceptButtonEnabled() const { - return true; + return false; } base::string16 AuthenticatorSelectAccountSheetModel::GetAcceptButtonLabel()
diff --git a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc index caa66b5..00328b9e 100644 --- a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc +++ b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.cc
@@ -28,6 +28,7 @@ #include "chrome/browser/chromeos/arc/tracing/arc_tracing_graphics_model.h" #include "chrome/browser/chromeos/arc/tracing/arc_tracing_model.h" #include "chrome/browser/chromeos/file_manager/path_util.h" +#include "chrome/browser/platform_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h" #include "components/arc/arc_prefs.h" @@ -37,6 +38,7 @@ #include "components/exo/wm_helper.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/tracing_controller.h" +#include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" #include "ui/aura/client/aura_constants.h" #include "ui/base/ui_base_features.h" @@ -280,8 +282,10 @@ void ArcGraphicsTracingHandler::OnJankDetected(const base::Time& timestamp) { VLOG(1) << "Jank detected " << timestamp; - if (tracing_active_ && stop_on_jank_) + if (tracing_active_ && stop_on_jank_) { StopTracing(); + Activate(); + } } void ArcGraphicsTracingHandler::OnWindowPropertyChanged(aura::Window* window, @@ -305,10 +309,12 @@ !event->IsControlDown() || !event->IsShiftDown()) { return; } - if (tracing_active_) + if (tracing_active_) { StopTracing(); - else + Activate(); + } else { StartTracing(); + } } void ArcGraphicsTracingHandler::UpdateActiveArcWindowInfo() { @@ -350,6 +356,17 @@ arc_active_window_ = nullptr; } +void ArcGraphicsTracingHandler::Activate() { + aura::Window* const window = + web_ui()->GetWebContents()->GetTopLevelNativeWindow(); + if (!window) { + LOG(ERROR) << "Failed to activate, no top level window."; + return; + } + + platform_util::ActivateWindow(window); +} + void ArcGraphicsTracingHandler::StartTracing() { SetStatus("Collecting samples...");
diff --git a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h index d986704..dc37db9 100644 --- a/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h +++ b/chrome/browser/ui/webui/chromeos/arc_graphics_tracing/arc_graphics_tracing_handler.h
@@ -59,6 +59,7 @@ void OnKeyEvent(ui::KeyEvent* event) override; private: + void Activate(); void StartTracing(); void StopTracing(); void SetStatus(const std::string& status);
diff --git a/chrome/browser/vr/service/xr_device_impl.cc b/chrome/browser/vr/service/xr_device_impl.cc index 9dec13d..3d0be01 100644 --- a/chrome/browser/vr/service/xr_device_impl.cc +++ b/chrome/browser/vr/service/xr_device_impl.cc
@@ -44,7 +44,6 @@ device::mojom::XRRuntimeSessionOptionsPtr runtime_options = device::mojom::XRRuntimeSessionOptions::New(); runtime_options->immersive = options->immersive; - runtime_options->has_user_activation = options->has_user_activation; runtime_options->environment_integration = options->environment_integration; runtime_options->use_legacy_webvr_render_path = options->use_legacy_webvr_render_path;
diff --git a/chrome/browser/vr/service/xr_runtime_manager.cc b/chrome/browser/vr/service/xr_runtime_manager.cc index 08fd87b..8729fea 100644 --- a/chrome/browser/vr/service/xr_runtime_manager.cc +++ b/chrome/browser/vr/service/xr_runtime_manager.cc
@@ -172,8 +172,8 @@ // AR requested. if (options->environment_integration) { - if (options->immersive) { - // No support for immersive AR. + if (!options->immersive) { + DVLOG(1) << __func__ << ": non-immersive AR mode is unsupported"; return nullptr; } // Return the ARCore device. @@ -227,6 +227,7 @@ device::mojom::VRDisplayInfoPtr XRRuntimeManager::GetCurrentVRDisplayInfo( XRDeviceImpl* device) { + DVLOG(1) << __func__; // Get an immersive_runtime device if there is one. auto* immersive_runtime = GetImmersiveRuntime(); if (immersive_runtime) {
diff --git a/chrome/browser/vr/service/xr_runtime_manager_unittest.cc b/chrome/browser/vr/service/xr_runtime_manager_unittest.cc index 628f83f..a1afa975 100644 --- a/chrome/browser/vr/service/xr_runtime_manager_unittest.cc +++ b/chrome/browser/vr/service/xr_runtime_manager_unittest.cc
@@ -142,6 +142,7 @@ device::mojom::XRSessionOptions options = {}; options.environment_integration = true; + options.immersive = true; EXPECT_TRUE(DeviceManager()->GetRuntimeForOptions(&options)); Provider()->RemoveDevice(device->GetId()); EXPECT_TRUE(!DeviceManager()->GetRuntimeForOptions(&options));
diff --git a/chrome/credential_provider/gaiacp/associated_user_validator.cc b/chrome/credential_provider/gaiacp/associated_user_validator.cc index dfb8c70..e51e638 100644 --- a/chrome/credential_provider/gaiacp/associated_user_validator.cc +++ b/chrome/credential_provider/gaiacp/associated_user_validator.cc
@@ -518,10 +518,20 @@ return block_deny_access_update_ > 0; } -bool AssociatedUserValidator::IsUserAccessBlocked( +bool AssociatedUserValidator::IsUserAccessBlockedForTesting( const base::string16& sid) const { base::AutoLock locker(validator_lock_); return locked_user_sids_.find(sid) != locked_user_sids_.end(); } +void AssociatedUserValidator::ForceRefreshTokenHandlesForTesting() { + base::AutoLock locker(validator_lock_); + for (const auto& user_info : user_to_token_handle_info_) { + // Make the last update time outside the validity lifetime of the token + // handle. + user_info.second->last_update = + base::Time::Now() - kTokenHandleValidityLifetime; + } +} + } // namespace credential_provider
diff --git a/chrome/credential_provider/gaiacp/associated_user_validator.h b/chrome/credential_provider/gaiacp/associated_user_validator.h index 0f5cb9d..da28902 100644 --- a/chrome/credential_provider/gaiacp/associated_user_validator.h +++ b/chrome/credential_provider/gaiacp/associated_user_validator.h
@@ -139,7 +139,11 @@ // Returns whether the user should be locked out of sign in (only used in // tests). - bool IsUserAccessBlocked(const base::string16& sid) const; + bool IsUserAccessBlockedForTesting(const base::string16& sid) const; + + // Forces a refresh of all token handles the next time they are queried. + // This function should only be called in tests. + void ForceRefreshTokenHandlesForTesting(); private: bool IsTokenHandleValidForUserInternal(const base::string16& sid);
diff --git a/chrome/credential_provider/gaiacp/associated_user_validator_unittests.cc b/chrome/credential_provider/gaiacp/associated_user_validator_unittests.cc index 6f12e4c..96dfd57 100644 --- a/chrome/credential_provider/gaiacp/associated_user_validator_unittests.cc +++ b/chrome/credential_provider/gaiacp/associated_user_validator_unittests.cc
@@ -281,16 +281,16 @@ &validator); EXPECT_FALSE( validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); - EXPECT_FALSE(validator.IsUserAccessBlocked(OLE2W(sid))); + EXPECT_FALSE(validator.IsUserAccessBlockedForTesting(OLE2W(sid))); } EXPECT_FALSE( validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); - EXPECT_FALSE(validator.IsUserAccessBlocked(OLE2W(sid))); + EXPECT_FALSE(validator.IsUserAccessBlockedForTesting(OLE2W(sid))); } // Unblock deny access. User should not be blocked. EXPECT_TRUE(validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); - EXPECT_TRUE(validator.IsUserAccessBlocked(OLE2W(sid))); + EXPECT_TRUE(validator.IsUserAccessBlockedForTesting(OLE2W(sid))); EXPECT_EQ(1u, fake_http_url_fetcher_factory()->requests_created()); } @@ -360,12 +360,13 @@ EXPECT_EQ(!internet_available || (!mdm_url_set && token_handle_valid) || (mdm_url_set && mdm_enrolled && token_handle_valid), validator.IsTokenHandleValidForUser(OLE2W(sid))); - EXPECT_EQ(should_user_be_blocked, validator.IsUserAccessBlocked(OLE2W(sid))); + EXPECT_EQ(should_user_be_blocked, + validator.IsUserAccessBlockedForTesting(OLE2W(sid))); // Unlock the user. validator.AllowSigninForUsersWithInvalidTokenHandles(); - EXPECT_EQ(false, validator.IsUserAccessBlocked(OLE2W(sid))); + EXPECT_EQ(false, validator.IsUserAccessBlockedForTesting(OLE2W(sid))); EXPECT_NE(S_OK, GetMachineRegDWORD(kWinlogonUserListRegKey, username, ®_value)); }
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.cc b/chrome/credential_provider/gaiacp/gaia_credential_base.cc index 66c6b2f..fee4613 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_base.cc +++ b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
@@ -1692,6 +1692,11 @@ << "'. Maximum attempts reached."; *error_text = AllocErrorString(IDS_INTERNAL_ERROR_BASE); return hr; + } else if (FAILED(hr)) { + LOGFN(ERROR) << "Failed to create user '" << found_domain << "\\" + << found_username << "'. hr=" << putHR(hr); + *error_text = AllocErrorString(IDS_INTERNAL_ERROR_BASE); + return hr; } *domain = ::SysAllocString(found_domain); @@ -1793,6 +1798,12 @@ USES_CONVERSION; LOGFN(INFO); + // Provider may be unset if the GLS process ended as a result of a kill + // request coming from Terminate() which would release the |provider_| + // reference. + if (!provider_) + return S_OK; + result_status_ = status; // If the user cancelled out of the logon, the process may be already
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.h b/chrome/credential_provider/gaiacp/gaia_credential_base.h index d4848a4..a79a41c 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_base.h +++ b/chrome/credential_provider/gaiacp/gaia_credential_base.h
@@ -87,9 +87,6 @@ const base::Optional<base::Value>& get_authentication_results() const { return authentication_results_; } - void set_current_windows_password(BSTR password) { - current_windows_password_ = password; - } // Returns true if the current credentials stored in |username_| and // |password_| are valid and should succeed a local Windows logon. This
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc index 0ede0ea..6ea8685 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc +++ b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
@@ -16,72 +16,27 @@ namespace testing { -// This class is used to implement a test credential based off only -// CGaiaCredentialBase which requires certain functions be implemented. -class ATL_NO_VTABLE CTestCredentialForBase - : public CTestCredentialBase<CGaiaCredentialBase>, - public CComObjectRootEx<CComMultiThreadModel> { - public: - DECLARE_NO_REGISTRY() - - CTestCredentialForBase(); - ~CTestCredentialForBase(); - - HRESULT FinalConstruct() { return S_OK; } - void FinalRelease() {} - - private: - BEGIN_COM_MAP(CTestCredentialForBase) - COM_INTERFACE_ENTRY(IGaiaCredential) - COM_INTERFACE_ENTRY(ICredentialProviderCredential) - COM_INTERFACE_ENTRY(ITestCredential) - END_COM_MAP() - - DECLARE_PROTECT_FINAL_CONSTRUCT() -}; - -CTestCredentialForBase::CTestCredentialForBase() = default; - -CTestCredentialForBase::~CTestCredentialForBase() = default; - -namespace { - -HRESULT CreateCredential(ICredentialProviderCredential** credential) { - return CComCreator<CComObject<CTestCredentialForBase>>::CreateInstance( - nullptr, IID_ICredentialProviderCredential, - reinterpret_cast<void**>(credential)); -} - -HRESULT CreateCredentialWithProvider( - IGaiaCredentialProvider* provider, - IGaiaCredential** gaia_credential, - ICredentialProviderCredential** credential) { - HRESULT hr = CreateCredential(credential); - if (SUCCEEDED(hr)) { - hr = (*credential) - ->QueryInterface(IID_IGaiaCredential, - reinterpret_cast<void**>(gaia_credential)); - if (SUCCEEDED(hr)) - hr = (*gaia_credential)->Initialize(provider); - } - return hr; -} - -} // namespace - class GcpGaiaCredentialBaseTest : public GlsRunnerTestBase {}; TEST_F(GcpGaiaCredentialBaseTest, Advise) { + // Create provider with credentials. This should Advise the credential. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredential(&cred)); - ASSERT_EQ(S_OK, cred->Advise(nullptr)); - ASSERT_EQ(S_OK, cred->UnAdvise()); + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); + + // Release ref count so the credential can be deleted by the call to + // ReleaseProvider. + cred.Release(); + + // Release the provider. This should unadvise the credential. + ASSERT_EQ(S_OK, ReleaseProvider()); } TEST_F(GcpGaiaCredentialBaseTest, SetSelected) { + // Create provider and credential only. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredential(&cred)); + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); // A credential that has not attempted to sign in a user yet should return // false for |auto_login|. @@ -91,65 +46,38 @@ } TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_NoInternet) { - FakeGaiaCredentialProvider provider; FakeInternetAvailabilityChecker internet_checker( FakeInternetAvailabilityChecker::kHicForceNo); - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); - CComPtr<ITestCredential> test; - ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcess(cred, /*succeeds=*/false)); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); + ASSERT_EQ(S_OK, StartLogonProcess(/*succeeds=*/false)); } TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_Start) { - FakeGaiaCredentialProvider provider; - - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); - CComPtr<ITestCredential> test; - ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); } TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_Finish) { - FakeGaiaCredentialProvider provider; - - // Start logon. - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); - - // Now finish the logon. - CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; - CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; - wchar_t* status_text; - CREDENTIAL_PROVIDER_STATUS_ICON status_icon; - ASSERT_EQ(S_OK, - cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); - EXPECT_EQ(nullptr, status_text); - EXPECT_EQ(CPSI_SUCCESS, status_icon); - EXPECT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr); - EXPECT_LT(0u, cpcs.cbSerialization); - EXPECT_NE(nullptr, cpcs.rgbSerialization); + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); - // State was not reset. - EXPECT_TRUE(test->AreCredentialsValid()); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); + + EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); // Make sure a "foo" user was created. PSID sid; @@ -157,43 +85,26 @@ OSUserManager::GetLocalDomain().c_str(), kDefaultUsername, &sid)); ::LocalFree(sid); - EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); - - wchar_t* report_status_text = nullptr; - CREDENTIAL_PROVIDER_STATUS_ICON report_icon; - EXPECT_EQ(S_OK, cred->ReportResult(0, 0, &report_status_text, &report_icon)); - // State was reset. - EXPECT_FALSE(test->AreCredentialsValid()); - - EXPECT_EQ(S_OK, gaia_cred->Terminate()); // New user should be created. EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); } TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_Abort) { - FakeGaiaCredentialProvider provider; - - // Start logon. - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); ASSERT_EQ(S_OK, test->SetDefaultExitCode(kUiecAbort)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); - // Nothing should have been propagated to the provider, but also no - // error should be reported. - EXPECT_EQ(0u, provider.username().Length()); - EXPECT_EQ(0u, provider.password().Length()); - EXPECT_EQ(0u, provider.sid().Length()); - EXPECT_EQ(FALSE, provider.credentials_changed_fired()); - EXPECT_EQ(nullptr, test->GetErrorText()); - - EXPECT_EQ(S_OK, gaia_cred->Terminate()); + // Logon process should not signal credentials change or raise an error + // message. + ASSERT_EQ(S_OK, FinishLogonProcess(false, false, 0)); } TEST_F(GcpGaiaCredentialBaseTest, @@ -207,57 +118,30 @@ base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &first_sid)); ASSERT_EQ(2ul, fake_os_user_manager()->GetUserCount()); - FakeGaiaCredentialProvider provider; - - // Start logon. - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); - - // Now finish the logon. - CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; - CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; - wchar_t* status_text; - CREDENTIAL_PROVIDER_STATUS_ICON status_icon; - ASSERT_EQ(S_OK, - cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); - EXPECT_EQ(nullptr, status_text); - EXPECT_EQ(CPSI_SUCCESS, status_icon); - EXPECT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr); - EXPECT_LT(0u, cpcs.cbSerialization); - EXPECT_NE(nullptr, cpcs.rgbSerialization); - - // State was not reset. - EXPECT_TRUE(test->AreCredentialsValid()); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); // User should have been associated. EXPECT_EQ(test->GetFinalUsername(), username); // Email should be the same as the default one. EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); - wchar_t* report_status_text = nullptr; - CREDENTIAL_PROVIDER_STATUS_ICON report_icon; - EXPECT_EQ(S_OK, cred->ReportResult(0, 0, &report_status_text, &report_icon)); - // State was reset. - EXPECT_FALSE(test->AreCredentialsValid()); - - EXPECT_EQ(S_OK, gaia_cred->Terminate()); - // No new user should be created. EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); } TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_MultipleCalls) { - FakeGaiaCredentialProvider provider; - - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); @@ -270,7 +154,7 @@ ASSERT_EQ(S_OK, test->SetStartGlsEventName(kStartGlsEventName)); base::WaitableEvent start_event(std::move(start_event_handle)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcess(cred, /*succeeds=*/true)); + ASSERT_EQ(S_OK, StartLogonProcess(/*succeeds=*/true)); // Calling GetSerialization again while the credential is waiting for the // logon process should yield CPGSR_NO_CREDENTIAL_NOT_FINISHED as a @@ -288,14 +172,12 @@ // Signal that the gls process can finish. start_event.Signal(); - ASSERT_EQ(S_OK, run_helper()->WaitForLogonProcess(cred)); - ASSERT_EQ(S_OK, gaia_cred->Terminate()); + ASSERT_EQ(S_OK, WaitForLogonProcess()); } TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_PasswordChangedForAssociatedUser) { USES_CONVERSION; - FakeGaiaCredentialProvider provider; // Create a fake user for which the windows password does not match the gaia // password supplied by the test gls process. @@ -306,15 +188,15 @@ L"foo", (BSTR)windows_password, L"Full Name", L"comment", base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); - // Start logon. - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); EXPECT_TRUE(test->CanAttemptWindowsLogon()); EXPECT_EQ(S_OK, test->IsWindowsPasswordValidForStoredUser()); @@ -336,14 +218,14 @@ // Set an invalid password and try to get serialization again. Credentials // should still be valid but serialization is not complete. CComBSTR invalid_windows_password = L"a"; - test->SetWindowsPassword(invalid_windows_password); + cred->SetStringValue(FID_CURRENT_PASSWORD_FIELD, invalid_windows_password); EXPECT_EQ(nullptr, status_text); ASSERT_EQ(S_OK, cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); EXPECT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr); // Update the Windows password to be the real password created for the user. - test->SetWindowsPassword(windows_password); + cred->SetStringValue(FID_CURRENT_PASSWORD_FIELD, windows_password); // Sign in information should still be available. EXPECT_TRUE(test->GetFinalEmail().length()); @@ -351,30 +233,13 @@ EXPECT_TRUE(test->CanAttemptWindowsLogon()); EXPECT_EQ(S_FALSE, test->IsWindowsPasswordValidForStoredUser()); - // Serialization should complete without any errors. - ASSERT_EQ(S_OK, - cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); - EXPECT_EQ(nullptr, status_text); - EXPECT_EQ(CPSI_SUCCESS, status_icon); - EXPECT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr); - EXPECT_LT(0u, cpcs.cbSerialization); - EXPECT_NE(nullptr, cpcs.rgbSerialization); - - // State was not reset. - EXPECT_TRUE(test->AreCredentialsValid()); - wchar_t* report_status_text = nullptr; - CREDENTIAL_PROVIDER_STATUS_ICON report_icon; - EXPECT_EQ(S_OK, cred->ReportResult(0, 0, &report_status_text, &report_icon)); - // State was reset. - EXPECT_FALSE(test->AreCredentialsValid()); - - EXPECT_EQ(S_OK, gaia_cred->Terminate()); + // Finish logon successfully but with no credential changed event. + ASSERT_EQ(S_OK, FinishLogonProcess(true, false, 0)); } TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_ForgotPasswordForAssociatedUser) { USES_CONVERSION; - FakeGaiaCredentialProvider provider; // Create a fake user for which the windows password does not match the gaia // password supplied by the test gls process. @@ -385,15 +250,15 @@ L"foo", (BSTR)windows_password, L"Full Name", L"comment", base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); - // Start logon. - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); EXPECT_TRUE(test->CanAttemptWindowsLogon()); EXPECT_EQ(S_OK, test->IsWindowsPasswordValidForStoredUser()); @@ -415,35 +280,13 @@ // Simulate a click on the "Forgot Password" link. cred->CommandLinkClicked(FID_FORGOT_PASSWORD_LINK); - // Serialization should complete without any errors. - ASSERT_EQ(S_OK, - cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); - EXPECT_EQ(nullptr, status_text); - EXPECT_EQ(CPSI_SUCCESS, status_icon); - EXPECT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr); - EXPECT_LT(0u, cpcs.cbSerialization); - EXPECT_NE(nullptr, cpcs.rgbSerialization); - - // State was not reset. - EXPECT_TRUE(test->AreCredentialsValid()); - wchar_t* report_status_text = nullptr; - CREDENTIAL_PROVIDER_STATUS_ICON report_icon; - EXPECT_EQ(S_OK, cred->ReportResult(0, 0, &report_status_text, &report_icon)); - // State was reset. - EXPECT_FALSE(test->AreCredentialsValid()); - - // User password should be force changed to the one from gaia. - EXPECT_EQ(S_OK, - fake_os_user_manager()->IsWindowsPasswordValid( - OSUserManager::GetLocalDomain().c_str(), L"foo", L"password")); - - EXPECT_EQ(S_OK, gaia_cred->Terminate()); + // Finish logon successfully but with no credential changed event. + ASSERT_EQ(S_OK, FinishLogonProcess(true, false, 0)); } TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_AlternateForgotPasswordAssociatedUser) { USES_CONVERSION; - FakeGaiaCredentialProvider provider; // Create a fake user for which the windows password does not match the gaia // password supplied by the test gls process. @@ -454,15 +297,15 @@ L"foo", (BSTR)windows_password, L"Full Name", L"comment", base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); - // Start logon. - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); EXPECT_TRUE(test->CanAttemptWindowsLogon()); EXPECT_EQ(S_OK, test->IsWindowsPasswordValidForStoredUser()); @@ -490,14 +333,14 @@ // Set an invalid password and try to get serialization again. Credentials // should still be valid but serialization is not complete. CComBSTR invalid_windows_password = L"a"; - test->SetWindowsPassword(invalid_windows_password); + cred->SetStringValue(FID_CURRENT_PASSWORD_FIELD, invalid_windows_password); EXPECT_EQ(nullptr, status_text); ASSERT_EQ(S_OK, cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); EXPECT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr); // Update the Windows password to be the real password created for the user. - test->SetWindowsPassword(windows_password); + cred->SetStringValue(FID_CURRENT_PASSWORD_FIELD, windows_password); // Sign in information should still be available. EXPECT_TRUE(test->GetFinalEmail().length()); @@ -505,32 +348,15 @@ EXPECT_TRUE(test->CanAttemptWindowsLogon()); EXPECT_EQ(S_FALSE, test->IsWindowsPasswordValidForStoredUser()); - // Serialization should complete without any errors. - ASSERT_EQ(S_OK, - cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); - EXPECT_EQ(nullptr, status_text); - EXPECT_EQ(CPSI_SUCCESS, status_icon); - EXPECT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr); - EXPECT_LT(0u, cpcs.cbSerialization); - EXPECT_NE(nullptr, cpcs.rgbSerialization); - - // State was not reset. - EXPECT_TRUE(test->AreCredentialsValid()); - wchar_t* report_status_text = nullptr; - CREDENTIAL_PROVIDER_STATUS_ICON report_icon; - EXPECT_EQ(S_OK, cred->ReportResult(0, 0, &report_status_text, &report_icon)); - // State was reset. - EXPECT_FALSE(test->AreCredentialsValid()); - - EXPECT_EQ(S_OK, gaia_cred->Terminate()); + // Finish logon successfully but with no credential changed event. + ASSERT_EQ(S_OK, FinishLogonProcess(true, false, 0)); } TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_Cancel) { - FakeGaiaCredentialProvider provider; - - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); @@ -544,73 +370,78 @@ ASSERT_EQ(S_OK, test->SetStartGlsEventName(kStartGlsEventName)); base::WaitableEvent start_event(std::move(start_event_handle)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcess(cred, /*succeeds=*/true)); + ASSERT_EQ(S_OK, StartLogonProcess(/*succeeds=*/true)); // Deselect the credential provider so that it cancels the GLS process and // returns. ASSERT_EQ(S_OK, cred->SetDeselected()); - ASSERT_EQ(S_OK, run_helper()->WaitForLogonProcess(cred)); - ASSERT_EQ(S_OK, gaia_cred->Terminate()); + ASSERT_EQ(S_OK, WaitForLogonProcess()); + + // Logon process should not signal credentials change or raise an error + // message. + ASSERT_EQ(S_OK, FinishLogonProcess(false, false, 0)); +} + +TEST_F(GcpGaiaCredentialBaseTest, FailedUserCreation) { + // Create provider and start logon. + CComPtr<ICredentialProviderCredential> cred; + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); + + // Fail user creation. + fake_os_user_manager()->SetShouldFailUserCreation(true); + + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); + + // Logon process should fail with an internal error. + ASSERT_EQ(S_OK, FinishLogonProcess(false, false, IDS_INTERNAL_ERROR_BASE)); } TEST_F(GcpGaiaCredentialBaseTest, StripEmailTLD) { USES_CONVERSION; - FakeGaiaCredentialProvider provider; - - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); - constexpr char email[] = "foo@imfl.info"; + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + + constexpr char email[] = "foo@imfl.info"; + ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); ASSERT_STREQ(W2COLE(L"foo_imfl"), test->GetFinalUsername()); EXPECT_EQ(test->GetFinalEmail(), email); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); } TEST_F(GcpGaiaCredentialBaseTest, NewUserDisabledThroughUsageScenario) { USES_CONVERSION; - FakeGaiaCredentialProvider provider; - - // Start logon. - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); - provider.SetUsageScenario(CPUS_UNLOCK_WORKSTATION); + // Set the other user tile so that we can get the anonymous credential + // that may try create a new user. + fake_user_array()->SetAccountOptions(CPAO_EMPTY_LOCAL); + + SetUsageScenario(CPUS_UNLOCK_WORKSTATION); + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); - - // Check that values were not propagated to the provider. - EXPECT_EQ(0u, provider.username().Length()); - EXPECT_EQ(0u, provider.password().Length()); - EXPECT_EQ(0u, provider.sid().Length()); - EXPECT_EQ(FALSE, provider.credentials_changed_fired()); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); // Sign in should fail with an error stating that no new users can be created. - ASSERT_STREQ( - test->GetErrorText(), - GetStringResource(IDS_INVALID_UNLOCK_WORKSTATION_USER_BASE).c_str()); + ASSERT_EQ(S_OK, FinishLogonProcess(false, false, + IDS_INVALID_UNLOCK_WORKSTATION_USER_BASE)); } TEST_F(GcpGaiaCredentialBaseTest, NewUserDisabledThroughMdm) { USES_CONVERSION; - FakeAssociatedUserValidator validator; - FakeInternetAvailabilityChecker internet_checker; - // Enforce single user mode for MDM. ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmUrl, L"https://mdm.com")); ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmAllowConsumerAccounts, 1)); @@ -624,40 +455,32 @@ L"foo_registered", L"password", L"name", L"comment", L"gaia-id-registered", base::string16(), &sid)); - FakeGaiaCredentialProvider provider; + // Populate the associated users list. The created user's token handle + // should be valid so that no reauth credential is created. + fake_associated_user_validator()->StartRefreshingTokenHandleValidity(); - // Populate the associated users list, token handle validity does not matter - // in this test. - validator.StartRefreshingTokenHandleValidity(); + // Set the other user tile so that we can get the anonymous credential + // that may try to sign in a user. + fake_user_array()->SetAccountOptions(CPAO_EMPTY_LOCAL); - // Start logon. - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); - - // Check that values were not propagated to the provider. - EXPECT_EQ(0u, provider.username().Length()); - EXPECT_EQ(0u, provider.password().Length()); - EXPECT_EQ(0u, provider.sid().Length()); - EXPECT_EQ(FALSE, provider.credentials_changed_fired()); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); // Sign in should fail with an error stating that no new users can be created. - ASSERT_STREQ(test->GetErrorText(), - GetStringResource(IDS_ADD_USER_DISALLOWED_BASE).c_str()); + ASSERT_EQ(S_OK, + FinishLogonProcess(false, false, IDS_ADD_USER_DISALLOWED_BASE)); } TEST_F(GcpGaiaCredentialBaseTest, InvalidUserUnlockedAfterSignin) { // Enforce token handle verification with user locking when the token handle // is not valid. - FakeAssociatedUserValidator validator; - FakeInternetAvailabilityChecker internet_checker; ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmUrl, L"https://mdm.com")); ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmAllowConsumerAccounts, 1)); GoogleMdmEnrollmentStatusForTesting force_success(true); @@ -672,52 +495,36 @@ base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid)); ASSERT_EQ(2ul, fake_os_user_manager()->GetUserCount()); - // Invalid token fetch result. - fake_http_url_fetcher_factory()->SetFakeResponse( - GURL(AssociatedUserValidator::kTokenInfoUrl), - FakeWinHttpUrlFetcher::Headers(), "{}"); - - // Lock the user through their token handle. - validator.StartRefreshingTokenHandleValidity(); - validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON); - - // User should have invalid token handle and be locked. - EXPECT_FALSE(validator.IsTokenHandleValidForUser(OLE2W(sid))); - EXPECT_EQ(true, validator.IsUserAccessBlocked(OLE2W(sid))); - - FakeGaiaCredentialProvider provider; - - // Start logon. - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); + + // Create with invalid token handle response. + SetDefaultTokenHandleResponse(kDefaultInvalidTokenHandleResponse); + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + // User should have invalid token handle and be locked. + EXPECT_FALSE( + fake_associated_user_validator()->IsTokenHandleValidForUser(OLE2W(sid))); + EXPECT_EQ(true, + fake_associated_user_validator()->IsUserAccessBlockedForTesting( + OLE2W(sid))); - // Now finish the logon, this should unlock the user. - CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; - CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; - wchar_t* status_text; - CREDENTIAL_PROVIDER_STATUS_ICON status_icon; - ASSERT_EQ(S_OK, - cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); - EXPECT_EQ(nullptr, status_text); - EXPECT_EQ(CPSI_SUCCESS, status_icon); - EXPECT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr); - EXPECT_LT(0u, cpcs.cbSerialization); - EXPECT_NE(nullptr, cpcs.rgbSerialization); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); // User should have been associated. EXPECT_EQ(test->GetFinalUsername(), username); // Email should be the same as the default one. EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); - EXPECT_EQ(false, validator.IsUserAccessBlocked(OLE2W(sid))); + // Now finish the logon, this should unlock the user. + ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); - ASSERT_EQ(S_OK, gaia_cred->Terminate()); + EXPECT_EQ(false, + fake_associated_user_validator()->IsUserAccessBlockedForTesting( + OLE2W(sid))); // No new user should be created. EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); @@ -726,11 +533,10 @@ TEST_F(GcpGaiaCredentialBaseTest, DenySigninBlockedDuringSignin) { USES_CONVERSION; - FakeAssociatedUserValidator validator; - FakeInternetAvailabilityChecker internet_checker; ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmUrl, L"https://mdm.com")); + ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmSupportsMultiUser, 1)); ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmAllowConsumerAccounts, 1)); - GoogleMdmEnrollmentStatusForTesting force_success(true); + GoogleMdmEnrolledStatusForTesting force_success(true); // Create a fake user that has the same gaia id as the test gaia id. CComBSTR first_sid; @@ -740,45 +546,36 @@ base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &first_sid)); ASSERT_EQ(2ul, fake_os_user_manager()->GetUserCount()); - FakeGaiaCredentialProvider provider; - // Invalid token fetch result. - fake_http_url_fetcher_factory()->SetFakeResponse( - GURL(AssociatedUserValidator::kTokenInfoUrl), - FakeWinHttpUrlFetcher::Headers(), "{}"); - - validator.StartRefreshingTokenHandleValidity(); - - // Start logon. - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); + + // Create with valid token handle response and sign in the anonymous + // credential with the user that should still be valid. + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); + + // Change token response to an invalid one. + fake_http_url_fetcher_factory()->SetFakeResponse( + GURL(AssociatedUserValidator::kTokenInfoUrl), + FakeWinHttpUrlFetcher::Headers(), "{}"); + + // Force refresh of all token handles on the next query. + fake_associated_user_validator()->ForceRefreshTokenHandlesForTesting(); // Signin process has already started. User should not be locked even if their // token handle is invalid. - EXPECT_FALSE(validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); - EXPECT_FALSE(validator.IsUserAccessBlocked(OLE2W(first_sid))); + EXPECT_FALSE(fake_associated_user_validator() + ->DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); + EXPECT_FALSE(fake_associated_user_validator()->IsUserAccessBlockedForTesting( + OLE2W(first_sid))); // Now finish the logon. - CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; - CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; - wchar_t* status_text; - CREDENTIAL_PROVIDER_STATUS_ICON status_icon; - ASSERT_EQ(S_OK, - cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); - EXPECT_EQ(nullptr, status_text); - EXPECT_EQ(CPSI_SUCCESS, status_icon); - EXPECT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr); - EXPECT_LT(0u, cpcs.cbSerialization); - EXPECT_NE(nullptr, cpcs.rgbSerialization); - - // State was not reset. - EXPECT_TRUE(test->AreCredentialsValid()); + ASSERT_EQ(S_OK, FinishLogonProcessWithCred(true, true, 0, cred)); // User should have been associated. EXPECT_EQ(test->GetFinalUsername(), username); @@ -786,20 +583,18 @@ EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); // Result has not been reported yet, user signin should still not be denied. - EXPECT_FALSE(validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); - EXPECT_FALSE(validator.IsUserAccessBlocked(OLE2W(first_sid))); + EXPECT_FALSE(fake_associated_user_validator() + ->DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); + EXPECT_FALSE(fake_associated_user_validator()->IsUserAccessBlockedForTesting( + OLE2W(first_sid))); - wchar_t* report_status_text = nullptr; - CREDENTIAL_PROVIDER_STATUS_ICON report_icon; - EXPECT_EQ(S_OK, cred->ReportResult(0, 0, &report_status_text, &report_icon)); - // State was reset. - EXPECT_FALSE(test->AreCredentialsValid()); - - EXPECT_EQ(S_OK, gaia_cred->Terminate()); + ReportLogonProcessResult(cred); // Now signin can be denied for the user if their token handle is invalid. - EXPECT_TRUE(validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); - EXPECT_TRUE(validator.IsUserAccessBlocked(OLE2W(first_sid))); + EXPECT_TRUE(fake_associated_user_validator() + ->DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); + EXPECT_TRUE(fake_associated_user_validator()->IsUserAccessBlockedForTesting( + OLE2W(first_sid))); // No new user should be created. EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); @@ -807,178 +602,167 @@ TEST_F(GcpGaiaCredentialBaseTest, StripEmailTLD_Gmail) { USES_CONVERSION; - FakeGaiaCredentialProvider provider; - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); - constexpr char email[] = "bar@gmail.com"; + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + + constexpr char email[] = "bar@gmail.com"; + ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); ASSERT_STREQ(W2COLE(L"bar"), test->GetFinalUsername()); EXPECT_EQ(test->GetFinalEmail(), email); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); } TEST_F(GcpGaiaCredentialBaseTest, StripEmailTLD_Googlemail) { USES_CONVERSION; - FakeGaiaCredentialProvider provider; - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); - constexpr char email[] = "toto@googlemail.com"; + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + + constexpr char email[] = "toto@googlemail.com"; + ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); ASSERT_STREQ(W2COLE(L"toto"), test->GetFinalUsername()); EXPECT_EQ(test->GetFinalEmail(), email); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); } TEST_F(GcpGaiaCredentialBaseTest, InvalidUsernameCharacters) { USES_CONVERSION; - FakeGaiaCredentialProvider provider; - - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); - constexpr char email[] = "a\\[]:|<>+=;?*z@gmail.com"; + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + + constexpr char email[] = "a\\[]:|<>+=;?*z@gmail.com"; + ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); ASSERT_STREQ(W2COLE(L"a____________z"), test->GetFinalUsername()); EXPECT_EQ(test->GetFinalEmail(), email); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); } TEST_F(GcpGaiaCredentialBaseTest, EmailTooLong) { USES_CONVERSION; - FakeGaiaCredentialProvider provider; - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); - constexpr char email[] = "areallylongemailadressdude@gmail.com"; + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + + constexpr char email[] = "areallylongemailadressdude@gmail.com"; + ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); ASSERT_STREQ(W2COLE(L"areallylongemailadre"), test->GetFinalUsername()); EXPECT_EQ(test->GetFinalEmail(), email); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); } TEST_F(GcpGaiaCredentialBaseTest, EmailTooLong2) { USES_CONVERSION; - FakeGaiaCredentialProvider provider; - - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); - constexpr char email[] = "foo@areallylongdomaindude.com"; + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + + constexpr char email[] = "foo@areallylongdomaindude.com"; + ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); ASSERT_STREQ(W2COLE(L"foo_areallylongdomai"), test->GetFinalUsername()); EXPECT_EQ(test->GetFinalEmail(), email); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); } TEST_F(GcpGaiaCredentialBaseTest, EmailIsNoAt) { USES_CONVERSION; - FakeGaiaCredentialProvider provider; - - CComPtr<IGaiaCredential> gaia_cred; - CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); - constexpr char email[] = "foo"; + // Create provider and start logon. + CComPtr<ICredentialProviderCredential> cred; + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); + CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); ASSERT_STREQ(W2COLE(L"foo_gmail"), test->GetFinalUsername()); EXPECT_EQ(test->GetFinalEmail(), email); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); } TEST_F(GcpGaiaCredentialBaseTest, EmailIsAtCom) { USES_CONVERSION; - FakeGaiaCredentialProvider provider; - - CComPtr<IGaiaCredential> gaia_cred; - CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); constexpr char email[] = "@com"; + // Create provider and start logon. + CComPtr<ICredentialProviderCredential> cred; + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); + CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); ASSERT_STREQ(W2COLE(L"_com"), test->GetFinalUsername()); EXPECT_EQ(test->GetFinalEmail(), email); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); } TEST_F(GcpGaiaCredentialBaseTest, EmailIsAtDotCom) { USES_CONVERSION; - FakeGaiaCredentialProvider provider; - - CComPtr<IGaiaCredential> gaia_cred; - CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); constexpr char email[] = "@.com"; + // Create provider and start logon. + CComPtr<ICredentialProviderCredential> cred; + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); + CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); ASSERT_STREQ(W2COLE(L"_.com"), test->GetFinalUsername()); EXPECT_EQ(test->GetFinalEmail(), email); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); } // Tests various sign in scenarios with consumer and non-consumer domains. @@ -1033,19 +817,17 @@ ASSERT_EQ(2ul, fake_os_user_manager()->GetUserCount()); } - FakeGaiaCredentialProvider provider; - - // Start logon. - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); test->SetGlsEmailAddress(user_email); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); bool should_signin_succeed = !mdm_enabled || (mdm_consumer_accounts_reg_key_set && @@ -1054,36 +836,18 @@ // Sign in success. if (should_signin_succeed) { - CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; - CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; - wchar_t* status_text; - CREDENTIAL_PROVIDER_STATUS_ICON status_icon; - ASSERT_EQ(S_OK, cred->GetSerialization(&cpgsr, &cpcs, &status_text, - &status_icon)); - EXPECT_EQ(nullptr, status_text); - EXPECT_EQ(CPSI_SUCCESS, status_icon); - EXPECT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr); - EXPECT_LT(0u, cpcs.cbSerialization); - EXPECT_NE(nullptr, cpcs.rgbSerialization); - // User should have been associated. EXPECT_EQ(test->GetFinalUsername(), username); // Email should be the same as the default one. EXPECT_EQ(test->GetFinalEmail(), user_email); + + ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); } else { - // Nothing was propagated to the provider. - EXPECT_EQ(0u, provider.username().Length()); - EXPECT_EQ(0u, provider.password().Length()); - EXPECT_EQ(0u, provider.sid().Length()); - EXPECT_EQ(FALSE, provider.credentials_changed_fired()); - // Error message concerning invalid domain is sent. - EXPECT_STREQ(test->GetErrorText(), - GetStringResource(IDS_INVALID_EMAIL_DOMAIN_BASE).c_str()); + ASSERT_EQ(S_OK, + FinishLogonProcess(false, false, IDS_INVALID_EMAIL_DOMAIN_BASE)); } - gaia_cred->Terminate(); - if (user_created) { // No new user should be created. EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount());
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider.cc index a08a5d73..9032fe2 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_provider.cc +++ b/chrome/credential_provider/gaiacp/gaia_credential_provider.cc
@@ -95,6 +95,19 @@ return S_OK; } +template <class CredentialT> +HRESULT CreateCredentialObject( + CGaiaCredentialProvider::CredentialCreatorFn creator_fn, + CGaiaCredentialProvider::GaiaCredentialComPtrStorage* credential_com_ptr) { + if (creator_fn) { + return creator_fn(credential_com_ptr); + } + + return CComCreator<CComObject<CredentialT>>::CreateInstance( + nullptr, IID_IGaiaCredential, + reinterpret_cast<void**>(&credential_com_ptr->gaia_cred)); +} + } // namespace // Class that when constructed automatically starts a thread that tries @@ -173,8 +186,10 @@ return 0; } -CGaiaCredentialProvider::ComPtrStorage::ComPtrStorage() = default; -CGaiaCredentialProvider::ComPtrStorage::~ComPtrStorage() = default; +CGaiaCredentialProvider::GaiaCredentialComPtrStorage:: + GaiaCredentialComPtrStorage() = default; +CGaiaCredentialProvider::GaiaCredentialComPtrStorage:: + ~GaiaCredentialComPtrStorage() = default; CGaiaCredentialProvider::ProviderConcurrentState::ProviderConcurrentState() = default; @@ -230,7 +245,7 @@ void CGaiaCredentialProvider::ProviderConcurrentState::GetUpdatedState( bool* needs_to_refresh_users, - ComPtrStorage* auto_logon_credential) { + GaiaCredentialComPtrStorage* auto_logon_credential) { DCHECK(needs_to_refresh_users); DCHECK(auto_logon_credential); base::AutoLock locker(state_update_lock_); @@ -314,22 +329,22 @@ HRESULT CGaiaCredentialProvider::CreateAnonymousCredentialIfNeeded( bool showing_other_user) { - CComPtr<IGaiaCredential> cred; + GaiaCredentialComPtrStorage cred; HRESULT hr = E_FAIL; if (showing_other_user) { - hr = CComCreator<CComObject<COtherUserGaiaCredential>>::CreateInstance( - nullptr, IID_IGaiaCredential, reinterpret_cast<void**>(&cred)); + hr = CreateCredentialObject<COtherUserGaiaCredential>( + other_user_cred_creator_, &cred); } else if (CanNewUsersBeCreated(cpus_)) { - hr = CComCreator<CComObject<CGaiaCredential>>::CreateInstance( - nullptr, IID_IGaiaCredential, reinterpret_cast<void**>(&cred)); + hr = + CreateCredentialObject<CGaiaCredential>(anonymous_cred_creator_, &cred); } else { return S_OK; } if (SUCCEEDED(hr)) { - hr = cred->Initialize(this); + hr = cred.gaia_cred->Initialize(this); if (SUCCEEDED(hr)) { - AddCredentialAndCheckAutoLogon(cred, base::string16(), nullptr); + AddCredentialAndCheckAutoLogon(cred.gaia_cred, base::string16(), nullptr); } else { LOG(ERROR) << "Could not create credential hr=" << putHR(hr); } @@ -340,7 +355,7 @@ HRESULT CGaiaCredentialProvider::CreateReauthCredentials( ICredentialProviderUserArray* users, - ComPtrStorage* auto_logon_credential) { + GaiaCredentialComPtrStorage* auto_logon_credential) { std::map<base::string16, std::pair<base::string16, base::string16>> sid_to_username; @@ -401,21 +416,22 @@ if (AssociatedUserValidator::Get()->IsTokenHandleValidForUser(sid)) continue; - CComPtr<IGaiaCredential> cred; - HRESULT hr = CComCreator<CComObject<CReauthCredential>>::CreateInstance( - nullptr, IID_IGaiaCredential, reinterpret_cast<void**>(&cred)); + GaiaCredentialComPtrStorage cred; + HRESULT hr = + CreateCredentialObject<CReauthCredential>(reauth_cred_creator_, &cred); if (FAILED(hr)) { LOG(ERROR) << "Could not create credential hr=" << putHR(hr); return hr; } - hr = InitializeReauthCredential(this, sid, domain, username, cred); + hr = + InitializeReauthCredential(this, sid, domain, username, cred.gaia_cred); if (FAILED(hr)) { LOG(ERROR) << "InitializeReauthCredential hr=" << putHR(hr); return hr; } - AddCredentialAndCheckAutoLogon(cred, sid, auto_logon_credential); + AddCredentialAndCheckAutoLogon(cred.gaia_cred, sid, auto_logon_credential); } return S_OK; @@ -424,7 +440,7 @@ void CGaiaCredentialProvider::AddCredentialAndCheckAutoLogon( const CComPtr<IGaiaCredential>& cred, const base::string16& sid, - ComPtrStorage* auto_logon_credential) { + GaiaCredentialComPtrStorage* auto_logon_credential) { USES_CONVERSION; users_.emplace_back(cred); @@ -451,7 +467,7 @@ } void CGaiaCredentialProvider::RecreateCredentials( - ComPtrStorage* auto_logon_credential) { + GaiaCredentialComPtrStorage* auto_logon_credential) { LOGFN(INFO); DCHECK(user_array_); @@ -470,6 +486,55 @@ LOG(ERROR) << "CreateReauthCredentials hr=" << putHR(hr); } +void CGaiaCredentialProvider::SetCredentialCreatorFunctionsForTesting( + CredentialCreatorFn anonymous_cred_creator, + CredentialCreatorFn other_user_cred_creator, + CredentialCreatorFn reauth_cred_creator) { + DCHECK(!anonymous_cred_creator_); + DCHECK(!other_user_cred_creator_); + DCHECK(!reauth_cred_creator_); + + anonymous_cred_creator_ = anonymous_cred_creator; + other_user_cred_creator_ = other_user_cred_creator; + reauth_cred_creator_ = reauth_cred_creator; +} + +HRESULT CGaiaCredentialProvider::OnUserAuthenticatedImpl( + IUnknown* credential, + BSTR /*username*/, + BSTR /*password*/, + BSTR sid, + BOOL fire_credentials_changed) { + DCHECK(!credential || sid); + + if (!fire_credentials_changed) + return S_OK; + + // Ensure that user access cannot be denied at this time so that the user + // that is about to sign in won't be locked. If a ScopedLockDenyAccessUpdate + // is created before calling this function this should guarantee that + // situation because the call to BlockDenyAccessUpdate is locked with the + // same lock that is used in DenySigninForUsersWithInvalidTokenHandles. + // So either the call to Deny has finished and no new deny will occur + // afterwards or the Deny will be disabled because the block has been + // incremented first. + CHECK(!credential || + AssociatedUserValidator::Get()->IsDenyAccessUpdateBlocked()); + + CComPtr<IGaiaCredential> gaia_credential; + if (credential->QueryInterface(IID_IGaiaCredential, + reinterpret_cast<void**>(&gaia_credential)) == + S_OK) { + // Try to set the auto logon credential. If it succeeds we can raise a + // credential changed event. + if (concurrent_state_.SetAutoLogonCredential(gaia_credential) && events_) + events_->CredentialsChanged(advise_context_); + } + + LOGFN(INFO) << "Signing in authenticated sid=" << OLE2CW(sid); + return S_OK; +} + // Static. bool CGaiaCredentialProvider::IsUsageScenarioSupported( CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus) { @@ -527,38 +592,12 @@ HRESULT CGaiaCredentialProvider::OnUserAuthenticated( IUnknown* credential, - BSTR /*username*/, - BSTR /*password*/, + BSTR username, + BSTR password, BSTR sid, BOOL fire_credentials_changed) { - DCHECK(!credential || sid); - - if (!fire_credentials_changed) - return S_OK; - - // Ensure that user access cannot be denied at this time so that the user - // that is about to sign in won't be locked. If a ScopedLockDenyAccessUpdate - // is created before calling this function this should guarantee that - // situation because the call to BlockDenyAccessUpdate is locked with the - // same lock that is used in DenySigninForUsersWithInvalidTokenHandles. - // So either the call to Deny has finished and no new deny will occur - // afterwards or the Deny will be disabled because the block has been - // incremented first. - CHECK(!credential || - AssociatedUserValidator::Get()->IsDenyAccessUpdateBlocked()); - - CComPtr<IGaiaCredential> gaia_credential; - if (credential->QueryInterface(IID_IGaiaCredential, - reinterpret_cast<void**>(&gaia_credential)) == - S_OK) { - // Try to set the auto logon credential. If it succeeds we can raise a - // credential changed event. - if (concurrent_state_.SetAutoLogonCredential(gaia_credential) && events_) - events_->CredentialsChanged(advise_context_); - } - - LOGFN(INFO) << "Signing in authenticated sid=" << OLE2CW(sid); - return S_OK; + return OnUserAuthenticatedImpl(credential, username, password, sid, + fire_credentials_changed); } // ICredentialProviderSetUserArray //////////////////////////////////////////// @@ -710,7 +749,7 @@ DWORD* default_index, BOOL* autologin_with_default) { bool needs_to_refresh_users = false; - ComPtrStorage local_auto_logon_credential; + GaiaCredentialComPtrStorage local_auto_logon_credential; // Get the mutually exclusive state of the provider so that we can // determine the correct next step (recreate credentials or auto logon).
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider.h b/chrome/credential_provider/gaiacp/gaia_credential_provider.h index 2a6db40..54edb10 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_provider.h +++ b/chrome/credential_provider/gaiacp/gaia_credential_provider.h
@@ -24,7 +24,8 @@ // Event handler that can be notified when a user's access has been revoked, // allowing the credential provider to update the list of available credentials. -class ICredentialUpdateEventsHandler { +class DECLSPEC_UUID("fc2c889b-b468-4eb9-a61c-c984be8cc496") + ICredentialUpdateEventsHandler : public IUnknown { public: virtual ~ICredentialUpdateEventsHandler() = default; virtual void UpdateCredentialsIfNeeded(bool user_access_changed) = 0; @@ -51,6 +52,7 @@ COM_INTERFACE_ENTRY(IGaiaCredentialProvider) COM_INTERFACE_ENTRY(ICredentialProviderSetUserArray) COM_INTERFACE_ENTRY(ICredentialProvider) + COM_INTERFACE_ENTRY(ICredentialUpdateEventsHandler) END_COM_MAP() DECLARE_PROTECT_FINAL_CONSTRUCT() @@ -70,17 +72,31 @@ // determine the result of this query. static bool CanNewUsersBeCreated(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus); - private: - HRESULT DestroyCredentials(); - // Struct to allow passing CComPtr by pointer without the implicit conversion // to ** version of the CComPtr - struct ComPtrStorage { - ComPtrStorage(); - ~ComPtrStorage(); + struct GaiaCredentialComPtrStorage { + GaiaCredentialComPtrStorage(); + ~GaiaCredentialComPtrStorage(); CComPtr<IGaiaCredential> gaia_cred; }; + typedef HRESULT (*CredentialCreatorFn)(GaiaCredentialComPtrStorage*); + + protected: + void SetCredentialCreatorFunctionsForTesting( + CredentialCreatorFn anonymous_cred_creator, + CredentialCreatorFn other_user_cred_creator, + CredentialCreatorFn reauth_cred_creator); + + virtual HRESULT OnUserAuthenticatedImpl(IUnknown* credential, + BSTR username, + BSTR password, + BSTR sid, + BOOL fire_credentials_changed); + + private: + HRESULT DestroyCredentials(); + // Class used to store state information for the provider that may be accessed // concurrently. This class is thread safe and ensures that the correct state // can be set / queried at any moment. When modifying the state, the modifying @@ -116,7 +132,7 @@ // |needs_to_refresh_users| and |auto_logon_credential| can be set to a non // default value. void GetUpdatedState(bool* needs_to_refresh_users, - ComPtrStorage* auto_logon_credential); + GaiaCredentialComPtrStorage* auto_logon_credential); // Resets the state of the provider to be default one. On the next call to // GetCredentialCount no auto logon should be performed and no refresh of @@ -158,24 +174,26 @@ // Creates all the reauth credentials from the users that is returned from // |users|. Fills the |gaia_cred| in |auto_logon_credential| with a reference // to the credential that needs to perform auto logon (if any). - HRESULT CreateReauthCredentials(ICredentialProviderUserArray* users, - ComPtrStorage* auto_logon_credential); + HRESULT CreateReauthCredentials( + ICredentialProviderUserArray* users, + GaiaCredentialComPtrStorage* auto_logon_credential); // This function will always add |cred| to |users_| and will also try to check // if the |sid| matches the one set in |set_serialization_sid_| to allow auto // logon of remote connections. Fills the |gaia_cred| in // |auto_logon_credential| with a reference to the credential that needs to // perform auto logon (if any). - void AddCredentialAndCheckAutoLogon(const CComPtr<IGaiaCredential>& cred, - const base::string16& sid, - ComPtrStorage* auto_logon_credential); + void AddCredentialAndCheckAutoLogon( + const CComPtr<IGaiaCredential>& cred, + const base::string16& sid, + GaiaCredentialComPtrStorage* auto_logon_credential); // Destroys existing credentials and recreates them based on the contents of // |user_array_|. This member must be set via a call to SetUserArray before // RecreateCredentials is called. Fills the |gaia_cred| in // |auto_logon_credential| with a reference to the credential that needs to // perform auto logon (if any). - void RecreateCredentials(ComPtrStorage* auto_logon_credential); + void RecreateCredentials(GaiaCredentialComPtrStorage* auto_logon_credential); void ClearTransient(); void CleanupOlderVersions(); @@ -235,6 +253,10 @@ base::string16 set_serialization_sid_; ProviderConcurrentState concurrent_state_; + + CredentialCreatorFn anonymous_cred_creator_ = nullptr; + CredentialCreatorFn other_user_cred_creator_ = nullptr; + CredentialCreatorFn reauth_cred_creator_ = nullptr; }; // OBJECT_ENTRY_AUTO() contains an extra semicolon.
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider_filter.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider_filter.cc index 486dd40..6506180a 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_provider_filter.cc +++ b/chrome/credential_provider/gaiacp/gaia_credential_provider_filter.cc
@@ -82,6 +82,8 @@ pcpcs_in->cbSerialization); pcpcs_out->cbSerialization = pcpcs_in->cbSerialization; pcpcs_out->clsidCredentialProvider = CLSID_GaiaCredentialProvider; + pcpcs_out->ulAuthenticationPackage = pcpcs_in->ulAuthenticationPackage; + return S_OK; }
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc index f69e873..fd92c70 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc +++ b/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
@@ -10,10 +10,8 @@ #include <tuple> #include "base/synchronization/waitable_event.h" -#include "base/win/registry.h" #include "base/win/win_util.h" #include "chrome/credential_provider/common/gcp_strings.h" -#include "chrome/credential_provider/gaiacp/associated_user_validator.h" #include "chrome/credential_provider/gaiacp/auth_utils.h" #include "chrome/credential_provider/gaiacp/gaia_credential_provider.h" #include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h" @@ -21,41 +19,14 @@ #include "chrome/credential_provider/gaiacp/reg_utils.h" #include "chrome/credential_provider/test/com_fakes.h" #include "chrome/credential_provider/test/gcp_fakes.h" -#include "testing/gtest/include/gtest/gtest.h" +#include "chrome/credential_provider/test/gls_runner_test_base.h" +#include "chrome/credential_provider/test/test_credential.h" namespace credential_provider { -class GcpCredentialProviderTest : public ::testing::Test { - protected: - void CreateGCPWUser(const wchar_t* username, - const wchar_t* email, - const wchar_t* password, - const wchar_t* fullname, - const wchar_t* comment, - const wchar_t* gaia_id, - BSTR* sid) { - ASSERT_EQ(S_OK, - fake_os_user_manager_.CreateTestOSUser( - username, password, fullname, comment, gaia_id, email, sid)); - } +namespace testing { - FakeOSUserManager* fake_os_user_manager() { return &fake_os_user_manager_; } - FakeWinHttpUrlFetcherFactory* fake_http_url_fetcher_factory() { - return &fake_http_url_fetcher_factory_; - } - - void SetUp() override; - - private: - registry_util::RegistryOverrideManager registry_override_; - FakeOSUserManager fake_os_user_manager_; - FakeScopedLsaPolicyFactory fake_scoped_lsa_policy_factory_; - FakeWinHttpUrlFetcherFactory fake_http_url_fetcher_factory_; -}; - -void GcpCredentialProviderTest::SetUp() { - InitializeRegistryOverrideForTesting(®istry_override_); -} +class GcpCredentialProviderTest : public GlsRunnerTestBase {}; TEST_F(GcpCredentialProviderTest, Basic) { CComPtr<IGaiaCredentialProvider> provider; @@ -65,164 +36,92 @@ } TEST_F(GcpCredentialProviderTest, SetUserArray_NoGaiaUsers) { - CComPtr<ICredentialProviderSetUserArray> user_array; - ASSERT_EQ( - S_OK, - CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance( - nullptr, IID_ICredentialProviderSetUserArray, (void**)&user_array)); - - FakeCredentialProviderUserArray array; - array.AddUser(L"sid", L"username"); - ASSERT_EQ(S_OK, user_array->SetUserArray(&array)); + CComBSTR sid; + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + L"username", L"password", L"full name", L"comment", L"", + L"", &sid)); CComPtr<ICredentialProvider> provider; - ASSERT_EQ(S_OK, user_array.QueryInterface(&provider)); + DWORD count = 0; + ASSERT_EQ(S_OK, InitializeProviderWithCredentials(&count, &provider)); - // There should be no credentials. Only users with the requisite registry - // entry will be counted. - DWORD count; - DWORD default_index; - BOOL autologon; - ASSERT_EQ(S_OK, - provider->GetCredentialCount(&count, &default_index, &autologon)); - EXPECT_EQ(0u, count); - EXPECT_EQ(CREDENTIAL_PROVIDER_NO_DEFAULT, default_index); - EXPECT_FALSE(autologon); + // There should only be the anonymous credential. Only users with the + // requisite registry entry will be counted. + EXPECT_EQ(1u, count); + + CComPtr<ICredentialProviderCredential> cred; + ASSERT_EQ(S_OK, provider->GetCredentialAt(0, &cred)); + + CComPtr<ICredentialProviderCredential2> cred2; + ASSERT_NE(S_OK, cred.QueryInterface(&cred2)); + + CComPtr<IReauthCredential> reauth_cred; + ASSERT_NE(S_OK, cred.QueryInterface(&reauth_cred)); } TEST_F(GcpCredentialProviderTest, CpusLogon) { + CComBSTR sid; + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + L"username", L"password", L"full name", L"comment", L"", + L"", &sid)); + CComPtr<ICredentialProvider> provider; - ASSERT_EQ(S_OK, - CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance( - nullptr, IID_ICredentialProvider, (void**)&provider)); + DWORD count = 0; + ASSERT_EQ(S_OK, InitializeProviderWithCredentials(&count, &provider)); - // Start process for logon screen. - ASSERT_EQ(S_OK, provider->SetUsageScenario(CPUS_LOGON, 0)); + // There should only be the anonymous credential. Only users with the + // requisite registry entry will be counted. + EXPECT_EQ(1u, count); - // Give list of users visible on welcome screen. - CComPtr<ICredentialProviderSetUserArray> user_array; - ASSERT_EQ(S_OK, provider.QueryInterface(&user_array)); - FakeCredentialProviderUserArray array; - array.AddUser(L"sid1", L"username1"); - ASSERT_EQ(S_OK, user_array->SetUserArray(&array)); - - // Activate the CP. - FakeCredentialProviderEvents events; - ASSERT_EQ(S_OK, provider->Advise(&events, 0)); - - // Check credentials. - DWORD count; - DWORD default_index; - BOOL autologon; - ASSERT_EQ(S_OK, - provider->GetCredentialCount(&count, &default_index, &autologon)); - ASSERT_EQ(1u, count); - EXPECT_EQ(CREDENTIAL_PROVIDER_NO_DEFAULT, default_index); - EXPECT_FALSE(autologon); CComPtr<ICredentialProviderCredential> cred; ASSERT_EQ(S_OK, provider->GetCredentialAt(0, &cred)); - CComPtr<IGaiaCredential> gaia_cred; - EXPECT_EQ(S_OK, cred.QueryInterface(&gaia_cred)); - // Get fields. - DWORD field_count; - ASSERT_EQ(S_OK, provider->GetFieldDescriptorCount(&field_count)); - EXPECT_EQ(FIELD_COUNT, field_count); + CComPtr<ICredentialProviderCredential2> cred2; + ASSERT_NE(S_OK, cred.QueryInterface(&cred2)); - // Deactivate the CP. - ASSERT_EQ(S_OK, provider->UnAdvise()); + CComPtr<IReauthCredential> reauth_cred; + ASSERT_NE(S_OK, cred.QueryInterface(&reauth_cred)); } TEST_F(GcpCredentialProviderTest, CpusUnlock) { + CComBSTR sid; + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + L"username", L"password", L"full name", L"comment", L"", + L"", &sid)); + CComPtr<ICredentialProvider> provider; - ASSERT_EQ(S_OK, - CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance( - nullptr, IID_ICredentialProvider, (void**)&provider)); - - // Start process for logon screen. - ASSERT_EQ(S_OK, provider->SetUsageScenario(CPUS_UNLOCK_WORKSTATION, 0)); - - // Give list of users visible on welcome screen. - CComPtr<ICredentialProviderSetUserArray> user_array; - ASSERT_EQ(S_OK, provider.QueryInterface(&user_array)); - FakeCredentialProviderUserArray array; - array.AddUser(L"sid1", L"username1"); - ASSERT_EQ(S_OK, user_array->SetUserArray(&array)); - - // Activate the CP. - FakeCredentialProviderEvents events; - ASSERT_EQ(S_OK, provider->Advise(&events, 0)); + DWORD count = 0; + SetUsageScenario(CPUS_UNLOCK_WORKSTATION); + ASSERT_EQ(S_OK, InitializeProviderWithCredentials(&count, &provider)); // Check credentials. None should be available because the anonymous // credential is not allowed during an unlock scenario. - DWORD count; - DWORD default_index; - BOOL autologon; - ASSERT_EQ(S_OK, - provider->GetCredentialCount(&count, &default_index, &autologon)); ASSERT_EQ(0u, count); - EXPECT_EQ(CREDENTIAL_PROVIDER_NO_DEFAULT, default_index); - EXPECT_FALSE(autologon); - - // Get fields. - DWORD field_count; - ASSERT_EQ(S_OK, provider->GetFieldDescriptorCount(&field_count)); - EXPECT_EQ(FIELD_COUNT, field_count); - - // Deactivate the CP. - ASSERT_EQ(S_OK, provider->UnAdvise()); } TEST_F(GcpCredentialProviderTest, AutoLogonAfterUserRefresh) { USES_CONVERSION; - CComPtr<ICredentialProvider> provider; - ASSERT_EQ(S_OK, - CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance( - nullptr, IID_ICredentialProvider, (void**)&provider)); + CComBSTR sid; + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + L"username", L"password", L"full name", L"comment", L"", + L"", &sid)); + + CComPtr<ICredentialProviderCredential> cred; + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); + + CComPtr<ICredentialProvider> provider = created_provider(); CComPtr<IGaiaCredentialProvider> gaia_provider; ASSERT_EQ(S_OK, provider.QueryInterface(&gaia_provider)); - CComBSTR sid; - ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( - L"username", L"passowrd", L"Full Name", L"Comment", L"", - L"", &sid)); - // Start process for logon screen. - ASSERT_EQ(S_OK, provider->SetUsageScenario(CPUS_LOGON, 0)); - - // Give empty list of users so that only the anonymous credential is created. - CComPtr<ICredentialProviderSetUserArray> user_array; - ASSERT_EQ(S_OK, provider.QueryInterface(&user_array)); - FakeCredentialProviderUserArray array; - ASSERT_EQ(S_OK, user_array->SetUserArray(&array)); - - // Activate the CP. - FakeCredentialProviderEvents events; - ASSERT_EQ(S_OK, provider->Advise(&events, 0)); - - // Only the anonymous credential should exist. - DWORD count; - DWORD default_index; - BOOL autologon; - ASSERT_EQ(S_OK, - provider->GetCredentialCount(&count, &default_index, &autologon)); - ASSERT_EQ(1u, count); - EXPECT_EQ(CREDENTIAL_PROVIDER_NO_DEFAULT, default_index); - EXPECT_FALSE(autologon); - - // Get the anonymous credential. - CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, provider->GetCredentialAt(0, &cred)); - // Notify that user access is denied to fake a forced recreation of the users. - ICredentialUpdateEventsHandler* update_handler = - static_cast<ICredentialUpdateEventsHandler*>( - static_cast<CGaiaCredentialProvider*>(provider.p)); + CComPtr<ICredentialUpdateEventsHandler> update_handler; + ASSERT_EQ(S_OK, provider.QueryInterface(&update_handler)); update_handler->UpdateCredentialsIfNeeded(true); // Credential changed event should have been received. - EXPECT_TRUE(events.CredentialsChangedReceived()); - events.ResetCredentialsChangedReceived(); + EXPECT_TRUE(fake_provider_events()->CredentialsChangedReceived()); + fake_provider_events()->ResetCredentialsChangedReceived(); // At the same time notify that a user has authenticated and requires a // sign in. @@ -236,10 +135,14 @@ } // No credential changed should have been signalled here. - EXPECT_FALSE(events.CredentialsChangedReceived()); + EXPECT_FALSE(fake_provider_events()->CredentialsChangedReceived()); // GetCredentialCount should return back the same credential that was just // auto logged on. + + DWORD count; + DWORD default_index; + BOOL autologon; ASSERT_EQ(S_OK, provider->GetCredentialCount(&count, &default_index, &autologon)); ASSERT_EQ(1u, count); @@ -257,7 +160,7 @@ update_handler->UpdateCredentialsIfNeeded(false); // Credential changed event should have been received. - EXPECT_TRUE(events.CredentialsChangedReceived()); + EXPECT_TRUE(fake_provider_events()->CredentialsChangedReceived()); // GetCredentialCount should return new credentials with no auto logon. ASSERT_EQ(S_OK, @@ -272,12 +175,12 @@ // Another request to refresh the credentials should yield no credential // changed event or refresh of credentials. - events.ResetCredentialsChangedReceived(); + fake_provider_events()->ResetCredentialsChangedReceived(); update_handler->UpdateCredentialsIfNeeded(false); // No credential changed event should have been received. - EXPECT_FALSE(events.CredentialsChangedReceived()); + EXPECT_FALSE(fake_provider_events()->CredentialsChangedReceived()); // GetCredentialCount should return the same credentials with no change. ASSERT_EQ(S_OK, @@ -289,55 +192,24 @@ CComPtr<ICredentialProviderCredential> unchanged_cred; ASSERT_EQ(S_OK, provider->GetCredentialAt(0, &unchanged_cred)); EXPECT_TRUE(new_cred.IsEqualObject(unchanged_cred)); - - // Deactivate the CP. - ASSERT_EQ(S_OK, provider->UnAdvise()); } TEST_F(GcpCredentialProviderTest, AutoLogonBeforeUserRefresh) { USES_CONVERSION; - CComPtr<ICredentialProvider> provider; - ASSERT_EQ(S_OK, - CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance( - nullptr, IID_ICredentialProvider, (void**)&provider)); + CComBSTR sid; + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + L"username", L"password", L"full name", L"comment", L"", + L"", &sid)); + CComPtr<ICredentialProviderCredential> cred; + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); + + CComPtr<ICredentialProvider> provider = created_provider(); CComPtr<IGaiaCredentialProvider> gaia_provider; ASSERT_EQ(S_OK, provider.QueryInterface(&gaia_provider)); - CComBSTR sid; - ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( - L"username", L"passowrd", L"Full Name", L"Comment", L"", - L"", &sid)); - // Start process for logon screen. - ASSERT_EQ(S_OK, provider->SetUsageScenario(CPUS_LOGON, 0)); - - // Give empty list of users so that only the anonymous credential is created. - CComPtr<ICredentialProviderSetUserArray> user_array; - ASSERT_EQ(S_OK, provider.QueryInterface(&user_array)); - FakeCredentialProviderUserArray array; - ASSERT_EQ(S_OK, user_array->SetUserArray(&array)); - - // Activate the CP. - FakeCredentialProviderEvents events; - ASSERT_EQ(S_OK, provider->Advise(&events, 0)); - - // Only the anonymous credential should exist. - DWORD count; - DWORD default_index; - BOOL autologon; - ASSERT_EQ(S_OK, - provider->GetCredentialCount(&count, &default_index, &autologon)); - ASSERT_EQ(1u, count); - EXPECT_EQ(CREDENTIAL_PROVIDER_NO_DEFAULT, default_index); - EXPECT_FALSE(autologon); - - // Get the anonymous credential. - CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, provider->GetCredentialAt(0, &cred)); - - ICredentialUpdateEventsHandler* update_handler = - static_cast<ICredentialUpdateEventsHandler*>( - static_cast<CGaiaCredentialProvider*>(provider.p)); + CComPtr<ICredentialUpdateEventsHandler> update_handler; + ASSERT_EQ(S_OK, provider.QueryInterface(&update_handler)); // Notify user auto logon first and then notify user access denied to ensure // that auto logon always has precedence over user access denied. @@ -351,18 +223,22 @@ } // Credential changed event should have been received. - EXPECT_TRUE(events.CredentialsChangedReceived()); - events.ResetCredentialsChangedReceived(); + EXPECT_TRUE(fake_provider_events()->CredentialsChangedReceived()); + fake_provider_events()->ResetCredentialsChangedReceived(); // Notify that user access is denied. This should not cause a credential // changed since an event was already processed. update_handler->UpdateCredentialsIfNeeded(true); // No credential changed should have been signalled here. - EXPECT_FALSE(events.CredentialsChangedReceived()); + EXPECT_FALSE(fake_provider_events()->CredentialsChangedReceived()); // GetCredentialCount should return back the same credential that was just // auto logged on. + DWORD count; + DWORD default_index; + BOOL autologon; + ASSERT_EQ(S_OK, provider->GetCredentialCount(&count, &default_index, &autologon)); ASSERT_EQ(1u, count); @@ -380,7 +256,7 @@ update_handler->UpdateCredentialsIfNeeded(false); // Credential changed event should have been received. - EXPECT_TRUE(events.CredentialsChangedReceived()); + EXPECT_TRUE(fake_provider_events()->CredentialsChangedReceived()); // GetCredentialCount should return new credentials with no auto logon. ASSERT_EQ(S_OK, @@ -398,9 +274,6 @@ } TEST_F(GcpCredentialProviderTest, AddPersonAfterUserRemove) { - FakeAssociatedUserValidator associated_user_validator; - FakeInternetAvailabilityChecker internet_checker; - // Set up such that MDM is enabled, mulit-users is not, and a user already // exists. ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmUrl, L"https://mdm.com")); @@ -410,39 +283,21 @@ const wchar_t kDummyUsername[] = L"username"; const wchar_t kDummyPassword[] = L"password"; CComBSTR sid; - CreateGCPWUser(kDummyUsername, L"foo@gmail.com", kDummyPassword, L"Full Name", - L"Comment", L"gaia-id", &sid); - - // Start token handle refresh threads. - associated_user_validator.StartRefreshingTokenHandleValidity(); + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + kDummyUsername, kDummyPassword, L"full name", L"comment", + L"gaia-id", L"foo@gmail.com", &sid)); { + CComPtr<ICredentialProviderCredential> cred; CComPtr<ICredentialProvider> provider; - ASSERT_EQ(S_OK, - CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance( - nullptr, IID_ICredentialProvider, (void**)&provider)); - ASSERT_EQ(S_OK, provider->SetUsageScenario(CPUS_LOGON, 0)); - - // Empty user array. - CComPtr<ICredentialProviderSetUserArray> user_array; - ASSERT_EQ(S_OK, provider.QueryInterface(&user_array)); - FakeCredentialProviderUserArray array; - ASSERT_EQ(S_OK, user_array->SetUserArray(&array)); - - // Activate the CP. - FakeCredentialProviderEvents events; - ASSERT_EQ(S_OK, provider->Advise(&events, 0)); + DWORD count = 0; + ASSERT_EQ(S_OK, InitializeProviderWithCredentials(&count, &provider)); // In this case no credential should be returned. - DWORD count; - DWORD default_index; - BOOL autologon; - ASSERT_EQ(S_OK, - provider->GetCredentialCount(&count, &default_index, &autologon)); ASSERT_EQ(0u, count); - // Deactivate the CP. - ASSERT_EQ(S_OK, provider->UnAdvise()); + // Release the CP so we can create another one. + ASSERT_EQ(S_OK, ReleaseProvider()); } // Delete the OS user. At this point, info in the HKLM registry about this @@ -453,47 +308,64 @@ { CComPtr<ICredentialProvider> provider; - ASSERT_EQ(S_OK, - CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance( - nullptr, IID_ICredentialProvider, (void**)&provider)); - ASSERT_EQ(S_OK, provider->SetUsageScenario(CPUS_LOGON, 0)); - - // Empty user array. - CComPtr<ICredentialProviderSetUserArray> user_array; - ASSERT_EQ(S_OK, provider.QueryInterface(&user_array)); - FakeCredentialProviderUserArray array; - ASSERT_EQ(S_OK, user_array->SetUserArray(&array)); - - // Activate the CP. - FakeCredentialProviderEvents events; - ASSERT_EQ(S_OK, provider->Advise(&events, 0)); + DWORD count = 0; + ASSERT_EQ(S_OK, InitializeProviderWithCredentials(&count, &provider)); // This time a credential should be returned. - DWORD count; - DWORD default_index; - BOOL autologon; - ASSERT_EQ(S_OK, - provider->GetCredentialCount(&count, &default_index, &autologon)); ASSERT_EQ(1u, count); - // Deactivate the CP. + // And this credential should be the anonymous one. + CComPtr<ICredentialProviderCredential> cred; + ASSERT_EQ(S_OK, provider->GetCredentialAt(0, &cred)); + + CComPtr<ICredentialProviderCredential2> cred2; + ASSERT_NE(S_OK, cred.QueryInterface(&cred2)); + + CComPtr<IReauthCredential> reauth_cred; + ASSERT_NE(S_OK, cred.QueryInterface(&reauth_cred)); + + // Release the CP. ASSERT_EQ(S_OK, provider->UnAdvise()); } } +class GcpCredentialProviderExecutionTest : public GlsRunnerTestBase {}; + +TEST_F(GcpCredentialProviderExecutionTest, UnAdviseDuringGls) { + USES_CONVERSION; + + CComPtr<ICredentialProviderCredential> cred; + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); + + CComPtr<ITestCredential> test; + ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + + // This event is merely used to keep the gls running while it is killed by + // Terminate(). + constexpr wchar_t kStartGlsEventName[] = L"UnAdviseDuringGls_Signal"; + base::win::ScopedHandle start_event_handle( + ::CreateEvent(nullptr, false, false, kStartGlsEventName)); + ASSERT_TRUE(start_event_handle.IsValid()); + ASSERT_EQ(S_OK, test->SetStartGlsEventName(kStartGlsEventName)); + base::WaitableEvent start_event(std::move(start_event_handle)); + + ASSERT_EQ(S_OK, StartLogonProcess(/*succeeds=*/true)); + + // Release the provider which should also Terminate the credential that + // was created. + ReleaseProvider(); +} + // Tests auto logon enabled when set serialization is called. // Parameters: // 1. bool: are the users' token handles still valid. // 2. CREDENTIAL_PROVIDER_USAGE_SCENARIO - the usage scenario. class GcpCredentialProviderSetSerializationTest : public GcpCredentialProviderTest, - public testing::WithParamInterface< + public ::testing::WithParamInterface< std::tuple<bool, CREDENTIAL_PROVIDER_USAGE_SCENARIO>> {}; TEST_P(GcpCredentialProviderSetSerializationTest, CheckAutoLogon) { - FakeAssociatedUserValidator associated_user_validator; - FakeInternetAvailabilityChecker internet_checker; - const bool valid_token_handles = std::get<0>(GetParam()); const CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus = std::get<1>(GetParam()); @@ -502,25 +374,15 @@ CComBSTR first_sid; constexpr wchar_t first_username[] = L"username"; - CreateGCPWUser(first_username, L"foo@gmail.com", L"password", L"Full Name", - L"Comment", L"gaia-id", &first_sid); + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + first_username, L"password", L"full name", L"comment", + L"gaia-id", L"foo@gmail.com", &first_sid)); CComBSTR second_sid; constexpr wchar_t second_username[] = L"username2"; - CreateGCPWUser(second_username, L"foo2@gmail.com", L"password", L"Full Name", - L"Comment", L"gaia-id2", &second_sid); - - // Token fetch result. - fake_http_url_fetcher_factory()->SetFakeResponse( - GURL(AssociatedUserValidator::kTokenInfoUrl), - FakeWinHttpUrlFetcher::Headers(), - valid_token_handles ? "{\"expires_in\":1}" : "{}"); - - // Start token handle refresh threads. - associated_user_validator.StartRefreshingTokenHandleValidity(); - - // Lock users as needed based on the validity of their token handles. - associated_user_validator.DenySigninForUsersWithInvalidTokenHandles(cpus); + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + second_username, L"password", L"Full Name", L"Comment", + L"gaia-id2", L"foo2@gmail.com", &second_sid)); // Build a dummy authentication buffer that can be passed to SetSerialization. CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; @@ -539,32 +401,18 @@ &dummy_domain[0], &dummy_username[0], &dummy_password[0], cpus, &cpcs)); + GetAuthenticationPackageId(&cpcs.ulAuthenticationPackage); cpcs.clsidCredentialProvider = CLSID_GaiaCredentialProvider; - CComPtr<ICredentialProviderSetUserArray> user_array; - ASSERT_EQ( - S_OK, - CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance( - nullptr, IID_ICredentialProviderSetUserArray, (void**)&user_array)); + CComPtr<ICredentialProviderCredential> cred; CComPtr<ICredentialProvider> provider; - ASSERT_EQ(S_OK, user_array.QueryInterface(&provider)); - - ASSERT_EQ(S_OK, provider->SetUsageScenario(cpus, 0)); - - ASSERT_EQ(S_OK, provider->SetSerialization(&cpcs)); + SetDefaultTokenHandleResponse(valid_token_handles + ? kDefaultValidTokenHandleResponse + : kDefaultInvalidTokenHandleResponse); + ASSERT_EQ(S_OK, InitializeProviderWithRemoteCredentials(&cpcs, &provider)); ::CoTaskMemFree(cpcs.rgbSerialization); - FakeCredentialProviderUserArray array; - array.AddUser(OLE2CW(first_sid), first_username); - array.AddUser(OLE2CW(second_sid), second_username); - - ASSERT_EQ(S_OK, user_array->SetUserArray(&array)); - - // Activate the CP. - FakeCredentialProviderEvents events; - ASSERT_EQ(S_OK, provider->Advise(&events, 0)); - // Check the correct number of credentials are created and whether autologon // is enabled based on the token handle validity. DWORD count; @@ -578,9 +426,6 @@ EXPECT_EQ(autologon, should_autologon); EXPECT_EQ(default_index, should_autologon ? 1 : CREDENTIAL_PROVIDER_NO_DEFAULT); - - // Deactivate the CP. - ASSERT_EQ(S_OK, provider->UnAdvise()); } INSTANTIATE_TEST_SUITE_P( @@ -599,12 +444,9 @@ // bool: whether an existing user exists. class GcpCredentialProviderMdmTest : public GcpCredentialProviderTest, - public testing::WithParamInterface<std::tuple<bool, int, bool>> {}; + public ::testing::WithParamInterface<std::tuple<bool, int, bool>> {}; TEST_P(GcpCredentialProviderMdmTest, Basic) { - FakeAssociatedUserValidator associated_user_validator; - FakeInternetAvailabilityChecker internet_checker; - const bool config_mdm_url = std::get<0>(GetParam()); const int supports_multi_users = std::get<1>(GetParam()); const bool user_exists = std::get<2>(GetParam()); @@ -626,40 +468,16 @@ if (user_exists) { CComBSTR sid; - CreateGCPWUser(L"username", L"foo@gmail.com", L"password", L"Full Name", - L"Comment", L"gaia-id", &sid); + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + L"username", L"password", L"full name", L"comment", + L"gaia-id", L"foo@gmail.com", &sid)); } - // Valid token fetch result. - fake_http_url_fetcher_factory()->SetFakeResponse( - GURL(AssociatedUserValidator::kTokenInfoUrl), - FakeWinHttpUrlFetcher::Headers(), "{\"expires_in\":1}"); - - associated_user_validator.StartRefreshingTokenHandleValidity(); - + CComPtr<ICredentialProviderCredential> cred; CComPtr<ICredentialProvider> provider; - ASSERT_EQ(S_OK, - CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance( - nullptr, IID_ICredentialProvider, (void**)&provider)); + DWORD count = 0; + ASSERT_EQ(S_OK, InitializeProviderWithCredentials(&count, &provider)); - // Start process for logon screen. - ASSERT_EQ(S_OK, provider->SetUsageScenario(CPUS_LOGON, 0)); - - // Empty user array. - CComPtr<ICredentialProviderSetUserArray> user_array; - ASSERT_EQ(S_OK, provider.QueryInterface(&user_array)); - FakeCredentialProviderUserArray array; - ASSERT_EQ(S_OK, user_array->SetUserArray(&array)); - - // Activate the CP. - FakeCredentialProviderEvents events; - ASSERT_EQ(S_OK, provider->Advise(&events, 0)); - - DWORD count; - DWORD default_index; - BOOL autologon; - ASSERT_EQ(S_OK, - provider->GetCredentialCount(&count, &default_index, &autologon)); ASSERT_EQ(expected_credential_count, count); // Deactivate the CP. @@ -668,9 +486,9 @@ INSTANTIATE_TEST_SUITE_P(GcpCredentialProviderMdmTest, GcpCredentialProviderMdmTest, - ::testing::Combine(testing::Bool(), - testing::Range(0, 3), - testing::Bool())); + ::testing::Combine(::testing::Bool(), + ::testing::Range(0, 3), + ::testing::Bool())); // Check that reauth credentials only exist when the token handle for the // associated user is no longer valid and internet is available. @@ -688,61 +506,32 @@ const bool has_token_handle = std::get<0>(GetParam()); const bool valid_token_handle = std::get<1>(GetParam()); const bool has_internet = std::get<2>(GetParam()); - FakeAssociatedUserValidator associated_user_validator; - FakeInternetAvailabilityChecker internet_checker( + fake_internet_checker()->SetHasInternetConnection( has_internet ? FakeInternetAvailabilityChecker::kHicForceYes : FakeInternetAvailabilityChecker::kHicForceNo); CComBSTR sid; - CreateGCPWUser(L"username", L"foo@gmail.com", L"password", L"Full Name", - L"Comment", L"gaia-id", &sid); + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + L"username", L"password", L"full name", L"comment", + L"gaia-id", L"foo@gmail.com", &sid)); if (!has_token_handle) ASSERT_EQ(S_OK, SetUserProperty((BSTR)sid, kUserTokenHandle, L"")); - // Token fetch result. - fake_http_url_fetcher_factory()->SetFakeResponse( - GURL(AssociatedUserValidator::kTokenInfoUrl), - FakeWinHttpUrlFetcher::Headers(), - valid_token_handle ? "{\"expires_in\":1}" : "{}"); - - // Start token handle refresh threads. - associated_user_validator.StartRefreshingTokenHandleValidity(); - - CComPtr<ICredentialProviderSetUserArray> user_array; - ASSERT_EQ( - S_OK, - CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance( - nullptr, IID_ICredentialProviderSetUserArray, (void**)&user_array)); - + CComPtr<ICredentialProviderCredential> cred; CComPtr<ICredentialProvider> provider; - ASSERT_EQ(S_OK, user_array.QueryInterface(&provider)); - - ASSERT_EQ(S_OK, provider->SetUsageScenario(CPUS_LOGON, 0)); - - FakeCredentialProviderUserArray array; - array.AddUser(OLE2CW(sid), L"username"); - ASSERT_EQ(S_OK, user_array->SetUserArray(&array)); - - // Activate the CP. - FakeCredentialProviderEvents events; - ASSERT_EQ(S_OK, provider->Advise(&events, 0)); + DWORD count = 0; + SetDefaultTokenHandleResponse(valid_token_handle + ? kDefaultValidTokenHandleResponse + : kDefaultInvalidTokenHandleResponse); + ASSERT_EQ(S_OK, InitializeProviderWithCredentials(&count, &provider)); bool should_reauth_user = has_internet && (!has_token_handle || !valid_token_handle); // Check if there is a IReauthCredential depending on the state of the token // handle. - DWORD count; - DWORD default_index; - BOOL autologon; - // There should always be the anonymous credential and potentially a reauth - // credential. - ASSERT_EQ(S_OK, - provider->GetCredentialCount(&count, &default_index, &autologon)); ASSERT_EQ(should_reauth_user ? 2u : 1u, count); - EXPECT_EQ(CREDENTIAL_PROVIDER_NO_DEFAULT, default_index); - EXPECT_FALSE(autologon); if (should_reauth_user) { CComPtr<ICredentialProviderCredential> cred; @@ -750,9 +539,6 @@ CComPtr<IReauthCredential> reauth; EXPECT_EQ(S_OK, cred.QueryInterface(&reauth)); } - - // Deactivate the CP. - ASSERT_EQ(S_OK, provider->UnAdvise()); } INSTANTIATE_TEST_SUITE_P(, @@ -788,9 +574,7 @@ } TEST_P(GcpCredentialProviderAvailableCredentialsTest, AvailableCredentials) { - FakeAssociatedUserValidator associated_user_validator; - FakeInternetAvailabilityChecker internet_checker; - FakeCredentialProviderUserArray array; + USES_CONVERSION; const bool valid_token_handles = std::get<0>(GetParam()); const CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus = std::get<1>(GetParam()); @@ -801,67 +585,33 @@ GoogleMdmEnrolledStatusForTesting forced_status(enrolled_to_mdm); if (other_user_tile_available) - array.SetAccountOptions(CPAO_EMPTY_LOCAL); + fake_user_array()->SetAccountOptions(CPAO_EMPTY_LOCAL); CComBSTR first_sid; constexpr wchar_t first_username[] = L"username"; - CreateGCPWUser(first_username, L"foo@gmail.com", L"password", L"Full Name", - L"Comment", L"gaia-id", &first_sid); + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + first_username, L"password", L"full name", L"comment", + L"gaia-id", L"foo@gmail.com", &first_sid)); CComBSTR second_sid; constexpr wchar_t second_username[] = L"username2"; - CreateGCPWUser(second_username, L"foo2@gmail.com", L"password", L"Full Name", - L"Comment", L"gaia-id2", &second_sid); + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + second_username, L"password", L"Full Name", L"Comment", + L"gaia-id2", L"foo2@gmail.com", &second_sid)); - // Token fetch result. - fake_http_url_fetcher_factory()->SetFakeResponse( - GURL(AssociatedUserValidator::kTokenInfoUrl), - FakeWinHttpUrlFetcher::Headers(), - valid_token_handles ? "{\"expires_in\":1}" : "{}"); + // Set the user locking the system. + SetSidLockingWorkstation(second_user_locking_system ? OLE2CW(second_sid) + : OLE2CW(first_sid)); - // Start token handle refresh threads. - associated_user_validator.StartRefreshingTokenHandleValidity(); - - // Lock users as needed based on the validity of their token handles. - associated_user_validator.DenySigninForUsersWithInvalidTokenHandles(cpus); - - CComPtr<ICredentialProviderSetUserArray> user_array; - ASSERT_EQ( - S_OK, - CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance( - nullptr, IID_ICredentialProviderSetUserArray, (void**)&user_array)); CComPtr<ICredentialProvider> provider; - ASSERT_EQ(S_OK, user_array.QueryInterface(&provider)); - - ASSERT_EQ(S_OK, provider->SetUsageScenario(cpus, 0)); - - // All users are shown if the usage is not for unlocking the workstation. - bool all_users_shown = cpus != CPUS_UNLOCK_WORKSTATION; - // If not all the users are shown, the user that locked the system is - // the only one that is in the user array (if the other user tile is - // not available). - if (all_users_shown || - (!second_user_locking_system && !other_user_tile_available)) { - array.AddUser(OLE2CW(first_sid), first_username); - } - if (all_users_shown || - (second_user_locking_system && !other_user_tile_available)) { - array.AddUser(OLE2CW(second_sid), second_username); - } - - ASSERT_EQ(S_OK, user_array->SetUserArray(&array)); - - // Activate the CP. - FakeCredentialProviderEvents events; - ASSERT_EQ(S_OK, provider->Advise(&events, 0)); + DWORD count = 0; + SetUsageScenario(cpus); + SetDefaultTokenHandleResponse(valid_token_handles + ? kDefaultValidTokenHandleResponse + : kDefaultInvalidTokenHandleResponse); + ASSERT_EQ(S_OK, InitializeProviderWithCredentials(&count, &provider)); // Check the correct number of credentials are created. - DWORD count; - DWORD default_index; - BOOL autologon; - ASSERT_EQ(S_OK, - provider->GetCredentialCount(&count, &default_index, &autologon)); - DWORD expected_credentials = 0; if (cpus != CPUS_UNLOCK_WORKSTATION) { expected_credentials = valid_token_handles && enrolled_to_mdm ? 0 : 2; @@ -876,14 +626,10 @@ } ASSERT_EQ(expected_credentials, count); - EXPECT_EQ(CREDENTIAL_PROVIDER_NO_DEFAULT, default_index); - EXPECT_FALSE(autologon); - if (expected_credentials == 0) { - // Deactivate the CP. - ASSERT_EQ(S_OK, provider->UnAdvise()); + // No credentials to verify. + if (expected_credentials == 0) return; - } CComPtr<ICredentialProviderCredential> cred; CComPtr<ICredentialProviderCredential2> cred2; @@ -942,9 +688,6 @@ EXPECT_EQ(guid_string, base::string16(guid_in_registry)); ::CoTaskMemFree(sid); } - - // Deactivate the CP. - ASSERT_EQ(S_OK, provider->UnAdvise()); } INSTANTIATE_TEST_SUITE_P( @@ -956,4 +699,6 @@ ::testing::Bool(), ::testing::Bool())); +} // namespace testing + } // namespace credential_provider
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_unittests.cc index a89c408..51fa2c8 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_unittests.cc +++ b/chrome/credential_provider/gaiacp/gaia_credential_unittests.cc
@@ -25,29 +25,15 @@ namespace testing { -namespace { - -HRESULT CreateGaiaCredentialWithProvider( - IGaiaCredentialProvider* provider, - IGaiaCredential** gaia_credential, - ICredentialProviderCredential** credential) { - return CreateBaseInheritedCredentialWithProvider<CGaiaCredential>( - provider, gaia_credential, credential); -} - -} // namespace - class GcpGaiaCredentialTest : public GlsRunnerTestBase { protected: GcpGaiaCredentialTest(); - FakeGaiaCredentialProvider* provider() { return &provider_; } BSTR signin_result() { return signin_result_; } CComBSTR MakeSigninResult(const std::string& password); private: - FakeGaiaCredentialProvider provider_; CComBSTR signin_result_; }; @@ -69,28 +55,36 @@ TEST_F(GcpGaiaCredentialTest, OnUserAuthenticated) { USES_CONVERSION; - CComPtr<IGaiaCredential> gaia_cred; CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, - CreateGaiaCredentialWithProvider(provider(), &gaia_cred, &cred)); + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); + + CComPtr<IGaiaCredential> gaia_cred; + ASSERT_EQ(S_OK, cred.QueryInterface(&gaia_cred)); CComBSTR error; ASSERT_EQ(S_OK, gaia_cred->OnUserAuthenticated(signin_result(), &error)); - EXPECT_TRUE(provider()->credentials_changed_fired()); + CComPtr<ITestCredentialProvider> test_provider; + ASSERT_EQ(S_OK, created_provider().QueryInterface(&test_provider)); + EXPECT_TRUE(test_provider->credentials_changed_fired()); } TEST_F(GcpGaiaCredentialTest, OnUserAuthenticated_SamePassword) { USES_CONVERSION; - CComPtr<IGaiaCredential> gaia_cred; CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, - CreateGaiaCredentialWithProvider(provider(), &gaia_cred, &cred)); + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); + + CComPtr<IGaiaCredential> gaia_cred; + ASSERT_EQ(S_OK, cred.QueryInterface(&gaia_cred)); CComBSTR error; ASSERT_EQ(S_OK, gaia_cred->OnUserAuthenticated(signin_result(), &error)); - CComBSTR first_sid = provider()->sid(); + CComPtr<ITestCredentialProvider> test_provider; + ASSERT_EQ(S_OK, created_provider().QueryInterface(&test_provider)); + CComBSTR first_sid = test_provider->sid(); // Report to register the user. wchar_t* report_status_text = nullptr; @@ -100,8 +94,9 @@ // Finishing with the same username+password should succeed. CComBSTR error2; ASSERT_EQ(S_OK, gaia_cred->OnUserAuthenticated(signin_result(), &error2)); - EXPECT_TRUE(provider()->credentials_changed_fired()); - EXPECT_EQ(first_sid, provider()->sid()); + + EXPECT_TRUE(test_provider->credentials_changed_fired()); + EXPECT_EQ(first_sid, test_provider->sid()); } TEST_F(GcpGaiaCredentialTest, OnUserAuthenticated_DiffPassword) { @@ -120,23 +115,28 @@ base::UTF8ToUTF16(test_data_storage.GetSuccessId()).c_str(), base::UTF8ToUTF16(test_data_storage.GetSuccessEmail()).c_str(), &sid)); - CComPtr<IGaiaCredential> cred; - ASSERT_EQ(S_OK, CComCreator<CComObject<CGaiaCredential>>::CreateInstance( - nullptr, IID_IGaiaCredential, (void**)&cred)); - ASSERT_EQ(S_OK, cred->Initialize(provider())); + CComPtr<ICredentialProviderCredential> cred; + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); + + CComPtr<IGaiaCredential> gaia_cred; + ASSERT_EQ(S_OK, cred.QueryInterface(&gaia_cred)); CComBSTR error; - ASSERT_EQ(S_OK, cred->OnUserAuthenticated(signin_result(), &error)); - EXPECT_TRUE(provider()->credentials_changed_fired()); + ASSERT_EQ(S_OK, gaia_cred->OnUserAuthenticated(signin_result(), &error)); - provider()->ResetCredentialsChangedFired(); + CComPtr<ITestCredentialProvider> test_provider; + ASSERT_EQ(S_OK, created_provider().QueryInterface(&test_provider)); + EXPECT_TRUE(test_provider->credentials_changed_fired()); + + test_provider->ResetCredentialsChangedFired(); CComBSTR new_signin_result = MakeSigninResult("password2"); // Finishing with the same username but different password should mark // the password as stale and not fire the credentials changed event. - EXPECT_EQ(S_FALSE, cred->OnUserAuthenticated(new_signin_result, &error)); - EXPECT_FALSE(provider()->credentials_changed_fired()); + EXPECT_EQ(S_FALSE, gaia_cred->OnUserAuthenticated(new_signin_result, &error)); + EXPECT_FALSE(test_provider->credentials_changed_fired()); } class GcpGaiaCredentialGlsRunnerTest : public GlsRunnerTestBase {}; @@ -155,34 +155,29 @@ base_gaia_id, base::string16(), &sid)); ASSERT_EQ(2u, fake_os_user_manager()->GetUserCount()); - FakeGaiaCredentialProvider provider; // Start logon. - CComPtr<IGaiaCredential> gaia_cred; CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, - CreateGaiaCredentialWithProvider(&provider, &gaia_cred, &cred)); + + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); + + CComPtr<IGaiaCredential> gaia_cred; + ASSERT_EQ(S_OK, cred.QueryInterface(&gaia_cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); ASSERT_EQ(S_OK, test->SetGlsEmailAddress(base::UTF16ToUTF8(base_username) + "@gmail.com")); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); // New username should be truncated at the end and have the last character // replaced with a new index EXPECT_STREQ((base_username.substr(0, base_username.size() - 1) + base::NumberToString16(kInitialDuplicateUsernameIndex)) .c_str(), - provider.username()); - EXPECT_NE(0u, provider.password().Length()); - EXPECT_NE(0u, provider.sid().Length()); - EXPECT_STREQ(test->GetErrorText(), nullptr); - EXPECT_EQ(TRUE, provider.credentials_changed_fired()); + test->GetFinalUsername()); // New user should be created. EXPECT_EQ(3u, fake_os_user_manager()->GetUserCount()); - - EXPECT_EQ(S_OK, gaia_cred->Terminate()); } // This test checks the expected success / failure of user creation when @@ -230,45 +225,38 @@ ASSERT_EQ(static_cast<size_t>(1 + last_user_index + 1), fake_os_user_manager()->GetUserCount()); - FakeGaiaCredentialProvider provider; - // Start logon. - CComPtr<IGaiaCredential> gaia_cred; + // Create provider. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, - CreateGaiaCredentialWithProvider(&provider, &gaia_cred, &cred)); + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred)); + + CComPtr<IGaiaCredential> gaia_cred; + ASSERT_EQ(S_OK, cred.QueryInterface(&gaia_cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + // Start logon. + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); if (should_succeed) { EXPECT_STREQ( (base_username + base::NumberToString16(last_user_index + kInitialDuplicateUsernameIndex)) .c_str(), - provider.username()); - EXPECT_NE(0u, provider.password().Length()); - EXPECT_NE(0u, provider.sid().Length()); - EXPECT_STREQ(test->GetErrorText(), nullptr); - EXPECT_EQ(TRUE, provider.credentials_changed_fired()); + OLE2CW(test->GetFinalUsername())); // New user should be created. EXPECT_EQ(static_cast<size_t>(last_user_index + 2 + 1), fake_os_user_manager()->GetUserCount()); + + ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); + } else { - EXPECT_EQ(0u, provider.username().Length()); - EXPECT_EQ(0u, provider.password().Length()); - EXPECT_EQ(0u, provider.sid().Length()); - EXPECT_STREQ(test->GetErrorText(), - GetStringResource(IDS_INTERNAL_ERROR_BASE).c_str()); - EXPECT_EQ(FALSE, provider.credentials_changed_fired()); // No new user should be created. EXPECT_EQ(static_cast<size_t>(last_user_index + 1 + 1), fake_os_user_manager()->GetUserCount()); + ASSERT_EQ(S_OK, FinishLogonProcess(false, false, IDS_INTERNAL_ERROR_BASE)); } - // Expect a different user name with the suffix added. - EXPECT_EQ(S_OK, gaia_cred->Terminate()); } // For a max retry of 10, it is possible to create users 'username',
diff --git a/chrome/credential_provider/gaiacp/gcp_utils.cc b/chrome/credential_provider/gaiacp/gcp_utils.cc index bdd4aad..43836a5 100644 --- a/chrome/credential_provider/gaiacp/gcp_utils.cc +++ b/chrome/credential_provider/gaiacp/gcp_utils.cc
@@ -578,6 +578,62 @@ return hr; } +HRESULT LookupLocalizedNameBySid(PSID sid, base::string16* localized_name) { + DCHECK(localized_name); + std::vector<wchar_t> localized_name_buffer; + DWORD group_name_size = 0; + std::vector<wchar_t> domain_buffer; + DWORD domain_size = 0; + SID_NAME_USE use; + + // Get the localized name of the local users group. The function + // NetLocalGroupAddMembers only accepts the name of the group and it + // may be localized on the system. + if (!::LookupAccountSidW(nullptr, sid, nullptr, &group_name_size, nullptr, + &domain_size, &use)) { + if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + HRESULT hr = HRESULT_FROM_WIN32(::GetLastError()); + LOGFN(ERROR) << "LookupAccountSidW hr=" << putHR(hr); + return hr; + } + + localized_name_buffer.resize(group_name_size); + domain_buffer.resize(domain_size); + if (!::LookupAccountSidW(nullptr, sid, localized_name_buffer.data(), + &group_name_size, domain_buffer.data(), + &domain_size, &use)) { + HRESULT hr = HRESULT_FROM_WIN32(::GetLastError()); + LOGFN(ERROR) << "LookupAccountSidW hr=" << putHR(hr); + return hr; + } + } + + if (localized_name_buffer.empty()) { + LOGFN(ERROR) << "Empty localized name"; + return E_UNEXPECTED; + } + *localized_name = base::string16(localized_name_buffer.data(), + localized_name_buffer.size() - 1); + + return S_OK; +} + +HRESULT LookupLocalizedNameForWellKnownSid(WELL_KNOWN_SID_TYPE sid_type, + base::string16* localized_name) { + BYTE well_known_sid[SECURITY_MAX_SID_SIZE]; + DWORD size_local_users_group_sid = base::size(well_known_sid); + + // Get the sid for the well known local users group. + if (!::CreateWellKnownSid(sid_type, nullptr, well_known_sid, + &size_local_users_group_sid)) { + HRESULT hr = HRESULT_FROM_WIN32(::GetLastError()); + LOGFN(ERROR) << "CreateWellKnownSid hr=" << putHR(hr); + return hr; + } + + return LookupLocalizedNameBySid(well_known_sid, localized_name); +} + bool VerifyStartupSentinel() { // Always try to write to the startup sentinel file. If writing or opening // fails for any reason (file locked, no access etc) consider this a failure.
diff --git a/chrome/credential_provider/gaiacp/gcp_utils.h b/chrome/credential_provider/gaiacp/gcp_utils.h index cc784da4..dd32b01 100644 --- a/chrome/credential_provider/gaiacp/gcp_utils.h +++ b/chrome/credential_provider/gaiacp/gcp_utils.h
@@ -196,6 +196,15 @@ const wchar_t* entrypoint, base::CommandLine* command_line); +// Looks up the name associated to the |sid| (if any). Returns an error on any +// failure or no name is associated with the |sid|. +HRESULT LookupLocalizedNameBySid(PSID sid, base::string16* localized_name); + +// Looks up the name associated to the well known |sid_type| (if any). Returns +// an error on any failure or no name is associated with the |sid_type|. +HRESULT LookupLocalizedNameForWellKnownSid(WELL_KNOWN_SID_TYPE sid_type, + base::string16* localized_name); + // Handles the writing and deletion of a startup sentinel file used to ensure // that the GCPW does not crash continuously on startup and render the // winlogon process unusable.
diff --git a/chrome/credential_provider/gaiacp/os_user_manager.cc b/chrome/credential_provider/gaiacp/os_user_manager.cc index 541f039..0d2ce8c 100644 --- a/chrome/credential_provider/gaiacp/os_user_manager.cc +++ b/chrome/credential_provider/gaiacp/os_user_manager.cc
@@ -234,6 +234,18 @@ DWORD* error) { DCHECK(sid); + base::string16 local_users_group_name; + // If adding to the local users group, make sure we can get the localized + // name for the group before proceeding. + if (add_to_users_group) { + HRESULT hr = LookupLocalizedNameForWellKnownSid(WinBuiltinUsersSid, + &local_users_group_name); + if (FAILED(hr)) { + LOGFN(ERROR) << "LookupLocalizedNameForWellKnownSid hr=" << putHR(hr); + return hr; + } + } + USER_INFO_1 info; memset(&info, 0, sizeof(info)); info.usri1_comment = _wcsdup(comment); @@ -279,12 +291,14 @@ } if (nsts == NERR_Success && add_to_users_group) { - // Add to the "Users" group so that it appears on login screen. + // Add to the well known local users group so that it appears on login + // screen. LOCALGROUP_MEMBERS_INFO_0 member_info; memset(&member_info, 0, sizeof(member_info)); member_info.lgrmi0_sid = user_info->usri4_user_sid; - nsts = ::NetLocalGroupAddMembers( - nullptr, L"Users", 0, reinterpret_cast<LPBYTE>(&member_info), 1); + nsts = + ::NetLocalGroupAddMembers(nullptr, local_users_group_name.c_str(), 0, + reinterpret_cast<LPBYTE>(&member_info), 1); if (nsts != NERR_Success && nsts != ERROR_MEMBER_IN_ALIAS) { LOGFN(ERROR) << "NetLocalGroupAddMembers nsts=" << nsts; } else {
diff --git a/chrome/credential_provider/gaiacp/reauth_credential_unittests.cc b/chrome/credential_provider/gaiacp/reauth_credential_unittests.cc index 7bbd799..f1a2767c 100644 --- a/chrome/credential_provider/gaiacp/reauth_credential_unittests.cc +++ b/chrome/credential_provider/gaiacp/reauth_credential_unittests.cc
@@ -25,19 +25,6 @@ namespace testing { -namespace { - -HRESULT CreateReauthCredentialWithProvider( - IGaiaCredentialProvider* provider, - IGaiaCredential** gaia_credential, - ICredentialProviderCredential** credential) { - return CreateInheritedCredentialWithProvider<CReauthCredential, - IReauthCredential>( - provider, gaia_credential, credential); -} - -} // namespace - class GcpReauthCredentialTest : public ::testing::Test { protected: FakeOSUserManager* fake_os_user_manager() { return &fake_os_user_manager_; } @@ -80,7 +67,9 @@ ::CoTaskMemFree(sid); } -TEST_F(GcpReauthCredentialTest, UserGaiaIdMismatch) { +class GcpReauthCredentialGlsRunnerTest : public GlsRunnerTestBase {}; + +TEST_F(GcpReauthCredentialGlsRunnerTest, UserGaiaIdMismatch) { USES_CONVERSION; CredentialProviderSigninDialogTestDataStorage test_data_storage; @@ -96,10 +85,6 @@ base::JSONWriter::Write(unexpected_full_result, &signin_result_utf8)); CComBSTR unexpected_signin_result = A2COLE(signin_result_utf8.c_str()); - // Create a fake credential provider. This object must outlive the reauth - // credential so it should be declared first. - FakeGaiaCredentialProvider provider; - CComBSTR username = L"foo_bar"; CComBSTR full_name = A2COLE(test_data_storage.GetSuccessFullName().c_str()); CComBSTR password = A2COLE(test_data_storage.GetSuccessPassword().c_str()); @@ -120,43 +105,31 @@ base::UTF8ToUTF16(unexpected_gaia_id), base::string16(), &second_sid)); - // Initialize a reauth credential for the valid gaia id. - CComPtr<IReauthCredential> reauth; - ASSERT_EQ(S_OK, CComCreator<CComObject<CReauthCredential>>::CreateInstance( - nullptr, IID_IReauthCredential, (void**)&reauth)); + // Create provider and start logon. + CComPtr<ICredentialProviderCredential> cred; - CComPtr<IGaiaCredential> gaia_cred; - gaia_cred = reauth; - ASSERT_TRUE(!!gaia_cred); + // Create with invalid token handle response so that a reauth occurs. + SetDefaultTokenHandleResponse(kDefaultInvalidTokenHandleResponse); + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(1, &cred)); - ASSERT_EQ(S_OK, gaia_cred->Initialize(&provider)); + CComPtr<ITestCredential> test; + ASSERT_EQ(S_OK, cred.QueryInterface(&test)); - ASSERT_EQ(S_OK, - reauth->SetOSUserInfo( - first_sid, CComBSTR(OSUserManager::GetLocalDomain().c_str()), - username)); - ASSERT_EQ(S_OK, reauth->SetEmailForReauth(email)); + // Force the GLS to return an invalid Gaia Id without reporting the usual + // kUiecEMailMissmatch exit code when this happens. This will test whether + // the credential can perform necessary validation in case the GLS ever + // does not do the validation for us. + test->SetGaiaIdOverride(unexpected_gaia_id, /*ignore_expected_gaia_id=*/true); - // Finishing reauth with an unexpected gaia id should fail. - CComBSTR error2; - ASSERT_NE(S_OK, - gaia_cred->OnUserAuthenticated(unexpected_signin_result, &error2)); - - ASSERT_EQ(S_OK, gaia_cred->Terminate()); - - EXPECT_EQ(0u, provider.username().Length()); - EXPECT_EQ(0u, provider.password().Length()); - EXPECT_EQ(0u, provider.sid().Length()); - ASSERT_STREQ((BSTR)error2, - GetStringResource(IDS_ACCOUNT_IN_USE_BASE).c_str()); - EXPECT_EQ(FALSE, provider.credentials_changed_fired()); + // The logon should have failed with an error about another user already + // associated to this Google account. + ASSERT_EQ(S_OK, FinishLogonProcess(false, false, IDS_ACCOUNT_IN_USE_BASE)); } -class GcpReauthCredentialGlsRunnerTest : public GlsRunnerTestBase {}; - TEST_F(GcpReauthCredentialGlsRunnerTest, NormalReauth) { USES_CONVERSION; CredentialProviderSigninDialogTestDataStorage test_data_storage; + CComBSTR username = L"foo_bar"; CComBSTR full_name = A2COLE(test_data_storage.GetSuccessFullName().c_str()); CComBSTR password = A2COLE(test_data_storage.GetSuccessPassword().c_str()); @@ -170,48 +143,33 @@ L"comment", base::UTF8ToUTF16(test_data_storage.GetSuccessId()), OLE2CW(email), &sid)); - FakeGaiaCredentialProvider provider; - - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, - CreateReauthCredentialWithProvider(&provider, &gaia_cred, &cred)); - CComPtr<IReauthCredential> reauth; - reauth = cred; - ASSERT_TRUE(!!reauth); - - ASSERT_EQ(S_OK, reauth->SetOSUserInfo( - sid, CComBSTR(OSUserManager::GetLocalDomain().c_str()), - username)); - ASSERT_EQ(S_OK, reauth->SetEmailForReauth(email)); + // Create with invalid token handle response so that a reauth occurs. + SetDefaultTokenHandleResponse(kDefaultInvalidTokenHandleResponse); + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(1, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + ASSERT_EQ(S_OK, test->SetGlsEmailAddress(std::string())); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); - ASSERT_EQ(S_OK, gaia_cred->Terminate()); - - // Check that values were propagated to the provider. - EXPECT_EQ(username, provider.username()); - EXPECT_EQ(password, provider.password()); - EXPECT_EQ(sid, provider.sid()); - EXPECT_EQ(TRUE, provider.credentials_changed_fired()); - - ASSERT_STREQ(test->GetErrorText(), NULL); + // Teardown of the test should confirm that the logon was successful. } TEST_F(GcpReauthCredentialGlsRunnerTest, NormalReauthWithoutEmail) { USES_CONVERSION; CredentialProviderSigninDialogTestDataStorage test_data_storage; + CComBSTR username = L"foo_bar"; CComBSTR full_name = A2COLE(test_data_storage.GetSuccessFullName().c_str()); CComBSTR password = A2COLE(test_data_storage.GetSuccessPassword().c_str()); CComBSTR email = A2COLE(test_data_storage.GetSuccessEmail().c_str()); - // Create a fake user to reauth. + // Create a fake user to reauth with no e-mail specified. CComBSTR sid; ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( @@ -219,86 +177,28 @@ L"comment", base::UTF8ToUTF16(test_data_storage.GetSuccessId()), base::string16(), &sid)); - FakeGaiaCredentialProvider provider; - - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, - CreateReauthCredentialWithProvider(&provider, &gaia_cred, &cred)); - CComPtr<IReauthCredential> reauth; - reauth = cred; - ASSERT_TRUE(!!reauth); - - ASSERT_EQ(S_OK, reauth->SetOSUserInfo( - sid, CComBSTR(OSUserManager::GetLocalDomain().c_str()), - username)); + // Create with invalid token handle response so that a reauth occurs. + SetDefaultTokenHandleResponse(kDefaultInvalidTokenHandleResponse); + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(1, &cred)); CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); // Email associated should be the default one EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); - ASSERT_EQ(S_OK, gaia_cred->Terminate()); - - // Check that values were propagated to the provider. - EXPECT_EQ(username, provider.username()); - EXPECT_EQ(password, provider.password()); - EXPECT_EQ(sid, provider.sid()); - EXPECT_EQ(TRUE, provider.credentials_changed_fired()); - - ASSERT_STREQ(test->GetErrorText(), NULL); -} - -TEST_F(GcpReauthCredentialGlsRunnerTest, NoGaiaIdAssociatedToCredential) { - USES_CONVERSION; - CredentialProviderSigninDialogTestDataStorage test_data_storage; - CComBSTR username = L"foo_bar"; - CComBSTR full_name = A2COLE(test_data_storage.GetSuccessFullName().c_str()); - CComBSTR password = A2COLE(test_data_storage.GetSuccessPassword().c_str()); - CComBSTR email = A2COLE(test_data_storage.GetSuccessEmail().c_str()); - - // Create a fake user to reauth. - CComBSTR sid; - ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( - OLE2CW(username), OLE2CW(password), OLE2CW(full_name), - L"comment", base::string16(), base::string16(), &sid)); - - FakeGaiaCredentialProvider provider; - - CComPtr<IGaiaCredential> gaia_cred; - CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, - CreateReauthCredentialWithProvider(&provider, &gaia_cred, &cred)); - - CComPtr<IReauthCredential> reauth; - reauth = cred; - ASSERT_TRUE(!!reauth); - - ASSERT_EQ(S_OK, reauth->SetOSUserInfo( - sid, CComBSTR(OSUserManager::GetLocalDomain().c_str()), - username)); - ASSERT_EQ(S_OK, reauth->SetEmailForReauth(email)); - - CComPtr<ITestCredential> test; - ASSERT_EQ(S_OK, cred.QueryInterface(&test)); - ASSERT_EQ(S_OK, test->SetGlsEmailAddress(std::string())); - - // This call should fail - CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; - CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; - wchar_t* status_text; - CREDENTIAL_PROVIDER_STATUS_ICON status_icon; - EXPECT_EQ(E_UNEXPECTED, - cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); + // Teardown of the test should confirm that the logon was successful. } TEST_F(GcpReauthCredentialGlsRunnerTest, GaiaIdMismatch) { USES_CONVERSION; CredentialProviderSigninDialogTestDataStorage test_data_storage; + CComBSTR username = L"foo_bar"; CComBSTR full_name = A2COLE(test_data_storage.GetSuccessFullName().c_str()); CComBSTR password = A2COLE(test_data_storage.GetSuccessPassword().c_str()); @@ -310,42 +210,28 @@ fake_os_user_manager()->CreateTestOSUser( OLE2CW(username), OLE2CW(password), OLE2CW(full_name), L"comment", base::UTF8ToUTF16(test_data_storage.GetSuccessId()), - base::string16(), &sid)); + OLE2CW(email), &sid)); std::string unexpected_gaia_id = "unexpected-gaia-id"; - FakeGaiaCredentialProvider provider; - CComPtr<IGaiaCredential> gaia_cred; + // Create provider and start logon. CComPtr<ICredentialProviderCredential> cred; - ASSERT_EQ(S_OK, - CreateReauthCredentialWithProvider(&provider, &gaia_cred, &cred)); - CComPtr<IReauthCredential> reauth; - reauth = cred; - ASSERT_TRUE(!!reauth); + // Create with invalid token handle response so that a reauth occurs. + SetDefaultTokenHandleResponse(kDefaultInvalidTokenHandleResponse); + ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(1, &cred)); - ASSERT_EQ(S_OK, reauth->SetOSUserInfo( - sid, CComBSTR(OSUserManager::GetLocalDomain().c_str()), - username)); - ASSERT_EQ(S_OK, reauth->SetEmailForReauth(email)); - - CComPtr<testing::ITestCredential> test; + CComPtr<ITestCredential> test; ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + ASSERT_EQ(S_OK, test->SetGlsEmailAddress(std::string())); - ASSERT_EQ(S_OK, test->SetGaiaIdOverride(unexpected_gaia_id)); + ASSERT_EQ(S_OK, test->SetGaiaIdOverride(unexpected_gaia_id, + /*ignore_expected_gaia_id=*/false)); - ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + ASSERT_EQ(S_OK, StartLogonProcessAndWait()); - ASSERT_EQ(S_OK, gaia_cred->Terminate()); - - // Check that values were not propagated to the provider. - EXPECT_EQ(0u, provider.username().Length()); - EXPECT_EQ(0u, provider.password().Length()); - EXPECT_EQ(0u, provider.sid().Length()); - EXPECT_EQ(FALSE, provider.credentials_changed_fired()); - - ASSERT_STREQ(test->GetErrorText(), - GetStringResource(IDS_EMAIL_MISMATCH_BASE).c_str()); + // The logon should have failed with an email mismatch error. + ASSERT_EQ(S_OK, FinishLogonProcess(false, false, IDS_EMAIL_MISMATCH_BASE)); } } // namespace testing
diff --git a/chrome/credential_provider/test/BUILD.gn b/chrome/credential_provider/test/BUILD.gn index 8cf6fe0..f6dfac8 100644 --- a/chrome/credential_provider/test/BUILD.gn +++ b/chrome/credential_provider/test/BUILD.gn
@@ -14,8 +14,6 @@ "../gaiacp/reauth_credential_unittests.cc", "com_fakes.cc", "com_fakes.h", - "fake_gls_run_helper.cc", - "fake_gls_run_helper.h", "gcp_fakes.cc", "gcp_fakes.h", "gcp_gls_output_unittest.cc", @@ -24,6 +22,7 @@ "gls_runner_test_base.cc", "gls_runner_test_base.h", "test_credential.h", + "test_credential_provider.h", ] deps = [
diff --git a/chrome/credential_provider/test/com_fakes.cc b/chrome/credential_provider/test/com_fakes.cc index 80fe137f..b98631c8 100644 --- a/chrome/credential_provider/test/com_fakes.cc +++ b/chrome/credential_provider/test/com_fakes.cc
@@ -7,13 +7,63 @@ #include <sddl.h> // For ConvertSidToStringSid() #include "base/logging.h" +#include "chrome/credential_provider/gaiacp/gaia_credential.h" +#include "chrome/credential_provider/gaiacp/gaia_credential_other_user.h" #include "chrome/credential_provider/gaiacp/os_user_manager.h" +#include "chrome/credential_provider/gaiacp/reauth_credential.h" #include "chrome/credential_provider/gaiacp/stdafx.h" #include "chrome/credential_provider/test/test_credential.h" #include "testing/gtest/include/gtest/gtest.h" namespace credential_provider { +namespace testing { + +// This class is used to implement a test credential based off a +// CGaiaCredential. +class ATL_NO_VTABLE CTestGaiaCredential + : public CTestCredentialBase<CGaiaCredential> { + public: + DECLARE_NO_REGISTRY() + + CTestGaiaCredential(); + ~CTestGaiaCredential(); + + private: + BEGIN_COM_MAP(CTestGaiaCredential) + COM_INTERFACE_ENTRY(IGaiaCredential) + COM_INTERFACE_ENTRY(ICredentialProviderCredential) + COM_INTERFACE_ENTRY(ITestCredential) + END_COM_MAP() +}; + +CTestGaiaCredential::CTestGaiaCredential() = default; + +CTestGaiaCredential::~CTestGaiaCredential() = default; + +// This class is used to implement a test credential based off a +// COtherUserGaiaCredential. +class ATL_NO_VTABLE CTestOtherUserGaiaCredential + : public CTestCredentialBase<COtherUserGaiaCredential> { + public: + DECLARE_NO_REGISTRY() + + CTestOtherUserGaiaCredential(); + ~CTestOtherUserGaiaCredential(); + + private: + BEGIN_COM_MAP(CTestOtherUserGaiaCredential) + COM_INTERFACE_ENTRY(IGaiaCredential) + COM_INTERFACE_ENTRY(ICredentialProviderCredential) + COM_INTERFACE_ENTRY(ICredentialProviderCredential2) + COM_INTERFACE_ENTRY(ITestCredential) + END_COM_MAP() +}; + +CTestOtherUserGaiaCredential::CTestOtherUserGaiaCredential() = default; + +CTestOtherUserGaiaCredential::~CTestOtherUserGaiaCredential() = default; + #define IMPL_IUNKOWN_NOQI_WITH_REF(cls) \ IFACEMETHODIMP cls::QueryInterface(REFIID riid, void** ppv) { \ return E_NOTIMPL; \ @@ -34,21 +84,19 @@ EXPECT_EQ(ref_count_, 1u); } -HRESULT STDMETHODCALLTYPE FakeCredentialProviderUser::GetSid(wchar_t** sid) { +HRESULT FakeCredentialProviderUser::GetSid(wchar_t** sid) { DWORD length = sid_.length() + 1; *sid = static_cast<wchar_t*>(::CoTaskMemAlloc(length * sizeof(wchar_t))); EXPECT_EQ(0, wcscpy_s(*sid, length, sid_.c_str())); return S_OK; } -HRESULT STDMETHODCALLTYPE -FakeCredentialProviderUser::GetProviderID(GUID* providerID) { +HRESULT FakeCredentialProviderUser::GetProviderID(GUID* providerID) { return E_NOTIMPL; } -HRESULT STDMETHODCALLTYPE -FakeCredentialProviderUser::GetStringValue(REFPROPERTYKEY key, - wchar_t** value) { +HRESULT FakeCredentialProviderUser::GetStringValue(REFPROPERTYKEY key, + wchar_t** value) { if (key != PKEY_Identity_UserName) return E_INVALIDARG; @@ -58,8 +106,8 @@ return S_OK; } -HRESULT STDMETHODCALLTYPE -FakeCredentialProviderUser::GetValue(REFPROPERTYKEY key, PROPVARIANT* value) { +HRESULT FakeCredentialProviderUser::GetValue(REFPROPERTYKEY key, + PROPVARIANT* value) { return E_NOTIMPL; } @@ -118,19 +166,55 @@ /////////////////////////////////////////////////////////////////////////////// -FakeGaiaCredentialProvider::FakeGaiaCredentialProvider() {} - -FakeGaiaCredentialProvider::~FakeGaiaCredentialProvider() { - EXPECT_EQ(ref_count_, 1u); +CTestGaiaCredentialProvider::CTestGaiaCredentialProvider() { + // Set functions for creating test credentials of all types. + SetCredentialCreatorFunctionsForTesting( + [](CGaiaCredentialProvider::GaiaCredentialComPtrStorage* + cred_ptr_storage) { + return CComCreator<CComObject<CTestGaiaCredential>>::CreateInstance( + nullptr, IID_IGaiaCredential, + reinterpret_cast<void**>(&cred_ptr_storage->gaia_cred)); + }, + [](CGaiaCredentialProvider::GaiaCredentialComPtrStorage* + cred_ptr_storage) { + return CComCreator<CComObject<CTestOtherUserGaiaCredential>>:: + CreateInstance( + nullptr, IID_IGaiaCredential, + reinterpret_cast<void**>(&cred_ptr_storage->gaia_cred)); + }, + [](CGaiaCredentialProvider::GaiaCredentialComPtrStorage* + cred_ptr_storage) { + return CComCreator<CComObject<testing::CTestCredentialForInherited< + CReauthCredential, IReauthCredential>>>:: + CreateInstance( + nullptr, IID_IGaiaCredential, + reinterpret_cast<void**>(&cred_ptr_storage->gaia_cred)); + }); } -HRESULT FakeGaiaCredentialProvider::GetUsageScenario(DWORD* cpus) { - DCHECK(cpus); - *cpus = static_cast<DWORD>(cpus_); - return S_OK; +CTestGaiaCredentialProvider::~CTestGaiaCredentialProvider() {} + +const CComBSTR& CTestGaiaCredentialProvider::username() const { + return username_; } -HRESULT FakeGaiaCredentialProvider::OnUserAuthenticated( +const CComBSTR& CTestGaiaCredentialProvider::password() const { + return password_; +} + +const CComBSTR& CTestGaiaCredentialProvider::sid() const { + return sid_; +} + +bool CTestGaiaCredentialProvider::credentials_changed_fired() const { + return credentials_changed_fired_; +} + +void CTestGaiaCredentialProvider::ResetCredentialsChangedFired() { + credentials_changed_fired_ = FALSE; +} + +HRESULT CTestGaiaCredentialProvider::OnUserAuthenticatedImpl( IUnknown* credential, BSTR username, BSTR password, @@ -140,9 +224,10 @@ password_ = password; sid_ = sid; credentials_changed_fired_ = fire_credentials_changed; - return S_OK; + return CGaiaCredentialProvider::OnUserAuthenticatedImpl( + credential, username, password, sid, fire_credentials_changed); } -IMPL_IUNKOWN_NOQI_WITH_REF(FakeGaiaCredentialProvider) +} // namespace testing } // namespace credential_provider
diff --git a/chrome/credential_provider/test/com_fakes.h b/chrome/credential_provider/test/com_fakes.h index f9bcdbb..4a8823b 100644 --- a/chrome/credential_provider/test/com_fakes.h +++ b/chrome/credential_provider/test/com_fakes.h
@@ -12,10 +12,14 @@ #include <vector> #include "base/strings/string16.h" +#include "chrome/credential_provider/gaiacp/gaia_credential_provider.h" #include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h" +#include "chrome/credential_provider/test/test_credential_provider.h" namespace credential_provider { +namespace testing { + #define DECLARE_IUNKOWN_NOQI_WITH_REF() \ IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv) override; \ ULONG STDMETHODCALLTYPE AddRef() override; \ @@ -94,38 +98,45 @@ /////////////////////////////////////////////////////////////////////////////// -// Fake the GaiaCredentialProvider COM object. -class FakeGaiaCredentialProvider : public IGaiaCredentialProvider { +// Test implementation of GaiaCredentialProvider that stores information from +// OnUserAuthenticatedImpl. +class CTestGaiaCredentialProvider : public CGaiaCredentialProvider, + public ITestCredentialProvider { public: - FakeGaiaCredentialProvider(); - virtual ~FakeGaiaCredentialProvider(); + const CComBSTR& STDMETHODCALLTYPE username() const override; + const CComBSTR& STDMETHODCALLTYPE password() const override; + const CComBSTR& STDMETHODCALLTYPE sid() const override; + bool STDMETHODCALLTYPE credentials_changed_fired() const override; + void STDMETHODCALLTYPE ResetCredentialsChangedFired() override; - const CComBSTR& username() const { return username_; } - const CComBSTR& password() const { return password_; } - const CComBSTR& sid() const { return sid_; } - bool credentials_changed_fired() const { return credentials_changed_fired_; } - void ResetCredentialsChangedFired() { credentials_changed_fired_ = FALSE; } - void SetUsageScenario(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus) { - cpus_ = cpus; - } + BEGIN_COM_MAP(CTestGaiaCredentialProvider) + COM_INTERFACE_ENTRY(IGaiaCredentialProvider) + COM_INTERFACE_ENTRY(ICredentialProviderSetUserArray) + COM_INTERFACE_ENTRY(ICredentialProvider) + COM_INTERFACE_ENTRY(ICredentialUpdateEventsHandler) + COM_INTERFACE_ENTRY(ITestCredentialProvider) + END_COM_MAP() - // IGaiaCredentialProvider - IFACEMETHODIMP GetUsageScenario(DWORD* cpus) override; - DECLARE_IUNKOWN_NOQI_WITH_REF() - IFACEMETHODIMP OnUserAuthenticated(IUnknown* credential, - BSTR username, - BSTR password, - BSTR sid, - BOOL fire_credentials_changed) override; + protected: + // CGaiaCredentialProvider + HRESULT OnUserAuthenticatedImpl(IUnknown* credential, + BSTR username, + BSTR password, + BSTR sid, + BOOL fire_credentials_changed) override; + + CTestGaiaCredentialProvider(); + ~CTestGaiaCredentialProvider() override; private: CComBSTR username_; CComBSTR password_; CComBSTR sid_; BOOL credentials_changed_fired_ = FALSE; - CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus_ = CPUS_LOGON; }; +} // namespace testing + } // namespace credential_provider #endif // CHROME_CREDENTIAL_PROVIDER_TEST_COM_FAKES_H_
diff --git a/chrome/credential_provider/test/fake_gls_run_helper.cc b/chrome/credential_provider/test/fake_gls_run_helper.cc deleted file mode 100644 index 70f2f06b..0000000 --- a/chrome/credential_provider/test/fake_gls_run_helper.cc +++ /dev/null
@@ -1,197 +0,0 @@ -// Copyright 2018 The Chromium 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 "fake_gls_run_helper.h" - -#include "chrome/credential_provider/gaiacp/stdafx.h" - -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/json/json_writer.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "base/test/multiprocess_test.h" -#include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h" -#include "chrome/credential_provider/gaiacp/scoped_lsa_policy.h" -#include "chrome/credential_provider/test/gcp_fakes.h" -#include "chrome/credential_provider/test/test_credential.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/multiprocess_func_list.h" - -namespace credential_provider { - -namespace switches { - -constexpr char kDefaultExitCode[] = "default-exit-code"; -constexpr char kGlsUserEmail[] = "gls-user-email"; -constexpr char kStartGlsEventName[] = "start-gls-event-name"; -constexpr char kOverrideGaiaId[] = "override-gaia-id"; - -} // namespace switches - -namespace testing { - -// Corresponding default email and username for tests that don't override them. -const char kDefaultEmail[] = "foo@gmail.com"; -const char kDefaultGaiaId[] = "test-gaia-id"; -const wchar_t kDefaultUsername[] = L"foo"; - -namespace { - -// Generates a common signin result given an email pass through the command -// line and writes this result to stdout. This is used as a fake GLS process -// for testing. -MULTIPROCESS_TEST_MAIN(gls_main) { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - - // If a start event name is specified, the process waits for an event from the - // tester telling it that it can start running. - if (command_line->HasSwitch(switches::kStartGlsEventName)) { - base::string16 start_event_name = - command_line->GetSwitchValueNative(switches::kStartGlsEventName); - if (!start_event_name.empty()) { - base::win::ScopedHandle start_event_handle(::CreateEvent( - nullptr, false, false, base::UTF16ToWide(start_event_name).c_str())); - if (start_event_handle.IsValid()) { - base::WaitableEvent start_event(std::move(start_event_handle)); - start_event.Wait(); - } - } - } - - int default_exit_code = kUiecSuccess; - EXPECT_TRUE(base::StringToInt( - command_line->GetSwitchValueASCII(switches::kDefaultExitCode), - &default_exit_code)); - std::string gls_email = - command_line->GetSwitchValueASCII(switches::kGlsUserEmail); - std::string gaia_id_override = - command_line->GetSwitchValueASCII(switches::kOverrideGaiaId); - std::string expected_gaia_id = - command_line->GetSwitchValueASCII(kGaiaIdSwitch); - std::string expected_email = - command_line->GetSwitchValueASCII(kPrefillEmailSwitch); - if (expected_email.empty()) { - expected_email = gls_email; - } else { - EXPECT_EQ(gls_email, std::string()); - } - if (expected_gaia_id.empty()) - expected_gaia_id = kDefaultGaiaId; - base::Value dict(base::Value::Type::DICTIONARY); - if (!gaia_id_override.empty() && gaia_id_override != expected_gaia_id) { - dict.SetIntKey(kKeyExitCode, kUiecEMailMissmatch); - } else { - dict.SetIntKey(kKeyExitCode, static_cast<UiExitCodes>(default_exit_code)); - dict.SetStringKey(kKeyEmail, expected_email); - dict.SetStringKey(kKeyFullname, "Full Name"); - dict.SetStringKey(kKeyId, expected_gaia_id); - dict.SetStringKey(kKeyMdmIdToken, "idt-123456"); - dict.SetStringKey(kKeyPassword, "password"); - dict.SetStringKey(kKeyRefreshToken, "rt-123456"); - dict.SetStringKey(kKeyTokenHandle, "th-123456"); - } - - std::string json; - if (!base::JSONWriter::Write(dict, &json)) - return -1; - - HANDLE hstdout = ::GetStdHandle(STD_OUTPUT_HANDLE); - DWORD written; - if (::WriteFile(hstdout, json.c_str(), json.length(), &written, nullptr)) { - return 0; - } - - return -1; -} -} // namespace - -FakeGlsRunHelper::FakeGlsRunHelper(FakeOSUserManager* fake_os_user_manager) { - // Create the special gaia account used to run GLS and save its password. - - BSTR sid; - DWORD error; - EXPECT_EQ(S_OK, fake_os_user_manager->AddUser( - kDefaultGaiaAccountName, L"password", L"fullname", - L"comment", true, &sid, &error)); - - auto policy = ScopedLsaPolicy::Create(POLICY_ALL_ACCESS); - EXPECT_EQ(S_OK, policy->StorePrivateData(kLsaKeyGaiaUsername, - kDefaultGaiaAccountName)); - EXPECT_EQ(S_OK, policy->StorePrivateData(kLsaKeyGaiaPassword, L"password")); -} - -FakeGlsRunHelper::~FakeGlsRunHelper() = default; - -HRESULT FakeGlsRunHelper::StartLogonProcess(ICredentialProviderCredential* cred, - bool succeeds) { - BOOL auto_login; - EXPECT_EQ(S_OK, cred->SetSelected(&auto_login)); - - // Logging on is an async process, so the call to GetSerialization() starts - // the process, but when it returns it has not completed. - CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; - CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; - wchar_t* status_text; - CREDENTIAL_PROVIDER_STATUS_ICON status_icon; - EXPECT_EQ(S_OK, - cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); - EXPECT_EQ(CPSI_NONE, status_icon); - if (succeeds) { - EXPECT_EQ(nullptr, status_text); - EXPECT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr); - } else { - EXPECT_NE(nullptr, status_text); - EXPECT_EQ(CPGSR_NO_CREDENTIAL_FINISHED, cpgsr); - } - return S_OK; -} - -HRESULT FakeGlsRunHelper::WaitForLogonProcess( - ICredentialProviderCredential* cred) { - CComPtr<testing::ITestCredential> test; - HRESULT hr = cred->QueryInterface(__uuidof(testing::ITestCredential), - reinterpret_cast<void**>(&test)); - if (FAILED(hr)) - return hr; - return test->WaitForGls(); -} - -HRESULT FakeGlsRunHelper::StartLogonProcessAndWait( - ICredentialProviderCredential* cred) { - HRESULT hr = StartLogonProcess(cred, /*succeeds=*/true); - if (FAILED(hr)) - return hr; - return WaitForLogonProcess(cred); -} - -// static -HRESULT FakeGlsRunHelper::GetFakeGlsCommandline( - UiExitCodes default_exit_code, - const std::string& gls_email, - const std::string& gaia_id_override, - const base::string16& start_gls_event_name, - base::CommandLine* command_line) { - *command_line = base::GetMultiProcessTestChildBaseCommandLine(); - command_line->AppendSwitchASCII(::switches::kTestChildProcess, "gls_main"); - command_line->AppendSwitchASCII(switches::kGlsUserEmail, gls_email); - command_line->AppendSwitchNative(switches::kDefaultExitCode, - base::NumberToString16(default_exit_code)); - - if (!gaia_id_override.empty()) { - command_line->AppendSwitchASCII(switches::kOverrideGaiaId, - gaia_id_override); - } - - if (!start_gls_event_name.empty()) { - command_line->AppendSwitchNative(switches::kStartGlsEventName, - start_gls_event_name); - } - - return S_OK; -} - -} // namespace testing - -} // namespace credential_provider
diff --git a/chrome/credential_provider/test/fake_gls_run_helper.h b/chrome/credential_provider/test/fake_gls_run_helper.h deleted file mode 100644 index f870adf8..0000000 --- a/chrome/credential_provider/test/fake_gls_run_helper.h +++ /dev/null
@@ -1,55 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_CREDENTIAL_PROVIDER_TEST_FAKE_GLS_RUN_HELPER_H_ -#define CHROME_CREDENTIAL_PROVIDER_TEST_FAKE_GLS_RUN_HELPER_H_ - -#include <atlcomcli.h> - -#include "base/strings/string16.h" -#include "chrome/credential_provider/common/gcp_strings.h" - -struct ICredentialProviderCredential; - -namespace base { -class CommandLine; -} - -namespace credential_provider { - -class FakeOSUserManager; - -namespace testing { - -extern const char kDefaultEmail[]; -extern const char kDefaultGaiaId[]; -extern const wchar_t kDefaultUsername[]; - -// Helper class used to run and wait for a fake GLS process to validate the -// functionality of a GCPW credential. -class FakeGlsRunHelper { - public: - explicit FakeGlsRunHelper(FakeOSUserManager* fake_os_user_manager); - ~FakeGlsRunHelper(); - - HRESULT StartLogonProcess(ICredentialProviderCredential* cred, bool succeeds); - HRESULT WaitForLogonProcess(ICredentialProviderCredential* cred); - HRESULT StartLogonProcessAndWait(ICredentialProviderCredential* cred); - - // Gets a command line that runs a fake GLS that produces the desired output. - // |default_exit_code| is the default value that will be written unless the - // other command line arguments require a specific error code to be returned. - static HRESULT GetFakeGlsCommandline( - UiExitCodes default_exit_code, - const std::string& gls_email, - const std::string& gaia_id_override, - const base::string16& start_gls_event_name, - base::CommandLine* command_line); -}; - -} // namespace testing - -} // namespace credential_provider - -#endif // CHROME_CREDENTIAL_PROVIDER_TEST_FAKE_GLS_RUN_HELPER_H_
diff --git a/chrome/credential_provider/test/gcp_fakes.cc b/chrome/credential_provider/test/gcp_fakes.cc index f26f199..613664d 100644 --- a/chrome/credential_provider/test/gcp_fakes.cc +++ b/chrome/credential_provider/test/gcp_fakes.cc
@@ -143,6 +143,9 @@ if (error) *error = 0; + if (should_fail_user_creation_) + return E_FAIL; + bool user_found = username_to_info_.count(username) > 0; if (user_found) { @@ -388,6 +391,16 @@ return S_OK; } +std::vector<std::pair<base::string16, base::string16>> +FakeOSUserManager::GetUsers() const { + std::vector<std::pair<base::string16, base::string16>> users; + + for (auto& kv : username_to_info_) + users.emplace_back(std::make_pair(kv.second.sid, kv.first)); + + return users; +} + /////////////////////////////////////////////////////////////////////////////// FakeScopedLsaPolicyFactory::FakeScopedLsaPolicyFactory()
diff --git a/chrome/credential_provider/test/gcp_fakes.h b/chrome/credential_provider/test/gcp_fakes.h index d471c05c..653db567 100644 --- a/chrome/credential_provider/test/gcp_fakes.h +++ b/chrome/credential_provider/test/gcp_fakes.h
@@ -101,6 +101,11 @@ HRESULT ModifyUserAccessWithLogonHours(const wchar_t* domain, const wchar_t* username, bool allow) override; + + void SetShouldFailUserCreation(bool should_fail) { + should_fail_user_creation_ = should_fail; + } + struct UserInfo { UserInfo(const wchar_t* domain, const wchar_t* password, @@ -138,11 +143,13 @@ BSTR* sid); size_t GetUserCount() const { return username_to_info_.size(); } + std::vector<std::pair<base::string16, base::string16>> GetUsers() const; private: OSUserManager* original_manager_; DWORD next_rid_ = 0; std::map<base::string16, UserInfo> username_to_info_; + bool should_fail_user_creation_ = false; }; /////////////////////////////////////////////////////////////////////////////// @@ -294,7 +301,8 @@ explicit FakeAssociatedUserValidator(base::TimeDelta validation_timeout); ~FakeAssociatedUserValidator() override; - using AssociatedUserValidator::IsUserAccessBlocked; + using AssociatedUserValidator::ForceRefreshTokenHandlesForTesting; + using AssociatedUserValidator::IsUserAccessBlockedForTesting; private: AssociatedUserValidator* original_validator_ = nullptr;
diff --git a/chrome/credential_provider/test/gls_runner_test_base.cc b/chrome/credential_provider/test/gls_runner_test_base.cc index 6cc03139..c3857eb 100644 --- a/chrome/credential_provider/test/gls_runner_test_base.cc +++ b/chrome/credential_provider/test/gls_runner_test_base.cc
@@ -4,20 +4,589 @@ #include "gls_runner_test_base.h" +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/json/json_writer.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/multiprocess_test.h" +#include "chrome/credential_provider/gaiacp/gaia_credential_provider_filter.h" +#include "chrome/credential_provider/gaiacp/scoped_lsa_policy.h" +#include "chrome/credential_provider/test/test_credential.h" +#include "testing/multiprocess_func_list.h" + namespace credential_provider { +namespace switches { + +constexpr char kDefaultExitCode[] = "default-exit-code"; +constexpr char kIgnoreExpectedGaiaId[] = "ignore-expected-gaia-id"; +constexpr char kGlsUserEmail[] = "gls-user-email"; +constexpr char kStartGlsEventName[] = "start-gls-event-name"; +constexpr char kOverrideGaiaId[] = "override-gaia-id"; + +} // namespace switches + namespace testing { -GlsRunnerTestBase::GlsRunnerTestBase() : run_helper_(&fake_os_user_manager_) {} +// Corresponding default email and username for tests that don't override them. +const char kDefaultEmail[] = "foo@gmail.com"; +const char kDefaultGaiaId[] = "test-gaia-id"; +const wchar_t kDefaultUsername[] = L"foo"; +const char kDefaultInvalidTokenHandleResponse[] = "{}"; +const char kDefaultValidTokenHandleResponse[] = "{\"expires_in\":1}"; + +namespace { + +// Generates a common signin result given an email pass through the command +// line and writes this result to stdout. This is used as a fake GLS process +// for testing. +MULTIPROCESS_TEST_MAIN(gls_main) { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + + // If a start event name is specified, the process waits for an event from the + // tester telling it that it can start running. + if (command_line->HasSwitch(switches::kStartGlsEventName)) { + base::string16 start_event_name = + command_line->GetSwitchValueNative(switches::kStartGlsEventName); + if (!start_event_name.empty()) { + base::win::ScopedHandle start_event_handle(::CreateEvent( + nullptr, false, false, base::UTF16ToWide(start_event_name).c_str())); + if (start_event_handle.IsValid()) { + base::WaitableEvent start_event(std::move(start_event_handle)); + start_event.Wait(); + } + } + } + + int default_exit_code = kUiecSuccess; + EXPECT_TRUE(base::StringToInt( + command_line->GetSwitchValueASCII(switches::kDefaultExitCode), + &default_exit_code)); + std::string gls_email = + command_line->GetSwitchValueASCII(switches::kGlsUserEmail); + std::string gaia_id_override = + command_line->GetSwitchValueASCII(switches::kOverrideGaiaId); + std::string expected_gaia_id = + command_line->GetSwitchValueASCII(kGaiaIdSwitch); + std::string expected_email = + command_line->GetSwitchValueASCII(kPrefillEmailSwitch); + if (expected_email.empty()) { + expected_email = gls_email; + } else { + EXPECT_EQ(gls_email, std::string()); + } + if (expected_gaia_id.empty()) + expected_gaia_id = kDefaultGaiaId; + + if (command_line->HasSwitch(switches::kIgnoreExpectedGaiaId)) { + DCHECK(!gaia_id_override.empty()); + expected_gaia_id = gaia_id_override; + } + + base::Value dict(base::Value::Type::DICTIONARY); + if (!gaia_id_override.empty() && gaia_id_override != expected_gaia_id) { + dict.SetIntKey(kKeyExitCode, kUiecEMailMissmatch); + } else { + dict.SetIntKey(kKeyExitCode, static_cast<UiExitCodes>(default_exit_code)); + dict.SetStringKey(kKeyEmail, expected_email); + dict.SetStringKey(kKeyFullname, "Full Name"); + dict.SetStringKey(kKeyId, expected_gaia_id); + dict.SetStringKey(kKeyMdmIdToken, "idt-123456"); + dict.SetStringKey(kKeyPassword, "password"); + dict.SetStringKey(kKeyRefreshToken, "rt-123456"); + dict.SetStringKey(kKeyTokenHandle, "th-123456"); + } + + std::string json; + if (!base::JSONWriter::Write(dict, &json)) + return -1; + + HANDLE hstdout = ::GetStdHandle(STD_OUTPUT_HANDLE); + DWORD written; + if (::WriteFile(hstdout, json.c_str(), json.length(), &written, nullptr)) { + return 0; + } + + return -1; +} + +} // namespace + +GlsRunnerTestBase::GlsRunnerTestBase() + : cpus_(CPUS_LOGON), + default_token_handle_response_(kDefaultValidTokenHandleResponse) {} GlsRunnerTestBase::~GlsRunnerTestBase() = default; void GlsRunnerTestBase::SetUp() { + // Create the special gaia account used to run GLS and save its password. + BSTR sid; + DWORD error; + EXPECT_EQ(S_OK, fake_os_user_manager()->AddUser( + kDefaultGaiaAccountName, L"password", L"fullname", + L"comment", true, &sid, &error)); + + auto policy = ScopedLsaPolicy::Create(POLICY_ALL_ACCESS); + EXPECT_EQ(S_OK, policy->StorePrivateData(kLsaKeyGaiaUsername, + kDefaultGaiaAccountName)); + EXPECT_EQ(S_OK, policy->StorePrivateData(kLsaKeyGaiaPassword, L"password")); + // Make sure not to read random GCPW settings from the machine that is running // the tests. InitializeRegistryOverrideForTesting(®istry_override_); } +void GlsRunnerTestBase::TearDown() { + // If credential has not been explicitly completed and the logon process + // was started, then complete here under the assumption that it will + // complete successfully. + ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0)); + + ASSERT_EQ(S_OK, ReleaseProvider()); +} + +HRESULT GlsRunnerTestBase::ReleaseProvider() { + if (!gaia_provider_) + return S_OK; + + // If any logon are still pending, they are about to be killed now. + logon_process_started_successfully_ = false; + + HRESULT hr = S_OK; + // Complete the release of the provider. + // Unadvise all the credentials. + DWORD count; + DWORD default_index; + BOOL autologon; + HRESULT get_count_hr = + gaia_provider_->GetCredentialCount(&count, &default_index, &autologon); + if (SUCCEEDED(get_count_hr)) { + for (DWORD i = 0; i < count; ++i) { + CComPtr<ICredentialProviderCredential> credential; + HRESULT get_hr = gaia_provider_->GetCredentialAt(i, &credential); + EXPECT_EQ(get_hr, S_OK); + if (SUCCEEDED(get_hr)) { + get_hr = credential->UnAdvise(); + if (FAILED(get_hr)) + hr = get_hr; + } else { + hr = get_hr; + } + } + } else { + hr = get_count_hr; + } + + // Unadvise the provider. + HRESULT unadvise_hr = gaia_provider_->UnAdvise(); + if (FAILED(unadvise_hr)) + hr = unadvise_hr; + gaia_provider_.Release(); + + return hr; +} + +HRESULT +GlsRunnerTestBase::InitializeProviderWithCredentials( + DWORD* credential_count, + ICredentialProvider** provider) { + HRESULT hr = InternalInitializeProvider(nullptr, credential_count); + if (FAILED(hr)) + return hr; + + return gaia_provider_.QueryInterface(provider); +} + +HRESULT GlsRunnerTestBase::InitializeProviderWithRemoteCredentials( + const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs_in, + ICredentialProvider** provider) { + HRESULT hr = InternalInitializeProvider(pcpcs_in, nullptr); + if (FAILED(hr)) + return hr; + + return gaia_provider_.QueryInterface(provider); +} + +HRESULT GlsRunnerTestBase::InitializeProviderAndGetCredential( + DWORD index, + ICredentialProviderCredential** credential) { + DCHECK(credential); + + *credential = nullptr; + DWORD count = 0; + HRESULT hr = InternalInitializeProvider(nullptr, &count); + if (FAILED(hr)) + return hr; + + if (index >= count) + return E_FAIL; + + // Reference specific credential that was requested. + hr = gaia_provider_->GetCredentialAt(index, &testing_cred_); + if (FAILED(hr)) + return hr; + + EXPECT_EQ(S_OK, testing_cred_.QueryInterface(credential)); + return S_OK; +} + +HRESULT GlsRunnerTestBase::InternalInitializeProvider( + const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs_in, + DWORD* count) { + if (count) + *count = 0; + + CComPtr<ICredentialProvider> provider; + + HRESULT hr = + CComCreator<CComObject<CTestGaiaCredentialProvider>>::CreateInstance( + nullptr, IID_ICredentialProvider, + reinterpret_cast<void**>(&provider)); + if (FAILED(hr)) + return hr; + + // Apply the filter and get remote credentials as needed. + { + CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION remote_credentials; + HRESULT update_remote_credentials_hr; + hr = ApplyProviderFilter(provider, pcpcs_in, &remote_credentials, + &update_remote_credentials_hr); + if (FAILED(hr)) + return hr; + + // Start process for logon screen. + hr = provider->SetUsageScenario(cpus_, 0); + if (FAILED(hr)) + return hr; + + if (SUCCEEDED(update_remote_credentials_hr) && + remote_credentials.rgbSerialization) { + // Apply remote credentials if any. + if (remote_credentials.clsidCredentialProvider == + CLSID_GaiaCredentialProvider) { + provider->SetSerialization(&remote_credentials); + } + ::CoTaskMemFree(remote_credentials.rgbSerialization); + } + } + + // Give list of users visible on welcome screen. + CComPtr<ICredentialProviderSetUserArray> provider_user_array; + hr = provider.QueryInterface(&provider_user_array); + if (FAILED(hr)) + return hr; + + // All users are shown if the usage is not for unlocking the workstation. + bool all_users_shown = cpus_ != CPUS_UNLOCK_WORKSTATION; + + CREDENTIAL_PROVIDER_ACCOUNT_OPTIONS cpao; + ICredentialProviderUserArray* user_array = fake_user_array(); + hr = user_array->GetAccountOptions(&cpao); + if (FAILED(hr)) + return hr; + + bool other_user_tile_available = cpao == CPAO_EMPTY_LOCAL; + + for (auto& sid_and_username : fake_os_user_manager_.GetUsers()) { + // If not all the users are shown, the user that locked the system is + // the only one that is in the user array (if the other user tile is + // not available). + if (!all_users_shown && + ((!sid_locking_workstation_.empty() && + sid_locking_workstation_ != sid_and_username.first) || + other_user_tile_available)) { + continue; + } + fake_user_array_.AddUser(sid_and_username.first.c_str(), + sid_and_username.second.c_str()); + } + + hr = provider_user_array->SetUserArray(&fake_user_array_); + if (FAILED(hr)) + return hr; + + // Activate the CP. + FakeCredentialProviderEvents events; + hr = provider->Advise(&fake_provider_events_, 0); + if (FAILED(hr)) + return hr; + + // This class can now take ownership of the provider so that it can + // be correctly uninitialized later. + gaia_provider_ = provider; + + // GetCredentialCount must be called to initialize the credentials (if + // desired). + if (count) { + DWORD default_index; + BOOL autologon; + hr = gaia_provider_->GetCredentialCount(count, &default_index, &autologon); + if (FAILED(hr)) + return hr; + + EXPECT_EQ(CREDENTIAL_PROVIDER_NO_DEFAULT, default_index); + EXPECT_FALSE(autologon); + + // Advise all the credentials + for (DWORD i = 0; i < *count; ++i) { + CComPtr<ICredentialProviderCredential> current_credential; + hr = gaia_provider_->GetCredentialAt(i, ¤t_credential); + if (FAILED(hr)) + break; + + hr = current_credential->Advise(nullptr); + if (FAILED(hr)) + break; + } + } + + // Verify fields. + DWORD field_count; + hr = gaia_provider_->GetFieldDescriptorCount(&field_count); + if (FAILED(hr)) + return hr; + + for (DWORD i = 0; i < field_count; ++i) { + CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR* ppcpfd; + hr = gaia_provider_->GetFieldDescriptorAt(i, &ppcpfd); + if (FAILED(hr)) + return hr; + } + + return S_OK; +} + +HRESULT GlsRunnerTestBase::ApplyProviderFilter( + const CComPtr<ICredentialProvider>& provider, + const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs_in, + CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs_out, + HRESULT* update_remote_credentials_hr) { + if (update_remote_credentials_hr) + *update_remote_credentials_hr = E_NOTIMPL; + + // Filter only lives long enough to apply filter and get serialization + // credentials. + CComPtr<ICredentialProviderFilter> filter; + HRESULT hr = + CComCreator<CComObject<CGaiaCredentialProviderFilter>>::CreateInstance( + nullptr, IID_ICredentialProviderFilter, (void**)&filter); + if (FAILED(hr)) + return hr; + + // Set token fetch result before starting the filter. + fake_http_url_fetcher_factory_.SetFakeResponse( + GURL(AssociatedUserValidator::kTokenInfoUrl), + FakeWinHttpUrlFetcher::Headers(), default_token_handle_response_); + + // Start initial refresh of token handles. The filter will apply user access + // restrictions as needed. + fake_associated_user_validator_.StartRefreshingTokenHandleValidity(); + + // Perform initial filter code. + hr = filter->Filter(cpus_, 0, nullptr, nullptr, 0); + if (FAILED(hr)) + return hr; + + // Apply remote credentials if any. + if (pcpcs_in && pcpcs_out && update_remote_credentials_hr) + *update_remote_credentials_hr = + filter->UpdateRemoteCredential(pcpcs_in, pcpcs_out); + + return S_OK; +} + +HRESULT GlsRunnerTestBase::StartLogonProcess(bool succeeds) { + DCHECK(testing_cred_); + DCHECK(!logon_process_started_successfully_); + BOOL auto_login; + EXPECT_EQ(S_OK, testing_cred_->SetSelected(&auto_login)); + + // Logging on is an async process, so the call to GetSerialization() starts + // the process, but when it returns it has not completed. + CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; + CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; + wchar_t* status_text; + CREDENTIAL_PROVIDER_STATUS_ICON status_icon; + EXPECT_EQ(S_OK, testing_cred_->GetSerialization(&cpgsr, &cpcs, &status_text, + &status_icon)); + EXPECT_EQ(CPSI_NONE, status_icon); + if (succeeds) { + EXPECT_EQ(nullptr, status_text); + EXPECT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr); + logon_process_started_successfully_ = true; + } else { + EXPECT_NE(nullptr, status_text); + EXPECT_EQ(CPGSR_NO_CREDENTIAL_FINISHED, cpgsr); + } + return S_OK; +} + +HRESULT GlsRunnerTestBase::WaitForLogonProcess() { + CComPtr<testing::ITestCredential> test; + HRESULT hr = testing_cred_->QueryInterface(&test); + if (FAILED(hr)) + return hr; + return test->WaitForGls(); +} + +HRESULT GlsRunnerTestBase::StartLogonProcessAndWait() { + HRESULT hr = StartLogonProcess(/*succeeds=*/true); + if (FAILED(hr)) + return hr; + return WaitForLogonProcess(); +} + +// static +HRESULT GlsRunnerTestBase::GetFakeGlsCommandline( + UiExitCodes default_exit_code, + const std::string& gls_email, + const std::string& gaia_id_override, + const base::string16& start_gls_event_name, + bool ignore_expected_gaia_id, + base::CommandLine* command_line) { + *command_line = base::GetMultiProcessTestChildBaseCommandLine(); + command_line->AppendSwitchASCII(::switches::kTestChildProcess, "gls_main"); + command_line->AppendSwitchASCII(switches::kGlsUserEmail, gls_email); + command_line->AppendSwitchNative(switches::kDefaultExitCode, + base::NumberToString16(default_exit_code)); + + if (ignore_expected_gaia_id) + command_line->AppendSwitch(switches::kIgnoreExpectedGaiaId); + + if (!gaia_id_override.empty()) { + command_line->AppendSwitchASCII(switches::kOverrideGaiaId, + gaia_id_override); + } + + if (!start_gls_event_name.empty()) { + command_line->AppendSwitchNative(switches::kStartGlsEventName, + start_gls_event_name); + } + + return S_OK; +} + +HRESULT GlsRunnerTestBase::FinishLogonProcess( + bool expected_success, + bool expected_credentials_change_fired, + int expected_error_message) { + // If no logon process was started, there is nothing to finish. + if (!logon_process_started_successfully_) + return S_OK; + + CComPtr<ICredentialProviderCredential> local_testing_cred = testing_cred_; + // Release ownership on the testing_cred_ which should be finishing. + testing_cred_.Release(); + + HRESULT hr = FinishLogonProcessWithCred( + expected_success, expected_credentials_change_fired, + expected_error_message, local_testing_cred); + + EXPECT_EQ(hr, S_OK); + if (FAILED(hr)) + return hr; + + if (expected_credentials_change_fired) { + hr = ReportLogonProcessResult(local_testing_cred); + EXPECT_EQ(hr, S_OK); + return hr; + } + + return S_OK; +} + +HRESULT GlsRunnerTestBase::FinishLogonProcessWithCred( + bool expected_success, + bool expected_credentials_change_fired, + int expected_error_message, + const CComPtr<ICredentialProviderCredential>& local_testing_cred) { + // If no logon process was started, there is nothing to finish. + if (!logon_process_started_successfully_) + return S_OK; + + logon_process_started_successfully_ = false; + DCHECK(gaia_provider_); + + CComPtr<ITestCredential> test_cred; + HRESULT hr = local_testing_cred.QueryInterface(&test_cred); + if (FAILED(hr)) + return hr; + + CComPtr<ITestCredentialProvider> test_provider; + hr = gaia_provider_.QueryInterface(&test_provider); + if (FAILED(hr)) + return hr; + + EXPECT_EQ(test_provider->credentials_changed_fired(), + expected_success && expected_credentials_change_fired); + + if (!expected_success) { + // Check that values were not propagated to the provider. + EXPECT_EQ(0u, test_provider->username().Length()); + EXPECT_EQ(0u, test_provider->password().Length()); + EXPECT_EQ(0u, test_provider->sid().Length()); + + if (expected_error_message) { + EXPECT_STREQ(test_cred->GetErrorText(), + GetStringResource(expected_error_message).c_str()); + } else { + EXPECT_EQ(test_cred->GetErrorText(), nullptr); + } + return S_OK; + } + + // Call final GetSerialization and expect it to be finished. + CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; + CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; + wchar_t* status_text; + CREDENTIAL_PROVIDER_STATUS_ICON status_icon; + hr = local_testing_cred->GetSerialization(&cpgsr, &cpcs, &status_text, + &status_icon); + + if (FAILED(hr)) + return hr; + + EXPECT_EQ(nullptr, status_text); + EXPECT_EQ(CPSI_SUCCESS, status_icon); + EXPECT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr); + EXPECT_LT(0u, cpcs.cbSerialization); + EXPECT_NE(nullptr, cpcs.rgbSerialization); + + // Check that values were propagated to the provider. + if (expected_credentials_change_fired) { + EXPECT_NE(0u, test_provider->username().Length()); + EXPECT_NE(0u, test_provider->password().Length()); + EXPECT_NE(0u, test_provider->sid().Length()); + } + + EXPECT_EQ(test_cred->GetErrorText(), nullptr); + + return S_OK; +} + +HRESULT GlsRunnerTestBase::ReportLogonProcessResult( + const CComPtr<ICredentialProviderCredential>& local_testing_cred) { + CComPtr<ITestCredential> test_cred; + HRESULT hr = local_testing_cred.QueryInterface(&test_cred); + if (FAILED(hr)) + return hr; + + // State was not reset. + EXPECT_TRUE(test_cred->AreCredentialsValid()); + wchar_t* report_status_text = nullptr; + CREDENTIAL_PROVIDER_STATUS_ICON report_icon; + hr = + local_testing_cred->ReportResult(0, 0, &report_status_text, &report_icon); + if (FAILED(hr)) + return hr; + + // State was reset. + EXPECT_FALSE(test_cred->AreCredentialsValid()); + + return S_OK; +} + } // namespace testing } // namespace credential_provider
diff --git a/chrome/credential_provider/test/gls_runner_test_base.h b/chrome/credential_provider/test/gls_runner_test_base.h index 63f1124c..66955ad 100644 --- a/chrome/credential_provider/test/gls_runner_test_base.h +++ b/chrome/credential_provider/test/gls_runner_test_base.h
@@ -6,8 +6,9 @@ #define CHROME_CREDENTIAL_PROVIDER_TEST_GLS_RUNNER_TEST_BASE_H_ #include "base/test/test_reg_util_win.h" +#include "chrome/credential_provider/common/gcp_strings.h" +#include "chrome/credential_provider/gaiacp/gaia_credential_provider.h" #include "chrome/credential_provider/test/com_fakes.h" -#include "chrome/credential_provider/test/fake_gls_run_helper.h" #include "chrome/credential_provider/test/gcp_fakes.h" #include "testing/gtest/include/gtest/gtest.h" @@ -15,31 +16,188 @@ namespace testing { -// Helper class used to run and wait for a fake GLS process to validate the -// functionality of a GCPW credential. +extern const char kDefaultEmail[]; +extern const char kDefaultGaiaId[]; +extern const wchar_t kDefaultUsername[]; +extern const char kDefaultInvalidTokenHandleResponse[]; +extern const char kDefaultValidTokenHandleResponse[]; + +// Helper class used to test the full call sequence of a credential provider by +// LoginUI. This includes creation of a credential provider filter and +// application of remote credentials if specified. There are default token +// handle responses (always valid token handles) and usage scenarios +// (CPUS_LOGON) that can be overridden before starting the call sequence for the +// credential provider. class GlsRunnerTestBase : public ::testing::Test { + public: + // Gets a command line that runs a fake GLS that produces the desired output. + // |default_exit_code| is the default value that will be written unless the + // other command line arguments require a specific error code to be returned. + static HRESULT GetFakeGlsCommandline( + UiExitCodes default_exit_code, + const std::string& gls_email, + const std::string& gaia_id_override, + const base::string16& start_gls_event_name, + bool ignore_expected_gaia_id, + base::CommandLine* command_line); + protected: GlsRunnerTestBase(); ~GlsRunnerTestBase() override; void SetUp() override; - FakeGlsRunHelper* run_helper() { return &run_helper_; } + void TearDown() override; FakeOSUserManager* fake_os_user_manager() { return &fake_os_user_manager_; } FakeWinHttpUrlFetcherFactory* fake_http_url_fetcher_factory() { return &fake_http_url_fetcher_factory_; } + FakeCredentialProviderUserArray* fake_user_array() { + return &fake_user_array_; + } + FakeAssociatedUserValidator* fake_associated_user_validator() { + return &fake_associated_user_validator_; + } + FakeCredentialProviderEvents* fake_provider_events() { + return &fake_provider_events_; + } + FakeInternetAvailabilityChecker* fake_internet_checker() { + return &fake_internet_checker_; + } + + const CComPtr<ICredentialProvider>& created_provider() const { + return gaia_provider_; + } + + void SetSidLockingWorkstation(const base::string16& sid) { + sid_locking_workstation_ = sid; + } + + void SetDefaultTokenHandleResponse(const std::string& response) { + default_token_handle_response_ = response; + } + + void SetUsageScenario(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus) { + // Must be called before creating the provide. The usage does not normally + // change during the execution of a provider. + DCHECK(!gaia_provider_); + cpus_ = cpus; + } + + // Creates the provider and also all the credentials associated to users that + // are already created before this call. Fills |credential_count| with the + // number of credentials in the provider and |provider| with a pointer to the + // created provider (this also correctly adds a reference to the provider). + HRESULT + InitializeProviderWithCredentials(DWORD* credential_count, + ICredentialProvider** provider); + + // Creates the provider and also all the credentials associated to users that + // are already created before this call. If |pcps_in| is non null then it will + // pass this information as remote credentials to the credential provider + // filter and provider. Fills |provider| with a pointer to the created + // provider (this also correctly adds a reference to the provider). + HRESULT + InitializeProviderWithRemoteCredentials( + const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs_in, + ICredentialProvider** provider); + + // Creates the provider and also all the credentials associated to users that + // are already created before this call. Once credentials are created, the + // function tries to fill |credential| with the credential at index |index|. + HRESULT InitializeProviderAndGetCredential( + DWORD index, + ICredentialProviderCredential** credential); + + // Used to release the provider before normal TearDown to test certain + // cancellation scenarios. No other references should be held on the provider + // to ensure that the provider can actually be released. + HRESULT ReleaseProvider(); + + // Initiates the logon process on the current |testing_credential_| that + // is selected by a call to InitializeProviderAndGetCredential. + // |succeeds| specifies whether we expect the first call to GetSerialization + // on |testing_credential_| to succeed and start a GLS process or not. + // If false, we will check that an appropriate error has been returned. + HRESULT StartLogonProcess(bool succeeds); + + // Waits for the GLS process that was started in StartLogonProcess to + // complete and returns. + HRESULT WaitForLogonProcess(); + + // Combines StartLogonProcess and WaitForLogonProcess. + HRESULT StartLogonProcessAndWait(); + + // Calls the final GetSerialization on the |testing_credential_| to complete + // the logon process. |expected_success| specifies whether the final + // GetSerialization is expected to succeed. + // |expected_credentials_change_fired| specifies if a credential changed fired + // event should have been detected by the provider. |expected_error_message| + // is the error message that is expected. This message only applies if + // |expected_success| is false. + // This function combines the calls to FinishLogonProcessWithPred and + // ReportLogonProcessResult which can be called separately to perform extra + // operations between the last GetSerialization and the call to ReportResult. + HRESULT FinishLogonProcess(bool expected_success, + bool expected_credentials_change_fired, + int expected_error_message); + HRESULT FinishLogonProcessWithCred( + bool expected_success, + bool expected_credentials_change_fired, + int expected_error_message, + const CComPtr<ICredentialProviderCredential>& local_testing_cred); + HRESULT ReportLogonProcessResult( + const CComPtr<ICredentialProviderCredential>& local_testing_cred); private: + HRESULT InternalInitializeProvider( + const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs_in, + DWORD* count); + HRESULT ApplyProviderFilter( + const CComPtr<ICredentialProvider>& provider, + const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs_in, + CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs_out, + HRESULT* update_remote_credentials_hr); + + registry_util::RegistryOverrideManager registry_override_; + FakeOSProcessManager fake_os_process_manager_; FakeOSUserManager fake_os_user_manager_; FakeScopedLsaPolicyFactory fake_scoped_lsa_policy_factory_; FakeScopedUserProfileFactory fake_scoped_user_profile_factory_; - registry_util::RegistryOverrideManager registry_override_; - FakeGlsRunHelper run_helper_; FakeInternetAvailabilityChecker fake_internet_checker_; FakeAssociatedUserValidator fake_associated_user_validator_; FakeWinHttpUrlFetcherFactory fake_http_url_fetcher_factory_; + FakeCredentialProviderEvents fake_provider_events_; + FakeCredentialProviderUserArray fake_user_array_; + + // SID of the user that is considered to be locking the workstation. This is + // only relevant for CPUS_UNLOCK_WORKSTATION usage. + base::string16 sid_locking_workstation_; + + // Reference to the provider that is created and owned by this class. + CComPtr<ICredentialProvider> gaia_provider_; + + // Reference to the credential in provider that is being tested by this class. + // This member is kept so that it can be automatically released on destruction + // of the test if the test did not explicitly release it. This allows us to + // write less boiler plate test code and ensures that proper destruction order + // of the credentials is respected. + CComPtr<ICredentialProviderCredential> testing_cred_; + + // Keeps track of whether a logon process has started for |testing_cred_|. + // Testers who do not explicitly call FinishLogonProcess before the end of + // their test will leave the completion of the logon process to the TearDown + // of this class. + bool logon_process_started_successfully_ = false; + + // The current usage scenario that this test is running. This should be + // set before |gaia_provider_| is set. + CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus_; + + // Default response returned by |fake_http_url_fetcher_factory_| when checking + // for token handle validity. + std::string default_token_handle_response_; }; } // namespace testing
diff --git a/chrome/credential_provider/test/test_credential.h b/chrome/credential_provider/test/test_credential.h index c72858e..0061a3c 100644 --- a/chrome/credential_provider/test/test_credential.h +++ b/chrome/credential_provider/test/test_credential.h
@@ -17,7 +17,7 @@ #include "base/synchronization/waitable_event.h" #include "chrome/credential_provider/common/gcp_strings.h" #include "chrome/credential_provider/gaiacp/gaia_credential_base.h" -#include "chrome/credential_provider/test/fake_gls_run_helper.h" +#include "chrome/credential_provider/test/gls_runner_test_base.h" namespace base { class CommandLine; @@ -35,7 +35,8 @@ virtual HRESULT STDMETHODCALLTYPE SetGlsEmailAddress(const std::string& email) = 0; virtual HRESULT STDMETHODCALLTYPE - SetGaiaIdOverride(const std::string& gaia_id) = 0; + SetGaiaIdOverride(const std::string& gaia_id, + bool ignore_expected_gaia_id) = 0; virtual HRESULT STDMETHODCALLTYPE WaitForGls() = 0; virtual HRESULT STDMETHODCALLTYPE SetStartGlsEventName(const base::string16& event_name) = 0; @@ -45,8 +46,6 @@ virtual bool STDMETHODCALLTYPE AreCredentialsValid() = 0; virtual bool STDMETHODCALLTYPE CanAttemptWindowsLogon() = 0; virtual bool STDMETHODCALLTYPE IsWindowsPasswordValidForStoredUser() = 0; - virtual void STDMETHODCALLTYPE - SetWindowsPassword(const CComBSTR& windows_password) = 0; virtual bool STDMETHODCALLTYPE IsGlsRunning() = 0; }; @@ -71,7 +70,8 @@ // ITestCredential. IFACEMETHODIMP SetDefaultExitCode(UiExitCodes default_exit_code) override; IFACEMETHODIMP SetGlsEmailAddress(const std::string& email) override; - IFACEMETHODIMP SetGaiaIdOverride(const std::string& gaia_id) override; + IFACEMETHODIMP SetGaiaIdOverride(const std::string& gaia_id, + bool ignore_expected_gaia_id) override; IFACEMETHODIMP WaitForGls() override; IFACEMETHODIMP SetStartGlsEventName( const base::string16& event_name) override; @@ -81,8 +81,6 @@ bool STDMETHODCALLTYPE AreCredentialsValid() override; bool STDMETHODCALLTYPE CanAttemptWindowsLogon() override; bool STDMETHODCALLTYPE IsWindowsPasswordValidForStoredUser() override; - void STDMETHODCALLTYPE - SetWindowsPassword(const CComBSTR& windows_password) override; bool STDMETHODCALLTYPE IsGlsRunning() override; void SignalGlsCompletion(); @@ -120,6 +118,7 @@ base::string16 start_gls_event_name_; CComBSTR error_text_; bool gls_process_started_ = false; + bool ignore_expected_gaia_id_ = false; }; template <class T> @@ -145,7 +144,10 @@ } template <class T> -HRESULT CTestCredentialBase<T>::SetGaiaIdOverride(const std::string& gaia_id) { +HRESULT CTestCredentialBase<T>::SetGaiaIdOverride( + const std::string& gaia_id, + bool ignore_expected_gaia_id) { + ignore_expected_gaia_id_ = ignore_expected_gaia_id; gaia_id_override_ = gaia_id; return S_OK; } @@ -208,12 +210,6 @@ } template <class T> -void CTestCredentialBase<T>::SetWindowsPassword( - const CComBSTR& windows_password) { - this->set_current_windows_password(windows_password); -} - -template <class T> bool CTestCredentialBase<T>::IsGlsRunning() { return this->IsGaiaLogonStubRunning(); } @@ -226,9 +222,9 @@ template <class T> HRESULT CTestCredentialBase<T>::GetBaseGlsCommandline( base::CommandLine* command_line) { - return FakeGlsRunHelper::GetFakeGlsCommandline( + return GlsRunnerTestBase::GetFakeGlsCommandline( default_exit_code_, gls_email_, gaia_id_override_, start_gls_event_name_, - command_line); + ignore_expected_gaia_id_, command_line); } template <class T> @@ -287,7 +283,8 @@ } // This class is used to implement a test credential based off a fully -// implemented CGaiaCredentialBase class. +// implemented CGaiaCredentialBase class that does not expose +// ICredentialProviderCredential2. template <class T> class ATL_NO_VTABLE CTestCredentialForBaseInherited : public CTestCredentialBase<T> { @@ -301,7 +298,6 @@ BEGIN_COM_MAP(CTestCredentialForBaseInherited) COM_INTERFACE_ENTRY(IGaiaCredential) COM_INTERFACE_ENTRY(ICredentialProviderCredential) - COM_INTERFACE_ENTRY(ICredentialProviderCredential2) COM_INTERFACE_ENTRY(ITestCredential) END_COM_MAP() }; @@ -313,30 +309,6 @@ CTestCredentialForBaseInherited<T>::~CTestCredentialForBaseInherited() = default; -template <class T> -HRESULT CreateBaseInheritedCredential( - ICredentialProviderCredential** credential) { - return CComCreator<CComObject<testing::CTestCredentialForBaseInherited<T>>>:: - CreateInstance(nullptr, IID_ICredentialProviderCredential, - reinterpret_cast<void**>(credential)); -} - -template <class T> -HRESULT CreateBaseInheritedCredentialWithProvider( - IGaiaCredentialProvider* provider, - IGaiaCredential** gaia_credential, - ICredentialProviderCredential** credential) { - HRESULT hr = CreateBaseInheritedCredential<T>(credential); - if (SUCCEEDED(hr)) { - hr = (*credential) - ->QueryInterface(IID_IGaiaCredential, - reinterpret_cast<void**>(gaia_credential)); - if (SUCCEEDED(hr)) - hr = (*gaia_credential)->Initialize(provider); - } - return hr; -} - // This class is used to implement a test credential based off a fully // implemented CGaiaCredentialBase class. The additional InterfaceT parameter // is used to specify any additional interfaces that should be registerd for @@ -370,30 +342,6 @@ CTestCredentialForInherited<T, InterfaceT>::~CTestCredentialForInherited() = default; -template <class T, class InterfaceT> -HRESULT CreateInheritedCredential(ICredentialProviderCredential** credential) { - return CComCreator<CComObject<testing::CTestCredentialForInherited< - T, InterfaceT>>>::CreateInstance(nullptr, - IID_ICredentialProviderCredential, - reinterpret_cast<void**>(credential)); -} - -template <class T, class InterfaceT> -HRESULT CreateInheritedCredentialWithProvider( - IGaiaCredentialProvider* provider, - IGaiaCredential** gaia_credential, - ICredentialProviderCredential** credential) { - HRESULT hr = CreateInheritedCredential<T, InterfaceT>(credential); - if (SUCCEEDED(hr)) { - hr = (*credential) - ->QueryInterface(IID_IGaiaCredential, - reinterpret_cast<void**>(gaia_credential)); - if (SUCCEEDED(hr)) - hr = (*gaia_credential)->Initialize(provider); - } - return hr; -} - } // namespace testing } // namespace credential_provider
diff --git a/chrome/credential_provider/test/test_credential_provider.h b/chrome/credential_provider/test/test_credential_provider.h new file mode 100644 index 0000000..a8ef146 --- /dev/null +++ b/chrome/credential_provider/test/test_credential_provider.h
@@ -0,0 +1,29 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_CREDENTIAL_PROVIDER_TEST_TEST_CREDENTIAL_PROVIDER_H_ +#define CHROME_CREDENTIAL_PROVIDER_TEST_TEST_CREDENTIAL_PROVIDER_H_ + +#include <atlbase.h> +#include <atlcom.h> +#include <atlcomcli.h> + +namespace credential_provider { + +namespace testing { + +class DECLSPEC_UUID("d8108fd0-1e0d-4853-9a8a-1f6aed8bf64d") + ITestCredentialProvider : public IUnknown { + public: + virtual const CComBSTR& STDMETHODCALLTYPE username() const = 0; + virtual const CComBSTR& STDMETHODCALLTYPE password() const = 0; + virtual const CComBSTR& STDMETHODCALLTYPE sid() const = 0; + virtual bool STDMETHODCALLTYPE credentials_changed_fired() const = 0; + virtual void STDMETHODCALLTYPE ResetCredentialsChangedFired() = 0; +}; + +} // namespace testing +} // namespace credential_provider + +#endif // CHROME_CREDENTIAL_PROVIDER_TEST_TEST_CREDENTIAL_PROVIDER_H_
diff --git a/chrome/renderer/extensions/chrome_extensions_renderer_client.cc b/chrome/renderer/extensions/chrome_extensions_renderer_client.cc index 04cd438..486c5a65 100644 --- a/chrome/renderer/extensions/chrome_extensions_renderer_client.cc +++ b/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
@@ -39,6 +39,7 @@ #include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h" #include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h" #include "extensions/renderer/script_context.h" +#include "services/network/public/cpp/features.h" #include "third_party/blink/public/platform/web_url.h" #include "third_party/blink/public/web/web_document.h" #include "third_party/blink/public/web/web_local_frame.h" @@ -241,6 +242,13 @@ extensions::PermissionsData::PageAccess::kAllowed) { *attach_same_site_cookies = true; } + } else if (base::FeatureList::IsEnabled( + network::features::kNetworkService)) { + // If there is no extension installed for the origin, it may be from a + // recently uninstalled extension. The tabs of such extensions are + // automatically closed, but subframes and content scripts may stick + // around. Fail such requests without killing the process. + *new_url = GURL(chrome::kExtensionInvalidRequestURL); } }
diff --git a/chrome/test/chromedriver/key_converter.cc b/chrome/test/chromedriver/key_converter.cc index e0d0eec..d372b0a 100644 --- a/chrome/test/chromedriver/key_converter.cc +++ b/chrome/test/chromedriver/key_converter.cc
@@ -35,7 +35,7 @@ // points defined by the Unicode standard. const ui::KeyboardCode kSpecialWebDriverKeys[] = { ui::VKEY_UNKNOWN, // \uE000 - ui::VKEY_UNKNOWN, + ui::VKEY_CANCEL, // \uE001 ui::VKEY_HELP, ui::VKEY_BACK, ui::VKEY_TAB,
diff --git a/chrome/test/chromedriver/key_converter_unittest.cc b/chrome/test/chromedriver/key_converter_unittest.cc index dc83a00..2e0908e 100644 --- a/chrome/test/chromedriver/key_converter_unittest.cc +++ b/chrome/test/chromedriver/key_converter_unittest.cc
@@ -376,29 +376,21 @@ int modifiers = 0; keys.push_back(0xE000U + i); std::list<KeyEvent> events; - if (i == 1) { - EXPECT_NE(kOk, ConvertKeysToKeyEvents(keys, - true /* release_modifiers*/, - &modifiers, &events).code()) - << "Index: " << i; + EXPECT_EQ(kOk, ConvertKeysToKeyEvents(keys, + true /* release_modifiers */, + &modifiers, &events).code()) + << "Index: " << i; + if (i == 0) { EXPECT_EQ(0u, events.size()) << "Index: " << i; + } else if (i >= base::size(kTextForKeys) || kTextForKeys[i] == 0) { + EXPECT_EQ(2u, events.size()) << "Index: " << i; } else { - EXPECT_EQ(kOk, ConvertKeysToKeyEvents(keys, - true /* release_modifiers */, - &modifiers, &events).code()) + ASSERT_EQ(3u, events.size()) << "Index: " << i; + std::list<KeyEvent>::const_iterator it = events.begin(); + ++it; // Move to the second event. + ASSERT_EQ(1u, it->unmodified_text.length()) << "Index: " << i; + EXPECT_EQ(kTextForKeys[i], it->unmodified_text[0]) << "Index: " << i; - if (i == 0) { - EXPECT_EQ(0u, events.size()) << "Index: " << i; - } else if (i >= base::size(kTextForKeys) || kTextForKeys[i] == 0) { - EXPECT_EQ(2u, events.size()) << "Index: " << i; - } else { - ASSERT_EQ(3u, events.size()) << "Index: " << i; - std::list<KeyEvent>::const_iterator it = events.begin(); - ++it; // Move to the second event. - ASSERT_EQ(1u, it->unmodified_text.length()) << "Index: " << i; - EXPECT_EQ(kTextForKeys[i], it->unmodified_text[0]) - << "Index: " << i; - } } } }
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn index 2871ba5e..6e1a50df 100644 --- a/chrome/test/data/webui/BUILD.gn +++ b/chrome/test/data/webui/BUILD.gn
@@ -186,6 +186,7 @@ "../../../browser/resources/chromeos/select_to_speak/rect_utils_unittest.gtestjs", "../../../browser/resources/chromeos/select_to_speak/select_to_speak_unittest.gtestjs", "../../../browser/resources/chromeos/select_to_speak/word_utils_unittest.gtestjs", + "../../../browser/resources/chromeos/switch_access/rect_helper_unittest.gtestjs", ] extra_js_files += [ "../../../browser/resources/chromeos/braille_ime/braille_ime.js", @@ -195,6 +196,7 @@ "../../../browser/resources/chromeos/select_to_speak/test_support.js", "../../../browser/resources/chromeos/select_to_speak/word_utils.js", "../../../browser/resources/chromeos/select_to_speak/node_utils.js", + "../../../browser/resources/chromeos/switch_access/rect_helper.js", ] } }
diff --git a/chrome/test/data/webui/app_management/dom_switch_test.js b/chrome/test/data/webui/app_management/dom_switch_test.js index 830f990..1905dc77 100644 --- a/chrome/test/data/webui/app_management/dom_switch_test.js +++ b/chrome/test/data/webui/app_management/dom_switch_test.js
@@ -13,7 +13,7 @@ const template = ` <dom-bind> - <template is="dom-bind"> + <template> <app-management-dom-switch id="switch"> <template> <div id='child1' route-id='1'>[[property.x]]</div> @@ -26,9 +26,7 @@ document.body.innerHTML = template; domSwitch = document.getElementById('switch'); - // TODO(dpapad): Remove conditional when Polymer 2 migration has completed. - domBind = document.querySelector( - Polymer.DomBind ? 'dom-bind' : 'template[is=\'dom-bind\']'); + domBind = document.querySelector('dom-bind'); }); test('children are attached/detached when the route changes', function() {
diff --git a/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js b/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js index 94ed2b74..b8eb86ac 100644 --- a/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js +++ b/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js
@@ -15,7 +15,7 @@ PolymerTest.clearBody(); const template = ` <dom-bind> - <template is="dom-bind"> + <template> <cr-lazy-render id="lazy"> <template> <h1> @@ -28,9 +28,7 @@ </dom-bind>`; document.body.innerHTML = template; lazy = document.getElementById('lazy'); - // TODO(dpapad): Remove conditional when Polymer 2 migration has completed. - bind = document.querySelector( - Polymer.DomBind ? 'dom-bind' : 'template[is=\'dom-bind\']'); + bind = document.querySelector('dom-bind'); }); test('stamps after get()', function() {
diff --git a/chrome/test/data/webui/cr_elements/cr_search_field_tests.js b/chrome/test/data/webui/cr_elements/cr_search_field_tests.js index 1f0578f..ca978d2a 100644 --- a/chrome/test/data/webui/cr_elements/cr_search_field_tests.js +++ b/chrome/test/data/webui/cr_elements/cr_search_field_tests.js
@@ -79,7 +79,7 @@ field.setValue('foo'); field.setValue(''); - assertEquals(['query1', '', 'query2', 'foo', ''].join(), searches.join()); + assertDeepEquals(['query1', '', 'query2', 'foo', ''], searches); }); test('does not notify on setValue with noEvent=true', function() { @@ -87,6 +87,24 @@ field.setValue('foo', true); field.setValue('bar'); field.setValue('baz', true); - assertEquals(['bar'].join(), searches.join()); + assertDeepEquals(['bar'], searches); + }); + + test('setValue will return early if the query has not changed', () => { + // Need a space at the end, since the effective query will strip the spaces + // at the beginning, but not at the end of the query. + const value = 'test '; + assertNotEquals(value, field.getValue()); + let calledSetValue = false; + field.onSearchTermInput = () => { + if (!calledSetValue) { + calledSetValue = true; + field.setValue(value); + } + }; + field.setValue(value, true); + field.setValue(` ${value} `); + assertTrue(calledSetValue); + assertEquals(0, searches.length); }); });
diff --git a/chrome/test/data/webui/settings/languages_page_tests.js b/chrome/test/data/webui/settings/languages_page_tests.js index 8faca078..49029ce 100644 --- a/chrome/test/data/webui/settings/languages_page_tests.js +++ b/chrome/test/data/webui/settings/languages_page_tests.js
@@ -317,8 +317,7 @@ let item = null; let listItems = languagesCollapse.querySelectorAll('.list-item'); - let domRepeat = assert(languagesCollapse.querySelector( - Polymer.DomRepeat ? 'dom-repeat' : 'template[is="dom-repeat"]')); + let domRepeat = assert(languagesCollapse.querySelector('dom-repeat')); let num_visibles = 0; Array.from(listItems).forEach(function(el) { @@ -407,8 +406,7 @@ // Find the new language item. const items = languagesCollapse.querySelectorAll('.list-item'); - const domRepeat = assert(languagesCollapse.querySelector( - Polymer.DomRepeat ? 'dom-repeat' : 'template[is="dom-repeat"]')); + const domRepeat = assert(languagesCollapse.querySelector('dom-repeat')); const item = Array.from(items).find(function(el) { return domRepeat.itemForElement(el) && domRepeat.itemForElement(el).language.code == 'no'; @@ -435,8 +433,7 @@ ['en-US'], languageHelper.prefs.translate_blocked_languages.value); const items = languagesCollapse.querySelectorAll('.list-item'); - const domRepeat = assert(languagesCollapse.querySelector( - Polymer.DomRepeat ? 'dom-repeat' : 'template[is="dom-repeat"]')); + const domRepeat = assert(languagesCollapse.querySelector('dom-repeat')); const item = Array.from(items).find(function(el) { return domRepeat.itemForElement(el) && domRepeat.itemForElement(el).language.code == 'en-US'; @@ -451,8 +448,7 @@ test('remove language when starting with 2 languages', function() { const items = languagesCollapse.querySelectorAll('.list-item'); - const domRepeat = assert(languagesCollapse.querySelector( - Polymer.DomRepeat ? 'dom-repeat' : 'template[is="dom-repeat"]')); + const domRepeat = assert(languagesCollapse.querySelector('dom-repeat')); const item = Array.from(items).find(function(el) { return domRepeat.itemForElement(el) && domRepeat.itemForElement(el).language.code == 'sw';
diff --git a/chrome/test/data/webui/settings/test_util.js b/chrome/test/data/webui/settings/test_util.js index 8affff3..cfdce28 100644 --- a/chrome/test/data/webui/settings/test_util.js +++ b/chrome/test/data/webui/settings/test_util.js
@@ -269,35 +269,10 @@ */ function waitForRender(element) { return new Promise(resolve => { - // TODO(dpapad): Remove early return once Polymer 2 migration is complete. - if (!Polymer.DomIf) { - resolve(); - return; - } - Polymer.RenderStatus.beforeNextRender(element, resolve); }); } - /** - * Similar to waitForRender(), but resolves after setTimeout() for Polymer 1. - * TODO (rbpotter): Delete this function when the Polymer 2 migration is - * complete, and update callers to use waitForRender(). - * @param {!Element} element - * @return {!Promise} - */ - function waitForRenderOrTimeout0(element) { - return new Promise(resolve => { - if (Polymer.DomIf) { - Polymer.RenderStatus.beforeNextRender(element, resolve); - } else { - setTimeout(() => { - resolve(); - }); - } - }); - } - return { createContentSettingTypeToValuePair: createContentSettingTypeToValuePair, createDefaultContentSetting: createDefaultContentSetting, @@ -311,8 +286,6 @@ getContentSettingsTypeFromChooserType: getContentSettingsTypeFromChooserType, waitForRender: waitForRender, - waitForRenderOrTimeout0: waitForRenderOrTimeout0, whenAttributeIs: whenAttributeIs, }; - });
diff --git a/chrome/test/data/webui/welcome/welcome_app_test.js b/chrome/test/data/webui/welcome/welcome_app_test.js index bb284d77..cd17f3d 100644 --- a/chrome/test/data/webui/welcome/welcome_app_test.js +++ b/chrome/test/data/webui/welcome/welcome_app_test.js
@@ -71,7 +71,7 @@ test('new user route (can set default)', function() { simulateCanSetDefault(); welcome.navigateTo(welcome.Routes.NEW_USER, 1); - return test_util.waitForRenderOrTimeout0(testElement).then(() => { + return test_util.waitForRender(testElement).then(() => { const views = testElement.shadowRoot.querySelectorAll('[slot=view]'); assertEquals(views.length, 5); ['LANDING-VIEW', @@ -88,7 +88,7 @@ test('new user route (cannot set default)', function() { simulateCannotSetDefault(); welcome.navigateTo(welcome.Routes.NEW_USER, 1); - return test_util.waitForRenderOrTimeout0(testElement).then(() => { + return test_util.waitForRender(testElement).then(() => { const views = testElement.shadowRoot.querySelectorAll('[slot=view]'); assertEquals(views.length, 4); ['LANDING-VIEW', @@ -104,7 +104,7 @@ test('returning user route (can set default)', function() { simulateCanSetDefault(); welcome.navigateTo(welcome.Routes.RETURNING_USER, 1); - return test_util.waitForRenderOrTimeout0(testElement).then(() => { + return test_util.waitForRender(testElement).then(() => { const views = testElement.shadowRoot.querySelectorAll('[slot=view]'); assertEquals(views.length, 2); assertEquals(views[0].tagName, 'LANDING-VIEW'); @@ -134,7 +134,7 @@ // Use the new-user route to test if nux-set-as-default module gets // initialized. welcome.navigateTo(welcome.Routes.NEW_USER, 1); - return test_util.waitForRenderOrTimeout0(testElement).then(() => { + return test_util.waitForRender(testElement).then(() => { // Use the existence of the nux-set-as-default as indication of // whether or not the promise is resolved with the expected result. assertEquals(
diff --git a/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js b/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js index ea89e4e..24534fd 100644 --- a/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js +++ b/chrome/test/data/xr/e2e_test_files/resources/webxr_boilerplate.js
@@ -41,6 +41,7 @@ constructor() { this.session = null; this.frameOfRef = null; + this.error = null; } get currentSession() { @@ -62,6 +63,7 @@ clearSession() { this.session = null; this.frameOfRef = null; + this.error = null; } } @@ -73,8 +75,7 @@ function getSessionType(session) { if (session.mode == 'immersive-vr') { return sessionTypes.IMMERSIVE; - } else if (session.mode == 'immersive-ar' || - session.mode == 'legacy-inline-ar') { + } else if (session.mode == 'immersive-ar') { return sessionTypes.AR; } else { return sessionTypes.MAGIC_WINDOW; @@ -91,6 +92,7 @@ sessionInfos[sessionTypes.IMMERSIVE].currentSession = session; onSessionStarted(session); }, (error) => { + sessionInfos[sessionTypes.IMMERSIVE].error = error; console.info('Immersive VR session request rejected with: ' + error); }); break; @@ -102,21 +104,9 @@ sessionInfos[sessionTypes.AR].currentSession = session; onSessionStarted(session); }, (error) => { + sessionInfos[sessionTypes.AR].error = error; console.info('Immersive AR session request rejected with: ' + error); - console.info('Attempting to fall back to legacy AR mode'); - navigator.xr.requestSession('legacy-inline-ar').then( - (session) => { - session.mode = 'legacy-inline-ar'; - session.updateRenderState({ - outputContext: webglCanvas.getContext('xrpresent') - }); - console.info('Legacy AR session request succeeded'); - sessionInfos[sessionTypes.AR].currentSession = session; - onSessionStarted(session); - }, (error) => { - console.info('Legacy AR session request rejected with: ' + error); - }); - }); + }); break; default: throw 'Given unsupported WebXR session type enum ' + sessionTypeToRequest;
diff --git a/chromecast/external_mojo/external_service_support/external_connector.cc b/chromecast/external_mojo/external_service_support/external_connector.cc index 388d3416..e92a3d1 100644 --- a/chromecast/external_mojo/external_service_support/external_connector.cc +++ b/chromecast/external_mojo/external_service_support/external_connector.cc
@@ -84,6 +84,15 @@ } } +void ExternalConnector::QueryServiceList( + base::OnceCallback<void( + std::vector<chromecast::external_mojo::mojom::ExternalServiceInfoPtr>)> + callback) { + if (BindConnectorIfNecessary()) { + connector_->QueryServiceList(std::move(callback)); + } +} + void ExternalConnector::BindInterface( const std::string& service_name, const std::string& interface_name,
diff --git a/chromecast/external_mojo/external_service_support/external_connector.h b/chromecast/external_mojo/external_service_support/external_connector.h index e5f0f77..ac41af79 100644 --- a/chromecast/external_mojo/external_service_support/external_connector.h +++ b/chromecast/external_mojo/external_service_support/external_connector.h
@@ -8,6 +8,7 @@ #include <memory> #include <string> #include <utility> +#include <vector> #include "base/callback.h" #include "base/macros.h" @@ -78,6 +79,13 @@ // Sends a request for a Chromium ServiceManager connector. void SendChromiumConnectorRequest(mojo::ScopedMessagePipeHandle request); + // Query the list of available services from this connector. + void QueryServiceList( + base::OnceCallback< + void(std::vector< + chromecast::external_mojo::mojom::ExternalServiceInfoPtr>)> + callback); + private: void OnConnectionError(); bool BindConnectorIfNecessary();
diff --git a/chromecast/external_mojo/public/cpp/external_mojo_broker.cc b/chromecast/external_mojo/public/cpp/external_mojo_broker.cc index bad226d..6f7d8f98 100644 --- a/chromecast/external_mojo/public/cpp/external_mojo_broker.cc +++ b/chromecast/external_mojo/public/cpp/external_mojo_broker.cc
@@ -235,6 +235,11 @@ } pending_bind_requests_.erase(p); } + + auto& info_entry = services_info_[service_name]; + info_entry.name = service_name; + info_entry.connect_time = base::TimeTicks::Now(); + info_entry.disconnect_time = base::TimeTicks(); } void BindInterface(const std::string& service_name, @@ -276,6 +281,14 @@ service_manager::mojom::ConnectorRequest(std::move(interface_pipe))); } + void QueryServiceList(QueryServiceListCallback callback) override { + std::vector<chromecast::external_mojo::mojom::ExternalServiceInfoPtr> infos; + for (const auto& it : services_info_) { + infos.emplace_back(it.second.Clone()); + } + std::move(callback).Run(std::move(infos)); + } + void OnQueryResult(const std::string& service_name, const std::string& interface_name, mojo::ScopedMessagePipeHandle interface_pipe, @@ -305,6 +318,7 @@ void OnServiceLost(const std::string& service_name) { LOG(INFO) << service_name << " disconnected"; services_.erase(service_name); + services_info_[service_name].disconnect_time = base::TimeTicks::Now(); } ServiceManagerConnectorFacade connector_facade_; @@ -316,6 +330,7 @@ std::map<std::string, mojom::ExternalServicePtr> services_; std::map<std::string, std::vector<PendingBindRequest>> pending_bind_requests_; + std::map<std::string, mojom::ExternalServiceInfo> services_info_; DISALLOW_COPY_AND_ASSIGN(ConnectorImpl); };
diff --git a/chromecast/external_mojo/public/mojom/BUILD.gn b/chromecast/external_mojo/public/mojom/BUILD.gn index 0e60fb8..0cc2050 100644 --- a/chromecast/external_mojo/public/mojom/BUILD.gn +++ b/chromecast/external_mojo/public/mojom/BUILD.gn
@@ -8,4 +8,8 @@ sources = [ "connector.mojom", ] + + public_deps = [ + "//mojo/public/mojom/base", + ] }
diff --git a/chromecast/external_mojo/public/mojom/connector.mojom b/chromecast/external_mojo/public/mojom/connector.mojom index 9939a4b..189721a7 100644 --- a/chromecast/external_mojo/public/mojom/connector.mojom +++ b/chromecast/external_mojo/public/mojom/connector.mojom
@@ -4,6 +4,19 @@ module chromecast.external_mojo.mojom; +import "mojo/public/mojom/base/time.mojom"; + +// Struct for information provided when calling QueryServiceList(). +struct ExternalServiceInfo { + // Service name provided to RegisterServiceInstance. + string name; + // TimeTicks value when the service was last registered. + mojo_base.mojom.TimeTicks connect_time; + // TimeTicks value when the service was disconnected. + // This value is set to TimeTicks() if the service is currently connected. + mojo_base.mojom.TimeTicks disconnect_time; +}; + // Interface for external (non-Chromium process) Mojo services to receive Mojo // binding requests from other processes/services. interface ExternalService { @@ -36,4 +49,7 @@ // Binds to a Chromium service_manager::Connector instance, if possible. BindChromiumConnector(handle<message_pipe> interface_pipe); + + // Query services that are available from this connector. + QueryServiceList() => (array<ExternalServiceInfo> services); };
diff --git a/chromecast/media/cma/base/demuxer_stream_for_test.cc b/chromecast/media/cma/base/demuxer_stream_for_test.cc index 983fe22..4e4c027d 100644 --- a/chromecast/media/cma/base/demuxer_stream_for_test.cc +++ b/chromecast/media/cma/base/demuxer_stream_for_test.cc
@@ -58,7 +58,7 @@ return ::media::VideoDecoderConfig( ::media::kCodecH264, ::media::VIDEO_CODEC_PROFILE_UNKNOWN, ::media::PIXEL_FORMAT_YV12, ::media::VideoColorSpace(), - ::media::VIDEO_ROTATION_0, coded_size, visible_rect, natural_size, + ::media::kNoTransformation, coded_size, visible_rect, natural_size, ::media::EmptyExtraData(), ::media::Unencrypted()); }
diff --git a/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc b/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc index 847a567d..73de848f 100644 --- a/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc +++ b/chromecast/media/cma/pipeline/audio_video_pipeline_impl_unittest.cc
@@ -155,7 +155,7 @@ video_configs.push_back(::media::VideoDecoderConfig( ::media::kCodecH264, ::media::H264PROFILE_MAIN, ::media::PIXEL_FORMAT_I420, ::media::VideoColorSpace(), - ::media::VIDEO_ROTATION_0, gfx::Size(640, 480), + ::media::kNoTransformation, gfx::Size(640, 480), gfx::Rect(0, 0, 640, 480), gfx::Size(640, 480), ::media::EmptyExtraData(), ::media::EncryptionScheme())); VideoPipelineClient client;
diff --git a/chromecast/media/cma/test/mock_frame_provider.cc b/chromecast/media/cma/test/mock_frame_provider.cc index 83f3593b..7a23e77 100644 --- a/chromecast/media/cma/test/mock_frame_provider.cc +++ b/chromecast/media/cma/test/mock_frame_provider.cc
@@ -82,7 +82,7 @@ video_config = ::media::VideoDecoderConfig( ::media::kCodecH264, ::media::VIDEO_CODEC_PROFILE_UNKNOWN, ::media::PIXEL_FORMAT_YV12, ::media::VideoColorSpace(), - ::media::VIDEO_ROTATION_0, coded_size, visible_rect, natural_size, + ::media::kNoTransformation, coded_size, visible_rect, natural_size, ::media::EmptyExtraData(), ::media::Unencrypted()); audio_config = ::media::AudioDecoderConfig(
diff --git a/components/cronet/native/engine.cc b/components/cronet/native/engine.cc index 299af9d6..59698194 100644 --- a/components/cronet/native/engine.cc +++ b/components/cronet/native/engine.cc
@@ -18,6 +18,7 @@ #include "components/cronet/cronet_url_request_context.h" #include "components/cronet/native/generated/cronet.idl_impl_struct.h" #include "components/cronet/native/include/cronet_c.h" +#include "components/cronet/native/runnables.h" #include "components/cronet/url_request_context_config.h" #include "components/cronet/version.h" #include "components/grpc_support/include/bidirectional_stream_c.h" @@ -288,6 +289,35 @@ } } +using RequestInfo = base::RefCountedData<Cronet_RequestFinishedInfo>; + +void Cronet_EngineImpl::ReportRequestFinished( + scoped_refptr<RequestInfo> request_info) { + base::flat_map<Cronet_RequestFinishedInfoListenerPtr, Cronet_ExecutorPtr> + registrations; + { + base::AutoLock lock(lock_); + // We copy under to avoid calling callbacks (which may run on direct + // executors and call Engine methods) with the lock held. + // + // The map holds only pointers and shouldn't be very large. + registrations = request_finished_registrations_; + } + for (auto& pair : registrations) { + auto* request_finished_listener = pair.first; + auto* request_finished_executor = pair.second; + + request_finished_executor->Execute( + new cronet::OnceClosureRunnable(base::BindOnce( + [](scoped_refptr<RequestInfo> request_info, + Cronet_RequestFinishedInfoListenerPtr + request_finished_listener) { + request_finished_listener->OnRequestFinished(&request_info->data); + }, + request_info, request_finished_listener))); + } +} + Cronet_RESULT Cronet_EngineImpl::CheckResult(Cronet_RESULT result) { if (enable_check_result_) CHECK_EQ(Cronet_RESULT_SUCCESS, result);
diff --git a/components/cronet/native/engine.h b/components/cronet/native/engine.h index 14ead83..2075d9c1 100644 --- a/components/cronet/native/engine.h +++ b/components/cronet/native/engine.h
@@ -10,6 +10,8 @@ #include "base/containers/flat_map.h" #include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_refptr.h" #include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" #include "base/thread_annotations.h" @@ -65,6 +67,11 @@ // AddRequestFinishedListener()), and false otherwise. bool HasRequestFinishedListener(); + // Provide |request_info| to all registered RequestFinishedListeners. + void ReportRequestFinished( + scoped_refptr<base::RefCountedData<Cronet_RequestFinishedInfo>> + request_info); + private: class StreamEngineImpl; class Callback;
diff --git a/components/cronet/native/engine_unittest.cc b/components/cronet/native/engine_unittest.cc index 9b2e3769..86532934 100644 --- a/components/cronet/native/engine_unittest.cc +++ b/components/cronet/native/engine_unittest.cc
@@ -13,6 +13,9 @@ namespace { +// Fake sent byte count for metrics testing. +constexpr int64_t kSentByteCount = 12345; + // App implementation of Cronet_Executor methods. void TestExecutor_Execute(Cronet_ExecutorPtr self, Cronet_RunnablePtr command) { CHECK(self); @@ -20,11 +23,24 @@ Cronet_Runnable_Destroy(command); } +// Context for TestRequestInfoListener_OnRequestFinished(). +using TestOnRequestFinishedClientContext = int; + // App implementation of Cronet_RequestFinishedInfoListener methods. +// +// Expects a client context of type TestOnRequestFinishedClientContext -- will +// increment this value. void TestRequestInfoListener_OnRequestFinished( Cronet_RequestFinishedInfoListenerPtr self, Cronet_RequestFinishedInfoPtr request_info) { CHECK(self); + Cronet_ClientContext context = + Cronet_RequestFinishedInfoListener_GetClientContext(self); + auto* listener_run_count = + static_cast<TestOnRequestFinishedClientContext*>(context); + ++(*listener_run_count); + auto* metrics = Cronet_RequestFinishedInfo_metrics_get(request_info); + EXPECT_EQ(kSentByteCount, Cronet_Metrics_sent_byte_count_get(metrics)); } TEST(EngineUnitTest, HasNoRequestFinishedInfoListener) { @@ -56,6 +72,43 @@ Cronet_Engine_Destroy(engine); } +TEST(EngineUnitTest, RequestFinishedInfoListeners) { + using RequestInfo = base::RefCountedData<Cronet_RequestFinishedInfo>; + constexpr int kNumListeners = 5; + TestOnRequestFinishedClientContext listener_run_count = 0; + + Cronet_EnginePtr engine = Cronet_Engine_Create(); + Cronet_EngineParamsPtr engine_params = Cronet_EngineParams_Create(); + + Cronet_RequestFinishedInfoListenerPtr listeners[kNumListeners]; + Cronet_ExecutorPtr executor = + Cronet_Executor_CreateWith(TestExecutor_Execute); + for (int i = 0; i < kNumListeners; ++i) { + listeners[i] = Cronet_RequestFinishedInfoListener_CreateWith( + TestRequestInfoListener_OnRequestFinished); + Cronet_RequestFinishedInfoListener_SetClientContext(listeners[i], + &listener_run_count); + Cronet_Engine_AddRequestFinishedListener(engine, listeners[i], executor); + } + + // Simulate the UrlRequest reporting metrics to the engine. + auto* engine_impl = static_cast<Cronet_EngineImpl*>(engine); + auto request_info = base::MakeRefCounted<RequestInfo>(); + auto metrics = std::make_unique<Cronet_Metrics>(); + metrics->sent_byte_count = kSentByteCount; + request_info->data.metrics.emplace(*metrics); + engine_impl->ReportRequestFinished(request_info); + EXPECT_EQ(kNumListeners, listener_run_count); + + for (auto* listener : listeners) { + Cronet_RequestFinishedInfoListener_Destroy(listener); + Cronet_Engine_RemoveRequestFinishedListener(engine, listener); + } + Cronet_Executor_Destroy(executor); + Cronet_Engine_Destroy(engine); + Cronet_EngineParams_Destroy(engine_params); +} + // EXPECT_DEBUG_DEATH(), used by the tests below, isn't available on iOS. #if !defined(OS_IOS)
diff --git a/components/cronet/native/test/BUILD.gn b/components/cronet/native/test/BUILD.gn index bcd4e08..023157d 100644 --- a/components/cronet/native/test/BUILD.gn +++ b/components/cronet/native/test/BUILD.gn
@@ -37,6 +37,7 @@ "//components/grpc_support:bidirectional_stream_test", "//components/grpc_support/test:get_stream_engine_header", "//net:test_support", + "//testing/gmock", "//testing/gtest", ]
diff --git a/components/cronet/native/test/url_request_test.cc b/components/cronet/native/test/url_request_test.cc index 29b70b2..ee4ecccd 100644 --- a/components/cronet/native/test/url_request_test.cc +++ b/components/cronet/native/test/url_request_test.cc
@@ -18,12 +18,15 @@ #include "components/cronet/native/test/test_util.h" #include "components/cronet/test/test_server.h" #include "cronet_c.h" +#include "net/test/embedded_test_server/default_handlers.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" using cronet::test::TestUploadDataProvider; using cronet::test::TestUrlRequestCallback; +using ::testing::HasSubstr; namespace { @@ -134,8 +137,9 @@ const std::string& url, std::unique_ptr<TestUrlRequestCallback> test_callback, const std::string& http_method, - TestUploadDataProvider* test_upload_data_provider) { - Cronet_EnginePtr engine = cronet::test::CreateTestEngine(0); + TestUploadDataProvider* test_upload_data_provider, + int remapped_port) { + Cronet_EnginePtr engine = cronet::test::CreateTestEngine(remapped_port); Cronet_UrlRequestPtr request = Cronet_UrlRequest_Create(); Cronet_UrlRequestParamsPtr request_params = Cronet_UrlRequestParams_Create(); @@ -189,6 +193,16 @@ std::unique_ptr<TestUrlRequestCallback> StartAndWaitForComplete( const std::string& url, + std::unique_ptr<TestUrlRequestCallback> test_callback, + const std::string& http_method, + TestUploadDataProvider* test_upload_data_provider) { + return StartAndWaitForComplete(url, std::move(test_callback), http_method, + test_upload_data_provider, + /* remapped_port = */ 0); + } + + std::unique_ptr<TestUrlRequestCallback> StartAndWaitForComplete( + const std::string& url, std::unique_ptr<TestUrlRequestCallback> test_callback) { return StartAndWaitForComplete(url, std::move(test_callback), /* http_method = */ std::string(), @@ -519,6 +533,28 @@ EXPECT_EQ("net::ERR_CERT_INVALID", callback->last_error_message_); } +TEST_P(UrlRequestTest, SSLUpload) { + net::EmbeddedTestServer ssl_server(net::EmbeddedTestServer::TYPE_HTTPS); + net::test_server::RegisterDefaultHandlers(&ssl_server); + ASSERT_TRUE(ssl_server.Start()); + + constexpr char kUrl[] = "https://test.example.com/echoall"; + constexpr char kUploadString[] = + "The quick brown fox jumps over the lazy dog."; + TestUploadDataProvider data_provider(TestUploadDataProvider::SYNC, + /* executor = */ nullptr); + data_provider.AddRead(kUploadString); + auto callback = + std::make_unique<TestUrlRequestCallback>(GetDirectExecutorParam()); + callback = StartAndWaitForComplete(kUrl, std::move(callback), std::string(), + &data_provider, ssl_server.port()); + data_provider.AssertClosed(); + EXPECT_NE(nullptr, callback->response_info_); + EXPECT_EQ("", callback->last_error_message_); + EXPECT_EQ(200, callback->response_info_->http_status_code); + EXPECT_THAT(callback->response_as_string_, HasSubstr(kUploadString)); +} + TEST_P(UrlRequestTest, UploadMultiplePiecesSync) { const std::string url = cronet::TestServer::GetEchoRequestBodyURL(); auto callback =
diff --git a/components/dom_distiller/core/css/distilledpage.css b/components/dom_distiller/core/css/distilledpage.css index 9880e2cd..9a28e9b 100644 --- a/components/dom_distiller/core/css/distilledpage.css +++ b/components/dom_distiller/core/css/distilledpage.css
@@ -2,6 +2,10 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ +/* This file contains style used across ALL platforms. Platform-specific styling + * should be placed in the corresponding file (e.g. desktop style goes in + * distilledpage_desktop.css).*/ + /* Set the global 'box-sizing' state to 'border-box'. * *::after and *::before used to select pseudo-elements not selectable by *. */ @@ -287,12 +291,6 @@ width: 100%; } -@media screen { - #mainContent { - max-width: 35em; - } -} - #articleHeader { margin-top: 24px; width: 100%; @@ -402,4 +400,3 @@ top: 0px; width: 100%; } -
diff --git a/components/dom_distiller/core/css/distilledpage_desktop.css b/components/dom_distiller/core/css/distilledpage_desktop.css new file mode 100644 index 0000000..a41abde5 --- /dev/null +++ b/components/dom_distiller/core/css/distilledpage_desktop.css
@@ -0,0 +1,13 @@ +/* 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. */ + +/* This file should contain style used on desktop but not Android or iOS. */ + +#mainContent { + width: 75%; + box-shadow: 0 0 5px 1px rgba(0, 0, 0, 0.4); + padding: 1em; + margin-top: 1em; + margin-bottom: 1em; +}
diff --git a/components/dom_distiller/core/css/distilledpage_mobile.css b/components/dom_distiller/core/css/distilledpage_mobile.css new file mode 100644 index 0000000..73e8b23 --- /dev/null +++ b/components/dom_distiller/core/css/distilledpage_mobile.css
@@ -0,0 +1,9 @@ +/* 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. */ + +/* This file should contain style shared by Android and iOS but not desktop. */ + +#mainContent { + max-width: 35em; +}
diff --git a/components/dom_distiller/core/viewer.cc b/components/dom_distiller/core/viewer.cc index 8c3f533..9bdc077c 100644 --- a/components/dom_distiller/core/viewer.cc +++ b/components/dom_distiller/core/viewer.cc
@@ -10,6 +10,7 @@ #include "base/json/json_writer.h" #include "base/metrics/histogram_macros.h" +#include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "build/build_config.h" @@ -54,6 +55,23 @@ const char kSansSerifCssClass[] = "sans-serif"; const char kMonospaceCssClass[] = "monospace"; +std::string GetCssFromResourceId(int id) { + return ui::ResourceBundle::GetSharedInstance() + .GetRawDataResource(id) + .as_string(); +} + +std::string GetPlatformSpecificCss() { +#if defined(OS_IOS) + return base::StrCat({GetCssFromResourceId(IDR_DISTILLER_MOBILE_CSS), + GetCssFromResourceId(IDR_DISTILLER_IOS_CSS)}); +#elif defined(OS_ANDROID) + return GetCssFromResourceId(IDR_DISTILLER_MOBILE_CSS); +#else // Desktop + return GetCssFromResourceId(IDR_DISTILLER_DESKTOP_CSS); +#endif +} + // Maps themes to JS themes. const std::string GetJsTheme(DistilledPagePrefs::Theme theme) { if (theme == DistilledPagePrefs::THEME_DARK) @@ -112,7 +130,7 @@ #if defined(OS_IOS) // On iOS the content is inlined as there is no API to detect those requests // and return the local data once a page is loaded. - css << "<style>" << viewer::GetCss() << viewer::GetIOSCss() << "</style>"; + css << "<style>" << viewer::GetCss() << "</style>"; svg << viewer::GetLoadingImage(); #else css << "<link rel=\"stylesheet\" href=\"/" << kViewerCssPath << "\">"; @@ -215,9 +233,8 @@ } const std::string GetCss() { - return ui::ResourceBundle::GetSharedInstance() - .GetRawDataResource(IDR_DISTILLER_CSS) - .as_string(); + return base::StrCat( + {GetCssFromResourceId(IDR_DISTILLER_CSS), GetPlatformSpecificCss()}); } const std::string GetLoadingImage() { @@ -226,12 +243,6 @@ .as_string(); } -const std::string GetIOSCss() { - return ui::ResourceBundle::GetSharedInstance() - .GetRawDataResource(IDR_DISTILLER_IOS_CSS) - .as_string(); -} - const std::string GetJavaScript() { return ui::ResourceBundle::GetSharedInstance() .GetRawDataResource(IDR_DOM_DISTILLER_VIEWER_JS)
diff --git a/components/dom_distiller/core/viewer.h b/components/dom_distiller/core/viewer.h index eef5b17..724e95a 100644 --- a/components/dom_distiller/core/viewer.h +++ b/components/dom_distiller/core/viewer.h
@@ -62,15 +62,12 @@ // the last page of the article (i.e. loading indicator should be removed). const std::string GetToggleLoadingIndicatorJs(bool is_last_page); -// Returns the default CSS to be used for a viewer. +// Returns the CSS to use for a viewer. const std::string GetCss(); // Returns the animated SVG loading image for a viewer. const std::string GetLoadingImage(); -// Returns the iOS specific CSS to be used for the distiller viewer. -const std::string GetIOSCss(); - // Returns the default JS to be used for a viewer. const std::string GetJavaScript();
diff --git a/components/password_manager/content/common/credential_manager_mojom_traits.cc b/components/password_manager/content/common/credential_manager_mojom_traits.cc index dff9cb42..97a463e8 100644 --- a/components/password_manager/content/common/credential_manager_mojom_traits.cc +++ b/components/password_manager/content/common/credential_manager_mojom_traits.cc
@@ -96,6 +96,7 @@ case blink::mojom::CredentialManagerError::NOT_IMPLEMENTED: case blink::mojom::CredentialManagerError::NOT_FOCUSED: case blink::mojom::CredentialManagerError::RESIDENT_CREDENTIALS_UNSUPPORTED: + case blink::mojom::CredentialManagerError::PROTECTION_POLICY_INCONSISTENT: case blink::mojom::CredentialManagerError::UNKNOWN: *output = password_manager::CredentialManagerError::UNKNOWN; return true;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index 644310a..998043c 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json
@@ -11944,9 +11944,9 @@ 'caption': '''Password entry is required every twelve hours''', }, { - 'name': 'Day', + 'name': 'TwoDays', 'value': 2, - 'caption': '''Password entry is required every day (24 hours)''', + 'caption': '''Password entry is required every two days (48 hours)''', }, { 'name': 'Week',
diff --git a/components/resources/dom_distiller_resources.grdp b/components/resources/dom_distiller_resources.grdp index 3c3cc1a..a274979e 100644 --- a/components/resources/dom_distiller_resources.grdp +++ b/components/resources/dom_distiller_resources.grdp
@@ -7,7 +7,9 @@ <include name="IDR_DOM_DISTILLER_VIEWER_JS" file="../dom_distiller/core/javascript/dom_distiller_viewer.js" type="BINDATA" /> <include name="IDR_DISTILLER_JS" file="../dom_distiller/core/javascript/domdistiller.js" flattenhtml="true" type="BINDATA" /> <include name="IDR_DISTILLER_CSS" file="../dom_distiller/core/css/distilledpage.css" type="BINDATA" /> + <include name="IDR_DISTILLER_DESKTOP_CSS" file="../dom_distiller/core/css/distilledpage_desktop.css" type="BINDATA" /> <include name="IDR_DISTILLER_IOS_CSS" file="../dom_distiller/core/css/distilledpage_ios.css" type="BINDATA" /> + <include name="IDR_DISTILLER_MOBILE_CSS" file="../dom_distiller/core/css/distilledpage_mobile.css" type="BINDATA" /> <include name="IDR_DISTILLER_LOADING_IMAGE" file="../dom_distiller/core/images/dom_distiller_material_spinner.svg" type="BINDATA" /> <include name="IDR_EXTRACT_PAGE_FEATURES_JS" file="../dom_distiller/core/javascript/extract_features.js" type="BINDATA" /> <include name="IDR_DISTILLABLE_PAGE_SERIALIZED_MODEL_NEW" file="../dom_distiller/core/data/distillable_page_model_new.bin" type="BINDATA" />
diff --git a/components/test/data/vr_browser_video/render_tests/VrBrowserWebInputEditingTest.fullscreen_video_paused_browser_content.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_video/render_tests/VrBrowserWebInputEditingTest.fullscreen_video_paused_browser_content.Pixel_XL-25.png.sha1 index 5714e3f9..f46fb206 100644 --- a/components/test/data/vr_browser_video/render_tests/VrBrowserWebInputEditingTest.fullscreen_video_paused_browser_content.Pixel_XL-25.png.sha1 +++ b/components/test/data/vr_browser_video/render_tests/VrBrowserWebInputEditingTest.fullscreen_video_paused_browser_content.Pixel_XL-25.png.sha1
@@ -1 +1 @@ -9d6c493f9dfe09fce6eaca19349a88d149cb2898 \ No newline at end of file +9ef5447ea3d00d9978e8389ab1f25eb12d012396 \ No newline at end of file
diff --git a/components/test/data/vr_browser_video/render_tests/VrBrowserWebInputEditingTest.fullscreen_video_paused_browser_content.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_video/render_tests/VrBrowserWebInputEditingTest.fullscreen_video_paused_browser_content.Pixel_XL-26.png.sha1 index 609f6d8..63d588f5 100644 --- a/components/test/data/vr_browser_video/render_tests/VrBrowserWebInputEditingTest.fullscreen_video_paused_browser_content.Pixel_XL-26.png.sha1 +++ b/components/test/data/vr_browser_video/render_tests/VrBrowserWebInputEditingTest.fullscreen_video_paused_browser_content.Pixel_XL-26.png.sha1
@@ -1 +1 @@ -6076800c2aa6ed0cd77881abfe553230a2529cd1 \ No newline at end of file +3a6e79cb8d4e001ebb370274022286eff523dee9 \ No newline at end of file
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc index 0cdf417..1643390 100644 --- a/components/viz/common/features.cc +++ b/components/viz/common/features.cc
@@ -64,19 +64,11 @@ } bool IsVizDisplayCompositorEnabled() { -#if defined(OS_MACOSX) || defined(OS_WIN) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS)) - // We can't remove the feature switch yet because OOP-D isn't enabled on all - // platforms but turning it off on Mac, Windows and Linux is broken. Don't - // check the feature switch for these platforms anymore. - return true; -#else #if defined(OS_ANDROID) if (features::IsAndroidSurfaceControlEnabled()) return true; #endif return base::FeatureList::IsEnabled(kVizDisplayCompositor); -#endif } bool IsVizHitTestingDebugEnabled() {
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn index 2134574..6ad1ea5 100644 --- a/components/viz/service/BUILD.gn +++ b/components/viz/service/BUILD.gn
@@ -322,6 +322,8 @@ "display_embedder/skia_output_device_gl.h", "display_embedder/skia_output_device_offscreen.cc", "display_embedder/skia_output_device_offscreen.h", + "display_embedder/skia_output_surface_base.cc", + "display_embedder/skia_output_surface_base.h", "display_embedder/skia_output_surface_impl.cc", "display_embedder/skia_output_surface_impl.h", "display_embedder/skia_output_surface_impl_non_ddl.cc",
diff --git a/components/viz/service/display_embedder/skia_output_surface_base.cc b/components/viz/service/display_embedder/skia_output_surface_base.cc new file mode 100644 index 0000000..b11dcce --- /dev/null +++ b/components/viz/service/display_embedder/skia_output_surface_base.cc
@@ -0,0 +1,147 @@ +// 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 "components/viz/service/display_embedder/skia_output_surface_base.h" + +#include "build/build_config.h" +#include "components/viz/service/display/output_surface_frame.h" +#include "components/viz/service/display/resource_metadata.h" +#include "components/viz/service/display_embedder/image_context.h" +#include "components/viz/service/gl/gpu_service_impl.h" +#include "ui/gl/gl_bindings.h" + +namespace viz { + +SkiaOutputSurfaceBase::SkiaOutputSurfaceBase() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); +} + +SkiaOutputSurfaceBase::~SkiaOutputSurfaceBase() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); +} + +void SkiaOutputSurfaceBase::BindToClient(OutputSurfaceClient* client) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(client); + DCHECK(!client_); + client_ = client; +} + +void SkiaOutputSurfaceBase::BindFramebuffer() { + // TODO(penghuang): remove this method when GLRenderer is removed. +} + +void SkiaOutputSurfaceBase::SetDrawRectangle(const gfx::Rect& draw_rectangle) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + // This GLSurface::SetDrawRectangle is a no-op for all GLSurface subclasses + // except DirectCompositionSurfaceWin. +#if defined(OS_WIN) + NOTIMPLEMENTED(); +#endif +} + +void SkiaOutputSurfaceBase::SwapBuffers(OutputSurfaceFrame frame) { + NOTREACHED(); +} + +uint32_t SkiaOutputSurfaceBase::GetFramebufferCopyTextureFormat() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + return GL_RGB; +} + +OverlayCandidateValidator* SkiaOutputSurfaceBase::GetOverlayCandidateValidator() + const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return nullptr; +} + +bool SkiaOutputSurfaceBase::IsDisplayedAsOverlayPlane() const { + return false; +} + +unsigned SkiaOutputSurfaceBase::GetOverlayTextureId() const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return 0; +} + +gfx::BufferFormat SkiaOutputSurfaceBase::GetOverlayBufferFormat() const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return gfx::BufferFormat::RGBX_8888; +} + +bool SkiaOutputSurfaceBase::HasExternalStencilTest() const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + return false; +} + +void SkiaOutputSurfaceBase::ApplyExternalStencil() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); +} + +unsigned SkiaOutputSurfaceBase::UpdateGpuFence() { + return 0; +} + +void SkiaOutputSurfaceBase::SetNeedsSwapSizeNotifications( + bool needs_swap_size_notifications) { + needs_swap_size_notifications_ = needs_swap_size_notifications; +} + +void SkiaOutputSurfaceBase::SetUpdateVSyncParametersCallback( + UpdateVSyncParametersCallback callback) {} + +void SkiaOutputSurfaceBase::AddContextLostObserver( + ContextLostObserver* observer) { + observers_.AddObserver(observer); +} + +void SkiaOutputSurfaceBase::RemoveContextLostObserver( + ContextLostObserver* observer) { + observers_.RemoveObserver(observer); +} + +void SkiaOutputSurfaceBase::PrepareYUVATextureIndices( + const std::vector<ResourceMetadata>& metadatas, + bool has_alpha, + SkYUVAIndex indices[4]) { + DCHECK((has_alpha && (metadatas.size() == 3 || metadatas.size() == 4)) || + (!has_alpha && (metadatas.size() == 2 || metadatas.size() == 3))); + + bool uv_interleaved = + has_alpha ? metadatas.size() == 3 : metadatas.size() == 2; + + indices[SkYUVAIndex::kY_Index].fIndex = 0; + indices[SkYUVAIndex::kY_Index].fChannel = SkColorChannel::kR; + + if (uv_interleaved) { + indices[SkYUVAIndex::kU_Index].fIndex = 1; + indices[SkYUVAIndex::kU_Index].fChannel = SkColorChannel::kR; + + indices[SkYUVAIndex::kV_Index].fIndex = 1; + indices[SkYUVAIndex::kV_Index].fChannel = SkColorChannel::kG; + + indices[SkYUVAIndex::kA_Index].fIndex = has_alpha ? 2 : -1; + indices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR; + } else { + indices[SkYUVAIndex::kU_Index].fIndex = 1; + indices[SkYUVAIndex::kU_Index].fChannel = SkColorChannel::kR; + + indices[SkYUVAIndex::kV_Index].fIndex = 2; + indices[SkYUVAIndex::kV_Index].fChannel = SkColorChannel::kR; + + indices[SkYUVAIndex::kA_Index].fIndex = has_alpha ? 3 : -1; + indices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR; + } +} + +void SkiaOutputSurfaceBase::ContextLost() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + for (auto& observer : observers_) + observer.OnContextLost(); +} + +} // namespace viz
diff --git a/components/viz/service/display_embedder/skia_output_surface_base.h b/components/viz/service/display_embedder/skia_output_surface_base.h new file mode 100644 index 0000000..7eba081 --- /dev/null +++ b/components/viz/service/display_embedder/skia_output_surface_base.h
@@ -0,0 +1,75 @@ +// 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 COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_BASE_H_ +#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_BASE_H_ + +#include <vector> + +#include "base/macros.h" +#include "base/observer_list.h" +#include "base/threading/thread_checker.h" +#include "components/viz/service/display/skia_output_surface.h" +#include "components/viz/service/viz_service_export.h" +#include "third_party/skia/include/core/SkYUVAIndex.h" + +namespace viz { + +struct ImageContext; + +class VIZ_SERVICE_EXPORT SkiaOutputSurfaceBase : public SkiaOutputSurface { + public: + // OutputSurface implementation: + void BindToClient(OutputSurfaceClient* client) override; + void BindFramebuffer() override; + void SetDrawRectangle(const gfx::Rect& draw_rectangle) override; + void SwapBuffers(OutputSurfaceFrame frame) override; + uint32_t GetFramebufferCopyTextureFormat() override; + OverlayCandidateValidator* GetOverlayCandidateValidator() const override; + bool IsDisplayedAsOverlayPlane() const override; + unsigned GetOverlayTextureId() const override; + gfx::BufferFormat GetOverlayBufferFormat() const override; + bool HasExternalStencilTest() const override; + void ApplyExternalStencil() override; + unsigned UpdateGpuFence() override; + void SetNeedsSwapSizeNotifications( + bool needs_swap_size_notifications) override; + void SetUpdateVSyncParametersCallback( + UpdateVSyncParametersCallback callback) override; + + // SkiaOutputSurface implementation: + void AddContextLostObserver(ContextLostObserver* observer) override; + void RemoveContextLostObserver(ContextLostObserver* observer) override; + + protected: + SkiaOutputSurfaceBase(); + ~SkiaOutputSurfaceBase() override; + + void PrepareYUVATextureIndices(const std::vector<ResourceMetadata>& metadatas, + bool has_alpha, + SkYUVAIndex indices[4]); + void ContextLost(); + + OutputSurfaceClient* client_ = nullptr; + bool needs_swap_size_notifications_ = false; + + // Cached promise image. + base::flat_map<ResourceId, std::unique_ptr<ImageContext>> + promise_image_cache_; + + // Images for current frame or render pass. + std::vector<ImageContext*> images_in_current_paint_; + + THREAD_CHECKER(thread_checker_); + + private: + // Observers for context lost. + base::ObserverList<ContextLostObserver>::Unchecked observers_; + + DISALLOW_COPY_AND_ASSIGN(SkiaOutputSurfaceBase); +}; + +} // namespace viz + +#endif // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SKIA_OUTPUT_SURFACE_BASE_H_
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc index 45f09fc2..13b89f2 100644 --- a/components/viz/service/display_embedder/skia_output_surface_impl.cc +++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -17,7 +17,6 @@ #include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_util.h" -#include "components/viz/common/gpu/context_lost_observer.h" #include "components/viz/common/resources/resource_format_utils.h" #include "components/viz/service/display/output_surface_client.h" #include "components/viz/service/display/output_surface_frame.h" @@ -29,9 +28,7 @@ #include "gpu/command_buffer/service/scheduler.h" #include "gpu/command_buffer/service/shared_image_representation.h" #include "gpu/vulkan/buildflags.h" -#include "third_party/skia/include/core/SkYUVAIndex.h" #include "ui/gfx/skia_util.h" -#include "ui/gl/gl_bindings.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_gl_api_implementation.h" @@ -71,7 +68,7 @@ gpu::SurfaceHandle surface_handle, const RendererSettings& renderer_settings) : gpu_service_(gpu_service), - is_using_vulkan_(gpu_service->is_using_vulkan()), + is_using_vulkan_(gpu_service_->is_using_vulkan()), surface_handle_(surface_handle), renderer_settings_(renderer_settings), weak_ptr_factory_(this) { @@ -118,10 +115,7 @@ void SkiaOutputSurfaceImpl::BindToClient(OutputSurfaceClient* client) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK(client); - DCHECK(!client_); - - client_ = client; + SkiaOutputSurfaceBase::BindToClient(client); weak_ptr_ = weak_ptr_factory_.GetWeakPtr(); client_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get(); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, @@ -150,20 +144,6 @@ ScheduleGpuTask(std::move(callback), std::vector<gpu::SyncToken>()); } -void SkiaOutputSurfaceImpl::BindFramebuffer() { - // TODO(penghuang): remove this method when GLRenderer is removed. -} - -void SkiaOutputSurfaceImpl::SetDrawRectangle(const gfx::Rect& draw_rectangle) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - // This GLSurface::SetDrawRectangle is a no-op for all GLSurface subclasses - // except DirectCompositionSurfaceWin. -#if defined(OS_WIN) - NOTIMPLEMENTED(); -#endif -} - void SkiaOutputSurfaceImpl::Reshape(const gfx::Size& size, float device_scale_factor, const gfx::ColorSpace& color_space, @@ -197,55 +177,6 @@ ScheduleGpuTask(std::move(callback), std::vector<gpu::SyncToken>()); } -void SkiaOutputSurfaceImpl::SwapBuffers(OutputSurfaceFrame frame) { - NOTREACHED(); -} - -uint32_t SkiaOutputSurfaceImpl::GetFramebufferCopyTextureFormat() { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - return GL_RGB; -} - -OverlayCandidateValidator* SkiaOutputSurfaceImpl::GetOverlayCandidateValidator() - const { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return nullptr; -} - -bool SkiaOutputSurfaceImpl::IsDisplayedAsOverlayPlane() const { - return false; -} - -unsigned SkiaOutputSurfaceImpl::GetOverlayTextureId() const { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return 0; -} - -gfx::BufferFormat SkiaOutputSurfaceImpl::GetOverlayBufferFormat() const { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return gfx::BufferFormat::RGBX_8888; -} - -bool SkiaOutputSurfaceImpl::HasExternalStencilTest() const { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - return false; -} - -void SkiaOutputSurfaceImpl::ApplyExternalStencil() { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); -} - -unsigned SkiaOutputSurfaceImpl::UpdateGpuFence() { - return 0; -} - -void SkiaOutputSurfaceImpl::SetNeedsSwapSizeNotifications( - bool needs_swap_size_notifications) { - needs_swap_size_notifications_ = needs_swap_size_notifications; -} - void SkiaOutputSurfaceImpl::SetUpdateVSyncParametersCallback( UpdateVSyncParametersCallback callback) { update_vsync_parameters_callback_ = std::move(callback); @@ -322,23 +253,15 @@ DCHECK((has_alpha && (metadatas.size() == 3 || metadatas.size() == 4)) || (!has_alpha && (metadatas.size() == 2 || metadatas.size() == 3))); - bool uv_interleaved = - has_alpha ? metadatas.size() == 3 : metadatas.size() == 2; + SkYUVAIndex indices[4]; + PrepareYUVATextureIndices(metadatas, has_alpha, indices); - GrBackendFormat formats[4]; - SkYUVAIndex indices[4] = { - {-1, SkColorChannel::kR}, - {-1, SkColorChannel::kR}, - {-1, SkColorChannel::kR}, - {-1, SkColorChannel::kR}, - }; + GrBackendFormat formats[4] = {}; SkISize yuva_sizes[4] = {}; SkDeferredDisplayListRecorder::PromiseImageTextureContext - texture_contexts[4] = {nullptr, nullptr, nullptr, nullptr}; - - std::vector<std::unique_ptr<ImageContext>> image_contexts(metadatas.size()); - const auto process_planar = [&](size_t i) { - auto metadata = metadatas[i]; + texture_contexts[4] = {}; + for (size_t i = 0; i < metadatas.size(); ++i) { + const auto& metadata = metadatas[i]; DCHECK(metadata.origin == kTopLeft_GrSurfaceOrigin); formats[i] = GetGrBackendFormatForTexture( metadata.resource_format, metadata.mailbox_holder.texture_target); @@ -359,42 +282,8 @@ } images_in_current_paint_.push_back(image_context.get()); texture_contexts[i] = image_context.get(); - }; - - if (uv_interleaved) { - process_planar(0); - indices[SkYUVAIndex::kY_Index].fIndex = 0; - indices[SkYUVAIndex::kY_Index].fChannel = SkColorChannel::kR; - - process_planar(1); - indices[SkYUVAIndex::kU_Index].fIndex = 1; - indices[SkYUVAIndex::kU_Index].fChannel = SkColorChannel::kR; - - indices[SkYUVAIndex::kV_Index].fIndex = 1; - indices[SkYUVAIndex::kV_Index].fChannel = SkColorChannel::kG; - if (has_alpha) { - process_planar(2); - indices[SkYUVAIndex::kA_Index].fIndex = 2; - indices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR; - } - } else { - process_planar(0); - indices[SkYUVAIndex::kY_Index].fIndex = 0; - indices[SkYUVAIndex::kY_Index].fChannel = SkColorChannel::kR; - - process_planar(1); - indices[SkYUVAIndex::kU_Index].fIndex = 1; - indices[SkYUVAIndex::kU_Index].fChannel = SkColorChannel::kR; - - process_planar(2); - indices[SkYUVAIndex::kV_Index].fIndex = 2; - indices[SkYUVAIndex::kV_Index].fChannel = SkColorChannel::kR; - if (has_alpha) { - process_planar(3); - indices[SkYUVAIndex::kA_Index].fIndex = 3; - indices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR; - } } + auto image = recorder_->makeYUVAPromiseTexture( yuv_color_space, formats, yuva_sizes, indices, yuva_sizes[0].width(), yuva_sizes[0].height(), kTopLeft_GrSurfaceOrigin, dst_color_space, @@ -585,16 +474,6 @@ ScheduleGpuTask(std::move(callback), std::vector<gpu::SyncToken>()); } -void SkiaOutputSurfaceImpl::AddContextLostObserver( - ContextLostObserver* observer) { - observers_.AddObserver(observer); -} - -void SkiaOutputSurfaceImpl::RemoveContextLostObserver( - ContextLostObserver* observer) { - observers_.RemoveObserver(observer); -} - void SkiaOutputSurfaceImpl::SetCapabilitiesForTesting( bool flipped_output_surface) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); @@ -703,12 +582,6 @@ } } -void SkiaOutputSurfaceImpl::ContextLost() { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - for (auto& observer : observers_) - observer.OnContextLost(); -} - void SkiaOutputSurfaceImpl::ScheduleGpuTask( base::OnceClosure callback, std::vector<gpu::SyncToken> sync_tokens) {
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h index 08ff06ef..6f9b2c27 100644 --- a/components/viz/service/display_embedder/skia_output_surface_impl.h +++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -9,13 +9,10 @@ #include <vector> #include "base/macros.h" -#include "base/observer_list.h" #include "base/optional.h" -#include "base/threading/thread_checker.h" #include "components/viz/common/display/renderer_settings.h" -#include "components/viz/common/display/update_vsync_parameters_callback.h" #include "components/viz/common/resources/resource_id.h" -#include "components/viz/service/display/skia_output_surface.h" +#include "components/viz/service/display_embedder/skia_output_surface_base.h" #include "components/viz/service/viz_service_export.h" #include "gpu/command_buffer/common/sync_token.h" #include "gpu/ipc/common/surface_handle.h" @@ -30,7 +27,6 @@ namespace viz { -struct ImageContext; class GpuServiceImpl; class SkiaOutputSurfaceImplOnGpu; @@ -44,7 +40,7 @@ // render into. In SwapBuffers, it detaches a SkDeferredDisplayList from the // recorder and plays it back on the framebuffer SkSurface on the GPU thread // through SkiaOutputSurfaceImpleOnGpu. -class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurface { +class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurfaceBase { public: SkiaOutputSurfaceImpl(GpuServiceImpl* gpu_service, gpu::SurfaceHandle surface_handle, @@ -55,24 +51,11 @@ void BindToClient(OutputSurfaceClient* client) override; void EnsureBackbuffer() override; void DiscardBackbuffer() override; - void BindFramebuffer() override; - void SetDrawRectangle(const gfx::Rect& draw_rectangle) override; void Reshape(const gfx::Size& size, float device_scale_factor, const gfx::ColorSpace& color_space, bool has_alpha, bool use_stencil) override; - void SwapBuffers(OutputSurfaceFrame frame) override; - uint32_t GetFramebufferCopyTextureFormat() override; - OverlayCandidateValidator* GetOverlayCandidateValidator() const override; - bool IsDisplayedAsOverlayPlane() const override; - unsigned GetOverlayTextureId() const override; - gfx::BufferFormat GetOverlayBufferFormat() const override; - bool HasExternalStencilTest() const override; - void ApplyExternalStencil() override; - unsigned UpdateGpuFence() override; - void SetNeedsSwapSizeNotifications( - bool needs_swap_size_notifications) override; void SetUpdateVSyncParametersCallback( UpdateVSyncParametersCallback callback) override; @@ -103,8 +86,6 @@ const copy_output::RenderPassGeometry& geometry, const gfx::ColorSpace& color_space, std::unique_ptr<CopyOutputRequest> request) override; - void AddContextLostObserver(ContextLostObserver* observer) override; - void RemoveContextLostObserver(ContextLostObserver* observer) override; // ExternalUseClient implementation: void ReleaseCachedResources(const std::vector<ResourceId>& ids) override; @@ -124,20 +105,16 @@ void DidSwapBuffersComplete(gpu::SwapBuffersCompleteParams params, const gfx::Size& pixel_size); void BufferPresented(const gfx::PresentationFeedback& feedback); - void ContextLost(); void ScheduleGpuTask(base::OnceClosure callback, std::vector<gpu::SyncToken> sync_tokens); GrBackendFormat GetGrBackendFormatForTexture(ResourceFormat resource_format, uint32_t gl_texture_target); uint64_t sync_fence_release_ = 0; - GpuServiceImpl* const gpu_service_; - const bool is_using_vulkan_; const gpu::SurfaceHandle surface_handle_; UpdateVSyncParametersCallback update_vsync_parameters_callback_; - OutputSurfaceClient* client_ = nullptr; std::unique_ptr<base::WaitableEvent> initialize_waitable_event_; SkSurfaceCharacterization characterization_; @@ -157,17 +134,10 @@ // |nway_canvas_| contains |overdraw_canvas_| and root canvas. base::Optional<SkNWayCanvas> nway_canvas_; - // The cached for promise images indexed by resource id. - base::flat_map<ResourceId, std::unique_ptr<ImageContext>> - promise_image_cache_; - // The cache for promise image created from render passes. base::flat_map<RenderPassId, std::unique_ptr<ImageContext>> render_pass_image_cache_; - // Image contexts which are used for the current frame or render pass. - std::vector<ImageContext*> images_in_current_paint_; - // Sync tokens for resources which are used for the current frame or render // pass. std::vector<gpu::SyncToken> resource_sync_tokens_; @@ -180,14 +150,6 @@ // |impl_on_gpu| is created and destroyed on the GPU thread. std::unique_ptr<SkiaOutputSurfaceImplOnGpu> impl_on_gpu_; - // Whether to send OutputSurfaceClient::DidSwapWithSize notifications. - bool needs_swap_size_notifications_ = false; - - // Observers for context lost. - base::ObserverList<ContextLostObserver>::Unchecked observers_; - - THREAD_CHECKER(thread_checker_); - base::WeakPtr<SkiaOutputSurfaceImpl> weak_ptr_; base::WeakPtrFactory<SkiaOutputSurfaceImpl> weak_ptr_factory_;
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc index e4e197c..32c62d39 100644 --- a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc +++ b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc
@@ -13,7 +13,6 @@ #include "base/synchronization/waitable_event.h" #include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/common/frame_sinks/copy_output_request.h" -#include "components/viz/common/gpu/context_lost_observer.h" #include "components/viz/common/gpu/vulkan_context_provider.h" #include "components/viz/common/resources/resource_format_utils.h" #include "components/viz/service/display/output_surface_client.h" @@ -30,7 +29,6 @@ #include "gpu/command_buffer/service/texture_base.h" #include "gpu/vulkan/buildflags.h" #include "third_party/skia/include/core/SkPromiseImageTexture.h" -#include "third_party/skia/include/core/SkYUVAIndex.h" #include "third_party/skia/include/gpu/GrBackendSemaphore.h" #include "ui/gfx/skia_util.h" #include "ui/gl/color_space_utils.h" @@ -93,13 +91,6 @@ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); } -void SkiaOutputSurfaceImplNonDDL::BindToClient(OutputSurfaceClient* client) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK(client); - DCHECK(!client_); - client_ = client; -} - void SkiaOutputSurfaceImplNonDDL::EnsureBackbuffer() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); NOTIMPLEMENTED(); @@ -110,16 +101,6 @@ NOTIMPLEMENTED(); } -void SkiaOutputSurfaceImplNonDDL::BindFramebuffer() { - // TODO(penghuang): remove this method when GLRenderer is removed. -} - -void SkiaOutputSurfaceImplNonDDL::SetDrawRectangle( - const gfx::Rect& draw_rectangle) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - NOTIMPLEMENTED(); -} - void SkiaOutputSurfaceImplNonDDL::Reshape(const gfx::Size& size, float device_scale_factor, const gfx::ColorSpace& color_space, @@ -161,58 +142,6 @@ } } -void SkiaOutputSurfaceImplNonDDL::SwapBuffers(OutputSurfaceFrame frame) { - NOTIMPLEMENTED(); -} - -uint32_t SkiaOutputSurfaceImplNonDDL::GetFramebufferCopyTextureFormat() { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return GL_RGB; -} - -OverlayCandidateValidator* -SkiaOutputSurfaceImplNonDDL::GetOverlayCandidateValidator() const { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return nullptr; -} - -bool SkiaOutputSurfaceImplNonDDL::IsDisplayedAsOverlayPlane() const { - return false; -} - -unsigned SkiaOutputSurfaceImplNonDDL::GetOverlayTextureId() const { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return 0; -} - -gfx::BufferFormat SkiaOutputSurfaceImplNonDDL::GetOverlayBufferFormat() const { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return gfx::BufferFormat::RGBX_8888; -} - -bool SkiaOutputSurfaceImplNonDDL::HasExternalStencilTest() const { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return false; -} - -void SkiaOutputSurfaceImplNonDDL::ApplyExternalStencil() { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); -} - -unsigned SkiaOutputSurfaceImplNonDDL::UpdateGpuFence() { - return 0; -} - -void SkiaOutputSurfaceImplNonDDL::SetNeedsSwapSizeNotifications( - bool needs_swap_size_notifications) { - NOTIMPLEMENTED(); -} - -void SkiaOutputSurfaceImplNonDDL::SetUpdateVSyncParametersCallback( - UpdateVSyncParametersCallback callback) { - NOTIMPLEMENTED(); -} - SkCanvas* SkiaOutputSurfaceImplNonDDL::BeginPaintCurrentFrame() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_EQ(current_render_pass_id_, 0u); @@ -292,56 +221,14 @@ DCHECK((has_alpha && (metadatas.size() == 3 || metadatas.size() == 4)) || (!has_alpha && (metadatas.size() == 2 || metadatas.size() == 3))); - bool is_i420 = has_alpha ? metadatas.size() == 4 : metadatas.size() == 3; + SkYUVAIndex indices[4] = {}; + PrepareYUVATextureIndices(metadatas, has_alpha, indices); - GrBackendFormat formats[4]; - SkYUVAIndex indices[4] = { - {-1, SkColorChannel::kR}, - {-1, SkColorChannel::kR}, - {-1, SkColorChannel::kR}, - {-1, SkColorChannel::kR}, - }; GrBackendTexture yuva_textures[4] = {}; - const auto process_planar = [&](size_t i, ResourceFormat resource_format) { - auto metadata = metadatas[i]; - metadata.resource_format = resource_format; + for (size_t i = 0; i < metadatas.size(); ++i) { + const auto& metadata = metadatas[i]; if (!GetGrBackendTexture(metadata, &yuva_textures[i])) DLOG(ERROR) << "Failed to GetGrBackendTexture from a mailbox."; - }; - - if (is_i420) { - process_planar(0, RED_8); - indices[SkYUVAIndex::kY_Index].fIndex = 0; - indices[SkYUVAIndex::kY_Index].fChannel = SkColorChannel::kR; - - process_planar(1, RED_8); - indices[SkYUVAIndex::kU_Index].fIndex = 1; - indices[SkYUVAIndex::kU_Index].fChannel = SkColorChannel::kR; - - process_planar(2, RED_8); - indices[SkYUVAIndex::kV_Index].fIndex = 2; - indices[SkYUVAIndex::kV_Index].fChannel = SkColorChannel::kR; - if (has_alpha) { - process_planar(3, RED_8); - indices[SkYUVAIndex::kA_Index].fIndex = 3; - indices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR; - } - } else { - process_planar(0, RED_8); - indices[SkYUVAIndex::kY_Index].fIndex = 0; - indices[SkYUVAIndex::kY_Index].fChannel = SkColorChannel::kR; - - process_planar(1, RG_88); - indices[SkYUVAIndex::kU_Index].fIndex = 1; - indices[SkYUVAIndex::kU_Index].fChannel = SkColorChannel::kR; - - indices[SkYUVAIndex::kV_Index].fIndex = 1; - indices[SkYUVAIndex::kV_Index].fChannel = SkColorChannel::kG; - if (has_alpha) { - process_planar(2, RED_8); - indices[SkYUVAIndex::kA_Index].fIndex = 2; - indices[SkYUVAIndex::kA_Index].fChannel = SkColorChannel::kR; - } } return SkImage::MakeFromYUVATextures( @@ -492,16 +379,6 @@ NOTIMPLEMENTED(); } -void SkiaOutputSurfaceImplNonDDL::AddContextLostObserver( - ContextLostObserver* observer) { - observers_.AddObserver(observer); -} - -void SkiaOutputSurfaceImplNonDDL::RemoveContextLostObserver( - ContextLostObserver* observer) { - observers_.RemoveObserver(observer); -} - bool SkiaOutputSurfaceImplNonDDL::WaitSyncToken( const gpu::SyncToken& sync_token) { base::WaitableEvent event; @@ -615,12 +492,6 @@ client_->DidReceivePresentationFeedback(feedback); } -void SkiaOutputSurfaceImplNonDDL::ContextLost() { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - for (auto& observer : observers_) - observer.OnContextLost(); -} - void SkiaOutputSurfaceImplNonDDL::WaitSemaphores( std::vector<GrBackendSemaphore> semaphores) { if (semaphores.empty())
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h index e0511bc..763fb22 100644 --- a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h +++ b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h
@@ -11,10 +11,8 @@ #include "base/containers/flat_map.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" -#include "base/observer_list.h" #include "base/optional.h" -#include "base/threading/thread_checker.h" -#include "components/viz/service/display/skia_output_surface.h" +#include "components/viz/service/display_embedder/skia_output_surface_base.h" #include "components/viz/service/viz_service_export.h" #include "gpu/command_buffer/common/sync_token.h" #include "gpu/command_buffer/service/shared_context_state.h" @@ -43,14 +41,12 @@ namespace viz { -struct ImageContext; - // A SkiaOutputSurface implementation for running SkiaRenderer on GpuThread. // Comparing to SkiaOutputSurfaceImpl, it will issue skia draw operations // against OS graphics API (GL, Vulkan, etc) instead of recording deferred // display list first. class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImplNonDDL - : public SkiaOutputSurface { + : public SkiaOutputSurfaceBase { public: SkiaOutputSurfaceImplNonDDL( scoped_refptr<gl::GLSurface> gl_surface, @@ -62,29 +58,13 @@ ~SkiaOutputSurfaceImplNonDDL() override; // OutputSurface implementation: - void BindToClient(OutputSurfaceClient* client) override; void EnsureBackbuffer() override; void DiscardBackbuffer() override; - void BindFramebuffer() override; - void SetDrawRectangle(const gfx::Rect& draw_rectangle) override; void Reshape(const gfx::Size& size, float device_scale_factor, const gfx::ColorSpace& color_space, bool has_alpha, bool use_stencil) override; - void SwapBuffers(OutputSurfaceFrame frame) override; - uint32_t GetFramebufferCopyTextureFormat() override; - OverlayCandidateValidator* GetOverlayCandidateValidator() const override; - bool IsDisplayedAsOverlayPlane() const override; - unsigned GetOverlayTextureId() const override; - gfx::BufferFormat GetOverlayBufferFormat() const override; - bool HasExternalStencilTest() const override; - void ApplyExternalStencil() override; - unsigned UpdateGpuFence() override; - void SetNeedsSwapSizeNotifications( - bool needs_swap_size_notifications) override; - void SetUpdateVSyncParametersCallback( - UpdateVSyncParametersCallback callback) override; // SkiaOutputSurface implementation: SkCanvas* BeginPaintCurrentFrame() override; @@ -112,8 +92,6 @@ const copy_output::RenderPassGeometry& geometry, const gfx::ColorSpace& color_space, std::unique_ptr<CopyOutputRequest> request) override; - void AddContextLostObserver(ContextLostObserver* observer) override; - void RemoveContextLostObserver(ContextLostObserver* observer) override; // ExternalUseClient implementation: void ReleaseCachedResources(const std::vector<ResourceId>& ids) override; @@ -140,7 +118,6 @@ GrBackendTexture* backend_texture); void FinishPaint(uint64_t sync_fence_release); void BufferPresented(const gfx::PresentationFeedback& feedback); - void ContextLost(); void WaitSemaphores(std::vector<GrBackendSemaphore> semaphores); uint64_t sync_fence_release_ = 0; @@ -155,8 +132,6 @@ const bool need_swapbuffers_ack_; base::Optional<ScopedGpuTask> scoped_gpu_task_; - OutputSurfaceClient* client_ = nullptr; - unsigned int backing_framebuffer_object_ = 0; gfx::Size reshape_surface_size_; float reshape_device_scale_factor_ = 0.f; @@ -167,9 +142,6 @@ // The current render pass id set by BeginPaintRenderPass. RenderPassId current_render_pass_id_ = 0; - // Observers for context lost. - base::ObserverList<ContextLostObserver>::Unchecked observers_; - // The SkSurface for the framebuffer. sk_sp<SkSurface> sk_surface_; @@ -183,18 +155,9 @@ // Offscreen SkSurfaces for render passes. base::flat_map<RenderPassId, sk_sp<SkSurface>> offscreen_sk_surfaces_; - // Cached promise image. - base::flat_map<ResourceId, std::unique_ptr<ImageContext>> - promise_image_cache_; - - // Images for current frame or render pass. - std::vector<ImageContext*> images_in_current_paint_; - // Semaphores which need to be signalled for the current paint. std::vector<GrBackendSemaphore> pending_semaphores_; - THREAD_CHECKER(thread_checker_); - base::WeakPtrFactory<SkiaOutputSurfaceImplNonDDL> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(SkiaOutputSurfaceImplNonDDL);
diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc index 6986a9b..534eb18 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.cc +++ b/content/browser/renderer_host/render_widget_host_view_base.cc
@@ -908,12 +908,6 @@ #endif -#if defined(OS_MACOSX) -bool RenderWidgetHostViewBase::ShouldContinueToPauseForFrame() { - return false; -} -#endif - void RenderWidgetHostViewBase::DidNavigate() { if (host()) host()->SynchronizeVisualProperties();
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h index e8dadd6..ce353fb 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.h +++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -590,12 +590,6 @@ void OnChildFrameDestroyed(int routing_id); #endif -#if defined(OS_MACOSX) - // Use only for resize on macOS. Returns true if there is not currently a - // frame of the view's size being displayed. - virtual bool ShouldContinueToPauseForFrame(); -#endif - virtual void DidNavigate(); // Called when the RenderWidgetHostImpl has be initialized.
diff --git a/content/browser/service_worker/service_worker_clients_api_browsertest.cc b/content/browser/service_worker/service_worker_clients_api_browsertest.cc index e24d7bd..1a4425a 100644 --- a/content/browser/service_worker/service_worker_clients_api_browsertest.cc +++ b/content/browser/service_worker/service_worker_clients_api_browsertest.cc
@@ -94,12 +94,11 @@ return allow_open_url_; } - void OpenURL( - SiteInstance* site_instance, - const OpenURLParams& params, - const base::RepeatingCallback<void(WebContents*)>& callback) override { + void OpenURL(SiteInstance* site_instance, + const OpenURLParams& params, + base::OnceCallback<void(WebContents*)> callback) override { opened_url_ = params.url; - callback.Run(nullptr); + std::move(callback).Run(nullptr); if (opened_url_callback_) std::move(opened_url_callback_).Run(); }
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc index 2ff912c1..73f03a6 100644 --- a/content/browser/web_contents/web_contents_view_aura.cc +++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -1370,7 +1370,19 @@ const int key_modifiers = ui::EventFlagsToWebEventModifiers(event.flags()); #if defined(OS_WIN) - if (event.data().HasVirtualFilenames()) { + // As with real files, only add virtual files if the drag did not originate in + // the renderer process. Without this, if an anchor element is dragged and + // then dropped on the same page, the browser will navigate to the URL + // referenced by the anchor. That is because virtual ".url" file data + // (internet shortcut) is added to the data object on drag start, and if + // script doesn't handle the drop, the browser behaves just as if a .url file + // were dragged in from the desktop. Filtering out virtual files if the drag + // is renderer tainted also prevents the possibility of a compromised renderer + // gaining access to the backing temp file paths. + // TODO(https://crbug.com/958273): DragDrop: Extend virtual filename support + // to DropData, for parity with real filename support. + if (!current_drop_data_->did_originate_from_renderer && + event.data().HasVirtualFilenames()) { // Asynchronously retrieve the actual content of any virtual files now (this // step is not needed for "real" files already on the file system, e.g. // those dropped on Chromium from the desktop). When all content has been
diff --git a/content/browser/web_contents/web_contents_view_aura.h b/content/browser/web_contents/web_contents_view_aura.h index 22d8ee98..1c20db5 100644 --- a/content/browser/web_contents/web_contents_view_aura.h +++ b/content/browser/web_contents/web_contents_view_aura.h
@@ -67,7 +67,11 @@ friend class WebContentsViewAuraTest; FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, EnableDisableOverscroll); FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropFiles); + FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, + DragDropFilesOriginateFromRenderer); FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropVirtualFiles); + FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, + DragDropVirtualFilesOriginateFromRenderer); class WindowObserver;
diff --git a/content/browser/web_contents/web_contents_view_aura_unittest.cc b/content/browser/web_contents/web_contents_view_aura_unittest.cc index fb8f274..43a74ec 100644 --- a/content/browser/web_contents/web_contents_view_aura_unittest.cc +++ b/content/browser/web_contents/web_contents_view_aura_unittest.cc
@@ -77,18 +77,18 @@ RenderViewHostTestHarness::TearDown(); } - WebContentsViewAura* view() { + WebContentsViewAura* GetView() { WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents()); return static_cast<WebContentsViewAura*>(contents->GetView()); } aura::Window* GetNativeView() { return web_contents()->GetNativeView(); } - void CheckDropData(WebContentsViewAura* wcva) const { - EXPECT_EQ(nullptr, wcva->current_drop_data_); + void CheckDropData(WebContentsViewAura* view) const { + EXPECT_EQ(nullptr, view->current_drop_data_); ASSERT_NE(nullptr, drop_complete_data_); EXPECT_TRUE(drop_complete_data_->drop_allowed); - EXPECT_EQ(wcva->current_rwh_for_drag_.get(), + EXPECT_EQ(view->current_rwh_for_drag_.get(), drop_complete_data_->target_rwh.get()); EXPECT_EQ(kClientPt, drop_complete_data_->client_pt); // Screen point of event is ignored, instead cursor position used. @@ -130,11 +130,11 @@ }; TEST_F(WebContentsViewAuraTest, EnableDisableOverscroll) { - WebContentsViewAura* wcva = view(); - wcva->SetOverscrollControllerEnabled(false); - EXPECT_FALSE(wcva->gesture_nav_simple_); - wcva->SetOverscrollControllerEnabled(true); - EXPECT_TRUE(wcva->gesture_nav_simple_); + WebContentsViewAura* view = GetView(); + view->SetOverscrollControllerEnabled(false); + EXPECT_FALSE(view->gesture_nav_simple_); + view->SetOverscrollControllerEnabled(true); + EXPECT_TRUE(view->gesture_nav_simple_); } TEST_F(WebContentsViewAuraTest, ShowHideParent) { @@ -157,7 +157,7 @@ } TEST_F(WebContentsViewAuraTest, DragDropFiles) { - WebContentsViewAura* wcva = view(); + WebContentsViewAura* view = GetView(); ui::OSExchangeData data; const base::string16 string_data = base::ASCIIToUTF16("Some string data"); @@ -187,20 +187,20 @@ ui::DragDropTypes::DRAG_COPY); // Simulate drag enter. - EXPECT_EQ(nullptr, wcva->current_drop_data_); - wcva->OnDragEntered(event); - ASSERT_NE(nullptr, wcva->current_drop_data_); + EXPECT_EQ(nullptr, view->current_drop_data_); + view->OnDragEntered(event); + ASSERT_NE(nullptr, view->current_drop_data_); #if defined(USE_X11) // By design, OSExchangeDataProviderAuraX11::GetString returns an empty string // if file data is also present. - EXPECT_TRUE(wcva->current_drop_data_->text.string().empty()); + EXPECT_TRUE(view->current_drop_data_->text.string().empty()); #else - EXPECT_EQ(string_data, wcva->current_drop_data_->text.string()); + EXPECT_EQ(string_data, view->current_drop_data_->text.string()); #endif std::vector<ui::FileInfo> retrieved_file_infos = - wcva->current_drop_data_->filenames; + view->current_drop_data_->filenames; ASSERT_EQ(test_file_infos.size(), retrieved_file_infos.size()); for (size_t i = 0; i < retrieved_file_infos.size(); i++) { EXPECT_EQ(test_file_infos[i].path, retrieved_file_infos[i].path); @@ -211,15 +211,15 @@ // Simulate drop. auto callback = base::BindOnce(&WebContentsViewAuraTest::OnDropComplete, base::Unretained(this)); - wcva->RegisterDropCallbackForTesting(std::move(callback)); + view->RegisterDropCallbackForTesting(std::move(callback)); base::RunLoop run_loop; async_drop_closure_ = run_loop.QuitClosure(); - wcva->OnPerformDrop(event); + view->OnPerformDrop(event); run_loop.Run(); - CheckDropData(wcva); + CheckDropData(view); #if defined(USE_X11) // By design, OSExchangeDataProviderAuraX11::GetString returns an empty string @@ -238,9 +238,84 @@ } } +#if defined(OS_WIN) || defined(USE_X11) +TEST_F(WebContentsViewAuraTest, DragDropFilesOriginateFromRenderer) { + WebContentsViewAura* view = GetView(); + ui::OSExchangeData data; + + const base::string16 string_data = base::ASCIIToUTF16("Some string data"); + data.SetString(string_data); + +#if defined(OS_WIN) + const std::vector<ui::FileInfo> test_file_infos = { + {base::FilePath(FILE_PATH_LITERAL("C:\\tmp\\test_file1")), + base::FilePath()}, + {base::FilePath(FILE_PATH_LITERAL("C:\\tmp\\test_file2")), + base::FilePath()}, + { + base::FilePath(FILE_PATH_LITERAL("C:\\tmp\\test_file3")), + base::FilePath(), + }, + }; +#else + const std::vector<ui::FileInfo> test_file_infos = { + {base::FilePath(FILE_PATH_LITERAL("/tmp/test_file1")), base::FilePath()}, + {base::FilePath(FILE_PATH_LITERAL("/tmp/test_file2")), base::FilePath()}, + {base::FilePath(FILE_PATH_LITERAL("/tmp/test_file3")), base::FilePath()}, + }; +#endif + data.SetFilenames(test_file_infos); + + // Simulate the drag originating in the renderer process, in which case + // any file data should be filtered out (anchor drag scenario). + data.MarkOriginatedFromRenderer(); + + ui::DropTargetEvent event(data, kClientPt, kScreenPt, + ui::DragDropTypes::DRAG_COPY); + + // Simulate drag enter. + EXPECT_EQ(nullptr, view->current_drop_data_); + view->OnDragEntered(event); + ASSERT_NE(nullptr, view->current_drop_data_); + +#if defined(USE_X11) + // By design, OSExchangeDataProviderAuraX11::GetString returns an empty string + // if file data is also present. + EXPECT_TRUE(view->current_drop_data_->text.string().empty()); +#else + EXPECT_EQ(string_data, view->current_drop_data_->text.string()); +#endif + + ASSERT_TRUE(view->current_drop_data_->filenames.empty()); + + // Simulate drop. + auto callback = base::BindOnce(&WebContentsViewAuraTest::OnDropComplete, + base::Unretained(this)); + view->RegisterDropCallbackForTesting(std::move(callback)); + + base::RunLoop run_loop; + async_drop_closure_ = run_loop.QuitClosure(); + + view->OnPerformDrop(event); + run_loop.Run(); + + CheckDropData(view); + +#if defined(USE_X11) + // By design, OSExchangeDataProviderAuraX11::GetString returns an empty string + // if file data is also present. + EXPECT_TRUE(drop_complete_data_->drop_data.text.string().empty()); +#else + EXPECT_EQ(string_data, drop_complete_data_->drop_data.text.string()); +#endif + + ASSERT_TRUE(drop_complete_data_->drop_data.filenames.empty()); +} +#endif + #if defined(OS_WIN) TEST_F(WebContentsViewAuraTest, DragDropVirtualFiles) { - WebContentsViewAura* wcva = view(); + WebContentsViewAura* view = GetView(); ui::OSExchangeData data; const base::string16 string_data = base::ASCIIToUTF16("Some string data"); @@ -263,15 +338,15 @@ ui::DragDropTypes::DRAG_COPY); // Simulate drag enter. - EXPECT_EQ(nullptr, wcva->current_drop_data_); - wcva->OnDragEntered(event); - ASSERT_NE(nullptr, wcva->current_drop_data_); + EXPECT_EQ(nullptr, view->current_drop_data_); + view->OnDragEntered(event); + ASSERT_NE(nullptr, view->current_drop_data_); - EXPECT_EQ(string_data, wcva->current_drop_data_->text.string()); + EXPECT_EQ(string_data, view->current_drop_data_->text.string()); const base::FilePath path_placeholder(FILE_PATH_LITERAL("temp.tmp")); std::vector<ui::FileInfo> retrieved_file_infos = - wcva->current_drop_data_->filenames; + view->current_drop_data_->filenames; ASSERT_EQ(test_filenames_and_contents.size(), retrieved_file_infos.size()); for (size_t i = 0; i < retrieved_file_infos.size(); i++) { EXPECT_EQ(test_filenames_and_contents[i].first, @@ -283,15 +358,15 @@ // present). auto callback = base::BindOnce(&WebContentsViewAuraTest::OnDropComplete, base::Unretained(this)); - wcva->RegisterDropCallbackForTesting(std::move(callback)); + view->RegisterDropCallbackForTesting(std::move(callback)); base::RunLoop run_loop; async_drop_closure_ = run_loop.QuitClosure(); - wcva->OnPerformDrop(event); + view->OnPerformDrop(event); run_loop.Run(); - CheckDropData(wcva); + CheckDropData(view); EXPECT_EQ(string_data, drop_complete_data_->drop_data.text.string()); @@ -312,6 +387,61 @@ EXPECT_EQ(test_filenames_and_contents[i].second, read_contents); } } + +TEST_F(WebContentsViewAuraTest, DragDropVirtualFilesOriginateFromRenderer) { + WebContentsViewAura* view = GetView(); + ui::OSExchangeData data; + + const base::string16 string_data = base::ASCIIToUTF16("Some string data"); + data.SetString(string_data); + + const std::vector<std::pair<base::FilePath, std::string>> + test_filenames_and_contents = { + {base::FilePath(FILE_PATH_LITERAL("filename.txt")), + std::string("just some data")}, + {base::FilePath(FILE_PATH_LITERAL("another filename.txt")), + std::string("just some data\0with\0nulls", 25)}, + {base::FilePath(FILE_PATH_LITERAL("and another filename.txt")), + std::string("just some more data")}, + }; + + data.provider().SetVirtualFileContentsForTesting(test_filenames_and_contents, + TYMED_ISTREAM); + + // Simulate the drag originating in the renderer process, in which case + // any file data should be filtered out (anchor drag scenario). + data.MarkOriginatedFromRenderer(); + + ui::DropTargetEvent event(data, kClientPt, kScreenPt, + ui::DragDropTypes::DRAG_COPY); + + // Simulate drag enter. + EXPECT_EQ(nullptr, view->current_drop_data_); + view->OnDragEntered(event); + ASSERT_NE(nullptr, view->current_drop_data_); + + EXPECT_EQ(string_data, view->current_drop_data_->text.string()); + + ASSERT_TRUE(view->current_drop_data_->filenames.empty()); + + // Simulate drop (completes asynchronously since virtual file data is + // present). + auto callback = base::BindOnce(&WebContentsViewAuraTest::OnDropComplete, + base::Unretained(this)); + view->RegisterDropCallbackForTesting(std::move(callback)); + + base::RunLoop run_loop; + async_drop_closure_ = run_loop.QuitClosure(); + + view->OnPerformDrop(event); + run_loop.Run(); + + CheckDropData(view); + + EXPECT_EQ(string_data, drop_complete_data_->drop_data.text.string()); + + ASSERT_TRUE(drop_complete_data_->drop_data.filenames.empty()); +} #endif } // namespace content
diff --git a/content/browser/webauth/authenticator_common.cc b/content/browser/webauth/authenticator_common.cc index c9d549dc..038739b 100644 --- a/content/browser/webauth/authenticator_common.cc +++ b/content/browser/webauth/authenticator_common.cc
@@ -639,8 +639,10 @@ return; } - if (options->authenticator_selection && - options->authenticator_selection->require_resident_key && + const bool resident_key = + options->authenticator_selection && + options->authenticator_selection->require_resident_key; + if (resident_key && (!base::FeatureList::IsEnabled(device::kWebAuthResidentKeys) || !request_delegate_->SupportsResidentKeys())) { // Disallow the creation of resident credentials. @@ -650,6 +652,43 @@ return; } + auto authenticator_selection_criteria = + options->authenticator_selection + ? mojo::ConvertTo<device::AuthenticatorSelectionCriteria>( + options->authenticator_selection) + : device::AuthenticatorSelectionCriteria(); + + // Reject any non-sensical credProtect extension values. + if ( // Can't require the default policy (or no policy). + (options->enforce_protection_policy && + (options->protection_policy == + blink::mojom::ProtectionPolicy::UNSPECIFIED || + options->protection_policy == blink::mojom::ProtectionPolicy::NONE)) || + // For non-resident keys the only protection that makes sense is + // UV_REQUIRED (or UNSPECIFIED). + (!resident_key && + (options->protection_policy == blink::mojom::ProtectionPolicy::NONE || + options->protection_policy == + blink::mojom::ProtectionPolicy::UV_OR_CRED_ID_REQUIRED)) || + // UV_REQUIRED only makes sense if UV is required overall. + (options->protection_policy == + blink::mojom::ProtectionPolicy::UV_REQUIRED && + authenticator_selection_criteria.user_verification_requirement() != + device::UserVerificationRequirement::kRequired)) { + InvokeCallbackAndCleanup( + std::move(callback), + blink::mojom::AuthenticatorStatus::PROTECTION_POLICY_INCONSISTENT); + return; + } + + if (options->protection_policy == + blink::mojom::ProtectionPolicy::UNSPECIFIED && + resident_key) { + // If not specified, UV_OR_CRED_ID_REQUIRED is made the default. + options->protection_policy = + blink::mojom::ProtectionPolicy::UV_OR_CRED_ID_REQUIRED; + } + DCHECK(make_credential_response_callback_.is_null()); make_credential_response_callback_ = std::move(callback); @@ -690,12 +729,6 @@ {device::FidoTransportProtocol::kUsbHumanInterfaceDevice}) : transports_; - auto authenticator_selection_criteria = - options->authenticator_selection - ? mojo::ConvertTo<device::AuthenticatorSelectionCriteria>( - options->authenticator_selection) - : device::AuthenticatorSelectionCriteria(); - auto ctap_request = CreateCtapMakeCredentialRequest( client_data_json_, options, browser_context()->IsOffTheRecord()); // On dual protocol CTAP2/U2F devices, force credential creation over U2F. @@ -714,6 +747,21 @@ attestation_requested_ = attestation != ::device::AttestationConveyancePreference::NONE; + switch (options->protection_policy) { + case blink::mojom::ProtectionPolicy::UNSPECIFIED: + case blink::mojom::ProtectionPolicy::NONE: + break; + case blink::mojom::ProtectionPolicy::UV_OR_CRED_ID_REQUIRED: + ctap_request.cred_protect = + std::make_pair(device::CredProtect::kUVOrCredIDRequired, + options->enforce_protection_policy); + break; + case blink::mojom::ProtectionPolicy::UV_REQUIRED: + ctap_request.cred_protect = std::make_pair( + device::CredProtect::kUVRequired, options->enforce_protection_policy); + break; + } + request_ = std::make_unique<device::MakeCredentialRequestHandler>( connector_, transports, std::move(ctap_request), std::move(authenticator_selection_criteria), @@ -732,8 +780,7 @@ base::BindRepeating( &device::FidoRequestHandlerBase::InitiatePairingWithDevice, request_->GetWeakPtr()) /* ble_pairing_callback */); - if (options->authenticator_selection && - options->authenticator_selection->require_resident_key) { + if (resident_key) { request_delegate_->SetMightCreateResidentCredential(true); } request_->set_observer(request_delegate_.get());
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc index 694278f9..a172033 100644 --- a/content/browser/webauth/authenticator_impl_unittest.cc +++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -3452,6 +3452,8 @@ } } +// TODO(agl): test resident-key storage exhaustion. + TEST_F(ResidentKeyAuthenticatorImplTest, GetAssertionSingle) { ASSERT_TRUE(virtual_device_.mutable_state()->InjectResidentKey( /*credential_id=*/{{4, 3, 2, 1}}, kTestRelyingPartyId, @@ -3491,6 +3493,180 @@ EXPECT_TRUE(HasUV(callback_receiver)); } -// TODO(agl): test resident-key storage exhaustion. +static const char* ProtectionPolicyDescription( + blink::mojom::ProtectionPolicy p) { + switch (p) { + case blink::mojom::ProtectionPolicy::UNSPECIFIED: + return "UNSPECIFIED"; + case blink::mojom::ProtectionPolicy::NONE: + return "NONE"; + case blink::mojom::ProtectionPolicy::UV_OR_CRED_ID_REQUIRED: + return "UV_OR_CRED_ID_REQUIRED"; + case blink::mojom::ProtectionPolicy::UV_REQUIRED: + return "UV_REQUIRED"; + } +} + +TEST_F(ResidentKeyAuthenticatorImplTest, CredProtectRegistration) { + TestServiceManagerContext smc; + AuthenticatorPtr authenticator = ConnectToAuthenticator(); + + const auto UNSPECIFIED = blink::mojom::ProtectionPolicy::UNSPECIFIED; + const auto NONE = blink::mojom::ProtectionPolicy::NONE; + const auto UV_OR_CRED = + blink::mojom::ProtectionPolicy::UV_OR_CRED_ID_REQUIRED; + const auto UV_REQ = blink::mojom::ProtectionPolicy::UV_REQUIRED; + const int kOk = 0; + const int kNonsense = 1; + const int kNotAllow = 2; + + const struct { + bool supported_by_authenticator; + bool is_resident; + blink::mojom::ProtectionPolicy protection; + bool enforce; + bool uv; + int expected_outcome; + blink::mojom::ProtectionPolicy resulting_policy; + } kExpectations[] = { + // clang-format off + // Support | Resdnt | Level | Enf | UV || Result | Prot level + { false, false, UNSPECIFIED, false, false, kOk, NONE}, + { false, false, UNSPECIFIED, true, false, kNonsense, UNSPECIFIED}, + { false, false, NONE, false, false, kNonsense, UNSPECIFIED}, + { false, false, NONE, true, false, kNonsense, UNSPECIFIED}, + { false, false, UV_OR_CRED, false, false, kNonsense, UNSPECIFIED}, + { false, false, UV_OR_CRED, true, false, kNonsense, UNSPECIFIED}, + { false, false, UV_REQ, false, false, kNonsense, UNSPECIFIED}, + { false, false, UV_REQ, false, true, kOk, NONE}, + { false, false, UV_REQ, true, false, kNonsense, UNSPECIFIED}, + { false, false, UV_REQ, true, true, kNotAllow, UNSPECIFIED}, + { false, true, UNSPECIFIED, false, false, kOk, NONE}, + { false, true, UNSPECIFIED, true, false, kNonsense, UNSPECIFIED}, + { false, true, NONE, false, false, kOk, NONE}, + { false, true, NONE, true, false, kNonsense, UNSPECIFIED}, + { false, true, UV_OR_CRED, false, false, kOk, NONE}, + { false, true, UV_OR_CRED, true, false, kNotAllow, UNSPECIFIED}, + { false, true, UV_REQ, false, false, kNonsense, UNSPECIFIED}, + { false, true, UV_REQ, false, true, kOk, NONE}, + { false, true, UV_REQ, true, false, kNonsense, UNSPECIFIED}, + { false, true, UV_REQ, true, true, kNotAllow, UNSPECIFIED}, + + // For the case where the authenticator supports credProtect we do not + // repeat the cases above that are |kNonsense| on the assumption that + // authenticator support is irrelevant. Therefore these are just the non- + // kNonsense cases from the prior block. + { true, false, UNSPECIFIED, false, false, kOk, NONE}, + { true, false, UV_REQ, false, true, kOk, UV_REQ}, + { true, false, UV_REQ, true, true, kOk, UV_REQ}, + { true, true, UNSPECIFIED, false, false, kOk, UV_OR_CRED}, + { true, true, NONE, false, false, kOk, NONE}, + { true, true, UV_OR_CRED, false, false, kOk, UV_OR_CRED}, + { true, true, UV_OR_CRED, true, false, kOk, UV_OR_CRED}, + { true, true, UV_REQ, false, true, kOk, UV_REQ}, + { true, true, UV_REQ, true, true, kOk, UV_REQ}, + // clang-format on + }; + + for (const auto& test : kExpectations) { + device::VirtualCtap2Device::Config config; + config.pin_support = true; + config.resident_key_support = true; + config.cred_protect_support = test.supported_by_authenticator; + virtual_device_.SetCtap2Config(config); + virtual_device_.mutable_state()->registrations.clear(); + + SCOPED_TRACE(::testing::Message() << "uv=" << test.uv); + SCOPED_TRACE(::testing::Message() << "enforce=" << test.enforce); + SCOPED_TRACE(::testing::Message() + << "level=" << ProtectionPolicyDescription(test.protection)); + SCOPED_TRACE(::testing::Message() << "resident=" << test.is_resident); + SCOPED_TRACE(::testing::Message() + << "support=" << test.supported_by_authenticator); + + PublicKeyCredentialCreationOptionsPtr options = make_credential_options(); + options->authenticator_selection->require_resident_key = test.is_resident; + options->protection_policy = test.protection; + options->enforce_protection_policy = test.enforce; + options->authenticator_selection->user_verification = + test.uv ? blink::mojom::UserVerificationRequirement::REQUIRED + : blink::mojom::UserVerificationRequirement::DISCOURAGED; + + TestMakeCredentialCallback callback_receiver; + authenticator->MakeCredential(std::move(options), + callback_receiver.callback()); + callback_receiver.WaitForCallback(); + + switch (test.expected_outcome) { + case kOk: { + EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status()); + ASSERT_EQ(1u, virtual_device_.mutable_state()->registrations.size()); + const base::Optional<device::CredProtect> result = + virtual_device_.mutable_state() + ->registrations.begin() + ->second.protection; + + switch (test.resulting_policy) { + case UNSPECIFIED: + NOTREACHED(); + break; + case NONE: + EXPECT_FALSE(result); + break; + case UV_OR_CRED: + ASSERT_TRUE(result); + EXPECT_EQ(device::CredProtect::kUVOrCredIDRequired, *result); + break; + case UV_REQ: + ASSERT_TRUE(result); + EXPECT_EQ(device::CredProtect::kUVRequired, *result); + break; + } + break; + } + case kNonsense: + EXPECT_EQ(AuthenticatorStatus::PROTECTION_POLICY_INCONSISTENT, + callback_receiver.status()); + break; + case kNotAllow: + EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, + callback_receiver.status()); + break; + default: + NOTREACHED(); + } + } +} + +TEST_F(ResidentKeyAuthenticatorImplTest, ProtectedNonResidentCreds) { + // Until we have UVToken, there's a danger that we'll preflight UV-required + // credential IDs such that the authenticator denies knowledge of all of them + // for silent requests and then we fail the whole request. + device::VirtualCtap2Device::Config config; + config.pin_support = true; + config.resident_key_support = true; + config.cred_protect_support = true; + virtual_device_.SetCtap2Config(config); + ASSERT_TRUE(virtual_device_.mutable_state()->InjectRegistration( + /*credential_id=*/{{4, 3, 2, 1}}, kTestRelyingPartyId)); + ASSERT_EQ(1u, virtual_device_.mutable_state()->registrations.size()); + virtual_device_.mutable_state()->registrations.begin()->second.protection = + device::CredProtect::kUVRequired; + + TestServiceManagerContext smc; + AuthenticatorPtr authenticator = ConnectToAuthenticator(); + TestGetAssertionCallback callback_receiver; + // |SelectAccount| should not be called when there's only a single response. + test_client_.expected_accounts = "<invalid>"; + + PublicKeyCredentialRequestOptionsPtr options = get_credential_options(); + options->allow_credentials = GetTestCredentials(5); + options->allow_credentials[0]->id = {4, 3, 2, 1}; + + authenticator->GetAssertion(std::move(options), callback_receiver.callback()); + callback_receiver.WaitForCallback(); + EXPECT_EQ(AuthenticatorStatus::SUCCESS, callback_receiver.status()); + EXPECT_TRUE(HasUV(callback_receiver)); +} } // namespace content
diff --git a/content/browser/webauth/webauth_browsertest.cc b/content/browser/webauth/webauth_browsertest.cc index 8c00e09..b932edb4 100644 --- a/content/browser/webauth/webauth_browsertest.cc +++ b/content/browser/webauth/webauth_browsertest.cc
@@ -400,7 +400,8 @@ base::TimeDelta::FromSeconds(30), std::vector<blink::mojom::PublicKeyCredentialDescriptorPtr>(), nullptr, blink::mojom::AttestationConveyancePreference::NONE, nullptr, - false /* no hmac_secret */); + false /* no hmac_secret */, blink::mojom::ProtectionPolicy::UNSPECIFIED, + false /* protection policy not enforced */); return mojo_options; }
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc index bf0bbe2..23b7c63 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc
@@ -651,9 +651,9 @@ void ContentBrowserClient::OpenURL( content::SiteInstance* site_instance, const content::OpenURLParams& params, - const base::Callback<void(content::WebContents*)>& callback) { + base::OnceCallback<void(content::WebContents*)> callback) { DCHECK(site_instance); - callback.Run(nullptr); + std::move(callback).Run(nullptr); } std::string ContentBrowserClient::GetMetricSuffixForURL(const GURL& url) {
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index e295f5a9..1041df5 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h
@@ -1097,7 +1097,7 @@ // invoked with the appropriate WebContents* when available. virtual void OpenURL(SiteInstance* site_instance, const OpenURLParams& params, - const base::Callback<void(WebContents*)>& callback); + base::OnceCallback<void(WebContents*)> callback); // Allows the embedder to record |metric| for a specific |url|. virtual void RecordURLMetric(const std::string& metric, const GURL& url) {}
diff --git a/content/public/test/content_browser_test_utils.cc b/content/public/test/content_browser_test_utils.cc index 233c60c..48138adad 100644 --- a/content/public/test/content_browser_test_utils.cc +++ b/content/public/test/content_browser_test_utils.cc
@@ -30,7 +30,6 @@ #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_frame_navigation_observer.h" #include "content/public/test/test_navigation_observer.h" -#include "content/public/test/test_utils.h" #include "content/shell/browser/shell.h" #include "content/shell/browser/shell_javascript_dialog_manager.h" #include "net/base/filename_util.h" @@ -121,9 +120,9 @@ static_cast<ShellJavaScriptDialogManager*>( window->GetJavaScriptDialogManager(window->web_contents())); - scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner(); - dialog_manager->set_dialog_request_callback(runner->QuitClosure()); - runner->Run(); + base::RunLoop runner; + dialog_manager->set_dialog_request_callback(runner.QuitClosure()); + runner.Run(); } RenderFrameHost* ConvertToRenderFrameHost(Shell* shell) { @@ -160,18 +159,18 @@ run_loop.Run(); } -ShellAddedObserver::ShellAddedObserver() : shell_(nullptr) { - Shell::SetShellCreatedCallback( - base::Bind(&ShellAddedObserver::ShellCreated, base::Unretained(this))); +ShellAddedObserver::ShellAddedObserver() { + Shell::SetShellCreatedCallback(base::BindOnce( + &ShellAddedObserver::ShellCreated, base::Unretained(this))); } -ShellAddedObserver::~ShellAddedObserver() {} +ShellAddedObserver::~ShellAddedObserver() = default; Shell* ShellAddedObserver::GetShell() { if (shell_) return shell_; - runner_ = new MessageLoopRunner(); + runner_ = std::make_unique<base::RunLoop>(); runner_->Run(); return shell_; } @@ -179,8 +178,8 @@ void ShellAddedObserver::ShellCreated(Shell* shell) { DCHECK(!shell_); shell_ = shell; - if (runner_.get()) - runner_->QuitClosure().Run(); + if (runner_) + runner_->Quit(); } void IsolateOriginsForTesting(
diff --git a/content/public/test/content_browser_test_utils.h b/content/public/test/content_browser_test_utils.h index d0a77d4..cce4d63 100644 --- a/content/public/test/content_browser_test_utils.h +++ b/content/public/test/content_browser_test_utils.h
@@ -10,7 +10,7 @@ #include "base/callback.h" #include "base/macros.h" -#include "base/memory/ref_counted.h" +#include "base/run_loop.h" #include "content/public/common/page_type.h" #include "ui/gfx/native_widget_types.h" #include "url/gurl.h" @@ -41,8 +41,6 @@ // content\public\test\browser_test_utils.h namespace content { - -class MessageLoopRunner; class RenderFrameHost; class RenderWidgetHost; class Shell; @@ -134,8 +132,8 @@ private: void ShellCreated(Shell* shell); - Shell* shell_; - scoped_refptr<MessageLoopRunner> runner_; + Shell* shell_ = nullptr; + std::unique_ptr<base::RunLoop> runner_; DISALLOW_COPY_AND_ASSIGN(ShellAddedObserver); };
diff --git a/content/renderer/media/stream/webmediaplayer_ms.cc b/content/renderer/media/stream/webmediaplayer_ms.cc index 79f56e1..a91b08c 100644 --- a/content/renderer/media/stream/webmediaplayer_ms.cc +++ b/content/renderer/media/stream/webmediaplayer_ms.cc
@@ -24,7 +24,7 @@ #include "media/base/media_content_type.h" #include "media/base/media_log.h" #include "media/base/video_frame.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" #include "media/base/video_types.h" #include "media/blink/webmediaplayer_util.h" #include "media/video/gpu_memory_buffer_video_frame_pool.h" @@ -250,7 +250,7 @@ delegate_(delegate), delegate_id_(0), paused_(true), - video_rotation_(media::VIDEO_ROTATION_0), + video_transformation_(media::kNoTransformation), media_log_(std::move(media_log)), renderer_factory_(std::move(factory)), main_render_task_runner_(std::move(main_render_task_runner)), @@ -685,8 +685,8 @@ if (!video_frame_provider_) return blink::WebSize(); - if (video_rotation_ == media::VIDEO_ROTATION_90 || - video_rotation_ == media::VideoRotation::VIDEO_ROTATION_270) { + if (video_transformation_.rotation == media::VIDEO_ROTATION_90 || + video_transformation_.rotation == media::VIDEO_ROTATION_270) { const gfx::Size& current_size = compositor_->GetCurrentSize(); return blink::WebSize(current_size.height(), current_size.width()); } @@ -700,8 +700,8 @@ return blink::WebSize(); const gfx::Rect& visible_rect = video_frame->visible_rect(); - if (video_rotation_ == media::VIDEO_ROTATION_90 || - video_rotation_ == media::VideoRotation::VIDEO_ROTATION_270) { + if (video_transformation_.rotation == media::VIDEO_ROTATION_90 || + video_transformation_.rotation == media::VIDEO_ROTATION_270) { return blink::WebSize(visible_rect.height(), visible_rect.width()); } return blink::WebSize(visible_rect.width(), visible_rect.height()); @@ -787,7 +787,7 @@ return; } const gfx::RectF dest_rect(rect.x, rect.y, rect.width, rect.height); - video_renderer_.Paint(frame, canvas, dest_rect, flags, video_rotation_, + video_renderer_.Paint(frame, canvas, dest_rect, flags, video_transformation_, provider); } @@ -1054,7 +1054,7 @@ FROM_HERE, base::BindOnce(&WebMediaPlayerMSCompositor::EnableSubmission, compositor_, bridge_->GetSurfaceId(), bridge_->GetLocalSurfaceIdAllocationTime(), - video_rotation_, IsInPictureInPicture())); + video_transformation_, IsInPictureInPicture())); // If the element is already in Picture-in-Picture mode, it means that it // was set in this mode prior to this load, with a different @@ -1110,7 +1110,7 @@ void WebMediaPlayerMS::OnRotationChanged(media::VideoRotation video_rotation) { DVLOG(1) << __func__; DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - video_rotation_ = video_rotation; + video_transformation_ = {video_rotation, 0}; if (!bridge_) { // Keep the old |video_layer_| alive until SetCcLayer() is called with a new
diff --git a/content/renderer/media/stream/webmediaplayer_ms.h b/content/renderer/media/stream/webmediaplayer_ms.h index 891deea..138c138 100644 --- a/content/renderer/media/stream/webmediaplayer_ms.h +++ b/content/renderer/media/stream/webmediaplayer_ms.h
@@ -306,7 +306,7 @@ media::PaintCanvasVideoRenderer video_renderer_; bool paused_; - media::VideoRotation video_rotation_; + media::VideoTransformation video_transformation_; std::unique_ptr<media::MediaLog> media_log_;
diff --git a/content/renderer/media/stream/webmediaplayer_ms_compositor.cc b/content/renderer/media/stream/webmediaplayer_ms_compositor.cc index f9d85a5d..53ff571 100644 --- a/content/renderer/media/stream/webmediaplayer_ms_compositor.cc +++ b/content/renderer/media/stream/webmediaplayer_ms_compositor.cc
@@ -211,7 +211,7 @@ void WebMediaPlayerMSCompositor::EnableSubmission( const viz::SurfaceId& id, base::TimeTicks local_surface_id_allocation_time, - media::VideoRotation rotation, + media::VideoTransformation transformation, bool force_submit) { DCHECK(video_frame_compositor_task_runner_->BelongsToCurrentThread()); @@ -221,7 +221,7 @@ video_frame_provider_client_->StopUsingProvider(); } - submitter_->SetRotation(rotation); + submitter_->SetRotation(transformation.rotation); submitter_->SetForceSubmit(force_submit); submitter_->EnableSubmission(id, local_surface_id_allocation_time); video_frame_provider_client_ = submitter_.get();
diff --git a/content/renderer/media/stream/webmediaplayer_ms_compositor.h b/content/renderer/media/stream/webmediaplayer_ms_compositor.h index cf66a20f..d7e2520 100644 --- a/content/renderer/media/stream/webmediaplayer_ms_compositor.h +++ b/content/renderer/media/stream/webmediaplayer_ms_compositor.h
@@ -84,7 +84,7 @@ virtual void EnableSubmission( const viz::SurfaceId& id, base::TimeTicks local_surface_id_allocation_time, - media::VideoRotation rotation, + media::VideoTransformation transformation, bool force_submit); // Notifies the |submitter_| that the frames must be submitted.
diff --git a/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc b/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc index f26a722..26f0c91 100644 --- a/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc +++ b/content/renderer/media/webrtc/rtc_video_decoder_adapter.cc
@@ -159,7 +159,7 @@ media::VideoDecoderConfig config( ToVideoCodec(webrtc::PayloadStringToCodecType(format.name)), GuessVideoCodecProfile(format), kDefaultPixelFormat, - media::VideoColorSpace(), media::VIDEO_ROTATION_0, kDefaultSize, + media::VideoColorSpace(), media::kNoTransformation, kDefaultSize, gfx::Rect(kDefaultSize), kDefaultSize, media::EmptyExtraData(), media::Unencrypted()); if (!gpu_factories->IsDecoderConfigSupported(kImplementation, config))
diff --git a/content/renderer/media/webrtc/webrtc_video_utils.h b/content/renderer/media/webrtc/webrtc_video_utils.h index 1d91b2a4..5bcfcf0 100644 --- a/content/renderer/media/webrtc/webrtc_video_utils.h +++ b/content/renderer/media/webrtc/webrtc_video_utils.h
@@ -6,7 +6,7 @@ #define CONTENT_RENDERER_MEDIA_WEBRTC_WEBRTC_VIDEO_UTILS_H_ #include "media/base/video_color_space.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" #include "third_party/webrtc/api/video/color_space.h" #include "third_party/webrtc/api/video/video_rotation.h"
diff --git a/content/renderer/pepper/video_decoder_shim.cc b/content/renderer/pepper/video_decoder_shim.cc index 5c75dd4c..5163608 100644 --- a/content/renderer/pepper/video_decoder_shim.cc +++ b/content/renderer/pepper/video_decoder_shim.cc
@@ -894,7 +894,7 @@ media::VideoDecoderConfig video_decoder_config( codec, vda_config.profile, media::PIXEL_FORMAT_I420, - media::VideoColorSpace(), media::VIDEO_ROTATION_0, + media::VideoColorSpace(), media::kNoTransformation, gfx::Size(32, 24), // Small sizes that won't fail. gfx::Rect(32, 24), gfx::Size(32, 24), // TODO(bbudge): Verify extra data isn't needed.
diff --git a/content/shell/browser/shell.cc b/content/shell/browser/shell.cc index ad1d7259..0da00e47 100644 --- a/content/shell/browser/shell.cc +++ b/content/shell/browser/shell.cc
@@ -55,7 +55,7 @@ const int kDefaultTestWindowHeightDip = 600; std::vector<Shell*> Shell::windows_; -base::Callback<void(Shell*)> Shell::shell_created_callback_; +base::OnceCallback<void(Shell*)> Shell::shell_created_callback_; class Shell::DevToolsWebContentsObserver : public WebContentsObserver { public: @@ -107,10 +107,8 @@ windows_.push_back(this); - if (!shell_created_callback_.is_null()) { - shell_created_callback_.Run(this); - shell_created_callback_.Reset(); - } + if (shell_created_callback_) + std::move(shell_created_callback_).Run(this); } Shell::~Shell() { @@ -197,8 +195,8 @@ } void Shell::SetShellCreatedCallback( - base::Callback<void(Shell*)> shell_created_callback) { - DCHECK(shell_created_callback_.is_null()); + base::OnceCallback<void(Shell*)> shell_created_callback) { + DCHECK(!shell_created_callback_); shell_created_callback_ = std::move(shell_created_callback); }
diff --git a/content/shell/browser/shell.h b/content/shell/browser/shell.h index 354a936..9080bde 100644 --- a/content/shell/browser/shell.h +++ b/content/shell/browser/shell.h
@@ -124,7 +124,7 @@ // Used for content_browsertests. Called once. static void SetShellCreatedCallback( - base::Callback<void(Shell*)> shell_created_callback); + base::OnceCallback<void(Shell*)> shell_created_callback); WebContents* web_contents() const { return web_contents_.get(); } gfx::NativeWindow window() { return window_; } @@ -310,7 +310,7 @@ // of ordering. static std::vector<Shell*> windows_; - static base::Callback<void(Shell*)> shell_created_callback_; + static base::OnceCallback<void(Shell*)> shell_created_callback_; }; } // namespace content
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc index efc1902..e47f78b 100644 --- a/content/shell/browser/shell_content_browser_client.cc +++ b/content/shell/browser/shell_content_browser_client.cc
@@ -483,10 +483,11 @@ void ShellContentBrowserClient::OpenURL( SiteInstance* site_instance, const OpenURLParams& params, - const base::Callback<void(WebContents*)>& callback) { - callback.Run(Shell::CreateNewWindow(site_instance->GetBrowserContext(), - params.url, nullptr, gfx::Size()) - ->web_contents()); + base::OnceCallback<void(WebContents*)> callback) { + std::move(callback).Run( + Shell::CreateNewWindow(site_instance->GetBrowserContext(), params.url, + nullptr, gfx::Size()) + ->web_contents()); } std::unique_ptr<LoginDelegate> ShellContentBrowserClient::CreateLoginDelegate(
diff --git a/content/shell/browser/shell_content_browser_client.h b/content/shell/browser/shell_content_browser_client.h index 6d58d2dff..bc13ac56 100644 --- a/content/shell/browser/shell_content_browser_client.h +++ b/content/shell/browser/shell_content_browser_client.h
@@ -78,7 +78,7 @@ DevToolsManagerDelegate* GetDevToolsManagerDelegate() override; void OpenURL(SiteInstance* site_instance, const OpenURLParams& params, - const base::Callback<void(WebContents*)>& callback) override; + base::OnceCallback<void(WebContents*)> callback) override; std::unique_ptr<LoginDelegate> CreateLoginDelegate( const net::AuthChallengeInfo& auth_info, content::WebContents* web_contents,
diff --git a/content/shell/browser/shell_download_manager_delegate.cc b/content/shell/browser/shell_download_manager_delegate.cc index 2ca93dab..af9b2f3 100644 --- a/content/shell/browser/shell_download_manager_delegate.cc +++ b/content/shell/browser/shell_download_manager_delegate.cc
@@ -81,11 +81,9 @@ return true; } - FilenameDeterminedCallback filename_determined_callback = - base::Bind(&ShellDownloadManagerDelegate::OnDownloadPathGenerated, - weak_ptr_factory_.GetWeakPtr(), - download->GetId(), - callback); + FilenameDeterminedCallback filename_determined_callback = base::BindOnce( + &ShellDownloadManagerDelegate::OnDownloadPathGenerated, + weak_ptr_factory_.GetWeakPtr(), download->GetId(), callback); PostTaskWithTraits( FROM_HERE, @@ -118,7 +116,7 @@ const std::string& suggested_filename, const std::string& mime_type, const base::FilePath& suggested_directory, - const FilenameDeterminedCallback& callback) { + FilenameDeterminedCallback callback) { base::FilePath generated_name = net::GenerateFileName(url, content_disposition, std::string(), @@ -131,7 +129,7 @@ base::FilePath suggested_path(suggested_directory.Append(generated_name)); base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(callback, suggested_path)); + base::BindOnce(std::move(callback), suggested_path)); } void ShellDownloadManagerDelegate::OnDownloadPathGenerated(
diff --git a/content/shell/browser/shell_download_manager_delegate.h b/content/shell/browser/shell_download_manager_delegate.h index 6086da5..3e4922d 100644 --- a/content/shell/browser/shell_download_manager_delegate.h +++ b/content/shell/browser/shell_download_manager_delegate.h
@@ -38,15 +38,15 @@ private: friend class base::RefCountedThreadSafe<ShellDownloadManagerDelegate>; - typedef base::Callback<void(const base::FilePath&)> - FilenameDeterminedCallback; + using FilenameDeterminedCallback = + base::OnceCallback<void(const base::FilePath&)>; static void GenerateFilename(const GURL& url, const std::string& content_disposition, const std::string& suggested_filename, const std::string& mime_type, const base::FilePath& suggested_directory, - const FilenameDeterminedCallback& callback); + FilenameDeterminedCallback callback); void OnDownloadPathGenerated(uint32_t download_id, const DownloadTargetCallback& callback, const base::FilePath& suggested_path);
diff --git a/content/shell/browser/web_test/scoped_android_configuration.cc b/content/shell/browser/web_test/scoped_android_configuration.cc index ec1b9722..f5c3bae 100644 --- a/content/shell/browser/web_test/scoped_android_configuration.cc +++ b/content/shell/browser/web_test/scoped_android_configuration.cc
@@ -102,28 +102,27 @@ } void FinishRedirection( - const base::Callback<void(int)>& redirect, - const base::Callback<void(std::unique_ptr<net::SocketPosix>)>& - transfer_socket, + base::OnceCallback<void(int)> redirect, + base::OnceCallback<void(std::unique_ptr<net::SocketPosix>)> transfer_socket, base::WaitableEvent* event, std::unique_ptr<net::SocketPosix> socket) { - redirect.Run(socket->socket_fd()); - transfer_socket.Run(std::move(socket)); + std::move(redirect).Run(socket->socket_fd()); + std::move(transfer_socket).Run(std::move(socket)); event->Signal(); } -void RedirectStream( - uint16_t port, - const base::Callback<void(base::WaitableEvent*, - std::unique_ptr<net::SocketPosix>)>& - finish_redirection) { +void RedirectStream(uint16_t port, + base::OnceCallback<void(base::WaitableEvent*, + std::unique_ptr<net::SocketPosix>)> + finish_redirection) { base::WaitableEvent redirected( base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); base::PostTaskWithTraits( FROM_HERE, {BrowserThread::IO}, - base::BindOnce(&CreateAndConnectSocket, port, - base::BindOnce(finish_redirection, &redirected))); + base::BindOnce( + &CreateAndConnectSocket, port, + base::BindOnce(std::move(finish_redirection), &redirected))); base::ScopedAllowBaseSyncPrimitivesForTesting allow_wait; while (!redirected.IsSignaled()) redirected.Wait(); @@ -138,20 +137,20 @@ ScopedAndroidConfiguration::~ScopedAndroidConfiguration() = default; void ScopedAndroidConfiguration::RedirectStreams() { - // Unretained is safe here because all executions of add_socket finish - // before this function returns. - base::Callback<void(std::unique_ptr<net::SocketPosix>)> add_socket = - base::Bind(&ScopedAndroidConfiguration::AddSocket, - base::Unretained(this)); - std::string stdout_port_str = base::CommandLine::ForCurrentProcess()->GetSwitchValueNative( switches::kAndroidStdoutPort); unsigned stdout_port = 0; if (base::StringToUint(stdout_port_str, &stdout_port)) { - RedirectStream(base::checked_cast<uint16_t>(stdout_port), - base::Bind(&FinishRedirection, base::Bind(&RedirectStdout), - add_socket)); + auto redirect_callback = base::BindOnce(&RedirectStdout); + // Unretained is safe here because all executions of transfer_callback + // finish before this function returns. + auto transfer_callback = base::BindOnce( + &ScopedAndroidConfiguration::AddSocket, base::Unretained(this)); + RedirectStream( + base::checked_cast<uint16_t>(stdout_port), + base::BindOnce(&FinishRedirection, std::move(redirect_callback), + std::move(transfer_callback))); } std::string stdin_port_str = @@ -159,9 +158,15 @@ switches::kAndroidStdinPort); unsigned stdin_port = 0; if (base::StringToUint(stdin_port_str, &stdin_port)) { + auto redirect_callback = base::BindOnce(&RedirectStdin); + // Unretained is safe here because all executions of transfer_callback + // finish before this function returns. + auto transfer_callback = base::BindOnce( + &ScopedAndroidConfiguration::AddSocket, base::Unretained(this)); RedirectStream( base::checked_cast<uint16_t>(stdin_port), - base::Bind(&FinishRedirection, base::Bind(&RedirectStdin), add_socket)); + base::BindOnce(&FinishRedirection, std::move(redirect_callback), + std::move(transfer_callback))); } std::string stderr_port_str = @@ -169,9 +174,15 @@ switches::kAndroidStderrPort); unsigned stderr_port = 0; if (base::StringToUint(stderr_port_str, &stderr_port)) { - RedirectStream(base::checked_cast<uint16_t>(stderr_port), - base::Bind(&FinishRedirection, base::Bind(&RedirectStderr), - add_socket)); + auto redirect_callback = base::BindOnce(&RedirectStderr); + // Unretained is safe here because all executions of transfer_callback + // finish before this function returns. + auto transfer_callback = base::BindOnce( + &ScopedAndroidConfiguration::AddSocket, base::Unretained(this)); + RedirectStream( + base::checked_cast<uint16_t>(stderr_port), + base::BindOnce(&FinishRedirection, std::move(redirect_callback), + std::move(transfer_callback))); } }
diff --git a/device/fido/authenticator_supported_options.h b/device/fido/authenticator_supported_options.h index 65e6fea..b0ba9137 100644 --- a/device/fido/authenticator_supported_options.h +++ b/device/fido/authenticator_supported_options.h
@@ -60,6 +60,9 @@ // Indicates whether the authenticator supports the vendor-specific preview of // the CTAP2 authenticatorCredentialManagement command. bool supports_credential_management_preview = false; + // supports_cred_protect is true if the authenticator supports the + // `credProtect` extension. See CTAP2 draft for details. + bool supports_cred_protect = false; // Represents whether client pin is set and stored in authenticator. Set as // null optional if client pin capability is not supported by the // authenticator.
diff --git a/device/fido/ctap_make_credential_request.cc b/device/fido/ctap_make_credential_request.cc index ef8a693..e2c6d7c4 100644 --- a/device/fido/ctap_make_credential_request.cc +++ b/device/fido/ctap_make_credential_request.cc
@@ -60,9 +60,26 @@ cbor_map[cbor::Value(5)] = cbor::Value(std::move(exclude_list_array)); } + cbor::Value::MapValue extensions; + if (request.hmac_secret) { - cbor::Value::MapValue extensions; extensions[cbor::Value(kExtensionHmacSecret)] = cbor::Value(true); + } + + if (request.cred_protect) { + int value; + switch (request.cred_protect->first) { + case CredProtect::kUVOrCredIDRequired: + value = 2; + break; + case CredProtect::kUVRequired: + value = 3; + break; + } + extensions.emplace(kExtensionCredProtect, value); + } + + if (!extensions.empty()) { cbor_map[cbor::Value(6)] = cbor::Value(std::move(extensions)); }
diff --git a/device/fido/ctap_make_credential_request.h b/device/fido/ctap_make_credential_request.h index 25dda96..387bbfac 100644 --- a/device/fido/ctap_make_credential_request.h +++ b/device/fido/ctap_make_credential_request.h
@@ -75,6 +75,13 @@ base::Optional<uint8_t> pin_protocol; AttestationConveyancePreference attestation_preference = AttestationConveyancePreference::NONE; + + // cred_protect indicates the level of protection afforded to a credential. + // This depends on a CTAP2 extension that not all authenticators will support. + // The second element is true if the indicated protection level must be + // provided by the target authenticator for the MakeCredential request to be + // sent. + base::Optional<std::pair<CredProtect, bool>> cred_protect; }; } // namespace device
diff --git a/device/fido/device_response_converter.cc b/device/fido/device_response_converter.cc index 9ca4abe0..d351270 100644 --- a/device/fido/device_response_converter.cc +++ b/device/fido/device_response_converter.cc
@@ -206,6 +206,7 @@ std::move(protocol_versions), base::make_span<kAaguidLength>(it->second.GetBytestring())); + AuthenticatorSupportedOptions options; it = response_map.find(CBOR(2)); if (it != response_map.end()) { if (!it->second.is_array()) @@ -216,12 +217,15 @@ if (!extension.is_string()) return base::nullopt; - extensions.push_back(extension.GetString()); + const std::string& extension_str = extension.GetString(); + if (extension_str == kExtensionCredProtect) { + options.supports_cred_protect = true; + } + extensions.push_back(extension_str); } response.extensions = std::move(extensions); } - AuthenticatorSupportedOptions options; it = response_map.find(CBOR(4)); if (it != response_map.end()) { if (!it->second.is_map())
diff --git a/device/fido/fido_constants.cc b/device/fido/fido_constants.cc index fa78ae3..77088a7 100644 --- a/device/fido/fido_constants.cc +++ b/device/fido/fido_constants.cc
@@ -58,6 +58,7 @@ const char kU2fVersion[] = "U2F_V2"; const char kExtensionHmacSecret[] = "hmac-secret"; +const char kExtensionCredProtect[] = "credProtect"; const base::TimeDelta kBleDevicePairingModeWaitingInterval = base::TimeDelta::FromSeconds(2);
diff --git a/device/fido/fido_constants.h b/device/fido/fido_constants.h index 62e0b12..a2727a6 100644 --- a/device/fido/fido_constants.h +++ b/device/fido/fido_constants.h
@@ -385,6 +385,7 @@ COMPONENT_EXPORT(DEVICE_FIDO) extern const char kU2fVersion[]; COMPONENT_EXPORT(DEVICE_FIDO) extern const char kExtensionHmacSecret[]; +COMPONENT_EXPORT(DEVICE_FIDO) extern const char kExtensionCredProtect[]; // Maximum number of seconds the browser waits for Bluetooth authenticator to // send packets that advertises that the device is in pairing mode before @@ -405,6 +406,13 @@ ENTERPRISE, }; +// CredProtect enumerates the levels of credential protection specified by the +// `credProtect` CTAP2 extension. +enum class CredProtect : uint8_t { + kUVOrCredIDRequired = 1, + kUVRequired = 2, +}; + } // namespace device #endif // DEVICE_FIDO_FIDO_CONSTANTS_H_
diff --git a/device/fido/get_assertion_task.cc b/device/fido/get_assertion_task.cc index 26e0ca0..6bba57c 100644 --- a/device/fido/get_assertion_task.cc +++ b/device/fido/get_assertion_task.cc
@@ -91,12 +91,19 @@ // Silently probe each credential in the allow list to work around // authenticators rejecting lists over a certain size. Also probe silently if // the request may fall back to U2F and the authenticator doesn't recognize - // any of the provided credential IDs. (caBLE devices, however, might not - // support silent probing so don't do it with them.) - if (device()->DeviceTransport() != + // any of the provided credential IDs. + if ((request_.allow_list.size() > 1 || + MayFallbackToU2fWithAppIdExtension(*device(), request_)) && + // caBLE devices might not support silent probing so don't do it with + // them. + device()->DeviceTransport() != FidoTransportProtocol::kCloudAssistedBluetoothLowEnergy && - (request_.allow_list.size() > 1 || - MayFallbackToU2fWithAppIdExtension(*device(), request_))) { + // If the device supports credProtect then it might have UV-required + // credentials which it'll pretend don't exist for silent requests. + // TODO(agl): should support batching of, and filtering over-long, + // credentials based on GetInfo data. Also should support + // PIN-authenticated silent requests. + !device()->device_info()->options.supports_cred_protect) { sign_operation_ = std::make_unique<Ctap2DeviceOperation< CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>>( device(), NextSilentRequest(),
diff --git a/device/fido/make_credential_request_handler.cc b/device/fido/make_credential_request_handler.cc index 181fd94..37dd6b5 100644 --- a/device/fido/make_credential_request_handler.cc +++ b/device/fido/make_credential_request_handler.cc
@@ -76,6 +76,15 @@ return FidoReturnCode::kAuthenticatorMissingResidentKeys; } + // TODO(martinkr): the Windows integration needs to be able to pass the + // credProtect information to the DLL, and to fail if the DLL version is too + // low to support credProtect. + if (request.cred_protect && request.cred_protect->second && + (!authenticator->Options() || + !authenticator->Options()->supports_cred_protect)) { + return FidoReturnCode::kAuthenticatorMissingResidentKeys; + } + if (authenticator->WillNeedPINToMakeCredential(request, observer) == MakeCredentialPINDisposition::kUnsatisfiable) { return FidoReturnCode::kAuthenticatorMissingUserVerification; @@ -225,6 +234,11 @@ } else { request.user_verification = UserVerificationRequirement::kDiscouraged; } + + if (request.cred_protect && + !authenticator->Options()->supports_cred_protect) { + request.cred_protect.reset(); + } } ReportMakeCredentialRequestTransport(authenticator); @@ -507,6 +521,10 @@ // If doing a PIN operation then we don't ask the authenticator to also do // internal UV. request.user_verification = UserVerificationRequirement::kDiscouraged; + if (request.cred_protect && authenticator_->Options() && + !authenticator_->Options()->supports_cred_protect) { + request.cred_protect.reset(); + } ReportMakeCredentialRequestTransport(authenticator_);
diff --git a/device/fido/public_key_credential_user_entity.cc b/device/fido/public_key_credential_user_entity.cc index 990bc5d4..056b648349 100644 --- a/device/fido/public_key_credential_user_entity.cc +++ b/device/fido/public_key_credential_user_entity.cc
@@ -61,7 +61,8 @@ user_map.emplace(kEntityIdMapKey, user.id); if (user.name) user_map.emplace(kEntityNameMapKey, *user.name); - if (user.icon_url) + // Empty icon URLs result in CTAP1_ERR_INVALID_LENGTH on some security keys. + if (user.icon_url && !user.icon_url->is_empty()) user_map.emplace(kIconUrlMapKey, user.icon_url->spec()); if (user.display_name) user_map.emplace(kDisplayNameMapKey, *user.display_name);
diff --git a/device/fido/virtual_ctap2_device.cc b/device/fido/virtual_ctap2_device.cc index 734b5edb..eeb9a0c 100644 --- a/device/fido/virtual_ctap2_device.cc +++ b/device/fido/virtual_ctap2_device.cc
@@ -496,6 +496,11 @@ if (options_updated) { device_info_->options = std::move(options); } + + if (config.cred_protect_support) { + device_info_->extensions.emplace( + {std::string(device::kExtensionCredProtect)}); + } } VirtualCtap2Device::~VirtualCtap2Device() = default; @@ -608,7 +613,15 @@ } for (const auto& excluded_credential : *request.exclude_list) { - if (FindRegistrationData(excluded_credential.id(), rp_id_hash)) { + const RegistrationData* found = + FindRegistrationData(excluded_credential.id(), rp_id_hash); + if (found) { + if (found->protection == device::CredProtect::kUVRequired && + !user_verified) { + // Cannot disclose the existence of this credential without UV. If + // a credentials ends up being created it'll overwrite this one. + continue; + } if (mutable_state()->simulate_press_callback) { mutable_state()->simulate_press_callback.Run(); } @@ -648,10 +661,19 @@ std::vector<uint8_t> key_handle(hash.begin(), hash.end()); base::Optional<cbor::Value> extensions; + cbor::Value::MapValue extensions_map; if (request.hmac_secret) { - cbor::Value::MapValue extensions_map; extensions_map.emplace(cbor::Value(kExtensionHmacSecret), cbor::Value(true)); + } + + if (request.cred_protect) { + extensions_map.emplace( + cbor::Value(kExtensionCredProtect), + cbor::Value( + request.cred_protect->first == CredProtect::kUVRequired ? 3 : 2)); + } + if (!extensions_map.empty()) { extensions = cbor::Value(std::move(extensions_map)); } @@ -713,6 +735,10 @@ registration.user = request.user; } + if (request.cred_protect) { + registration.protection = request.cred_protect->first; + } + StoreNewKey(key_handle, std::move(registration)); return CtapDeviceResponseCode::kSuccess; } @@ -793,6 +819,25 @@ } } + // Enforce credProtect semantics. + found_registrations.erase( + std::remove_if( + found_registrations.begin(), found_registrations.end(), + [user_verified, &request]( + const std::pair<base::span<const uint8_t>, RegistrationData*>& + candidate) -> bool { + if (!candidate.second->protection) { + return false; + } + switch (*candidate.second->protection) { + case CredProtect::kUVOrCredIDRequired: + return request.allow_list.empty() && !user_verified; + case CredProtect::kUVRequired: + return !user_verified; + } + }), + found_registrations.end()); + if (config_.return_immediate_invalid_credential_error && found_registrations.empty()) { return CtapDeviceResponseCode::kCtap2ErrInvalidCredential; @@ -1439,6 +1484,29 @@ } request.hmac_secret = hmac_secret_it->second.GetBool(); } + + const auto cred_protect_it = + extensions.find(cbor::Value(device::kExtensionCredProtect)); + if (cred_protect_it != extensions.end()) { + if (!cred_protect_it->second.is_unsigned()) { + return base::nullopt; + } + switch (cred_protect_it->second.GetUnsigned()) { + case 1: + // Default behaviour. + break; + case 2: + request.cred_protect = + std::make_pair(device::CredProtect::kUVOrCredIDRequired, false); + break; + case 3: + request.cred_protect = + std::make_pair(device::CredProtect::kUVRequired, false); + break; + default: + return base::nullopt; + } + } } const auto option_it = request_map.find(cbor::Value(7));
diff --git a/device/fido/virtual_ctap2_device.h b/device/fido/virtual_ctap2_device.h index 7fe53ba8..23277298 100644 --- a/device/fido/virtual_ctap2_device.h +++ b/device/fido/virtual_ctap2_device.h
@@ -44,6 +44,7 @@ bool internal_uv_support = false; bool resident_key_support = false; bool credential_management_support = false; + bool cred_protect_support = false; // resident_credential_storage is the number of resident credentials that // the device will store before returning KEY_STORE_FULL. size_t resident_credential_storage = 3;
diff --git a/device/fido/virtual_fido_device.h b/device/fido/virtual_fido_device.h index bfcd28f..f5d63b4a 100644 --- a/device/fido/virtual_fido_device.h +++ b/device/fido/virtual_fido_device.h
@@ -55,6 +55,7 @@ bool is_resident = false; // is_u2f is true if the credential was created via a U2F interface. bool is_u2f = false; + base::Optional<device::CredProtect> protection; // user is only valid if |is_resident| is true. base::Optional<device::PublicKeyCredentialUserEntity> user;
diff --git a/device/vr/public/mojom/isolated_xr_service.mojom b/device/vr/public/mojom/isolated_xr_service.mojom index 2685841e..652b623 100644 --- a/device/vr/public/mojom/isolated_xr_service.mojom +++ b/device/vr/public/mojom/isolated_xr_service.mojom
@@ -50,10 +50,6 @@ int32 render_process_id; int32 render_frame_id; - // A flag to indicate if there has been a user activation when the request - // session is made. - bool has_user_activation; - // This flag ensures that render path's that are only supported in WebXR are // not used for WebVR 1.1. bool use_legacy_webvr_render_path;
diff --git a/device/vr/public/mojom/vr_service.mojom b/device/vr/public/mojom/vr_service.mojom index 1f1e6b1..b6b0bd3 100644 --- a/device/vr/public/mojom/vr_service.mojom +++ b/device/vr/public/mojom/vr_service.mojom
@@ -46,10 +46,6 @@ bool immersive; bool environment_integration; - // A flag to indicate if there has been a user activation when the request - // session is made. - bool has_user_activation; - // This flag ensures that render paths that are only supported in WebXR are // not used for WebVR 1.1. bool use_legacy_webvr_render_path; @@ -126,6 +122,8 @@ // // A field of view, given by 4 degrees describing the view from a center point. +// For a typical field of view that contains the center point, all angles are +// positive. struct VRFieldOfView { float upDegrees; float downDegrees; @@ -266,10 +264,6 @@ // imply no mapping. int16 frame_id; - // Pass through camera values - gfx.mojom.Size? buffer_size; - array<float, 16>? projection_matrix; - // Eye parameters may be provided per-frame for some runtimes. If both of // these are null, it indicates that there was no change since the previous // frame. If either are non-null, it indicates that data has changed. If only @@ -348,14 +342,6 @@ // XRSession. For example, some AR sessions would implement hit test to allow // developers to get the information about the world that its sensors supply. interface XREnvironmentIntegrationProvider { - // Different devices can have different native orientations - 0 is the native - // orientation, and then increments of 90 degrees from there. Session geometry - // is needed by the device when integrating environment image data, i.e. - // camera feeds, into a session. - UpdateSessionGeometry( - gfx.mojom.Size frame_size, - display.mojom.Rotation display_rotation); - // Performs a raycast into the scene and returns a list of XRHitResults sorted // from closest to furthest hit from the ray. Each hit result contains a // hit_matrix containing the transform of the hit where the rotation
diff --git a/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc b/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc index 7af9904..959b85c 100644 --- a/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc +++ b/device/vr/windows_mixed_reality/mixed_reality_input_helper.cc
@@ -13,8 +13,11 @@ #include <unordered_map> #include <vector> +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" #include "device/gamepad/public/cpp/gamepads.h" #include "device/vr/public/mojom/isolated_xr_service.mojom.h" +#include "device/vr/util/copy_to_ustring.h" #include "device/vr/windows_mixed_reality/wrappers/wmr_input_location.h" #include "device/vr/windows_mixed_reality/wrappers/wmr_input_manager.h" #include "device/vr/windows_mixed_reality/wrappers/wmr_input_source.h" @@ -45,6 +48,10 @@ namespace { constexpr double kDeadzoneMinimum = 0.1; +double ApplyAxisDeadzone(double value) { + return std::fabs(value) < kDeadzoneMinimum ? 0 : value; +} + void AddButton(mojom::XRGamepadPtr& gamepad, ButtonData* data) { if (data) { auto button = mojom::XRGamepadButton::New(); @@ -61,13 +68,11 @@ // These methods are only called for the thumbstick and touchpad, which both // have an X and Y. void AddAxes(mojom::XRGamepadPtr& gamepad, ButtonData data) { - gamepad->axes.push_back( - std::fabs(data.x_axis) < kDeadzoneMinimum ? 0 : data.x_axis); - gamepad->axes.push_back( - std::fabs(data.y_axis) < kDeadzoneMinimum ? 0 : data.y_axis); + gamepad->axes.push_back(ApplyAxisDeadzone(data.x_axis)); + gamepad->axes.push_back(ApplyAxisDeadzone(data.y_axis)); } -void AddButtonAndAxes(mojom::XRGamepadPtr& gamepad, ButtonData data) { +void AddButtonWithAxes(mojom::XRGamepadPtr& gamepad, ButtonData data) { AddButton(gamepad, &data); AddAxes(gamepad, data); } @@ -133,11 +138,11 @@ // use the polled button state for select here. Voice (which we cannot get // via polling), lacks enough data to be considered a "Gamepad", and if we // used eventing the pressed state may be inconsistent. - AddButtonAndAxes(gamepad, input_state.button_data[ButtonName::kThumbstick]); + AddButtonWithAxes(gamepad, input_state.button_data[ButtonName::kThumbstick]); AddButton(gamepad, &input_state.button_data[ButtonName::kSelect]); AddButton(gamepad, &input_state.button_data[ButtonName::kGrip]); AddButton(gamepad, nullptr); // Nothing seems to trigger this button in Edge. - AddButtonAndAxes(gamepad, input_state.button_data[ButtonName::kTouchpad]); + AddButtonWithAxes(gamepad, input_state.button_data[ButtonName::kTouchpad]); gamepad->pose = ConvertToVRPose(input_state.gamepad_pose); gamepad->hand = input_state.source_state->description->handedness; @@ -149,6 +154,70 @@ return gamepad; } +GamepadHand MojoToGamepadHandedness(device::mojom::XRHandedness handedness) { + switch (handedness) { + case device::mojom::XRHandedness::LEFT: + return GamepadHand::kLeft; + case device::mojom::XRHandedness::RIGHT: + return GamepadHand::kRight; + case device::mojom::XRHandedness::NONE: + return GamepadHand::kNone; + } + + NOTREACHED(); +} + +void AddButton(Gamepad& gamepad, ButtonData* data) { + DCHECK_LT(gamepad.buttons_length, Gamepad::kButtonsLengthCap); + if (data) { + gamepad.buttons[gamepad.buttons_length++] = + GamepadButton(data->pressed, data->touched, data->value); + } else { + gamepad.buttons[gamepad.buttons_length++] = GamepadButton(); + } +} + +void AddAxes(Gamepad& gamepad, ButtonData data) { + DCHECK_LT(gamepad.axes_length + 1, Gamepad::kAxesLengthCap); + gamepad.axes[gamepad.axes_length++] = ApplyAxisDeadzone(data.x_axis); + gamepad.axes[gamepad.axes_length++] = ApplyAxisDeadzone(data.y_axis); +} + +void AddButtonWithAxes(Gamepad& gamepad, ButtonData data) { + AddButton(gamepad, &data); + AddAxes(gamepad, data); +} + +Gamepad GetWebXRGamepad(ParsedInputState& input_state) { + Gamepad gamepad; + gamepad.connected = true; + gamepad.timestamp = base::TimeTicks::Now().since_origin().InMicroseconds(); + + // TODO(https://crbug.com/942201): Get correct ID string once WebXR spec issue + // #550 (https://github.com/immersive-web/webxr/issues/550) is resolved. + CopyToUString(base::UTF8ToUTF16("unknown"), gamepad.id, + base::size(gamepad.id)); + + CopyToUString(base::UTF8ToUTF16("xr-standard"), gamepad.mapping, + base::size(gamepad.mapping)); + + if (input_state.source_state && input_state.source_state->description) { + gamepad.hand = MojoToGamepadHandedness( + input_state.source_state->description->handedness); + } else { + gamepad.hand = GamepadHand::kNone; + } + + // The order of these buttons is dictated by the xr-standard Gamepad mapping. + // Thumbstick is considered the primary 2D input axis, while the touchpad is + // the secondary 2D input axis. + AddButton(gamepad, &input_state.button_data[ButtonName::kSelect]); + AddButtonWithAxes(gamepad, input_state.button_data[ButtonName::kThumbstick]); + AddButton(gamepad, &input_state.button_data[ButtonName::kGrip]); + AddButtonWithAxes(gamepad, input_state.button_data[ButtonName::kTouchpad]); + return gamepad; +} + // Note that since this is built by polling, and so eventing changes are not // accounted for here. std::unordered_map<ButtonName, ButtonData> ParseButtonState( @@ -348,8 +417,11 @@ for (auto state : source_states) { auto parsed_source_state = LockedParseWindowsSourceState(state, origin); - if (parsed_source_state.source_state) + if (parsed_source_state.source_state) { + parsed_source_state.source_state->gamepad = + GetWebXRGamepad(parsed_source_state); input_states.push_back(std::move(parsed_source_state.source_state)); + } } for (unsigned int i = 0; i < pending_voice_states_.size(); i++) {
diff --git a/extensions/browser/url_loader_factory_manager.cc b/extensions/browser/url_loader_factory_manager.cc index 5d4b0ebb..fc242e5 100644 --- a/extensions/browser/url_loader_factory_manager.cc +++ b/extensions/browser/url_loader_factory_manager.cc
@@ -155,6 +155,7 @@ "7BFE588B209A15260DE12777B4BBB738DE98FE6C", "7C9DEE7EABBF6C722DC7C1B86460F0507E5AA561", "808FA9BB3CD501D7801D1CD6D5A3DBA088FDD46F", + "81FD24AF95679B900370DB857CE2EACADBE50A9B", "82FDBBF79F3517C3946BD89EAAF90C46DFDA4681", "83431421F759AE7A3BDAC00A4959D13095C65805", "834BD6E8E9F59D388DBB264453EB08A5DE45ED03", @@ -200,6 +201,7 @@ "AF0965B74237AFF383C981C05178732C9A05A140", "B3CF6C01796E8D03378FAA77AF507E27BB847E9D", "B4782AE831D849EFCC2AF4BE2012816EDDF8D908", + "B6903E9A5A8CC5D74A688DA0A67AFA2B0944F605", "BF5224FB246A6B67EA986EFF77A43F6C1BCA9672", "C0A30989F3717CE5B1B2FE462797951EA6D3922A", "C4A81852B9ACE6CE02DAB58BB77BDA0AD75716EC", @@ -217,6 +219,7 @@ "D572BE31227F6D0BE95B9430BE2D5F21D7D9CF9A", "D7C3879A8898618E3A23B0E6BFB6A38D01606246", "D9A97CD75380C697C65D37512E53DBECDFA45FB9", + "DC39837AC518B832FCB2D2DC1CE8BA148F54758E", "DC88B4C9E547F3E321B3E64CCDBD4B698116D2F4", "DDA21167F058A65D878DF84C3CF3FCC60B053E80", "E134BC4A0FF6C59CE42CC76BA6B2D6F5DC648EC4", @@ -231,6 +234,7 @@ "EC24668224116D19FF1A5FFAA61238B88773982C", "EC4A841BD03C8E5202043165188A9E060BF703A3", "EE4BE5F23D2E59E4713958465941EFB4A18166B7", + "EE711E704D4A365C4644EE4637076C81DF454EA6", "EF97543DC0DE66EF00D804A55DCF73E0BACB8773", "F1ACA279F460440E47078D91FE372212DD9B8709", "F273C23C616F5C56E8EDBAE24B21F5D408936A0D",
diff --git a/gpu/angle_end2end_tests_main.cc b/gpu/angle_end2end_tests_main.cc index e6fde85..5d9353cb 100644 --- a/gpu/angle_end2end_tests_main.cc +++ b/gpu/angle_end2end_tests_main.cc
@@ -24,8 +24,8 @@ int main(int argc, char** argv) { base::CommandLine::Init(argc, argv); - testing::InitGoogleMock(&argc, argv); ANGLEProcessTestArgs(&argc, argv); + testing::InitGoogleMock(&argc, argv); base::TestSuite test_suite(argc, argv); int rt = base::LaunchUnitTestsWithOptions( argc, argv,
diff --git a/gpu/command_buffer/client/webgpu_implementation.cc b/gpu/command_buffer/client/webgpu_implementation.cc index af47df4..7d875ed90 100644 --- a/gpu/command_buffer/client/webgpu_implementation.cc +++ b/gpu/command_buffer/client/webgpu_implementation.cc
@@ -8,6 +8,7 @@ #include <vector> #include "base/numerics/checked_math.h" +#include "base/trace_event/trace_event.h" #include "gpu/command_buffer/client/gpu_control.h" #include "gpu/command_buffer/client/shared_memory_limits.h" @@ -195,6 +196,14 @@ void WebGPUImplementation::OnGpuControlReturnData( base::span<const uint8_t> data) { #if BUILDFLAG(USE_DAWN) + + static uint32_t return_trace_id = 0; + TRACE_EVENT_FLOW_END0(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"), + "DawnReturnCommands", return_trace_id++); + + TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"), + "WebGPUImplementation::OnGpuControlReturnData", "bytes", + data.size()); if (!wire_client_->HandleCommands( reinterpret_cast<const char*>(data.data()), data.size())) { // TODO(enga): Lose the context. @@ -229,6 +238,8 @@ uint32_t allocation_size = std::max(c2s_buffer_default_size_, static_cast<uint32_t>(size)); + TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"), + "WebGPUImplementation::GetCmdSpace", "bytes", allocation_size); c2s_buffer_.Reset(allocation_size); c2s_put_offset_ = 0; next_offset = size; @@ -248,6 +259,14 @@ bool WebGPUImplementation::Flush() { if (c2s_buffer_.valid()) { + TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"), + "WebGPUImplementation::Flush", "bytes", c2s_put_offset_); + + TRACE_EVENT_FLOW_BEGIN0( + TRACE_DISABLED_BY_DEFAULT("gpu.dawn"), "DawnCommands", + (static_cast<uint64_t>(c2s_buffer_.shm_id()) << 32) + + c2s_buffer_.offset()); + c2s_buffer_.Shrink(c2s_put_offset_); helper_->DawnCommands(c2s_buffer_.shm_id(), c2s_buffer_.offset(), c2s_put_offset_);
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc index 597d456..d5a1901 100644 --- a/gpu/command_buffer/service/webgpu_decoder_impl.cc +++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -13,6 +13,7 @@ #include "base/logging.h" #include "base/macros.h" +#include "base/trace_event/trace_event.h" #include "gpu/command_buffer/common/mailbox.h" #include "gpu/command_buffer/common/webgpu_cmd_format.h" #include "gpu/command_buffer/common/webgpu_cmd_ids.h" @@ -81,6 +82,13 @@ bool WireServerCommandSerializer::Flush() { if (put_offset_ > 0) { + TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"), + "WireServerCommandSerializer::Flush", "bytes", put_offset_); + + static uint32_t return_trace_id = 0; + TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"), + "DawnReturnCommands", return_trace_id++); + client_->HandleReturnData(base::make_span(buffer_.data(), put_offset_)); put_offset_ = 0; } @@ -171,6 +179,8 @@ void PerformPollingWork() override { DCHECK(dawn_device_); DCHECK(wire_serializer_); + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"), + "WebGPUDecoderImpl::PerformPollingWork"); dawn_procs_.deviceTick(dawn_device_); wire_serializer_->Flush(); } @@ -524,6 +534,12 @@ return error::kOutOfBounds; } + TRACE_EVENT_FLOW_END0( + TRACE_DISABLED_BY_DEFAULT("gpu.dawn"), "DawnCommands", + (static_cast<uint64_t>(commands_shm_id) << 32) + commands_shm_offset); + + TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"), + "WebGPUDecoderImpl::HandleDawnCommands", "bytes", size); std::vector<char> commands(shm_commands, shm_commands + size); if (!wire_server_->HandleCommands(commands.data(), size)) { NOTREACHED();
diff --git a/gpu/vulkan/vulkan_fence_helper.cc b/gpu/vulkan/vulkan_fence_helper.cc index 15df368..3bbafcf 100644 --- a/gpu/vulkan/vulkan_fence_helper.cc +++ b/gpu/vulkan/vulkan_fence_helper.cc
@@ -43,7 +43,7 @@ VulkanFenceHelper::FenceHandle VulkanFenceHelper::EnqueueFence(VkFence fence) { FenceHandle handle(fence, next_generation_++); - cleanup_tasks_.emplace(handle, std::move(tasks_pending_fence_)); + cleanup_tasks_.emplace_back(handle, std::move(tasks_pending_fence_)); tasks_pending_fence_ = std::vector<CleanupTask>(); return handle; @@ -82,12 +82,7 @@ // |current_generation_| as far as possible. This assumes that fences pass in // order, which isn't a hard API guarantee, but should be close enough / // efficient enough for the purpose or processing cleanup tasks. - // - // Also runs any cleanup tasks for generations that have passed. Create a - // temporary vector of tasks to run to avoid reentrancy issues. - std::vector<CleanupTask> tasks_to_run; - while (!cleanup_tasks_.empty()) { - TasksForFence& tasks_for_fence = cleanup_tasks_.front(); + for (const auto& tasks_for_fence : cleanup_tasks_) { VkResult result = vkGetFenceStatus(device, tasks_for_fence.handle.fence_); if (result == VK_NOT_READY) break; @@ -96,12 +91,22 @@ return; } current_generation_ = tasks_for_fence.handle.generation_id_; - vkDestroyFence(device, tasks_for_fence.handle.fence_, nullptr); + } + // Runs any cleanup tasks for generations that have passed. Create a temporary + // vector of tasks to run to avoid reentrancy issues. + std::vector<CleanupTask> tasks_to_run; + while (!cleanup_tasks_.empty()) { + TasksForFence& tasks_for_fence = cleanup_tasks_.front(); + if (tasks_for_fence.handle.generation_id_ > current_generation_) + break; + DCHECK_EQ(vkGetFenceStatus(device, tasks_for_fence.handle.fence_), + VK_SUCCESS); + vkDestroyFence(device, tasks_for_fence.handle.fence_, nullptr); tasks_to_run.insert(tasks_to_run.end(), std::make_move_iterator(tasks_for_fence.tasks.begin()), std::make_move_iterator(tasks_for_fence.tasks.end())); - cleanup_tasks_.pop(); + cleanup_tasks_.pop_front(); } for (auto& task : tasks_to_run) @@ -190,7 +195,7 @@ tasks_to_run.insert(tasks_to_run.end(), std::make_move_iterator(tasks_for_fence.tasks.begin()), std::make_move_iterator(tasks_for_fence.tasks.end())); - cleanup_tasks_.pop(); + cleanup_tasks_.pop_front(); } tasks_to_run.insert(tasks_to_run.end(), std::make_move_iterator(tasks_pending_fence_.begin()),
diff --git a/gpu/vulkan/vulkan_fence_helper.h b/gpu/vulkan/vulkan_fence_helper.h index 5d64eee..36a7416 100644 --- a/gpu/vulkan/vulkan_fence_helper.h +++ b/gpu/vulkan/vulkan_fence_helper.h
@@ -8,7 +8,7 @@ #include <vulkan/vulkan.h> #include "base/callback.h" -#include "base/containers/queue.h" +#include "base/containers/circular_deque.h" #include "base/macros.h" #include "gpu/vulkan/vulkan_export.h" @@ -115,7 +115,7 @@ FenceHandle handle; std::vector<CleanupTask> tasks; }; - base::queue<TasksForFence> cleanup_tasks_; + base::circular_deque<TasksForFence> cleanup_tasks_; DISALLOW_COPY_AND_ASSIGN(VulkanFenceHelper); };
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg index 0dc291f5..0a21c593 100644 --- a/infra/config/cr-buildbucket.cfg +++ b/infra/config/cr-buildbucket.cfg
@@ -751,12 +751,6 @@ } builders { - name: "Android Cronet Builder" - mixins: "android-ci" - dimensions: "os:Ubuntu-14.04" - } - - builders { name: "Android FYI 32 dEQP Vk Release (Pixel 2)" mixins: "android-gpu-fyi-ci" } @@ -2147,12 +2141,6 @@ mixins: "fuzz-ci" } builders { - name: "Android Cronet Marshmallow 64bit Builder" - dimensions: "os:Ubuntu-14.04" - dimensions: "device_os:MMB29Q" - mixins: "android-ci" - } - builders { name: "Win7" dimensions: "os:Windows-7" mixins: "chromedriver-ci" @@ -2294,12 +2282,6 @@ dimensions: "os:Ubuntu-14.04" mixins: "memory-ci" } - builders { - name: "Android Cronet Lollipop Builder" - dimensions: "os:Ubuntu-14.04" - dimensions: "device_os:LMY48I" - mixins: "android-ci" - } # TODO(crbug.com/888810): Remove once these bots are migrated. builders { name: "android-dbg" @@ -2344,21 +2326,11 @@ mixins: "fuzz-ci" } builders { - name: "Android Cronet x86 Builder" - dimensions: "os:Ubuntu-14.04" - mixins: "android-ci" - } - builders { name: "Linux remote_run Tester" dimensions: "os:Ubuntu-14.04" mixins: "fyi-ci" } builders { - name: "Android Cronet ARM64 Builder" - dimensions: "os:Ubuntu-14.04" - mixins: "android-ci" - } - builders { name: "WebKit Mac10.13 (retina)" dimensions: "os:Mac-10.13" mixins: "mac-ci" @@ -2377,11 +2349,6 @@ mixins: "fyi-ci" } builders { - name: "Android Cronet Builder Asan" - dimensions: "os:Ubuntu-14.04" - mixins: "android-ci" - } - builders { name: "Linux MSan Tests" dimensions: "os:Ubuntu-14.04" mixins: "memory-ci" @@ -2500,11 +2467,6 @@ dimensions: "cores:4" mixins: "fyi-ci" } - builders { - name: "Android Cronet KitKat Builder" - dimensions: "os:Ubuntu-14.04" - mixins: "android-fyi-ci" - } # TODO(crbug.com/888810): Remove once these bots are migrated. builders { name: "linux-dbg" @@ -2589,11 +2551,6 @@ mixins: "memory-ci" } builders { - name: "Android Cronet Marshmallow 64bit Perf" - dimensions: "os:Ubuntu-14.04" - mixins: "android-ci" - } - builders { name: "chromeos-amd64-generic-rel-goma-canary" dimensions: "os:Ubuntu-14.04" mixins: "fyi-ci" @@ -2636,25 +2593,10 @@ mixins: "fyi-ci" } builders { - name: "Android Cronet Builder (dbg)" - dimensions: "os:Ubuntu-14.04" - mixins: "android-fyi-ci" - } - builders { - name: "Android Cronet ARM64 Builder (dbg)" - dimensions: "os:Ubuntu-14.04" - mixins: "android-ci" - } - builders { name: "Win ASan Release Media" dimensions: "os:Windows-10" mixins: "fuzz-ci" } - builders { - name: "Android Cronet x86 Builder (dbg)" - dimensions: "os:Ubuntu-14.04" - mixins: "android-ci" - } # TODO(crbug.com/888810): Remove once these bots are migrated. builders { name: "mac-dbg"
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg index 2d06be8..112fe1b 100644 --- a/infra/config/luci-milo.cfg +++ b/infra/config/luci-milo.cfg
@@ -1430,120 +1430,60 @@ refs: "refs/heads/master" manifest_name: "REVISION" builders { - name: "buildbucket/luci.chromium.ci/Android Cronet Builder" - category: "cronet" - short_name: "rel" - } - builders { - name: "buildbot/chromium.android/Android Cronet Builder (dbg)" - name: "buildbucket/luci.chromium.ci/Android Cronet Builder (dbg)" - category: "cronet" - short_name: "dbg" - } - builders { - name: "buildbot/chromium.android/Android Cronet Builder Asan" - name: "buildbucket/luci.chromium.ci/Android Cronet Builder Asan" - category: "cronet" - short_name: "asn" - } - builders { - name: "buildbot/chromium.android/Android Cronet KitKat Builder" - name: "buildbucket/luci.chromium.ci/Android Cronet KitKat Builder" - category: "cronet" - short_name: "kit" - } - builders { - name: "buildbot/chromium.android/Android Cronet Lollipop Builder" - name: "buildbucket/luci.chromium.ci/Android Cronet Lollipop Builder" - category: "cronet" - short_name: "lol" - } - builders { - name: "buildbot/chromium.android/Android Cronet Marshmallow 64bit Builder" - name: "buildbucket/luci.chromium.ci/Android Cronet Marshmallow 64bit Builder" - category: "cronet" - short_name: "mar" - } - builders { - name: "buildbot/chromium.android/Android Cronet Marshmallow 64bit Perf" - name: "buildbucket/luci.chromium.ci/Android Cronet Marshmallow 64bit Perf" - category: "cronet" - short_name: "prf" - } - builders { - name: "buildbot/chromium.android/Android Cronet ARM64 Builder" - name: "buildbucket/luci.chromium.ci/Android Cronet ARM64 Builder" - category: "cronet|arm64" - short_name: "rel" - } - builders { - name: "buildbot/chromium.android/Android Cronet ARM64 Builder (dbg)" - name: "buildbucket/luci.chromium.ci/Android Cronet ARM64 Builder (dbg)" - category: "cronet|arm64" - short_name: "dbg" - } - builders { - name: "buildbot/chromium.android/Android Cronet x86 Builder" - name: "buildbucket/luci.chromium.ci/Android Cronet x86 Builder" - category: "cronet|x86" - short_name: "rel" - } - builders { - name: "buildbot/chromium.android/Android Cronet x86 Builder (dbg)" - name: "buildbucket/luci.chromium.ci/Android Cronet x86 Builder (dbg)" - category: "cronet|x86" - short_name: "dbg" - } - builders { name: "buildbucket/luci.chromium.ci/android-cronet-arm-dbg" - category: "cronet|luci|arm" + category: "cronet|arm" short_name: "dbg" } builders { name: "buildbucket/luci.chromium.ci/android-cronet-arm-rel" - category: "cronet|luci|arm" + category: "cronet|arm" short_name: "rel" } builders { name: "buildbucket/luci.chromium.ci/android-cronet-arm64-dbg" - category: "cronet|luci|arm64" + category: "cronet|arm64" short_name: "dbg" } builders { name: "buildbucket/luci.chromium.ci/android-cronet-arm64-rel" - category: "cronet|luci|arm64" + category: "cronet|arm64" short_name: "rel" } builders { - name: "buildbucket/luci.chromium.ci/android-cronet-asan-arm-rel" - category: "cronet|luci|asan" - } - builders { - name: "buildbucket/luci.chromium.ci/android-cronet-kitkat-arm-rel" - category: "cronet|luci|test" - short_name: "k" - } - builders { - name: "buildbucket/luci.chromium.ci/android-cronet-lollipop-arm-rel" - category: "cronet|luci|test" - short_name: "l" - } - builders { - name: "buildbucket/luci.chromium.ci/android-cronet-marshmallow-arm64-rel" - category: "cronet|luci|test" - short_name: "m" - } - builders { name: "buildbucket/luci.chromium.ci/android-cronet-x86-dbg" - category: "cronet|luci|x86" + category: "cronet|x86" short_name: "dbg" } builders { name: "buildbucket/luci.chromium.ci/android-cronet-x86-rel" - category: "cronet|luci|x86" + category: "cronet|x86" short_name: "rel" } builders { + name: "buildbucket/luci.chromium.ci/android-cronet-asan-arm-rel" + category: "cronet|asan" + } + builders { + name: "buildbucket/luci.chromium.ci/android-cronet-kitkat-arm-rel" + category: "cronet|test" + short_name: "k" + } + builders { + name: "buildbucket/luci.chromium.ci/android-cronet-lollipop-arm-rel" + category: "cronet|test" + short_name: "l" + } + builders { + name: "buildbucket/luci.chromium.ci/android-cronet-marshmallow-arm64-rel" + category: "cronet|test" + short_name: "m" + } + builders { + name: "buildbot/chromium.android/Android Cronet Marshmallow 64bit Perf" + category: "cronet|buildbot" + short_name: "prf" + } + builders { name: "buildbucket/luci.chromium.ci/android-jumbo-rel" category: "builder" } @@ -1691,18 +1631,6 @@ category: "Memory" } builders { - name: "buildbot/chromium.android.fyi/Android Cronet Builder (dbg)" - name: "buildbucket/luci.chromium.ci/Android Cronet Builder (dbg)" - category: "Cronet" - short_name: "dbg" - } - builders { - name: "buildbot/chromium.android.fyi/Android Cronet KitKat Builder" - name: "buildbucket/luci.chromium.ci/Android Cronet KitKat Builder" - category: "Cronet" - short_name: "K" - } - builders { name: "buildbucket/luci.chromium.ci/Android Tests (trial)(dbg)" } builders {
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg index 9b7a4f5..55dfb686 100644 --- a/infra/config/luci-scheduler.cfg +++ b/infra/config/luci-scheduler.cfg
@@ -71,7 +71,6 @@ triggers: "Android Builder (dbg) Goma Canary" triggers: "Android Builder (dbg) Goma Latest Client" triggers: "Android CFI" - triggers: "Android Cronet Builder" triggers: "Android FYI 32 Vk Release (Pixel 2)" triggers: "Android FYI 32 Vk Release (Pixel XL)" triggers: "Android FYI 32 dEQP Vk Release (Pixel 2)" @@ -451,16 +450,6 @@ } job { - id: "Android Cronet Builder" - acl_sets: "default" - buildbucket: { - server: "cr-buildbucket.appspot.com" - bucket: "luci.chromium.ci" - builder: "Android Cronet Builder" - } -} - -job { id: "Android FYI 32 dEQP Vk Release (Pixel 2)" acl_sets: "default" buildbucket: { @@ -3340,106 +3329,6 @@ } job { - id: "Android Cronet ARM64 Builder" - acl_sets: "default" - buildbucket: { - server: "cr-buildbucket.appspot.com" - bucket: "luci.chromium.ci" - builder: "Android Cronet ARM64 Builder" - } -} - -job { - id: "Android Cronet ARM64 Builder (dbg)" - acl_sets: "default" - buildbucket: { - server: "cr-buildbucket.appspot.com" - bucket: "luci.chromium.ci" - builder: "Android Cronet ARM64 Builder (dbg)" - } -} - -job { - id: "Android Cronet Builder (dbg)" - acl_sets: "default" - buildbucket: { - server: "cr-buildbucket.appspot.com" - bucket: "luci.chromium.ci" - builder: "Android Cronet Builder (dbg)" - } -} - -job { - id: "Android Cronet Builder Asan" - acl_sets: "default" - buildbucket: { - server: "cr-buildbucket.appspot.com" - bucket: "luci.chromium.ci" - builder: "Android Cronet Builder Asan" - } -} - -job { - id: "Android Cronet KitKat Builder" - acl_sets: "default" - buildbucket: { - server: "cr-buildbucket.appspot.com" - bucket: "luci.chromium.ci" - builder: "Android Cronet KitKat Builder" - } -} - -job { - id: "Android Cronet Lollipop Builder" - acl_sets: "default" - buildbucket: { - server: "cr-buildbucket.appspot.com" - bucket: "luci.chromium.ci" - builder: "Android Cronet Lollipop Builder" - } -} - -job { - id: "Android Cronet Marshmallow 64bit Builder" - acl_sets: "default" - buildbucket: { - server: "cr-buildbucket.appspot.com" - bucket: "luci.chromium.ci" - builder: "Android Cronet Marshmallow 64bit Builder" - } -} - -job { - id: "Android Cronet Marshmallow 64bit Perf" - acl_sets: "default" - buildbucket: { - server: "cr-buildbucket.appspot.com" - bucket: "luci.chromium.ci" - builder: "Android Cronet Marshmallow 64bit Perf" - } -} - -job { - id: "Android Cronet x86 Builder" - acl_sets: "default" - buildbucket: { - server: "cr-buildbucket.appspot.com" - bucket: "luci.chromium.ci" - builder: "Android Cronet x86 Builder" - } -} - -job { - id: "Android Cronet x86 Builder (dbg)" - acl_sets: "default" - buildbucket: { - server: "cr-buildbucket.appspot.com" - bucket: "luci.chromium.ci" - builder: "Android Cronet x86 Builder (dbg)" - } -} - -job { id: "Chromium Mac 10.13" acl_sets: "default" buildbucket: {
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm index bc4a91e..435b2f37f 100644 --- a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm +++ b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
@@ -64,11 +64,6 @@ // Returns the ID of the active tab in |web_state_list|. NSString* GetActiveTabId(WebStateList* web_state_list) { - // TODO(crbug.com/877792) : Real-world crashes have been caused by - // |web_state_list| being nil in this function. Capture histogram to retain - // visibility of issue severity. - UMA_HISTOGRAM_BOOLEAN("IOS.TabGridMediator.GetActiveTabIDNilWebStateList", - !web_state_list); if (!web_state_list) return nil; @@ -214,11 +209,6 @@ - (void)webStateList:(WebStateList*)webStateList didDetachWebState:(web::WebState*)webState atIndex:(int)index { - // TODO(crbug.com/877792) : Real-world crashes have been caused by - // |webStateList| being nil in this callback. Capture histogram to retain - // visibility of issue severity. - UMA_HISTOGRAM_BOOLEAN("IOS.TabGridMediator.DidDetachNilWebStateList", - !webStateList); if (!webStateList) return; TabIdTabHelper* tabHelper = TabIdTabHelper::FromWebState(webState);
diff --git a/ios/web/net/BUILD.gn b/ios/web/net/BUILD.gn index 2ebaf6a6..1d7f111 100644 --- a/ios/web/net/BUILD.gn +++ b/ios/web/net/BUILD.gn
@@ -31,8 +31,6 @@ "crw_ssl_status_updater.mm", "request_group_util.h", "request_group_util.mm", - "request_tracker_factory_impl.h", - "request_tracker_factory_impl.mm", "request_tracker_impl.h", "request_tracker_impl.mm", ]
diff --git a/ios/web/net/request_tracker_factory_impl.h b/ios/web/net/request_tracker_factory_impl.h deleted file mode 100644 index 3ac0e37..0000000 --- a/ios/web/net/request_tracker_factory_impl.h +++ /dev/null
@@ -1,30 +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. - -#ifndef IOS_WEB_NET_REQUEST_TRACKER_FACTORY_IMPL_H_ -#define IOS_WEB_NET_REQUEST_TRACKER_FACTORY_IMPL_H_ - -#include <string> - -#import "ios/net/request_tracker.h" - -namespace web { - -class RequestTrackerFactoryImpl - : public net::RequestTracker::RequestTrackerFactory { - public: - explicit RequestTrackerFactoryImpl(const std::string& application_scheme); - ~RequestTrackerFactoryImpl() override; - - private: - // RequestTracker::RequestTrackerFactory implementation - bool GetRequestTracker(NSURLRequest* request, - base::WeakPtr<net::RequestTracker>* tracker) override; - - NSString* application_scheme_; -}; - -} // namespace web - -#endif // IOS_WEB_NET_REQUEST_TRACKER_FACTORY_IMPL_H_
diff --git a/ios/web/net/request_tracker_factory_impl.mm b/ios/web/net/request_tracker_factory_impl.mm deleted file mode 100644 index 74b36f4..0000000 --- a/ios/web/net/request_tracker_factory_impl.mm +++ /dev/null
@@ -1,52 +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. - -#import "ios/web/net/request_tracker_factory_impl.h" - -#include "base/logging.h" -#include "base/memory/weak_ptr.h" -#include "base/strings/sys_string_conversions.h" -#import "ios/web/net/request_group_util.h" -#import "ios/web/net/request_tracker_impl.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -namespace web { - -RequestTrackerFactoryImpl::RequestTrackerFactoryImpl( - const std::string& application_scheme) { - if (!application_scheme.empty()) { - application_scheme_ = [base::SysUTF8ToNSString(application_scheme) copy]; - DCHECK(application_scheme_); - } -} - -RequestTrackerFactoryImpl::~RequestTrackerFactoryImpl() { -} - -bool RequestTrackerFactoryImpl::GetRequestTracker( - NSURLRequest* request, - base::WeakPtr<net::RequestTracker>* tracker) { - DCHECK(tracker); - DCHECK(!tracker->get()); - NSString* request_group_id = - web::ExtractRequestGroupIDFromRequest(request, application_scheme_); - if (!request_group_id) { - // There was no request_group_id, so the request was from something like a - // data: or file: URL. - return true; - } - RequestTrackerImpl* tracker_impl = - RequestTrackerImpl::GetTrackerForRequestGroupID(request_group_id); - if (tracker_impl) - *tracker = tracker_impl->GetWeakPtr(); - // If there is a request group ID, but no associated tracker, return false. - // This usually happens when the tab has been closed, but can maybe also - // happen in other cases (see http://crbug.com/228397). - return tracker->get() != nullptr; -} - -} // namespace web
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn index e746dc1..229bcf0 100644 --- a/media/base/BUILD.gn +++ b/media/base/BUILD.gn
@@ -285,10 +285,10 @@ "video_frame_pool.h", "video_renderer.cc", "video_renderer.h", - "video_rotation.cc", - "video_rotation.h", "video_thumbnail_decoder.cc", "video_thumbnail_decoder.h", + "video_transformation.cc", + "video_transformation.h", "video_types.cc", "video_types.h", "video_util.cc",
diff --git a/media/base/demuxer_stream.h b/media/base/demuxer_stream.h index 3888102a..fff2f3a 100644 --- a/media/base/demuxer_stream.h +++ b/media/base/demuxer_stream.h
@@ -9,7 +9,7 @@ #include "base/memory/ref_counted.h" #include "base/time/time.h" #include "media/base/media_export.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" namespace media {
diff --git a/media/base/fake_demuxer_stream.cc b/media/base/fake_demuxer_stream.cc index 736f694..f71aff3 100644 --- a/media/base/fake_demuxer_stream.cc +++ b/media/base/fake_demuxer_stream.cc
@@ -158,7 +158,7 @@ const gfx::Rect kVisibleRect(kStartWidth, kStartHeight); video_decoder_config_.Initialize( kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, PIXEL_FORMAT_I420, - VideoColorSpace(), VIDEO_ROTATION_0, next_coded_size_, kVisibleRect, + VideoColorSpace(), kNoTransformation, next_coded_size_, kVisibleRect, next_coded_size_, EmptyExtraData(), is_encrypted_ ? AesCtrEncryptionScheme() : Unencrypted()); next_coded_size_.Enlarge(kWidthDelta, kHeightDelta);
diff --git a/media/base/ipc/media_param_traits_macros.h b/media/base/ipc/media_param_traits_macros.h index 6590f05..e093ed8 100644 --- a/media/base/ipc/media_param_traits_macros.h +++ b/media/base/ipc/media_param_traits_macros.h
@@ -32,7 +32,7 @@ #include "media/base/subsample_entry.h" #include "media/base/video_codecs.h" #include "media/base/video_color_space.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" #include "media/base/video_types.h" #include "media/base/waiting.h" #include "media/base/watch_time_keys.h"
diff --git a/media/base/pipeline.h b/media/base/pipeline.h index 7b96cb2..da27057 100644 --- a/media/base/pipeline.h +++ b/media/base/pipeline.h
@@ -20,7 +20,7 @@ #include "media/base/ranges.h" #include "media/base/text_track.h" #include "media/base/video_decoder_config.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" #include "media/base/waiting.h" #include "ui/gfx/geometry/size.h"
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc index 686b2131..6bf5f84 100644 --- a/media/base/pipeline_impl.cc +++ b/media/base/pipeline_impl.cc
@@ -953,7 +953,7 @@ if (stream->type() == DemuxerStream::VIDEO && !metadata.has_video) { metadata.has_video = true; metadata.natural_size = GetRotatedVideoSize( - stream->video_decoder_config().video_rotation(), + stream->video_decoder_config().video_transformation().rotation, stream->video_decoder_config().natural_size()); metadata.video_decoder_config = stream->video_decoder_config(); }
diff --git a/media/base/pipeline_metadata.h b/media/base/pipeline_metadata.h index 1838222..4f34ff8 100644 --- a/media/base/pipeline_metadata.h +++ b/media/base/pipeline_metadata.h
@@ -8,7 +8,7 @@ #include "base/time/time.h" #include "media/base/audio_decoder_config.h" #include "media/base/video_decoder_config.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" #include "ui/gfx/geometry/size.h" namespace media {
diff --git a/media/base/test_helpers.cc b/media/base/test_helpers.cc index dff8401..80a26ca 100644 --- a/media/base/test_helpers.cc +++ b/media/base/test_helpers.cc
@@ -136,8 +136,9 @@ gfx::Size natural_size = coded_size; return VideoDecoderConfig( - codec, profile, PIXEL_FORMAT_I420, color_space, rotation, coded_size, - visible_rect, natural_size, EmptyExtraData(), + codec, profile, PIXEL_FORMAT_I420, color_space, + VideoTransformation(rotation), coded_size, visible_rect, natural_size, + EmptyExtraData(), is_encrypted ? AesCtrEncryptionScheme() : Unencrypted()); }
diff --git a/media/base/video_decoder_config.cc b/media/base/video_decoder_config.cc index f514abe..23aa265e 100644 --- a/media/base/video_decoder_config.cc +++ b/media/base/video_decoder_config.cc
@@ -62,14 +62,14 @@ : codec_(kUnknownVideoCodec), profile_(VIDEO_CODEC_PROFILE_UNKNOWN), format_(PIXEL_FORMAT_UNKNOWN), - rotation_(VIDEO_ROTATION_0) {} + transformation_(kNoTransformation) {} VideoDecoderConfig::VideoDecoderConfig( VideoCodec codec, VideoCodecProfile profile, VideoPixelFormat format, const VideoColorSpace& color_space, - VideoRotation rotation, + VideoTransformation rotation, const gfx::Size& coded_size, const gfx::Rect& visible_rect, const gfx::Size& natural_size, @@ -105,7 +105,7 @@ VideoCodecProfile profile, VideoPixelFormat format, const VideoColorSpace& color_space, - VideoRotation rotation, + VideoTransformation transformation, const gfx::Size& coded_size, const gfx::Rect& visible_rect, const gfx::Size& natural_size, @@ -114,7 +114,7 @@ codec_ = codec; profile_ = profile; format_ = format; - rotation_ = rotation; + transformation_ = transformation; coded_size_ = coded_size; visible_rect_ = visible_rect; natural_size_ = natural_size; @@ -133,7 +133,7 @@ bool VideoDecoderConfig::Matches(const VideoDecoderConfig& config) const { return ((codec() == config.codec()) && (format() == config.format()) && (profile() == config.profile()) && - (video_rotation() == config.video_rotation()) && + (video_transformation() == config.video_transformation()) && (coded_size() == config.coded_size()) && (visible_rect() == config.visible_rect()) && (natural_size() == config.natural_size()) && @@ -154,7 +154,8 @@ << natural_size().height() << "]" << ", has extra data: " << (extra_data().empty() ? "false" : "true") << ", encryption scheme: " << encryption_scheme() - << ", rotation: " << VideoRotationToString(video_rotation()) + << ", rotation: " << VideoRotationToString(video_transformation().rotation) + << ", flipped: " << video_transformation().mirrored << ", color space: " << color_space_info().ToGfxColorSpace().ToString(); if (hdr_metadata().has_value()) { s << std::setprecision(4) << ", luminance range: "
diff --git a/media/base/video_decoder_config.h b/media/base/video_decoder_config.h index bd65211..ef65fee 100644 --- a/media/base/video_decoder_config.h +++ b/media/base/video_decoder_config.h
@@ -17,7 +17,7 @@ #include "media/base/media_export.h" #include "media/base/video_codecs.h" #include "media/base/video_color_space.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" #include "media/base/video_types.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" @@ -41,7 +41,7 @@ VideoCodecProfile profile, VideoPixelFormat format, const VideoColorSpace& color_space, - VideoRotation rotation, + VideoTransformation transformation, const gfx::Size& coded_size, const gfx::Rect& visible_rect, const gfx::Size& natural_size, @@ -57,7 +57,7 @@ VideoCodecProfile profile, VideoPixelFormat format, const VideoColorSpace& color_space, - VideoRotation rotation, + VideoTransformation transformation, const gfx::Size& coded_size, const gfx::Rect& visible_rect, const gfx::Size& natural_size, @@ -94,7 +94,7 @@ // scaling to natural_size(). // // TODO(sandersd): Which direction is orientation measured in? - VideoRotation video_rotation() const { return rotation_; } + VideoTransformation video_transformation() const { return transformation_; } // Deprecated. TODO(wolenetz): Remove. See https://crbug.com/665539. // Width and height of video frame immediately post-decode. Not all pixels @@ -154,7 +154,7 @@ VideoPixelFormat format_; - VideoRotation rotation_; + VideoTransformation transformation_; // Deprecated. TODO(wolenetz): Remove. See https://crbug.com/665539. gfx::Size coded_size_;
diff --git a/media/base/video_decoder_config_unittest.cc b/media/base/video_decoder_config_unittest.cc index 33d849e..3c7b4e3 100644 --- a/media/base/video_decoder_config_unittest.cc +++ b/media/base/video_decoder_config_unittest.cc
@@ -18,7 +18,7 @@ TEST(VideoDecoderConfigTest, Invalid_UnsupportedPixelFormat) { VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, PIXEL_FORMAT_UNKNOWN, VideoColorSpace(), - VIDEO_ROTATION_0, kCodedSize, kVisibleRect, + kNoTransformation, kCodedSize, kVisibleRect, kNaturalSize, EmptyExtraData(), Unencrypted()); EXPECT_FALSE(config.IsValidConfig()); } @@ -26,7 +26,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorZero) { gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 0, 1); VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat, - VideoColorSpace(), VIDEO_ROTATION_0, kCodedSize, + VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect, natural_size, EmptyExtraData(), Unencrypted()); EXPECT_FALSE(config.IsValidConfig()); @@ -35,7 +35,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorZero) { gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, 0); VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat, - VideoColorSpace(), VIDEO_ROTATION_0, kCodedSize, + VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect, natural_size, EmptyExtraData(), Unencrypted()); EXPECT_FALSE(config.IsValidConfig()); @@ -44,7 +44,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorNegative) { gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), -1, 1); VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat, - VideoColorSpace(), VIDEO_ROTATION_0, kCodedSize, + VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect, natural_size, EmptyExtraData(), Unencrypted()); EXPECT_FALSE(config.IsValidConfig()); @@ -53,7 +53,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorNegative) { gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, -1); VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat, - VideoColorSpace(), VIDEO_ROTATION_0, kCodedSize, + VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect, natural_size, EmptyExtraData(), Unencrypted()); EXPECT_FALSE(config.IsValidConfig()); @@ -64,7 +64,7 @@ int num = ceil(static_cast<double>(limits::kMaxDimension + 1) / width); gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), num, 1); VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat, - VideoColorSpace(), VIDEO_ROTATION_0, kCodedSize, + VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect, natural_size, EmptyExtraData(), Unencrypted()); EXPECT_FALSE(config.IsValidConfig()); @@ -78,7 +78,7 @@ EXPECT_EQ(320, natural_size.width()); EXPECT_EQ(240 * 641, natural_size.height()); VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat, - VideoColorSpace(), VIDEO_ROTATION_0, kCodedSize, + VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect, natural_size, EmptyExtraData(), Unencrypted()); EXPECT_FALSE(config.IsValidConfig());
diff --git a/media/base/video_frame_metadata.h b/media/base/video_frame_metadata.h index 4ba35f0..86207131 100644 --- a/media/base/video_frame_metadata.h +++ b/media/base/video_frame_metadata.h
@@ -15,7 +15,7 @@ #include "base/values.h" #include "build/build_config.h" #include "media/base/media_export.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" namespace gfx { class Rect;
diff --git a/media/base/video_rotation.cc b/media/base/video_rotation.cc deleted file mode 100644 index accfd55..0000000 --- a/media/base/video_rotation.cc +++ /dev/null
@@ -1,26 +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 "media/base/video_rotation.h" - -#include "base/logging.h" - -namespace media { - -std::string VideoRotationToString(VideoRotation rotation) { - switch (rotation) { - case VIDEO_ROTATION_0: - return "0°"; - case VIDEO_ROTATION_90: - return "90°"; - case VIDEO_ROTATION_180: - return "180°"; - case VIDEO_ROTATION_270: - return "270°"; - } - NOTREACHED(); - return ""; -} - -} // namespace media
diff --git a/media/base/video_rotation.h b/media/base/video_rotation.h deleted file mode 100644 index 05690ae..0000000 --- a/media/base/video_rotation.h +++ /dev/null
@@ -1,26 +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. - -#ifndef MEDIA_BASE_VIDEO_ROTATION_H_ -#define MEDIA_BASE_VIDEO_ROTATION_H_ - -#include <string> - -namespace media { - -// Enumeration to represent 90 degree video rotation for MP4 videos -// where it can be rotated by 90 degree intervals. -enum VideoRotation : int { - VIDEO_ROTATION_0 = 0, - VIDEO_ROTATION_90, - VIDEO_ROTATION_180, - VIDEO_ROTATION_270, - VIDEO_ROTATION_MAX = VIDEO_ROTATION_270 -}; - -std::string VideoRotationToString(VideoRotation rotation); - -} // namespace media - -#endif // MEDIA_BASE_VIDEO_ROTATION_H_
diff --git a/media/base/video_thumbnail_decoder_unittest.cc b/media/base/video_thumbnail_decoder_unittest.cc index 1f64291..a898b1c 100644 --- a/media/base/video_thumbnail_decoder_unittest.cc +++ b/media/base/video_thumbnail_decoder_unittest.cc
@@ -35,7 +35,7 @@ mock_video_decoder_ = mock_video_decoder.get(); VideoDecoderConfig valid_config( kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420, VideoColorSpace(), - VIDEO_ROTATION_0, gfx::Size(1, 1), gfx::Rect(1, 1), gfx::Size(1, 1), + kNoTransformation, gfx::Size(1, 1), gfx::Rect(1, 1), gfx::Size(1, 1), EmptyExtraData(), Unencrypted()); thumbnail_decoder_ = std::make_unique<VideoThumbnailDecoder>(
diff --git a/media/base/video_transformation.cc b/media/base/video_transformation.cc new file mode 100644 index 0000000..4ba3658 --- /dev/null +++ b/media/base/video_transformation.cc
@@ -0,0 +1,95 @@ +// 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 "media/base/video_transformation.h" + +#include <math.h> +#include <stddef.h> + +#include "base/logging.h" + +namespace media { +namespace { + +double FixedToFloatingPoint(int32_t i) { + return static_cast<double>(i >> 16); +} + +} // namespace + +std::string VideoRotationToString(VideoRotation rotation) { + switch (rotation) { + case VIDEO_ROTATION_0: + return "0°"; + case VIDEO_ROTATION_90: + return "90°"; + case VIDEO_ROTATION_180: + return "180°"; + case VIDEO_ROTATION_270: + return "270°"; + } + NOTREACHED(); + return ""; +} + +bool operator==(const struct VideoTransformation& first, + const struct VideoTransformation& second) { + return first.rotation == second.rotation && first.mirrored == second.mirrored; +} + +VideoTransformation::VideoTransformation(int32_t matrix[4]) { + // Rotation by angle Θ is represented in the matrix as: + // [ cos(Θ), -sin(Θ)] + // [ sin(Θ), cos(Θ)] + // A vertical flip is represented by the cosine's having opposite signs + // and a horizontal flip is represented by the sine's having the same sign. + + // Check the matrix for validity + if (abs(matrix[0]) != abs(matrix[3]) || abs(matrix[1]) != abs(matrix[2])) { + rotation = VIDEO_ROTATION_0; + mirrored = false; + return; + } + + double angle = acos(FixedToFloatingPoint(matrix[0])) * 180 / base::kPiDouble; + + // Calculate angle offsets for rotation - rotating about the X axis + // can be expressed as a 180 degree rotation and a Y axis rotation + mirrored = false; + if (matrix[0] != matrix[3] && matrix[0] != 0) { + mirrored = !mirrored; + angle += 180; + } + + if (matrix[1] == matrix[3] && matrix[1] != 0) { + mirrored = !mirrored; + } + + // Normalize the angle + while (angle < 0) + angle += 360; + + while (angle >= 360) + angle -= 360; + + // 16 bits of fixed point decimal is enough to give 6 decimals of precision + // to cos(Θ). A delta of ±0.000001 causes acos(cos(Θ)) to differ by a minimum + // of 0.0002, which is why we only need to check that the angle is only + // accurate to within four decimal places. This is preferred to checking for + // a more precise accuracy, as the 'double' type is architecture dependent and + // there may be variance in floating point errors. + if (abs(angle - 0) < 1e-4) { + rotation = VIDEO_ROTATION_0; + } else if (abs(angle - 180) < 1e-4) { + rotation = VIDEO_ROTATION_180; + } else if (abs(angle - 90) < 1e-4) { + bool quadrant = asin(FixedToFloatingPoint(matrix[2])) < 0; + rotation = quadrant ? VIDEO_ROTATION_90 : VIDEO_ROTATION_270; + } else { + rotation = VIDEO_ROTATION_0; + mirrored = false; + } +} + +} // namespace media
diff --git a/media/base/video_transformation.h b/media/base/video_transformation.h new file mode 100644 index 0000000..eff89cd --- /dev/null +++ b/media/base/video_transformation.h
@@ -0,0 +1,61 @@ +// 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 MEDIA_BASE_VIDEO_TRANSFORMATION_H_ +#define MEDIA_BASE_VIDEO_TRANSFORMATION_H_ + +#include <string> + +#include "base/numerics/math_constants.h" +#include "media/base/media_export.h" + +namespace media { + +// Enumeration to represent 90 degree video rotation for MP4 videos +// where it can be rotated by 90 degree intervals. +enum VideoRotation : int { + VIDEO_ROTATION_0 = 0, + VIDEO_ROTATION_90, + VIDEO_ROTATION_180, + VIDEO_ROTATION_270, + VIDEO_ROTATION_MAX = VIDEO_ROTATION_270 +}; + +// Stores frame rotation & mirroring values. These are usually calculated from +// a rotation matrix from a demuxer, and we only support 90 degree rotation +// increments. +struct MEDIA_EXPORT VideoTransformation { + constexpr VideoTransformation(VideoRotation rotation, bool mirrored) + : rotation(rotation), mirrored(mirrored) {} + constexpr VideoTransformation(VideoRotation r) + : VideoTransformation(r, false) {} + constexpr VideoTransformation() + : VideoTransformation(VIDEO_ROTATION_0, false) {} + + // Rotation by angle Θ is represented in the matrix as: + // [ cos(Θ), -sin(Θ)] + // [ sin(Θ), cos(Θ)] + // A vertical flip is represented by the cosine's having opposite signs + // and a horizontal flip is represented by the sine's having the same sign. + VideoTransformation(int32_t matrix[4]); + + // The video rotation value, in 90 degree steps. + VideoRotation rotation; + + // Whether the video should be flipped about its Y axis. + // This transformation takes place _after_ rotation, since they are not + // commutative. + bool mirrored; +}; + +MEDIA_EXPORT bool operator==(const struct VideoTransformation& first, + const struct VideoTransformation& second); + +constexpr VideoTransformation kNoTransformation = VideoTransformation(); + +std::string VideoRotationToString(VideoRotation rotation); + +} // namespace media + +#endif // MEDIA_BASE_VIDEO_TRANSFORMATION_H_
diff --git a/media/blink/video_decode_stats_reporter_unittest.cc b/media/blink/video_decode_stats_reporter_unittest.cc index e9d11ca..bc41f4c 100644 --- a/media/blink/video_decode_stats_reporter_unittest.cc +++ b/media/blink/video_decode_stats_reporter_unittest.cc
@@ -48,7 +48,7 @@ gfx::Size coded_size = natural_size; gfx::Rect visible_rect(coded_size.width(), coded_size.height()); return VideoDecoderConfig(codec, profile, PIXEL_FORMAT_I420, - VideoColorSpace::JPEG(), VIDEO_ROTATION_0, + VideoColorSpace::JPEG(), kNoTransformation, coded_size, visible_rect, natural_size, EmptyExtraData(), Unencrypted()); }
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc index 184b38eb4..a628daf 100644 --- a/media/blink/webmediaplayer_impl.cc +++ b/media/blink/webmediaplayer_impl.cc
@@ -1214,7 +1214,7 @@ } video_renderer_.Paint( video_frame, canvas, gfx::RectF(gfx_rect), flags, - pipeline_metadata_.video_decoder_config.video_rotation(), + pipeline_metadata_.video_decoder_config.video_transformation(), context_provider_.get()); } @@ -1776,9 +1776,10 @@ pipeline_metadata_ = metadata; SetReadyState(WebMediaPlayer::kReadyStateHaveMetadata); - UMA_HISTOGRAM_ENUMERATION("Media.VideoRotation", - metadata.video_decoder_config.video_rotation(), - VIDEO_ROTATION_MAX + 1); + UMA_HISTOGRAM_ENUMERATION( + "Media.VideoRotation", + metadata.video_decoder_config.video_transformation().rotation, + VIDEO_ROTATION_MAX + 1); if (HasAudio()) { RecordEncryptionScheme("Audio", @@ -1808,9 +1809,11 @@ ActivateSurfaceLayerForVideo(); } else { DCHECK(!video_layer_); + // TODO(tmathmeyer) does this need support for reflections as well? video_layer_ = cc::VideoLayer::Create( compositor_.get(), - pipeline_metadata_.video_decoder_config.video_rotation()); + pipeline_metadata_.video_decoder_config.video_transformation() + .rotation); video_layer_->SetContentsOpaque(opaque_); client_->SetCcLayer(video_layer_.get()); } @@ -1844,14 +1847,17 @@ .Run(this, compositor_->GetUpdateSubmissionStateCallback()); bridge_->CreateSurfaceLayer(); + // TODO(tmathmeyer) does this need support for the reflection transformation + // as well? vfc_task_runner_->PostTask( FROM_HERE, - base::BindOnce(&VideoFrameCompositor::EnableSubmission, - base::Unretained(compositor_.get()), - bridge_->GetSurfaceId(), - bridge_->GetLocalSurfaceIdAllocationTime(), - pipeline_metadata_.video_decoder_config.video_rotation(), - IsInPictureInPicture())); + base::BindOnce( + &VideoFrameCompositor::EnableSubmission, + base::Unretained(compositor_.get()), bridge_->GetSurfaceId(), + bridge_->GetLocalSurfaceIdAllocationTime(), + pipeline_metadata_.video_decoder_config.video_transformation() + .rotation, + IsInPictureInPicture())); bridge_->SetContentsOpaque(opaque_); // If the element is already in Picture-in-Picture mode, it means that it @@ -2107,7 +2113,8 @@ // The input |size| is from the decoded video frame, which is the original // natural size and need to be rotated accordingly. gfx::Size rotated_size = GetRotatedVideoSize( - pipeline_metadata_.video_decoder_config.video_rotation(), size); + pipeline_metadata_.video_decoder_config.video_transformation().rotation, + size); RecordVideoNaturalSize(rotated_size); @@ -3114,8 +3121,8 @@ } bool WebMediaPlayerImpl::DoesOverlaySupportMetadata() const { - return pipeline_metadata_.video_decoder_config.video_rotation() == - VIDEO_ROTATION_0; + return pipeline_metadata_.video_decoder_config.video_transformation() == + kNoTransformation; } void WebMediaPlayerImpl::ActivateViewportIntersectionMonitoring(bool activate) {
diff --git a/media/cast/sender/h264_vt_encoder_unittest.cc b/media/cast/sender/h264_vt_encoder_unittest.cc index 6441d90..814cbc4 100644 --- a/media/cast/sender/h264_vt_encoder_unittest.cc +++ b/media/cast/sender/h264_vt_encoder_unittest.cc
@@ -303,7 +303,7 @@ TEST_F(H264VideoToolboxEncoderTest, DISABLED_CheckFramesAreDecodable) { VideoDecoderConfig config( kCodecH264, H264PROFILE_MAIN, frame_->format(), VideoColorSpace(), - VIDEO_ROTATION_0, frame_->coded_size(), frame_->visible_rect(), + kNoTransformation, frame_->coded_size(), frame_->visible_rect(), frame_->natural_size(), EmptyExtraData(), Unencrypted()); scoped_refptr<EndToEndFrameChecker> checker(new EndToEndFrameChecker(config));
diff --git a/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc b/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc index fcc8375..9504b1f 100644 --- a/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc +++ b/media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.cc
@@ -55,8 +55,7 @@ VideoDecoderConfig media_config( ToMediaVideoCodec(config.codec), ToMediaVideoCodecProfile(config.profile), ToMediaVideoFormat(config.format), ToMediaColorSpace(config.color_space), - VideoRotation::VIDEO_ROTATION_0, coded_size, gfx::Rect(coded_size), - coded_size, + kNoTransformation, coded_size, gfx::Rect(coded_size), coded_size, std::vector<uint8_t>(config.extra_data, config.extra_data + config.extra_data_size), Unencrypted());
diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc index 142e992..1d5d0851 100644 --- a/media/ffmpeg/ffmpeg_common.cc +++ b/media/ffmpeg/ffmpeg_common.cc
@@ -621,8 +621,10 @@ extra_data.assign(codec_context->extradata, codec_context->extradata + codec_context->extradata_size); } - config->Initialize(codec, profile, format, color_space, video_rotation, - coded_size, visible_rect, natural_size, extra_data, + // TODO(tmathmeyer) ffmpeg can't provide us with an actual video rotation yet. + config->Initialize(codec, profile, format, color_space, + VideoTransformation(video_rotation), coded_size, + visible_rect, natural_size, extra_data, GetEncryptionScheme(stream)); if (stream->nb_side_data) {
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc index cada939..db87c5f 100644 --- a/media/filters/ffmpeg_demuxer_unittest.cc +++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -1288,7 +1288,7 @@ ASSERT_TRUE(stream); const VideoDecoderConfig& video_config = stream->video_decoder_config(); - ASSERT_EQ(VIDEO_ROTATION_0, video_config.video_rotation()); + ASSERT_EQ(VIDEO_ROTATION_0, video_config.video_transformation().rotation); } TEST_F(FFmpegDemuxerTest, Rotate_Metadata_90) { @@ -1299,7 +1299,7 @@ ASSERT_TRUE(stream); const VideoDecoderConfig& video_config = stream->video_decoder_config(); - ASSERT_EQ(VIDEO_ROTATION_90, video_config.video_rotation()); + ASSERT_EQ(VIDEO_ROTATION_90, video_config.video_transformation().rotation); } TEST_F(FFmpegDemuxerTest, Rotate_Metadata_180) { @@ -1310,7 +1310,7 @@ ASSERT_TRUE(stream); const VideoDecoderConfig& video_config = stream->video_decoder_config(); - ASSERT_EQ(VIDEO_ROTATION_180, video_config.video_rotation()); + ASSERT_EQ(VIDEO_ROTATION_180, video_config.video_transformation().rotation); } TEST_F(FFmpegDemuxerTest, Rotate_Metadata_270) { @@ -1321,7 +1321,7 @@ ASSERT_TRUE(stream); const VideoDecoderConfig& video_config = stream->video_decoder_config(); - ASSERT_EQ(VIDEO_ROTATION_270, video_config.video_rotation()); + ASSERT_EQ(VIDEO_ROTATION_270, video_config.video_transformation().rotation); } TEST_F(FFmpegDemuxerTest, NaturalSizeWithoutPASP) {
diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc index dcc31bf..1a8fdfa 100644 --- a/media/filters/ffmpeg_video_decoder_unittest.cc +++ b/media/filters/ffmpeg_video_decoder_unittest.cc
@@ -227,7 +227,7 @@ TEST_F(FFmpegVideoDecoderTest, Initialize_OpenDecoderFails) { // Specify Theora w/o extra data so that avcodec_open2() fails. VideoDecoderConfig config(kCodecTheora, VIDEO_CODEC_PROFILE_UNKNOWN, - kVideoFormat, VideoColorSpace(), VIDEO_ROTATION_0, + kVideoFormat, VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect, kNaturalSize, EmptyExtraData(), Unencrypted()); InitializeWithConfigWithResult(config, false);
diff --git a/media/filters/source_buffer_state_unittest.cc b/media/filters/source_buffer_state_unittest.cc index 2f3139ea..d0777f3 100644 --- a/media/filters/source_buffer_state_unittest.cc +++ b/media/filters/source_buffer_state_unittest.cc
@@ -37,7 +37,7 @@ gfx::Rect visible_rect(size); return VideoDecoderConfig(codec, VIDEO_CODEC_PROFILE_UNKNOWN, PIXEL_FORMAT_I420, VideoColorSpace::REC709(), - VIDEO_ROTATION_0, size, visible_rect, size, + kNoTransformation, size, visible_rect, size, EmptyExtraData(), Unencrypted()); }
diff --git a/media/filters/vpx_video_decoder_fuzzertest.cc b/media/filters/vpx_video_decoder_fuzzertest.cc index abe5145..d784d58 100644 --- a/media/filters/vpx_video_decoder_fuzzertest.cc +++ b/media/filters/vpx_video_decoder_fuzzertest.cc
@@ -95,8 +95,10 @@ auto coded_size = gfx::Size(1 + (rng() % 127), 1 + (rng() % 127)); auto visible_rect = gfx::Rect(coded_size); auto natural_size = gfx::Size(1 + (rng() % 127), 1 + (rng() % 127)); + uint8_t reflection = rng() % 4; - VideoDecoderConfig config(codec, profile, pixel_format, color_space, rotation, + VideoDecoderConfig config(codec, profile, pixel_format, color_space, + VideoTransformation(rotation, reflection), coded_size, visible_rect, natural_size, EmptyExtraData(), Unencrypted());
diff --git a/media/formats/mp2t/es_adapter_video_unittest.cc b/media/formats/mp2t/es_adapter_video_unittest.cc index 3c078a2..6ae3659 100644 --- a/media/formats/mp2t/es_adapter_video_unittest.cc +++ b/media/formats/mp2t/es_adapter_video_unittest.cc
@@ -33,7 +33,7 @@ gfx::Rect visible_rect(0, 0, 320, 240); gfx::Size natural_size(320, 240); return VideoDecoderConfig(kCodecH264, H264PROFILE_MAIN, PIXEL_FORMAT_I420, - VideoColorSpace(), VIDEO_ROTATION_0, coded_size, + VideoColorSpace(), kNoTransformation, coded_size, visible_rect, natural_size, EmptyExtraData(), Unencrypted()); }
diff --git a/media/formats/mp2t/es_parser_h264.cc b/media/formats/mp2t/es_parser_h264.cc index 41d85555..29e577b7 100644 --- a/media/formats/mp2t/es_parser_h264.cc +++ b/media/formats/mp2t/es_parser_h264.cc
@@ -527,7 +527,7 @@ VideoDecoderConfig video_decoder_config( kCodecH264, profile, PIXEL_FORMAT_I420, VideoColorSpace::REC709(), - VIDEO_ROTATION_0, coded_size.value(), visible_rect.value(), natural_size, + kNoTransformation, coded_size.value(), visible_rect.value(), natural_size, EmptyExtraData(), scheme); if (!video_decoder_config.IsValidConfig()) {
diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc index 623bc53..14848c7 100644 --- a/media/formats/mp4/mp4_stream_parser.cc +++ b/media/formats/mp4/mp4_stream_parser.cc
@@ -245,12 +245,9 @@ return ParseResult::kOk; } -static inline double FixedToFloatingPoint(const int32_t& i) { - return static_cast<double>(i >> 16); -} - -VideoRotation MP4StreamParser::CalculateRotation(const TrackHeader& track, - const MovieHeader& movie) { +VideoTransformation MP4StreamParser::CalculateRotation( + const TrackHeader& track, + const MovieHeader& movie) { static_assert(kDisplayMatrixDimension == 9, "Display matrix must be 3x3"); // 3x3 matrix: [ a b c ] // [ d e f ] @@ -275,39 +272,9 @@ } } - // Rotation by angle Θ is represented in the matrix as: - // [ cos(Θ), -sin(Θ), ...] - // [ sin(Θ), cos(Θ), ...] - // [ ..., ..., 1 ] - // But we only need cos(Θ) for the angle and sin(Θ) for the quadrant. - double angle = acos(FixedToFloatingPoint(rotation_matrix[0])) - * 180 / base::kPiDouble; - - if (angle < 0) - angle += 360; - - if (angle >= 360) - angle -= 360; - - // 16 bits of fixed point decimal is enough to give 6 decimals of precision - // to cos(Θ). A delta of ±0.000001 causes acos(cos(Θ)) to differ by a minimum - // of 0.0002, which is why we only need to check that the angle is only - // accurate to within four decimal places. This is preferred to checking for - // a more precise accuracy, as the 'double' type is architecture dependant and - // ther may variance in floating point errors. - if (abs(angle - 0) < 1e-4) - return VIDEO_ROTATION_0; - - if (abs(angle - 180) < 1e-4) - return VIDEO_ROTATION_180; - - if (abs(angle - 90) < 1e-4) { - bool quadrant = asin(FixedToFloatingPoint(rotation_matrix[3])) < 0; - return quadrant ? VIDEO_ROTATION_90 : VIDEO_ROTATION_270; - } - - // TODO(tmathmeyer): Record this event and the faulty matrix somewhere. - return VIDEO_ROTATION_0; + int32_t rotation_only[4] = {rotation_matrix[0], rotation_matrix[1], + rotation_matrix[3], rotation_matrix[4]}; + return VideoTransformation(rotation_only); } bool MP4StreamParser::ParseMoov(BoxReader* reader) {
diff --git a/media/formats/mp4/mp4_stream_parser.h b/media/formats/mp4/mp4_stream_parser.h index 909bf55..86ef746 100644 --- a/media/formats/mp4/mp4_stream_parser.h +++ b/media/formats/mp4/mp4_stream_parser.h
@@ -52,8 +52,8 @@ bool Parse(const uint8_t* buf, int size) override; // Calculates the rotation value from the track header display matricies. - VideoRotation CalculateRotation(const TrackHeader& track, - const MovieHeader& movie); + VideoTransformation CalculateRotation(const TrackHeader& track, + const MovieHeader& movie); private: enum State {
diff --git a/media/formats/mp4/mp4_stream_parser_unittest.cc b/media/formats/mp4/mp4_stream_parser_unittest.cc index afd3e55..7b9cf6b 100644 --- a/media/formats/mp4/mp4_stream_parser_unittest.cc +++ b/media/formats/mp4/mp4_stream_parser_unittest.cc
@@ -738,7 +738,8 @@ } // <cos(θ), sin(θ), θ expressed as a rotation Enum> -using MatrixRotationTestCaseParam = std::tuple<double, double, VideoRotation>; +using MatrixRotationTestCaseParam = + std::tuple<double, double, VideoTransformation>; class MP4StreamParserRotationMatrixEvaluatorTest : public ::testing::TestWithParam<MatrixRotationTestCaseParam> { @@ -771,17 +772,23 @@ track_header.display_matrix[1] = -(std::get<1>(data) * (1 << 16)); track_header.display_matrix[3] = std::get<1>(data) * (1 << 16); - EXPECT_EQ(parser_->CalculateRotation(track_header, movie_header), - std::get<2>(data)); + VideoTransformation expected = std::get<2>(data); + VideoTransformation actual = + parser_->CalculateRotation(track_header, movie_header); + EXPECT_EQ(actual.rotation, expected.rotation); + EXPECT_EQ(actual.mirrored, expected.mirrored); } MatrixRotationTestCaseParam rotation_test_cases[6] = { - {1, 0, VIDEO_ROTATION_0}, // cos(0) = 1, sin(0) = 0 - {0, -1, VIDEO_ROTATION_90}, // cos(90) = 0, sin(90) =-1 - {-1, 0, VIDEO_ROTATION_180}, // cos(180)=-1, sin(180)= 0 - {0, 1, VIDEO_ROTATION_270}, // cos(270)= 0, sin(270)= 1 - {1, 1, VIDEO_ROTATION_0}, // Error case - {5, 5, VIDEO_ROTATION_0}, // Error case + {1, 0, VideoTransformation(VIDEO_ROTATION_0)}, // cos(0) = 1, sin(0) = 0 + {0, -1, + VideoTransformation(VIDEO_ROTATION_90)}, // cos(90) = 0, sin(90) =-1 + {-1, 0, + VideoTransformation(VIDEO_ROTATION_180)}, // cos(180)=-1, sin(180)= 0 + {0, 1, + VideoTransformation(VIDEO_ROTATION_270)}, // cos(270)= 0, sin(270)= 1 + {1, 1, VideoTransformation(VIDEO_ROTATION_0)}, // Error case + {5, 5, VideoTransformation(VIDEO_ROTATION_0)}, // Error case }; INSTANTIATE_TEST_SUITE_P(CheckMath, MP4StreamParserRotationMatrixEvaluatorTest,
diff --git a/media/formats/webm/webm_video_client.cc b/media/formats/webm/webm_video_client.cc index 231723bb..d2e5b8c8 100644 --- a/media/formats/webm/webm_video_client.cc +++ b/media/formats/webm/webm_video_client.cc
@@ -128,7 +128,7 @@ config->set_hdr_metadata(color_metadata.hdr_metadata); } config->Initialize(video_codec, profile, format, color_space, - VIDEO_ROTATION_0, coded_size, visible_rect, natural_size, + kNoTransformation, coded_size, visible_rect, natural_size, codec_private, encryption_scheme); return config->IsValidConfig(); }
diff --git a/media/formats/webm/webm_video_client_unittest.cc b/media/formats/webm/webm_video_client_unittest.cc index dd9e33da..d9f2dd7 100644 --- a/media/formats/webm/webm_video_client_unittest.cc +++ b/media/formats/webm/webm_video_client_unittest.cc
@@ -58,7 +58,7 @@ VideoDecoderConfig expected_config( kCodecVP9, profile, PIXEL_FORMAT_I420, VideoColorSpace::REC709(), - VIDEO_ROTATION_0, kCodedSize, gfx::Rect(kCodedSize), kCodedSize, + kNoTransformation, kCodedSize, gfx::Rect(kCodedSize), kCodedSize, codec_private, Unencrypted()); EXPECT_TRUE(config.Matches(expected_config))
diff --git a/media/gpu/android/android_video_surface_chooser.h b/media/gpu/android/android_video_surface_chooser.h index c5254c6e..3b88a20 100644 --- a/media/gpu/android/android_video_surface_chooser.h +++ b/media/gpu/android/android_video_surface_chooser.h
@@ -10,7 +10,7 @@ #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "media/base/android/android_overlay.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" #include "media/gpu/media_gpu_export.h" #include "ui/gfx/geometry/rect.h"
diff --git a/media/gpu/android/media_codec_video_decoder.cc b/media/gpu/android/media_codec_video_decoder.cc index a4f2675..a9f588c5 100644 --- a/media/gpu/android/media_codec_video_decoder.cc +++ b/media/gpu/android/media_codec_video_decoder.cc
@@ -279,7 +279,8 @@ } decoder_config_ = config; - surface_chooser_helper_.SetVideoRotation(decoder_config_.video_rotation()); + surface_chooser_helper_.SetVideoRotation( + decoder_config_.video_transformation().rotation); output_cb_ = output_cb; waiting_cb_ = waiting_cb;
diff --git a/media/gpu/android/surface_chooser_helper.h b/media/gpu/android/surface_chooser_helper.h index 5d77f8ad..a019d03 100644 --- a/media/gpu/android/surface_chooser_helper.h +++ b/media/gpu/android/surface_chooser_helper.h
@@ -9,7 +9,7 @@ #include "base/macros.h" #include "base/time/time.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" #include "media/gpu/android/android_video_surface_chooser.h" #include "media/gpu/android/promotion_hint_aggregator.h" #include "media/gpu/media_gpu_export.h"
diff --git a/media/gpu/ipc/service/vda_video_decoder_unittest.cc b/media/gpu/ipc/service/vda_video_decoder_unittest.cc index c7c79fe..4e34ed6 100644 --- a/media/gpu/ipc/service/vda_video_decoder_unittest.cc +++ b/media/gpu/ipc/service/vda_video_decoder_unittest.cc
@@ -23,7 +23,7 @@ #include "media/base/simple_sync_token_client.h" #include "media/base/video_codecs.h" #include "media/base/video_frame.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" #include "media/base/video_types.h" #include "media/gpu/ipc/service/picture_buffer_manager.h" #include "media/gpu/test/fake_command_buffer_helper.h" @@ -142,7 +142,7 @@ EXPECT_CALL(init_cb_, Run(true)); InitializeWithConfig(VideoDecoderConfig( kCodecVP9, VP9PROFILE_PROFILE0, PIXEL_FORMAT_I420, - VideoColorSpace::REC709(), VIDEO_ROTATION_0, gfx::Size(1920, 1088), + VideoColorSpace::REC709(), kNoTransformation, gfx::Size(1920, 1088), gfx::Rect(1920, 1080), gfx::Size(1920, 1080), EmptyExtraData(), Unencrypted())); RunUntilIdle(); @@ -319,7 +319,7 @@ TEST_P(VdaVideoDecoderTest, Initialize_UnsupportedSize) { InitializeWithConfig( VideoDecoderConfig(kCodecVP9, VP9PROFILE_PROFILE0, PIXEL_FORMAT_I420, - VideoColorSpace::REC601(), VIDEO_ROTATION_0, + VideoColorSpace::REC601(), kNoTransformation, gfx::Size(320, 240), gfx::Rect(320, 240), gfx::Size(320, 240), EmptyExtraData(), Unencrypted())); EXPECT_CALL(init_cb_, Run(false)); @@ -329,7 +329,7 @@ TEST_P(VdaVideoDecoderTest, Initialize_UnsupportedCodec) { InitializeWithConfig(VideoDecoderConfig( kCodecH264, H264PROFILE_BASELINE, PIXEL_FORMAT_I420, - VideoColorSpace::REC709(), VIDEO_ROTATION_0, gfx::Size(1920, 1088), + VideoColorSpace::REC709(), kNoTransformation, gfx::Size(1920, 1088), gfx::Rect(1920, 1080), gfx::Size(1920, 1080), EmptyExtraData(), Unencrypted())); EXPECT_CALL(init_cb_, Run(false)); @@ -340,7 +340,7 @@ EXPECT_CALL(*vda_, Initialize(_, vdavd_.get())).WillOnce(Return(false)); InitializeWithConfig(VideoDecoderConfig( kCodecVP9, VP9PROFILE_PROFILE0, PIXEL_FORMAT_I420, - VideoColorSpace::REC709(), VIDEO_ROTATION_0, gfx::Size(1920, 1088), + VideoColorSpace::REC709(), kNoTransformation, gfx::Size(1920, 1088), gfx::Rect(1920, 1080), gfx::Size(1920, 1080), EmptyExtraData(), Unencrypted())); EXPECT_CALL(init_cb_, Run(false)); @@ -423,7 +423,7 @@ .WillOnce(Return(GetParam())); InitializeWithConfig(VideoDecoderConfig( kCodecVP9, VP9PROFILE_PROFILE0, PIXEL_FORMAT_I420, - VideoColorSpace::REC709(), VIDEO_ROTATION_0, gfx::Size(640, 480), + VideoColorSpace::REC709(), kNoTransformation, gfx::Size(640, 480), gfx::Rect(640, 480), gfx::Size(1280, 480), EmptyExtraData(), Unencrypted())); EXPECT_CALL(init_cb_, Run(true));
diff --git a/media/gpu/test/video_player/video_decoder_client.cc b/media/gpu/test/video_player/video_decoder_client.cc index bb58de63..ef02976 100644 --- a/media/gpu/test/video_player/video_decoder_client.cc +++ b/media/gpu/test/video_player/video_decoder_client.cc
@@ -143,7 +143,7 @@ VideoDecoderConfig config( video_->Codec(), video_->Profile(), PIXEL_FORMAT_I420, VideoColorSpace(), - VIDEO_ROTATION_0, video_->Resolution(), gfx::Rect(video_->Resolution()), + kNoTransformation, video_->Resolution(), gfx::Rect(video_->Resolution()), video_->Resolution(), std::vector<uint8_t>(0), EncryptionScheme()); VideoDecoder::InitCB init_cb = BindToCurrentLoop(base::BindRepeating(
diff --git a/media/gpu/video_encode_accelerator_unittest.cc b/media/gpu/video_encode_accelerator_unittest.cc index 221b179e..ab1cf895 100644 --- a/media/gpu/video_encode_accelerator_unittest.cc +++ b/media/gpu/video_encode_accelerator_unittest.cc
@@ -1003,17 +1003,17 @@ VideoDecoderConfig config; if (IsVP8(profile_)) { config.Initialize(kCodecVP8, VP8PROFILE_ANY, pixel_format_, - VideoColorSpace(), VIDEO_ROTATION_0, coded_size, + VideoColorSpace(), kNoTransformation, coded_size, visible_size, natural_size, EmptyExtraData(), Unencrypted()); } else if (IsVP9(profile_)) { config.Initialize(kCodecVP9, VP9PROFILE_PROFILE0, pixel_format_, - VideoColorSpace(), VIDEO_ROTATION_0, coded_size, + VideoColorSpace(), kNoTransformation, coded_size, visible_size, natural_size, EmptyExtraData(), Unencrypted()); } else if (IsH264(profile_)) { config.Initialize(kCodecH264, H264PROFILE_MAIN, pixel_format_, - VideoColorSpace(), VIDEO_ROTATION_0, coded_size, + VideoColorSpace(), kNoTransformation, coded_size, visible_size, natural_size, EmptyExtraData(), Unencrypted()); } else {
diff --git a/media/mojo/interfaces/media_types.mojom b/media/mojo/interfaces/media_types.mojom index 31b47d6a..cfae8698 100644 --- a/media/mojo/interfaces/media_types.mojom +++ b/media/mojo/interfaces/media_types.mojom
@@ -58,10 +58,16 @@ [Native] enum VideoPixelFormat; -// See media/base/video_rotation.h for descriptions. +// See media/base/video_transformation.h for descriptions. [Native] enum VideoRotation; +// See media/base/video_transformation.h for descriptions. +struct VideoTransformation { + VideoRotation rotation; + bool mirrored; +}; + // See media/base/waiting.h for descriptions. [Native] enum WaitingReason; @@ -156,7 +162,7 @@ VideoCodec codec; VideoCodecProfile profile; VideoPixelFormat format; - VideoRotation video_rotation; + VideoTransformation transformation; gfx.mojom.Size coded_size; gfx.mojom.Rect visible_rect; gfx.mojom.Size natural_size;
diff --git a/media/mojo/interfaces/media_types.typemap b/media/mojo/interfaces/media_types.typemap index 65ea50f..4488db03e 100644 --- a/media/mojo/interfaces/media_types.typemap +++ b/media/mojo/interfaces/media_types.typemap
@@ -21,19 +21,27 @@ "//media/base/sample_format.h", "//media/base/subsample_entry.h", "//media/base/video_codecs.h", - "//media/base/video_rotation.h", + "//media/base/video_transformation.h", "//media/base/video_types.h", "//media/base/waiting.h", "//media/base/watch_time_keys.h", ] -traits_headers = [ "//media/base/ipc/media_param_traits_macros.h" ] +traits_headers = [ + "//media/base/ipc/media_param_traits_macros.h", + "//media/mojo/interfaces/video_transformation_mojom_traits.h", +] public_deps = [ "//media", "//media/base/ipc", ] +sources = [ + "//media/mojo/interfaces/video_transformation_mojom_traits.cc", + "//media/mojo/interfaces/video_transformation_mojom_traits.h", +] + type_mappings = [ "media.mojom.AudioCodec=media::AudioCodec", "media.mojom.BufferingState=media::BufferingState", @@ -52,6 +60,7 @@ "media.mojom.VideoCodecProfile=media::VideoCodecProfile", "media.mojom.VideoPixelFormat=media::VideoPixelFormat", "media.mojom.VideoRotation=media::VideoRotation", + "media.mojom.VideoTransformation=media::VideoTransformation", "media.mojom.WaitingReason=media::WaitingReason", "media.mojom.WatchTimeKey=media::WatchTimeKey", "media.mojom.EncryptionPattern=media::EncryptionPattern",
diff --git a/media/mojo/interfaces/video_decoder_config_struct_traits.cc b/media/mojo/interfaces/video_decoder_config_struct_traits.cc index ee076d7a..df3f80f2 100644 --- a/media/mojo/interfaces/video_decoder_config_struct_traits.cc +++ b/media/mojo/interfaces/video_decoder_config_struct_traits.cc
@@ -23,8 +23,8 @@ if (!input.ReadFormat(&format)) return false; - media::VideoRotation rotation; - if (!input.ReadVideoRotation(&rotation)) + media::VideoTransformation transformation; + if (!input.ReadTransformation(&transformation)) return false; gfx::Size coded_size; @@ -55,8 +55,9 @@ if (!input.ReadHdrMetadata(&hdr_metadata)) return false; - output->Initialize(codec, profile, format, color_space, rotation, coded_size, - visible_rect, natural_size, extra_data, encryption_scheme); + output->Initialize(codec, profile, format, color_space, transformation, + coded_size, visible_rect, natural_size, extra_data, + encryption_scheme); if (hdr_metadata) output->set_hdr_metadata(hdr_metadata.value());
diff --git a/media/mojo/interfaces/video_decoder_config_struct_traits.h b/media/mojo/interfaces/video_decoder_config_struct_traits.h index 6161bd8..4f2ab00f 100644 --- a/media/mojo/interfaces/video_decoder_config_struct_traits.h +++ b/media/mojo/interfaces/video_decoder_config_struct_traits.h
@@ -11,6 +11,7 @@ #include "media/mojo/interfaces/hdr_metadata_struct_traits.h" #include "media/mojo/interfaces/media_types.mojom.h" #include "media/mojo/interfaces/video_color_space_struct_traits.h" +#include "media/mojo/interfaces/video_transformation_mojom_traits.h" #include "ui/gfx/geometry/mojo/geometry_struct_traits.h" namespace mojo { @@ -59,9 +60,9 @@ return input.color_space_info(); } - static media::VideoRotation video_rotation( + static media::VideoTransformation transformation( const media::VideoDecoderConfig& input) { - return input.video_rotation(); + return input.video_transformation(); } static const base::Optional<media::HDRMetadata>& hdr_metadata(
diff --git a/media/mojo/interfaces/video_decoder_config_struct_traits_unittest.cc b/media/mojo/interfaces/video_decoder_config_struct_traits_unittest.cc index d028eb5..6619e13 100644 --- a/media/mojo/interfaces/video_decoder_config_struct_traits_unittest.cc +++ b/media/mojo/interfaces/video_decoder_config_struct_traits_unittest.cc
@@ -26,7 +26,7 @@ const std::vector<uint8_t> kExtraDataVector( &kExtraData[0], &kExtraData[0] + base::size(kExtraData)); VideoDecoderConfig input(kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420, - VideoColorSpace(), VIDEO_ROTATION_0, kCodedSize, + VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect, kNaturalSize, kExtraDataVector, Unencrypted()); std::vector<uint8_t> data = @@ -40,7 +40,7 @@ TEST(VideoDecoderConfigStructTraitsTest, ConvertVideoDecoderConfig_EmptyExtraData) { VideoDecoderConfig input(kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420, - VideoColorSpace(), VIDEO_ROTATION_0, kCodedSize, + VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect, kNaturalSize, EmptyExtraData(), Unencrypted()); std::vector<uint8_t> data = @@ -53,7 +53,7 @@ TEST(VideoDecoderConfigStructTraitsTest, ConvertVideoDecoderConfig_Encrypted) { VideoDecoderConfig input(kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420, - VideoColorSpace(), VIDEO_ROTATION_0, kCodedSize, + VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect, kNaturalSize, EmptyExtraData(), AesCtrEncryptionScheme()); std::vector<uint8_t> data = @@ -72,7 +72,7 @@ VideoColorSpace::TransferID::SMPTEST2084, VideoColorSpace::MatrixID::BT2020_CL, gfx::ColorSpace::RangeID::LIMITED), - VIDEO_ROTATION_0, kCodedSize, kVisibleRect, kNaturalSize, + kNoTransformation, kCodedSize, kVisibleRect, kNaturalSize, EmptyExtraData(), Unencrypted()); std::vector<uint8_t> data = media::mojom::VideoDecoderConfig::Serialize(&input); @@ -85,7 +85,7 @@ TEST(VideoDecoderConfigStructTraitsTest, ConvertVideoDecoderConfig_HDRMetadata) { VideoDecoderConfig input(kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420, - VideoColorSpace(), VIDEO_ROTATION_0, kCodedSize, + VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect, kNaturalSize, EmptyExtraData(), Unencrypted()); HDRMetadata hdr_metadata; @@ -127,7 +127,7 @@ // Next try an non-empty invalid config. Natural size must not be zero. const gfx::Size kInvalidNaturalSize(0, 0); input.Initialize(kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_I420, - VideoColorSpace(), VIDEO_ROTATION_0, kCodedSize, + VideoColorSpace(), kNoTransformation, kCodedSize, kVisibleRect, kInvalidNaturalSize, EmptyExtraData(), Unencrypted()); EXPECT_FALSE(input.IsValidConfig());
diff --git a/media/mojo/interfaces/video_transformation_mojom_traits.cc b/media/mojo/interfaces/video_transformation_mojom_traits.cc new file mode 100644 index 0000000..b8b51db --- /dev/null +++ b/media/mojo/interfaces/video_transformation_mojom_traits.cc
@@ -0,0 +1,22 @@ +// 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 "media/mojo/interfaces/video_transformation_mojom_traits.h" + +namespace mojo { + +// static +bool StructTraits<media::mojom::VideoTransformationDataView, + media::VideoTransformation>:: + Read(media::mojom::VideoTransformationDataView input, + media::VideoTransformation* output) { + if (!input.ReadRotation(&output->rotation)) + return false; + + output->mirrored = input.mirrored(); + + return true; +} + +} // namespace mojo
diff --git a/media/mojo/interfaces/video_transformation_mojom_traits.h b/media/mojo/interfaces/video_transformation_mojom_traits.h new file mode 100644 index 0000000..fe9c630 --- /dev/null +++ b/media/mojo/interfaces/video_transformation_mojom_traits.h
@@ -0,0 +1,32 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_MOJO_INTERFACES_VIDEO_TRANSFORMATION_MOJOM_TRAITS_H_ +#define MEDIA_MOJO_INTERFACES_VIDEO_TRANSFORMATION_MOJOM_TRAITS_H_ + +#include "media/base/ipc/media_param_traits.h" +#include "media/base/video_transformation.h" +#include "media/mojo/interfaces/media_types.mojom.h" + +namespace mojo { + +template <> +struct StructTraits<media::mojom::VideoTransformationDataView, + media::VideoTransformation> { + static media::VideoRotation rotation( + const media::VideoTransformation& input) { + return input.rotation; + } + + static bool mirrored(const media::VideoTransformation& input) { + return input.mirrored; + } + + static bool Read(media::mojom::VideoTransformationDataView input, + media::VideoTransformation* output); +}; + +} // namespace mojo + +#endif // MEDIA_MOJO_INTERFACES_VIDEO_TRANSFORMATION_MOJOM_TRAITS_H_
diff --git a/media/remoting/fake_media_resource.cc b/media/remoting/fake_media_resource.cc index 7fb2baff..1611d2e2 100644 --- a/media/remoting/fake_media_resource.cc +++ b/media/remoting/fake_media_resource.cc
@@ -29,7 +29,7 @@ gfx::Rect rect(0, 0, 640, 480); video_config_.Initialize(kCodecH264, H264PROFILE_BASELINE, PIXEL_FORMAT_I420, VideoColorSpace::REC601(), - VIDEO_ROTATION_0, size, rect, size, + kNoTransformation, size, rect, size, std::vector<uint8_t>(), Unencrypted()); } ON_CALL(*this, Read(_))
diff --git a/media/remoting/proto_utils.cc b/media/remoting/proto_utils.cc index ae23fe79..daac2b92 100644 --- a/media/remoting/proto_utils.cc +++ b/media/remoting/proto_utils.cc
@@ -377,7 +377,7 @@ ToMediaVideoCodec(video_message.codec()).value(), ToMediaVideoCodecProfile(video_message.profile()).value(), ToMediaVideoPixelFormat(video_message.format()).value(), color_space, - VIDEO_ROTATION_0, + kNoTransformation, gfx::Size(video_message.coded_size().width(), video_message.coded_size().height()), gfx::Rect(video_message.visible_rect().x(),
diff --git a/media/remoting/stream_provider.cc b/media/remoting/stream_provider.cc index df7482d1..12598b59 100644 --- a/media/remoting/stream_provider.cc +++ b/media/remoting/stream_provider.cc
@@ -10,7 +10,7 @@ #include "base/containers/circular_deque.h" #include "base/logging.h" #include "media/base/decoder_buffer.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" #include "media/remoting/proto_enum_utils.h" #include "media/remoting/proto_utils.h"
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc index 4e7e91d6..d702375 100644 --- a/media/renderers/paint_canvas_video_renderer.cc +++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -578,7 +578,7 @@ cc::PaintCanvas* canvas, const gfx::RectF& dest_rect, cc::PaintFlags& flags, - VideoRotation video_rotation, + VideoTransformation video_transformation, viz::ContextProvider* context_provider) { DCHECK(thread_checker_.CalledOnValidThread()); if (flags.getAlpha() == 0) { @@ -609,10 +609,11 @@ video_flags.setBlendMode(flags.getBlendMode()); video_flags.setFilterQuality(flags.getFilterQuality()); - const bool need_rotation = video_rotation != VIDEO_ROTATION_0; + const bool need_rotation = video_transformation.rotation != VIDEO_ROTATION_0; const bool need_scaling = dest_rect.size() != gfx::SizeF(last_image_.width(), last_image_.height()); const bool need_translation = !dest_rect.origin().IsOrigin(); + // TODO(tmathmeyer): apply horizontal / vertical mirroring if needed. bool need_transform = need_rotation || need_scaling || need_translation; if (need_transform) { canvas->save(); @@ -620,7 +621,7 @@ SkFloatToScalar(dest_rect.x() + (dest_rect.width() * 0.5f)), SkFloatToScalar(dest_rect.y() + (dest_rect.height() * 0.5f))); SkScalar angle = SkFloatToScalar(0.0f); - switch (video_rotation) { + switch (video_transformation.rotation) { case VIDEO_ROTATION_0: break; case VIDEO_ROTATION_90: @@ -636,8 +637,8 @@ canvas->rotate(angle); gfx::SizeF rotated_dest_size = dest_rect.size(); - if (video_rotation == VIDEO_ROTATION_90 || - video_rotation == VIDEO_ROTATION_270) { + if (video_transformation.rotation == VIDEO_ROTATION_90 || + video_transformation.rotation == VIDEO_ROTATION_270) { rotated_dest_size = gfx::SizeF(rotated_dest_size.height(), rotated_dest_size.width()); } @@ -687,7 +688,7 @@ flags.setFilterQuality(kLow_SkFilterQuality); Paint(video_frame, canvas, gfx::RectF(gfx::SizeF(video_frame->visible_rect().size())), flags, - media::VIDEO_ROTATION_0, context_provider); + media::kNoTransformation, context_provider); } namespace {
diff --git a/media/renderers/paint_canvas_video_renderer.h b/media/renderers/paint_canvas_video_renderer.h index a222300..ca5d6fc 100644 --- a/media/renderers/paint_canvas_video_renderer.h +++ b/media/renderers/paint_canvas_video_renderer.h
@@ -20,7 +20,7 @@ #include "media/base/media_export.h" #include "media/base/timestamp_constants.h" #include "media/base/video_frame.h" -#include "media/base/video_rotation.h" +#include "media/base/video_transformation.h" namespace gfx { class RectF; @@ -57,7 +57,7 @@ cc::PaintCanvas* canvas, const gfx::RectF& dest_rect, cc::PaintFlags& flags, - VideoRotation video_rotation, + VideoTransformation video_transformation, viz::ContextProvider* context_provider); // Paints |video_frame| scaled to its visible size on |canvas|.
diff --git a/media/renderers/paint_canvas_video_renderer_unittest.cc b/media/renderers/paint_canvas_video_renderer_unittest.cc index 0d23afa..7a4432b 100644 --- a/media/renderers/paint_canvas_video_renderer_unittest.cc +++ b/media/renderers/paint_canvas_video_renderer_unittest.cc
@@ -86,7 +86,7 @@ const gfx::RectF& dest_rect, Color color, SkBlendMode mode, - VideoRotation video_rotation); + VideoTransformation video_transformation); void Copy(const scoped_refptr<VideoFrame>& video_frame, cc::PaintCanvas* canvas); @@ -226,7 +226,7 @@ void PaintCanvasVideoRendererTest::PaintWithoutFrame(cc::PaintCanvas* canvas) { cc::PaintFlags flags; flags.setFilterQuality(kLow_SkFilterQuality); - renderer_.Paint(nullptr, canvas, kNaturalRect, flags, VIDEO_ROTATION_0, + renderer_.Paint(nullptr, canvas, kNaturalRect, flags, kNoTransformation, nullptr); } @@ -235,7 +235,7 @@ cc::PaintCanvas* canvas, Color color) { PaintRotated(video_frame, canvas, kNaturalRect, color, SkBlendMode::kSrcOver, - VIDEO_ROTATION_0); + kNoTransformation); } void PaintCanvasVideoRendererTest::PaintRotated( @@ -244,7 +244,7 @@ const gfx::RectF& dest_rect, Color color, SkBlendMode mode, - VideoRotation video_rotation) { + VideoTransformation video_transformation) { switch (color) { case kNone: break; @@ -261,7 +261,7 @@ cc::PaintFlags flags; flags.setBlendMode(mode); flags.setFilterQuality(kLow_SkFilterQuality); - renderer_.Paint(video_frame, canvas, dest_rect, flags, video_rotation, + renderer_.Paint(video_frame, canvas, dest_rect, flags, video_transformation, nullptr); } @@ -283,7 +283,7 @@ PaintRotated( VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(), target_canvas(), kNaturalRect, kNone, SkBlendMode::kSrcOver, - VIDEO_ROTATION_0); + kNoTransformation); EXPECT_EQ(static_cast<SkColor>(SK_ColorRED), bitmap()->getColor(0, 0)); } @@ -293,7 +293,7 @@ PaintRotated( VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(), target_canvas(), kNaturalRect, kNone, SkBlendMode::kSrc, - VIDEO_ROTATION_0); + kNoTransformation); EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), bitmap()->getColor(0, 0)); } @@ -385,7 +385,7 @@ SkBitmap bitmap = AllocBitmap(kWidth, kHeight); cc::SkiaPaintCanvas canvas(bitmap); PaintRotated(cropped_frame(), &canvas, kNaturalRect, kNone, - SkBlendMode::kSrcOver, VIDEO_ROTATION_90); + SkBlendMode::kSrcOver, VideoTransformation(VIDEO_ROTATION_90)); // Check the corners. EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(0, 0)); EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth - 1, 0)); @@ -397,7 +397,7 @@ SkBitmap bitmap = AllocBitmap(kWidth, kHeight); cc::SkiaPaintCanvas canvas(bitmap); PaintRotated(cropped_frame(), &canvas, kNaturalRect, kNone, - SkBlendMode::kSrcOver, VIDEO_ROTATION_180); + SkBlendMode::kSrcOver, VideoTransformation(VIDEO_ROTATION_180)); // Check the corners. EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(0, 0)); EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth - 1, 0)); @@ -409,7 +409,7 @@ SkBitmap bitmap = AllocBitmap(kWidth, kHeight); cc::SkiaPaintCanvas canvas(bitmap); PaintRotated(cropped_frame(), &canvas, kNaturalRect, kNone, - SkBlendMode::kSrcOver, VIDEO_ROTATION_270); + SkBlendMode::kSrcOver, VideoTransformation(VIDEO_ROTATION_270)); // Check the corners. EXPECT_EQ(SK_ColorRED, bitmap.getColor(0, 0)); EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth - 1, 0)); @@ -424,7 +424,7 @@ PaintRotated(cropped_frame(), &canvas, gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2), - kNone, SkBlendMode::kSrcOver, VIDEO_ROTATION_0); + kNone, SkBlendMode::kSrcOver, kNoTransformation); // Check the corners of quadrant 2 and 4. EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0)); EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0)); @@ -444,7 +444,8 @@ PaintRotated(cropped_frame(), &canvas, gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2), - kNone, SkBlendMode::kSrcOver, VIDEO_ROTATION_90); + kNone, SkBlendMode::kSrcOver, + VideoTransformation(VIDEO_ROTATION_90)); // Check the corners of quadrant 2 and 4. EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0)); EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0)); @@ -464,7 +465,8 @@ PaintRotated(cropped_frame(), &canvas, gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2), - kNone, SkBlendMode::kSrcOver, VIDEO_ROTATION_180); + kNone, SkBlendMode::kSrcOver, + VideoTransformation(VIDEO_ROTATION_180)); // Check the corners of quadrant 2 and 4. EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0)); EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0)); @@ -484,7 +486,8 @@ PaintRotated(cropped_frame(), &canvas, gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2), - kNone, SkBlendMode::kSrcOver, VIDEO_ROTATION_270); + kNone, SkBlendMode::kSrcOver, + VideoTransformation(VIDEO_ROTATION_270)); // Check the corners of quadrant 2 and 4. EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0)); EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0)); @@ -566,7 +569,7 @@ flags.setFilterQuality(kNone_SkFilterQuality); renderer_.Paint(video_frame, &canvas, gfx::RectF(bitmap.width(), bitmap.height()), flags, - VIDEO_ROTATION_0, nullptr); + kNoTransformation, nullptr); for (int j = 0; j < bitmap.height(); j++) { for (int i = 0; i < bitmap.width(); i++) { const int value = i + j * bitmap.width(); @@ -706,7 +709,7 @@ cc::PaintFlags flags; flags.setFilterQuality(kLow_SkFilterQuality); - renderer_.Paint(video_frame, &canvas, kNaturalRect, flags, VIDEO_ROTATION_90, + renderer_.Paint(video_frame, &canvas, kNaturalRect, flags, kNoTransformation, context_provider.get()); } @@ -731,7 +734,7 @@ gfx::RectF visible_rect(visible_size.width(), visible_size.height()); cc::PaintFlags flags; - renderer_.Paint(video_frame, &canvas, visible_rect, flags, VIDEO_ROTATION_0, + renderer_.Paint(video_frame, &canvas, visible_rect, flags, kNoTransformation, nullptr); EXPECT_EQ(fWidth / 2, renderer_.LastImageDimensionsForTesting().width());
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc index 1d9af55..ce17721f 100644 --- a/media/test/pipeline_integration_test.cc +++ b/media/test/pipeline_integration_test.cc
@@ -2797,24 +2797,26 @@ #if BUILDFLAG(USE_PROPRIETARY_CODECS) TEST_F(PipelineIntegrationTest, Rotated_Metadata_0) { ASSERT_EQ(PIPELINE_OK, Start("bear_rotate_0.mp4")); - ASSERT_EQ(VIDEO_ROTATION_0, metadata_.video_decoder_config.video_rotation()); + ASSERT_EQ(VIDEO_ROTATION_0, + metadata_.video_decoder_config.video_transformation().rotation); } TEST_F(PipelineIntegrationTest, Rotated_Metadata_90) { ASSERT_EQ(PIPELINE_OK, Start("bear_rotate_90.mp4")); - ASSERT_EQ(VIDEO_ROTATION_90, metadata_.video_decoder_config.video_rotation()); + ASSERT_EQ(VIDEO_ROTATION_90, + metadata_.video_decoder_config.video_transformation().rotation); } TEST_F(PipelineIntegrationTest, Rotated_Metadata_180) { ASSERT_EQ(PIPELINE_OK, Start("bear_rotate_180.mp4")); ASSERT_EQ(VIDEO_ROTATION_180, - metadata_.video_decoder_config.video_rotation()); + metadata_.video_decoder_config.video_transformation().rotation); } TEST_F(PipelineIntegrationTest, Rotated_Metadata_270) { ASSERT_EQ(PIPELINE_OK, Start("bear_rotate_270.mp4")); ASSERT_EQ(VIDEO_ROTATION_270, - metadata_.video_decoder_config.video_rotation()); + metadata_.video_decoder_config.video_transformation().rotation); } TEST_F(PipelineIntegrationTest, Spherical) {
diff --git a/mojo/public/cpp/bindings/README.md b/mojo/public/cpp/bindings/README.md index 36bbe87..2d1ccc7 100644 --- a/mojo/public/cpp/bindings/README.md +++ b/mojo/public/cpp/bindings/README.md
@@ -1797,6 +1797,6 @@ ### Additional Documentation -[Calling Mojo From Blink](https://www.chromium.org/developers/design-documents/mojo/calling-mojo-from-blink) -: A brief overview of what it looks like to use Mojom C++ bindings from - within Blink code. +[Calling Mojo From Blink](/docs/mojo_ipc_conversion.md#Blink_Specific-Advice): +A brief overview of what it looks like to use Mojom C++ bindings from +within Blink code.
diff --git a/net/BUILD.gn b/net/BUILD.gn index 87eecdd8..584c613 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -1464,6 +1464,10 @@ "third_party/quiche/src/quic/core/http/quic_header_list.h", "third_party/quiche/src/quic/core/http/quic_headers_stream.cc", "third_party/quiche/src/quic/core/http/quic_headers_stream.h", + "third_party/quiche/src/quic/core/http/quic_receive_control_stream.cc", + "third_party/quiche/src/quic/core/http/quic_receive_control_stream.h", + "third_party/quiche/src/quic/core/http/quic_send_control_stream.cc", + "third_party/quiche/src/quic/core/http/quic_send_control_stream.h", "third_party/quiche/src/quic/core/http/quic_server_session_base.cc", "third_party/quiche/src/quic/core/http/quic_server_session_base.h", "third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.cc", @@ -1612,6 +1616,7 @@ "third_party/quiche/src/quic/core/quic_versions.h", "third_party/quiche/src/quic/core/quic_write_blocked_list.cc", "third_party/quiche/src/quic/core/quic_write_blocked_list.h", + "third_party/quiche/src/quic/core/session_notifier_interface.h", "third_party/quiche/src/quic/core/tls_client_handshaker.cc", "third_party/quiche/src/quic/core/tls_client_handshaker.h", "third_party/quiche/src/quic/core/tls_handshaker.cc", @@ -1665,7 +1670,10 @@ "third_party/quiche/src/quic/platform/api/quic_stack_trace.h", "third_party/quiche/src/quic/platform/api/quic_str_cat.h", "third_party/quiche/src/quic/platform/api/quic_string_piece.h", + "third_party/quiche/src/quic/platform/api/quic_string_utils.h", "third_party/quiche/src/quic/platform/api/quic_text_utils.h", + "third_party/quiche/src/quic/platform/api/quic_thread.h", + "third_party/quiche/src/quic/platform/api/quic_uint128.h", "third_party/quiche/src/spdy/core/hpack/hpack_constants.cc", "third_party/quiche/src/spdy/core/hpack/hpack_constants.h", "third_party/quiche/src/spdy/core/hpack/hpack_decoder_adapter.cc", @@ -3121,6 +3129,7 @@ "third_party/quiche/src/quic/core/quic_packet_reader.cc", "third_party/quiche/src/quic/core/quic_packet_reader.h", "third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h", + "third_party/quiche/src/quic/platform/api/quic_epoll.h", "third_party/quiche/src/quic/platform/api/quic_stream_buffer_allocator.h", "third_party/quiche/src/quic/platform/api/quic_system_event_loop.h", "third_party/quiche/src/quic/tools/quic_client.cc", @@ -3464,6 +3473,7 @@ "third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h", "third_party/quiche/src/quic/tools/quic_simple_dispatcher.cc", "third_party/quiche/src/quic/tools/quic_simple_dispatcher.h", + "third_party/quiche/src/quic/tools/quic_simple_server_backend.h", "third_party/quiche/src/quic/tools/quic_simple_server_session.cc", "third_party/quiche/src/quic/tools/quic_simple_server_session.h", "third_party/quiche/src/quic/tools/quic_simple_server_stream.cc", @@ -3537,6 +3547,19 @@ "//third_party/protobuf:protobuf_lite", ] } + executable("quic_crypto_message_printer") { + sources = [ + "third_party/quiche/src/quic/core/crypto/crypto_message_printer_bin.cc", + ] + deps = [ + ":net", + ":simple_quic_tools", + "//base", + "//build/win:default_exe_manifest", + "//third_party/boringssl", + "//third_party/protobuf:protobuf_lite", + ] + } executable("quic_reject_reason_decoder") { sources = [ "third_party/quiche/src/quic/tools/quic_reject_reason_decoder_bin.cc", @@ -5265,6 +5288,7 @@ "third_party/quiche/src/quic/core/congestion_control/prr_sender_test.cc", "third_party/quiche/src/quic/core/congestion_control/rtt_stats_test.cc", "third_party/quiche/src/quic/core/congestion_control/send_algorithm_test.cc", + "third_party/quiche/src/quic/core/congestion_control/tcp_cubic_sender_bytes_test.cc", "third_party/quiche/src/quic/core/congestion_control/uber_loss_algorithm_test.cc", "third_party/quiche/src/quic/core/congestion_control/windowed_filter_test.cc", "third_party/quiche/src/quic/core/crypto/aes_128_gcm_12_decrypter_test.cc", @@ -5302,6 +5326,8 @@ "third_party/quiche/src/quic/core/http/quic_client_push_promise_index_test.cc", "third_party/quiche/src/quic/core/http/quic_header_list_test.cc", "third_party/quiche/src/quic/core/http/quic_headers_stream_test.cc", + "third_party/quiche/src/quic/core/http/quic_receive_control_stream_test.cc", + "third_party/quiche/src/quic/core/http/quic_send_control_stream_test.cc", "third_party/quiche/src/quic/core/http/quic_server_session_base_test.cc", "third_party/quiche/src/quic/core/http/quic_spdy_session_test.cc", "third_party/quiche/src/quic/core/http/quic_spdy_stream_body_buffer_test.cc", @@ -5371,6 +5397,7 @@ "third_party/quiche/src/quic/core/quic_write_blocked_list_test.cc", "third_party/quiche/src/quic/core/tls_handshaker_test.cc", "third_party/quiche/src/quic/core/uber_quic_stream_id_manager_test.cc", + "third_party/quiche/src/quic/core/uber_received_packet_manager_test.cc", "third_party/quiche/src/quic/platform/api/quic_containers_test.cc", "third_party/quiche/src/quic/platform/api/quic_endian_test.cc", "third_party/quiche/src/quic/platform/api/quic_hostname_utils_test.cc", @@ -5380,6 +5407,7 @@ "third_party/quiche/src/quic/platform/api/quic_mem_slice_test.cc", "third_party/quiche/src/quic/platform/api/quic_reference_counted_test.cc", "third_party/quiche/src/quic/platform/api/quic_str_cat_test.cc", + "third_party/quiche/src/quic/platform/api/quic_string_utils_test.cc", "third_party/quiche/src/quic/platform/api/quic_text_utils_test.cc", "third_party/quiche/src/quic/test_tools/crypto_test_utils_test.cc", "third_party/quiche/src/quic/test_tools/mock_quic_time_wait_list_manager.cc", @@ -5388,6 +5416,7 @@ "third_party/quiche/src/quic/test_tools/simple_session_notifier_test.cc", "third_party/quiche/src/quic/test_tools/simulator/quic_endpoint_test.cc", "third_party/quiche/src/quic/test_tools/simulator/simulator_test.cc", + "third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper_test.cc", "third_party/quiche/src/spdy/core/array_output_buffer.cc", "third_party/quiche/src/spdy/core/array_output_buffer.h", "third_party/quiche/src/spdy/core/array_output_buffer_test.cc", @@ -5484,6 +5513,19 @@ "third_party/quiche/src/quic/quartc/simulated_packet_transport.cc", "third_party/quiche/src/quic/quartc/simulated_packet_transport.h", "third_party/quiche/src/quic/quartc/simulated_packet_transport_test.cc", + "third_party/quiche/src/quic/quartc/test/bidi_test_runner.cc", + "third_party/quiche/src/quic/quartc/test/bidi_test_runner.h", + "third_party/quiche/src/quic/quartc/test/quartc_bidi_test.cc", + "third_party/quiche/src/quic/quartc/test/quartc_data_source.cc", + "third_party/quiche/src/quic/quartc/test/quartc_data_source.h", + "third_party/quiche/src/quic/quartc/test/quartc_data_source_test.cc", + "third_party/quiche/src/quic/quartc/test/quartc_peer.cc", + "third_party/quiche/src/quic/quartc/test/quartc_peer.h", + "third_party/quiche/src/quic/quartc/test/quartc_peer_test.cc", + "third_party/quiche/src/quic/quartc/test/random_delay_link.cc", + "third_party/quiche/src/quic/quartc/test/random_delay_link.h", + "third_party/quiche/src/quic/quartc/test/random_packet_filter.cc", + "third_party/quiche/src/quic/quartc/test/random_packet_filter.h", ] } @@ -5611,7 +5653,6 @@ "third_party/quiche/src/quic/tools/quic_url_test.cc", "tools/quic/quic_http_proxy_backend_stream_test.cc", "tools/quic/quic_http_proxy_backend_test.cc", - "tools/quic/quic_simple_server_session_helper_test.cc", "tools/quic/quic_simple_server_test.cc", ] deps += [ @@ -5959,6 +6000,7 @@ "socket/fuzzed_socket.h", "socket/fuzzed_socket_factory.cc", "socket/fuzzed_socket_factory.h", + "third_party/quiche/src/quic/platform/api/quic_fuzzed_data_provider.h", ] public_deps = [ "//base/test:test_support", @@ -6564,6 +6606,20 @@ ] } +fuzzer_test("net_quic_framer_fuzzer") { + sources = [ + "third_party/quiche/src/quic/test_tools/fuzzing/quic_framer_fuzzer.cc", + ] + + deps = [ + ":net_fuzzer_test_support", + ":quic_test_tools", + ":test_support", + "//net", + "//net/data/ssl/certificates:generate_fuzzer_cert_includes", + ] +} + fuzzer_test("net_uri_template_fuzzer") { sources = [ "third_party/uri_template/uri_template_fuzzer.cc",
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h index 6d3b6ef..9614b3d8 100644 --- a/net/base/net_error_list.h +++ b/net/base/net_error_list.h
@@ -282,8 +282,7 @@ // which exceeds size threshold). NET_ERROR(MSG_TOO_BIG, -142) -// A SPDY session already exists, and should be used instead of this connection. -NET_ERROR(SPDY_SESSION_ALREADY_EXISTS, -143) +// Error -143 was removed (SPDY_SESSION_ALREADY_EXISTS) // Error -144 was removed (LIMIT_VIOLATION).
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index df46a606..b1c0dc5 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc
@@ -35,6 +35,7 @@ #include "base/test/simple_test_tick_clock.h" #include "base/test/test_file_util.h" #include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" #include "net/base/auth.h" #include "net/base/chunked_upload_data_stream.h" #include "net/base/completion_once_callback.h" @@ -5906,9 +5907,9 @@ } // Test the case where a proxied H2 session doesn't exist when an auth challenge -// is observed, but does exist by the time auth credentials are provided. -// Proxy-Connection: Close is used so that there's a second DNS lookup, which is -// what causes the existing H2 session to be noticed and reused. +// is observed, but does exist by the time auth credentials are provided. In +// this case, auth and SSL are fully negotated on the second request, but then +// the socket is discarded to use the shared session. TEST_F(HttpNetworkTransactionTest, ProxiedH2SessionAppearsDuringAuth) { ProxyConfig proxy_config; proxy_config.set_auto_detect(true); @@ -5945,6 +5946,10 @@ "CONNECT www.example.org:443 HTTP/1.1\r\n" "Host: www.example.org:443\r\n" "Proxy-Connection: keep-alive\r\n\r\n"), + MockWrite(ASYNC, 2, + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org:443\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), }; MockRead auth_challenge_reads[] = { @@ -5975,6 +5980,18 @@ MockRead(SYNCHRONOUS, ERR_IO_PENDING, 8), }; + MockWrite auth_response_writes_discarded_socket[] = { + MockWrite(ASYNC, 0, + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org:443\r\n" + "Proxy-Connection: keep-alive\r\n" + "Proxy-Authorization: Basic Zm9vOmJhcg==\r\n\r\n"), + }; + + MockRead auth_response_reads_discarded_socket[] = { + MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n\r\n"), + }; + SequencedSocketData auth_challenge1(auth_challenge_reads, auth_challenge_writes); session_deps_.socket_factory->AddSocketDataProvider(&auth_challenge1); @@ -5986,10 +6003,20 @@ SequencedSocketData spdy_data(spdy_reads, spdy_writes); session_deps_.socket_factory->AddSocketDataProvider(&spdy_data); + SequencedSocketData auth_response_discarded_socket( + auth_response_reads_discarded_socket, + auth_response_writes_discarded_socket); + session_deps_.socket_factory->AddSocketDataProvider( + &auth_response_discarded_socket); + SSLSocketDataProvider ssl(ASYNC, OK); ssl.next_proto = kProtoHTTP2; session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); + SSLSocketDataProvider ssl2(ASYNC, OK); + ssl2.next_proto = kProtoHTTP2; + session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl2); + TestCompletionCallback callback; std::string response_data;
diff --git a/net/http/http_proxy_connect_job.cc b/net/http/http_proxy_connect_job.cc index ed1b7a2..83f167d2 100644 --- a/net/http/http_proxy_connect_job.cc +++ b/net/http/http_proxy_connect_job.cc
@@ -448,12 +448,6 @@ if (result != OK) { UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Insecure.Error", base::TimeTicks::Now() - connect_start_time_); - // This is a special error code meaning to reuse an existing SPDY session - // rather than use a fresh socket. Overriding it with a proxy error message - // would cause the request to fail, instead of switching to using the SPDY - // session. - if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) - return result; return ERR_PROXY_CONNECTION_FAILED; } @@ -503,13 +497,6 @@ // same way as server cert errors. return ERR_PROXY_CERTIFICATE_INVALID; } - // A SPDY session to the proxy completed prior to resolving the proxy - // hostname. Surface this error, and allow the delegate to retry. - // See crbug.com/334413. - if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) { - DCHECK(!nested_connect_job_->socket()); - return ERR_SPDY_SESSION_ALREADY_EXISTS; - } if (result < 0) { UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.Error", base::TimeTicks::Now() - connect_start_time_);
diff --git a/net/http/http_stream_factory_job.cc b/net/http/http_stream_factory_job.cc index 152f818..28af63b 100644 --- a/net/http/http_stream_factory_job.cc +++ b/net/http/http_stream_factory_job.cc
@@ -43,6 +43,7 @@ #include "net/quic/quic_http_stream.h" #include "net/socket/client_socket_handle.h" #include "net/socket/client_socket_pool_manager.h" +#include "net/socket/connect_job.h" #include "net/socket/ssl_client_socket.h" #include "net/socket/stream_socket.h" #include "net/spdy/bidirectional_stream_spdy_impl.h" @@ -486,23 +487,6 @@ // |this| may be deleted after this call. } -// static -int HttpStreamFactory::Job::OnHostResolution( - SpdySessionPool* spdy_session_pool, - const SpdySessionKey& spdy_session_key, - bool enable_ip_based_pooling, - bool is_websocket, - const AddressList& addresses, - const NetLogWithSource& net_log) { - // It is OK to dereference spdy_session_pool, because the - // ClientSocketPoolManager will be destroyed in the same callback that - // destroys the SpdySessionPool. - return spdy_session_pool->FindAvailableSession( - spdy_session_key, enable_ip_based_pooling, is_websocket, net_log) - ? ERR_SPDY_SESSION_ALREADY_EXISTS - : OK; -} - void HttpStreamFactory::Job::OnIOComplete(int result) { TRACE_EVENT0(NetTracingCategory(), "HttpStreamFactory::Job::OnIOComplete"); RunLoop(result); @@ -693,8 +677,7 @@ int HttpStreamFactory::Job::DoInitConnection() { net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB_INIT_CONNECTION); int result = DoInitConnectionImpl(); - if (result != ERR_SPDY_SESSION_ALREADY_EXISTS && - !expect_on_quic_host_resolution_) { + if (!expect_on_quic_host_resolution_) { delegate_->OnConnectionInitialized(this, result); } return result; @@ -888,15 +871,6 @@ request_info_.privacy_mode, net_log_, num_streams_); } - // If we can't use a HTTP/2 session, don't bother checking for one after - // the hostname is resolved. - OnHostResolutionCallback resolution_callback = - CanUseExistingSpdySession() - ? base::Bind(&Job::OnHostResolution, session_->spdy_session_pool(), - spdy_session_key_, enable_ip_based_pooling_, - try_websocket_over_http2_) - : OnHostResolutionCallback(); - ClientSocketPool::ProxyAuthCallback proxy_auth_callback = base::BindRepeating(&HttpStreamFactory::Job::OnNeedsProxyAuthCallback, base::Unretained(this)); @@ -907,16 +881,15 @@ return InitSocketHandleForWebSocketRequest( GetSocketGroup(), destination_, request_info_.load_flags, priority_, session_, proxy_info_, websocket_server_ssl_config, proxy_ssl_config_, - request_info_.privacy_mode, net_log_, connection_.get(), - resolution_callback, io_callback_, proxy_auth_callback); + request_info_.privacy_mode, net_log_, connection_.get(), io_callback_, + proxy_auth_callback); } return InitSocketHandleForHttpRequest( GetSocketGroup(), destination_, request_info_.load_flags, priority_, session_, proxy_info_, server_ssl_config_, proxy_ssl_config_, request_info_.privacy_mode, request_info_.socket_tag, net_log_, - connection_.get(), resolution_callback, io_callback_, - proxy_auth_callback); + connection_.get(), io_callback_, proxy_auth_callback); } void HttpStreamFactory::Job::OnQuicHostResolution(int result) { @@ -945,23 +918,6 @@ return OK; } - if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) { - // We found a HTTP/2 connection after resolving the host. This is - // probably an IP pooled connection. - existing_spdy_session_ = - session_->spdy_session_pool()->FindAvailableSession( - spdy_session_key_, enable_ip_based_pooling_, - try_websocket_over_http2_, net_log_); - if (existing_spdy_session_) { - using_spdy_ = true; - next_state_ = STATE_CREATE_STREAM; - } else { - // It is possible that the HTTP/2 session no longer exists. - ReturnToStateInitConnection(true /* close connection */); - } - return OK; - } - // |result| may be the result of any of the stacked pools. The following // logic is used when determining how to interpret an error. // If |result| < 0: @@ -1225,6 +1181,12 @@ base::WeakPtr<SpdySession> spdy_session) { DCHECK(spdy_session); + // No need for the connection any more, since |spdy_session| can be used + // instead, and there's no benefit from keeping the old ConnectJob in the + // socket pool. + if (connection_) + connection_->ResetAndCloseSocket(); + // Once a connection is initialized, or if there's any out-of-band callback, // like proxy auth challenge, the SpdySessionRequest is cancelled. DCHECK(next_state_ == STATE_INIT_CONNECTION ||
diff --git a/net/http/http_stream_factory_job.h b/net/http/http_stream_factory_job.h index e7fb8e63..3946a9a 100644 --- a/net/http/http_stream_factory_job.h +++ b/net/http/http_stream_factory_job.h
@@ -359,17 +359,6 @@ void MaybeCopyConnectionAttemptsFromSocketOrHandle(); - // Invoked by the transport socket pool after host resolution is complete - // to allow the connection to be aborted, if a matching SPDY session can - // be found. Will return ERR_SPDY_SESSION_ALREADY_EXISTS if such a - // session is found, and OK otherwise. - static int OnHostResolution(SpdySessionPool* spdy_session_pool, - const SpdySessionKey& spdy_session_key, - bool enable_ip_based_pooling, - bool is_websocket, - const AddressList& addresses, - const NetLogWithSource& net_log); - // Returns true if the request should be throttled to allow for only one // connection attempt to be made to an H2 server at a time. bool ShouldThrottleConnectForSpdy() const;
diff --git a/net/http/http_stream_factory_unittest.cc b/net/http/http_stream_factory_unittest.cc index 4ad52f4..cc67340 100644 --- a/net/http/http_stream_factory_unittest.cc +++ b/net/http/http_stream_factory_unittest.cc
@@ -2003,7 +2003,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( std::make_unique<SSLConfig>() /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); ClientSocketPool::GroupId group_id(host_port_pair, ClientSocketPool::SocketType::kSsl, PrivacyMode::PRIVACY_MODE_DISABLED);
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json index 5713762..88b51910 100644 --- a/net/http/transport_security_state_static.json +++ b/net/http/transport_security_state_static.json
@@ -42454,7 +42454,6 @@ { "name": "inscomers.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "intae.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "intpforum.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "inventoryexpress.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ip-tanz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "ipv6.jetzt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "isakssons.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, @@ -59106,7 +59105,6 @@ { "name": "downtownautospecialists.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dpecuador.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "draliabadi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, - { "name": "dreamstream.network", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dreamstream.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dreamstream.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true }, { "name": "dreamstream.video", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
diff --git a/net/socket/client_socket_pool.cc b/net/socket/client_socket_pool.cc index df6de12..5c9f818 100644 --- a/net/socket/client_socket_pool.cc +++ b/net/socket/client_socket_pool.cc
@@ -11,11 +11,12 @@ #include "net/http/http_proxy_connect_job.h" #include "net/log/net_log_event_type.h" #include "net/log/net_log_with_source.h" +#include "net/socket/connect_job.h" #include "net/socket/socks_connect_job.h" #include "net/socket/ssl_connect_job.h" #include "net/socket/stream_socket.h" -#include "net/socket/transport_connect_job.h" -#include "net/socket/websocket_transport_connect_job.h" +#include "net/spdy/spdy_session.h" +#include "net/spdy/spdy_session_pool.h" namespace net { @@ -24,23 +25,41 @@ // The maximum duration, in seconds, to keep used idle persistent sockets alive. int64_t g_used_idle_socket_timeout_s = 300; // 5 minutes +// Invoked by the transport socket pool after host resolution is complete +// to allow the connection to be aborted, if a matching SPDY session can +// be found. Returns OnHostResolutionCallbackResult::kMayBeDeletedAsync if such +// a session is found, as it will post a task that may delete the calling +// ConnectJob. Also returns kMayBeDeletedAsync if there may already be such +// a task posted. +OnHostResolutionCallbackResult OnHostResolution( + SpdySessionPool* spdy_session_pool, + const SpdySessionKey& spdy_session_key, + bool is_for_websockets, + const HostPortPair& host_port_pair, + const AddressList& addresses) { + DCHECK(host_port_pair == spdy_session_key.host_port_pair()); + + // It is OK to dereference spdy_session_pool, because the + // ClientSocketPoolManager will be destroyed in the same callback that + // destroys the SpdySessionPool. + return spdy_session_pool->OnHostResolutionComplete( + spdy_session_key, is_for_websockets, addresses); +} + } // namespace ClientSocketPool::SocketParams::SocketParams( std::unique_ptr<SSLConfig> ssl_config_for_origin, - std::unique_ptr<SSLConfig> ssl_config_for_proxy, - const OnHostResolutionCallback& resolution_callback) + std::unique_ptr<SSLConfig> ssl_config_for_proxy) : ssl_config_for_origin_(std::move(ssl_config_for_origin)), - ssl_config_for_proxy_(std::move(ssl_config_for_proxy)), - resolution_callback_(resolution_callback) {} + ssl_config_for_proxy_(std::move(ssl_config_for_proxy)) {} ClientSocketPool::SocketParams::~SocketParams() = default; scoped_refptr<ClientSocketPool::SocketParams> ClientSocketPool::SocketParams::CreateForHttpForTesting() { return base::MakeRefCounted<SocketParams>(nullptr /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, - OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); } ClientSocketPool::GroupId::GroupId() @@ -128,12 +147,32 @@ SocketTag socket_tag, ConnectJob::Delegate* delegate) { bool using_ssl = group_id.socket_type() == ClientSocketPool::SocketType::kSsl; + + // If applicable, set up a callback to handle checking for H2 IP pooling + // opportunities. + OnHostResolutionCallback resolution_callback; + if (using_ssl && proxy_server.is_direct()) { + resolution_callback = base::BindRepeating( + &OnHostResolution, common_connect_job_params->spdy_session_pool, + SpdySessionKey(group_id.destination(), proxy_server, + group_id.privacy_mode(), + SpdySessionKey::IsProxySession::kFalse, socket_tag), + is_for_websockets); + } else if (proxy_server.is_https()) { + resolution_callback = base::BindRepeating( + &OnHostResolution, common_connect_job_params->spdy_session_pool, + SpdySessionKey(proxy_server.host_port_pair(), ProxyServer::Direct(), + group_id.privacy_mode(), + SpdySessionKey::IsProxySession::kTrue, socket_tag), + is_for_websockets); + } + return ConnectJob::CreateConnectJob( using_ssl, group_id.destination(), proxy_server, proxy_annotation_tag, socket_params->ssl_config_for_origin(), socket_params->ssl_config_for_proxy(), is_for_websockets, - group_id.privacy_mode(), socket_params->resolution_callback(), - request_priority, socket_tag, common_connect_job_params, delegate); + group_id.privacy_mode(), resolution_callback, request_priority, + socket_tag, common_connect_job_params, delegate); } } // namespace net
diff --git a/net/socket/client_socket_pool.h b/net/socket/client_socket_pool.h index fdf9534..4db565e 100644 --- a/net/socket/client_socket_pool.h +++ b/net/socket/client_socket_pool.h
@@ -153,18 +153,6 @@ PrivacyMode privacy_mode_; }; - // Callback to create a ConnectJob using the provided arguments. The lower - // level parameters used to construct the ConnectJob (like hostname, type of - // socket, proxy, etc) are all already bound to the callback. If - // |websocket_endpoint_lock_manager| is non-null, a ConnectJob for use by - // WebSockets should be created. - using CreateConnectJobCallback = - base::RepeatingCallback<std::unique_ptr<ConnectJob>( - RequestPriority priority, - const SocketTag& socket_tag, - const CommonConnectJobParams* common_connect_job_params, - ConnectJob::Delegate* delegate)>; - // Parameters that, in combination with GroupId, proxy, websocket information, // and global state, are sufficient to create a ConnectJob. // @@ -179,8 +167,7 @@ // For non-SSL requests / non-HTTPS proxies, the corresponding SSLConfig // argument may be nullptr. SocketParams(std::unique_ptr<SSLConfig> ssl_config_for_origin, - std::unique_ptr<SSLConfig> ssl_config_for_proxy, - const OnHostResolutionCallback& resolution_callback); + std::unique_ptr<SSLConfig> ssl_config_for_proxy); // Creates a SocketParams object with none of the fields populated. This // works for the HTTP case only. @@ -194,17 +181,12 @@ return ssl_config_for_proxy_.get(); } - const OnHostResolutionCallback& resolution_callback() const { - return resolution_callback_; - } - private: friend class base::RefCounted<SocketParams>; ~SocketParams(); std::unique_ptr<SSLConfig> ssl_config_for_origin_; std::unique_ptr<SSLConfig> ssl_config_for_proxy_; - const OnHostResolutionCallback resolution_callback_; DISALLOW_COPY_AND_ASSIGN(SocketParams); };
diff --git a/net/socket/client_socket_pool_manager.cc b/net/socket/client_socket_pool_manager.cc index aaae25b..35572c4d 100644 --- a/net/socket/client_socket_pool_manager.cc +++ b/net/socket/client_socket_pool_manager.cc
@@ -90,15 +90,13 @@ const ClientSocketPool::GroupId& group_id, const ProxyServer& proxy_server, const SSLConfig& ssl_config_for_origin, - const SSLConfig& ssl_config_for_proxy, - const OnHostResolutionCallback& resolution_callback) { + const SSLConfig& ssl_config_for_proxy) { bool using_ssl = group_id.socket_type() == ClientSocketPool::SocketType::kSsl; bool using_proxy_ssl = proxy_server.is_http_like() && !proxy_server.is_http(); return base::MakeRefCounted<ClientSocketPool::SocketParams>( using_ssl ? std::make_unique<SSLConfig>(ssl_config_for_origin) : nullptr, using_proxy_ssl ? std::make_unique<SSLConfig>(ssl_config_for_proxy) - : nullptr, - resolution_callback); + : nullptr); } int InitSocketPoolHelper( @@ -117,7 +115,6 @@ int num_preconnect_streams, ClientSocketHandle* socket_handle, HttpNetworkSession::SocketPoolType socket_pool_type, - const OnHostResolutionCallback& resolution_callback, CompletionOnceCallback callback, const ClientSocketPool::ProxyAuthCallback& proxy_auth_callback) { bool using_ssl = group_type == ClientSocketPoolManager::SSL_GROUP; @@ -133,8 +130,7 @@ CreateGroupId(group_type, origin_host_port, proxy_info, privacy_mode); scoped_refptr<ClientSocketPool::SocketParams> socket_params = CreateSocketParams(connection_group, proxy_info.proxy_server(), - ssl_config_for_origin, ssl_config_for_proxy, - resolution_callback); + ssl_config_for_origin, ssl_config_for_proxy); ClientSocketPool* pool = session->GetSocketPool(socket_pool_type, proxy_info.proxy_server()); @@ -247,7 +243,6 @@ const SocketTag& socket_tag, const NetLogWithSource& net_log, ClientSocketHandle* socket_handle, - const OnHostResolutionCallback& resolution_callback, CompletionOnceCallback callback, const ClientSocketPool::ProxyAuthCallback& proxy_auth_callback) { DCHECK(socket_handle); @@ -256,7 +251,7 @@ proxy_info, ssl_config_for_origin, ssl_config_for_proxy, false /* is_for_websockets */, privacy_mode, socket_tag, net_log, 0, socket_handle, HttpNetworkSession::NORMAL_SOCKET_POOL, - resolution_callback, std::move(callback), proxy_auth_callback); + std::move(callback), proxy_auth_callback); } int InitSocketHandleForWebSocketRequest( @@ -271,7 +266,6 @@ PrivacyMode privacy_mode, const NetLogWithSource& net_log, ClientSocketHandle* socket_handle, - const OnHostResolutionCallback& resolution_callback, CompletionOnceCallback callback, const ClientSocketPool::ProxyAuthCallback& proxy_auth_callback) { DCHECK(socket_handle); @@ -284,7 +278,7 @@ proxy_info, ssl_config_for_origin, ssl_config_for_proxy, true /* is_for_websockets */, privacy_mode, SocketTag(), net_log, 0, socket_handle, HttpNetworkSession::WEBSOCKET_SOCKET_POOL, - resolution_callback, std::move(callback), proxy_auth_callback); + std::move(callback), proxy_auth_callback); } int PreconnectSocketsForHttpRequest( @@ -307,8 +301,7 @@ proxy_info, ssl_config_for_origin, ssl_config_for_proxy, false /* force_tunnel */, privacy_mode, SocketTag(), net_log, num_preconnect_streams, nullptr, HttpNetworkSession::NORMAL_SOCKET_POOL, - OnHostResolutionCallback(), CompletionOnceCallback(), - ClientSocketPool::ProxyAuthCallback()); + CompletionOnceCallback(), ClientSocketPool::ProxyAuthCallback()); } } // namespace net
diff --git a/net/socket/client_socket_pool_manager.h b/net/socket/client_socket_pool_manager.h index 1ec33cd..bfff5f1 100644 --- a/net/socket/client_socket_pool_manager.h +++ b/net/socket/client_socket_pool_manager.h
@@ -26,9 +26,6 @@ namespace net { -typedef base::Callback<int(const AddressList&, const NetLogWithSource& net_log)> - OnHostResolutionCallback; - class ClientSocketHandle; class HostPortPair; class NetLogWithSource; @@ -112,7 +109,6 @@ const SocketTag& socket_tag, const NetLogWithSource& net_log, ClientSocketHandle* socket_handle, - const OnHostResolutionCallback& resolution_callback, CompletionOnceCallback callback, const ClientSocketPool::ProxyAuthCallback& proxy_auth_callback); @@ -137,7 +133,6 @@ PrivacyMode privacy_mode, const NetLogWithSource& net_log, ClientSocketHandle* socket_handle, - const OnHostResolutionCallback& resolution_callback, CompletionOnceCallback callback, const ClientSocketPool::ProxyAuthCallback& proxy_auth_callback);
diff --git a/net/socket/connect_job.h b/net/socket/connect_job.h index 70083a8ff..2ce8d90 100644 --- a/net/socket/connect_job.h +++ b/net/socket/connect_job.h
@@ -97,9 +97,27 @@ WebSocketEndpointLockManager* websocket_endpoint_lock_manager; }; +// When a host resolution completes, OnHostResolutionCallback() is invoked. If +// it returns |kContinue|, the ConnectJob can continue immediately. If it +// returns |kMayBeDeletedAsync|, the ConnectJob may be slated for asychronous +// destruction, so should post a task before continuing, in case it will be +// deleted. The purpose of kMayBeDeletedAsync is to avoid needlessly creating +// and connecting a socket when it might not be needed. +enum class OnHostResolutionCallbackResult { + kContinue, + kMayBeDeletedAsync, +}; + +// If non-null, invoked when host resolution completes. May not destroy the +// ConnectJob synchronously, but may signal the ConnectJob may be destroyed +// asynchronously. See OnHostResolutionCallbackResult above. +// +// |address_list| is the list of addresses the host being connected to was +// resolved to, with the port fields populated to the port being connected to. using OnHostResolutionCallback = - base::RepeatingCallback<int(const AddressList&, - const NetLogWithSource& net_log)>; + base::RepeatingCallback<OnHostResolutionCallbackResult( + const HostPortPair& host_port_pair, + const AddressList& address_list)>; // ConnectJob provides an abstract interface for "connecting" a socket. // The connection may involve host resolution, tcp connection, ssl connection,
diff --git a/net/socket/transport_client_socket_pool_unittest.cc b/net/socket/transport_client_socket_pool_unittest.cc index 1265406..d8d59ca 100644 --- a/net/socket/transport_client_socket_pool_unittest.cc +++ b/net/socket/transport_client_socket_pool_unittest.cc
@@ -1029,7 +1029,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( GetSSLConfig() /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); ClientSocketHandle handle; TestCompletionCallback callback; @@ -1304,7 +1304,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( nullptr /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); SOCKS5MockData data(socket_io_mode); data.data_provider()->set_connect_data(MockConnect(socket_io_mode, OK)); @@ -1377,8 +1377,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( GetSSLConfig() /* ssl_config_for_origin */, - GetSSLConfig() /* ssl_config_for_proxy */, - OnHostResolutionCallback()); + GetSSLConfig() /* ssl_config_for_proxy */); ClientSocketPool::GroupId group_id(kEndpoint, ClientSocketPool::SocketType::kSsl, @@ -1482,8 +1481,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( GetSSLConfig() /* ssl_config_for_origin */, - GetSSLConfig() /* ssl_config_for_proxy */, - OnHostResolutionCallback()); + GetSSLConfig() /* ssl_config_for_proxy */); ClientSocketPool::GroupId group_id(kEndpoint, ClientSocketPool::SocketType::kSsl, @@ -1580,8 +1578,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( GetSSLConfig() /* ssl_config_for_origin */, - GetSSLConfig() /* ssl_config_for_proxy */, - OnHostResolutionCallback()); + GetSSLConfig() /* ssl_config_for_proxy */); int rv = handle.Init( ClientSocketPool::GroupId(kEndpoint, @@ -1755,7 +1752,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socks_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( nullptr /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); // Test socket is tagged when created synchronously. SOCKS5MockData data_sync(SYNCHRONOUS); @@ -1849,7 +1846,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( std::make_unique<SSLConfig>() /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); // Test socket is tagged before connected. uint64_t old_traffic = GetTaggedBytes(tag_val1); @@ -1918,7 +1915,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( GetSSLConfig() /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); // Test connect jobs that are orphaned and then adopted, appropriately apply // new tag. Request socket with |tag1|. @@ -1981,7 +1978,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( GetSSLConfig() /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); // Test that sockets paused by a full underlying socket pool are properly // connected and tagged when underlying pool is freed up. @@ -2061,7 +2058,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( nullptr /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); // Verify requested socket is tagged properly. ClientSocketHandle handle; @@ -2135,7 +2132,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( GetSSLConfig() /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); // Verify requested socket is tagged properly. ClientSocketHandle handle; @@ -2248,7 +2245,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( nullptr /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); session_deps.socket_factory->AddSocketDataProvider(&provider_socket_1); ClientSocketHandle connection; TestCompletionCallback callback; @@ -2293,7 +2290,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( nullptr /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); SequencedSocketData provider_socket_2(MockConnect(ASYNC, OK), base::span<MockRead>(), base::span<MockWrite>());
diff --git a/net/socket/transport_connect_job.cc b/net/socket/transport_connect_job.cc index 30707ffa..047ac7a 100644 --- a/net/socket/transport_connect_job.cc +++ b/net/socket/transport_connect_job.cc
@@ -12,6 +12,7 @@ #include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_util.h" +#include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "base/values.h" #include "net/base/ip_endpoint.h" @@ -100,7 +101,8 @@ NetLogEventType::TRANSPORT_CONNECT_JOB_CONNECT), params_(params), next_state_(STATE_NONE), - resolve_result_(OK) { + resolve_result_(OK), + weak_ptr_factory_(this) { // This is only set for WebSockets. DCHECK(!common_connect_job_params->websocket_endpoint_lock_manager); } @@ -278,15 +280,22 @@ return result; DCHECK(request_->GetAddressResults()); - // Invoke callback, and abort if it fails. + next_state_ = STATE_TRANSPORT_CONNECT; + + // Invoke callback. If it indicates |this| may be slated for deletion, then + // only continue after a PostTask. if (!params_->host_resolution_callback().is_null()) { - result = params_->host_resolution_callback().Run( - request_->GetAddressResults().value(), net_log()); - if (result != OK) - return result; + OnHostResolutionCallbackResult callback_result = + params_->host_resolution_callback().Run( + params_->destination(), request_->GetAddressResults().value()); + if (callback_result == OnHostResolutionCallbackResult::kMayBeDeletedAsync) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&TransportConnectJob::OnIOComplete, + weak_ptr_factory_.GetWeakPtr(), OK)); + return ERR_IO_PENDING; + } } - next_state_ = STATE_TRANSPORT_CONNECT; return result; }
diff --git a/net/socket/transport_connect_job.h b/net/socket/transport_connect_job.h index 7d8b23f..0258e52 100644 --- a/net/socket/transport_connect_job.h +++ b/net/socket/transport_connect_job.h
@@ -11,6 +11,7 @@ #include "base/callback.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "net/base/host_port_pair.h" @@ -169,6 +170,8 @@ ConnectionAttempts connection_attempts_; ConnectionAttempts fallback_connection_attempts_; + base::WeakPtrFactory<TransportConnectJob> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(TransportConnectJob); };
diff --git a/net/socket/websocket_transport_connect_job.cc b/net/socket/websocket_transport_connect_job.cc index 0a28e158..1b80ee69 100644 --- a/net/socket/websocket_transport_connect_job.cc +++ b/net/socket/websocket_transport_connect_job.cc
@@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/location.h" #include "base/logging.h" +#include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "base/values.h" @@ -40,7 +41,8 @@ next_state_(STATE_NONE), race_result_(TransportConnectJob::RACE_UNKNOWN), had_ipv4_(false), - had_ipv6_(false) { + had_ipv6_(false), + weak_ptr_factory_(this) { DCHECK(common_connect_job_params->websocket_endpoint_lock_manager); } @@ -127,15 +129,22 @@ return result; DCHECK(request_->GetAddressResults()); - // Invoke callback, and abort if it fails. + next_state_ = STATE_TRANSPORT_CONNECT; + + // Invoke callback. If it indicates |this| may be slated for deletion, then + // only continue after a PostTask. if (!params_->host_resolution_callback().is_null()) { - result = params_->host_resolution_callback().Run( - request_->GetAddressResults().value(), net_log()); - if (result != OK) - return result; + OnHostResolutionCallbackResult callback_result = + params_->host_resolution_callback().Run( + params_->destination(), request_->GetAddressResults().value()); + if (callback_result == OnHostResolutionCallbackResult::kMayBeDeletedAsync) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&WebSocketTransportConnectJob::OnIOComplete, + weak_ptr_factory_.GetWeakPtr(), OK)); + return ERR_IO_PENDING; + } } - next_state_ = STATE_TRANSPORT_CONNECT; return result; }
diff --git a/net/socket/websocket_transport_connect_job.h b/net/socket/websocket_transport_connect_job.h index 1ebf709d..6ba51a0 100644 --- a/net/socket/websocket_transport_connect_job.h +++ b/net/socket/websocket_transport_connect_job.h
@@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "net/base/net_export.h" @@ -105,6 +106,8 @@ bool had_ipv4_; bool had_ipv6_; + base::WeakPtrFactory<WebSocketTransportConnectJob> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(WebSocketTransportConnectJob); };
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index aaefe03..b8adb41 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -17,6 +17,7 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/test_file_util.h" #include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" #include "net/base/auth.h" #include "net/base/chunked_upload_data_stream.h" #include "net/base/completion_once_callback.h" @@ -2547,6 +2548,8 @@ TEST_F(SpdyNetworkTransactionTest, RedirectGetRequest) { SpdyURLRequestContext spdy_url_request_context; + // Use a different port to avoid trying to reuse the initial H2 session. + const char kRedirectUrl[] = "https://www.foo.com:8080/index.php"; SSLSocketDataProvider ssl_provider0(ASYNC, OK); ssl_provider0.next_proto = kProtoHTTP2; @@ -2564,8 +2567,7 @@ spdy_util_.ConstructSpdyRstStream(1, spdy::ERROR_CODE_CANCEL)); MockWrite writes0[] = {CreateMockWrite(req0, 0), CreateMockWrite(rst, 2)}; - const char* const kExtraHeaders[] = {"location", - "https://www.foo.com/index.php"}; + const char* const kExtraHeaders[] = {"location", kRedirectUrl}; spdy::SpdySerializedFrame resp0(spdy_util_.ConstructSpdyReplyError( "301", kExtraHeaders, base::size(kExtraHeaders) / 2, 1)); MockRead reads0[] = {CreateMockRead(resp0, 1), MockRead(ASYNC, 0, 3)}; @@ -2580,7 +2582,7 @@ SpdyTestUtil spdy_util1; spdy::SpdyHeaderBlock headers1( - spdy_util1.ConstructGetHeaderBlock("https://www.foo.com/index.php")); + spdy_util1.ConstructGetHeaderBlock(kRedirectUrl)); headers1["user-agent"] = ""; headers1["accept-encoding"] = "gzip, deflate"; spdy::SpdySerializedFrame req1( @@ -3837,6 +3839,394 @@ helper.VerifyDataConsumed(); } +TEST_F(SpdyNetworkTransactionTest, NoConnectionPoolingOverTunnel) { + // Use port 443 for two reasons: This makes the endpoint is port 443 check in + // NormalSpdyTransactionHelper pass, and this means that the tunnel uses the + // same port as the servers, to further confuse things. + const char kPacString[] = "PROXY myproxy:443"; + + auto session_deps = std::make_unique<SpdySessionDependencies>( + ProxyResolutionService::CreateFixedFromPacResult( + kPacString, TRAFFIC_ANNOTATION_FOR_TESTS)); + NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, + std::move(session_deps)); + + // Only one request uses the first connection. + spdy::SpdySerializedFrame req1( + spdy_util_.ConstructSpdyGet("https://www.example.org", 1, LOWEST)); + MockWrite writes1[] = { + MockWrite(ASYNC, 0, + "CONNECT www.example.org:443 HTTP/1.1\r\n" + "Host: www.example.org:443\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), + CreateMockWrite(req1, 2), + }; + + spdy::SpdySerializedFrame resp1( + spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); + spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true)); + MockRead reads1[] = {MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n\r\n"), + CreateMockRead(resp1, 3), CreateMockRead(body1, 4), + MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5)}; + + MockConnect connect1(ASYNC, OK); + SequencedSocketData data1(connect1, reads1, writes1); + + // Run a transaction to completion to set up a SPDY session. + helper.RunToCompletion(&data1); + TransactionHelperResult out = helper.output(); + EXPECT_THAT(out.rv, IsOk()); + EXPECT_EQ("HTTP/1.1 200", out.status_line); + EXPECT_EQ("hello!", out.response_data); + + // A new SPDY session should have been created. + SpdySessionKey key1(HostPortPair("www.example.org", 443), + ProxyServer::FromPacString(kPacString), + PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, SocketTag()); + base::WeakPtr<SpdySession> session1 = + helper.session()->spdy_session_pool()->FindAvailableSession( + key1, true /* enable_up_base_pooling */, false /* is_websocket */, + NetLogWithSource()); + ASSERT_TRUE(session1); + + // The second request uses a second connection. + SpdyTestUtil spdy_util2; + spdy::SpdySerializedFrame req2( + spdy_util2.ConstructSpdyGet("https://example.test", 1, LOWEST)); + MockWrite writes2[] = { + MockWrite(ASYNC, 0, + "CONNECT example.test:443 HTTP/1.1\r\n" + "Host: example.test:443\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), + CreateMockWrite(req2, 2), + }; + + spdy::SpdySerializedFrame resp2( + spdy_util2.ConstructSpdyGetReply(nullptr, 0, 1)); + spdy::SpdySerializedFrame body2(spdy_util2.ConstructSpdyDataFrame(1, true)); + MockRead reads2[] = {MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n\r\n"), + CreateMockRead(resp2, 3), CreateMockRead(body2, 4), + MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5)}; + + MockConnect connect2(ASYNC, OK); + SequencedSocketData data2(connect2, reads2, writes2); + helper.AddData(&data2); + + HttpRequestInfo request2; + request2.method = "GET"; + request2.url = GURL("https://example.test/"); + request2.load_flags = 0; + request2.traffic_annotation = + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); + auto trans2 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, + helper.session()); + + TestCompletionCallback callback; + EXPECT_THAT(trans2->Start(&request2, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + + // Wait for the second request to get headers. It should create a new H2 + // session to do so. + EXPECT_THAT(callback.WaitForResult(), IsOk()); + + const HttpResponseInfo* response = trans2->GetResponseInfo(); + ASSERT_TRUE(response); + ASSERT_TRUE(response->headers); + EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); + EXPECT_TRUE(response->was_fetched_via_spdy); + EXPECT_TRUE(response->was_alpn_negotiated); + std::string response_data; + ASSERT_THAT(ReadTransaction(trans2.get(), &response_data), IsOk()); + EXPECT_EQ("hello!", response_data); + + // Inspect the new session. + SpdySessionKey key2(HostPortPair("example.test", 443), + ProxyServer::FromPacString(kPacString), + PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, SocketTag()); + base::WeakPtr<SpdySession> session2 = + helper.session()->spdy_session_pool()->FindAvailableSession( + key2, true /* enable_up_base_pooling */, false /* is_websocket */, + NetLogWithSource()); + ASSERT_TRUE(session2); + ASSERT_TRUE(session1); + EXPECT_NE(session1.get(), session2.get()); +} + +// Check that if a session is found after host resolution, but is closed before +// the task to try to use it executes, the request will continue to create a new +// socket and use it. +TEST_F(SpdyNetworkTransactionTest, ConnectionPoolingSessionClosedBeforeUse) { + NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr); + + // Only one request uses the first connection. + spdy::SpdySerializedFrame req1( + spdy_util_.ConstructSpdyGet("https://www.example.org", 1, LOWEST)); + MockWrite writes1[] = { + CreateMockWrite(req1, 0), + }; + + spdy::SpdySerializedFrame resp1( + spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); + spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true)); + MockRead reads1[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2), + MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3)}; + + MockConnect connect1(ASYNC, OK); + SequencedSocketData data1(connect1, reads1, writes1); + + // Run a transaction to completion to set up a SPDY session. + helper.RunToCompletion(&data1); + TransactionHelperResult out = helper.output(); + EXPECT_THAT(out.rv, IsOk()); + EXPECT_EQ("HTTP/1.1 200", out.status_line); + EXPECT_EQ("hello!", out.response_data); + + // A new SPDY session should have been created. + SpdySessionKey key1(HostPortPair("www.example.org", 443), + ProxyServer::Direct(), PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, SocketTag()); + EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession( + key1, true /* enable_up_base_pooling */, false /* is_websocket */, + NetLogWithSource())); + + // The second request uses a second connection. + SpdyTestUtil spdy_util2; + spdy::SpdySerializedFrame req2( + spdy_util2.ConstructSpdyGet("https://example.test", 1, LOWEST)); + MockWrite writes2[] = { + CreateMockWrite(req2, 0), + }; + + spdy::SpdySerializedFrame resp2( + spdy_util2.ConstructSpdyGetReply(nullptr, 0, 1)); + spdy::SpdySerializedFrame body2(spdy_util2.ConstructSpdyDataFrame(1, true)); + MockRead reads2[] = {CreateMockRead(resp2, 1), CreateMockRead(body2, 2), + MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3)}; + + MockConnect connect2(ASYNC, OK); + SequencedSocketData data2(connect2, reads2, writes2); + helper.AddData(&data2); + + HttpRequestInfo request2; + request2.method = "GET"; + request2.url = GURL("https://example.test/"); + request2.load_flags = 0; + request2.traffic_annotation = + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); + auto trans2 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, + helper.session()); + + // Set on-demand mode and run the second request to the DNS lookup. + helper.session_deps()->host_resolver->set_ondemand_mode(true); + TestCompletionCallback callback; + EXPECT_THAT(trans2->Start(&request2, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + base::RunLoop().RunUntilIdle(); + ASSERT_TRUE(helper.session_deps()->host_resolver->has_pending_requests()); + + // Resolve the request now, which should create an alias for the SpdySession + // immediately, but the task to use the session for the second request should + // run asynchronously, so it hasn't run yet. + helper.session_deps()->host_resolver->ResolveOnlyRequestNow(); + SpdySessionKey key2(HostPortPair("example.test", 443), ProxyServer::Direct(), + PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, SocketTag()); + base::WeakPtr<SpdySession> session1 = + helper.session()->spdy_session_pool()->FindAvailableSession( + key2, true /* enable_up_base_pooling */, false /* is_websocket */, + NetLogWithSource()); + ASSERT_TRUE(session1); + EXPECT_EQ(key1, session1->spdy_session_key()); + // Remove the session before the second request can try to use it. + helper.session()->spdy_session_pool()->CloseAllSessions(); + + // Wait for the second request to get headers. It should create a new H2 + // session to do so. + EXPECT_THAT(callback.WaitForResult(), IsOk()); + + const HttpResponseInfo* response = trans2->GetResponseInfo(); + ASSERT_TRUE(response); + ASSERT_TRUE(response->headers); + EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); + EXPECT_TRUE(response->was_fetched_via_spdy); + EXPECT_TRUE(response->was_alpn_negotiated); + std::string response_data; + ASSERT_THAT(ReadTransaction(trans2.get(), &response_data), IsOk()); + EXPECT_EQ("hello!", response_data); + + // Inspect the new session. + base::WeakPtr<SpdySession> session2 = + helper.session()->spdy_session_pool()->FindAvailableSession( + key2, true /* enable_up_base_pooling */, false /* is_websocket */, + NetLogWithSource()); + ASSERT_TRUE(session2); + EXPECT_EQ(key2, session2->spdy_session_key()); + helper.VerifyDataConsumed(); +} + +#if defined(OS_ANDROID) + +// Test this if two HttpNetworkTransactions try to repurpose the same +// SpdySession with two different SocketTags, only one request gets the session, +// while the other makes a new SPDY session. +TEST_F(SpdyNetworkTransactionTest, ConnectionPoolingMultipleSocketTags) { + const SocketTag kSocketTag1(SocketTag::UNSET_UID, 1); + const SocketTag kSocketTag2(SocketTag::UNSET_UID, 2); + const SocketTag kSocketTag3(SocketTag::UNSET_UID, 3); + + NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr); + + // The first and third requests use the first connection. + spdy::SpdySerializedFrame req1( + spdy_util_.ConstructSpdyGet("https://www.example.org", 1, LOWEST)); + spdy_util_.UpdateWithStreamDestruction(1); + spdy::SpdySerializedFrame req3( + spdy_util_.ConstructSpdyGet("https://example.test/request3", 3, LOWEST)); + MockWrite writes1[] = { + CreateMockWrite(req1, 0), + CreateMockWrite(req3, 3), + }; + + spdy::SpdySerializedFrame resp1( + spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); + spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true)); + spdy::SpdySerializedFrame resp3( + spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); + spdy::SpdySerializedFrame body3(spdy_util_.ConstructSpdyDataFrame(3, true)); + MockRead reads1[] = {CreateMockRead(resp1, 1), CreateMockRead(body1, 2), + CreateMockRead(resp3, 4), CreateMockRead(body3, 5), + MockRead(SYNCHRONOUS, ERR_IO_PENDING, 6)}; + + SequencedSocketData data1(MockConnect(ASYNC, OK), reads1, writes1); + helper.AddData(&data1); + + // Due to the vagaries of how the socket pools work, in this particular case, + // the second ConnectJob will be cancelled, but only after it tries to start + // connecting. This does not happen in the general case of a bunch of requests + // using the same socket tag. + SequencedSocketData data2(MockConnect(SYNCHRONOUS, ERR_IO_PENDING), + base::span<const MockRead>(), + base::span<const MockWrite>()); + helper.AddData(&data2); + + // The second request uses a second connection. + SpdyTestUtil spdy_util2; + spdy::SpdySerializedFrame req2( + spdy_util2.ConstructSpdyGet("https://example.test/request2", 1, LOWEST)); + MockWrite writes2[] = { + CreateMockWrite(req2, 0), + }; + + spdy::SpdySerializedFrame resp2( + spdy_util2.ConstructSpdyGetReply(nullptr, 0, 1)); + spdy::SpdySerializedFrame body2(spdy_util2.ConstructSpdyDataFrame(1, true)); + MockRead reads2[] = {CreateMockRead(resp2, 1), CreateMockRead(body2, 2), + MockRead(SYNCHRONOUS, ERR_IO_PENDING, 3)}; + + SequencedSocketData data3(MockConnect(ASYNC, OK), reads2, writes2); + helper.AddData(&data3); + + // Run a transaction to completion to set up a SPDY session. This can't use + // RunToCompletion(), since it can't call VerifyDataConsumed() yet. + helper.RunPreTestSetup(); + helper.RunDefaultTest(); + TransactionHelperResult out = helper.output(); + EXPECT_THAT(out.rv, IsOk()); + EXPECT_EQ("HTTP/1.1 200", out.status_line); + EXPECT_EQ("hello!", out.response_data); + + // A new SPDY session should have been created. + SpdySessionKey key1(HostPortPair("www.example.org", 443), + ProxyServer::Direct(), PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, SocketTag()); + EXPECT_TRUE(helper.session()->spdy_session_pool()->FindAvailableSession( + key1, true /* enable_up_base_pooling */, false /* is_websocket */, + NetLogWithSource())); + + // Set on-demand mode for the next two requests. + helper.session_deps()->host_resolver->set_ondemand_mode(true); + + HttpRequestInfo request2; + request2.socket_tag = kSocketTag2; + request2.method = "GET"; + request2.url = GURL("https://example.test/request2"); + request2.load_flags = 0; + request2.traffic_annotation = + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); + auto trans2 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, + helper.session()); + TestCompletionCallback callback2; + EXPECT_THAT( + trans2->Start(&request2, callback2.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + + HttpRequestInfo request3; + request3.socket_tag = kSocketTag3; + request3.method = "GET"; + request3.url = GURL("https://example.test/request3"); + request3.load_flags = 0; + request3.traffic_annotation = + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); + auto trans3 = std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, + helper.session()); + TestCompletionCallback callback3; + EXPECT_THAT( + trans3->Start(&request3, callback3.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + + // Run the message loop until both requests are waiting on the host resolver. + base::RunLoop().RunUntilIdle(); + ASSERT_TRUE(helper.session_deps()->host_resolver->has_pending_requests()); + + // Complete the second requests's DNS lookup now, which should create an alias + // for the SpdySession immediately, but the task to use the session for the + // second request should run asynchronously, so it hasn't run yet. + helper.session_deps()->host_resolver->ResolveNow(2); + SpdySessionKey key2(HostPortPair("example.test", 443), ProxyServer::Direct(), + PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, kSocketTag2); + + // Complete the third requests's DNS lookup now, which should hijack the + // SpdySession from the second request. + helper.session_deps()->host_resolver->ResolveNow(3); + SpdySessionKey key3(HostPortPair("example.test", 443), ProxyServer::Direct(), + PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, kSocketTag3); + + // Wait for the second request to get headers. It should create a new H2 + // session to do so. + EXPECT_THAT(callback2.WaitForResult(), IsOk()); + + const HttpResponseInfo* response = trans2->GetResponseInfo(); + ASSERT_TRUE(response); + ASSERT_TRUE(response->headers); + EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); + EXPECT_TRUE(response->was_fetched_via_spdy); + EXPECT_TRUE(response->was_alpn_negotiated); + std::string response_data; + ASSERT_THAT(ReadTransaction(trans2.get(), &response_data), IsOk()); + EXPECT_EQ("hello!", response_data); + + // Wait for the third request to get headers. It should have reused the first + // session. + EXPECT_THAT(callback3.WaitForResult(), IsOk()); + + response = trans3->GetResponseInfo(); + ASSERT_TRUE(response); + ASSERT_TRUE(response->headers); + EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); + EXPECT_TRUE(response->was_fetched_via_spdy); + EXPECT_TRUE(response->was_alpn_negotiated); + ASSERT_THAT(ReadTransaction(trans3.get(), &response_data), IsOk()); + EXPECT_EQ("hello!", response_data); + + helper.VerifyDataConsumed(); +} + +#endif // defined(OS_ANDROID) + // Regression test for https://crbug.com/727653. TEST_F(SpdyNetworkTransactionTest, RejectServerPushWithNoMethod) { base::HistogramTester histogram_tester; @@ -8065,6 +8455,285 @@ /* expected_count = */ 1); } +// Same as above, but checks that a WebSocket connection avoids creating a new +// socket if it detects an H2 session when host resolution completes, and +// requests also use different hostnames. +TEST_F(SpdyNetworkTransactionTest, + WebSocketOverHTTP2DetectsNewSessionWithAliasing) { + base::HistogramTester histogram_tester; + auto session_deps = std::make_unique<SpdySessionDependencies>(); + session_deps->enable_websocket_over_http2 = true; + session_deps->host_resolver->set_ondemand_mode(true); + NormalSpdyTransactionHelper helper(request_, HIGHEST, log_, + std::move(session_deps)); + helper.RunPreTestSetup(); + + spdy::SpdySerializedFrame req( + spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST)); + spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); + + spdy::SpdyHeaderBlock websocket_request_headers; + websocket_request_headers[spdy::kHttp2MethodHeader] = "CONNECT"; + websocket_request_headers[spdy::kHttp2AuthorityHeader] = "example.test"; + websocket_request_headers[spdy::kHttp2SchemeHeader] = "https"; + websocket_request_headers[spdy::kHttp2PathHeader] = "/"; + websocket_request_headers[spdy::kHttp2ProtocolHeader] = "websocket"; + websocket_request_headers["origin"] = "http://example.test"; + websocket_request_headers["sec-websocket-version"] = "13"; + websocket_request_headers["sec-websocket-extensions"] = + "permessage-deflate; client_max_window_bits"; + spdy::SpdySerializedFrame websocket_request(spdy_util_.ConstructSpdyHeaders( + 3, std::move(websocket_request_headers), MEDIUM, false)); + + spdy::SpdySerializedFrame priority1( + spdy_util_.ConstructSpdyPriority(3, 0, MEDIUM, true)); + spdy::SpdySerializedFrame priority2( + spdy_util_.ConstructSpdyPriority(1, 3, LOWEST, true)); + + MockWrite writes[] = { + CreateMockWrite(req, 0), CreateMockWrite(settings_ack, 2), + CreateMockWrite(websocket_request, 4), CreateMockWrite(priority1, 5), + CreateMockWrite(priority2, 6)}; + + spdy::SettingsMap settings; + settings[spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL] = 1; + spdy::SpdySerializedFrame settings_frame( + spdy_util_.ConstructSpdySettings(settings)); + spdy::SpdySerializedFrame resp1( + spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); + spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true)); + spdy::SpdySerializedFrame websocket_response( + spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); + MockRead reads[] = {CreateMockRead(settings_frame, 1), + CreateMockRead(resp1, 3), CreateMockRead(body1, 7), + CreateMockRead(websocket_response, 8), + MockRead(SYNCHRONOUS, ERR_IO_PENDING, 9)}; + + SequencedSocketData data(reads, writes); + helper.AddData(&data); + + TestCompletionCallback callback1; + int rv = helper.trans()->Start(&request_, callback1.callback(), log_); + ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); + + HttpRequestInfo request2; + request2.method = "GET"; + request2.url = GURL("wss://example.test/"); + request2.traffic_annotation = + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); + request2.extra_headers.SetHeader("Origin", "http://example.test"); + request2.extra_headers.SetHeader("Sec-WebSocket-Version", "13"); + // The following two headers must be removed by WebSocketHttp2HandshakeStream. + request2.extra_headers.SetHeader("Connection", "Upgrade"); + request2.extra_headers.SetHeader("Upgrade", "websocket"); + + TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper; + + HttpNetworkTransaction trans2(MEDIUM, helper.session()); + trans2.SetWebSocketHandshakeStreamCreateHelper( + &websocket_stream_create_helper); + + TestCompletionCallback callback2; + rv = trans2.Start(&request2, callback2.callback(), log_); + ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); + + // Make sure both requests are blocked on host resolution. + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(helper.session_deps()->host_resolver->has_pending_requests()); + // Complete the first DNS lookup, which should result in the first transaction + // creating an H2 session (And completing successfully). + helper.session_deps()->host_resolver->ResolveNow(1); + base::RunLoop().RunUntilIdle(); + + SpdySessionKey key1(HostPortPair::FromURL(request_.url), + ProxyServer::Direct(), PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, SocketTag()); + base::WeakPtr<SpdySession> spdy_session1 = + helper.session()->spdy_session_pool()->FindAvailableSession( + key1, /* enable_ip_based_pooling = */ true, + /* is_websocket = */ false, log_); + ASSERT_TRUE(spdy_session1); + EXPECT_TRUE(spdy_session1->support_websocket()); + + // Second DNS lookup completes, which results in creating a WebSocket stream. + helper.session_deps()->host_resolver->ResolveNow(2); + ASSERT_TRUE(spdy_session1); + + SpdySessionKey key2(HostPortPair::FromURL(request2.url), + ProxyServer::Direct(), PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, SocketTag()); + base::WeakPtr<SpdySession> spdy_session2 = + helper.session()->spdy_session_pool()->FindAvailableSession( + key1, /* enable_ip_based_pooling = */ true, + /* is_websocket = */ true, log_); + ASSERT_TRUE(spdy_session2); + EXPECT_EQ(spdy_session1.get(), spdy_session2.get()); + + base::RunLoop().RunUntilIdle(); + + // First request has HIGHEST priority, WebSocket request has MEDIUM priority. + // Changing the priority of the first request to LOWEST changes their order, + // and therefore triggers sending PRIORITY frames. + helper.trans()->SetPriority(LOWEST); + + rv = callback1.WaitForResult(); + ASSERT_THAT(rv, IsOk()); + + const HttpResponseInfo* response = helper.trans()->GetResponseInfo(); + ASSERT_TRUE(response->headers); + EXPECT_TRUE(response->was_fetched_via_spdy); + EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); + + std::string response_data; + rv = ReadTransaction(helper.trans(), &response_data); + EXPECT_THAT(rv, IsOk()); + EXPECT_EQ("hello!", response_data); + + rv = callback2.WaitForResult(); + ASSERT_THAT(rv, IsOk()); + + helper.VerifyDataConsumed(); +} + +// Same as above, but the SpdySession is closed just before use, so the +// WebSocket is sent over a new HTTP/1.x connection instead. +TEST_F(SpdyNetworkTransactionTest, + WebSocketOverDetectsNewSessionWithAliasingButClosedBeforeUse) { + base::HistogramTester histogram_tester; + auto session_deps = std::make_unique<SpdySessionDependencies>(); + session_deps->enable_websocket_over_http2 = true; + session_deps->host_resolver->set_ondemand_mode(true); + NormalSpdyTransactionHelper helper(request_, HIGHEST, log_, + std::move(session_deps)); + helper.RunPreTestSetup(); + + spdy::SpdySerializedFrame req( + spdy_util_.ConstructSpdyGet(nullptr, 0, 1, HIGHEST)); + spdy::SpdySerializedFrame settings_ack(spdy_util_.ConstructSpdySettingsAck()); + + MockWrite writes[] = {CreateMockWrite(req, 0), + CreateMockWrite(settings_ack, 2)}; + + spdy::SettingsMap settings; + settings[spdy::SETTINGS_ENABLE_CONNECT_PROTOCOL] = 1; + spdy::SpdySerializedFrame settings_frame( + spdy_util_.ConstructSpdySettings(settings)); + spdy::SpdySerializedFrame resp1( + spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); + spdy::SpdySerializedFrame body1(spdy_util_.ConstructSpdyDataFrame(1, true)); + MockRead reads[] = {CreateMockRead(settings_frame, 1), + CreateMockRead(resp1, 3), CreateMockRead(body1, 4), + MockRead(SYNCHRONOUS, ERR_IO_PENDING, 5)}; + + SequencedSocketData data(reads, writes); + helper.AddData(&data); + + MockWrite writes2[] = { + MockWrite("GET / HTTP/1.1\r\n" + "Host: example.test\r\n" + "Connection: Upgrade\r\n" + "Upgrade: websocket\r\n" + "Origin: http://example.test\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Extensions: permessage-deflate; " + "client_max_window_bits\r\n\r\n")}; + MockRead reads2[] = { + MockRead("HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n")}; + StaticSocketDataProvider data2(reads2, writes2); + auto ssl_provider2 = std::make_unique<SSLSocketDataProvider>(ASYNC, OK); + // Test that request has empty |alpn_protos|, that is, HTTP/2 is disabled. + ssl_provider2->next_protos_expected_in_ssl_config = NextProtoVector{}; + // Force socket to use HTTP/1.1, the default protocol without ALPN. + ssl_provider2->next_proto = kProtoHTTP11; + ssl_provider2->ssl_info.cert = + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + helper.AddDataWithSSLSocketDataProvider(&data2, std::move(ssl_provider2)); + + TestCompletionCallback callback1; + int rv = helper.trans()->Start(&request_, callback1.callback(), log_); + ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); + + HttpRequestInfo request2; + request2.method = "GET"; + request2.url = GURL("wss://example.test/"); + request2.traffic_annotation = + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); + request2.extra_headers.SetHeader("Connection", "Upgrade"); + request2.extra_headers.SetHeader("Upgrade", "websocket"); + request2.extra_headers.SetHeader("Origin", "http://example.test"); + request2.extra_headers.SetHeader("Sec-WebSocket-Version", "13"); + + TestWebSocketHandshakeStreamCreateHelper websocket_stream_create_helper; + + HttpNetworkTransaction trans2(MEDIUM, helper.session()); + trans2.SetWebSocketHandshakeStreamCreateHelper( + &websocket_stream_create_helper); + + TestCompletionCallback callback2; + rv = trans2.Start(&request2, callback2.callback(), log_); + ASSERT_THAT(rv, IsError(ERR_IO_PENDING)); + + // Make sure both requests are blocked on host resolution. + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(helper.session_deps()->host_resolver->has_pending_requests()); + // Complete the first DNS lookup, which should result in the first transaction + // creating an H2 session (And completing successfully). + helper.session_deps()->host_resolver->ResolveNow(1); + + // Complete first request. + rv = callback1.WaitForResult(); + ASSERT_THAT(rv, IsOk()); + const HttpResponseInfo* response = helper.trans()->GetResponseInfo(); + ASSERT_TRUE(response->headers); + EXPECT_TRUE(response->was_fetched_via_spdy); + EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); + std::string response_data; + rv = ReadTransaction(helper.trans(), &response_data); + EXPECT_THAT(rv, IsOk()); + EXPECT_EQ("hello!", response_data); + + SpdySessionKey key1(HostPortPair::FromURL(request_.url), + ProxyServer::Direct(), PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, SocketTag()); + base::WeakPtr<SpdySession> spdy_session1 = + helper.session()->spdy_session_pool()->FindAvailableSession( + key1, /* enable_ip_based_pooling = */ true, + /* is_websocket = */ false, log_); + ASSERT_TRUE(spdy_session1); + EXPECT_TRUE(spdy_session1->support_websocket()); + + // Second DNS lookup completes, which results in creating an alias for the + // SpdySession immediately, and a task is posted asynchronously to use the + // alias.. + helper.session_deps()->host_resolver->ResolveNow(2); + + SpdySessionKey key2(HostPortPair::FromURL(request2.url), + ProxyServer::Direct(), PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, SocketTag()); + base::WeakPtr<SpdySession> spdy_session2 = + helper.session()->spdy_session_pool()->FindAvailableSession( + key1, /* enable_ip_based_pooling = */ true, + /* is_websocket = */ true, log_); + ASSERT_TRUE(spdy_session2); + EXPECT_EQ(spdy_session1.get(), spdy_session2.get()); + + // But the session is closed before it can be used. + helper.session()->spdy_session_pool()->CloseAllSessions(); + + // The second request establishes another connection (without even doing + // another DNS lookup) instead, and uses HTTP/1.x. + rv = callback2.WaitForResult(); + ASSERT_THAT(rv, IsOk()); + + helper.VerifyDataConsumed(); +} + TEST_F(SpdyNetworkTransactionTest, WebSocketNegotiatesHttp2) { HttpRequestInfo request; request.method = "GET";
diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc index e2447a2..8cc2dbd 100644 --- a/net/spdy/spdy_session_pool.cc +++ b/net/spdy/spdy_session_pool.cc
@@ -190,13 +190,6 @@ it->second->net_log().source().ToEventParametersCallback()); return it->second; } - - // Remove session from available sessions and from aliases, and remove - // key from the session's pooled alias set, so that a new session can be - // created with this |key|. - it->second->RemovePooledAlias(key); - UnmapKey(key); - RemoveAliases(key); return base::WeakPtr<SpdySession>(); } @@ -244,9 +237,8 @@ if (is_websocket && !available_session->support_websocket()) continue; - // If the session is a secure one, we need to verify that the - // server is authenticated to serve traffic for |host_port_proxy_pair| - // too. + // Need to verify that the server is authenticated to serve traffic for + // |host_port_proxy_pair| too. if (!available_session->VerifyDomainAuthentication( key.host_port_pair().host())) { UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 0, 2); @@ -279,7 +271,8 @@ UnmapKey(old_key); MapKeyToAvailableSession(new_key, available_session); - // Remap alias. + // Remap alias. From this point on |alias_it| is invalid, so no more + // iterations of the loop should be allowed. aliases_.insert(AliasMap::value_type(alias_it->first, new_key)); aliases_.erase(alias_it); @@ -316,6 +309,9 @@ MapKeyToAvailableSession(key, available_session); available_session->AddPooledAlias(key); } + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&SpdySessionPool::UpdatePendingRequests, + weak_ptr_factory_.GetWeakPtr(), key)); return available_session; } } @@ -359,6 +355,146 @@ return nullptr; } +OnHostResolutionCallbackResult SpdySessionPool::OnHostResolutionComplete( + const SpdySessionKey& key, + bool is_websocket, + const AddressList& addresses) { + // If there are no pending requests for that alias, nothing to do. + if (spdy_session_request_map_.find(key) == spdy_session_request_map_.end()) + return OnHostResolutionCallbackResult::kContinue; + + // Check if there's already a matching session. If so, there may already + // be a pending task to inform consumers of the alias. In this case, do + // nothing, but inform the caller to wait for such a task to run. + auto existing_session_it = LookupAvailableSessionByKey(key); + if (existing_session_it != available_sessions_.end()) { + // If this is an alias, the host resolution is for a websocket + // connection, and the aliased session doesn't support websockets, + // continue looking for an aliased session that does. Unlikely there + // is one, but can't hurt to check. + bool continue_searching_for_websockets = + is_websocket && !existing_session_it->second->support_websocket(); + + if (!continue_searching_for_websockets) + return OnHostResolutionCallbackResult::kMayBeDeletedAsync; + } + + for (const auto& address : addresses) { + auto range = aliases_.equal_range(address); + for (auto alias_it = range.first; alias_it != range.second; ++alias_it) { + // We found a potential alias. + const SpdySessionKey& alias_key = alias_it->second; + + auto available_session_it = LookupAvailableSessionByKey(alias_key); + // It shouldn't be in the aliases table if it doesn't exist! + DCHECK(available_session_it != available_sessions_.end()); + + // This session can be reused only if the proxy and privacy settings + // match. + if (!(alias_key.proxy_server() == key.proxy_server()) || + !(alias_key.privacy_mode() == key.privacy_mode()) || + !(alias_key.is_proxy_session() == key.is_proxy_session())) { + continue; + } + + if (is_websocket && !available_session_it->second->support_websocket()) + continue; + + // Make copy of WeakPtr as call to UnmapKey() will delete original. + const base::WeakPtr<SpdySession> available_session = + available_session_it->second; + + // Need to verify that the server is authenticated to serve traffic for + // |host_port_proxy_pair| too. + if (!available_session->VerifyDomainAuthentication( + key.host_port_pair().host())) { + UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 0, 2); + continue; + } + + UMA_HISTOGRAM_ENUMERATION("Net.SpdyIPPoolDomainMatch", 1, 2); + + bool adding_pooled_alias = true; + + // If socket tags differ, see if session's socket tag can be changed. + if (alias_key.socket_tag() != key.socket_tag()) { + SpdySessionKey old_key = available_session->spdy_session_key(); + SpdySessionKey new_key(old_key.host_port_pair(), old_key.proxy_server(), + old_key.privacy_mode(), + old_key.is_proxy_session(), key.socket_tag()); + + // If there is already a session with |new_key|, skip this one. + // It will be found in |aliases_| in a future iteration. + if (available_sessions_.find(new_key) != available_sessions_.end()) + continue; + + if (!available_session->ChangeSocketTag(key.socket_tag())) + continue; + + DCHECK(available_session->spdy_session_key() == new_key); + + // If this isn't a pooled alias, but the actual session that needs to + // have its socket tag change, there's no need to add an alias. + if (new_key == key) + adding_pooled_alias = false; + + // Remap main session key. + UnmapKey(old_key); + MapKeyToAvailableSession(new_key, available_session); + + // Remap alias. From this point on |alias_it| is invalid, so no more + // iterations of the loop should be allowed. + aliases_.insert(AliasMap::value_type(alias_it->first, new_key)); + aliases_.erase(alias_it); + + // Remap pooled session keys. + const auto& aliases = available_session->pooled_aliases(); + for (auto it = aliases.begin(); it != aliases.end();) { + // Ignore aliases this loop is inserting. + if (it->socket_tag() == key.socket_tag()) { + ++it; + continue; + } + UnmapKey(*it); + SpdySessionKey new_pool_alias_key = SpdySessionKey( + it->host_port_pair(), it->proxy_server(), it->privacy_mode(), + it->is_proxy_session(), key.socket_tag()); + MapKeyToAvailableSession(new_pool_alias_key, available_session); + auto old_it = it; + ++it; + available_session->RemovePooledAlias(*old_it); + available_session->AddPooledAlias(new_pool_alias_key); + + // If this is desired key, no need to add an alias for the desired key + // at the end of this method. + if (new_pool_alias_key == key) + adding_pooled_alias = false; + } + } + + if (adding_pooled_alias) { + // Add this session to the map so that we can find it next time. + MapKeyToAvailableSession(key, available_session); + available_session->AddPooledAlias(key); + } + + // Post task to inform pending requests for session for |key| that a + // matching session is now available. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&SpdySessionPool::UpdatePendingRequests, + weak_ptr_factory_.GetWeakPtr(), key)); + + // Inform the caller that the Callback may be deleted if the consumer is + // switched over to the newly aliased session. It's not guaranteed to be + // deleted, as the session may be closed, or taken by yet another pending + // request with a different SocketTag before the the request can try and + // use the session. + return OnHostResolutionCallbackResult::kMayBeDeletedAsync; + } + } + return OnHostResolutionCallbackResult::kContinue; +} + void SpdySessionPool::MakeSessionUnavailable( const base::WeakPtr<SpdySession>& available_session) { UnmapKey(available_session->spdy_session_key()); @@ -607,6 +743,20 @@ UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet", IMPORTED_FROM_SOCKET, SPDY_SESSION_GET_MAX); + // If there's a pre-existing matching session, it has to be an alias. Remove + // the alias. + auto it = LookupAvailableSessionByKey(key); + if (it != available_sessions_.end()) { + DCHECK(key != it->second->spdy_session_key()); + + // Remove session from available sessions and from aliases, and remove + // key from the session's pooled alias set, so that a new session can be + // created with this |key|. + it->second->RemovePooledAlias(key); + UnmapKey(key); + RemoveAliases(key); + } + return std::make_unique<SpdySession>( key, http_server_properties_, transport_security_state_, ssl_config_service_, quic_supported_versions_,
diff --git a/net/spdy/spdy_session_pool.h b/net/spdy/spdy_session_pool.h index c7ec1e3..9d9476ec 100644 --- a/net/spdy/spdy_session_pool.h +++ b/net/spdy/spdy_session_pool.h
@@ -28,6 +28,7 @@ #include "net/cert/cert_database.h" #include "net/log/net_log_source.h" #include "net/proxy_resolution/proxy_config.h" +#include "net/socket/connect_job.h" #include "net/spdy/http2_push_promise_index.h" #include "net/spdy/server_push_delegate.h" #include "net/spdy/spdy_session_key.h" @@ -231,6 +232,14 @@ std::unique_ptr<SpdySessionRequest>* spdy_session_request, bool* is_blocking_request_for_session); + // Invoked when a host resolution completes. Returns + // OnHostResolutionCallbackResult::kMayBeDeletedAsync if there's a SPDY + // session that's a suitable alias for |key|, setting up the alias if needed. + OnHostResolutionCallbackResult OnHostResolutionComplete( + const SpdySessionKey& key, + bool is_websocket, + const AddressList& addresses); + // Remove all mappings and aliases for the given session, which must // still be available. Except for in tests, this must be called by // the given session itself.
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc index 085e89d..a7fde303 100644 --- a/net/spdy/spdy_test_util_common.cc +++ b/net/spdy/spdy_test_util_common.cc
@@ -495,7 +495,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socket_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( std::make_unique<SSLConfig>() /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); int rv = connection->Init( ClientSocketPool::GroupId(key.host_port_pair(), ClientSocketPool::SocketType::kSsl,
diff --git a/net/tools/quic/quic_simple_server_session_helper_test.cc b/net/tools/quic/quic_simple_server_session_helper_test.cc deleted file mode 100644 index 581b9ae..0000000 --- a/net/tools/quic/quic_simple_server_session_helper_test.cc +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/third_party/quiche/src/quic/core/quic_connection_id.h" -#include "net/third_party/quiche/src/quic/core/quic_utils.h" -#include "net/third_party/quiche/src/quic/test_tools/mock_random.h" -#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" -#include "net/third_party/quiche/src/quic/tools/quic_simple_crypto_server_stream_helper.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { - -TEST(QuicSimpleCryptoServerStreamHelperTest, GenerateConnectionIdForReject) { - quic::test::MockRandom random; - quic::QuicSimpleCryptoServerStreamHelper helper(&random); - - EXPECT_EQ(quic::QuicUtils::CreateRandomConnectionId(&random), - // N.B., version number and ID are ignored in the helper. - helper.GenerateConnectionIdForReject( - quic::QUIC_VERSION_46, quic::test::TestConnectionId(42))); -} - -} // namespace net
diff --git a/net/websockets/websocket_basic_stream_adapters_test.cc b/net/websockets/websocket_basic_stream_adapters_test.cc index 02403680..18a3cedd 100644 --- a/net/websockets/websocket_basic_stream_adapters_test.cc +++ b/net/websockets/websocket_basic_stream_adapters_test.cc
@@ -22,7 +22,6 @@ #include "net/http/http_network_session.h" #include "net/log/net_log_with_source.h" #include "net/socket/client_socket_handle.h" -#include "net/socket/client_socket_pool_manager_impl.h" #include "net/socket/connect_job.h" #include "net/socket/socket_tag.h" #include "net/socket/socket_test_util.h" @@ -56,41 +55,10 @@ protected: WebSocketClientSocketHandleAdapterTest() : host_port_pair_("www.example.org", 443), - socket_pool_manager_(std::make_unique<ClientSocketPoolManagerImpl>( - CommonConnectJobParams( - &socket_factory_, - &host_resolver, - nullptr /* http_auth_cache */, - nullptr /* http_auth_handler_factory */, - nullptr /* spdy_session_pool */, - nullptr /* quic_supported_versions */, - nullptr /* quic_stream_factory */, - nullptr /* proxy_delegate */, - nullptr /* http_user_agent_settings */, - SSLClientSocketContext(), - SSLClientSocketContext(), - nullptr /* socket_performance_watcher_factory */, - nullptr /* network_quality_estimator */, - net_log_.net_log(), - nullptr /* websocket_endpoint_lock_manager */), - CommonConnectJobParams( - &socket_factory_, - &host_resolver, - nullptr /* http_auth_cache */, - nullptr /* http_auth_handler_factory */, - nullptr /* spdy_session_pool */, - nullptr /* quic_supported_versions */, - nullptr /* quic_stream_factory */, - nullptr /* proxy_delegate */, - nullptr /* http_user_agent_settings */, - SSLClientSocketContext(), - SSLClientSocketContext(), - nullptr /* socket_performance_watcher_factory */, - nullptr /* network_quality_estimator */, - net_log_.net_log(), - &websocket_endpoint_lock_manager_), - nullptr /* ssl_config_service */, - HttpNetworkSession::NORMAL_SOCKET_POOL)) {} + network_session_( + SpdySessionDependencies::SpdyCreateSession(&session_deps_)), + websocket_endpoint_lock_manager_( + network_session_->websocket_endpoint_lock_manager()) {} ~WebSocketClientSocketHandleAdapterTest() override = default; @@ -98,7 +66,7 @@ scoped_refptr<ClientSocketPool::SocketParams> socks_params = base::MakeRefCounted<ClientSocketPool::SocketParams>( std::make_unique<SSLConfig>() /* ssl_config_for_origin */, - nullptr /* ssl_config_for_proxy */, OnHostResolutionCallback()); + nullptr /* ssl_config_for_proxy */); TestCompletionCallback callback; int rv = connection->Init( ClientSocketPool::GroupId(host_port_pair_, @@ -107,17 +75,17 @@ socks_params, TRAFFIC_ANNOTATION_FOR_TESTS /* proxy_annotation_tag */, MEDIUM, SocketTag(), ClientSocketPool::RespectLimits::ENABLED, callback.callback(), ClientSocketPool::ProxyAuthCallback(), - socket_pool_manager_->GetSocketPool(ProxyServer::Direct()), net_log_); + network_session_->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL, + ProxyServer::Direct()), + NetLogWithSource()); rv = callback.GetResult(rv); return rv == OK; } const HostPortPair host_port_pair_; - NetLogWithSource net_log_; - MockClientSocketFactory socket_factory_; - MockHostResolver host_resolver; - std::unique_ptr<ClientSocketPoolManagerImpl> socket_pool_manager_; - WebSocketEndpointLockManager websocket_endpoint_lock_manager_; + SpdySessionDependencies session_deps_; + std::unique_ptr<HttpNetworkSession> network_session_; + WebSocketEndpointLockManager* websocket_endpoint_lock_manager_; }; TEST_F(WebSocketClientSocketHandleAdapterTest, Uninitialized) { @@ -128,9 +96,9 @@ TEST_F(WebSocketClientSocketHandleAdapterTest, IsInitialized) { StaticSocketDataProvider data; - socket_factory_.AddSocketDataProvider(&data); + session_deps_.socket_factory->AddSocketDataProvider(&data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); - socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data); + session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); auto connection = std::make_unique<ClientSocketHandle>(); ClientSocketHandle* const connection_ptr = connection.get(); @@ -145,9 +113,9 @@ TEST_F(WebSocketClientSocketHandleAdapterTest, Disconnect) { StaticSocketDataProvider data; - socket_factory_.AddSocketDataProvider(&data); + session_deps_.socket_factory->AddSocketDataProvider(&data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); - socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data); + session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); auto connection = std::make_unique<ClientSocketHandle>(); EXPECT_TRUE(InitClientSocketHandle(connection.get())); @@ -165,9 +133,9 @@ TEST_F(WebSocketClientSocketHandleAdapterTest, Read) { MockRead reads[] = {MockRead(SYNCHRONOUS, "foo"), MockRead("bar")}; StaticSocketDataProvider data(reads, base::span<MockWrite>()); - socket_factory_.AddSocketDataProvider(&data); + session_deps_.socket_factory->AddSocketDataProvider(&data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); - socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data); + session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); auto connection = std::make_unique<ClientSocketHandle>(); EXPECT_TRUE(InitClientSocketHandle(connection.get())); @@ -196,9 +164,9 @@ TEST_F(WebSocketClientSocketHandleAdapterTest, ReadIntoSmallBuffer) { MockRead reads[] = {MockRead(SYNCHRONOUS, "foo"), MockRead("bar")}; StaticSocketDataProvider data(reads, base::span<MockWrite>()); - socket_factory_.AddSocketDataProvider(&data); + session_deps_.socket_factory->AddSocketDataProvider(&data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); - socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data); + session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); auto connection = std::make_unique<ClientSocketHandle>(); EXPECT_TRUE(InitClientSocketHandle(connection.get())); @@ -235,9 +203,9 @@ TEST_F(WebSocketClientSocketHandleAdapterTest, Write) { MockWrite writes[] = {MockWrite(SYNCHRONOUS, "foo"), MockWrite("bar")}; StaticSocketDataProvider data(base::span<MockRead>(), writes); - socket_factory_.AddSocketDataProvider(&data); + session_deps_.socket_factory->AddSocketDataProvider(&data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); - socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data); + session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); auto connection = std::make_unique<ClientSocketHandle>(); EXPECT_TRUE(InitClientSocketHandle(connection.get())); @@ -269,9 +237,9 @@ MockRead reads[] = {MockRead("foobar")}; MockWrite writes[] = {MockWrite("baz")}; StaticSocketDataProvider data(reads, writes); - socket_factory_.AddSocketDataProvider(&data); + session_deps_.socket_factory->AddSocketDataProvider(&data); SSLSocketDataProvider ssl_socket_data(ASYNC, OK); - socket_factory_.AddSSLSocketDataProvider(&ssl_socket_data); + session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); auto connection = std::make_unique<ClientSocketHandle>(); EXPECT_TRUE(InitClientSocketHandle(connection.get())); @@ -345,18 +313,17 @@ } base::WeakPtr<SpdySession> CreateSpdySession() { - return ::net::CreateSpdySession(session_.get(), key_, net_log_); + return ::net::CreateSpdySession(session_.get(), key_, NetLogWithSource()); } base::WeakPtr<SpdyStream> CreateSpdyStream( base::WeakPtr<SpdySession> session) { return CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM, session, url_, - LOWEST, net_log_); + LOWEST, NetLogWithSource()); } SpdyTestUtil spdy_util_; StrictMock<MockDelegate> mock_delegate_; - NetLogWithSource net_log_; private: const GURL url_; @@ -375,7 +342,8 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); - WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, net_log_); + WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, + NetLogWithSource()); EXPECT_TRUE(adapter.is_initialized()); base::RunLoop().RunUntilIdle(); @@ -408,7 +376,8 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); - WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, net_log_); + WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, + NetLogWithSource()); EXPECT_TRUE(adapter.is_initialized()); int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND); @@ -449,7 +418,8 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); - WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, net_log_); + WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, + NetLogWithSource()); EXPECT_TRUE(adapter.is_initialized()); int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND); @@ -491,7 +461,8 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); - WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, net_log_); + WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, + NetLogWithSource()); EXPECT_TRUE(adapter.is_initialized()); int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND); @@ -522,7 +493,8 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); - WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, net_log_); + WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, + NetLogWithSource()); EXPECT_TRUE(adapter.is_initialized()); EXPECT_TRUE(session); @@ -550,7 +522,8 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); - WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, net_log_); + WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, + NetLogWithSource()); EXPECT_TRUE(adapter.is_initialized()); int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND); @@ -585,7 +558,8 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); - WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, net_log_); + WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, + NetLogWithSource()); EXPECT_TRUE(adapter.is_initialized()); int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND); @@ -615,7 +589,8 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); - WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, net_log_); + WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, + NetLogWithSource()); EXPECT_TRUE(adapter.is_initialized()); // No Delegate methods shall be called after this. @@ -660,7 +635,8 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); - WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, net_log_); + WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, + NetLogWithSource()); EXPECT_TRUE(adapter.is_initialized()); int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND); @@ -728,7 +704,8 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); - WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, net_log_); + WebSocketSpdyStreamAdapter adapter(stream, &mock_delegate_, + NetLogWithSource()); EXPECT_TRUE(adapter.is_initialized()); int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND); @@ -784,7 +761,7 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); - WebSocketSpdyStreamAdapter adapter(stream, nullptr, net_log_); + WebSocketSpdyStreamAdapter adapter(stream, nullptr, NetLogWithSource()); EXPECT_TRUE(adapter.is_initialized()); int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND); @@ -829,7 +806,7 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); - WebSocketSpdyStreamAdapter adapter(stream, nullptr, net_log_); + WebSocketSpdyStreamAdapter adapter(stream, nullptr, NetLogWithSource()); EXPECT_TRUE(adapter.is_initialized()); int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND); @@ -903,7 +880,7 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); auto adapter = std::make_unique<WebSocketSpdyStreamAdapter>( - stream, &mock_delegate_, net_log_); + stream, &mock_delegate_, NetLogWithSource()); EXPECT_TRUE(adapter->is_initialized()); int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND); @@ -953,7 +930,7 @@ base::WeakPtr<SpdySession> session = CreateSpdySession(); base::WeakPtr<SpdyStream> stream = CreateSpdyStream(session); auto adapter = std::make_unique<WebSocketSpdyStreamAdapter>( - stream, &mock_delegate_, net_log_); + stream, &mock_delegate_, NetLogWithSource()); EXPECT_TRUE(adapter->is_initialized()); int rv = stream->SendRequestHeaders(RequestHeaders(), MORE_DATA_TO_SEND);
diff --git a/services/network/public/cpp/network_connection_tracker.h b/services/network/public/cpp/network_connection_tracker.h index be9fd29d..e338ac5 100644 --- a/services/network/public/cpp/network_connection_tracker.h +++ b/services/network/public/cpp/network_connection_tracker.h
@@ -65,7 +65,8 @@ // will contain the current connection type, and |callback| will not be // called; Otherwise, returns false and does not modify |type|, in which // case, |callback| will be called on the calling thread when connection type - // is ready. This method is thread safe. Please also refer to + // is ready. The connection type being available does not imply it is not + // CONNECTION_UNKNKOWN. This method is thread safe. Please also refer to // net::NetworkChangeNotifier::GetConnectionType() for documentation. virtual bool GetConnectionType(network::mojom::ConnectionType* type, ConnectionTypeCallback callback);
diff --git a/skia/BUILD.gn b/skia/BUILD.gn index 789762f..a84a225 100644 --- a/skia/BUILD.gn +++ b/skia/BUILD.gn
@@ -408,7 +408,7 @@ ] } - if (is_linux || is_android || is_fuchsia) { + if (is_linux || is_android) { sources += [ # Retain the files for the SkFontMgr_Android on linux to emulate android # fonts. See content/zygote/zygote_main_linux.cc @@ -434,6 +434,9 @@ ] deps += [ "//third_party/fuchsia-sdk/sdk:fonts", + "//third_party/fuchsia-sdk/sdk:fonts", + "//third_party/fuchsia-sdk/sdk:io", + "//third_party/fuchsia-sdk/sdk:sys", "//third_party/fuchsia-sdk/sdk:zx", "//third_party/icu:icuuc", ] @@ -749,7 +752,7 @@ } # Font copies. -if (is_android || is_fuchsia) { +if (is_android) { copy("copy_android_fonts_config") { sources = [ "ext/data/test_fonts/android_fallback_fonts.xml", @@ -760,6 +763,16 @@ ] } } +if (is_fuchsia) { + copy("copy_fuchsia_fonts_manifest") { + sources = [ + "ext/data/test_fonts/fuchsia_test_fonts_manifest.json", + ] + outputs = [ + "$root_out_dir/test_fonts/{{source_file_part}}", + ] + } +} if (is_mac) { bundle_data("test_fonts_bundle_data") { public_deps = [ @@ -789,10 +802,14 @@ data_deps += [ "//third_party/test_fonts" ] } - if (is_android || is_fuchsia) { + if (is_android) { deps += [ ":copy_android_fonts_config" ] data_deps += [ ":copy_android_fonts_config" ] } + if (is_fuchsia) { + deps += [ ":copy_fuchsia_fonts_manifest" ] + data_deps += [ ":copy_fuchsia_fonts_manifest" ] + } } source_set("test_fonts") {
diff --git a/skia/ext/data/test_fonts/fuchsia_test_fonts_manifest.json b/skia/ext/data/test_fonts/fuchsia_test_fonts_manifest.json new file mode 100644 index 0000000..bc6962af --- /dev/null +++ b/skia/ext/data/test_fonts/fuchsia_test_fonts_manifest.json
@@ -0,0 +1,205 @@ +{ + "families": [ + { + "family": "Arimo", + "aliases": [ + "sans", + "sans serif", + "sans-serif", + "Arial", + "Helvetica" + ], + "fallback": true, + "fallback_group": "sans-serif", + "fonts": [ + { + "asset": "Arimo-Regular.ttf" + }, + { + "asset": "Arimo-Bold.ttf", + "weight": 700 + }, + { + "asset": "Arimo-Italic.ttf", + "slant": "italic" + }, + { + "asset": "Arimo-BoldItalic.ttf", + "weight": 700, + "slant": "italic" + } + ] + }, + { + "family": "Tinos", + "aliases": [ + "serif", + "Times", + "Times New Roman", + "Monaco", + "SubpixelPositioning" + ], + "fallback": true, + "fallback_group": "serif", + "fonts": [ + { + "asset": "Tinos-Regular.ttf" + }, + { + "asset": "Tinos-Bold.ttf", + "weight": 700 + }, + { + "asset": "Tinos-Italic.ttf", + "slant": "italic" + }, + { + "asset": "Tinos-BoldItalic.ttf", + "weight": 700, + "slant": "italic" + } + ] + }, + { + "family": "Cousine", + "aliases": [ + "mono", + "monospace", + "Courier", + "Courier New" + ], + "fallback": true, + "fallback_group": "monospace", + "fonts": [ + { + "asset": "Cousine-Regular.ttf" + }, + { + "asset": "Cousine-Bold.ttf", + "weight": 700 + }, + { + "asset": "Cousine-Italic.ttf", + "slant": "italic" + }, + { + "asset": "Cousine-BoldItalic.ttf", + "weight": 700, + "slant": "italic" + } + ] + }, + { + "family": "Gelasio", + "aliases": [ + "Georgia" + ], + "fallback": false, + "fonts": [ + { + "asset": "Gelasio-Regular.ttf" + }, + { + "asset": "Gelasio-Bold.ttf", + "weight": 700 + }, + { + "asset": "Gelasio-Italic.ttf", + "slant": "italic" + }, + { + "asset": "Gelasio-BoldItalic.ttf", + "weight": 700, + "slant": "italic" + } + ] + }, + { + "family": "Ahem", + "aliases": [ + "SubpixelPositioningAhem" + ], + "fallback": false, + "fallback_group": "sans-serif", + "fonts": [ + { + "asset": "Ahem.ttf" + } + ] + }, + { + "family": "DejaVu Sans", + "fallback": true, + "fonts": [ + { + "asset": "DejaVuSans.ttf" + } + ] + }, + { + "family": "Garuda", + "fallback": true, + "fonts": [ + { + "asset": "Garuda.ttf" + } + ] + }, + { + "family": "Lohit Devanagari", + "fallback": true, + "fonts": [ + { + "asset": "Lohit-Devanagari.ttf" + } + ] + }, + { + "family": "Lohit Gurmukhi", + "fallback": true, + "fonts": [ + { + "asset": "Lohit-Gurmukhi.ttf" + } + ] + }, + { + "family": "Lohit Tamil", + "fallback": true, + "fonts": [ + { + "asset": "Lohit-Tamil.ttf" + } + ] + }, + { + "family": "Mukti", + "fallback": true, + "fonts": [ + { + "asset": "MuktiNarrow.ttf" + } + ] + }, + { + "family": "Noto Sans Khmer", + "fallback": true, + "fonts": [ + { + "asset": "NotoSansKhmer-Regular.ttf", + "language": "km" + } + ] + }, + { + "family": "Noto Sans CJK JP", + "fallback": true, + "fonts": [ + { + "asset": "NotoSansCJKjp-Regular.otf", + "language": "ja" + } + ] + } + ] +} \ No newline at end of file
diff --git a/skia/ext/test_fonts_fuchsia.cc b/skia/ext/test_fonts_fuchsia.cc index 073ff91..d277605 100644 --- a/skia/ext/test_fonts_fuchsia.cc +++ b/skia/ext/test_fonts_fuchsia.cc
@@ -4,25 +4,63 @@ #include "skia/ext/test_fonts.h" +#include <fuchsia/fonts/cpp/fidl.h> +#include <fuchsia/io/cpp/fidl.h> +#include <fuchsia/sys/cpp/fidl.h> +#include <lib/fidl/cpp/interface_handle.h> + +#include "base/fuchsia/file_utils.h" +#include "base/fuchsia/service_directory_client.h" +#include "base/no_destructor.h" +#include "base/path_service.h" #include "skia/ext/fontmgr_default.h" #include "third_party/skia/include/core/SkFontMgr.h" -#include "third_party/skia/include/ports/SkFontMgr_android.h" +#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h" namespace skia { void ConfigureTestFont() { - // TODO(https://crbugs.com/927980): Use SkFontMgr_Fuchsia instead of - // SkFontMgr_Android. We just need to start a fonts::Provider instance with - // a custom manifest files and connect to it instead of the default - // fonts::Provider instance.. - SkFontMgr_Android_CustomFonts custom; - custom.fSystemFontUse = SkFontMgr_Android_CustomFonts::kOnlyCustom; - custom.fBasePath = "/pkg/test_fonts/"; - custom.fFontsXml = "/pkg/test_fonts/android_main_fonts.xml"; - custom.fFallbackFontsXml = "/pkg/test_fonts/android_fallback_fonts.xml"; - custom.fIsolated = false; + // ComponentController for the font provider service started below. It's a + // static field to keep the service running until the test process is + // destroyed. + static base::NoDestructor< + fidl::InterfaceHandle<fuchsia::sys::ComponentController>> + test_font_provider_controller; + DCHECK(!*test_font_provider_controller); - skia::OverrideDefaultSkFontMgr(SkFontMgr_New_Android(&custom)); + // Start a fuchsia.fonts.Provider instance and configure it to load the test + // fonts, which must be bundled in the calling process' package. + fuchsia::sys::LaunchInfo launch_info; + launch_info.url = "fuchsia-pkg://fuchsia.com/fonts#meta/fonts.cmx"; + launch_info.arguments.reset( + {"--no-default-fonts", + "--font-manifest=/test_fonts/fuchsia_test_fonts_manifest.json"}); + launch_info.flat_namespace = fuchsia::sys::FlatNamespace::New(); + launch_info.flat_namespace->paths.push_back("/test_fonts"); + + base::FilePath assets_path; + if (!base::PathService::Get(base::DIR_ASSETS, &assets_path)) + LOG(FATAL) << "Can't get DIR_ASSETS"; + launch_info.flat_namespace->directories.push_back( + base::fuchsia::OpenDirectory(assets_path.AppendASCII("test_fonts")) + .TakeChannel()); + + fidl::InterfaceHandle<fuchsia::io::Directory> font_provider_services_dir; + launch_info.directory_request = + font_provider_services_dir.NewRequest().TakeChannel(); + + fuchsia::sys::LauncherSyncPtr launcher = + base::fuchsia::ServiceDirectoryClient::ForCurrentProcess() + ->ConnectToServiceSync<fuchsia::sys::Launcher>(); + launcher->CreateComponent(std::move(launch_info), + test_font_provider_controller->NewRequest()); + + base::fuchsia::ServiceDirectoryClient font_provider_services_client( + std::move(font_provider_services_dir)); + + skia::OverrideDefaultSkFontMgr(SkFontMgr_New_Fuchsia( + font_provider_services_client + .ConnectToServiceSync<fuchsia::fonts::Provider>())); } } // namespace skia
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json index f8f5a020..0723d16b 100644 --- a/testing/buildbot/chromium.android.fyi.json +++ b/testing/buildbot/chromium.android.fyi.json
@@ -1,680 +1,6 @@ { "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {}, "AAAAA2 See generate_buildbot_json.py to make changes": {}, - "Android Cronet Builder (dbg)": { - "gtest_tests": [ - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cronet_sample_test_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_sample_test_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cronet_smoketests_missing_native_library_instrumentation_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_smoketests_missing_native_library_instrumentation_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cronet_smoketests_platform_only_instrumentation_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_smoketests_platform_only_instrumentation_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cronet_test_instrumentation_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_test_instrumentation_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cronet_tests_android" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_tests_android" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cronet_unittests_android" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_unittests_android" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "net_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ], - "shards": 4 - }, - "test": "net_unittests" - } - ] - }, - "Android Cronet Builder Asan": { - "gtest_tests": [ - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "cronet_sample_test_apk" - }, - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "cronet_smoketests_missing_native_library_instrumentation_apk" - }, - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "cronet_smoketests_platform_only_instrumentation_apk" - }, - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "cronet_test_instrumentation_apk" - }, - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "cronet_tests_android" - }, - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "cronet_unittests_android" - }, - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "net_unittests" - } - ] - }, - "Android Cronet KitKat Builder": { - "gtest_tests": [ - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cronet_sample_test_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_sample_test_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cronet_smoketests_missing_native_library_instrumentation_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_smoketests_missing_native_library_instrumentation_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cronet_smoketests_platform_only_instrumentation_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_smoketests_platform_only_instrumentation_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cronet_test_instrumentation_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_test_instrumentation_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cronet_tests_android" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_tests_android" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cronet_unittests_android" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_unittests_android" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "net_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ], - "shards": 4 - }, - "test": "net_unittests" - } - ], - "scripts": [ - { - "args": [ - "--platform", - "android-cronet", - "--perf-id", - "android_cronet_builder", - "cronet-arm/sizes" - ], - "name": "sizes", - "override_compile_targets": [ - "cronet" - ], - "script": "cronet_sizes.py" - } - ] - }, "Android WebView O NetworkService (dbg)": { "gtest_tests": [ {
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json index 263e6b2..c356e48 100644 --- a/testing/buildbot/chromium.android.json +++ b/testing/buildbot/chromium.android.json
@@ -6,543 +6,6 @@ "all" ] }, - "Android Cronet Builder": { - "additional_compile_targets": [ - "cronet_package" - ] - }, - "Android Cronet Builder (dbg)": { - "gtest_tests": [ - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_sample_test_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_smoketests_missing_native_library_instrumentation_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_smoketests_platform_only_instrumentation_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_test_instrumentation_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_tests_android" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_unittests_android" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ], - "shards": 4 - }, - "test": "net_unittests" - } - ] - }, - "Android Cronet Builder Asan": { - "gtest_tests": [ - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "cronet_sample_test_apk" - }, - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "cronet_smoketests_missing_native_library_instrumentation_apk" - }, - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "cronet_smoketests_platform_only_instrumentation_apk" - }, - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "cronet_test_instrumentation_apk" - }, - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "cronet_tests_android" - }, - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "cronet_unittests_android" - }, - { - "swarming": { - "can_use_on_swarming_builders": false - }, - "test": "net_unittests" - } - ] - }, - "Android Cronet KitKat Builder": { - "gtest_tests": [ - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_sample_test_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_smoketests_missing_native_library_instrumentation_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_smoketests_platform_only_instrumentation_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_test_instrumentation_apk" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_tests_android" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ] - }, - "test": "cronet_unittests_android" - }, - { - "args": [ - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "swarming": { - "can_use_on_swarming_builders": true, - "cipd_packages": [ - { - "cipd_package": "infra/tools/luci/logdog/butler/${platform}", - "location": "bin", - "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c" - } - ], - "dimension_sets": [ - { - "device_os": "KTU84P", - "device_type": "hammerhead", - "os": "Android" - } - ], - "output_links": [ - { - "link": [ - "https://luci-logdog.appspot.com/v/?s", - "=android%2Fswarming%2Flogcats%2F", - "${TASK_ID}%2F%2B%2Funified_logcats" - ], - "name": "shard #${SHARD_INDEX} logcats" - } - ], - "shards": 4 - }, - "test": "net_unittests" - } - ] - }, "Android WebView L (dbg)": { "gtest_tests": [ {
diff --git a/testing/buildbot/filters/fuchsia.content_unittests.filter b/testing/buildbot/filters/fuchsia.content_unittests.filter index d64c8f38..c93a056 100644 --- a/testing/buildbot/filters/fuchsia.content_unittests.filter +++ b/testing/buildbot/filters/fuchsia.content_unittests.filter
@@ -61,6 +61,7 @@ # Flaky, https://crbug.com/884250. -ReferrerSanitizerTest.SanitizesPolicyForNonEmptyReferrers -# This test requires OSExchangeDataProviderFactory::CreateProvider to +# These tests require OSExchangeDataProviderFactory::CreateProvider to # be implemented: https://crbug.com/750934. -WebContentsViewAuraTest.DragDropFiles +-WebContentsViewAuraTest.DragDropFilesOriginateFromRenderer
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl index d72385b..2ae803d8 100644 --- a/testing/buildbot/test_suite_exceptions.pyl +++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1147,19 +1147,6 @@ }, 'sizes': { 'modifications': { - # chromium.android.fyi - 'Android Cronet KitKat Builder': { - 'args': [ - '--platform', - 'android-cronet', - '--perf-id', - 'android_cronet_builder', - 'cronet-arm/sizes', - ], - 'override_compile_targets': [ - 'cronet', - ], - }, 'android-cronet-arm-dbg': { 'args': [ '--platform', 'android-cronet',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl index 1e2526b..5c6a151 100644 --- a/testing/buildbot/waterfalls.pyl +++ b/testing/buildbot/waterfalls.pyl
@@ -156,59 +156,6 @@ 'all', ], }, - 'Android Cronet Builder': { - 'additional_compile_targets': [ - 'cronet_package', - ], - }, - 'Android Cronet Builder (dbg)': { - 'test_suites': { - 'gtest_tests': 'cronet_gtests', - }, - 'swarming': { - 'dimension_sets': [ - { - 'device_os': 'KTU84P', - 'device_type': 'hammerhead', - 'os': 'Android', - }, - ], - }, - 'os_type': 'android', - 'skip_merge_script': True, - }, - 'Android Cronet Builder Asan': { - 'test_suites': { - 'gtest_tests': 'cronet_gtests', - }, - 'use_swarming': False, - 'swarming': { - 'dimension_sets': [ - { - 'device_os': 'KTU84P', - 'device_type': 'hammerhead', - 'os': 'Android', - }, - ], - }, - 'os_type': 'android', - }, - 'Android Cronet KitKat Builder': { - 'test_suites': { - 'gtest_tests': 'cronet_gtests', - }, - 'swarming': { - 'dimension_sets': [ - { - 'device_os': 'KTU84P', - 'device_type': 'hammerhead', - 'os': 'Android', - }, - ], - }, - 'os_type': 'android', - 'skip_merge_script': True, - }, 'Android WebView L (dbg)': { 'test_suites': { 'gtest_tests': 'webview_bot_gtests', @@ -660,44 +607,6 @@ { 'name': 'chromium.android.fyi', 'machines': { - 'Android Cronet Builder (dbg)': { - 'test_suites': { - 'gtest_tests': 'cronet_gtests', - }, - 'swarming': { - 'dimension_sets': [ - { - 'device_os': 'KTU84P', - 'device_type': 'hammerhead', - 'os': 'Android', - }, - ], - }, - 'os_type': 'android', - }, - 'Android Cronet Builder Asan': { - 'test_suites': { - 'gtest_tests': 'cronet_gtests', - }, - 'os_type': 'android', - 'use_swarming': False, - }, - 'Android Cronet KitKat Builder': { - 'test_suites': { - 'gtest_tests': 'cronet_gtests', - 'scripts': 'cronet_scripts', - }, - 'swarming': { - 'dimension_sets': [ - { - 'device_os': 'KTU84P', - 'device_type': 'hammerhead', - 'os': 'Android', - }, - ], - }, - 'os_type': 'android', - }, 'Android WebView O NetworkService (dbg)': { 'test_suites': { 'gtest_tests': 'webview_cts_tests_gtest',
diff --git a/third_party/blink/public/mojom/credentialmanager/credential_manager.mojom b/third_party/blink/public/mojom/credentialmanager/credential_manager.mojom index 405998d..e22ed21 100644 --- a/third_party/blink/public/mojom/credentialmanager/credential_manager.mojom +++ b/third_party/blink/public/mojom/credentialmanager/credential_manager.mojom
@@ -31,6 +31,7 @@ NOT_IMPLEMENTED, NOT_FOCUSED, RESIDENT_CREDENTIALS_UNSUPPORTED, + PROTECTION_POLICY_INCONSISTENT, ANDROID_ALGORITHM_UNSUPPORTED, ANDROID_EMPTY_ALLOW_CREDENTIALS, ANDROID_NOT_SUPPORTED_ERROR,
diff --git a/third_party/blink/public/mojom/webauthn/authenticator.mojom b/third_party/blink/public/mojom/webauthn/authenticator.mojom index 155ff22b..2aff731c 100644 --- a/third_party/blink/public/mojom/webauthn/authenticator.mojom +++ b/third_party/blink/public/mojom/webauthn/authenticator.mojom
@@ -26,6 +26,7 @@ ALGORITHM_UNSUPPORTED, EMPTY_ALLOW_CREDENTIALS, ANDROID_NOT_SUPPORTED_ERROR, + PROTECTION_POLICY_INCONSISTENT, UNKNOWN_ERROR, }; @@ -251,6 +252,14 @@ CROSS_PLATFORM, }; +enum ProtectionPolicy { + // UNSPECIFIED means that no value was given at the Javascript level. + UNSPECIFIED, + NONE, + UV_OR_CRED_ID_REQUIRED, + UV_REQUIRED, +}; + // See https://w3c.github.io/webauthn/#dictdef-authenticatorselectioncriteria. struct AuthenticatorSelectionCriteria { // Filter authenticators by attachment type. @@ -313,6 +322,13 @@ // the RP. See https://w3c.github.io/webauthn/#sctn-uvm-extension [EnableIf=is_android] bool user_verification_methods; + + // The value of the `credentialProtectionPolicy` extension, or UNSPECIFIED if + // none was provided. + ProtectionPolicy protection_policy; + // The value of the `enforceCredentialProtectionPolicy`, or false if none was + // provided. + bool enforce_protection_policy; }; enum PublicKeyCredentialType {
diff --git a/third_party/blink/public/platform/DEPS b/third_party/blink/public/platform/DEPS index cafa3e7d..14ffffe4 100644 --- a/third_party/blink/public/platform/DEPS +++ b/third_party/blink/public/platform/DEPS
@@ -23,7 +23,7 @@ "+build/build_config.h", "+cc", "+components/viz/common", - "+media/base/video_rotation.h", + "+media/base/video_transformation.h", "+mojo/public", "+net/cert", "+net/http",
diff --git a/third_party/blink/renderer/core/accessibility/apply_dark_mode.cc b/third_party/blink/renderer/core/accessibility/apply_dark_mode.cc index 8166679d..1be8571 100644 --- a/third_party/blink/renderer/core/accessibility/apply_dark_mode.cc +++ b/third_party/blink/renderer/core/accessibility/apply_dark_mode.cc
@@ -53,6 +53,7 @@ dark_mode_settings.grayscale = frame_settings.GetDarkModeGrayscale(); dark_mode_settings.contrast = frame_settings.GetDarkModeContrast(); dark_mode_settings.image_policy = frame_settings.GetDarkModeImagePolicy(); + dark_mode_settings.image_style = frame_settings.GetDarkModeImageStyle(); return dark_mode_settings; }
diff --git a/third_party/blink/renderer/core/execution_context/security_context.cc b/third_party/blink/renderer/core/execution_context/security_context.cc index f1bf023a..afee13a1 100644 --- a/third_party/blink/renderer/core/execution_context/security_context.cc +++ b/third_party/blink/renderer/core/execution_context/security_context.cc
@@ -37,14 +37,14 @@ namespace { -// Bucketize image compression into percentage in the following fashion: -// if an image's compression ratio is 0.1, it will be represented as 1 percent -// if an image's compression ratio is 5, it will be represented as 50 percents. -int BucketizeCompressionRatio(double compression_ratio) { - int compression_ratio_percent = 10 * compression_ratio; - if (compression_ratio_percent < 0) +// Bucketize image metrics into percentage in the following fashion: +// if an image's metrics is 0.1, it will be represented as 1 percent +// if an image's metrics is 5, it will be represented as 50 percents. +int BucketizeImageMetrics(double ratio) { + int ratio_percent = 10 * ratio; + if (ratio_percent < 0) return 0; - return compression_ratio_percent > 100 ? 100 : compression_ratio_percent; + return ratio_percent > 100 ? 100 : ratio_percent; } inline const char* GetImagePolicyHistogramName( @@ -56,6 +56,8 @@ return "Blink.UseCounter.FeaturePolicy.LosslessImageCompression"; case mojom::FeaturePolicyFeature::kUnoptimizedLosslessImagesStrict: return "Blink.UseCounter.FeaturePolicy.StrictLosslessImageCompression"; + case mojom::FeaturePolicyFeature::kOversizedImages: + return "Blink.UseCounter.FeaturePolicy.ImageDownscalingRatio"; default: NOTREACHED(); break; @@ -286,11 +288,11 @@ // properly inherit the parent policy. DCHECK(feature_policy_); - // Log metrics for unoptimized-*-images policies. - if (feature == mojom::FeaturePolicyFeature::kUnoptimizedLossyImages || - feature == mojom::FeaturePolicyFeature::kUnoptimizedLosslessImages || - feature == - mojom::FeaturePolicyFeature::kUnoptimizedLosslessImagesStrict) { + // Log metrics for unoptimized-*-images and oversized-images policies. + if ((feature >= mojom::FeaturePolicyFeature::kUnoptimizedLossyImages && + feature <= + mojom::FeaturePolicyFeature::kUnoptimizedLosslessImagesStrict) || + feature == mojom::FeaturePolicyFeature::kOversizedImages) { // Only log metrics if an image policy is specified. // If an image policy is specified, the policy value would be less than the // max value, otherwise by default the policy value is set to be the max @@ -304,7 +306,7 @@ static_cast<int>( mojom::FeaturePolicyFeature::kUnoptimizedLosslessImagesStrict) + 1, - Add(BucketizeCompressionRatio(threshold_value.DoubleValue())), + Add(BucketizeImageMetrics(threshold_value.DoubleValue())), base::LinearHistogram::FactoryGet( GetImagePolicyHistogramName(feature), 0, 100, 101, 0x1)); }
diff --git a/third_party/blink/renderer/core/exported/web_layer_test.cc b/third_party/blink/renderer/core/exported/web_layer_test.cc index b8301003..47cf3fc 100644 --- a/third_party/blink/renderer/core/exported/web_layer_test.cc +++ b/third_party/blink/renderer/core/exported/web_layer_test.cc
@@ -904,7 +904,7 @@ auto effect_tree_index = outer_element_layer->effect_tree_index(); auto* effect_node = GetPropertyTrees()->effect_tree.Node(effect_tree_index); EXPECT_EQ(effect_node->opacity, 0.5f); - EXPECT_FALSE(effect_node->has_render_surface); + EXPECT_FALSE(effect_node->HasRenderSurface()); } } // namespace blink
diff --git a/third_party/blink/renderer/core/frame/settings.json5 b/third_party/blink/renderer/core/frame/settings.json5 index 2247bd690..7acc674 100644 --- a/third_party/blink/renderer/core/frame/settings.json5 +++ b/third_party/blink/renderer/core/frame/settings.json5
@@ -913,6 +913,12 @@ invalidate: "Paint", }, { + name: "darkModeImageStyle", + initial: "DarkModeImageStyle::kDefault", + type: "DarkModeImageStyle", + invalidate: "Paint", + }, + { name: "navigatorPlatformOverride", type: "String", },
diff --git a/third_party/blink/renderer/core/html/forms/range_input_type.cc b/third_party/blink/renderer/core/html/forms/range_input_type.cc index 916b61a2..e3d19561 100644 --- a/third_party/blink/renderer/core/html/forms/range_input_type.cc +++ b/third_party/blink/renderer/core/html/forms/range_input_type.cc
@@ -332,7 +332,7 @@ } inline SliderThumbElement* RangeInputType::GetSliderThumbElement() const { - return ToSliderThumbElementOrDie( + return To<SliderThumbElement>( GetElement().UserAgentShadowRoot()->getElementById( shadow_element_names::SliderThumb())); }
diff --git a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc index 051cdeb..01b7be2 100644 --- a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc +++ b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
@@ -392,7 +392,7 @@ } TouchList* touches = event->targetTouches(); - SliderThumbElement* thumb = ToSliderThumbElement( + auto* thumb = To<SliderThumbElement>( GetTreeScope().getElementById(shadow_element_names::SliderThumb())); if (!thumb || !touches) return;
diff --git a/third_party/blink/renderer/core/html/forms/slider_thumb_element.h b/third_party/blink/renderer/core/html/forms/slider_thumb_element.h index 0423972..f7cdcb8 100644 --- a/third_party/blink/renderer/core/html/forms/slider_thumb_element.h +++ b/third_party/blink/renderer/core/html/forms/slider_thumb_element.h
@@ -33,6 +33,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_SLIDER_THUMB_ELEMENT_H_ #include "third_party/blink/renderer/core/html/html_div_element.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" #include "third_party/blink/renderer/platform/wtf/forward.h" namespace blink { @@ -79,7 +80,15 @@ } // FIXME: There are no ways to check if a node is a SliderThumbElement. -DEFINE_ELEMENT_TYPE_CASTS(SliderThumbElement, IsHTMLElement()); +template <> +inline bool IsElementOfType<const SliderThumbElement>(const Node& node) { + return node.IsHTMLElement(); +} + +template <> +struct DowncastTraits<SliderThumbElement> { + static bool AllowFrom(const Node& node) { return node.IsHTMLElement(); } +}; class SliderContainerElement final : public HTMLDivElement { public:
diff --git a/third_party/blink/renderer/core/layout/custom/pending_layout_registry.cc b/third_party/blink/renderer/core/layout/custom/pending_layout_registry.cc index 68559c90..fe52968 100644 --- a/third_party/blink/renderer/core/layout/custom/pending_layout_registry.cc +++ b/third_party/blink/renderer/core/layout/custom/pending_layout_registry.cc
@@ -26,7 +26,7 @@ const ComputedStyle& style = node->GetLayoutObject()->StyleRef(); if (style.IsDisplayLayoutCustomBox() && style.DisplayLayoutCustomName() == name) - node->LazyReattachIfAttached(); + node->SetForceReattachLayoutTree(); } } }
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc index 4d5f46e..74631c1 100644 --- a/third_party/blink/renderer/core/layout/layout_block_flow_line.cc +++ b/third_party/blink/renderer/core/layout/layout_block_flow_line.cc
@@ -21,6 +21,7 @@ * */ +#include "base/containers/span.h" #include "build/build_config.h" #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" #include "third_party/blink/renderer/core/editing/editing_utilities.h" @@ -56,7 +57,7 @@ unsigned opportunities_in_run; if (text.Is8Bit()) { opportunities_in_run = Character::ExpansionOpportunityCount( - text.Characters8() + run.start_, run.stop_ - run.start_, + {text.Characters8() + run.start_, run.stop_ - run.start_}, run.box_->Direction(), is_after_expansion, text_justify); } else if (run.line_layout_item_.IsCombineText()) { // Justfication applies to before and after the combined text as if @@ -66,7 +67,7 @@ is_after_expansion = true; } else { opportunities_in_run = Character::ExpansionOpportunityCount( - text.Characters16() + run.start_, run.stop_ - run.start_, + {text.Characters16() + run.start_, run.stop_ - run.start_}, run.box_->Direction(), is_after_expansion, text_justify); } runs_with_expansions_.push_back(opportunities_in_run);
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc index 0db5093..9fefcdc 100644 --- a/third_party/blink/renderer/core/layout/layout_box.cc +++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -3142,9 +3142,13 @@ // Reset width so that any percent margins on inline children do not // use it when calculating min/max preferred width. // TODO(crbug.com/710026): Remove const_cast + LayoutUnit w = LogicalWidth(); const_cast<LayoutBox*>(this)->SetLogicalWidth(LayoutUnit()); - return std::max(MinPreferredLogicalWidth(), - std::min(MaxPreferredLogicalWidth(), logical_width_result)); + LayoutUnit result = + std::max(MinPreferredLogicalWidth(), + std::min(MaxPreferredLogicalWidth(), logical_width_result)); + const_cast<LayoutBox*>(this)->SetLogicalWidth(w); + return result; } return logical_width_result; }
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc index 1b0a5ffb..c199ca1 100644 --- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc +++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -533,11 +533,7 @@ // and can just return the already-set logical width. if (!CrossAxisLengthIsDefinite(child, child.StyleRef().LogicalWidth())) { LogicalExtentComputedValues values; - // ComputeLogicalWidth has the side-effect of setting LogicalWidth to 0; - // so we store and re-set it. - LayoutUnit w = child.LogicalWidth(); child.ComputeLogicalWidth(values); - const_cast<LayoutBox&>(child).SetLogicalWidth(w); return values.extent_; }
diff --git a/third_party/blink/renderer/core/layout/layout_slider.cc b/third_party/blink/renderer/core/layout/layout_slider.cc index 4e349a8..db984bd95 100644 --- a/third_party/blink/renderer/core/layout/layout_slider.cc +++ b/third_party/blink/renderer/core/layout/layout_slider.cc
@@ -58,7 +58,7 @@ } inline SliderThumbElement* LayoutSlider::GetSliderThumbElement() const { - return ToSliderThumbElement( + return To<SliderThumbElement>( ToElement(GetNode())->UserAgentShadowRoot()->getElementById( shadow_element_names::SliderThumb())); }
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc index 84263d0..1a9981b 100644 --- a/third_party/blink/renderer/core/loader/resource/image_resource_content.cc +++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.cc
@@ -523,7 +523,23 @@ double compression_ratio_1k = (resource_length - 1024) / pixels; double compression_ratio_10k = (resource_length - 10240) / pixels; - int compression_format = GetCompressionFormat(); + ImageDecoder::CompressionFormat compression_format = GetCompressionFormat(); + const auto max_value = + PolicyValue::CreateMaxPolicyValue(mojom::PolicyValueType::kDecDouble); + // If an unoptimized-*-images policy is specified, the specified compression + // ratio will be less than the max value. + bool is_policy_specified = + !context.IsFeatureEnabled( + mojom::FeaturePolicyFeature::kUnoptimizedLossyImages, max_value) || + !context.IsFeatureEnabled( + mojom::FeaturePolicyFeature::kUnoptimizedLosslessImagesStrict, + max_value) || + !context.IsFeatureEnabled( + mojom::FeaturePolicyFeature::kUnoptimizedLosslessImages, max_value); + if (is_policy_specified) { + UMA_HISTOGRAM_ENUMERATION("Blink.UseCounter.FeaturePolicy.ImageFormats", + compression_format); + } if (compression_format == ImageDecoder::kLossyFormat) { // Enforce the lossy image policy. return context.IsFeatureEnabled(
diff --git a/third_party/blink/renderer/core/paint/image_element_timing.cc b/third_party/blink/renderer/core/paint/image_element_timing.cc index 8a04837..64ac720 100644 --- a/third_party/blink/renderer/core/paint/image_element_timing.cc +++ b/third_party/blink/renderer/core/paint/image_element_timing.cc
@@ -62,7 +62,7 @@ } void ImageElementTiming::NotifyImagePaintedInternal( - const Node* node, + Node* node, const LayoutObject& layout_object, const ImageResourceContent& cached_image, const PropertyTreeState& current_paint_chunk_properties) { @@ -77,7 +77,7 @@ FloatRect intersection_rect = ComputeIntersectionRect( frame, layout_object, current_paint_chunk_properties); - const Element* element = ToElement(node); + Element* element = ToElement(node); const AtomicString attr = element->FastGetAttribute(html_names::kElementtimingAttr); if (!ShouldReportElement(frame, attr, intersection_rect)) @@ -104,7 +104,8 @@ performance->AddElementTiming( AtomicString(url.GetString()), intersection_rect, TimeTicks(), cached_image.LoadResponseEnd(), attr, - cached_image.IntrinsicSize(kDoNotRespectImageOrientation), id); + cached_image.IntrinsicSize(kDoNotRespectImageOrientation), id, + element); } return; } @@ -116,10 +117,10 @@ const String& image_name = url.ProtocolIsData() ? url.GetString().Left(kInlineImageMaxChars) : url.GetString(); - element_timings_.emplace_back( + element_timings_.emplace_back(MakeGarbageCollected<ElementTimingInfo>( AtomicString(image_name), intersection_rect, cached_image.LoadResponseEnd(), attr, - cached_image.IntrinsicSize(kDoNotRespectImageOrientation), id); + cached_image.IntrinsicSize(kDoNotRespectImageOrientation), id, element)); // Only queue a swap promise when |element_timings_| was empty. All of the // records in |element_timings_| will be processed when the promise succeeds // or fails, and at that time the vector is cleared. @@ -132,7 +133,7 @@ } void ImageElementTiming::NotifyBackgroundImagePainted( - const Node* node, + Node* node, const StyleImage* background_image, const PropertyTreeState& current_paint_chunk_properties) { DCHECK(node); @@ -193,9 +194,10 @@ performance->ShouldBufferEntries())) { for (const auto& element_timing : element_timings_) { performance->AddElementTiming( - element_timing.name, element_timing.rect, timestamp, - element_timing.response_end, element_timing.identifier, - element_timing.intrinsic_size, element_timing.id); + element_timing->name, element_timing->rect, timestamp, + element_timing->response_end, element_timing->identifier, + element_timing->intrinsic_size, element_timing->id, + element_timing->element); } } element_timings_.clear(); @@ -212,6 +214,7 @@ } void ImageElementTiming::Trace(blink::Visitor* visitor) { + visitor->Trace(element_timings_); Supplement<LocalDOMWindow>::Trace(visitor); }
diff --git a/third_party/blink/renderer/core/paint/image_element_timing.h b/third_party/blink/renderer/core/paint/image_element_timing.h index ab2ddc6..0be100a 100644 --- a/third_party/blink/renderer/core/paint/image_element_timing.h +++ b/third_party/blink/renderer/core/paint/image_element_timing.h
@@ -43,7 +43,7 @@ const PropertyTreeState& current_paint_chunk_properties); void NotifyBackgroundImagePainted( - const Node*, + Node*, const StyleImage* background_image, const PropertyTreeState& current_paint_chunk_properties); @@ -59,7 +59,7 @@ friend class ImageElementTimingTest; void NotifyImagePaintedInternal( - const Node*, + Node*, const LayoutObject&, const ImageResourceContent& cached_image, const PropertyTreeState& current_paint_chunk_properties); @@ -78,20 +78,27 @@ void ReportImagePaintSwapTime(WebWidgetClient::SwapResult, base::TimeTicks timestamp); - // Struct containing information about image element timing. - struct ElementTimingInfo { + // Class containing information about image element timing. + class ElementTimingInfo + : public GarbageCollectedFinalized<ElementTimingInfo> { + public: ElementTimingInfo(const AtomicString& name, const FloatRect& rect, const TimeTicks& response_end, const AtomicString& identifier, const IntSize& intrinsic_size, - const AtomicString& id) + const AtomicString& id, + Element* element) : name(name), rect(rect), response_end(response_end), identifier(identifier), intrinsic_size(intrinsic_size), - id(id) {} + id(id), + element(element) {} + ~ElementTimingInfo() = default; + + void Trace(blink::Visitor* visitor) { visitor->Trace(element); } AtomicString name; FloatRect rect; @@ -99,10 +106,15 @@ AtomicString identifier; IntSize intrinsic_size; AtomicString id; + Member<Element> element; + + private: + DISALLOW_COPY_AND_ASSIGN(ElementTimingInfo); }; + // Vector containing the element timing infos that will be reported during the // next swap promise callback. - WTF::Vector<ElementTimingInfo> element_timings_; + HeapVector<Member<ElementTimingInfo>> element_timings_; // Hashmap of LayoutObjects for which paint has already been notified. WTF::HashSet<const LayoutObject*> images_notified_; // Hashmap of pairs of elements, background images whose paint has been
diff --git a/third_party/blink/renderer/core/svg/svg_graphics_element.cc b/third_party/blink/renderer/core/svg/svg_graphics_element.cc index 4cc5fa1..4ef3fc3 100644 --- a/third_party/blink/renderer/core/svg/svg_graphics_element.cc +++ b/third_party/blink/renderer/core/svg/svg_graphics_element.cc
@@ -124,7 +124,7 @@ // creation. if (SVGTests::IsKnownAttribute(attr_name)) { SVGElement::InvalidationGuard invalidation_guard(this); - LazyReattachIfAttached(); + SetForceReattachLayoutTree(); return; }
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.cc b/third_party/blink/renderer/core/timing/performance_element_timing.cc index 9e976848..b1924de 100644 --- a/third_party/blink/renderer/core/timing/performance_element_timing.cc +++ b/third_party/blink/renderer/core/timing/performance_element_timing.cc
@@ -18,14 +18,16 @@ const AtomicString& identifier, int naturalWidth, int naturalHeight, - const AtomicString& id) { + const AtomicString& id, + Element* element) { // It is possible to 'paint' images which have naturalWidth or naturalHeight // equal to 0. DCHECK_GE(naturalWidth, 0); DCHECK_GE(naturalHeight, 0); + DCHECK(element); return MakeGarbageCollected<PerformanceElementTiming>( name, intersection_rect, start_time, response_end, identifier, - naturalWidth, naturalHeight, id); + naturalWidth, naturalHeight, id, element); } PerformanceElementTiming::PerformanceElementTiming( @@ -36,8 +38,10 @@ const AtomicString& identifier, int naturalWidth, int naturalHeight, - const AtomicString& id) + const AtomicString& id, + Element* element) : PerformanceEntry(name, start_time, start_time), + element_(element), intersection_rect_(DOMRectReadOnly::FromFloatRect(intersection_rect)), response_end_(response_end), identifier_(identifier), @@ -55,12 +59,20 @@ return PerformanceEntry::EntryType::kElement; } +Element* PerformanceElementTiming::element() const { + if (!element_ || !element_->isConnected()) + return nullptr; + + return element_; +} + void PerformanceElementTiming::BuildJSONValue(V8ObjectBuilder& builder) const { PerformanceEntry::BuildJSONValue(builder); builder.Add("intersectionRect", intersection_rect_); } void PerformanceElementTiming::Trace(blink::Visitor* visitor) { + visitor->Trace(element_); visitor->Trace(intersection_rect_); PerformanceEntry::Trace(visitor); }
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.h b/third_party/blink/renderer/core/timing/performance_element_timing.h index 6bb3f24..ac920ed 100644 --- a/third_party/blink/renderer/core/timing/performance_element_timing.h +++ b/third_party/blink/renderer/core/timing/performance_element_timing.h
@@ -7,6 +7,7 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/dom_high_res_time_stamp.h" +#include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/geometry/dom_rect_read_only.h" #include "third_party/blink/renderer/core/timing/performance_entry.h" @@ -27,8 +28,8 @@ const AtomicString& identifier, int naturalWidth, int naturalHeight, - const AtomicString& id); - + const AtomicString& id, + Element*); PerformanceElementTiming(const AtomicString& name, const FloatRect& intersection_rect, DOMHighResTimeStamp start_time, @@ -36,7 +37,9 @@ const AtomicString& identifier, int naturalWidth, int naturalHeight, - const AtomicString& id); + const AtomicString& id, + Element*); + ~PerformanceElementTiming() override; AtomicString entryType() const override; @@ -54,11 +57,14 @@ AtomicString id() const { return id_; } + Element* element() const; + void Trace(blink::Visitor*) override; private: void BuildJSONValue(V8ObjectBuilder&) const override; + WeakMember<Element> element_; Member<DOMRectReadOnly> intersection_rect_; DOMHighResTimeStamp response_end_; AtomicString identifier_;
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.idl b/third_party/blink/renderer/core/timing/performance_element_timing.idl index e91161fd..7a2c5a5 100644 --- a/third_party/blink/renderer/core/timing/performance_element_timing.idl +++ b/third_party/blink/renderer/core/timing/performance_element_timing.idl
@@ -11,6 +11,7 @@ readonly attribute unsigned long naturalWidth; readonly attribute unsigned long naturalHeight; readonly attribute DOMString id; + readonly attribute Element? element; // TODO(peria): toJSON is not in spec. https://crbug.com/736332 [CallWith=ScriptState, ImplementedAs=toJSONForBinding] object toJSON();
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc index 5161099..b81f61b9 100644 --- a/third_party/blink/renderer/core/timing/window_performance.cc +++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -400,12 +400,13 @@ TimeTicks response_end, const AtomicString& identifier, const IntSize& intrinsic_size, - const AtomicString& id) { + const AtomicString& id, + Element* element) { DCHECK(RuntimeEnabledFeatures::ElementTimingEnabled(GetExecutionContext())); PerformanceElementTiming* entry = PerformanceElementTiming::Create( name, rect, MonotonicTimeToDOMHighResTimeStamp(start_time), MonotonicTimeToDOMHighResTimeStamp(response_end), identifier, - intrinsic_size.Width(), intrinsic_size.Height(), id); + intrinsic_size.Width(), intrinsic_size.Height(), id, element); if (HasObserverFor(PerformanceEntry::kElement)) { UseCounter::Count(GetFrame()->GetDocument(), WebFeature::kElementTimingExplicitlyRequested);
diff --git a/third_party/blink/renderer/core/timing/window_performance.h b/third_party/blink/renderer/core/timing/window_performance.h index 36b1bea3..2555946 100644 --- a/third_party/blink/renderer/core/timing/window_performance.h +++ b/third_party/blink/renderer/core/timing/window_performance.h
@@ -82,7 +82,8 @@ TimeTicks response_end, const AtomicString& identifier, const IntSize& intrinsic_size, - const AtomicString& id); + const AtomicString& id, + Element*); void AddLayoutJankFraction(double jank_fraction);
diff --git a/third_party/blink/renderer/core/workers/worker_animation_frame_provider.cc b/third_party/blink/renderer/core/workers/worker_animation_frame_provider.cc index ff75cab..c905828 100644 --- a/third_party/blink/renderer/core/workers/worker_animation_frame_provider.cc +++ b/third_party/blink/renderer/core/workers/worker_animation_frame_provider.cc
@@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/workers/worker_animation_frame_provider.h" #include "third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h" +#include "third_party/blink/renderer/core/timing/worker_global_scope_performance.h" #include "third_party/blink/renderer/platform/cross_thread_functional.h" #include "third_party/blink/renderer/platform/wtf/time.h" @@ -43,7 +44,11 @@ FROM_HERE, WTF::Bind( [](base::WeakPtr<WorkerAnimationFrameProvider> provider) { - double time = WTF::CurrentTimeTicksInMilliseconds(); + ExecutionContext* context = provider->context_; + Performance* performance = + WorkerGlobalScopePerformance::performance( + *To<WorkerGlobalScope>(context)); + double time = performance->now(); // We don't want to expose microseconds residues to users. time = round(time * 60) / 60;
diff --git a/third_party/blink/renderer/modules/credentialmanager/authentication_extensions_client_inputs.idl b/third_party/blink/renderer/modules/credentialmanager/authentication_extensions_client_inputs.idl index 801caf8e..6c05e22 100644 --- a/third_party/blink/renderer/modules/credentialmanager/authentication_extensions_client_inputs.idl +++ b/third_party/blink/renderer/modules/credentialmanager/authentication_extensions_client_inputs.idl
@@ -13,4 +13,7 @@ boolean hmacCreateSecret; // https://w3c.github.io/webauthn/#sctn-uvm-extension boolean uvm; + // https://drafts.fidoalliance.org/fido-2/latest/fido-client-to-authenticator-protocol-v2.0-wd-20190409.html#sctn-credProtect-extension + USVString credentialProtectionPolicy; + boolean enforceCredentialProtectionPolicy = false; };
diff --git a/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc b/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc index 8101bac..bf719a5 100644 --- a/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc +++ b/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc
@@ -144,6 +144,9 @@ case blink::mojom::blink::AuthenticatorStatus:: USER_VERIFICATION_UNSUPPORTED: return CredentialManagerError::ANDROID_USER_VERIFICATION_UNSUPPORTED; + case blink::mojom::blink::AuthenticatorStatus:: + PROTECTION_POLICY_INCONSISTENT: + return CredentialManagerError::PROTECTION_POLICY_INCONSISTENT; case blink::mojom::blink::AuthenticatorStatus::SUCCESS: NOTREACHED(); break; @@ -437,6 +440,8 @@ } } + mojo_options->protection_policy = blink::mojom::ProtectionPolicy::UNSPECIFIED; + mojo_options->enforce_protection_policy = false; if (options->hasExtensions()) { auto* extensions = options->extensions(); if (extensions->hasCableRegistration()) { @@ -454,6 +459,24 @@ mojo_options->user_verification_methods = extensions->uvm(); } #endif + if (extensions->hasCredentialProtectionPolicy()) { + const auto& policy = extensions->credentialProtectionPolicy(); + if (policy == "userVerificationOptional") { + mojo_options->protection_policy = blink::mojom::ProtectionPolicy::NONE; + } else if (policy == "userVerificationOptionalWithCredentialIDList") { + mojo_options->protection_policy = + blink::mojom::ProtectionPolicy::UV_OR_CRED_ID_REQUIRED; + } else if (policy == "userVerificationRequired") { + mojo_options->protection_policy = + blink::mojom::ProtectionPolicy::UV_REQUIRED; + } else { + return nullptr; + } + } + if (extensions->hasEnforceCredentialProtectionPolicy() && + extensions->enforceCredentialProtectionPolicy()) { + mojo_options->enforce_protection_policy = true; + } } return mojo_options;
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc index 56d1922..fe16478 100644 --- a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc +++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
@@ -250,6 +250,11 @@ "Resident credentials or empty " "'allowCredentials' lists are not supported " "at this time."); + case CredentialManagerError::PROTECTION_POLICY_INCONSISTENT: + return DOMException::Create( + DOMExceptionCode::kNotSupportedError, + "Requested protection policy is inconsistent or incongurent with " + "other requested parameters."); case CredentialManagerError::ANDROID_ALGORITHM_UNSUPPORTED: return DOMException::Create(DOMExceptionCode::kNotSupportedError, "None of the algorithms specified in "
diff --git a/third_party/blink/renderer/modules/xr/xr.cc b/third_party/blink/renderer/modules/xr/xr.cc index e764e865..b6d309e 100644 --- a/third_party/blink/renderer/modules/xr/xr.cc +++ b/third_party/blink/renderer/modules/xr/xr.cc
@@ -51,7 +51,7 @@ session_options->immersive = (mode == XRSession::kModeImmersiveVR || mode == XRSession::kModeImmersiveAR); session_options->environment_integration = - (mode == XRSession::kModeInlineAR || mode == XRSession::kModeImmersiveAR); + mode == XRSession::kModeImmersiveAR; return session_options; } @@ -60,9 +60,6 @@ if (mode_string == "inline") { return XRSession::kModeInline; } - if (mode_string == "legacy-inline-ar") { - return XRSession::kModeInlineAR; - } if (mode_string == "immersive-vr") { return XRSession::kModeImmersiveVR; } @@ -250,28 +247,19 @@ kActiveImmersiveSession)); } - // All immersive and AR sessions require a user gesture. + // All immersive sessions require a user gesture. bool has_user_activation = LocalFrame::HasTransientUserActivation(frame); - if ((is_immersive || session_mode == XRSession::kModeInlineAR) && - !has_user_activation) { + if (is_immersive && !has_user_activation) { return ScriptPromise::RejectWithDOMException( script_state, DOMException::Create(DOMExceptionCode::kSecurityError, kRequestRequiresUserActivation)); } - if (session_mode == XRSession::kModeInlineAR) { - doc->AddConsoleMessage(ConsoleMessage::Create( - mojom::ConsoleMessageSource::kOther, - mojom::ConsoleMessageLevel::kWarning, - "Inline AR is deprecated and will be removed soon.")); - } - auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); ScriptPromise promise = resolver->Promise(); PendingSessionQuery* query = MakeGarbageCollected<PendingSessionQuery>(resolver, session_mode); - query->has_user_activation = has_user_activation; if (!device_) { pending_session_requests_.push_back(query); @@ -304,7 +292,6 @@ device::mojom::blink::XRSessionOptionsPtr session_options = convertModeToMojo(query->mode); - session_options->has_user_activation = query->has_user_activation; // TODO(http://crbug.com/826899) Once device activation is sorted out for // WebXR, either pass in the correct value for metrics to know whether @@ -413,8 +400,7 @@ return; } - bool environment_integration = query->mode == XRSession::kModeInlineAR || - query->mode == XRSession::kModeImmersiveAR; + bool environment_integration = query->mode == XRSession::kModeImmersiveAR; // immersive sessions must supply display info. DCHECK(session_ptr->display_info); @@ -422,6 +408,11 @@ // as well. DCHECK(!environment_integration || session_ptr->display_info->capabilities ->canProvideEnvironmentIntegration); + DVLOG(2) << __func__ + << ": environment_integration=" << environment_integration + << "canProvideEnvironmentIntegration=" + << session_ptr->display_info->capabilities + ->canProvideEnvironmentIntegration; // TODO(https://crbug.com/944936): The blend mode could be "additive". XRSession::EnvironmentBlendMode blend_mode = XRSession::kBlendModeOpaque; @@ -435,6 +426,14 @@ if (query->mode == XRSession::kModeImmersiveVR || query->mode == XRSession::kModeImmersiveAR) { frameProvider()->BeginImmersiveSession(session, std::move(session_ptr)); + if (environment_integration) { + frameProvider()->GetDataProvider()->GetEnvironmentIntegrationProvider( + mojo::MakeRequest(&environment_provider_, + GetExecutionContext()->GetTaskRunner( + TaskType::kMiscPlatformAPI))); + environment_provider_.set_connection_error_handler(WTF::Bind( + &XR::OnEnvironmentProviderDisconnect, WrapWeakPersistent(this))); + } } else { magic_window_provider_.Bind(std::move(session_ptr->data_provider)); if (environment_integration) {
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc index 5dd5542..6b2d929b 100644 --- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc +++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -187,15 +187,6 @@ ScheduleNonImmersiveFrame(); } -bool XRFrameProvider::HasARSession() { - for (unsigned i = 0; i < requesting_sessions_.size(); ++i) { - XRSession* session = requesting_sessions_.at(i).Get(); - if (session->environmentIntegration()) - return true; - } - return false; -} - void XRFrameProvider::ScheduleImmersiveFrame() { TRACE_EVENT0("gpu", __FUNCTION__); if (pending_immersive_vsync_) @@ -208,13 +199,10 @@ WrapWeakPersistent(this))); } -// TODO(lincolnfrog): add a ScheduleNonImmersiveARFrame, if we want camera RAF -// alignment instead of doc RAF alignment. void XRFrameProvider::ScheduleNonImmersiveFrame() { TRACE_EVENT0("gpu", __FUNCTION__); DCHECK(!immersive_session_) << "Scheduling should be done via the exclusive session if present."; - DCHECK(xr_->xrMagicWindowProviderPtr() || !HasARSession()); if (pending_non_immersive_vsync_) return; @@ -247,15 +235,8 @@ frame_pose_ = nullptr; } - // TODO(https://crbug.com/839253): Generalize the pass-through images - // code path so that it also works for immersive sessions on an AR device - // with pass-through technology. - - // TODO(http://crbug.com/856257) Remove the special casing for AR and non-AR. - if (!HasARSession()) { - doc->RequestAnimationFrame( - MakeGarbageCollected<XRFrameProviderRequestCallback>(this)); - } + doc->RequestAnimationFrame( + MakeGarbageCollected<XRFrameProviderRequestCallback>(this)); } void XRFrameProvider::OnImmersiveFrameData( @@ -354,21 +335,6 @@ } frame_pose_ = std::move(frame_data->pose); - - base::TimeTicks monotonic_time_now = TimeTicks() + frame_data->time_delta; - double high_res_now_ms = - doc->Loader() - ->GetTiming() - .MonotonicTimeToZeroBasedDocumentTime(monotonic_time_now) - .InMillisecondsF(); - - if (HasARSession()) { - frame->GetTaskRunner(blink::TaskType::kInternalMedia) - ->PostTask(FROM_HERE, - WTF::Bind(&XRFrameProvider::ProcessScheduledFrame, - WrapWeakPersistent(this), std::move(frame_data), - high_res_now_ms)); - } } void XRFrameProvider::ProcessScheduledFrame( @@ -425,8 +391,7 @@ immersive_session_->UpdateStageParameters(frame_data->stage_parameters); } immersive_session_->OnFrame(high_res_now_ms, std::move(pose_matrix), - buffer_mailbox_holder_, base::nullopt, - base::nullopt); + buffer_mailbox_holder_); } else { // In the process of fulfilling the frame requests for each session they are // extremely likely to request another frame. Work off of a separate list @@ -443,26 +408,13 @@ frame_pose_->input_state.value()); } - if (frame_data && frame_data->projection_matrix.has_value()) { - session->SetNonImmersiveProjectionMatrix( - frame_data->projection_matrix.value()); - } - if (frame_pose_ && frame_pose_->pose_reset) { session->OnPoseReset(); } std::unique_ptr<TransformationMatrix> pose_matrix = getPoseMatrix(frame_pose_); - // TODO(https://crbug.com/837883): only render background for - // sessions that are using AR. - if (frame_data) { - session->OnFrame(high_res_now_ms, std::move(pose_matrix), base::nullopt, - frame_data->buffer_holder, frame_data->buffer_size); - } else { - session->OnFrame(high_res_now_ms, std::move(pose_matrix), base::nullopt, - base::nullopt, base::nullopt); - } + session->OnFrame(high_res_now_ms, std::move(pose_matrix), base::nullopt); } processing_sessions_.clear(); @@ -554,16 +506,24 @@ float width = layer->framebufferWidth(); float height = layer->framebufferHeight(); - WebFloatRect left_coords( - static_cast<float>(left->x()) / width, - static_cast<float>(height - (left->y() + left->height())) / height, - static_cast<float>(left->width()) / width, - static_cast<float>(left->height()) / height); - WebFloatRect right_coords( - static_cast<float>(right->x()) / width, - static_cast<float>(height - (right->y() + right->height())) / height, - static_cast<float>(right->width()) / width, - static_cast<float>(right->height()) / height); + // We may only have one eye view, i.e. in smartphone immersive AR mode. + // Use all-zero bounds for unused views. + WebFloatRect left_coords = + left ? WebFloatRect( + static_cast<float>(left->x()) / width, + static_cast<float>(height - (left->y() + left->height())) / + height, + static_cast<float>(left->width()) / width, + static_cast<float>(left->height()) / height) + : WebFloatRect(); + WebFloatRect right_coords = + right ? WebFloatRect( + static_cast<float>(right->x()) / width, + static_cast<float>(height - (right->y() + right->height())) / + height, + static_cast<float>(right->width()) / width, + static_cast<float>(right->height()) / height) + : WebFloatRect(); presentation_provider_->UpdateLayerBounds( frame_id_, left_coords, right_coords, WebSize(width, height));
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.h b/third_party/blink/renderer/modules/xr/xr_frame_provider.h index 50024bf..1710e5a 100644 --- a/third_party/blink/renderer/modules/xr/xr_frame_provider.h +++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.h
@@ -43,6 +43,10 @@ void Dispose(); void OnFocusChanged(); + device::mojom::blink::XRFrameDataProvider* GetDataProvider() { + return immersive_data_provider_.get(); + } + virtual void Trace(blink::Visitor*); private: @@ -56,8 +60,6 @@ void ProcessScheduledFrame(device::mojom::blink::XRFrameDataPtr frame_data, double high_res_now_ms); - bool HasARSession(); - const Member<XR> xr_; Member<XRSession> immersive_session_; Member<XRFrameTransport> frame_transport_;
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc index 1bcbbc25..539b47e 100644 --- a/third_party/blink/renderer/modules/xr/xr_session.cc +++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -115,8 +115,7 @@ bool sensorless_session) : xr_(xr), mode_(mode), - environment_integration_(mode == kModeInlineAR || - mode == kModeImmersiveAR), + environment_integration_(mode == kModeImmersiveAR), client_binding_(this, std::move(client_request)), callback_collection_( MakeGarbageCollected<XRFrameRequestCallbackCollection>( @@ -150,16 +149,6 @@ return mode_ == kModeImmersiveVR || mode_ == kModeImmersiveAR; } -void XRSession::SetNonImmersiveProjectionMatrix( - const WTF::Vector<float>& projection_matrix) { - DCHECK_EQ(projection_matrix.size(), 16lu); - - non_immersive_projection_matrix_ = projection_matrix; - // It is about as expensive to check equality as to just - // update the views, so just update. - update_views_next_frame_ = true; -} - ExecutionContext* XRSession::GetExecutionContext() const { return xr_->GetExecutionContext(); } @@ -511,12 +500,16 @@ return OutputCanvasSize(); } - double width = (display_info_->leftEye->renderWidth + - display_info_->rightEye->renderWidth); - double height = std::max(display_info_->leftEye->renderHeight, - display_info_->rightEye->renderHeight); - double scale = display_info_->webxr_default_framebuffer_scale; + double width = display_info_->leftEye->renderWidth; + double height = display_info_->leftEye->renderHeight; + + if (display_info_->rightEye) { + width += display_info_->rightEye->renderWidth; + height = std::max(display_info_->leftEye->renderHeight, + display_info_->rightEye->renderHeight); + } + return DoubleSize(width * scale, height * scale); } @@ -639,9 +632,7 @@ void XRSession::OnFrame( double timestamp, std::unique_ptr<TransformationMatrix> base_pose_matrix, - const base::Optional<gpu::MailboxHolder>& output_mailbox_holder, - const base::Optional<gpu::MailboxHolder>& background_mailbox_holder, - const base::Optional<IntSize>& background_size) { + const base::Optional<gpu::MailboxHolder>& output_mailbox_holder) { TRACE_EVENT0("gpu", __FUNCTION__); DVLOG(2) << __FUNCTION__; // Don't process any outstanding frames once the session is ended. @@ -678,15 +669,6 @@ frame_base_layer->OnFrameStart(output_mailbox_holder); - // TODO(836349): revisit sending background image data to blink at all. - if (background_mailbox_holder) { - // If using a background image, the caller must provide its pixel size - // also. The source size can differ from the current drawing buffer size. - DCHECK(background_size); - frame_base_layer->HandleBackgroundImage(background_mailbox_holder.value(), - background_size.value()); - } - // Resolve the queued requestAnimationFrame callbacks. All XR rendering will // happen within these calls. resolving_frame_ will be true for the duration // of the callbacks. @@ -746,12 +728,6 @@ DVLOG(2) << __FUNCTION__ << ": got angle=" << output_angle; } - if (xr_->xrEnvironmentProviderPtr()) { - xr_->xrEnvironmentProviderPtr()->UpdateSessionGeometry( - IntSize(output_width_, output_height_), - display::Display::DegreesToRotation(output_angle)); - } - if (render_state_->baseLayer()) { render_state_->baseLayer()->OnResize(); } @@ -975,16 +951,21 @@ // If we don't already have the views allocated, do so now. if (views_.IsEmpty()) { views_.push_back(MakeGarbageCollected<XRView>(this, XRView::kEyeLeft)); - views_.push_back(MakeGarbageCollected<XRView>(this, XRView::kEyeRight)); + if (display_info_->rightEye) { + views_.push_back( + MakeGarbageCollected<XRView>(this, XRView::kEyeRight)); + } } // In immersive mode the projection and view matrices must be aligned with // the device's physical optics. UpdateViewFromEyeParameters( views_[XRView::kEyeLeft], display_info_->leftEye, render_state_->depthNear(), render_state_->depthFar()); - UpdateViewFromEyeParameters( - views_[XRView::kEyeRight], display_info_->rightEye, - render_state_->depthNear(), render_state_->depthFar()); + if (display_info_->rightEye) { + UpdateViewFromEyeParameters( + views_[XRView::kEyeRight], display_info_->rightEye, + render_state_->depthNear(), render_state_->depthFar()); + } } else { if (views_.IsEmpty()) { views_.push_back(MakeGarbageCollected<XRView>(this, XRView::kEyeLeft)); @@ -997,31 +978,15 @@ static_cast<float>(output_height_); } - if (non_immersive_projection_matrix_.size() > 0) { - views_[XRView::kEyeLeft]->UpdateProjectionMatrixFromRawValues( - non_immersive_projection_matrix_, render_state_->depthNear(), - render_state_->depthFar()); - } else { - // In non-immersive mode, if there is no explicit projection matrix - // provided, the projection matrix must be aligned with the - // output canvas dimensions. - views_[XRView::kEyeLeft]->UpdateProjectionMatrixFromAspect( - kMagicWindowVerticalFieldOfView, aspect, render_state_->depthNear(), - render_state_->depthFar()); - } + // In non-immersive mode, if there is no explicit projection matrix + // provided, the projection matrix must be aligned with the + // output canvas dimensions. + views_[XRView::kEyeLeft]->UpdateProjectionMatrixFromAspect( + kMagicWindowVerticalFieldOfView, aspect, render_state_->depthNear(), + render_state_->depthFar()); } views_dirty_ = false; - } else { - // TODO(https://crbug.com/836926): views_dirty_ is not working right for - // AR mode, we're not picking up the change on the right frame. Remove this - // fallback once that's sorted out. - DVLOG(2) << __FUNCTION__ << ": FIXME, fallback proj matrix update"; - if (non_immersive_projection_matrix_.size() > 0) { - views_[XRView::kEyeLeft]->UpdateProjectionMatrixFromRawValues( - non_immersive_projection_matrix_, render_state_->depthNear(), - render_state_->depthFar()); - } } return views_;
diff --git a/third_party/blink/renderer/modules/xr/xr_session.h b/third_party/blink/renderer/modules/xr/xr_session.h index 7c1c559d..9567a8e 100644 --- a/third_party/blink/renderer/modules/xr/xr_session.h +++ b/third_party/blink/renderer/modules/xr/xr_session.h
@@ -49,12 +49,7 @@ USING_GARBAGE_COLLECTED_MIXIN(XRSession); public: - enum SessionMode { - kModeInline = 0, - kModeImmersiveVR, - kModeImmersiveAR, - kModeInlineAR - }; + enum SessionMode { kModeInline = 0, kModeImmersiveVR, kModeImmersiveAR }; enum EnvironmentBlendMode { kBlendModeOpaque = 0, @@ -70,7 +65,6 @@ ~XRSession() override = default; XR* xr() const { return xr_; } - bool environmentIntegration() const { return environment_integration_; } const String& environmentBlendMode() const { return blend_mode_string_; } XRRenderState* renderState() const { return render_state_; } XRWorldTrackingState* worldTrackingState() { return nullptr; } @@ -134,9 +128,7 @@ void OnFocusChanged(); void OnFrame(double timestamp, std::unique_ptr<TransformationMatrix>, - const base::Optional<gpu::MailboxHolder>& output_mailbox_holder, - const base::Optional<gpu::MailboxHolder>& bg_mailbox_holder, - const base::Optional<IntSize>& background_size); + const base::Optional<gpu::MailboxHolder>& output_mailbox_holder); void OnInputStateChange( int16_t frame_id, const WTF::Vector<device::mojom::blink::XRInputSourceStatePtr>&); @@ -164,6 +156,11 @@ return true; } + // Immersive sessions currently use two views for VR, and only a single view + // for smartphone immersive AR mode. Convention is that we use the left eye + // if there's only a single view. + bool StereoscopicViews() { return display_info_ && display_info_->rightEye; } + void UpdateEyeParameters( const device::mojom::blink::VREyeParametersPtr& left_eye, const device::mojom::blink::VREyeParametersPtr& right_eye); @@ -175,7 +172,6 @@ unsigned int DisplayInfoPtrId() const { return display_info_id_; } unsigned int StageParametersId() const { return stage_parameters_id_; } - void SetNonImmersiveProjectionMatrix(const WTF::Vector<float>&); void SetXRDisplayInfo(device::mojom::blink::VRDisplayInfoPtr display_info); void Trace(blink::Visitor*) override; @@ -238,8 +234,6 @@ Member<XRFrameRequestCallbackCollection> callback_collection_; std::unique_ptr<TransformationMatrix> base_pose_matrix_; - WTF::Vector<float> non_immersive_projection_matrix_; - bool blurred_; bool ended_ = false; bool pending_frame_ = false;
diff --git a/third_party/blink/renderer/modules/xr/xr_session.idl b/third_party/blink/renderer/modules/xr/xr_session.idl index f5e8c74..bec991ce2 100644 --- a/third_party/blink/renderer/modules/xr/xr_session.idl +++ b/third_party/blink/renderer/modules/xr/xr_session.idl
@@ -5,7 +5,6 @@ // https://immersive-web.github.io/webxr/#xrsession-interface enum XRSessionMode { "inline", - "legacy-inline-ar", "immersive-vr", "immersive-ar", };
diff --git a/third_party/blink/renderer/modules/xr/xr_view.cc b/third_party/blink/renderer/modules/xr/xr_view.cc index 7290624..ea2be55 100644 --- a/third_party/blink/renderer/modules/xr/xr_view.cc +++ b/third_party/blink/renderer/modules/xr/xr_view.cc
@@ -64,30 +64,6 @@ return session_; } -// TODO(http://crbug.com/836496): This method only supports -// straight-ahead projection matrices. In order to support -// multiple sessions embedded with projection matrices that act -// like views into the shared camera space, this math needs to -// be updated. -void XRView::UpdateProjectionMatrixFromRawValues( - const WTF::Vector<float>& projection_matrix, - float near_depth, - float far_depth) { - DCHECK_EQ(projection_matrix.size(), 16lu); - float* out = projection_matrix_->Data(); - for (int i = 0; i < 16; i++) { - out[i] = projection_matrix[i]; - } - - // Recalculate elements that depend on near/far depth. The input matrix used - // arbitrary values, need to adjust to what the client uses. - float inverse_near_far = 1.0f / (near_depth - far_depth); - out[10] = (near_depth + far_depth) * inverse_near_far; - out[14] = (2.0f * far_depth * near_depth) * inverse_near_far; - - inv_projection_dirty_ = true; -} - void XRView::UpdateProjectionMatrixFromFoV(float up_rad, float down_rad, float left_rad,
diff --git a/third_party/blink/renderer/modules/xr/xr_view.h b/third_party/blink/renderer/modules/xr/xr_view.h index 77e25afb..4f215d6d 100644 --- a/third_party/blink/renderer/modules/xr/xr_view.h +++ b/third_party/blink/renderer/modules/xr/xr_view.h
@@ -39,11 +39,6 @@ DOMFloat32Array* projectionMatrix() const { return projection_matrix_; } XRRigidTransform* transform() const; - void UpdateProjectionMatrixFromRawValues( - const WTF::Vector<float>& projection_matrix, - float near_depth, - float far_depth); - void UpdateProjectionMatrixFromFoV(float up_rad, float down_rad, float left_rad,
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc index 4cde7cd6..9f97a4f 100644 --- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc +++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.cc
@@ -214,13 +214,24 @@ viewports_dirty_ = false; if (session()->immersive()) { - left_viewport_ = MakeGarbageCollected<XRViewport>( - 0, 0, framebuffer_width * 0.5 * viewport_scale_, - framebuffer_height * viewport_scale_); - right_viewport_ = MakeGarbageCollected<XRViewport>( - framebuffer_width * 0.5 * viewport_scale_, 0, - framebuffer_width * 0.5 * viewport_scale_, - framebuffer_height * viewport_scale_); + if (session()->StereoscopicViews()) { + left_viewport_ = MakeGarbageCollected<XRViewport>( + 0, 0, framebuffer_width * 0.5 * viewport_scale_, + framebuffer_height * viewport_scale_); + right_viewport_ = MakeGarbageCollected<XRViewport>( + framebuffer_width * 0.5 * viewport_scale_, 0, + framebuffer_width * 0.5 * viewport_scale_, + framebuffer_height * viewport_scale_); + } else { + // Phone immersive AR only uses one viewport, but the second viewport is + // needed for the UpdateLayerBounds mojo call which currently expects + // exactly two views. This should be revisited as part of a refactor to + // handle a more general list of viewports, cf. https://crbug.com/928433. + left_viewport_ = MakeGarbageCollected<XRViewport>( + 0, 0, framebuffer_width * viewport_scale_, + framebuffer_height * viewport_scale_); + right_viewport_ = nullptr; + } session()->xr()->frameProvider()->UpdateWebGLLayerViewports(this); @@ -274,13 +285,6 @@ } } -void XRWebGLLayer::OverwriteColorBufferFromMailboxTexture( - const gpu::MailboxHolder& mailbox_holder, - const IntSize& size) { - drawing_buffer_->OverwriteColorBufferFromMailboxTexture(mailbox_holder, size); - framebuffer_->SetContentsChanged(true); -} - void XRWebGLLayer::OnFrameStart( const base::Optional<gpu::MailboxHolder>& buffer_mailbox_holder) { // If the requested scale has changed since the last from, update it now. @@ -337,12 +341,6 @@ viewports_dirty_ = true; } -void XRWebGLLayer::HandleBackgroundImage( - const gpu::MailboxHolder& mailbox_holder, - const IntSize& size) { - OverwriteColorBufferFromMailboxTexture(mailbox_holder, size); -} - scoped_refptr<StaticBitmapImage> XRWebGLLayer::TransferToStaticBitmapImage( std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) { return drawing_buffer_->TransferToStaticBitmapImage(out_release_callback);
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.h b/third_party/blink/renderer/modules/xr/xr_webgl_layer.h index b43b2ae..1219109 100644 --- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.h +++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
@@ -69,11 +69,6 @@ void OnFrameStart(const base::Optional<gpu::MailboxHolder>&) override; void OnFrameEnd() override; void OnResize() override; - void HandleBackgroundImage(const gpu::MailboxHolder&, - const IntSize&) override; - - void OverwriteColorBufferFromMailboxTexture(const gpu::MailboxHolder&, - const IntSize& size); void UpdateWebXRMirror();
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h b/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h index f59552c7..44e55bc 100644 --- a/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h +++ b/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h
@@ -63,25 +63,23 @@ : length_(kDeletedValueLength), direction_(static_cast<unsigned>(TextDirection::kLtr)) {} - SmallStringKey(const LChar* characters, - uint16_t length, - TextDirection direction) - : length_(length), direction_(static_cast<unsigned>(direction)) { - DCHECK(length <= kCapacity); + SmallStringKey(base::span<const LChar> characters, TextDirection direction) + : length_(static_cast<uint16_t>(characters.size())), + direction_(static_cast<unsigned>(direction)) { + DCHECK(characters.size() <= kCapacity); // Up-convert from LChar to UChar. - for (uint16_t i = 0; i < length; ++i) { + for (uint16_t i = 0; i < characters.size(); ++i) { characters_[i] = characters[i]; } HashString(); } - SmallStringKey(const UChar* characters, - uint16_t length, - TextDirection direction) - : length_(length), direction_(static_cast<unsigned>(direction)) { - DCHECK(length <= kCapacity); - memcpy(characters_, characters, length * sizeof(UChar)); + SmallStringKey(base::span<const UChar> characters, TextDirection direction) + : length_(static_cast<uint16_t>(characters.size())), + direction_(static_cast<unsigned>(direction)) { + DCHECK(characters.size() <= kCapacity); + memcpy(characters_, characters.data(), characters.size_bytes()); HashString(); } @@ -152,10 +150,9 @@ private: ShapeCacheEntry* AddSlowCase(const TextRun& run, ShapeCacheEntry entry) { - unsigned length = run.length(); bool is_new_entry; ShapeCacheEntry* value; - if (length == 1) { + if (run.length() == 1) { uint32_t key = run[0]; // All current codepoints in UTF-32 are bewteen 0x0 and 0x10FFFF, // as such use bit 31 (zero-based) to indicate direction. @@ -167,11 +164,9 @@ } else { SmallStringKey small_string_key; if (run.Is8Bit()) { - small_string_key = - SmallStringKey(run.Characters8(), length, run.Direction()); + small_string_key = SmallStringKey(run.Span8(), run.Direction()); } else { - small_string_key = - SmallStringKey(run.Characters16(), length, run.Direction()); + small_string_key = SmallStringKey(run.Span16(), run.Direction()); } SmallStringMap::AddResult add_result =
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc index 128481c..ad24510 100644 --- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc +++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.cc
@@ -84,12 +84,10 @@ bool is_after_expansion = is_after_expansion_; if (text_.Is8Bit()) { expansion_opportunity_count_ = Character::ExpansionOpportunityCount( - text_.Characters8(), text_.length(), direction, is_after_expansion, - text_justify_); + text_.Span8(), direction, is_after_expansion, text_justify_); } else { expansion_opportunity_count_ = Character::ExpansionOpportunityCount( - text_.Characters16(), text_.length(), direction, is_after_expansion, - text_justify_); + text_.Span16(), direction, is_after_expansion, text_justify_); } if (is_after_expansion && !allows_trailing_expansion) { DCHECK_GT(expansion_opportunity_count_, 0u);
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc index f82f270..c163574 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -1119,22 +1119,22 @@ return false; } -static bool IsRenderSurfaceCandidate( +static cc::RenderSurfaceReason GetRenderSurfaceCandidateReason( const cc::EffectNode& effect, const Vector<const EffectPaintPropertyNode*>& blink_effects) { - if (effect.has_render_surface) - return false; + if (effect.HasRenderSurface()) + return cc::RenderSurfaceReason::kNone; if (effect.blend_mode != SkBlendMode::kSrcOver) - return true; + return cc::RenderSurfaceReason::kBlendModeDstIn; if (effect.opacity != 1.f) - return true; + return cc::RenderSurfaceReason::kOpacity; if (static_cast<size_t>(effect.id) < blink_effects.size() && blink_effects[effect.id] && blink_effects[effect.id]->HasActiveOpacityAnimation()) - return true; + return cc::RenderSurfaceReason::kOpacityAnimation; if (effect.is_fast_rounded_corner) - return true; - return false; + return cc::RenderSurfaceReason::kRoundedCorner; + return cc::RenderSurfaceReason::kNone; } // Every effect is supposed to have render surface enabled for grouping, but we @@ -1163,16 +1163,18 @@ for (auto id = effect_tree.size() - 1; id > cc::EffectTree::kSecondaryRootNodeId; id--) { auto* effect = effect_tree.Node(id); - if (effect_layer_counts[id] > 1 && - IsRenderSurfaceCandidate(*effect, blink_effects)) { - // The render surface candidate needs a render surface because it - // controls more than 1 layer. - effect->has_render_surface = true; + if (effect_layer_counts[id] > 1) { + auto reason = GetRenderSurfaceCandidateReason(*effect, blink_effects); + if (reason != cc::RenderSurfaceReason::kNone) { + // The render surface candidate needs a render surface because it + // controls more than 1 layer. + effect->render_surface_reason = reason; + } } // We should not have visited the parent. DCHECK_NE(-1, effect_layer_counts[effect->parent_id]); - if (effect->has_render_surface) { + if (effect->HasRenderSurface()) { // A sub-render-surface counts as one controlled layer of the parent. effect_layer_counts[effect->parent_id]++; } else {
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc index 565ffcf..6543a73 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -2033,7 +2033,7 @@ EXPECT_EQ(gfx::Size(200, 200), masked_layer->bounds()); const cc::EffectNode* masked_group = GetPropertyTrees().effect_tree.Node(masked_layer->effect_tree_index()); - EXPECT_TRUE(masked_group->has_render_surface); + EXPECT_TRUE(masked_group->HasRenderSurface()); const cc::Layer* masking_layer = ContentLayerAt(1); EXPECT_THAT( @@ -2043,7 +2043,7 @@ EXPECT_EQ(gfx::Size(100, 100), masking_layer->bounds()); const cc::EffectNode* masking_group = GetPropertyTrees().effect_tree.Node(masking_layer->effect_tree_index()); - EXPECT_FALSE(masking_group->has_render_surface); + EXPECT_FALSE(masking_group->HasRenderSurface()); EXPECT_EQ(masked_group->id, masking_group->parent_id); ASSERT_EQ(1u, masking_group->filters.size()); EXPECT_EQ(cc::FilterOperation::REFERENCE, @@ -2081,7 +2081,7 @@ GetPropertyTrees().effect_tree.Node(masking_layer->effect_tree_index()); // There is a render surface because there are two children. - EXPECT_TRUE(masking_group->has_render_surface); + EXPECT_TRUE(masking_group->HasRenderSurface()); ASSERT_EQ(1u, masking_group->filters.size()); EXPECT_EQ(cc::FilterOperation::REFERENCE, masking_group->filters.at(0).type()); @@ -2112,7 +2112,7 @@ GetPropertyTrees().effect_tree.Node(masking_layer->effect_tree_index()); /// This requires a render surface. - EXPECT_TRUE(masking_group->has_render_surface); + EXPECT_TRUE(masking_group->HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, UpdateProducesNewSequenceNumber) { @@ -3483,7 +3483,7 @@ const auto* effect = GetPropertyTrees().effect_tree.Node(effect_id); \ EXPECT_EQ(expected_opacity, effect->opacity); \ EXPECT_EQ(!!((expected_flags)&kHasRenderSurface), \ - effect->has_render_surface); \ + effect->HasRenderSurface()); \ } while (false) TEST_P(PaintArtifactCompositorTest, OpacityRenderSurfaces) { @@ -3680,7 +3680,7 @@ const auto* effect = GetPropertyTrees().effect_tree.Node( ContentLayerAt(1)->effect_tree_index()); - EXPECT_TRUE(effect->has_render_surface); + EXPECT_TRUE(effect->HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, @@ -3701,7 +3701,7 @@ const auto* effect = GetPropertyTrees().effect_tree.Node( ContentLayerAt(1)->effect_tree_index()); - EXPECT_TRUE(effect->has_render_surface); + EXPECT_TRUE(effect->HasRenderSurface()); } TEST_P(PaintArtifactCompositorTest, OpacityIndirectlyAffectingTwoLayers) {
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc index 7d7b210..d6ee4d3 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -261,7 +261,7 @@ CompositorElementIdFromUniqueObjectId(unique_id).GetInternalValue(); effect_node.transform_id = kRealRootNodeId; effect_node.clip_id = kSecondaryRootNodeId; - effect_node.has_render_surface = true; + effect_node.render_surface_reason = cc::RenderSurfaceReason::kRoot; root_layer_->SetEffectTreeIndex(effect_node.id); SetCurrentEffectState(effect_node, CcEffectType::kEffect, @@ -294,9 +294,8 @@ return false; } -static bool TransformsMayBe2dAxisMisaligned( - const TransformPaintPropertyNode& a, - const TransformPaintPropertyNode& b) { +bool TransformsMayBe2dAxisMisaligned(const TransformPaintPropertyNode& a, + const TransformPaintPropertyNode& b) { if (&a == &b) return false; const auto& translation_2d_or_matrix = @@ -306,8 +305,10 @@ return true; // Assume any animation can cause 2d axis misalignment. const auto& lca = LowestCommonAncestor(a, b); - return TransformsToAncestorHaveActiveAnimation(a, lca) || - TransformsToAncestorHaveActiveAnimation(b, lca); + if (TransformsToAncestorHaveActiveAnimation(a, lca) || + TransformsToAncestorHaveActiveAnimation(b, lca)) + return true; + return false; } void PropertyTreeManager::SetCurrentEffectState( @@ -324,11 +325,11 @@ DCHECK(!clip.IsParentAlias() || !clip.Parent()); current_.clip = &clip; - if (cc_effect_node.has_render_surface) { - current_.may_be_2d_axis_misaligned_to_render_surface = false; + if (cc_effect_node.HasRenderSurface()) { + current_.may_be_2d_axis_misaligned_to_render_surface = 0; } else if (previous_transform && !current_.may_be_2d_axis_misaligned_to_render_surface) { - current_.may_be_2d_axis_misaligned_to_render_surface = + current_.may_be_2d_axis_misaligned_to_render_surface |= TransformsMayBe2dAxisMisaligned(*previous_transform, current_.Transform()); } @@ -336,8 +337,10 @@ // TODO(crbug.com/504464): Remove this when move render surface decision logic // into cc compositor thread. -void PropertyTreeManager::SetCurrentEffectHasRenderSurface() { - GetEffectTree().Node(current_.effect_id)->has_render_surface = true; +void PropertyTreeManager::SetCurrentEffectRenderSurfaceReason( + cc::RenderSurfaceReason reason) { + auto* effect = GetEffectTree().Node(current_.effect_id); + effect->render_surface_reason = reason; } int PropertyTreeManager::EnsureCompositorTransformNode( @@ -420,12 +423,14 @@ // context of an ancestor, cc needs a render surface for correct flattening. // TODO(crbug.com/504464): Move the logic into cc compositor thread. auto* current_cc_effect = GetEffectTree().Node(current_.effect_id); - if (current_cc_effect && !current_cc_effect->has_render_surface && + if (current_cc_effect && !current_cc_effect->HasRenderSurface() && current_cc_effect->transform_id == parent_id && transform_node.FlattensInheritedTransform() && transform_node.Parent() && transform_node.Parent()->RenderingContextId() && - !transform_node.Parent()->FlattensInheritedTransform()) - SetCurrentEffectHasRenderSurface(); + !transform_node.Parent()->FlattensInheritedTransform()) { + SetCurrentEffectRenderSurfaceReason( + cc::RenderSurfaceReason::k3dTransformFlattening); + } auto result = transform_node_map_.Set(&transform_node, id); DCHECK(result.is_new_entry); @@ -745,7 +750,7 @@ // Cc requires that a rectangluar clip is 2d-axis-aligned with the render // surface to correctly apply the clip. - if (current_.may_be_2d_axis_misaligned_to_render_surface || + if (current_.may_be_2d_axis_misaligned_to_render_surface | TransformsMayBe2dAxisMisaligned(clip.LocalTransformSpace(), current_.Transform())) return CcEffectType::kSyntheticFor2dAxisAlignment; @@ -766,7 +771,7 @@ // An effect node can't omit render surface if it has child with exotic // blending mode. See comments below for more detail. // TODO(crbug.com/504464): Remove premature optimization here. - SetCurrentEffectHasRenderSurface(); + SetCurrentEffectRenderSurfaceReason(cc::RenderSurfaceReason::kBlendMode); } else { // Exit synthetic effects until there are no more synthesized clips below // our lowest common ancestor. @@ -826,7 +831,6 @@ // when the effect is closed. For now the default value INVALID_STABLE_ID // is used. See PropertyTreeManager::EmitClipMaskLayer(). } else { - DCHECK_EQ(pending_clip.type, CcEffectType::kSyntheticFor2dAxisAlignment); synthetic_effect.stable_id = CompositorElementIdFromUniqueObjectId(NewUniqueObjectId()) .GetInternalValue(); @@ -845,7 +849,16 @@ gfx::RRectF(pending_clip.clip->ClipRect()); synthetic_effect.is_fast_rounded_corner = true; } else { - synthetic_effect.has_render_surface = true; + if (pending_clip.type == CcEffectType::kSyntheticForNonTrivialClip) { + synthetic_effect.render_surface_reason = + pending_clip.clip->ClipRect().IsRounded() + ? cc::RenderSurfaceReason::kRoundedCorner + : cc::RenderSurfaceReason::kClipPath; + } else { + synthetic_effect.render_surface_reason = + cc::RenderSurfaceReason::kClipAxisAlignment; + } + pending_synthetic_mask_layers_.insert(synthetic_effect.id); } @@ -934,7 +947,7 @@ // blending mode. // TODO(crbug.com/504464): Remove premature optimization here. if (effect.BlendMode() != SkBlendMode::kSrcOver) - SetCurrentEffectHasRenderSurface(); + SetCurrentEffectRenderSurfaceReason(cc::RenderSurfaceReason::kBlendMode); blend_mode = effect.BlendMode(); output_clip = current_.clip; @@ -957,12 +970,20 @@ // Also, kDstIn and kSrcOver blend modes have fast paths if only one layer // is under the blend mode. This value is adjusted in PaintArtifactCompositor // ::UpdateRenderSurfaceForEffects() to account for more than one layer. - if (!effect.Filter().IsEmpty() || effect.HasActiveFilterAnimation() || - !effect.BackdropFilter().IsEmpty() || - effect.HasActiveBackdropFilterAnimation() || - (blend_mode != SkBlendMode::kSrcOver && - blend_mode != SkBlendMode::kDstIn)) { - effect_node.has_render_surface = true; + if (!effect.Filter().IsEmpty()) { + effect_node.render_surface_reason = cc::RenderSurfaceReason::kFilter; + } else if (effect.HasActiveFilterAnimation()) { + effect_node.render_surface_reason = + cc::RenderSurfaceReason::kFilterAnimation; + } else if (!effect.BackdropFilter().IsEmpty()) { + effect_node.render_surface_reason = + cc::RenderSurfaceReason::kBackdropFilter; + } else if (effect.HasActiveBackdropFilterAnimation()) { + effect_node.render_surface_reason = + cc::RenderSurfaceReason::kBackdropFilterAnimation; + } else if (blend_mode != SkBlendMode::kSrcOver && + blend_mode != SkBlendMode::kDstIn) { + effect_node.render_surface_reason = cc::RenderSurfaceReason::kBlendMode; } effect_node.opacity = effect.Opacity();
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h index 2b6ab16..6eb8ada0 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h +++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h
@@ -22,6 +22,7 @@ class TransformTree; struct EffectNode; struct TransformNode; +enum class RenderSurfaceReason : uint8_t; } namespace blink { @@ -198,7 +199,7 @@ CcEffectType, const EffectPaintPropertyNode&, const ClipPaintPropertyNode&); - void SetCurrentEffectHasRenderSurface(); + void SetCurrentEffectRenderSurfaceReason(cc::RenderSurfaceReason); cc::TransformTree& GetTransformTree(); cc::ClipTree& GetClipTree();
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc index 7229c9e..5d0233c2 100644 --- a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc +++ b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
@@ -1,12 +1,32 @@ #include "third_party/blink/renderer/platform/graphics/dark_mode_filter.h" +#include "base/optional.h" +#include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/effects/SkHighContrastFilter.h" #include "third_party/skia/include/effects/SkTableColorFilter.h" namespace blink { -DarkModeFilter::DarkModeFilter() : default_filter_(nullptr) { +namespace { + +bool ShouldApplyToImage(const DarkModeSettings& settings, + const FloatRect& src_rect, + Image* image) { + switch (settings.image_policy) { + case DarkModeImagePolicy::kFilterSmart: + return image->ShouldApplyDarkModeFilter(src_rect); + case DarkModeImagePolicy::kFilterAll: + return true; + default: + return false; + } +} + +} // namespace + +DarkModeFilter::DarkModeFilter() + : default_filter_(nullptr), image_filter_(nullptr) { settings_.mode = DarkMode::kOff; settings_.image_policy = DarkModeImagePolicy::kFilterNone; } @@ -18,6 +38,7 @@ switch (settings_.mode) { case DarkMode::kOff: default_filter_.reset(nullptr); + image_filter_.reset(nullptr); return; case DarkMode::kSimpleInvertForTesting: { uint8_t identity[256], invert[256]; @@ -27,6 +48,7 @@ } default_filter_ = SkTableColorFilter::MakeARGB(identity, invert, invert, invert); + image_filter_.reset(nullptr); return; } case DarkMode::kInvertBrightness: @@ -41,32 +63,49 @@ config.fGrayscale = settings_.grayscale; config.fContrast = settings_.contrast; default_filter_ = SkHighContrastFilter::Make(config); -} -sk_sp<SkColorFilter> DarkModeFilter::GetColorFilter() { - return default_filter_; -} - -bool DarkModeFilter::ShouldApplyToImage(Image& image, - const FloatRect& src_rect) { - if (!GetColorFilter()) - return false; - - switch (settings_.image_policy) { - case DarkModeImagePolicy::kFilterSmart: - return image.ShouldApplyDarkModeFilter(src_rect); - case DarkModeImagePolicy::kFilterAll: - return true; - default: - return false; + if (settings_.image_style == DarkModeImageStyle::kGrayscale) { + config.fGrayscale = true; + image_filter_ = SkHighContrastFilter::Make(config); + } else { + image_filter_.reset(nullptr); } } -Color DarkModeFilter::Apply(const Color& color) { - sk_sp<SkColorFilter> filter = GetColorFilter(); - if (!filter) +Color DarkModeFilter::ApplyIfNeeded(const Color& color) { + if (!default_filter_) return color; - return Color(filter->filterColor(color.Rgb())); + return Color(default_filter_->filterColor(color.Rgb())); +} + +// TODO(gilmanmh): Investigate making |image| a const reference. This code +// relies on Image::ShouldApplyDarkModeFilter(), which is not const. If it could +// be made const, then |image| could also be const. +void DarkModeFilter::ApplyToImageFlagsIfNeeded(const FloatRect& src_rect, + Image* image, + cc::PaintFlags* flags) { + sk_sp<SkColorFilter> filter = image_filter_; + if (!filter) + filter = default_filter_; + + if (!filter || !ShouldApplyToImage(settings(), src_rect, image)) + return; + flags->setColorFilter(std::move(filter)); +} + +base::Optional<cc::PaintFlags> DarkModeFilter::ApplyToFlagsIfNeeded( + const cc::PaintFlags& flags) { + if (!default_filter_) + return base::nullopt; + + cc::PaintFlags dark_mode_flags = flags; + if (flags.HasShader()) { + dark_mode_flags.setColorFilter(default_filter_); + } else { + dark_mode_flags.setColor(default_filter_->filterColor(flags.getColor())); + } + + return base::make_optional<cc::PaintFlags>(std::move(dark_mode_flags)); } } // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter.h b/third_party/blink/renderer/platform/graphics/dark_mode_filter.h index 6052af1..3f8a1fae 100644 --- a/third_party/blink/renderer/platform/graphics/dark_mode_filter.h +++ b/third_party/blink/renderer/platform/graphics/dark_mode_filter.h
@@ -1,6 +1,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_FILTER_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DARK_MODE_FILTER_H_ +#include "cc/paint/paint_flags.h" #include "third_party/blink/renderer/platform/geometry/float_rect.h" #include "third_party/blink/renderer/platform/graphics/color.h" #include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h" @@ -18,15 +19,22 @@ const DarkModeSettings& settings() const { return settings_; } void UpdateSettings(const DarkModeSettings& new_settings); - sk_sp<SkColorFilter> GetColorFilter(); + Color ApplyIfNeeded(const Color& color); - bool ShouldApplyToImage(Image& image, const FloatRect& src_rect); + // |image| and |flags| must not be null. + void ApplyToImageFlagsIfNeeded(const FloatRect& src_rect, + Image* image, + cc::PaintFlags* flags); - Color Apply(const Color& color); + // |flags| must not be null. + base::Optional<cc::PaintFlags> ApplyToFlagsIfNeeded( + const cc::PaintFlags& flags); private: DarkModeSettings settings_; + sk_sp<SkColorFilter> default_filter_; + sk_sp<SkColorFilter> image_filter_; }; } // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_settings.h b/third_party/blink/renderer/platform/graphics/dark_mode_settings.h index 42f67d8..21d1d9e2 100644 --- a/third_party/blink/renderer/platform/graphics/dark_mode_settings.h +++ b/third_party/blink/renderer/platform/graphics/dark_mode_settings.h
@@ -25,6 +25,14 @@ kFilterSmart, }; +// For images that should have a filter applied, which filter should be used? +enum class DarkModeImageStyle { + // Invert images the same way as other elements + kDefault, + // Apply grayscale to images as well as inverting them + kGrayscale +}; + enum class DarkModePagePolicy { // Apply dark-mode filter to all frames, regardless of content. kFilterAll, @@ -37,6 +45,7 @@ bool grayscale = false; float contrast = 0.0; // Valid range from -1.0 to 1.0 DarkModeImagePolicy image_policy = DarkModeImagePolicy::kFilterAll; + DarkModeImageStyle image_style = DarkModeImageStyle::kDefault; }; } // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc index d866b641..3c14d62 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc +++ b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.cc
@@ -267,42 +267,6 @@ return IntSize(width, height); } -void XRWebGLDrawingBuffer::OverwriteColorBufferFromMailboxTexture( - const gpu::MailboxHolder& mailbox_holder, - const IntSize& size_in) { - TRACE_EVENT0("gpu", __FUNCTION__); - gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL(); - - gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); - - GLuint source_texture = - gl->CreateAndConsumeTextureCHROMIUM(mailbox_holder.mailbox.name); - - GLuint dest_texture = back_color_buffer_->texture_id; - - // TODO(836496): clean this up and move some of the math to call site. - int dest_width = size_.Width(); - int dest_height = size_.Height(); - int source_width = size_in.Width(); - int source_height = size_in.Height(); - - int copy_width = std::min(source_width, dest_width); - int copy_height = std::min(source_height, dest_height); - - // If the source is too small, center the image. - int dest_x0 = source_width < dest_width ? (dest_width - source_width) / 2 : 0; - int dest_y0 = - source_height < dest_height ? (dest_height - source_height) / 2 : 0; - int src_x0 = source_width > dest_width ? (source_width - dest_width) / 2 : 0; - int src_y0 = - source_height > dest_height ? (source_height - dest_height) / 2 : 0; - - gl->CopySubTextureCHROMIUM( - source_texture, 0, GL_TEXTURE_2D, dest_texture, 0, dest_x0, dest_y0, - src_x0, src_y0, copy_width, copy_height, false /* flipY */, - false /* premultiplyAlpha */, false /* unmultiplyAlpha */); -} - void XRWebGLDrawingBuffer::UseSharedBuffer( const gpu::MailboxHolder& buffer_mailbox_holder) { DVLOG(3) << __FUNCTION__;
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h index 157ce76..c723ce9 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h +++ b/third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h
@@ -43,9 +43,6 @@ void Resize(const IntSize&); - void OverwriteColorBufferFromMailboxTexture(const gpu::MailboxHolder&, - const IntSize& size); - scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage( std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback);
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc index 340fbf38..efe0bd83 100644 --- a/third_party/blink/renderer/platform/graphics/graphics_context.cc +++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -59,25 +59,23 @@ namespace blink { +// Effectively allows modifying the provided |flags| without technically +// violating its constness. +// +// TODO(gilmanmh): Investigate removing const from |flags| in the calling +// methods so that this isn't necessary. class GraphicsContext::DarkModeFlags final { STACK_ALLOCATED(); public: // This helper's lifetime should never exceed |flags|'. DarkModeFlags(GraphicsContext* gc, const PaintFlags& flags) { - sk_sp<SkColorFilter> filter = gc->dark_mode_filter_.GetColorFilter(); - if (!filter) { - flags_ = &flags; - } else { - dark_mode_flags_ = flags; - if (flags.HasShader()) { - dark_mode_flags_->setColorFilter(filter); - } else { - dark_mode_flags_->setColor(filter->filterColor(flags.getColor())); - } - + dark_mode_flags_ = gc->dark_mode_filter_.ApplyToFlagsIfNeeded(flags); + if (dark_mode_flags_) { flags_ = &dark_mode_flags_.value(); + return; } + flags_ = &flags; } operator const PaintFlags&() const { return *flags_; } @@ -387,15 +385,15 @@ void GraphicsContext::DrawFocusRingPath(const SkPath& path, const Color& color, float width) { - DrawPlatformFocusRing(path, canvas_, dark_mode_filter_.Apply(color).Rgb(), - width); + DrawPlatformFocusRing(path, canvas_, + dark_mode_filter_.ApplyIfNeeded(color).Rgb(), width); } void GraphicsContext::DrawFocusRingRect(const SkRect& rect, const Color& color, float width) { - DrawPlatformFocusRing(rect, canvas_, dark_mode_filter_.Apply(color).Rgb(), - width); + DrawPlatformFocusRing(rect, canvas_, + dark_mode_filter_.ApplyIfNeeded(color).Rgb(), width); } void GraphicsContext::DrawFocusRing(const Path& focus_ring_path, @@ -470,7 +468,7 @@ if (ContextDisabled()) return; - Color shadow_color = dark_mode_filter_.Apply(orig_shadow_color); + Color shadow_color = dark_mode_filter_.ApplyIfNeeded(orig_shadow_color); FloatRect hole_rect(rect.Rect()); hole_rect.Inflate(-shadow_spread); @@ -873,9 +871,8 @@ image_flags.setBlendMode(op); image_flags.setColor(SK_ColorBLACK); image_flags.setFilterQuality(ComputeFilterQuality(image, dest, src)); - if (dark_mode_filter_.ShouldApplyToImage(*image, src)) { - image_flags.setColorFilter(dark_mode_filter_.GetColorFilter()); - } + + dark_mode_filter_.ApplyToImageFlagsIfNeeded(src, image, &image_flags); image->Draw(canvas_, image_flags, dest, src, should_respect_image_orientation, Image::kClampImageToSourceRect, decode_mode); @@ -910,9 +907,8 @@ image_flags.setColor(SK_ColorBLACK); image_flags.setFilterQuality( ComputeFilterQuality(image, dest.Rect(), src_rect)); - if (dark_mode_filter_.ShouldApplyToImage(*image, src_rect)) { - image_flags.setColorFilter(dark_mode_filter_.GetColorFilter()); - } + + dark_mode_filter_.ApplyToImageFlagsIfNeeded(src_rect, image, &image_flags); bool use_shader = (visible_src == src_rect) && (respect_orientation == kDoNotRespectImageOrientation); @@ -1122,7 +1118,7 @@ canvas_->drawDRRect(outer, inner, ImmutableState()->FillFlags()); } else { PaintFlags flags(ImmutableState()->FillFlags()); - flags.setColor(dark_mode_filter_.Apply(color).Rgb()); + flags.setColor(dark_mode_filter_.ApplyIfNeeded(color).Rgb()); canvas_->drawDRRect(outer, inner, flags); } @@ -1135,7 +1131,7 @@ stroke_r_rect.inset(stroke_width / 2, stroke_width / 2); PaintFlags stroke_flags(ImmutableState()->FillFlags()); - stroke_flags.setColor(dark_mode_filter_.Apply(color).Rgb()); + stroke_flags.setColor(dark_mode_filter_.ApplyIfNeeded(color).Rgb()); stroke_flags.setStyle(PaintFlags::kStroke_Style); stroke_flags.setStrokeWidth(stroke_width); @@ -1330,7 +1326,7 @@ return; PaintFlags flags(ImmutableState()->FillFlags()); - flags.setColor(dark_mode_filter_.Apply(color).Rgb()); + flags.setColor(dark_mode_filter_.ApplyIfNeeded(color).Rgb()); canvas_->drawDRRect(SkRRect::MakeRect(rect), rounded_hole_rect, flags); }
diff --git a/third_party/blink/renderer/platform/heap/heap_allocator.cc b/third_party/blink/renderer/platform/heap/heap_allocator.cc index 411f19a..748fe6ec 100644 --- a/third_party/blink/renderer/platform/heap/heap_allocator.cc +++ b/third_party/blink/renderer/platform/heap/heap_allocator.cc
@@ -106,6 +106,13 @@ return false; HeapObjectHeader* header = HeapObjectHeader::FromPayload(address); + + // Compaction may register slots for compaction in slots of vector backings. + // E.g., when vectors are embedded in each other. To avoid dereferincing a + // broken slot, bail out on already marked backings. + if (header->IsMarked()) + return false; + NormalPageArena* arena = static_cast<NormalPage*>(page)->ArenaForNormalPage(); // We shrink the object only if the shrinking will make a non-small // prompt-free block.
diff --git a/third_party/blink/renderer/platform/text/character.cc b/third_party/blink/renderer/platform/text/character.cc index f5877135..381135c 100644 --- a/third_party/blink/renderer/platform/text/character.cc +++ b/third_party/blink/renderer/platform/text/character.cc
@@ -124,19 +124,19 @@ RETURN_HAS_PROPERTY(character, kIsHangul); } -unsigned Character::ExpansionOpportunityCount(const LChar* characters, - unsigned length, - TextDirection direction, - bool& is_after_expansion, - const TextJustify text_justify) { - unsigned count = 0; +unsigned Character::ExpansionOpportunityCount( + base::span<const LChar> characters, + TextDirection direction, + bool& is_after_expansion, + const TextJustify text_justify) { if (text_justify == TextJustify::kDistribute) { is_after_expansion = true; - return length; + return characters.size(); } + unsigned count = 0; if (direction == TextDirection::kLtr) { - for (unsigned i = 0; i < length; ++i) { + for (unsigned i = 0; i < characters.size(); ++i) { if (TreatAsSpace(characters[i])) { count++; is_after_expansion = true; @@ -145,7 +145,7 @@ } } } else { - for (unsigned i = length; i > 0; --i) { + for (unsigned i = characters.size(); i > 0; --i) { if (TreatAsSpace(characters[i - 1])) { count++; is_after_expansion = true; @@ -158,21 +158,21 @@ return count; } -unsigned Character::ExpansionOpportunityCount(const UChar* characters, - unsigned length, - TextDirection direction, - bool& is_after_expansion, - const TextJustify text_justify) { +unsigned Character::ExpansionOpportunityCount( + base::span<const UChar> characters, + TextDirection direction, + bool& is_after_expansion, + const TextJustify text_justify) { unsigned count = 0; if (direction == TextDirection::kLtr) { - for (unsigned i = 0; i < length; ++i) { + for (unsigned i = 0; i < characters.size(); ++i) { UChar32 character = characters[i]; if (TreatAsSpace(character)) { count++; is_after_expansion = true; continue; } - if (U16_IS_LEAD(character) && i + 1 < length && + if (U16_IS_LEAD(character) && i + 1 < characters.size() && U16_IS_TRAIL(characters[i + 1])) { character = U16_GET_SUPPLEMENTARY(character, characters[i + 1]); i++; @@ -188,7 +188,7 @@ is_after_expansion = false; } } else { - for (unsigned i = length; i > 0; --i) { + for (unsigned i = characters.size(); i > 0; --i) { UChar32 character = characters[i - 1]; if (TreatAsSpace(character)) { count++;
diff --git a/third_party/blink/renderer/platform/text/character.h b/third_party/blink/renderer/platform/text/character.h index 5818db4..60ef74c 100644 --- a/third_party/blink/renderer/platform/text/character.h +++ b/third_party/blink/renderer/platform/text/character.h
@@ -31,6 +31,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_CHARACTER_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TEXT_CHARACTER_H_ +#include "base/containers/span.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/text/character_property.h" #include "third_party/blink/renderer/platform/text/text_direction.h" @@ -77,25 +78,22 @@ return c < 0x1100 ? false : IsHangulSlow(c); } - static unsigned ExpansionOpportunityCount(const LChar*, - unsigned length, + static unsigned ExpansionOpportunityCount(base::span<const LChar>, TextDirection, bool& is_after_expansion, const TextJustify); - static unsigned ExpansionOpportunityCount(const UChar*, - unsigned length, + static unsigned ExpansionOpportunityCount(base::span<const UChar>, TextDirection, bool& is_after_expansion, const TextJustify); static unsigned ExpansionOpportunityCount(const TextRun& run, bool& is_after_expansion) { if (run.Is8Bit()) - return ExpansionOpportunityCount(run.Characters8(), run.length(), - run.Direction(), is_after_expansion, + return ExpansionOpportunityCount(run.Span8(), run.Direction(), + is_after_expansion, run.GetTextJustify()); - return ExpansionOpportunityCount(run.Characters16(), run.length(), - run.Direction(), is_after_expansion, - run.GetTextJustify()); + return ExpansionOpportunityCount(run.Span16(), run.Direction(), + is_after_expansion, run.GetTextJustify()); } static bool IsUprightInMixedVertical(UChar32 character);
diff --git a/third_party/blink/renderer/platform/text/text_run.h b/third_party/blink/renderer/platform/text/text_run.h index c39f194..cb8d949 100644 --- a/third_party/blink/renderer/platform/text/text_run.h +++ b/third_party/blink/renderer/platform/text/text_run.h
@@ -26,6 +26,7 @@ #include <unicode/utf16.h> +#include "base/containers/span.h" #include "base/optional.h" #include "third_party/blink/renderer/platform/heap/heap.h" #include "third_party/blink/renderer/platform/platform_export.h" @@ -163,6 +164,16 @@ return &data_.characters16[i]; } + // Prefer Span8() and Span16() to Characters8() and Characters16(). + base::span<const LChar> Span8() const { + DCHECK(Is8Bit()); + return {data_.characters8, len_}; + } + base::span<const UChar> Span16() const { + DCHECK(!Is8Bit()); + return {data_.characters16, len_}; + } + const LChar* Characters8() const { DCHECK(Is8Bit()); return data_.characters8;
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 096e243..e4060b9 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -6332,3 +6332,5 @@ # Sheriff 2019-05-01 crbug.com/958347 [ Linux ] external/wpt/editing/run/removeformat.html [ Pass Crash ] +crbug.com/941931 [ Linux Win ] virtual/outofblink-cors/http/tests/security/contentSecurityPolicy/1.1/plugintypes-affects-cross-site-child-disallowed.html [ Pass Failure ] +crbug.com/958426 [ Mac10.13 ] fast/text/line-break-ascii.html [ Timeout Pass ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites index 21d9b5f..26ef64b 100644 --- a/third_party/blink/web_tests/VirtualTestSuites +++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -449,6 +449,11 @@ }, { "prefix": "dark-mode", + "base": "paint/dark-mode/grayscale-images", + "args": ["--blink-settings=darkMode=3,darkModeImagePolicy=0,darkModeImageStyle=1"] + }, + { + "prefix": "dark-mode", "base": "paint/dark-mode/image-filter-all", "args": ["--blink-settings=darkMode=3,darkModeImagePolicy=0"] },
diff --git a/third_party/blink/web_tests/WebDriverExpectations b/third_party/blink/web_tests/WebDriverExpectations index 11785794..eab9a49 100644 --- a/third_party/blink/web_tests/WebDriverExpectations +++ b/third_party/blink/web_tests/WebDriverExpectations
@@ -8,13 +8,11 @@ crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_screenshot/user_prompts.py>>test_dismiss[capabilities0-confirm] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/execute_script/promise.py>>test_promise_all_resolve [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_window/user_prompts.py>>test_default[prompt-None] [ Failure ] -crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_click/scroll_into_view.py>>test_partially_visible_does_not_scroll[1] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_non_secure_context[denied] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_invalid_parameters[parameters2] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_send_keys/interactability.py>>test_document_element_is_interactable [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/fullscreen_window/stress.py>>test_stress[4] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_click/bubbling.py>>test_spin_event_loop [ Failure ] -crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_click/scroll_into_view.py>>test_partially_visible_does_not_scroll[6] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_screenshot/user_prompts.py>>test_ignore[capabilities0-confirm] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/execute_script/promise.py>>test_promise_reject [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_set_to_state[realmSetting2-denied] [ Failure ] @@ -64,14 +62,12 @@ crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_screenshot/user_prompts.py>>test_accept_and_notify[capabilities0-alert] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/perform_actions/key_special_keys.py>>test_codepoint_keys_behave_correctly[\u1100\u1161\u11a8] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/status/status.py>>test_status_with_session_running_on_endpoint_node [ Failure ] -crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_click/scroll_into_view.py>>test_partially_visible_does_not_scroll[9] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_send_keys/scroll_into_view.py>>test_option_select_container_outside_of_scrollable_viewport [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_screenshot/user_prompts.py>>test_default[prompt] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_element_screenshot/user_prompts.py>>test_dismiss[capabilities0-alert] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_set_to_state[realmSetting0-denied] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/fullscreen_window/user_prompts.py>>test_dismiss[capabilities0-alert-None] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/maximize_window/maximize.py>>test_maximize_when_resized_to_max_size [ Failure ] -crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_click/scroll_into_view.py>>test_partially_visible_does_not_scroll[3] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/execute_script/json_serialize_windowproxy.py>>test_window_open [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_window/user_prompts.py>>test_dismiss_and_notify[capabilities0-alert-None] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_window/new.py>>test_no_browsing_context [ Failure ] @@ -85,11 +81,9 @@ crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/execute_script/promise.py>>test_promise_resolve_timeout [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_window/user_prompts.py>>test_ignore[capabilities0-confirm] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_screenshot/user_prompts.py>>test_dismiss_and_notify[capabilities0-prompt] [ Failure ] -crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_click/scroll_into_view.py>>test_partially_visible_does_not_scroll[8] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/perform_actions/key_events.py>>test_modifier_key_sends_correct_events[\ue03d-META] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_element_screenshot/user_prompts.py>>test_dismiss_and_notify[capabilities0-prompt] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_screenshot/user_prompts.py>>test_accept[capabilities0-prompt] [ Failure ] -crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_click/scroll_into_view.py>>test_partially_visible_does_not_scroll[2] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_window/new_window.py>>test_new_window_opens_about_blank [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_session/merge.py>>test_merge_browserName [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_session/default_values.py>>test_no_capabilites [ Failure ] @@ -117,7 +111,6 @@ crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/execute_script/promise.py>>test_await_promise_reject [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_element_screenshot/user_prompts.py>>test_default[confirm] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/fullscreen_window/stress.py>>test_stress[3] [ Failure ] -crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_click/scroll_into_view.py>>test_partially_visible_does_not_scroll[5] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_window/new.py>>test_type_with_invalid_type[type_hint3] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_window/user_prompts.py>>test_ignore[capabilities0-prompt] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_window/user_prompts.py>>test_dismiss[capabilities0-confirm-False] [ Failure ] @@ -134,7 +127,6 @@ crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/perform_actions/pointer_contextmenu.py>>test_control_click[\ue009-ctrlKey] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_window/new.py>>test_type_with_null_value [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_element_screenshot/user_prompts.py>>test_dismiss[capabilities0-confirm] [ Failure ] -crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_click/scroll_into_view.py>>test_partially_visible_does_not_scroll[4] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/perform_actions/pointer_contextmenu.py>>test_release_control_click [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/execute_script/promise.py>>test_promise_resolve_delayed [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/take_element_screenshot/user_prompts.py>>test_accept[capabilities0-alert] [ Failure ] @@ -159,7 +151,6 @@ crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/execute_script/json_serialize_windowproxy.py>>test_initial_window [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_clear/clear.py>>test_contenteditable [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_invalid_parameters[parameters3] [ Failure ] -crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_click/scroll_into_view.py>>test_partially_visible_does_not_scroll[7] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_window/user_prompts.py>>test_accept_and_notify[capabilities0-confirm-True] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_window/new_tab.py>>test_new_tab_opens_about_blank [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_session/default_values.py>>test_valid_but_unmatchable_key [ Failure ] @@ -174,4 +165,4 @@ crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_window/new_window.py>>test_new_window_sets_no_opener [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/new_window/user_prompts.py>>test_dismiss_and_notify[capabilities0-prompt-None] [ Failure ] crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_set_to_state[realmSetting2-prompt] [ Failure ] -crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_set_to_state_cross_realm[realmSetting0-prompt] [ Failure ] \ No newline at end of file +crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/permissions/set.py>>test_set_to_state_cross_realm[realmSetting0-prompt] [ Failure ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json index 04d89a81..29f1c41 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -459570,7 +459570,7 @@ "support" ], "interfaces/IndexedDB.idl": [ - "21ff252fffe57f1c49649790b7be8d59083aef86", + "f2625ebe53ddde37b5625802b7a417220e7cafbd", "support" ], "interfaces/InputDeviceCapabilities.idl": [ @@ -460038,7 +460038,7 @@ "support" ], "interfaces/wake-lock.idl": [ - "c0d4bc9c239aae45d48924ef79c6fe354bbe7204", + "466d697cff81c37465c1f7ed73d40a93301832a2", "support" ], "interfaces/wasm-js-api.idl": [ @@ -498642,7 +498642,7 @@ "support" ], "wake-lock/idlharness.https.any-expected.txt": [ - "0f847f0ba3997d884cbdd8cb06fda6b3bbd1e47d", + "2dc5105bb38d7f0244841e1cfcc05ea022b382a7", "support" ], "wake-lock/idlharness.https.any.js": [ @@ -498650,7 +498650,7 @@ "testharness" ], "wake-lock/idlharness.https.any.worker-expected.txt": [ - "0d9d4a9e4c28a28e406dfb29ad2b3731b739ed18", + "86359c7da0dbe79366836d82d30c0dff279b87f8", "support" ], "wake-lock/wakelock-applicability-manual.https-expected.txt": [
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/background-image-data-uri.html b/third_party/blink/web_tests/external/wpt/element-timing/background-image-data-uri.html index 696f34ff..16d6dfcb 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/background-image-data-uri.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/background-image-data-uri.html
@@ -25,7 +25,7 @@ // Only the first characters of the data URI are included in the entry. const uriPrefix = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKAQMAAAC3/F3+AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAA'; checkElementWithoutResourceTiming(entry, uriPrefix, 'my_div', 'target', - beforeRender); + beforeRender, document.getElementById('target')); // The background image is a red square of length 10. checkRect(entry, [0, 100, 0, 50]); checkNaturalSize(entry, 10, 10);
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/background-image-multiple-elements.html b/third_party/blink/web_tests/external/wpt/element-timing/background-image-multiple-elements.html index 669f94d..22b4158 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/background-image-multiple-elements.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/background-image-multiple-elements.html
@@ -36,14 +36,16 @@ numObservedElements++; if (entry.id == 'div1') { observedDiv1 = true; - checkElement(entry, pathname, 'et1', 'div1', beforeRender); + checkElement(entry, pathname, 'et1', 'div1', beforeRender, + document.getElementById('div1')); // Div is in the top left corner. checkRect(entry, [0, 100, 0, 100]); checkNaturalSize(entry, 100, 100); } else if (entry.id == 'div2') { observedDiv2 = true; - checkElement(entry, pathname, 'et2', 'div2', beforeRender); + checkElement(entry, pathname, 'et2', 'div2', beforeRender, + document.getElementById('div2')); // Div is below div1, on the left. checkRect(entry, [0, 200, 100, 200]); checkNaturalSize(entry, 100, 100);
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/background-image-stretched.html b/third_party/blink/web_tests/external/wpt/element-timing/background-image-stretched.html index 8f93b43..28c355398 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/background-image-stretched.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/background-image-stretched.html
@@ -25,7 +25,8 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/square100.png'; - checkElement(entry, pathname, 'my_div', 'target', beforeRender); + checkElement(entry, pathname, 'my_div', 'target', beforeRender, + document.getElementById('target')); // The background image extends to occupy to full size of the div. checkRect(entry, [0, 200, 0, 150]); // The natural size of the square remains unchanged.
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html b/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html index 805777f2..03c7048 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/buffer-before-onload.html
@@ -28,7 +28,7 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/square20.jpg'; - checkElement(entry, pathname, 'my_image', 'my_id', beforeRender); + checkElement(entry, pathname, 'my_image', 'my_id', beforeRender, img); checkNaturalSize(entry, 20, 20); }); }, "Element Timing: image loads before onload.");
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/cross-origin-element.sub.html b/third_party/blink/web_tests/external/wpt/element-timing/cross-origin-element.sub.html index b1a5b7c..0af0ae96 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/cross-origin-element.sub.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/cross-origin-element.sub.html
@@ -12,13 +12,14 @@ <script src="resources/element-timing-helpers.js"></script> <script> async_test((t) => { + let img; const pathname = 'http://{{domains[www]}}:{{ports[http][1]}}' + '/element-timing/resources/square100.png'; const observer = new PerformanceObserver( t.step_func_done((entryList) => { assert_equals(entryList.getEntries().length, 1); const entry = entryList.getEntries()[0]; - checkElement(entry, pathname, 'my_image', 'the_id', 0); + checkElement(entry, pathname, 'my_image', 'the_id', 0, img); assert_equals(entry.startTime, 0, 'The startTime of a cross-origin image should be 0.'); checkRect(entry, [0, 100, 0, 100]); @@ -31,7 +32,7 @@ // TODO(npm): change observer to use buffered flag. window.onload = t.step_func(() => { // Add a cross origin image resource. - const img = document.createElement('img'); + img = document.createElement('img'); img.src = pathname; img.setAttribute('elementtiming', 'my_image'); img.setAttribute('id', 'the_id');
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/disconnect-image.html b/third_party/blink/web_tests/external/wpt/element-timing/disconnect-image.html new file mode 100644 index 0000000..4ee0516 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/element-timing/disconnect-image.html
@@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<meta charset=utf-8> +<title>Element Timing: element attribute returns null when element is disconnected</title> +<body> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/element-timing-helpers.js"></script> +<script> + let beforeRender; + let img; + async_test(function (t) { + const observer = new PerformanceObserver( + t.step_func_done(function(entryList) { + assert_equals(entryList.getEntries().length, 1); + const entry = entryList.getEntries()[0]; + const index = window.location.href.lastIndexOf('/'); + const pathname = window.location.href.substring(0, index) + + '/resources/square100.png'; + // This method will check that entry.element is |img|. + checkElement(entry, pathname, 'my_image', 'my_id', beforeRender, img); + + img.parentNode.removeChild(img); + // After removing image, entry.element should return null. + assert_equals(entry.element, null); + }) + ); + observer.observe({entryTypes: ['element']}); + // We add the image during onload to be sure that the observer is registered + // in time for it to observe the element timing. + window.onload = () => { + // Add image of width equal to 100 and height equal to 100. + img = document.createElement('img'); + img.src = 'resources/square100.png'; + img.setAttribute('elementtiming', 'my_image'); + img.setAttribute('id', 'my_id'); + document.body.appendChild(img); + beforeRender = performance.now(); + }; + }, 'Disconnected elements have null as their |element| attribute.'); +</script> + +</body>
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html b/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html index 0e24af06..4ec8aa7d 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-TAO-wildcard.sub.html
@@ -13,13 +13,14 @@ <script> async_test((t) => { let beforeRender; + let img; const img_src = 'http://{{domains[www]}}:{{ports[http][1]}}/element-timing/' + 'resources/TAOImage.py?tao=wildcard'; const observer = new PerformanceObserver( t.step_func_done((entryList) => { assert_equals(entryList.getEntries().length, 1); const entry = entryList.getEntries()[0]; - checkElement(entry, img_src, 'my_image', 'my_id', beforeRender); + checkElement(entry, img_src, 'my_image', 'my_id', beforeRender, img); // Assume viewport has size at least 20, so the element is fully visible. checkRect(entry, [0, 20, 0, 20]); checkNaturalSize(entry, 20, 20); @@ -30,7 +31,7 @@ // in time for it to observe the element timing. // TODO(npm): change observer to use buffered flag. window.onload = t.step_func(() => { - const img = document.createElement('img'); + img = document.createElement('img'); img.src = img_src; img.setAttribute('elementtiming', 'my_image'); img.setAttribute('id', 'my_id');
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-carousel.html b/third_party/blink/web_tests/external/wpt/element-timing/image-carousel.html index 9f0ef79e..404eca3 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-carousel.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-carousel.html
@@ -37,13 +37,15 @@ const observer = new PerformanceObserver(list => { list.getEntries().forEach(entry => { if (entry_count % 2 == 0) { - checkElement(entry, pathname0, 'image0', 'image0', beforeRenderTimes[entry_count]); + checkElement(entry, pathname0, 'image0', 'image0', beforeRenderTimes[entry_count], + document.getElementById('image0')); checkRect(entry, [0, 200, 0, 200]); checkNaturalSize(entry, 200, 200); entry_count_per_element[0]++; } else { - checkElement(entry, pathname1, 'image1', 'image1', beforeRenderTimes[entry_count]); + checkElement(entry, pathname1, 'image1', 'image1', beforeRenderTimes[entry_count], + document.getElementById('image1')); checkRect(entry, [0, 100, 0, 100]); checkNaturalSize(entry, 100, 100); entry_count_per_element[1]++;
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-clipped-svg.html b/third_party/blink/web_tests/external/wpt/element-timing/image-clipped-svg.html index 36cf1b1..3007bf7 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-clipped-svg.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-clipped-svg.html
@@ -14,7 +14,8 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/circle.svg'; - checkElement(entry, pathname, 'my_svg', 'SVG', beforeRender); + checkElement(entry, pathname, 'my_svg', 'SVG', beforeRender, + document.getElementById('SVG')); // Image size is 200x200 but SVG size is 100x100 so it is clipped. checkRect(entry, [0, 100, 0, 100]); checkNaturalSize(entry, 200, 200);
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-data-uri.html b/third_party/blink/web_tests/external/wpt/element-timing/image-data-uri.html index 22ff911..2b5d04e45 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-data-uri.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-data-uri.html
@@ -23,7 +23,7 @@ // Only the first characters of the data URI are included in the entry. const uriPrefix = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKAQMAAAC3/F3+AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAA'; checkElementWithoutResourceTiming(entry, uriPrefix, 'my_img', 'inline_wee', - beforeRender); + beforeRender, document.getElementById('inline_wee')); // The image is a red square of length 10. checkRect(entry, [0, 10, 0, 10]); checkNaturalSize(entry, 10, 10);
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-not-fully-visible.html b/third_party/blink/web_tests/external/wpt/element-timing/image-not-fully-visible.html index 279fa03..5716249 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-not-fully-visible.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-not-fully-visible.html
@@ -12,6 +12,7 @@ <script src="resources/element-timing-helpers.js"></script> <script> let beforeRender; + let img; async_test(function (t) { const observer = new PerformanceObserver( t.step_func_done(function(entryList) { @@ -20,7 +21,7 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/square20.png'; - checkElement(entry, pathname, 'not_fully_visible', '', beforeRender); + checkElement(entry, pathname, 'not_fully_visible', '', beforeRender, img); // Image will not be fully visible. It should start from the top left part // of the document, excluding the margin, and then overflow. checkRect(entry, @@ -33,7 +34,7 @@ // in time for it to observe the element timing. window.onload = () => { // Add an image setting width and height equal to viewport. - const img = document.createElement('img'); + img = document.createElement('img'); img.src = 'resources/square20.png'; img.setAttribute('elementtiming', 'not_fully_visible'); img.width = document.documentElement.clientWidth;
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-rect-iframe.html b/third_party/blink/web_tests/external/wpt/element-timing/image-rect-iframe.html index 94c872e..f051130 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-rect-iframe.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-rect-iframe.html
@@ -23,6 +23,7 @@ assert_equals(e.data.naturalWidth, 100); assert_equals(e.data.naturalHeight, 100); assert_equals(e.data.id, 'iframe_img_id'); + assert_equals(e.data.elementId, 'iframe_img_id'); t.done(); }); }, 'Element Timing entry in iframe has coordinates relative to the iframe.');
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-with-css-scale.html b/third_party/blink/web_tests/external/wpt/element-timing/image-with-css-scale.html index 6d77429e..bdffdb26 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-with-css-scale.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-with-css-scale.html
@@ -28,7 +28,8 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index - 14) + 'images/black-rectangle.png'; - checkElement(entry, pathname, 'rectangle', 'rect_id', beforeRender); + checkElement(entry, pathname, 'rectangle', 'rect_id', beforeRender, + document.getElementById('rect_id')); checkRect(entry, [0, 200, 25, 125]); checkNaturalSize(entry, 100, 50); })
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/image-with-rotation.html b/third_party/blink/web_tests/external/wpt/element-timing/image-with-rotation.html index 70b635e..4433ecb 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/image-with-rotation.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/image-with-rotation.html
@@ -28,7 +28,8 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index - 14) + 'images/black-rectangle.png'; - checkElement(entry, pathname, 'rectangle', 'rect_id', beforeRender); + checkElement(entry, pathname, 'rectangle', 'rect_id', beforeRender, + document.getElementById('rect_id')); checkNaturalSize(entry, 100, 50); const rect = entry.intersectionRect; // The div rotates with respect to the origin, so part of it will be invisible.
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/images-repeated-resource.html b/third_party/blink/web_tests/external/wpt/element-timing/images-repeated-resource.html index dbcad24..fbb2d6a1 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/images-repeated-resource.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/images-repeated-resource.html
@@ -15,6 +15,8 @@ let numEntries = 0; let responseEnd1; let responseEnd2; + let img; + let img2; const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/square100.png'; @@ -22,15 +24,18 @@ const observer = new PerformanceObserver( t.step_func(function(entryList) { entryList.getEntries().forEach(entry => { - checkElement(entry, pathname, entry.identifier, 'image_id', beforeRender); + // Easier to check the |element| attribute here since element ID is the same for both images. + checkElement(entry, pathname, entry.identifier, 'image_id', beforeRender, null); checkNaturalSize(entry, 100, 100); if (entry.identifier === 'my_image') { ++numEntries; responseEnd1 = entry.responseEnd; + assert_equals(entry.element, img); } else if (entry.identifier === 'my_image2') { ++numEntries; responseEnd2 = entry.responseEnd; + assert_equals(entry.element, img2); } }); if (numEntries == 2) { @@ -44,13 +49,13 @@ // in time for it to observe the element timing. window.onload = () => { // Add image of width and height equal to 100. - const img = document.createElement('img'); + img = document.createElement('img'); img.src = 'resources/square100.png'; img.setAttribute('elementtiming', 'my_image'); img.setAttribute('id', 'image_id'); document.body.appendChild(img); - const img2 = document.createElement('img'); + img2 = document.createElement('img'); img2.src = 'resources/square100.png'; img2.setAttribute('elementtiming', 'my_image2'); img2.setAttribute('id', 'image_id');
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/multiple-background-images.html b/third_party/blink/web_tests/external/wpt/element-timing/multiple-background-images.html index ca349fe..f3fbe76 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/multiple-background-images.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/multiple-background-images.html
@@ -23,19 +23,20 @@ let observedSquare = false; const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/'; + let div = document.getElementById('target'); const observer = new PerformanceObserver( t.step_func(entryList => { entryList.getEntries().forEach(entry => { numObservedElements++; if (entry.name.endsWith('square100.png')) { observedSquare = true; - checkElement(entry, pathname + 'square100.png', 'multi', 'target', beforeRender); + checkElement(entry, pathname + 'square100.png', 'multi', 'target', beforeRender, div); checkRect(entry, [0, 200, 0, 200]); checkNaturalSize(entry, 100, 100); } else if (entry.name.endsWith('circle.svg')) { observedCircle = true; - checkElement(entry, pathname + 'circle.svg', 'multi', 'target', beforeRender); + checkElement(entry, pathname + 'circle.svg', 'multi', 'target', beforeRender, div); checkRect(entry, [0, 200, 0, 200]); checkNaturalSize(entry, 200, 200); }
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-background-image.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-background-image.html index 0669b4c..680c5e4 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/observe-background-image.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-background-image.html
@@ -25,7 +25,8 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index - 14) + 'images/black-rectangle.png'; - checkElement(entry, pathname, 'my_div', 'target', beforeRender); + checkElement(entry, pathname, 'my_div', 'target', beforeRender, + document.getElementById('target')); checkRect(entry, [0, 100, 0, 50]); checkNaturalSize(entry, 100, 50); })
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-elementtiming.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-elementtiming.html index 39fea05..73f9351 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/observe-elementtiming.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-elementtiming.html
@@ -12,6 +12,7 @@ <script src="resources/element-timing-helpers.js"></script> <script> let beforeRender; + let img; async_test(function (t) { const observer = new PerformanceObserver( t.step_func_done(function(entryList) { @@ -20,7 +21,7 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/square100.png'; - checkElement(entry, pathname, 'my_image', 'my_id', beforeRender); + checkElement(entry, pathname, 'my_image', 'my_id', beforeRender, img); // Assume viewport has size at least 100, so the element is fully visible. checkRect(entry, [0, 100, 0, 100]); checkNaturalSize(entry, 100, 100); @@ -31,7 +32,7 @@ // in time for it to observe the element timing. window.onload = () => { // Add image of width equal to 100 and height equal to 100. - const img = document.createElement('img'); + img = document.createElement('img'); img.src = 'resources/square100.png'; img.setAttribute('elementtiming', 'my_image'); img.setAttribute('id', 'my_id');
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-large-image.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-large-image.html index a08274c..13fc71b 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/observe-large-image.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-large-image.html
@@ -12,6 +12,7 @@ <script src="resources/element-timing-helpers.js"></script> <script> let beforeRender; + let img; async_test(function (t) { const observer = new PerformanceObserver( t.step_func_done(function(entryList) { @@ -20,7 +21,7 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/square20.jpg'; - checkElement(entry, pathname, '', 'large_one', beforeRender); + checkElement(entry, pathname, '', 'large_one', beforeRender, img); // Assume viewport hasn't changed, so the element occupies all of it. checkRect(entry, [0, document.documentElement.clientWidth, 0, document.documentElement.clientHeight]); @@ -32,7 +33,7 @@ // in time for it to observe the element timing. window.onload = () => { // Add an image setting width and height equal to viewport. - const img = document.createElement('img'); + img = document.createElement('img'); img.src = 'resources/square20.jpg'; img.width = document.documentElement.clientWidth; img.height = document.documentElement.clientHeight;
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-multiple-images.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-multiple-images.html index 05c54ac0..b9e82ed 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/observe-multiple-images.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-multiple-images.html
@@ -35,7 +35,8 @@ const pathname1 = window.location.href.substring(0, index) + '/resources/square100.png'; // The images do not contain ID, so expect an empty ID. - checkElement(entry, pathname1, 'image1', 'img1', beforeRender); + checkElement(entry, pathname1, 'image1', 'img1', beforeRender, + document.getElementById('img1')); // This image is horizontally centered. // Using abs and comparing to 1 because the viewport sizes could be odd. // If a size is odd, then image cannot be in the pure center, but left @@ -59,7 +60,8 @@ image2Observed = 1; const pathname2 = window.location.href.substring(0, index) + '/resources/square20.png'; - checkElement(entry, pathname2, 'image2', 'img2', beforeRender); + checkElement(entry, pathname2, 'image2', 'img2', beforeRender, + document.getElementById('img2')); // This image should be below image 1, and should respect the margin. checkRect(entry, [50, 250, 250, 450], "of image2"); checkNaturalSize(entry, 20, 20); @@ -72,7 +74,8 @@ image3Observed = 1; const pathname3 = window.location.href.substring(0, index) + '/resources/circle.svg'; - checkElement(entry, pathname3, 'image3', 'img3', beforeRender); + checkElement(entry, pathname3, 'image3', 'img3', beforeRender, + document.getElementById('img3')); // This image is just to the right of image2. checkRect(entry, [250, 450, 250, 450], "of image3"); checkNaturalSize(entry, 200, 200);
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-shadow-image.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-shadow-image.html index 1fa6dd4..a4d21be 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/observe-shadow-image.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-shadow-image.html
@@ -12,13 +12,14 @@ <div id='target'></div> <script> let beforeRender; + let img; async_test(function (t) { const observer = new PerformanceObserver( t.step_func_done(function(entryList) { assert_equals(entryList.getEntries().length, 1); const entry = entryList.getEntries()[0]; const pathname = window.location.origin + '/element-timing/resources/square100.png'; - checkElement(entry, pathname, 'my_image', 'my_id', beforeRender); + checkElement(entry, pathname, 'my_image', 'my_id', beforeRender, img); // Assume viewport has size at least 100, so the element is fully visible. checkRect(entry, [0, 100, 0, 100]); checkNaturalSize(entry, 100, 100); @@ -29,7 +30,7 @@ // in time for it to observe the element timing. window.onload = () => { // Add image of width equal to 100 and height equal to 100. - const img = document.createElement('img'); + img = document.createElement('img'); img.src = 'resources/square100.png'; img.setAttribute('elementtiming', 'my_image'); img.setAttribute('id', 'my_id');
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-svg-image.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-svg-image.html index 45e800d..c3c178e 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/observe-svg-image.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-svg-image.html
@@ -14,7 +14,8 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/circle.svg'; - checkElement(entry, pathname, 'my_svg', 'svg_id', beforeRender); + checkElement(entry, pathname, 'my_svg', 'svg_id', beforeRender, + document.getElementById('svg_id')); // Assume viewport has size at least 200, so the element is fully visible. checkRect(entry, [0, 200, 0, 200]); checkNaturalSize(entry, 200, 200);
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/observe-video-poster.html b/third_party/blink/web_tests/external/wpt/element-timing/observe-video-poster.html index d3a6993..500fcedc 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/observe-video-poster.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/observe-video-poster.html
@@ -14,7 +14,8 @@ const index = window.location.href.lastIndexOf('/'); const pathname = window.location.href.substring(0, index) + '/resources/circle.svg'; - checkElement(entry, pathname, 'my_poster', 'the_poster', beforeRender); + checkElement(entry, pathname, 'my_poster', 'the_poster', beforeRender, + document.getElementById('the_poster')); // Assume viewport has size at least 200, so the element is fully visible. checkRect(entry, [0, 200, 0, 200]); checkNaturalSize(entry, 200, 200);
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/progressively-loaded-image.html b/third_party/blink/web_tests/external/wpt/element-timing/progressively-loaded-image.html index c0a7d4f..c534621c 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/progressively-loaded-image.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/progressively-loaded-image.html
@@ -8,6 +8,7 @@ <body> <script> let beforeRender; + let img; // Number of characters to be read on the initial read, before sleeping. // Should be sufficient to do at least a first scan. let numInitial = 75; @@ -24,13 +25,13 @@ img_src; // Since the image is only fully loaded after the sleep, the render timestamp // must be greater than |beforeRender| + |sleep|. - checkElement(entry, pathname, 'my_image', '', beforeRender + sleep); + checkElement(entry, pathname, 'my_image', '', beforeRender + sleep, img); checkNaturalSize(entry, 20, 20); }) ); observer.observe({entryTypes: ['element']}); - const img = document.createElement('img'); + img = document.createElement('img'); img.src = img_src; img.setAttribute('elementtiming', 'my_image'); document.body.appendChild(img);
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/rectangular-image.html b/third_party/blink/web_tests/external/wpt/element-timing/rectangular-image.html index b0280845..a1af961 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/rectangular-image.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/rectangular-image.html
@@ -12,6 +12,7 @@ <script src="resources/element-timing-helpers.js"></script> <script> let beforeRender; + let img; async_test(function (t) { const observer = new PerformanceObserver( t.step_func_done(function(entryList) { @@ -21,7 +22,7 @@ // Subtracting 14 to remove 'element-timing'. const pathname = window.location.href.substring(0, index - 14) + 'images/black-rectangle.png'; - checkElement(entry, pathname, 'my_image', 'rectangle', beforeRender); + checkElement(entry, pathname, 'my_image', 'rectangle', beforeRender, img); // Assume viewport has size at least 100, so the element is fully visible. checkRect(entry, [20, 120, 20, 70]); checkNaturalSize(entry, 100, 50); @@ -32,7 +33,7 @@ // in time for it to observe the element timing. window.onload = () => { // Add image of width equal to 100 and height equal to 50. - const img = document.createElement('img'); + img = document.createElement('img'); img.src = '/images/black-rectangle.png'; img.id = 'rectangle'; img.setAttribute('elementtiming', 'my_image');
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js b/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js index b0ddf30..e378d617f 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js +++ b/third_party/blink/web_tests/external/wpt/element-timing/resources/element-timing-helpers.js
@@ -1,6 +1,6 @@ -// Checks that this is an ElementTiming entry with name |expectedName|. It also -// does a very basic check on |startTime|: after |beforeRender| and before now(). -function checkElement(entry, expectedName, expectedIdentifier, expectedID, beforeRender) { +// Common checks between checkElement() and checkElementWithoutResourceTiming(). +function checkElementInternal(entry, expectedName, expectedIdentifier, expectedID, beforeRender, + expectedElement) { assert_equals(entry.entryType, 'element'); assert_equals(entry.name, expectedName); assert_equals(entry.identifier, expectedIdentifier); @@ -8,20 +8,25 @@ assert_equals(entry.id, expectedID); assert_greater_than_equal(entry.startTime, beforeRender); assert_greater_than_equal(performance.now(), entry.startTime); + if (expectedElement !== null) + assert_equals(entry.element, expectedElement); +} + +// Checks that this is an ElementTiming entry with name |expectedName|. It also +// does a very basic check on |startTime|: after |beforeRender| and before now(). +function checkElement(entry, expectedName, expectedIdentifier, expectedID, beforeRender, + expectedElement) { + checkElementInternal(entry, expectedName, expectedIdentifier, expectedID, beforeRender, + expectedElement); const rt_entries = performance.getEntriesByName(expectedName, 'resource'); assert_equals(rt_entries.length, 1); assert_equals(rt_entries[0].responseEnd, entry.responseEnd); } function checkElementWithoutResourceTiming(entry, expectedName, expectedIdentifier, - expectedID, beforeRender) { - assert_equals(entry.entryType, 'element'); - assert_equals(entry.name, expectedName); - assert_equals(entry.identifier, expectedIdentifier); - assert_equals(entry.duration, 0); - assert_equals(entry.id, expectedID); - assert_greater_than_equal(entry.startTime, beforeRender); - assert_greater_than_equal(performance.now(), entry.startTime); + expectedID, beforeRender, expectedElement) { + checkElementInternal(entry, expectedName, expectedIdentifier, expectedID, beforeRender, + expectedElement); // No associated resource from ResourceTiming, so the responseEnd should be 0. assert_equals(entry.responseEnd, 0); }
diff --git a/third_party/blink/web_tests/external/wpt/element-timing/resources/iframe-with-square-sends-entry.html b/third_party/blink/web_tests/external/wpt/element-timing/resources/iframe-with-square-sends-entry.html index 25bd6793..b8af505 100644 --- a/third_party/blink/web_tests/external/wpt/element-timing/resources/iframe-with-square-sends-entry.html +++ b/third_party/blink/web_tests/external/wpt/element-timing/resources/iframe-with-square-sends-entry.html
@@ -15,6 +15,8 @@ 'naturalWidth' : entryList.getEntries()[0].naturalWidth, 'naturalHeight' : entryList.getEntries()[0].naturalHeight, 'id': entryList.getEntries()[0].id, + // Elements cannot be cloned, so just send the element ID. + 'elementId' : entryList.getEntries()[0].element.id, }, '*'); }); observer.observe({entryTypes: ['element']});
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/IndexedDB.idl b/third_party/blink/web_tests/external/wpt/interfaces/IndexedDB.idl index 21ff252f..f2625eb 100644 --- a/third_party/blink/web_tests/external/wpt/interfaces/IndexedDB.idl +++ b/third_party/blink/web_tests/external/wpt/interfaces/IndexedDB.idl
@@ -171,7 +171,7 @@ readonly attribute IDBCursorDirection direction; readonly attribute any key; readonly attribute any primaryKey; - readonly attribute IDBRequest request; + [SameObject] readonly attribute IDBRequest request; void advance([EnforceRange] unsigned long count); void continue(optional any key);
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/wake-lock.idl b/third_party/blink/web_tests/external/wpt/interfaces/wake-lock.idl index c0d4bc9..466d697c 100644 --- a/third_party/blink/web_tests/external/wpt/interfaces/wake-lock.idl +++ b/third_party/blink/web_tests/external/wpt/interfaces/wake-lock.idl
@@ -15,11 +15,14 @@ readonly attribute WakeLockType type; readonly attribute boolean active; attribute EventHandler onactivechange; - Promise<void> request(); - void abort(); + Promise<void> request(optional WakeLockRequestOptions options); static sequence<WakeLock> query(optional WakeLockQueryFilter filter); }; +dictionary WakeLockRequestOptions { + AbortSignal? signal = null; +}; + dictionary WakeLockQueryFilter { WakeLockType? type; boolean? active;
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt index 0f847f0..2dc5105 100644 --- a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt +++ b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt
@@ -10,8 +10,7 @@ PASS WakeLock interface: attribute type PASS WakeLock interface: attribute active PASS WakeLock interface: attribute onactivechange -FAIL WakeLock interface: operation request() assert_own_property: interface prototype object missing non-static operation expected property "request" missing -FAIL WakeLock interface: operation abort() assert_own_property: interface prototype object missing non-static operation expected property "abort" missing +FAIL WakeLock interface: operation request(WakeLockRequestOptions) assert_own_property: interface prototype object missing non-static operation expected property "request" missing FAIL WakeLock interface: operation query(WakeLockQueryFilter) assert_own_property: interface object missing static operation expected property "query" missing FAIL WakeLock must be primary interface of new WakeLock("screen") assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor" FAIL Stringification of new WakeLock("screen") assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor" @@ -20,8 +19,8 @@ FAIL WakeLock interface: new WakeLock("screen") must inherit property "type" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor" FAIL WakeLock interface: new WakeLock("screen") must inherit property "active" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor" FAIL WakeLock interface: new WakeLock("screen") must inherit property "onactivechange" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor" -FAIL WakeLock interface: new WakeLock("screen") must inherit property "request()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor" -FAIL WakeLock interface: new WakeLock("screen") must inherit property "abort()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor" +FAIL WakeLock interface: new WakeLock("screen") must inherit property "request(WakeLockRequestOptions)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor" +FAIL WakeLock interface: calling request(WakeLockRequestOptions) on new WakeLock("screen") with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor" FAIL WakeLock interface: new WakeLock("screen") must inherit property "query(WakeLockQueryFilter)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor" FAIL WakeLock interface: calling query(WakeLockQueryFilter) on new WakeLock("screen") with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor" Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.worker-expected.txt index 0d9d4a9e..86359c7d 100644 --- a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.worker-expected.txt +++ b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.worker-expected.txt
@@ -10,8 +10,7 @@ FAIL WakeLock interface: attribute type assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing FAIL WakeLock interface: attribute active assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing FAIL WakeLock interface: attribute onactivechange assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing -FAIL WakeLock interface: operation request() assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing -FAIL WakeLock interface: operation abort() assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing +FAIL WakeLock interface: operation request(WakeLockRequestOptions) assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing FAIL WakeLock interface: operation query(WakeLockQueryFilter) assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing FAIL WakeLock must be primary interface of new WakeLock("screen") assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined" FAIL Stringification of new WakeLock("screen") assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined" @@ -19,8 +18,8 @@ FAIL WakeLock interface: new WakeLock("screen") must inherit property "type" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined" FAIL WakeLock interface: new WakeLock("screen") must inherit property "active" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined" FAIL WakeLock interface: new WakeLock("screen") must inherit property "onactivechange" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined" -FAIL WakeLock interface: new WakeLock("screen") must inherit property "request()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined" -FAIL WakeLock interface: new WakeLock("screen") must inherit property "abort()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined" +FAIL WakeLock interface: new WakeLock("screen") must inherit property "request(WakeLockRequestOptions)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined" +FAIL WakeLock interface: calling request(WakeLockRequestOptions) on new WakeLock("screen") with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined" FAIL WakeLock interface: new WakeLock("screen") must inherit property "query(WakeLockQueryFilter)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined" FAIL WakeLock interface: calling query(WakeLockQueryFilter) on new WakeLock("screen") with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined" Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/fast/workers/worker-performance-time.html b/third_party/blink/web_tests/fast/workers/worker-performance-time.html new file mode 100644 index 0000000..7bea03f --- /dev/null +++ b/third_party/blink/web_tests/fast/workers/worker-performance-time.html
@@ -0,0 +1,33 @@ +<!DOCTYPE html> +<title>Test the timing in the worker to be consistent between animation and performance.now().</title> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> +<script id="worker" type="text/worker"> +var countWorker = 10; +function animate(time) { + var timeNow = performance.now(); + countWorker--; + self.postMessage([time,timeNow, countWorker]); + if(countWorker > 0) + requestAnimationFrame(animate); +}; + +self.onmessage = function(msg){ + requestAnimationFrame(animate); +} +</script> +<script> +async_test(function(t) { + var blob = new Blob([document.getElementById("worker").textContent]); + var worker = new Worker(URL.createObjectURL(blob)); + worker.onmessage = t.step_func(function(pairTime) { + var time = pairTime.data[0]; + var timeNow = pairTime.data[1]; + var count = pairTime.data[2]; + assert_approx_equals(time, timeNow, 1000, "Times must be close enough"); + if(count == 0) + t.done(); + }); + worker.postMessage(""); +}, 'Test the timing in the worker to be consistent between animation and performance.now'); +</script> \ No newline at end of file
diff --git a/third_party/blink/web_tests/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png b/third_party/blink/web_tests/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png new file mode 100644 index 0000000..87a54af4 --- /dev/null +++ b/third_party/blink/web_tests/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/paint/dark-mode/grayscale-images/desaturate-before-inversion.html b/third_party/blink/web_tests/paint/dark-mode/grayscale-images/desaturate-before-inversion.html new file mode 100644 index 0000000..09ebc168 --- /dev/null +++ b/third_party/blink/web_tests/paint/dark-mode/grayscale-images/desaturate-before-inversion.html
@@ -0,0 +1,4 @@ +<!DOCTYPE html> +<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100"> + <circle cx="50" cy="50" r="40" stroke="black" stroke-width="2" fill="navy" /> +</svg>
diff --git a/third_party/blink/web_tests/platform/mac/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png b/third_party/blink/web_tests/platform/mac/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png new file mode 100644 index 0000000..36b619a --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-mode/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-mode/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png new file mode 100644 index 0000000..9b9bd4a6 --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/virtual/dark-mode/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/grayscale-images/README.txt b/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/grayscale-images/README.txt new file mode 100644 index 0000000..895eda8 --- /dev/null +++ b/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/grayscale-images/README.txt
@@ -0,0 +1,3 @@ +# This suite runs the tests in LayoutTests/paint/dark-mode +# with --blink-settings="darkMode=3,darkModeImagePolicy=0,darkModeImageStyle=1" +# See the virtual_test_suites() method in tools/blinkpy/web_tests/port/base.py.
diff --git a/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png b/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png new file mode 100644 index 0000000..cc7960a --- /dev/null +++ b/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/grayscale-images/desaturate-before-inversion-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt index ad3ea216..0609c9f 100644 --- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt +++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -5354,6 +5354,7 @@ setter onresourcetimingbufferfull interface PerformanceElementTiming : PerformanceEntry attribute @@toStringTag + getter element getter id getter identifier getter intersectionRect
diff --git a/third_party/blink/web_tests/xr/ar_hittest.html b/third_party/blink/web_tests/xr/ar_hittest.html index d99409cf5..1ef6bef 100644 --- a/third_party/blink/web_tests/xr/ar_hittest.html +++ b/third_party/blink/web_tests/xr/ar_hittest.html
@@ -13,10 +13,10 @@ let testName = "Ensures hit-test returns expected mock results"; -let fakeDeviceInitParams = { supportsImmersive: false, +let fakeDeviceInitParams = { supportsImmersive: true, supportsEnvironmentIntegration: true }; -let requestSessionOptions = [ 'legacy-inline-ar' ]; +let requestSessionOptions = [ 'immersive-ar' ]; let expectedHitMatrix = [1, 0, 0, 1, 0, 1, 0, 2, @@ -24,7 +24,7 @@ 0, 0, 0, 1]; let testFunction = function(session, t, fakeDeviceController) { - assert_equals(session.mode, 'legacy-inline-ar'); + assert_equals(session.mode, 'immersive-ar'); assert_not_equals(session.environmentBlendMode, 'opaque'); return session.requestReferenceSpace({ type: "stationary", subtype: "eye-level" }).then((referenceSpace) => { let ray = new XRRay({x : 0.0, y : 0.0, z : 0.0}, {x : 1.0, y : 0.0, z: 0.0});
diff --git a/third_party/blink/web_tests/xr/xrSession_environmentProviderDisconnect.html b/third_party/blink/web_tests/xr/xrSession_environmentProviderDisconnect.html index bd72888..42ea0ee0 100644 --- a/third_party/blink/web_tests/xr/xrSession_environmentProviderDisconnect.html +++ b/third_party/blink/web_tests/xr/xrSession_environmentProviderDisconnect.html
@@ -13,10 +13,10 @@ let testName = "Outstanding promises get rejected if environmentProvider disconencts"; -let fakeDeviceInitParams = { supportsImmersive: false, +let fakeDeviceInitParams = { supportsImmersive: true, supportsEnvironmentIntegration: true }; -let requestSessionOptions = ['legacy-inline-ar']; +let requestSessionOptions = ['immersive-ar']; let refSpace = undefined; let ray = new XRRay({x : 0.0, y : 0.0, z : 0.0}, {x : 1.0, y : 0.0, z: 0.0});
diff --git a/third_party/webxr_test_pages/README.chromium b/third_party/webxr_test_pages/README.chromium index 2bf0d4ff..5bb9adc 100644 --- a/third_party/webxr_test_pages/README.chromium +++ b/third_party/webxr_test_pages/README.chromium
@@ -25,8 +25,7 @@ - Added missing license file and README.chromium for dat.gui - Removed the version shim, the samples are intended to work specifically - with the ToT Chrome version. The AR samples fall back to legacy-inline-ar - mode for now. + with the ToT Chrome version. Instructions:
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-hit-test.html b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-hit-test.html index 4d5dd47..a3df8810 100644 --- a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-hit-test.html +++ b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar-hit-test.html
@@ -107,35 +107,15 @@ textEnterXRTitle: "START AR", textXRNotFoundTitle: "AR NOT FOUND", textExitXRTitle: "EXIT AR", - supportedSessionTypes: ['immersive-ar', 'legacy-inline-ar'] + supportedSessionTypes: ['immersive-ar'] }); document.querySelector('header').appendChild(xrButton.domElement); } - function makeCanvas() { - // Create a fullscreen canvas element for use with legacy AR mode. - let canvas = document.createElement('canvas'); - canvas.style.width = '100%'; - canvas.style.height = '100%'; - canvas.style.left = 0; - canvas.style.top = 0; - canvas.style.right = 0; - canvas.style.bottom = 0; - canvas.style.margin = 0; - canvas.id = 'legacy-canvas'; - return canvas; - } - function onRequestSession() { navigator.xr.requestSession('immersive-ar').then((session) => { xrButton.setSession(session); onSessionStarted(session); - }).catch(() => { - navigator.xr.requestSession('legacy-inline-ar') - .then((session) => { - xrButton.setSession(session); - onSessionStarted(session); - }); }); } @@ -153,13 +133,7 @@ scene.setRenderer(renderer); } - let outputCanvas = makeCanvas(); - document.body.appendChild(outputCanvas); - - session.updateRenderState({ - baseLayer: new XRWebGLLayer(session, gl), - outputContext: outputCanvas.getContext('xrpresent') - }); + session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) }); session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => { xrRefSpace = refSpace; @@ -173,9 +147,6 @@ function onSessionEnded(event) { xrButton.setSession(null); - if (event.session.renderState.outputContext) { - document.body.removeChild(event.session.renderState.outputContext.canvas); - } } // Adds a new object to the scene at the
diff --git a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar.html b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar.html index 8c87585..ba1c455d1 100644 --- a/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar.html +++ b/third_party/webxr_test_pages/webxr-samples/proposals/phone-ar.html
@@ -88,25 +88,11 @@ textEnterXRTitle: "START AR", textXRNotFoundTitle: "AR NOT FOUND", textExitXRTitle: "EXIT AR", - supportedSessionTypes: ['immersive-ar', 'legacy-inline-ar'] + supportedSessionTypes: ['immersive-ar'] }); document.querySelector('header').appendChild(xrButton.domElement); } - function makeCanvas() { - // Create a fullscreen canvas element for use with legacy AR mode. - let canvas = document.createElement('canvas'); - canvas.style.width = '100%'; - canvas.style.height = '100%'; - canvas.style.left = 0; - canvas.style.top = 0; - canvas.style.right = 0; - canvas.style.bottom = 0; - canvas.style.margin = 0; - canvas.id = 'legacy-canvas'; - return canvas; - } - function onRequestSession() { // Requests an inline (non-immersive) session with environment integration // to get AR via video passthrough. @@ -119,12 +105,6 @@ navigator.xr.requestSession('immersive-ar').then((session) => { xrButton.setSession(session); onSessionStarted(session); - }).catch(() => { - navigator.xr.requestSession('legacy-inline-ar') - .then((session) => { - xrButton.setSession(session); - onSessionStarted(session); - }); }); } @@ -141,11 +121,7 @@ scene.setRenderer(renderer); } - let outputCanvas = makeCanvas(); - document.body.appendChild(outputCanvas); - - session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl), - outputContext: outputCanvas.getContext('xrpresent')}); + session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) }); session.requestReferenceSpace({ type: 'stationary', subtype: 'eye-level' }).then((refSpace) => { xrRefSpace = refSpace; @@ -159,9 +135,6 @@ function onSessionEnded(event) { xrButton.setSession(null); - if (event.session.renderState.outputContext) { - document.body.removeChild(event.session.renderState.outputContext.canvas); - } } // Called every time a XRSession requests that a new frame be drawn.
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index e0e07a7..ec95829 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -49,17 +49,7 @@ 'chromium.android': { 'Android ASAN (dbg)': 'android_clang_asan_debug_bot_minimal_symbols', - 'Android Cronet ARM64 Builder': 'android_cronet_release_bot_minimal_symbols_arm64', - 'Android Cronet ARM64 Builder (dbg)': 'android_cronet_debug_static_bot_arm64', - 'Android Cronet Builder': 'android_cronet_release_bot_minimal_symbols_arm_no_neon', - 'Android Cronet Builder (dbg)': 'android_cronet_debug_static_bot_arm_no_neon', - 'Android Cronet Builder Asan': 'android_cronet_release_bot_minimal_symbols_arm_no_neon_clang_asan', - 'Android Cronet KitKat Builder': 'android_cronet_release_bot_minimal_symbols_arm_no_neon', - 'Android Cronet Lollipop Builder': 'android_cronet_release_bot_minimal_symbols_arm_no_neon', - 'Android Cronet Marshmallow 64bit Builder': 'android_cronet_release_bot_minimal_symbols_arm64', 'Android Cronet Marshmallow 64bit Perf': 'android_cronet_release_bot_minimal_symbols_arm64', - 'Android Cronet x86 Builder': 'android_cronet_release_bot_minimal_symbols_x86', - 'Android Cronet x86 Builder (dbg)': 'android_cronet_debug_static_bot_x86', 'Android arm Builder (dbg)': 'android_debug_static_bot', 'Android arm64 Builder (dbg)': 'android_debug_static_bot_arm64', 'Android x64 Builder (dbg)': 'android_debug_static_bot_x64', @@ -85,9 +75,6 @@ }, 'chromium.android.fyi': { - 'Android Cronet Builder (dbg)': 'android_cronet_debug_static_bot_arm_no_neon', - 'Android Cronet Builder Asan': 'android_cronet_release_bot_minimal_symbols_arm_no_neon_clang_asan', - 'Android Cronet KitKat Builder': 'android_cronet_release_bot_minimal_symbols_arm_no_neon', 'Memory Infra Tester': 'android_release_thumb_bot', 'NDK Next arm Builder': 'android_ndk_next_release_bot_minimal_symbols',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index e513c8f..ec44b9e 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -32950,6 +32950,7 @@ <int value="-1160941363" label="AffiliationBasedMatching:disabled"/> <int value="-1160026273" label="enable-web-notification-custom-layouts"/> <int value="-1159563774" label="enable-accessibility-script-injection"/> + <int value="-1159369873" label="TabGroupsUiImprovementsAndroid:disabled"/> <int value="-1158993534" label="PrintScaling:enabled"/> <int value="-1156179600" label="OmniboxRichEntitySuggestions:enabled"/> <int value="-1155543191" label="CopylessPaste:disabled"/> @@ -33307,6 +33308,7 @@ <int value="-612480090" label="FasterLocationReload:enabled"/> <int value="-610411643" label="enable-printer-app-search"/> <int value="-606898702" label="MaterialDesignSettings:disabled"/> + <int value="-606696801" label="TabGroupsUiImprovementsAndroid:enabled"/> <int value="-606431158" label="DrawVerticallyEdgeToEdge:enabled"/> <int value="-604814313" label="enable-pinch"/> <int value="-604269405" @@ -33932,6 +33934,8 @@ <int value="393704200" label="account-consistency"/> <int value="398903399" label="GenericSensorExtraClasses:disabled"/> <int value="399039205" label="enable-webrtc-hw-vp9-encoding"/> + <int value="399398207" + label="OmniboxUIExperimentVerticalMarginLimitToNonTouchOnly:enabled"/> <int value="400272381" label="LazyFrameLoading:disabled"/> <int value="400322063" label="ash-disable-screen-orientation-lock"/> <int value="401983950" label="enable-spdy4"/> @@ -34799,6 +34803,8 @@ <int value="1750822869" label="CrostiniBackup:disabled"/> <int value="1752168018" label="enable-stale-while-revalidate"/> <int value="1755024316" label="HostWindowsInAppShimProcess:disabled"/> + <int value="1758262950" + label="OmniboxUIExperimentVerticalMarginLimitToNonTouchOnly:disabled"/> <int value="1760946944" label="MacViewsAutofillPopup:disabled"/> <int value="1762320532" label="AutofillKeyboardAccessory:enabled"/> <int value="1766676896" label="affiliation-based-matching:disabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index c1e5c6f..f65dca6c 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -5502,6 +5502,15 @@ </summary> </histogram> +<histogram name="Ash.Display.PrimaryDisplayZoomAtStartup" units="%" + expires_after="M79"> + <owner>jamescook@chromium.org</owner> + <owner>jessejames@chromium.org</owner> + <summary> + The display zoom setting for the primary display, recorded on startup. + </summary> +</histogram> + <histogram name="Ash.DisplayColorManager.HasColorCorrectionMatrix" enum="Boolean" expires_after="M77"> <owner>mcasas@chromium.org</owner> @@ -13036,6 +13045,19 @@ </summary> </histogram> +<histogram name="Blink.UseCounter.FeaturePolicy.ImageDownscalingRatio" + units="%" expires_after="M77"> + <owner>loonybear@chromium.org</owner> + <owner>iclelland@chromium.org</owner> + <summary> + Logs downscaling ratio in percentage for images enforced by feature policy + oversized-images policy going into origin trials in M75. If an image's + downscaling ratio is 1, it will be represented as 10 percent, if an image's + downscaling ratio is 5, it will be represented as 50 percents. Recorded when + oversized-images policy is enforced and the image is about to be painted. + </summary> +</histogram> + <histogram name="Blink.UseCounter.FeaturePolicy.ImageFormats" enum="FeaturePolicyImageCompressionFormat" expires_after="M77"> <owner>loonybear@chromium.org</owner> @@ -48411,6 +48433,9 @@ <histogram name="IOS.TabGridMediator.DidDetachNilWebStateList" enum="BooleanNil" expires_after="2019-05-01"> + <obsolete> + Deprecated 2019-04. + </obsolete> <owner>edchin@chromium.org</owner> <owner>marq@chromium.org</owner> <summary> @@ -48426,6 +48451,9 @@ <histogram name="IOS.TabGridMediator.GetActiveTabIDNilWebStateList" enum="BooleanNil" expires_after="2019-05-01"> + <obsolete> + Deprecated 2019-04. + </obsolete> <owner>edchin@chromium.org</owner> <owner>marq@chromium.org</owner> <summary> @@ -49821,6 +49849,12 @@ </summary> </histogram> +<histogram name="Login.DefaultPageZoom" units="%" expires_after="M79"> + <owner>jamescook@chromium.org</owner> + <owner>jessejames@chromium.org</owner> + <summary>The user's default page zoom setting, recorded on login.</summary> +</histogram> + <histogram name="Login.FailureReason" enum="LoginFailureReason"> <owner>achuith@chromium.org</owner> <summary>Chrome OS login failure reason.</summary>
diff --git a/tools/perf/contrib/cluster_telemetry/generic_trace.py b/tools/perf/contrib/cluster_telemetry/generic_trace.py index 653bb36..803c52a 100644 --- a/tools/perf/contrib/cluster_telemetry/generic_trace.py +++ b/tools/perf/contrib/cluster_telemetry/generic_trace.py
@@ -70,7 +70,7 @@ @benchmark.Info(emails=['wangxianzhu@chromium.org'], - documentation_url='https://bit.ly/2IMIMoI') + documentation_url='https://bit.ly/2DIOVy3') # For local verification. class GenericTraceTop25(_GenericTraceBenchmark): page_set = page_sets.StaticTop25PageSet @@ -81,7 +81,7 @@ @benchmark.Info(emails=['wangxianzhu@chromium.org'], - documentation_url='https://bit.ly/2IMIMoI') + documentation_url='https://bit.ly/2DIOVy3') class GenericTraceClusterTelemetry(_GenericTraceBenchmark): @classmethod def Name(cls):
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config index 5c90f6ad..1cbe92ac 100644 --- a/tools/perf/expectations.config +++ b/tools/perf/expectations.config
@@ -164,10 +164,6 @@ crbug.com/865400 [ Pixel_2 Android_Webview ] loading.mobile/VoiceMemos_cold_3g [ Skip ] crbug.com/919191 [ Nexus5X_Webview ] loading.mobile/OLX_3g [ Skip ] -# Benchmark: media.desktop -crbug.com/957977 [ Win ] media.desktop/video.html?src=crowd1080.mp4 [ Skip ] -crbug.com/957977 [ Win ] media.desktop/video.html?src=crowd1080.webm [ Skip ] - # Benchmark: oilpan_gc_times.key_silk_cases crbug.com/446332 [ All ] oilpan_gc_times.key_silk_cases/slide_drawer [ Skip ] crbug.com/507865 [ All ] oilpan_gc_times.key_silk_cases/polymer_topeka [ Skip ] @@ -324,6 +320,7 @@ crbug.com/953371 [ Win ] v8.browsing_desktop/browse:social:twitter_infinite_scroll:2018 [ Skip ] crbug.com/954959 [ Linux ] v8.browsing_desktop/browse:media:pinterest:2018 [ Skip ] crbug.com/954959 [ Linux ] v8.browsing_desktop/browse:tools:maps [ Skip ] +crbug.com/958422 [ Linux ] v8.browsing_desktop/browse:social:tumblr_infinite_scroll:2018 [ Skip ] # Benchmark v8.browsing_desktop-future crbug.com/788796 [ Linux ] v8.browsing_desktop-future/browse:media:imgur [ Skip ] @@ -331,6 +328,7 @@ crbug.com/773084 [ Mac ] v8.browsing_desktop-future/browse:tools:maps [ Skip ] crbug.com/906654 [ All ] v8.browsing_desktop-future/browse:search:google [ Skip ] crbug.com/953371 [ Win ] v8.browsing_desktop-future/browse:social:twitter_infinite_scroll:2018 [ Skip ] +crbug.com/958422 [ Linux ] v8.browsing_desktop-future/browse:social:tumblr_infinite_scroll:2018 [ Skip ] # Benchmark: v8.browsing_mobile crbug.com/714650 [ Android ] v8.browsing_mobile/browse:news:globo [ Skip ]
diff --git a/ui/events/keycodes/dom_us_layout_data.h b/ui/events/keycodes/dom_us_layout_data.h index b61b001..d5a1516 100644 --- a/ui/events/keycodes/dom_us_layout_data.h +++ b/ui/events/keycodes/dom_us_layout_data.h
@@ -555,7 +555,7 @@ // DomCode::LANG3 0x070092 Lang3 // DomCode::LANG4 0x070093 Lang4 // DomCode::LANG5 0x070094 Lang5 - // DomCode::ABORT 0x07009B Abort + {DomCode::ABORT, VKEY_CANCEL}, // 0x07009B Abort // DomCode::PROPS 0x0700A3 Props // DomCode::NUMPAD_PAREN_LEFT 0x0700B6 NumpadParenLeft // DomCode::NUMPAD_PAREN_RIGHT 0x0700B7 NumpadParenRight
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc index 3dd69b5..b71efc47 100644 --- a/ui/native_theme/native_theme.cc +++ b/ui/native_theme/native_theme.cc
@@ -6,8 +6,10 @@ #include <cstring> +#include "base/bind.h" #include "base/command_line.h" #include "ui/base/ui_base_switches.h" +#include "ui/native_theme/dark_mode_observer.h" #include "ui/native_theme/native_theme_observer.h" namespace ui { @@ -48,7 +50,10 @@ is_dark_mode_(IsForcedDarkMode()), is_high_contrast_(IsForcedHighContrast()) {} -NativeTheme::~NativeTheme() {} +NativeTheme::~NativeTheme() { + if (dark_mode_parent_observer_) + dark_mode_parent_observer_->Stop(); +} bool NativeTheme::SystemDarkModeEnabled() const { return is_dark_mode_; @@ -83,4 +88,16 @@ return CaptionStyle::FromSystemSettings(); } +void NativeTheme::SetDarkModeParent(NativeTheme* dark_mode_parent) { + dark_mode_parent_observer_ = std::make_unique<DarkModeObserver>( + dark_mode_parent, + base::BindRepeating(&NativeTheme::OnParentDarkModeChanged, + base::Unretained(this))); + dark_mode_parent_observer_->Start(); +} + +void NativeTheme::OnParentDarkModeChanged(bool is_dark_mode) { + set_dark_mode(is_dark_mode); + NotifyObservers(); +} } // namespace ui
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h index 7e3ee539..15f5391 100644 --- a/ui/native_theme/native_theme.h +++ b/ui/native_theme/native_theme.h
@@ -23,7 +23,7 @@ } namespace ui { - +class DarkModeObserver; class NativeThemeObserver; // This class supports drawing UI controls (like buttons, text fields, lists, @@ -423,6 +423,10 @@ // Returns the system's caption style. virtual CaptionStyle GetSystemCaptionStyle() const; + // Observes |dark_mode_parent| for dark mode changes and propagates them to + // self. + void SetDarkModeParent(NativeTheme* dark_mode_parent); + protected: NativeTheme(); virtual ~NativeTheme(); @@ -442,9 +446,13 @@ unsigned int track_color_; private: + // DarkModeObserver callback. + void OnParentDarkModeChanged(bool is_dark_mode); // Observers to notify when the native theme changes. base::ObserverList<NativeThemeObserver>::Unchecked native_theme_observers_; + std::unique_ptr<DarkModeObserver> dark_mode_parent_observer_; + bool is_dark_mode_ = false; bool is_high_contrast_ = false;
diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc index 24972aa..275f0c7d 100644 --- a/ui/native_theme/native_theme_win.cc +++ b/ui/native_theme/native_theme_win.cc
@@ -266,6 +266,7 @@ L"Themes\\Personalize", KEY_READ | KEY_NOTIFY) == ERROR_SUCCESS; if (key_open_succeeded) { + NativeTheme::GetInstanceForWeb()->SetDarkModeParent(this); UpdateDarkModeStatus(); RegisterThemeRegkeyObserver(); } @@ -1921,7 +1922,6 @@ hkcu_themes_regkey_.StartWatching(base::BindOnce( [](NativeThemeWin* native_theme) { native_theme->UpdateDarkModeStatus(); - native_theme->NotifyObservers(); // RegKey::StartWatching only provides one notification. Reregistration // is required to get future notifications. native_theme->RegisterThemeRegkeyObserver(); @@ -1938,6 +1938,7 @@ fDarkModeEnabled = (apps_use_light_theme == 0); } set_dark_mode(fDarkModeEnabled); + NotifyObservers(); } } // namespace ui
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc index bbcd9e7..48b8161 100644 --- a/ui/views/bubble/bubble_dialog_delegate_view.cc +++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -168,13 +168,8 @@ provider->GetInsetsMetric(INSETS_DIALOG_SUBSECTION)); frame->SetFootnoteView(CreateFootnoteView()); - BubbleBorder::Arrow adjusted_arrow = arrow(); - if (base::i18n::IsRTL()) { - adjusted_arrow = BubbleBorder::horizontal_mirror(adjusted_arrow); - arrow_ = adjusted_arrow; - } std::unique_ptr<BubbleBorder> border = - std::make_unique<BubbleBorder>(adjusted_arrow, GetShadow(), color()); + std::make_unique<BubbleBorder>(arrow(), GetShadow(), color()); if (CustomShadowsSupported() && ShouldHaveRoundCorners()) { border->SetCornerRadius( base::FeatureList::IsEnabled(features::kEnableMDRoundedCornersOnDialogs) @@ -278,6 +273,8 @@ } void BubbleDialogDelegateView::SetArrow(BubbleBorder::Arrow arrow) { + if (base::i18n::IsRTL()) + arrow = BubbleBorder::horizontal_mirror(arrow); if (arrow_ == arrow) return; arrow_ = arrow; @@ -321,12 +318,12 @@ : close_on_deactivate_(true), anchor_view_tracker_(std::make_unique<ViewTracker>()), anchor_widget_(nullptr), - arrow_(arrow), shadow_(shadow), color_explicitly_set_(false), accept_events_(true), adjust_if_offscreen_(true), parent_window_(nullptr) { + SetArrow(arrow); LayoutProvider* provider = LayoutProvider::Get(); // An individual bubble should override these margins if its layout differs // from the typical title/text/buttons.
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.h b/ui/views/bubble/bubble_dialog_delegate_view.h index 887bc541..a28d7a03 100644 --- a/ui/views/bubble/bubble_dialog_delegate_view.h +++ b/ui/views/bubble/bubble_dialog_delegate_view.h
@@ -81,7 +81,7 @@ // The anchor rect is used in the absence of an assigned anchor view. const gfx::Rect& anchor_rect() const { return anchor_rect_; } - BubbleBorder::Arrow arrow() const { return arrow_; } + // Set the desired arrow for the bubble. The arrow will be mirrored for RTL. void SetArrow(BubbleBorder::Arrow arrow); BubbleBorder::Shadow GetShadow() const; @@ -140,6 +140,9 @@ BubbleBorder::Arrow arrow, BubbleBorder::Shadow shadow = BubbleBorder::DIALOG_SHADOW); + // Returns the desired arrow post-RTL mirroring if needed. + BubbleBorder::Arrow arrow() const { return arrow_; } + // Get bubble bounds from the anchor rect and client view's preferred size. virtual gfx::Rect GetBubbleBounds(); @@ -218,8 +221,8 @@ // The anchor rect used in the absence of an anchor view. mutable gfx::Rect anchor_rect_; - // The arrow's location on the bubble. - BubbleBorder::Arrow arrow_; + // The arrow's default location on the bubble post-RTL mirroring if needed. + BubbleBorder::Arrow arrow_ = BubbleBorder::NONE; // Bubble border shadow to use. BubbleBorder::Shadow shadow_;
diff --git a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js index 88c79f23..48b9417 100644 --- a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js +++ b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js
@@ -24,14 +24,11 @@ reflectToAttribute: true, value: false, }, - - /** @private */ - lastValue_: { - type: String, - value: '', - }, }, + /** @private {string} */ + effectiveValue_: '', + /** @private {number} */ searchDelayTimer_: -1, @@ -55,11 +52,16 @@ * firing for this change. */ setValue: function(value, opt_noEvent) { - const searchInput = this.getSearchInput(); - searchInput.value = value; + const updated = this.updateEffectiveValue_(value); + this.getSearchInput().value = this.effectiveValue_; + if (!updated) { + return; + } this.onSearchTermInput(); - this.onValueChanged_(value, !!opt_noEvent); + if (!opt_noEvent) { + this.fire('search-changed', this.effectiveValue_); + } }, /** @private */ @@ -106,18 +108,27 @@ * @private */ onValueChanged_: function(newValue, noEvent) { - // Trim leading whitespace and replace consecutive whitespace with single - // space. This will prevent empty string searches and searches for - // effectively the same query. - const effectiveValue = newValue.replace(/\s+/g, ' ').replace(/^\s/, ''); - if (effectiveValue == this.lastValue_) { - return; + const updated = this.updateEffectiveValue_(newValue); + if (updated && !noEvent) { + this.fire('search-changed', this.effectiveValue_); + } + }, + + /** + * Trim leading whitespace and replace consecutive whitespace with single + * space. This will prevent empty string searches and searches for + * effectively the same query. + * @param {string} value + * @return {boolean} + * @private + */ + updateEffectiveValue_: function(value) { + const effectiveValue = value.replace(/\s+/g, ' ').replace(/^\s/, ''); + if (effectiveValue == this.effectiveValue_) { + return false; } - this.lastValue_ = effectiveValue; - - if (!noEvent) { - this.fire('search-changed', effectiveValue); - } + this.effectiveValue_ = effectiveValue; + return true; }, };