diff --git a/android_webview/browser/aw_browser_manifest_overlay.json b/android_webview/browser/aw_browser_manifest_overlay.json index a138c9d..8356644 100644 --- a/android_webview/browser/aw_browser_manifest_overlay.json +++ b/android_webview/browser/aw_browser_manifest_overlay.json
@@ -6,6 +6,7 @@ "renderer": [ "autofill::mojom::AutofillDriver", "autofill::mojom::PasswordManagerDriver", + "blink::mojom::TextSuggestionHost", "web_restrictions::mojom::WebRestrictions" ] }
diff --git a/base/memory/weak_ptr.cc b/base/memory/weak_ptr.cc index 81f02ea6..93c0bd0 100644 --- a/base/memory/weak_ptr.cc +++ b/base/memory/weak_ptr.cc
@@ -65,6 +65,14 @@ WeakReference::WeakReference(const WeakReference& other) = default; +WeakReference& WeakReference::operator=(WeakReference&& other) { + if (this == &other) + return *this; + flag_ = std::move(other.flag_); + other.flag_ = Flag::NullFlag(); + return *this; +} + WeakReferenceOwner::WeakReferenceOwner() : flag_(WeakReference::Flag::NullFlag()) {}
diff --git a/base/memory/weak_ptr.h b/base/memory/weak_ptr.h index d84e37e..6ea2014 100644 --- a/base/memory/weak_ptr.h +++ b/base/memory/weak_ptr.h
@@ -137,7 +137,7 @@ WeakReference(WeakReference&& other); WeakReference(const WeakReference& other); - WeakReference& operator=(WeakReference&& other) = default; + WeakReference& operator=(WeakReference&& other); WeakReference& operator=(const WeakReference& other) = default; uintptr_t is_valid() const { return flag_->IsValid(); }
diff --git a/base/process/launch_fuchsia.cc b/base/process/launch_fuchsia.cc index e7e6072..e6919fe5 100644 --- a/base/process/launch_fuchsia.cc +++ b/base/process/launch_fuchsia.cc
@@ -158,8 +158,10 @@ bool GetAppOutputWithExitCode(const CommandLine& cl, std::string* output, int* exit_code) { - bool result = GetAppOutputInternal(cl.argv(), false, output, exit_code); - return result && *exit_code == EXIT_SUCCESS; + // Contrary to GetAppOutput(), |true| return here means that the process was + // launched and the exit code was waited upon successfully, but not + // necessarily that the exit code was EXIT_SUCCESS. + return GetAppOutputInternal(cl.argv(), false, output, exit_code); } } // namespace base
diff --git a/base/process/process_util_unittest.cc b/base/process/process_util_unittest.cc index ca3e3354..0f8dd352 100644 --- a/base/process/process_util_unittest.cc +++ b/base/process/process_util_unittest.cc
@@ -79,10 +79,10 @@ #if defined(OS_ANDROID) const char kShellPath[] = "/system/bin/sh"; -const char kPosixShell[] = "sh"; +#elif defined(OS_FUCHSIA) +const char kShellPath[] = "/boot/bin/sh"; #else const char kShellPath[] = "/bin/sh"; -const char kPosixShell[] = "sh"; #endif #endif // defined(OS_POSIX) @@ -724,7 +724,7 @@ TEST_F(ProcessUtilTest, LaunchProcess) { base::EnvironmentMap env_changes; std::vector<std::string> echo_base_test; - echo_base_test.push_back(kPosixShell); + echo_base_test.push_back(kShellPath); echo_base_test.push_back("-c"); echo_base_test.push_back("echo $BASE_TEST");
diff --git a/base/sys_info.cc b/base/sys_info.cc index 80ba8a3..351704d 100644 --- a/base/sys_info.cc +++ b/base/sys_info.cc
@@ -15,11 +15,38 @@ #include "build/build_config.h" namespace base { +namespace { +static const int kLowMemoryDeviceThresholdMB = 512; +} + +// static +int64_t SysInfo::AmountOfPhysicalMemory() { + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableLowEndDeviceMode)) { + return kLowMemoryDeviceThresholdMB * 1024 * 1024; + } + + return AmountOfPhysicalMemoryImpl(); +} + +// static +int64_t SysInfo::AmountOfAvailablePhysicalMemory() { + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableLowEndDeviceMode)) { + // Estimate the available memory by subtracting our memory used estimate + // from the fake |kLowMemoryDeviceThresholdMB| limit. + size_t memory_used = + AmountOfPhysicalMemoryImpl() - AmountOfAvailablePhysicalMemoryImpl(); + size_t memory_limit = kLowMemoryDeviceThresholdMB * 1024 * 1024; + // std::min ensures no underflow, as |memory_used| can be > |memory_limit|. + return memory_limit - std::min(memory_used, memory_limit); + } + + return AmountOfAvailablePhysicalMemoryImpl(); +} #if !defined(OS_ANDROID) -static const int kLowMemoryDeviceThresholdMB = 512; - bool DetectLowEndDevice() { CommandLine* command_line = CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kEnableLowEndDeviceMode))
diff --git a/base/sys_info.h b/base/sys_info.h index 45c2f36..5f68df4 100644 --- a/base/sys_info.h +++ b/base/sys_info.h
@@ -162,6 +162,9 @@ FRIEND_TEST_ALL_PREFIXES(SysInfoTest, AmountOfAvailablePhysicalMemory); FRIEND_TEST_ALL_PREFIXES(debug::SystemMetricsTest, ParseMeminfo); + static int64_t AmountOfPhysicalMemoryImpl(); + static int64_t AmountOfAvailablePhysicalMemoryImpl(); + #if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX) static int64_t AmountOfAvailablePhysicalMemory( const SystemMemoryInfoKB& meminfo);
diff --git a/base/sys_info_freebsd.cc b/base/sys_info_freebsd.cc index 0b2008d5..85916559 100644 --- a/base/sys_info_freebsd.cc +++ b/base/sys_info_freebsd.cc
@@ -12,7 +12,7 @@ namespace base { -int64_t SysInfo::AmountOfPhysicalMemory() { +int64_t SysInfo::AmountOfPhysicalMemoryImpl() { int pages, page_size; size_t size = sizeof(pages); sysctlbyname("vm.stats.vm.v_page_count", &pages, &size, NULL, 0);
diff --git a/base/sys_info_fuchsia.cc b/base/sys_info_fuchsia.cc index fd4b50dc..bfd5358 100644 --- a/base/sys_info_fuchsia.cc +++ b/base/sys_info_fuchsia.cc
@@ -9,7 +9,7 @@ namespace base { // static -int64_t SysInfo::AmountOfPhysicalMemory() { +int64_t SysInfo::AmountOfPhysicalMemoryImpl() { return mx_system_get_physmem(); }
diff --git a/base/sys_info_ios.mm b/base/sys_info_ios.mm index 5fc2eb9..60a75319 100644 --- a/base/sys_info_ios.mm +++ b/base/sys_info_ios.mm
@@ -83,7 +83,7 @@ } // static -int64_t SysInfo::AmountOfPhysicalMemory() { +int64_t SysInfo::AmountOfPhysicalMemoryImpl() { struct host_basic_info hostinfo; mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; base::mac::ScopedMachSendRight host(mach_host_self()); @@ -100,7 +100,7 @@ } // static -int64_t SysInfo::AmountOfAvailablePhysicalMemory() { +int64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() { SystemMemoryInfoKB info; if (!GetSystemMemoryInfo(&info)) return 0;
diff --git a/base/sys_info_linux.cc b/base/sys_info_linux.cc index 0cd05b3..b1fecff4 100644 --- a/base/sys_info_linux.cc +++ b/base/sys_info_linux.cc
@@ -43,12 +43,12 @@ namespace base { // static -int64_t SysInfo::AmountOfPhysicalMemory() { +int64_t SysInfo::AmountOfPhysicalMemoryImpl() { return g_lazy_physical_memory.Get().value(); } // static -int64_t SysInfo::AmountOfAvailablePhysicalMemory() { +int64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() { SystemMemoryInfoKB info; if (!GetSystemMemoryInfo(&info)) return 0;
diff --git a/base/sys_info_mac.mm b/base/sys_info_mac.mm index f6b70c2d..8ba29cc 100644 --- a/base/sys_info_mac.mm +++ b/base/sys_info_mac.mm
@@ -90,7 +90,7 @@ } // static -int64_t SysInfo::AmountOfPhysicalMemory() { +int64_t SysInfo::AmountOfPhysicalMemoryImpl() { struct host_basic_info hostinfo; mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; base::mac::ScopedMachSendRight host(mach_host_self()); @@ -107,7 +107,7 @@ } // static -int64_t SysInfo::AmountOfAvailablePhysicalMemory() { +int64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() { SystemMemoryInfoKB info; if (!GetSystemMemoryInfo(&info)) return 0;
diff --git a/base/sys_info_openbsd.cc b/base/sys_info_openbsd.cc index 9c98784..5a1ad56 100644 --- a/base/sys_info_openbsd.cc +++ b/base/sys_info_openbsd.cc
@@ -42,12 +42,12 @@ } // static -int64_t SysInfo::AmountOfPhysicalMemory() { +int64_t SysInfo::AmountOfPhysicalMemoryImpl() { return AmountOfMemory(_SC_PHYS_PAGES); } // static -int64_t SysInfo::AmountOfAvailablePhysicalMemory() { +int64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() { // We should add inactive file-backed memory also but there is no such // information from OpenBSD unfortunately. return AmountOfMemory(_SC_AVPHYS_PAGES);
diff --git a/base/sys_info_win.cc b/base/sys_info_win.cc index d1c485c..3a3236b 100644 --- a/base/sys_info_win.cc +++ b/base/sys_info_win.cc
@@ -63,12 +63,12 @@ } // static -int64_t SysInfo::AmountOfPhysicalMemory() { +int64_t SysInfo::AmountOfPhysicalMemoryImpl() { return AmountOfMemory(&MEMORYSTATUSEX::ullTotalPhys); } // static -int64_t SysInfo::AmountOfAvailablePhysicalMemory() { +int64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() { SystemMemoryInfoKB info; if (!GetSystemMemoryInfo(&info)) return 0;
diff --git a/cc/input/scroll_boundary_behavior.h b/cc/input/scroll_boundary_behavior.h index f3ffa8f..878a5a4 100644 --- a/cc/input/scroll_boundary_behavior.h +++ b/cc/input/scroll_boundary_behavior.h
@@ -11,14 +11,14 @@ struct CC_EXPORT ScrollBoundaryBehavior { enum ScrollBoundaryBehaviorType { + // Same as contain but also hint that no overscroll affordance should be + // triggered. + kScrollBoundaryBehaviorTypeNone, // Allows the default behavior for the user agent. kScrollBoundaryBehaviorTypeAuto, // Hint to disable scroll chaining. The user agent may show an appropriate // overscroll affordance. - kScrollBoundaryBehaviorTypeContain, - // Same as contain but also hint that no overscroll affordance should be - // triggered. - kScrollBoundaryBehaviorTypeNone + kScrollBoundaryBehaviorTypeContain }; ScrollBoundaryBehavior()
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc index 2956490..d401565 100644 --- a/cc/scheduler/scheduler.cc +++ b/cc/scheduler/scheduler.cc
@@ -59,8 +59,9 @@ stopped_ = true; } -void Scheduler::SetNeedsImplSideInvalidation() { - state_machine_.SetNeedsImplSideInvalidation(); +void Scheduler::SetNeedsImplSideInvalidation( + bool needs_first_draw_on_activation) { + state_machine_.SetNeedsImplSideInvalidation(needs_first_draw_on_activation); ProcessScheduledActions(); }
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h index a0928ee..fb354cd 100644 --- a/cc/scheduler/scheduler.h +++ b/cc/scheduler/scheduler.h
@@ -100,7 +100,10 @@ // ScheduledActionRunImplSideInvalidation. // If ScheduledActionCommit is performed, the impl-side invalidations should // be merged with the main frame and the request is assumed to be completed. - void SetNeedsImplSideInvalidation(); + // If |needs_first_draw_on_activation| is set to true, an impl-side pending + // tree creates for this invalidation must be drawn at least once before a + // new tree can be activated. + void SetNeedsImplSideInvalidation(bool needs_first_draw_on_activation); // Drawing should result in submitting a CompositorFrame to the // LayerTreeFrameSink and then calling this.
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc index 9a18d6a7..59acfd0 100644 --- a/cc/scheduler/scheduler_state_machine.cc +++ b/cc/scheduler/scheduler_state_machine.cc
@@ -630,6 +630,9 @@ needs_impl_side_invalidation_ = false; has_pending_tree_ = true; did_perform_impl_side_invalidation_ = true; + pending_tree_needs_first_draw_on_activation_ = + next_invalidation_needs_first_draw_on_activation_; + next_invalidation_needs_first_draw_on_activation_ = false; // TODO(eseckler): Track impl-side invalidations for pending/active tree and // CompositorFrame freshness computation. } @@ -696,6 +699,7 @@ // We have a new pending tree. has_pending_tree_ = true; + pending_tree_needs_first_draw_on_activation_ = true; pending_tree_is_ready_for_activation_ = false; // Wait for the new pending tree to become ready to draw, which may happen // before or after activation. @@ -728,7 +732,8 @@ has_pending_tree_ = false; pending_tree_is_ready_for_activation_ = false; - active_tree_needs_first_draw_ = true; + active_tree_needs_first_draw_ = pending_tree_needs_first_draw_on_activation_; + pending_tree_needs_first_draw_on_activation_ = false; needs_redraw_ = true; previous_pending_tree_was_impl_side_ = current_pending_tree_is_impl_side_; @@ -802,8 +807,11 @@ DidDrawInternal(draw_result); } -void SchedulerStateMachine::SetNeedsImplSideInvalidation() { +void SchedulerStateMachine::SetNeedsImplSideInvalidation( + bool needs_first_draw_on_activation) { needs_impl_side_invalidation_ = true; + next_invalidation_needs_first_draw_on_activation_ |= + needs_first_draw_on_activation; } void SchedulerStateMachine::SetMainThreadWantsBeginMainFrameNotExpectedMessages(
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h index d8cd5352..bd4ed62 100644 --- a/cc/scheduler/scheduler_state_machine.h +++ b/cc/scheduler/scheduler_state_machine.h
@@ -261,7 +261,7 @@ // Indicates the active tree's visible tiles are ready to be drawn. void NotifyReadyToDraw(); - void SetNeedsImplSideInvalidation(); + void SetNeedsImplSideInvalidation(bool needs_first_draw_on_activation); bool has_pending_tree() const { return has_pending_tree_; } bool active_tree_needs_first_draw() const { @@ -376,12 +376,17 @@ bool did_draw_in_last_frame_ = false; bool did_submit_in_last_frame_ = false; bool needs_impl_side_invalidation_ = false; + bool next_invalidation_needs_first_draw_on_activation_ = false; bool previous_pending_tree_was_impl_side_ = false; bool current_pending_tree_is_impl_side_ = false; bool wants_begin_main_frame_not_expected_ = false; + // If set to true, the pending tree must be drawn at least once after + // activation before a new tree can be activated. + bool pending_tree_needs_first_draw_on_activation_ = false; + private: DISALLOW_COPY_AND_ASSIGN(SchedulerStateMachine); };
diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc index a367bbd..86516f56 100644 --- a/cc/scheduler/scheduler_state_machine_unittest.cc +++ b/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -2169,7 +2169,8 @@ // No impl-side invalidations should be performed while we are not visible. state.SetVisible(false); - state.SetNeedsImplSideInvalidation(); + bool needs_first_draw_on_activation = true; + state.SetNeedsImplSideInvalidation(needs_first_draw_on_activation); state.IssueNextBeginImplFrame(); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -2184,7 +2185,8 @@ // No impl-side invalidations should be performed while we can not make impl // frames. state.SetBeginFrameSourcePaused(true); - state.SetNeedsImplSideInvalidation(); + bool needs_first_draw_on_activation = true; + state.SetNeedsImplSideInvalidation(needs_first_draw_on_activation); state.IssueNextBeginImplFrame(); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -2195,7 +2197,8 @@ StateMachine state(settings); SET_UP_STATE(state); - state.SetNeedsImplSideInvalidation(); + bool needs_first_draw_on_activation = true; + state.SetNeedsImplSideInvalidation(needs_first_draw_on_activation); state.IssueNextBeginImplFrame(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.OnBeginImplFrameDeadline(); @@ -2217,7 +2220,8 @@ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // No impl-side invalidations should be performed during frame sink creation. - state.SetNeedsImplSideInvalidation(); + bool needs_first_draw_on_activation = true; + state.SetNeedsImplSideInvalidation(needs_first_draw_on_activation); state.IssueNextBeginImplFrame(); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -2251,7 +2255,8 @@ // Request an impl-side invalidation after the commit. The request should wait // till the current pending tree is activated. - state.SetNeedsImplSideInvalidation(); + bool needs_first_draw_on_activation = true; + state.SetNeedsImplSideInvalidation(needs_first_draw_on_activation); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); // Activate the pending tree. Since the commit fills the impl-side @@ -2286,7 +2291,8 @@ // Request an impl-side invalidation. The request should wait till a response // is received from the main thread. - state.SetNeedsImplSideInvalidation(); + bool needs_first_draw_on_activation = true; + state.SetNeedsImplSideInvalidation(needs_first_draw_on_activation); EXPECT_TRUE(state.needs_impl_side_invalidation()); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -2309,7 +2315,8 @@ SET_UP_STATE(state); // Set up a request for impl-side invalidation. - state.SetNeedsImplSideInvalidation(); + bool needs_first_draw_on_activation = true; + state.SetNeedsImplSideInvalidation(needs_first_draw_on_activation); state.IssueNextBeginImplFrame(); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE( @@ -2318,7 +2325,7 @@ // Request another invalidation, which should wait until the pending tree is // activated *and* we start the next BeginFrame. - state.SetNeedsImplSideInvalidation(); + state.SetNeedsImplSideInvalidation(needs_first_draw_on_activation); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); state.NotifyReadyToActivate(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE); @@ -2359,7 +2366,8 @@ // Request impl-side invalidation and start a new frame, which should be // blocked on the ack for the previous frame. - state.SetNeedsImplSideInvalidation(); + bool needs_first_draw_on_activation = true; + state.SetNeedsImplSideInvalidation(needs_first_draw_on_activation); state.IssueNextBeginImplFrame(); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE); @@ -2389,7 +2397,8 @@ // Request an impl-side invalidation and trigger the deadline, the // invalidation should run if the request is still pending when we enter the // deadline. - state.SetNeedsImplSideInvalidation(); + bool needs_first_draw_on_activation = true; + state.SetNeedsImplSideInvalidation(needs_first_draw_on_activation); state.OnBeginImplFrameDeadline(); EXPECT_ACTION_UPDATE_STATE( SchedulerStateMachine::ACTION_PERFORM_IMPL_SIDE_INVALIDATION); @@ -2403,7 +2412,8 @@ // Request a PrepareTiles and impl-side invalidation. The impl-side // invalidation should run first, since it will perform PrepareTiles as well. - state.SetNeedsImplSideInvalidation(); + bool needs_first_draw_on_activation = true; + state.SetNeedsImplSideInvalidation(needs_first_draw_on_activation); state.SetNeedsPrepareTiles(); state.IssueNextBeginImplFrame(); state.OnBeginImplFrameDeadline(); @@ -2522,5 +2532,36 @@ state.CurrentBeginImplFrameDeadlineMode()); } +TEST(SchedulerStateMachineTest, AllowSkippingActiveTreeFirstDraws) { + SchedulerSettings settings; + StateMachine state(settings); + SET_UP_STATE(state) + + // Impl-side invalidation creates a pending tree which is activated but not + // drawn in this frame. + bool needs_first_draw_on_activation = false; + state.SetNeedsImplSideInvalidation(needs_first_draw_on_activation); + state.OnBeginImplFrame(0, 1); + state.OnBeginImplFrameDeadline(); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_PERFORM_IMPL_SIDE_INVALIDATION); + state.NotifyReadyToActivate(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE); + state.OnBeginImplFrameIdle(); + + // Now we have a main frame. + state.SetNeedsBeginMainFrame(); + state.OnBeginImplFrame(0, 2); + EXPECT_ACTION_UPDATE_STATE( + SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME); + state.NotifyBeginMainFrameStarted(); + state.NotifyReadyToCommit(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT); + + // We should be able to activate this tree without drawing the active tree. + state.NotifyReadyToActivate(); + EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE); +} + } // namespace } // namespace cc
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc index 69ca6646..b91b2ef5 100644 --- a/cc/scheduler/scheduler_unittest.cc +++ b/cc/scheduler/scheduler_unittest.cc
@@ -3219,7 +3219,8 @@ // Request an impl-side invalidation and trigger the deadline. Ensure that the // invalidation runs inside the deadline. - scheduler_->SetNeedsImplSideInvalidation(); + bool needs_first_draw_on_activation = true; + scheduler_->SetNeedsImplSideInvalidation(needs_first_draw_on_activation); client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); EXPECT_ACTIONS("WillBeginImplFrame"); @@ -3236,7 +3237,8 @@ // Request a main frame and invalidation, the only action run should be // sending the main frame. scheduler_->SetNeedsBeginMainFrame(); - scheduler_->SetNeedsImplSideInvalidation(); + bool needs_first_draw_on_activation = true; + scheduler_->SetNeedsImplSideInvalidation(needs_first_draw_on_activation); client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame"); @@ -3261,7 +3263,8 @@ // Request a main frame and invalidation. scheduler_->SetNeedsBeginMainFrame(); - scheduler_->SetNeedsImplSideInvalidation(); + bool needs_first_draw_on_activation = true; + scheduler_->SetNeedsImplSideInvalidation(needs_first_draw_on_activation); client_->Reset(); EXPECT_SCOPED(AdvanceFrame()); EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame");
diff --git a/cc/test/fake_layer_tree_host_impl_client.cc b/cc/test/fake_layer_tree_host_impl_client.cc index b37a5e6..e4f6d842 100644 --- a/cc/test/fake_layer_tree_host_impl_client.cc +++ b/cc/test/fake_layer_tree_host_impl_client.cc
@@ -14,7 +14,8 @@ void FakeLayerTreeHostImplClient::PostAnimationEventsToMainThreadOnImplThread( std::unique_ptr<MutatorEvents> events) {} -void FakeLayerTreeHostImplClient::NeedsImplSideInvalidation() { +void FakeLayerTreeHostImplClient::NeedsImplSideInvalidation( + bool needs_first_draw_on_activation) { did_request_impl_side_invalidation_ = true; }
diff --git a/cc/test/fake_layer_tree_host_impl_client.h b/cc/test/fake_layer_tree_host_impl_client.h index 14ecadc..554fed9b 100644 --- a/cc/test/fake_layer_tree_host_impl_client.h +++ b/cc/test/fake_layer_tree_host_impl_client.h
@@ -35,7 +35,7 @@ void DidPrepareTiles() override {} void DidCompletePageScaleAnimationOnImplThread() override {} void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw) override {} - void NeedsImplSideInvalidation() override; + void NeedsImplSideInvalidation(bool needs_first_draw_on_activation) override; void RequestBeginMainFrameNotExpected(bool new_state) override {} void NotifyImageDecodeRequestFinished() override {}
diff --git a/cc/test/fake_tile_manager_client.h b/cc/test/fake_tile_manager_client.h index fd28b408..3597d79 100644 --- a/cc/test/fake_tile_manager_client.h +++ b/cc/test/fake_tile_manager_client.h
@@ -28,7 +28,7 @@ TreePriority tree_priority) override; void SetIsLikelyToRequireADraw(bool is_likely_to_require_a_draw) override {} gfx::ColorSpace GetRasterColorSpace() const override; - void RequestImplSideInvalidation() override {} + void RequestImplSideInvalidationForCheckerImagedTiles() override {} }; } // namespace cc
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc index 8671140..83919e6b 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc
@@ -227,7 +227,7 @@ void BlockImplSideInvalidationRequestsForTesting(bool block) override { block_impl_side_invalidation_ = block; if (!block_impl_side_invalidation_ && impl_side_invalidation_was_blocked_) { - RequestImplSideInvalidation(); + RequestImplSideInvalidationForCheckerImagedTiles(); impl_side_invalidation_was_blocked_ = false; } } @@ -279,7 +279,7 @@ test_hooks_->DidInvalidateContentOnImplSide(this); } - void RequestImplSideInvalidation() override { + void RequestImplSideInvalidationForCheckerImagedTiles() override { test_hooks_->DidReceiveImplSideInvalidationRequest(this); if (block_impl_side_invalidation_) { impl_side_invalidation_was_blocked_ = true; @@ -287,7 +287,7 @@ } impl_side_invalidation_was_blocked_ = false; - LayerTreeHostImpl::RequestImplSideInvalidation(); + LayerTreeHostImpl::RequestImplSideInvalidationForCheckerImagedTiles(); test_hooks_->DidRequestImplSideInvalidation(this); }
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc index a995b2e..f1dd3004 100644 --- a/cc/tiles/tile_manager.cc +++ b/cc/tiles/tile_manager.cc
@@ -1444,7 +1444,7 @@ } void TileManager::NeedsInvalidationForCheckerImagedTiles() { - client_->RequestImplSideInvalidation(); + client_->RequestImplSideInvalidationForCheckerImagedTiles(); } viz::ResourceFormat TileManager::DetermineResourceFormat(
diff --git a/cc/tiles/tile_manager.h b/cc/tiles/tile_manager.h index 1545b639..861a599 100644 --- a/cc/tiles/tile_manager.h +++ b/cc/tiles/tile_manager.h
@@ -85,7 +85,7 @@ // Requests that a pending tree be scheduled to invalidate content on the // pending on active tree. This is currently used when tiles that are // rasterized with missing images need to be invalidated. - virtual void RequestImplSideInvalidation() = 0; + virtual void RequestImplSideInvalidationForCheckerImagedTiles() = 0; protected: virtual ~TileManagerClient() {}
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index f801e77..655759c 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc
@@ -1394,8 +1394,11 @@ return result; } -void LayerTreeHostImpl::RequestImplSideInvalidation() { - client_->NeedsImplSideInvalidation(); +void LayerTreeHostImpl::RequestImplSideInvalidationForCheckerImagedTiles() { + // When using impl-side invalidation for checker-imaging, a pending tree does + // not need to be flushed as an independent update through the pipeline. + bool needs_first_draw_on_activation = false; + client_->NeedsImplSideInvalidation(needs_first_draw_on_activation); } void LayerTreeHostImpl::NotifyReadyToActivate() {
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h index dce25b1..3225455 100644 --- a/cc/trees/layer_tree_host_impl.h +++ b/cc/trees/layer_tree_host_impl.h
@@ -127,7 +127,8 @@ // Called when output surface asks for a draw. virtual void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw) = 0; - virtual void NeedsImplSideInvalidation() = 0; + virtual void NeedsImplSideInvalidation( + bool needs_first_draw_on_activation) = 0; // Called when a requested image decode completes. virtual void NotifyImageDecodeRequestFinished() = 0; @@ -353,7 +354,7 @@ TreePriority tree_priority) override; void SetIsLikelyToRequireADraw(bool is_likely_to_require_a_draw) override; gfx::ColorSpace GetRasterColorSpace() const override; - void RequestImplSideInvalidation() override; + void RequestImplSideInvalidationForCheckerImagedTiles() override; // ScrollbarAnimationControllerClient implementation. void PostDelayedScrollbarAnimationTask(const base::Closure& task,
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 40f8db1..84ff949 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -190,7 +190,7 @@ host_impl_->DidDrawAllLayers(*frame); last_on_draw_frame_ = std::move(frame); } - void NeedsImplSideInvalidation() override { + void NeedsImplSideInvalidation(bool needs_first_draw_on_activation) override { did_request_impl_side_invalidation_ = true; } void NotifyImageDecodeRequestFinished() override {}
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc index a79ada9..188c4665 100644 --- a/cc/trees/layer_tree_host_unittest_animation.cc +++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -1694,7 +1694,7 @@ // invalidation and make sure it finishes before the main thread is // released. did_request_impl_side_invalidation_ = true; - host_impl->RequestImplSideInvalidation(); + host_impl->RequestImplSideInvalidationForCheckerImagedTiles(); } }
diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc index bbbe7d0e..07ceba4 100644 --- a/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -2168,12 +2168,12 @@ // Request an impl-side invalidation to create an impl-side pending // tree. - host_impl->RequestImplSideInvalidation(); + host_impl->RequestImplSideInvalidationForCheckerImagedTiles(); break; case 3: // Request another impl-side invalidation so the aborted commit comes // after this tree is activated. - host_impl->RequestImplSideInvalidation(); + host_impl->RequestImplSideInvalidationForCheckerImagedTiles(); break; default: NOTREACHED();
diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc index 4859cafb..320c0a0 100644 --- a/cc/trees/proxy_impl.cc +++ b/cc/trees/proxy_impl.cc
@@ -475,9 +475,9 @@ scheduler_->OnDrawForLayerTreeFrameSink(resourceless_software_draw); } -void ProxyImpl::NeedsImplSideInvalidation() { +void ProxyImpl::NeedsImplSideInvalidation(bool needs_first_draw_on_activation) { DCHECK(IsImplThread()); - scheduler_->SetNeedsImplSideInvalidation(); + scheduler_->SetNeedsImplSideInvalidation(needs_first_draw_on_activation); } void ProxyImpl::NotifyImageDecodeRequestFinished() {
diff --git a/cc/trees/proxy_impl.h b/cc/trees/proxy_impl.h index ec2bef5d..af992ef7 100644 --- a/cc/trees/proxy_impl.h +++ b/cc/trees/proxy_impl.h
@@ -95,7 +95,7 @@ void DidPrepareTiles() override; void DidCompletePageScaleAnimationOnImplThread() override; void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw) override; - void NeedsImplSideInvalidation() override; + void NeedsImplSideInvalidation(bool needs_first_draw_on_activation) override; void NotifyImageDecodeRequestFinished() override; // SchedulerClient implementation
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc index 105d1a16..28b604d 100644 --- a/cc/trees/single_thread_proxy.cc +++ b/cc/trees/single_thread_proxy.cc
@@ -445,9 +445,11 @@ NOTREACHED() << "Implemented by ThreadProxy for synchronous compositor."; } -void SingleThreadProxy::NeedsImplSideInvalidation() { +void SingleThreadProxy::NeedsImplSideInvalidation( + bool needs_first_draw_on_activation) { DCHECK(scheduler_on_impl_thread_); - scheduler_on_impl_thread_->SetNeedsImplSideInvalidation(); + scheduler_on_impl_thread_->SetNeedsImplSideInvalidation( + needs_first_draw_on_activation); } void SingleThreadProxy::NotifyImageDecodeRequestFinished() {
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h index 3042df1..7d9b238 100644 --- a/cc/trees/single_thread_proxy.h +++ b/cc/trees/single_thread_proxy.h
@@ -103,7 +103,7 @@ void DidPrepareTiles() override; void DidCompletePageScaleAnimationOnImplThread() override; void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw) override; - void NeedsImplSideInvalidation() override; + void NeedsImplSideInvalidation(bool needs_first_draw_on_activation) override; void RequestBeginMainFrameNotExpected(bool new_state) override; void NotifyImageDecodeRequestFinished() override;
diff --git a/chrome/VERSION b/chrome/VERSION index f9c0405d..fb57a41 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=62 MINOR=0 -BUILD=3167 +BUILD=3168 PATCH=0
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml index 29e26b1..7320d4a 100644 --- a/chrome/android/java/AndroidManifest.xml +++ b/chrome/android/java/AndroidManifest.xml
@@ -674,7 +674,7 @@ </activity> <!-- Activities for Browser Actions --> - {% if channel in ['default'] %} + {% if channel in ['dev', 'canary', 'default'] %} <activity android:name="org.chromium.chrome.browser.browseractions.BrowserActionActivity" android:theme="@style/FullscreenTransparentActivityTheme" android:exported="true"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTask.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTask.java index 62996a22..4e4cc469 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTask.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTask.java
@@ -60,7 +60,7 @@ * TODO(dewittj): Handle skipping work if the battery percentage is too low. */ @CalledByNative - public static void scheduleTask(int additionalDelaySeconds) { + public static void scheduleTask(int additionalDelaySeconds, boolean updateCurrent) { TaskInfo taskInfo = TaskInfo.createOneOffTask(TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID, PrefetchBackgroundTask.class, @@ -72,7 +72,7 @@ TimeUnit.DAYS.toMillis(7)) .setRequiredNetworkType(TaskInfo.NETWORK_TYPE_UNMETERED) .setIsPersisted(true) - .setUpdateCurrent(true) + .setUpdateCurrent(updateCurrent) .build(); getScheduler().schedule(ContextUtils.getApplicationContext(), taskInfo); } @@ -125,7 +125,7 @@ @Override public void reschedule(Context context) { // TODO(dewittj): Set the backoff time appropriately. - scheduleTask(0); + scheduleTask(0, true); } /** @@ -170,8 +170,8 @@ @VisibleForTesting native boolean nativeStartPrefetchTask(Profile profile); @VisibleForTesting - native boolean nativeOnStopTask(long nativePrefetchBackgroundTask); + native boolean nativeOnStopTask(long nativePrefetchBackgroundTaskAndroid); native void nativeSetTaskReschedulingForTesting( - long nativePrefetchBackgroundTask, boolean reschedule, boolean backoff); - native void nativeSignalTaskFinishedForTesting(long nativePrefetchBackgroundTask); + long nativePrefetchBackgroundTaskAndroid, boolean reschedule, boolean backoff); + native void nativeSignalTaskFinishedForTesting(long nativePrefetchBackgroundTaskAndroid); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java index fda3186..8a64a33 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java
@@ -16,7 +16,7 @@ */ private long mJourneyLoggerAndroid; - private boolean mWasShowCalled; + private boolean mWasPaymentRequestTriggered; private boolean mHasRecorded; public JourneyLogger(boolean isIncognito, String url) { @@ -88,14 +88,6 @@ } /** - * Records the fact that the Payment Request was shown to the user. - */ - public void setShowCalled() { - mWasShowCalled = true; - nativeSetShowCalled(mJourneyLoggerAndroid); - } - - /** * Records that an event occurred. * * @param event The event that occured. @@ -103,6 +95,9 @@ public void setEventOccurred(int event) { assert event >= 0; assert event < Event.ENUM_MAX; + + if (event == Event.SHOWN || event == Event.SKIPPED_SHOW) mWasPaymentRequestTriggered = true; + nativeSetEventOccurred(mJourneyLoggerAndroid, event); } @@ -137,9 +132,9 @@ */ public void setCompleted() { assert !mHasRecorded; - assert mWasShowCalled; + assert mWasPaymentRequestTriggered; - if (!mHasRecorded && mWasShowCalled) { + if (!mHasRecorded && mWasPaymentRequestTriggered) { mHasRecorded = true; nativeSetCompleted(mJourneyLoggerAndroid); } @@ -156,7 +151,7 @@ // The abort reasons on Android cascade into each other, so only the first one should be // recorded. - if (!mHasRecorded && mWasShowCalled) { + if (!mHasRecorded && mWasPaymentRequestTriggered) { mHasRecorded = true; nativeSetAborted(mJourneyLoggerAndroid, reason); } @@ -169,7 +164,7 @@ */ public void setNotShown(int reason) { assert reason < NotShownReason.MAX; - assert !mWasShowCalled; + assert !mWasPaymentRequestTriggered; assert !mHasRecorded; if (!mHasRecorded) { @@ -188,7 +183,6 @@ private native void nativeIncrementSelectionAdds(long nativeJourneyLoggerAndroid, int section); private native void nativeSetCanMakePaymentValue( long nativeJourneyLoggerAndroid, boolean value); - private native void nativeSetShowCalled(long nativeJourneyLoggerAndroid); private native void nativeSetEventOccurred(long nativeJourneyLoggerAndroid, int event); private native void nativeSetSelectedPaymentMethod( long nativeJourneyLoggerAndroid, int paymentMethod);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java index b1966153..999966b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -672,7 +672,6 @@ mDidRecordShowEvent = true; mShouldRecordAbortReason = true; mJourneyLogger.setEventOccurred(Event.SKIPPED_SHOW); - mJourneyLogger.setShowCalled(); onPayClicked(null /* selectedShippingAddress */, null /* selectedShippingOption */, mPaymentMethodsSection.getItem(0)); @@ -1044,7 +1043,6 @@ mDidRecordShowEvent = true; mShouldRecordAbortReason = true; mJourneyLogger.setEventOccurred(Event.SHOWN); - mJourneyLogger.setShowCalled(); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/AccessibilityPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/AccessibilityPreferences.java index fea1c97..f6be37fb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/AccessibilityPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/AccessibilityPreferences.java
@@ -47,7 +47,7 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getActivity().setTitle(R.string.prefs_accessibility); - addPreferencesFromResource(R.xml.accessibility_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.accessibility_preferences); mFormat = NumberFormat.getPercentInstance(); mFontSizePrefs = FontSizePrefs.getInstance(getActivity());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ContentSuggestionsPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ContentSuggestionsPreferences.java index 1ba8fcea..cf171eaf 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ContentSuggestionsPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ContentSuggestionsPreferences.java
@@ -72,7 +72,7 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.suggestions_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.suggestions_preferences); setHasOptionsMenu(true); finishSwitchInitialisation();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/HomepagePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/HomepagePreferences.java index f1391eb..ec4b676 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/HomepagePreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/HomepagePreferences.java
@@ -29,7 +29,7 @@ super.onActivityCreated(savedInstanceState); mHomepageManager = HomepageManager.getInstance(getActivity()); getActivity().setTitle(R.string.options_homepage_title); - addPreferencesFromResource(R.xml.homepage_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.homepage_preferences); mHomepageSwitch = (ChromeSwitchPreference) findPreference(PREF_HOMEPAGE_SWITCH); boolean isHomepageEnabled = mHomepageManager.getPrefHomepageEnabled();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/LegalInformationPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/LegalInformationPreferences.java index 20ad5d5..2739980 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/LegalInformationPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/LegalInformationPreferences.java
@@ -17,7 +17,7 @@ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.legal_information_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.legal_information_preferences); getActivity().setTitle(R.string.legal_information_title); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AndroidPaymentAppsFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AndroidPaymentAppsFragment.java index a994e16..03dfc9e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AndroidPaymentAppsFragment.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AndroidPaymentAppsFragment.java
@@ -11,6 +11,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.payments.AndroidPaymentAppFactory; +import org.chromium.chrome.browser.preferences.PreferenceUtils; import org.chromium.chrome.browser.preferences.TextMessagePreference; import java.util.Map; @@ -22,7 +23,8 @@ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.autofill_and_payments_preference_fragment_screen); + PreferenceUtils.addPreferencesFromResource( + this, R.xml.autofill_and_payments_preference_fragment_screen); getActivity().setTitle(R.string.payment_apps_title); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillCreditCardsFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillCreditCardsFragment.java index d2e94359..55488e8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillCreditCardsFragment.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillCreditCardsFragment.java
@@ -15,6 +15,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.autofill.PersonalDataManager; import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard; +import org.chromium.chrome.browser.preferences.PreferenceUtils; /** * Autofill credit cards fragment, which allows the user to edit credit cards. @@ -24,7 +25,8 @@ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.autofill_and_payments_preference_fragment_screen); + PreferenceUtils.addPreferencesFromResource( + this, R.xml.autofill_and_payments_preference_fragment_screen); getActivity().setTitle(R.string.autofill_credit_cards_title); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfilesFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfilesFragment.java index 352c3cf7..ef7b9bd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfilesFragment.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillProfilesFragment.java
@@ -14,6 +14,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.autofill.PersonalDataManager; import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; +import org.chromium.chrome.browser.preferences.PreferenceUtils; /** * Autofill profiles fragment, which allows the user to edit autofill profiles. @@ -23,7 +24,8 @@ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.autofill_and_payments_preference_fragment_screen); + PreferenceUtils.addPreferencesFromResource( + this, R.xml.autofill_and_payments_preference_fragment_screen); getActivity().setTitle(R.string.autofill_profiles_title); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillServerProfilePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillServerProfilePreferences.java index 05d06cf..8678083 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillServerProfilePreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/autofill/AutofillServerProfilePreferences.java
@@ -15,6 +15,7 @@ import org.chromium.chrome.browser.autofill.PersonalDataManager; import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; import org.chromium.chrome.browser.customtabs.CustomTabActivity; +import org.chromium.chrome.browser.preferences.PreferenceUtils; /** * Fragment for settings page that allows user to view and edit a single server-provided address. @@ -29,7 +30,7 @@ @Override public void onCreate(Bundle savedState) { super.onCreate(savedState); - addPreferencesFromResource(R.xml.autofill_server_profile_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.autofill_server_profile_preferences); getActivity().setTitle(R.string.autofill_edit_profile); // We know which card to display based on the GUID stuffed in
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferences.java index 767d0b7..3b7b72c6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferences.java
@@ -125,9 +125,9 @@ getPreferenceScreen().removeAll(); createDataReductionSwitch(isEnabled); if (isEnabled) { - addPreferencesFromResource(R.xml.data_reduction_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.data_reduction_preferences); } else { - addPreferencesFromResource(R.xml.data_reduction_preferences_off); + PreferenceUtils.addPreferencesFromResource(this, R.xml.data_reduction_preferences_off); } mIsEnabled = isEnabled; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ContextualSearchPreferenceFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ContextualSearchPreferenceFragment.java index 0f9e3f6..d48d20b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ContextualSearchPreferenceFragment.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ContextualSearchPreferenceFragment.java
@@ -14,6 +14,7 @@ import org.chromium.chrome.browser.preferences.ChromeSwitchPreference; import org.chromium.chrome.browser.preferences.ManagedPreferenceDelegate; import org.chromium.chrome.browser.preferences.PrefServiceBridge; +import org.chromium.chrome.browser.preferences.PreferenceUtils; /** * Fragment to manage the Contextual Search preference and to explain to the user what it does. @@ -25,7 +26,7 @@ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.contextual_search_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.contextual_search_preferences); getActivity().setTitle(R.string.contextual_search_title); setHasOptionsMenu(true); initContextualSearchSwitch();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/DoNotTrackPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/DoNotTrackPreference.java index f75620af..6aa5237 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/DoNotTrackPreference.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/DoNotTrackPreference.java
@@ -12,6 +12,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.preferences.ChromeSwitchPreference; import org.chromium.chrome.browser.preferences.PrefServiceBridge; +import org.chromium.chrome.browser.preferences.PreferenceUtils; /** * Fragment to manage 'Do Not Track' preference and to explain to the user what it does. @@ -23,7 +24,7 @@ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.do_not_track_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.do_not_track_preferences); getActivity().setTitle(R.string.do_not_track_title); ChromeSwitchPreference doNotTrackSwitch =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PhysicalWebPreferenceFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PhysicalWebPreferenceFragment.java index 7fd2055..a35e6c978 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PhysicalWebPreferenceFragment.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PhysicalWebPreferenceFragment.java
@@ -19,6 +19,7 @@ import org.chromium.chrome.browser.physicalweb.PhysicalWebUma; import org.chromium.chrome.browser.preferences.ButtonPreference; import org.chromium.chrome.browser.preferences.ChromeSwitchPreference; +import org.chromium.chrome.browser.preferences.PreferenceUtils; /** * Fragment to manage the Physical Web preference and to explain to the user what it does. @@ -32,7 +33,7 @@ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.physical_web_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.physical_web_preferences); getActivity().setTitle(R.string.physical_web_pref_title); initPhysicalWebSwitch(); initLaunchButton();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/UsageAndCrashReportsPreferenceFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/UsageAndCrashReportsPreferenceFragment.java index 7be7919..15de6c3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/UsageAndCrashReportsPreferenceFragment.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/UsageAndCrashReportsPreferenceFragment.java
@@ -14,6 +14,7 @@ import org.chromium.chrome.browser.preferences.ChromeSwitchPreference; import org.chromium.chrome.browser.preferences.ManagedPreferenceDelegate; import org.chromium.chrome.browser.preferences.PrefServiceBridge; +import org.chromium.chrome.browser.preferences.PreferenceUtils; /** * Fragment to manage the Usage and crash reports preference and to explain to @@ -26,7 +27,7 @@ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.usage_and_crash_reports_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.usage_and_crash_reports_preferences); getActivity().setTitle(R.string.usage_and_crash_reports_title); initUsageAndCrashReportsSwitch(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java index 19c9e1ce..1d9f0f4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleCategoryPreferences.java
@@ -42,6 +42,7 @@ import org.chromium.chrome.browser.preferences.ManagedPreferenceDelegate; import org.chromium.chrome.browser.preferences.ManagedPreferencesUtils; import org.chromium.chrome.browser.preferences.PrefServiceBridge; +import org.chromium.chrome.browser.preferences.PreferenceUtils; import org.chromium.chrome.browser.preferences.ProtectedContentResetCredentialConfirmDialogFragment; import org.chromium.chrome.browser.preferences.website.Website.StoredDataClearedCallback; import org.chromium.chrome.browser.profiles.Profile; @@ -335,7 +336,7 @@ @Override public void onActivityCreated(Bundle savedInstanceState) { - addPreferencesFromResource(R.xml.website_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.website_preferences); ListView listView = (ListView) getView().findViewById(android.R.id.list); mEmptyView = (TextView) getView().findViewById(android.R.id.empty); listView.setEmptyView(mEmptyView); @@ -587,7 +588,7 @@ // This will remove the combo box at the top and all the sites listed below it. getPreferenceScreen().removeAll(); // And this will add the filter preference back (combo box). - addPreferencesFromResource(R.xml.website_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.website_preferences); configureGlobalToggles();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java index 253cbe5..d2a4c52 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SingleWebsitePreferences.java
@@ -29,6 +29,7 @@ import org.chromium.chrome.browser.notifications.channels.SiteChannelsManager; import org.chromium.chrome.browser.omnibox.geo.GeolocationHeader; import org.chromium.chrome.browser.preferences.PrefServiceBridge; +import org.chromium.chrome.browser.preferences.PreferenceUtils; import org.chromium.chrome.browser.search_engines.TemplateUrlService; import org.chromium.components.url_formatter.UrlFormatter; import org.chromium.content_public.browser.WebContents; @@ -270,7 +271,7 @@ * Must only be called once mSite is set. */ private void displaySitePermissions() { - addPreferencesFromResource(R.xml.single_website_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.single_website_preferences); Set<String> permissionPreferenceKeys = new HashSet<>(Arrays.asList(PERMISSION_PREFERENCE_KEYS));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java index 92ca580..4b19e79d9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java
@@ -15,6 +15,7 @@ import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; import org.chromium.chrome.browser.preferences.LocationSettings; import org.chromium.chrome.browser.preferences.PrefServiceBridge; +import org.chromium.chrome.browser.preferences.PreferenceUtils; import java.util.ArrayList; import java.util.List; @@ -59,7 +60,7 @@ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.site_settings_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.site_settings_preferences); getActivity().setTitle(R.string.prefs_site_settings); mProtectedContentMenuAvailable = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/TranslatePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/TranslatePreferences.java index 5234452..746405d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/TranslatePreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/TranslatePreferences.java
@@ -20,6 +20,7 @@ import org.chromium.chrome.browser.preferences.ChromeSwitchPreference; import org.chromium.chrome.browser.preferences.ManagedPreferenceDelegate; import org.chromium.chrome.browser.preferences.PrefServiceBridge; +import org.chromium.chrome.browser.preferences.PreferenceUtils; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.ui.widget.Toast; @@ -34,7 +35,7 @@ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.translate_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.translate_preferences); getActivity().setTitle(R.string.google_translate); setHasOptionsMenu(true);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/UsbChooserPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/UsbChooserPreferences.java index 08c6b42..99f3662 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/UsbChooserPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/UsbChooserPreferences.java
@@ -19,6 +19,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.help.HelpAndFeedback; +import org.chromium.chrome.browser.preferences.PreferenceUtils; import org.chromium.chrome.browser.profiles.Profile; import java.util.ArrayList; @@ -48,7 +49,7 @@ @Override public void onActivityCreated(Bundle savedInstanceState) { - addPreferencesFromResource(R.xml.usb_chooser_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.usb_chooser_preferences); ListView listView = (ListView) getView().findViewById(android.R.id.list); mEmptyView = (TextView) getView().findViewById(android.R.id.empty); listView.setEmptyView(mEmptyView); @@ -154,7 +155,7 @@ private void resetList() { getPreferenceScreen().removeAll(); - addPreferencesFromResource(R.xml.usb_chooser_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.usb_chooser_preferences); if (mPermissionsByObject.isEmpty() && mSearch.isEmpty() && mEmptyView != null) { mEmptyView.setText(R.string.website_settings_usb_no_devices);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/UsbDevicePreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/UsbDevicePreferences.java index 92d5430..822b968 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/UsbDevicePreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/UsbDevicePreferences.java
@@ -18,6 +18,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.help.HelpAndFeedback; +import org.chromium.chrome.browser.preferences.PreferenceUtils; import org.chromium.chrome.browser.profiles.Profile; import java.util.ArrayList; @@ -51,7 +52,7 @@ @Override @SuppressWarnings("unchecked") public void onActivityCreated(Bundle savedInstanceState) { - addPreferencesFromResource(R.xml.usb_device_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.usb_device_preferences); ListView listView = (ListView) getView().findViewById(android.R.id.list); listView.setDivider(null); @@ -179,7 +180,7 @@ private void resetList() { getPreferenceScreen().removeAll(); - addPreferencesFromResource(R.xml.usb_device_preferences); + PreferenceUtils.addPreferencesFromResource(this, R.xml.usb_device_preferences); PreferenceScreen preferenceScreen = getPreferenceScreen(); Preference header = preferenceScreen.findPreference(PREF_OBJECT_NAME);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java index b19aac8..23a4186 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheet.java
@@ -249,6 +249,9 @@ /** Whether {@link #destroy()} has been called. **/ private boolean mIsDestroyed; + /** The token used to enable browser controls persistence. */ + private int mPersistentControlsToken; + /** * An interface defining content that can be displayed inside of the bottom sheet for Chrome * Home. @@ -1065,6 +1068,8 @@ private void onSheetOpened() { if (mIsSheetOpen) return; + mIsSheetOpen = true; + // Make sure the toolbar is visible before expanding the sheet. Tab tab = getActiveTab(); if (isToolbarAndroidViewHidden() && tab != null) { @@ -1074,6 +1079,11 @@ mBottomSheetContentContainer.setVisibility(View.VISIBLE); mIsSheetOpen = true; + + // Browser controls should stay visible until the sheet is closed. + mPersistentControlsToken = + mFullscreenManager.getBrowserVisibilityDelegate().showControlsPersistent(); + dismissSelectedText(); for (BottomSheetObserver o : mObservers) o.onSheetOpened(); announceForAccessibility(getResources().getString(R.string.bottom_sheet_opened)); @@ -1094,6 +1104,11 @@ mBottomSheetContentContainer.setVisibility(View.INVISIBLE); mBackButtonDismissesChrome = false; mIsSheetOpen = false; + + // Update the browser controls since they are permanently shown while the sheet is open. + mFullscreenManager.getBrowserVisibilityDelegate().hideControlsPersistent( + mPersistentControlsToken); + for (BottomSheetObserver o : mObservers) o.onSheetClosed(); announceForAccessibility(getResources().getString(R.string.bottom_sheet_closed)); clearFocus();
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index 016f2624..cfa425e 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -1463,6 +1463,7 @@ "javatests/src/org/chromium/chrome/browser/infobar/PermissionUpdateInfobarTest.java", "javatests/src/org/chromium/chrome/browser/infobar/SearchGeolocationDisclosureInfoBarTest.java", "javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java", + "javatests/src/org/chromium/chrome/browser/input/TextSuggestionMenuTest.java", "javatests/src/org/chromium/chrome/browser/instantapps/InstantAppsHandlerTest.java", "javatests/src/org/chromium/chrome/browser/invalidation/ChromeBrowserSyncAdapterTest.java", "javatests/src/org/chromium/chrome/browser/invalidation/DelayedInvalidationsControllerTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/input/TextSuggestionMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/input/TextSuggestionMenuTest.java new file mode 100644 index 0000000..f3db74c --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/input/TextSuggestionMenuTest.java
@@ -0,0 +1,61 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.input; + +import android.support.test.filters.LargeTest; +import android.view.View; + +import org.junit.Assert; + +import org.chromium.chrome.browser.ChromeActivity; +import org.chromium.chrome.test.ChromeActivityTestCaseBase; +import org.chromium.content.R; +import org.chromium.content.browser.ContentViewCore; +import org.chromium.content.browser.test.util.Criteria; +import org.chromium.content.browser.test.util.CriteriaHelper; +import org.chromium.content.browser.test.util.DOMUtils; +import org.chromium.content.browser.test.util.TouchCommon; + +import java.util.concurrent.TimeoutException; + +/** + * Integration tests for the text suggestion menu. + */ +public class TextSuggestionMenuTest extends ChromeActivityTestCaseBase<ChromeActivity> { + private static final String URL = + "data:text/html, <div contenteditable id=\"div\">iuvwneaoanls</div>"; + + public TextSuggestionMenuTest() { + super(ChromeActivity.class); + } + + @Override + public void startMainActivity() throws InterruptedException { + startMainActivityOnBlankPage(); + } + + @LargeTest + public void testDeleteMisspelledWord() throws InterruptedException, TimeoutException { + loadUrl(URL); + final ContentViewCore cvc = getActivity().getActivityTab().getContentViewCore(); + DOMUtils.focusNode(cvc.getWebContents(), "div"); + DOMUtils.clickNode(cvc, "div"); + + // Wait for the suggestion menu to show + CriteriaHelper.pollUiThread(new Criteria() { + @Override + public boolean isSatisfied() { + return cvc.getTextSuggestionHostForTesting().getSuggestionsPopupWindowForTesting() + != null; + } + }); + + View view = cvc.getTextSuggestionHostForTesting() + .getSuggestionsPopupWindowForTesting() + .getContentViewForTesting(); + TouchCommon.singleClickView(view.findViewById(R.id.deleteButton)); + Assert.assertEquals("", DOMUtils.getNodeContents(cvc.getWebContents(), "div")); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskTest.java index ef62836..419488a2 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskTest.java
@@ -20,10 +20,10 @@ import org.chromium.base.ContextUtils; import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.CommandLineFlags; -import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; import org.chromium.components.background_task_scheduler.BackgroundTask.TaskFinishedCallback; import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler; import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerDelegate; @@ -42,8 +42,7 @@ "enable-features=OfflinePagesPrefetching"}) public class PrefetchBackgroundTaskTest { @Rule - public ChromeActivityTestRule<ChromeActivity> mActivityTestRule = - new ChromeActivityTestRule<>(ChromeActivity.class); + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); private static final double BACKOFF_JITTER_FACTOR = 0.33; private static final int SEMAPHORE_TIMEOUT_MS = 5000; @@ -221,7 +220,7 @@ @Test @SmallTest public void testSchedule() throws Exception { - PrefetchBackgroundTask.scheduleTask(0); + PrefetchBackgroundTask.scheduleTask(0, true); mScheduler.waitForTaskStarted(); TestPrefetchBackgroundTask task = validateAndGetScheduledTask(0); task.signalTaskFinished(); @@ -233,7 +232,7 @@ @SmallTest public void testScheduleWithAdditionalDelay() throws Exception { final int additionalDelaySeconds = 15; - PrefetchBackgroundTask.scheduleTask(additionalDelaySeconds); + PrefetchBackgroundTask.scheduleTask(additionalDelaySeconds, true); mScheduler.waitForTaskStarted(); TestPrefetchBackgroundTask task = validateAndGetScheduledTask(additionalDelaySeconds); task.signalTaskFinished(); @@ -244,7 +243,7 @@ @Test @SmallTest public void testReschedule() throws Exception { - PrefetchBackgroundTask.scheduleTask(0); + PrefetchBackgroundTask.scheduleTask(0, true); mScheduler.waitForTaskStarted(); TestPrefetchBackgroundTask task = validateAndGetScheduledTask(0);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java index 06c1f88a..3aad6da 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchBackgroundTaskUnitTest.java
@@ -134,7 +134,7 @@ @Test public void scheduleTask() { final int additionalDelaySeconds = 15; - PrefetchBackgroundTask.scheduleTask(additionalDelaySeconds); + PrefetchBackgroundTask.scheduleTask(additionalDelaySeconds, true); TaskInfo scheduledTask = mShadowTaskScheduler.getTaskInfo(TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID); assertNotNull(scheduledTask); @@ -151,7 +151,7 @@ mShadowTaskScheduler.getTaskInfo(TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID); assertNull(scheduledTask); - PrefetchBackgroundTask.scheduleTask(0); + PrefetchBackgroundTask.scheduleTask(0, true); scheduledTask = mShadowTaskScheduler.getTaskInfo(TaskIds.OFFLINE_PAGES_PREFETCH_JOB_ID); assertNotNull(scheduledTask); assertEquals(TimeUnit.SECONDS.toMillis(PrefetchBackgroundTask.DEFAULT_START_DELAY_SECONDS),
diff --git a/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkMetaDataKeys.java b/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkMetaDataKeys.java index 558da25..d09e9031 100644 --- a/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkMetaDataKeys.java +++ b/chrome/android/webapk/libs/common/src/org/chromium/webapk/lib/common/WebApkMetaDataKeys.java
@@ -28,7 +28,5 @@ public static final String ICON_URLS_AND_ICON_MURMUR2_HASHES = "org.chromium.webapk.shell_apk.iconUrlsAndIconMurmur2Hashes"; public static final String WEB_MANIFEST_URL = "org.chromium.webapk.shell_apk.webManifestUrl"; - // TODO(zpeng): crbug.com/715166. Assign value to {@link BADGE_ICON_ID} and sync it with - // WebAPK Android Manifest. - public static final String BADGE_ICON_ID = ""; + public static final String BADGE_ICON_ID = "org.chromium.webapk.shell_apk.badgeIconId"; }
diff --git a/chrome/android/webapk/shell_apk/AndroidManifest.xml b/chrome/android/webapk/shell_apk/AndroidManifest.xml index 7f5379b8..1e1a7e2 100644 --- a/chrome/android/webapk/shell_apk/AndroidManifest.xml +++ b/chrome/android/webapk/shell_apk/AndroidManifest.xml
@@ -43,6 +43,7 @@ <meta-data android:name="org.chromium.webapk.shell_apk.iconId" android:resource="@mipmap/app_icon" /> <meta-data android:name="org.chromium.webapk.shell_apk.iconUrlsAndIconMurmur2Hashes" android:value="{{{icon_urls_and_icon_murmur2_hashes}}}" /> <meta-data android:name="org.chromium.webapk.shell_apk.webManifestUrl" android:value="{{{web_manifest_url}}}" /> + {{#badge_icon_id}}<meta-data android:name="org.chromium.webapk.shell_apk.badgeIconId" android:resource="{{{badge_icon_id}}}" />{{/badge_icon_id}} <service android:name="org.chromium.webapk.shell_apk.WebApkServiceFactory" android:exported="true"
diff --git a/chrome/android/webapk/shell_apk/shell_apk_version.gni b/chrome/android/webapk/shell_apk/shell_apk_version.gni index c2c0b44..aaa97a6 100644 --- a/chrome/android/webapk/shell_apk/shell_apk_version.gni +++ b/chrome/android/webapk/shell_apk/shell_apk_version.gni
@@ -6,7 +6,7 @@ # (including AndroidManifest.xml) is updated. This version should be incremented # prior to uploading a new ShellAPK to the WebAPK Minting Server. # Does not affect Chrome.apk -template_shell_apk_version = 16 +template_shell_apk_version = 17 # The ShellAPK version expected by Chrome. Chrome will try to update the WebAPK # if the WebAPK's ShellAPK version is less than |expected_shell_apk_version|.
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index 33a6a5a..fc1bb23 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -5718,6 +5718,9 @@ <message name="IDS_ENTERPRISE_ENROLLMENT_ERROR_ACTIVE_DIRECTORY_POLICY_FETCH" desc="Error message shown on the enrollment screen when the system failed to fetch device policy from the local server, e.g. Samba or Active Directory."> Oops! The system failed to fetch policy for your device. </message> + <message name="IDS_ENTERPRISE_ENROLLMENT_ERROR_LICENSE_REQUEST" desc="Error message shown on the enrollment screen when the system failed to retrieve available license types."> + Oops! The system failed to load available licenses. + </message> <message name="IDS_ENTERPRISE_ENROLLMENT_ERROR_SAVE_DEVICE_CONFIGURATION" desc="Error message shown on the enrollment screen when the system failed to save some part of the device configuration. E.g. token from the Device Management server."> Oops! The system failed to save device configuration. </message> @@ -5819,6 +5822,24 @@ <message name="IDS_ENTERPRISE_ENROLLMENT_EXPLAIN_ATTRIBUTE_LINK" desc="Text on the link to the device attribute update help article."> Learn more. </message> + <message name="IDS_ENTERPRISE_ENROLLMENT_LICENSE_SELECTION" desc="The subtitle on the license selection prompt screen."> + Choose license type + </message> + <message name="IDS_ENTERPRISE_ENROLLMENT_LICENSE_SELECTION_EXPLANATION" desc="Multiple licenses message to be shown on license selection prompt screen."> + We have detected multiple license types for your domain. Please chose one to continue. + </message> + <message name="IDS_ENTERPRISE_ENROLLMENT_PERPETUAL_LICENSE_TYPE" desc="Text on radio button to select Perpetual License during enrollment."> + Perpetual + </message> + <message name="IDS_ENTERPRISE_ENROLLMENT_ANNUAL_LICENSE_TYPE" desc="Text on radio button to select Annual License during enrollment."> + Annual + </message> + <message name="IDS_ENTERPRISE_ENROLLMENT_KIOSK_LICENSE_TYPE" desc="Text on radio button to select Single App Kiosk License during enrollment."> + Single App Kiosk + </message> + <message name="IDS_ENTERPRISE_ENROLLMENT_LICENSES_REMAINING_TEMPLATE" desc="Label indicating the number of remaining licenses for some license type."> + <ph name="LICENSE_TYPE">$1<ex>Perpetual</ex></ph> (<ph name="LICENSE_COUNT">$2<ex>10</ex></ph> rem.) + </message> <!-- About network UI display strings --> <message name="IDS_NETWORK_UI_TITLE" desc="Title of the page">
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 7bd88ac..84db47c2 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -5410,9 +5410,15 @@ </message> <!-- Feature Enagagement Tracker strings --> - <message name="IDS_NEWTAB_PROMO" desc="Text shown on promotional UI appearing next to the New Tab button, which encourages users to use it."> + <message name="IDS_NEWTAB_PROMO_0" desc="Variations option 1. Text shown on promotional UI appearing next to the New Tab button, which encourages users to use it."> Open a new tab with one click </message> + <message name="IDS_NEWTAB_PROMO_1" desc="Variations option 2. Text shown on promotional UI appearing next to the New Tab button, which encourages users to use it."> + You can click here to open a new tab + </message> + <message name="IDS_NEWTAB_PROMO_2" desc="Variations option 3. Text shown on promotional UI appearing next to the New Tab button, which encourages users to use it."> + Open a new tab to browse two sites at once + </message> <!-- Browser Hung Plugin Detector --> <if expr="is_win">
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index fbfa7f7..877a5d0 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -2174,8 +2174,6 @@ "android/offline_pages/downloads/offline_page_notification_bridge.h", "android/offline_pages/downloads/resource_throttle.cc", "android/offline_pages/downloads/resource_throttle.h", - "android/offline_pages/prefetch/prefetch_background_task.cc", - "android/offline_pages/prefetch/prefetch_background_task.h", "offline_pages/background_loader_offliner.cc", "offline_pages/background_loader_offliner.h", "offline_pages/offline_page_bookmark_observer.cc", @@ -2202,6 +2200,9 @@ "offline_pages/prefetch/offline_metrics_collector_impl.h", "offline_pages/prefetch/offline_prefetch_download_client.cc", "offline_pages/prefetch/offline_prefetch_download_client.h", + "offline_pages/prefetch/prefetch_background_task.h", + "offline_pages/prefetch/prefetch_background_task_handler_impl.cc", + "offline_pages/prefetch/prefetch_background_task_handler_impl.h", "offline_pages/prefetch/prefetch_importer_impl.cc", "offline_pages/prefetch/prefetch_importer_impl.h", "offline_pages/prefetch/prefetch_instance_id_proxy.cc", @@ -2228,6 +2229,8 @@ "offline_pages/android/offline_page_bridge.cc", "offline_pages/android/offline_page_bridge.h", "offline_pages/android/offline_page_utils_android.cc", + "offline_pages/android/prefetch_background_task_android.cc", + "offline_pages/android/prefetch_background_task_android.h", "offline_pages/android/prerendering_offliner.cc", "offline_pages/android/prerendering_offliner.h", "offline_pages/android/request_coordinator_factory.cc",
diff --git a/chrome/browser/android/offline_pages/prefetch/prefetch_background_task.cc b/chrome/browser/android/offline_pages/prefetch/prefetch_background_task.cc deleted file mode 100644 index 223e7ad..0000000 --- a/chrome/browser/android/offline_pages/prefetch/prefetch_background_task.cc +++ /dev/null
@@ -1,156 +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 "chrome/browser/android/offline_pages/prefetch/prefetch_background_task.h" - -#include <memory> - -#include "base/time/time.h" -#include "chrome/browser/offline_pages/prefetch/prefetch_service_factory.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/profiles/profile_android.h" -#include "chrome/common/pref_names.h" -#include "components/offline_pages/core/offline_page_feature.h" -#include "components/offline_pages/core/prefetch/prefetch_service.h" -#include "components/prefs/pref_registry_simple.h" -#include "components/prefs/pref_service.h" -#include "content/public/browser/browser_context.h" -#include "jni/PrefetchBackgroundTask_jni.h" -#include "net/base/backoff_entry_serializer.h" - -using base::android::JavaParamRef; -using base::android::ScopedJavaGlobalRef; -using base::android::ScopedJavaLocalRef; - -namespace offline_pages { - -const net::BackoffEntry::Policy kBackoffPolicy = { - 0, // Number of initial errors to ignore without backoff. - 30 * 1000, // Initial delay for backoff in ms: 30 seconds. - 2, // Factor to multiply for exponential backoff. - 0.33, // Fuzzing percentage. - 24 * 3600 * 1000, // Maximum time to delay requests in ms: 1 day. - -1, // Don't discard entry even if unused. - false // Don't use initial delay unless the last was an error. -}; - -namespace prefetch { - -// JNI call to start request processing in scheduled mode. -static jboolean StartPrefetchTask(JNIEnv* env, - const JavaParamRef<jobject>& jcaller, - const JavaParamRef<jobject>& jprofile) { - Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile); - DCHECK(profile); - - PrefetchService* prefetch_service = - PrefetchServiceFactory::GetForBrowserContext(profile); - if (!prefetch_service) - return false; - - prefetch_service->GetPrefetchDispatcher()->BeginBackgroundTask( - base::MakeUnique<PrefetchBackgroundTask>(env, jcaller, prefetch_service, - profile)); - return true; -} - -} // namespace prefetch - -void RegisterPrefetchBackgroundTaskPrefs(PrefRegistrySimple* registry) { - registry->RegisterListPref(prefs::kOfflinePrefetchBackoff); -} - -// static -void PrefetchBackgroundTask::Schedule(int additional_delay_seconds) { - JNIEnv* env = base::android::AttachCurrentThread(); - return prefetch::Java_PrefetchBackgroundTask_scheduleTask( - env, additional_delay_seconds); -} - -// static -void PrefetchBackgroundTask::Cancel() { - JNIEnv* env = base::android::AttachCurrentThread(); - return prefetch::Java_PrefetchBackgroundTask_cancelTask(env); -} - -PrefetchBackgroundTask::PrefetchBackgroundTask( - JNIEnv* env, - const JavaParamRef<jobject>& java_prefetch_background_task, - PrefetchService* service, - Profile* profile) - : java_prefetch_background_task_(java_prefetch_background_task), - service_(service), - profile_(profile) { - // Give the Java side a pointer to the new background task object. - prefetch::Java_PrefetchBackgroundTask_setNativeTask( - env, java_prefetch_background_task_, reinterpret_cast<jlong>(this)); - - SetupBackOff(); -} - -PrefetchBackgroundTask::~PrefetchBackgroundTask() { - JNIEnv* env = base::android::AttachCurrentThread(); - prefetch::Java_PrefetchBackgroundTask_doneProcessing( - env, java_prefetch_background_task_, needs_reschedule_); - - if (needs_reschedule_) { - // If the task is killed due to the system, it should be rescheduled without - // backoff even when it is in effect because we want to rerun the task asap. - Schedule(task_killed_by_system_ ? 0 : GetAdditionalBackoffSeconds()); - } -} - -void PrefetchBackgroundTask::SetupBackOff() { - const base::ListValue* value = - profile_->GetPrefs()->GetList(prefs::kOfflinePrefetchBackoff); - if (value) { - backoff_ = net::BackoffEntrySerializer::DeserializeFromValue( - *value, &kBackoffPolicy, nullptr, base::Time::Now()); - } - - if (!backoff_) - backoff_ = base::MakeUnique<net::BackoffEntry>(&kBackoffPolicy); -} - -bool PrefetchBackgroundTask::OnStopTask(JNIEnv* env, - const JavaParamRef<jobject>& jcaller) { - task_killed_by_system_ = true; - needs_reschedule_ = true; - service_->GetPrefetchDispatcher()->StopBackgroundTask(); - return false; -} - -void PrefetchBackgroundTask::SetTaskReschedulingForTesting( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& jcaller, - jboolean reschedule, - jboolean backoff) { - SetNeedsReschedule(static_cast<bool>(reschedule), static_cast<bool>(backoff)); -} - -void PrefetchBackgroundTask::SignalTaskFinishedForTesting( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& jcaller) { - service_->GetPrefetchDispatcher()->RequestFinishBackgroundTaskForTest(); -} - -void PrefetchBackgroundTask::SetNeedsReschedule(bool reschedule, bool backoff) { - needs_reschedule_ = reschedule; - - if (reschedule && backoff) - backoff_->InformOfRequest(false); - else - backoff_->Reset(); - - std::unique_ptr<base::Value> value = - net::BackoffEntrySerializer::SerializeToValue(*backoff_, - base::Time::Now()); - profile_->GetPrefs()->Set(prefs::kOfflinePrefetchBackoff, *value); -} - -int PrefetchBackgroundTask::GetAdditionalBackoffSeconds() const { - return static_cast<int>(backoff_->GetTimeUntilRelease().InSeconds()); -} - -} // namespace offline_pages
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json index 92120fd..ef21c0b 100644 --- a/chrome/browser/chrome_content_browser_manifest_overlay.json +++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -55,6 +55,7 @@ "blink::mojom::BudgetService", "blink::mojom::InstalledAppProvider", "blink::mojom::ShareService", + "blink::mojom::TextSuggestionHost", "bluetooth::mojom::AdapterFactory", "chrome::mojom::OpenSearchDocumentDescriptionHandler", "chrome::mojom::PrerenderCanceler",
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc b/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc index a69e3f2..a0d7b4e 100644 --- a/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc +++ b/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc
@@ -202,6 +202,8 @@ auth_code, shark_controller_ != nullptr /* fetch_additional_token */); } +void EnrollmentScreen::OnLicenseTypeSelected(const std::string& license_type) {} + void EnrollmentScreen::OnRetry() { retry_task_.Cancel(); ProcessRetry();
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_screen.h b/chrome/browser/chromeos/login/enrollment/enrollment_screen.h index 86b343b5..ff3807a2 100644 --- a/chrome/browser/chromeos/login/enrollment/enrollment_screen.h +++ b/chrome/browser/chromeos/login/enrollment/enrollment_screen.h
@@ -64,6 +64,7 @@ // EnrollmentScreenView::Controller implementation: void OnLoginDone(const std::string& user, const std::string& auth_code) override; + void OnLicenseTypeSelected(const std::string& license_type) override; void OnRetry() override; void OnCancel() override; void OnConfirmationClosed() override;
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_screen_view.h b/chrome/browser/chromeos/login/enrollment/enrollment_screen_view.h index cebbbc34..da8763c 100644 --- a/chrome/browser/chromeos/login/enrollment/enrollment_screen_view.h +++ b/chrome/browser/chromeos/login/enrollment/enrollment_screen_view.h
@@ -7,6 +7,7 @@ #include <string> +#include "base/values.h" #include "chrome/browser/chromeos/login/enrollment/enterprise_enrollment_helper.h" #include "chrome/browser/chromeos/login/oobe_screen.h" @@ -30,6 +31,7 @@ virtual void OnLoginDone(const std::string& user, const std::string& auth_code) = 0; + virtual void OnLicenseTypeSelected(const std::string& license_type) = 0; virtual void OnRetry() = 0; virtual void OnCancel() = 0; virtual void OnConfirmationClosed() = 0; @@ -55,6 +57,10 @@ // Shows the signin screen. virtual void ShowSigninScreen() = 0; + // Shows the license type selection screen. + virtual void ShowLicenseTypeSelectionScreen( + const base::DictionaryValue& license_types) = 0; + // Shows the Active Directory domain joining screen. virtual void ShowAdJoin() = 0;
diff --git a/chrome/browser/chromeos/login/enrollment/enterprise_enrollment_helper_impl.cc b/chrome/browser/chromeos/login/enrollment/enterprise_enrollment_helper_impl.cc index 59433ec..747c21f 100644 --- a/chrome/browser/chromeos/login/enrollment/enterprise_enrollment_helper_impl.cc +++ b/chrome/browser/chromeos/login/enrollment/enterprise_enrollment_helper_impl.cc
@@ -433,6 +433,9 @@ case policy::EnrollmentStatus::DM_TOKEN_STORE_FAILED: UMA(policy::kMetricEnrollmentStoreDMTokenFailed); break; + case policy::EnrollmentStatus::LICENSE_REQUEST_FAILED: + UMA(policy::kMetricEnrollmentLicenseRequestFailed); + break; } }
diff --git a/chrome/browser/chromeos/login/enrollment/mock_enrollment_screen.h b/chrome/browser/chromeos/login/enrollment/mock_enrollment_screen.h index 139c43b..2f4932b 100644 --- a/chrome/browser/chromeos/login/enrollment/mock_enrollment_screen.h +++ b/chrome/browser/chromeos/login/enrollment/mock_enrollment_screen.h
@@ -31,6 +31,8 @@ MOCK_METHOD0(Show, void()); MOCK_METHOD0(Hide, void()); MOCK_METHOD0(ShowSigninScreen, void()); + MOCK_METHOD1(ShowLicenseTypeSelectionScreen, + void(const base::DictionaryValue&)); MOCK_METHOD0(ShowAdJoin, void()); MOCK_METHOD2(ShowAttributePromptScreen, void(const std::string& asset_id, const std::string& location));
diff --git a/chrome/browser/chromeos/policy/enrollment_status_chromeos.h b/chrome/browser/chromeos/policy/enrollment_status_chromeos.h index bacd989f..b99b0b79 100644 --- a/chrome/browser/chromeos/policy/enrollment_status_chromeos.h +++ b/chrome/browser/chromeos/policy/enrollment_status_chromeos.h
@@ -43,8 +43,9 @@ ACTIVE_DIRECTORY_POLICY_FETCH_FAILED = 17, // Failed to fetch Active // Directory policy via // authpolicyd. - DM_TOKEN_STORE_FAILED = 18, // Failed to store DM token into the - // local state. + DM_TOKEN_STORE_FAILED = 18, // Failed to store DM token into the + // local state. + LICENSE_REQUEST_FAILED = 19, // Failed to get available license types. }; // Helpers for constructing errors for relevant cases.
diff --git a/chrome/browser/component_updater/chrome_component_updater_configurator.cc b/chrome/browser/component_updater/chrome_component_updater_configurator.cc index a7f3b5a..1eecd2c 100644 --- a/chrome/browser/component_updater/chrome_component_updater_configurator.cc +++ b/chrome/browser/component_updater/chrome_component_updater_configurator.cc
@@ -9,10 +9,7 @@ #include <string> #include <vector> -#include "base/sequenced_task_runner.h" #include "base/strings/sys_string_conversions.h" -#include "base/task_scheduler/post_task.h" -#include "base/task_scheduler/task_traits.h" #include "base/version.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" @@ -64,8 +61,6 @@ bool EnabledComponentUpdates() const override; bool EnabledBackgroundDownloader() const override; bool EnabledCupSigning() const override; - scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() - const override; PrefService* GetPrefService() const override; bool IsPerUserInstall() const override; std::vector<uint8_t> GetRunActionKeyHash() const override; @@ -183,14 +178,6 @@ return configurator_impl_.EnabledCupSigning(); } -// Returns a task runner to run blocking tasks. -scoped_refptr<base::SequencedTaskRunner> -ChromeConfigurator::GetSequencedTaskRunner() const { - return base::CreateSequencedTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::BACKGROUND, - base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); -} - PrefService* ChromeConfigurator::GetPrefService() const { DCHECK(pref_service_); return pref_service_;
diff --git a/chrome/browser/extensions/updater/chrome_update_client_config.h b/chrome/browser/extensions/updater/chrome_update_client_config.h index b3a53ac8..b44ffad2 100644 --- a/chrome/browser/extensions/updater/chrome_update_client_config.h +++ b/chrome/browser/extensions/updater/chrome_update_client_config.h
@@ -13,7 +13,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "components/component_updater/configurator_impl.h" -#include "extensions/browser/updater/update_client_config.h" +#include "components/update_client/configurator.h" namespace content { class BrowserContext; @@ -21,7 +21,7 @@ namespace extensions { -class ChromeUpdateClientConfig : public UpdateClientConfig { +class ChromeUpdateClientConfig : public update_client::Configurator { public: explicit ChromeUpdateClientConfig(content::BrowserContext* context);
diff --git a/chrome/browser/external_protocol/external_protocol_handler_unittest.cc b/chrome/browser/external_protocol/external_protocol_handler_unittest.cc index c5394c5..71acc9f2 100644 --- a/chrome/browser/external_protocol/external_protocol_handler_unittest.cc +++ b/chrome/browser/external_protocol/external_protocol_handler_unittest.cc
@@ -12,6 +12,7 @@ #include "chrome/test/base/testing_profile.h" #include "components/prefs/testing_pref_service.h" #include "content/public/test/test_browser_thread_bundle.h" +#include "content/public/test/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" class FakeExternalProtocolHandlerWorker @@ -110,9 +111,7 @@ class ExternalProtocolHandlerTest : public testing::Test { protected: - ExternalProtocolHandlerTest() - : test_browser_thread_bundle_( - content::TestBrowserThreadBundle::REAL_FILE_THREAD) {} + ExternalProtocolHandlerTest() {} void SetUp() override { local_state_.reset(new TestingPrefServiceSimple); @@ -143,7 +142,7 @@ ExternalProtocolHandler::LaunchUrlWithDelegate( url, 0, 0, ui::PAGE_TRANSITION_LINK, true, &delegate_); if (block_state != ExternalProtocolHandler::BLOCK) - base::RunLoop().Run(); + content::RunAllBlockingPoolTasksUntilIdle(); ASSERT_EQ(should_prompt, delegate_.has_prompted()); ASSERT_EQ(should_launch, delegate_.has_launched());
diff --git a/chrome/browser/favicon/content_favicon_driver_browsertest.cc b/chrome/browser/favicon/content_favicon_driver_browsertest.cc index 4645fdaf..539aacec 100644 --- a/chrome/browser/favicon/content_favicon_driver_browsertest.cc +++ b/chrome/browser/favicon/content_favicon_driver_browsertest.cc
@@ -295,6 +295,82 @@ GetFaviconForPageURL(url, favicon_base::FAVICON).bitmap_data); } +// Test that a page which uses a meta refresh tag to redirect gets associated +// to the favicons listed in the landing page. +IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, + AssociateIconWithInitialPageDespiteMetaRefreshTag) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url = embedded_test_server()->GetURL( + "/favicon/page_with_meta_refresh_tag.html"); + GURL landing_url = + embedded_test_server()->GetURL("/favicon/page_with_favicon.html"); + + PendingTaskWaiter waiter(web_contents(), landing_url); + ui_test_utils::NavigateToURLWithDisposition( + browser(), url, WindowOpenDisposition::CURRENT_TAB, + ui_test_utils::BROWSER_TEST_NONE); + waiter.Wait(); + + EXPECT_NE(nullptr, + GetFaviconForPageURL(url, favicon_base::FAVICON).bitmap_data); + EXPECT_NE( + nullptr, + GetFaviconForPageURL(landing_url, favicon_base::FAVICON).bitmap_data); +} + +// Test that a page gets a server-side redirect followed by a meta refresh tag +// gets associated to the favicons listed in the landing page. +IN_PROC_BROWSER_TEST_F( + ContentFaviconDriverTest, + AssociateIconWithInitialPageDespite300ResponseAndMetaRefreshTag) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url_with_meta_refresh_tag = embedded_test_server()->GetURL( + "/favicon/page_with_meta_refresh_tag.html"); + GURL url = embedded_test_server()->GetURL("/server-redirect?" + + url_with_meta_refresh_tag.spec()); + GURL landing_url = + embedded_test_server()->GetURL("/favicon/page_with_favicon.html"); + + PendingTaskWaiter waiter(web_contents(), landing_url); + ui_test_utils::NavigateToURLWithDisposition( + browser(), url, WindowOpenDisposition::CURRENT_TAB, + ui_test_utils::BROWSER_TEST_NONE); + waiter.Wait(); + + EXPECT_NE(nullptr, + GetFaviconForPageURL(url, favicon_base::FAVICON).bitmap_data); + EXPECT_NE( + nullptr, + GetFaviconForPageURL(landing_url, favicon_base::FAVICON).bitmap_data); +} + +// Test that a page gets a server-side redirect, followed by a meta refresh tag, +// followed by a server-side redirect gets associated to the favicons listed in +// the landing page. +IN_PROC_BROWSER_TEST_F( + ContentFaviconDriverTest, + AssociateIconWithInitialPageDespite300ResponseAndMetaRefreshTagTo300) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url_with_meta_refresh_tag = embedded_test_server()->GetURL( + "/favicon/page_with_meta_refresh_tag_to_server_redirect.html"); + GURL url = embedded_test_server()->GetURL("/server-redirect?" + + url_with_meta_refresh_tag.spec()); + GURL landing_url = + embedded_test_server()->GetURL("/favicon/page_with_favicon.html"); + + PendingTaskWaiter waiter(web_contents(), landing_url); + ui_test_utils::NavigateToURLWithDisposition( + browser(), url, WindowOpenDisposition::CURRENT_TAB, + ui_test_utils::BROWSER_TEST_NONE); + waiter.Wait(); + + EXPECT_NE(nullptr, + GetFaviconForPageURL(url, favicon_base::FAVICON).bitmap_data); + EXPECT_NE( + nullptr, + GetFaviconForPageURL(landing_url, favicon_base::FAVICON).bitmap_data); +} + // Test that a page which uses JavaScript to override document.location.hash // gets associated favicons. IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, @@ -342,6 +418,31 @@ GetFaviconForPageURL(landing_url, favicon_base::FAVICON).bitmap_data); } +// Test that a page which uses JavaScript document.location.replace() to +// navigate to a different landing page gets associated favicons listed in the +// landing page. +IN_PROC_BROWSER_TEST_F( + ContentFaviconDriverTest, + AssociateIconWithInitialPageDespiteLocationOverrideToOtherPage) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url = embedded_test_server()->GetURL( + "/favicon/page_with_location_override_to_other_page.html"); + GURL landing_url = + embedded_test_server()->GetURL("/favicon/page_with_favicon.html"); + + PendingTaskWaiter waiter(web_contents(), landing_url); + ui_test_utils::NavigateToURLWithDisposition( + browser(), url, WindowOpenDisposition::CURRENT_TAB, + ui_test_utils::BROWSER_TEST_NONE); + waiter.Wait(); + + EXPECT_NE(nullptr, + GetFaviconForPageURL(url, favicon_base::FAVICON).bitmap_data); + EXPECT_NE( + nullptr, + GetFaviconForPageURL(landing_url, favicon_base::FAVICON).bitmap_data); +} + #if defined(OS_ANDROID) IN_PROC_BROWSER_TEST_F(ContentFaviconDriverTest, LoadIconFromWebManifestDespitePushState) {
diff --git a/chrome/browser/metrics/android_metrics_provider.cc b/chrome/browser/metrics/android_metrics_provider.cc index 79847f4..56d362b7 100644 --- a/chrome/browser/metrics/android_metrics_provider.cc +++ b/chrome/browser/metrics/android_metrics_provider.cc
@@ -37,13 +37,14 @@ AndroidMetricsProvider::~AndroidMetricsProvider() { } -void AndroidMetricsProvider::ProvideStabilityMetrics( - metrics::SystemProfileProto* system_profile_proto) { +void AndroidMetricsProvider::ProvidePreviousSessionData( + metrics::ChromeUserMetricsExtension* uma_proto) { ConvertStabilityPrefsToHistograms(); } -void AndroidMetricsProvider::ProvideGeneralMetrics( +void AndroidMetricsProvider::ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) { + ConvertStabilityPrefsToHistograms(); UMA_HISTOGRAM_ENUMERATION( "CustomTabs.Visible", chrome::android::GetCustomTabsVisibleValue(),
diff --git a/chrome/browser/metrics/android_metrics_provider.h b/chrome/browser/metrics/android_metrics_provider.h index bdab08a..0302af3f 100644 --- a/chrome/browser/metrics/android_metrics_provider.h +++ b/chrome/browser/metrics/android_metrics_provider.h
@@ -24,9 +24,9 @@ ~AndroidMetricsProvider() override; // metrics::MetricsProvider: - void ProvideStabilityMetrics( - metrics::SystemProfileProto* system_profile_proto) override; - void ProvideGeneralMetrics( + void ProvidePreviousSessionData( + metrics::ChromeUserMetricsExtension* uma_proto) override; + void ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) override; // Called when the Activity that the user interacts with is swapped out.
diff --git a/chrome/browser/metrics/chromeos_metrics_provider.cc b/chrome/browser/metrics/chromeos_metrics_provider.cc index 96630f3..09aaaba8 100644 --- a/chrome/browser/metrics/chromeos_metrics_provider.cc +++ b/chrome/browser/metrics/chromeos_metrics_provider.cc
@@ -243,8 +243,14 @@ } } -void ChromeOSMetricsProvider::ProvideGeneralMetrics( +void ChromeOSMetricsProvider::ProvidePreviousSessionData( metrics::ChromeUserMetricsExtension* uma_proto) { + ProvideStabilityMetrics(uma_proto->mutable_system_profile()); +} + +void ChromeOSMetricsProvider::ProvideCurrentSessionData( + metrics::ChromeUserMetricsExtension* uma_proto) { + ProvideStabilityMetrics(uma_proto->mutable_system_profile()); std::vector<SampledProfile> sampled_profiles; if (perf_provider_.GetSampledProfiles(&sampled_profiles)) { for (auto& profile : sampled_profiles) {
diff --git a/chrome/browser/metrics/chromeos_metrics_provider.h b/chrome/browser/metrics/chromeos_metrics_provider.h index 7e1aeaf..c61fc48 100644 --- a/chrome/browser/metrics/chromeos_metrics_provider.h +++ b/chrome/browser/metrics/chromeos_metrics_provider.h
@@ -63,7 +63,9 @@ metrics::SystemProfileProto* system_profile_proto) override; void ProvideStabilityMetrics( metrics::SystemProfileProto* system_profile_proto) override; - void ProvideGeneralMetrics( + void ProvidePreviousSessionData( + metrics::ChromeUserMetricsExtension* uma_proto) override; + void ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) override; private:
diff --git a/chrome/browser/metrics/https_engagement_metrics_provider.cc b/chrome/browser/metrics/https_engagement_metrics_provider.cc index 185d77e..31f10954 100644 --- a/chrome/browser/metrics/https_engagement_metrics_provider.cc +++ b/chrome/browser/metrics/https_engagement_metrics_provider.cc
@@ -13,7 +13,7 @@ HttpsEngagementMetricsProvider::~HttpsEngagementMetricsProvider() {} -void HttpsEngagementMetricsProvider::ProvideGeneralMetrics( +void HttpsEngagementMetricsProvider::ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) { ProfileManager* profile_manager = g_browser_process->profile_manager(); if (!profile_manager)
diff --git a/chrome/browser/metrics/https_engagement_metrics_provider.h b/chrome/browser/metrics/https_engagement_metrics_provider.h index 730d975..d701f84 100644 --- a/chrome/browser/metrics/https_engagement_metrics_provider.h +++ b/chrome/browser/metrics/https_engagement_metrics_provider.h
@@ -17,7 +17,7 @@ ~HttpsEngagementMetricsProvider() override; // metrics:MetricsProvider: - void ProvideGeneralMetrics( + void ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) override; };
diff --git a/chrome/browser/offline_pages/android/prefetch_background_task_android.cc b/chrome/browser/offline_pages/android/prefetch_background_task_android.cc new file mode 100644 index 0000000..7133b9df --- /dev/null +++ b/chrome/browser/offline_pages/android/prefetch_background_task_android.cc
@@ -0,0 +1,120 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "chrome/browser/offline_pages/android/prefetch_background_task_android.h" + +#include <memory> + +#include "base/time/time.h" +#include "chrome/browser/offline_pages/prefetch/prefetch_background_task.h" +#include "chrome/browser/offline_pages/prefetch/prefetch_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_android.h" +#include "components/offline_pages/core/offline_page_feature.h" +#include "components/offline_pages/core/prefetch/prefetch_service.h" +#include "jni/PrefetchBackgroundTask_jni.h" + +using base::android::JavaParamRef; +using base::android::ScopedJavaGlobalRef; +using base::android::ScopedJavaLocalRef; + +namespace offline_pages { + +namespace prefetch { + +// JNI call to start request processing in scheduled mode. +static jboolean StartPrefetchTask(JNIEnv* env, + const JavaParamRef<jobject>& jcaller, + const JavaParamRef<jobject>& jprofile) { + Profile* profile = ProfileAndroid::FromProfileAndroid(jprofile); + DCHECK(profile); + + PrefetchService* prefetch_service = + PrefetchServiceFactory::GetForBrowserContext(profile); + if (!prefetch_service) + return false; + + prefetch_service->GetPrefetchDispatcher()->BeginBackgroundTask( + base::MakeUnique<PrefetchBackgroundTaskAndroid>(env, jcaller, + prefetch_service)); + return true; +} + +} // namespace prefetch + +// static +void PrefetchBackgroundTask::Schedule(int additional_delay_seconds, + bool update_current) { + JNIEnv* env = base::android::AttachCurrentThread(); + return prefetch::Java_PrefetchBackgroundTask_scheduleTask( + env, additional_delay_seconds, update_current); +} + +// static +void PrefetchBackgroundTask::Cancel() { + JNIEnv* env = base::android::AttachCurrentThread(); + return prefetch::Java_PrefetchBackgroundTask_cancelTask(env); +} + +PrefetchBackgroundTaskAndroid::PrefetchBackgroundTaskAndroid( + JNIEnv* env, + const JavaParamRef<jobject>& java_prefetch_background_task, + PrefetchService* service) + : java_prefetch_background_task_(java_prefetch_background_task), + service_(service) { + // Give the Java side a pointer to the new background task object. + prefetch::Java_PrefetchBackgroundTask_setNativeTask( + env, java_prefetch_background_task_, reinterpret_cast<jlong>(this)); +} + +PrefetchBackgroundTaskAndroid::~PrefetchBackgroundTaskAndroid() { + JNIEnv* env = base::android::AttachCurrentThread(); + prefetch::Java_PrefetchBackgroundTask_doneProcessing( + env, java_prefetch_background_task_, needs_reschedule_); + + PrefetchBackgroundTaskHandler* handler = + service_->GetPrefetchBackgroundTaskHandler(); + if (needs_backoff_) + handler->Backoff(); + else + handler->ResetBackoff(); + + if (needs_reschedule_) { + // If the task is killed due to the system, it should be rescheduled without + // backoff even when it is in effect because we want to rerun the task asap. + PrefetchBackgroundTask::Schedule( + task_killed_by_system_ ? 0 : handler->GetAdditionalBackoffSeconds(), + true /*update_current*/); + } +} + +bool PrefetchBackgroundTaskAndroid::OnStopTask( + JNIEnv* env, + const JavaParamRef<jobject>& jcaller) { + task_killed_by_system_ = true; + needs_reschedule_ = true; + service_->GetPrefetchDispatcher()->StopBackgroundTask(); + return false; +} + +void PrefetchBackgroundTaskAndroid::SetTaskReschedulingForTesting( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& jcaller, + jboolean reschedule, + jboolean backoff) { + SetNeedsReschedule(static_cast<bool>(reschedule), static_cast<bool>(backoff)); +} + +void PrefetchBackgroundTaskAndroid::SignalTaskFinishedForTesting( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& jcaller) { + service_->GetPrefetchDispatcher()->RequestFinishBackgroundTaskForTest(); +} + +void PrefetchBackgroundTaskAndroid::SetNeedsReschedule(bool reschedule, + bool backoff) { + needs_reschedule_ = needs_reschedule_ || reschedule; + needs_backoff_ = needs_backoff_ || backoff; +} + +} // namespace offline_pages
diff --git a/chrome/browser/android/offline_pages/prefetch/prefetch_background_task.h b/chrome/browser/offline_pages/android/prefetch_background_task_android.h similarity index 61% rename from chrome/browser/android/offline_pages/prefetch/prefetch_background_task.h rename to chrome/browser/offline_pages/android/prefetch_background_task_android.h index ce7179d..cf124d4 100644 --- a/chrome/browser/android/offline_pages/prefetch/prefetch_background_task.h +++ b/chrome/browser/offline_pages/android/prefetch_background_task_android.h
@@ -2,38 +2,28 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_ANDROID_OFFLINE_PAGES_PREFETCH_PREFETCH_BACKGROUND_TASK_H_ -#define CHROME_BROWSER_ANDROID_OFFLINE_PAGES_PREFETCH_PREFETCH_BACKGROUND_TASK_H_ +#ifndef CHROME_BROWSER_OFFLINE_PAGES_ANDROID_PREFETCH_BACKGROUND_TASK_ANDROID_H_ +#define CHROME_BROWSER_OFFLINE_PAGES_ANDROID_PREFETCH_BACKGROUND_TASK_ANDROID_H_ #include "base/android/jni_android.h" +#include "components/offline_pages/core/prefetch/prefetch_background_task_handler.h" #include "components/offline_pages/core/prefetch/prefetch_dispatcher.h" -#include "components/offline_pages/core/prefetch/prefetch_service.h" #include "net/base/backoff_entry.h" -class PrefRegistrySimple; -class Profile; - namespace offline_pages { class PrefetchService; // A task with a counterpart in Java for managing the background activity of the // offline page prefetcher. Schedules and listens for events about prefetching // tasks. -class PrefetchBackgroundTask : public PrefetchDispatcher::ScopedBackgroundTask { +class PrefetchBackgroundTaskAndroid + : public PrefetchDispatcher::ScopedBackgroundTask { public: - PrefetchBackgroundTask( + PrefetchBackgroundTaskAndroid( JNIEnv* env, const base::android::JavaParamRef<jobject>& j_prefetch_background_task, - PrefetchService* service, - Profile* profile); - ~PrefetchBackgroundTask() override; - - // API for interacting with BackgroundTaskScheduler from native. - // Schedules the default 'NWake' prefetching task. - static void Schedule(int additional_delay_seconds); - - // Cancels the default 'NWake' prefetching task. - static void Cancel(); + PrefetchService* service); + ~PrefetchBackgroundTaskAndroid() override; // Java hooks. bool OnStopTask(JNIEnv* env, @@ -53,25 +43,17 @@ bool needs_reschedule() { return needs_reschedule_; } private: - void SetupBackOff(); - int GetAdditionalBackoffSeconds() const; - bool task_killed_by_system_ = false; bool needs_reschedule_ = false; + bool needs_backoff_ = false; // A pointer to the controlling |PrefetchBackgroundTask|. base::android::ScopedJavaGlobalRef<jobject> java_prefetch_background_task_; // The PrefetchService owns |this|, so a raw pointer is OK. PrefetchService* service_; - - Profile* profile_; - - std::unique_ptr<net::BackoffEntry> backoff_; }; -void RegisterPrefetchBackgroundTaskPrefs(PrefRegistrySimple* registry); - } // namespace offline_pages -#endif // CHROME_BROWSER_ANDROID_OFFLINE_PAGES_PREFETCH_PREFETCH_BACKGROUND_TASK_H_ +#endif // CHROME_BROWSER_OFFLINE_PAGES_ANDROID_PREFETCH_BACKGROUND_TASK_ANDROID_H_
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_background_task.h b/chrome/browser/offline_pages/prefetch/prefetch_background_task.h new file mode 100644 index 0000000..49420239 --- /dev/null +++ b/chrome/browser/offline_pages/prefetch/prefetch_background_task.h
@@ -0,0 +1,25 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_PREFETCH_BACKGROUND_TASK_H_ +#define CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_PREFETCH_BACKGROUND_TASK_H_ + +namespace offline_pages { + +class PrefetchBackgroundTask { + public: + // API for interacting with BackgroundTaskScheduler from native. + // Schedules the default 'NWake' prefetching task. + // |additional_delay_seconds| is relative to the default 15 minute delay. + // Implemented in platform-specific object files. + static void Schedule(int additional_delay_seconds, bool update_current); + + // Cancels the default 'NWake' prefetching task. + // Implemented in platform-specific object files. + static void Cancel(); +}; + +} // namespace offline_pages + +#endif // CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_PREFETCH_BACKGROUND_TASK_H_
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.cc b/chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.cc new file mode 100644 index 0000000..d2fe8df6 --- /dev/null +++ b/chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.cc
@@ -0,0 +1,93 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.h" + +#include "base/memory/ptr_util.h" +#include "chrome/browser/offline_pages/prefetch/prefetch_background_task.h" +#include "chrome/common/pref_names.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" +#include "net/base/backoff_entry_serializer.h" + +namespace offline_pages { + +const net::BackoffEntry::Policy kBackoffPolicy = { + 0, // Number of initial errors to ignore without backoff. + 30 * 1000, // Initial delay for backoff in ms: 30 seconds. + 2, // Factor to multiply for exponential backoff. + 0.33, // Fuzzing percentage. + 24 * 3600 * 1000, // Maximum time to delay requests in ms: 1 day. + -1, // Don't discard entry even if unused. + false // Don't use initial delay unless the last was an error. +}; + +PrefetchBackgroundTaskHandlerImpl::PrefetchBackgroundTaskHandlerImpl( + PrefService* prefs) + : prefs_(prefs) {} +PrefetchBackgroundTaskHandlerImpl::~PrefetchBackgroundTaskHandlerImpl() = + default; + +void PrefetchBackgroundTaskHandlerImpl::CancelBackgroundTask() { + PrefetchBackgroundTask::Cancel(); +} + +int PrefetchBackgroundTaskHandlerImpl::GetAdditionalBackoffSeconds() const { + return static_cast<int>( + GetCurrentBackoff()->GetTimeUntilRelease().InSeconds()); +} + +void PrefetchBackgroundTaskHandlerImpl::EnsureTaskScheduled() { + int seconds_until_release = 0; + std::unique_ptr<net::BackoffEntry> backoff = GetCurrentBackoff(); + if (backoff) + seconds_until_release = backoff->GetTimeUntilRelease().InSeconds(); + PrefetchBackgroundTask::Schedule(seconds_until_release, + false /*update_current*/); +} + +std::unique_ptr<net::BackoffEntry> +PrefetchBackgroundTaskHandlerImpl::GetCurrentBackoff() const { + const base::ListValue* value = + prefs_->GetList(prefs::kOfflinePrefetchBackoff); + std::unique_ptr<net::BackoffEntry> result; + if (value) { + result = net::BackoffEntrySerializer::DeserializeFromValue( + *value, &kBackoffPolicy, clock_, base::Time::Now()); + } + if (!result) + return base::MakeUnique<net::BackoffEntry>(&kBackoffPolicy, clock_); + return result; +} + +void PrefetchBackgroundTaskHandlerImpl::Backoff() { + std::unique_ptr<net::BackoffEntry> current = GetCurrentBackoff(); + current->InformOfRequest(false); + UpdateBackoff(current.get()); +} + +void PrefetchBackgroundTaskHandlerImpl::ResetBackoff() { + std::unique_ptr<net::BackoffEntry> current = GetCurrentBackoff(); + current->Reset(); + UpdateBackoff(current.get()); +} + +void PrefetchBackgroundTaskHandlerImpl::SetTickClockForTesting( + base::TickClock* clock) { + clock_ = clock; +} + +void PrefetchBackgroundTaskHandlerImpl::UpdateBackoff( + net::BackoffEntry* backoff) { + std::unique_ptr<base::Value> value = + net::BackoffEntrySerializer::SerializeToValue(*backoff, + base::Time::Now()); + prefs_->Set(prefs::kOfflinePrefetchBackoff, *value); +} + +void RegisterPrefetchBackgroundTaskPrefs(PrefRegistrySimple* registry) { + registry->RegisterListPref(prefs::kOfflinePrefetchBackoff); +} + +} // namespace offline_pages
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.h b/chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.h new file mode 100644 index 0000000..e959ab0 --- /dev/null +++ b/chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.h
@@ -0,0 +1,62 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_PREFETCH_BACKGROUND_TASK_HANDLER_IMPL_H_ +#define CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_PREFETCH_BACKGROUND_TASK_HANDLER_IMPL_H_ + +#include <memory> + +#include "base/macros.h" +#include "base/time/tick_clock.h" +#include "components/offline_pages/core/prefetch/prefetch_background_task_handler.h" + +class PrefRegistrySimple; +class PrefService; + +namespace base { +class TickClock; +} + +namespace net { +class BackoffEntry; +} + +namespace offline_pages { + +// A class living on the PrefetchService that is able to ask the Android +// background task system to schedule or cancel our task. Also manages the +// backoff, so we can schedule tasks at the appropriate time based on the +// backoff value. +class PrefetchBackgroundTaskHandlerImpl : public PrefetchBackgroundTaskHandler { + public: + explicit PrefetchBackgroundTaskHandlerImpl(PrefService* profile); + ~PrefetchBackgroundTaskHandlerImpl() override; + + // PrefetchBackgroundTaskHandler implementation. + void CancelBackgroundTask() override; + void EnsureTaskScheduled() override; + + // Backoff control. These functions directly modify/read prefs. + void Backoff() override; + void ResetBackoff() override; + int GetAdditionalBackoffSeconds() const override; + + // This is used to construct the backoff value. + void SetTickClockForTesting(base::TickClock* clock); + + private: + std::unique_ptr<net::BackoffEntry> GetCurrentBackoff() const; + void UpdateBackoff(net::BackoffEntry* backoff); + + PrefService* prefs_; + base::TickClock* clock_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(PrefetchBackgroundTaskHandlerImpl); +}; + +void RegisterPrefetchBackgroundTaskPrefs(PrefRegistrySimple* registry); + +} // namespace offline_pages + +#endif // CHROME_BROWSER_OFFLINE_PAGES_PREFETCH_PREFETCH_BACKGROUND_TASK_HANDLER_IMPL_H_
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl_unittest.cc b/chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl_unittest.cc new file mode 100644 index 0000000..947d65e5 --- /dev/null +++ b/chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl_unittest.cc
@@ -0,0 +1,74 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.h" + +#include "base/test/test_mock_time_task_runner.h" +#include "chrome/test/base/testing_profile.h" +#include "components/prefs/pref_service.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "net/base/backoff_entry.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace offline_pages { + +class PrefetchBackgroundTaskHandlerImplTest : public testing::Test { + public: + PrefetchBackgroundTaskHandlerImplTest(); + ~PrefetchBackgroundTaskHandlerImplTest() override; + + void SetUp() override; + + PrefetchBackgroundTaskHandlerImpl* task_handler() { + return task_handler_.get(); + } + int64_t BackoffTime() { + return task_handler()->GetAdditionalBackoffSeconds(); + } + + std::unique_ptr<PrefetchBackgroundTaskHandlerImpl> CreateHandler() { + auto result = base::MakeUnique<PrefetchBackgroundTaskHandlerImpl>( + profile_.GetPrefs()); + result->SetTickClockForTesting(clock_.get()); + return result; + } + + protected: + content::TestBrowserThreadBundle thread_bundle_; + TestingProfile profile_; + scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; + std::unique_ptr<base::TickClock> clock_; + std::unique_ptr<PrefetchBackgroundTaskHandlerImpl> task_handler_; + + private: + DISALLOW_COPY_AND_ASSIGN(PrefetchBackgroundTaskHandlerImplTest); +}; + +PrefetchBackgroundTaskHandlerImplTest::PrefetchBackgroundTaskHandlerImplTest() + : task_runner_(new base::TestMockTimeTaskRunner(base::Time::Now(), + base::TimeTicks::Now())), + clock_(task_runner_->GetMockTickClock()) { + task_handler_ = CreateHandler(); +} + +PrefetchBackgroundTaskHandlerImplTest:: + ~PrefetchBackgroundTaskHandlerImplTest() {} + +void PrefetchBackgroundTaskHandlerImplTest::SetUp() {} + +TEST_F(PrefetchBackgroundTaskHandlerImplTest, Backoff) { + EXPECT_EQ(0, task_handler()->GetAdditionalBackoffSeconds()); + + task_handler()->Backoff(); + EXPECT_GT(task_handler()->GetAdditionalBackoffSeconds(), 0); + + // Reset the task handler to ensure it lasts across restarts. + task_handler_ = CreateHandler(); + EXPECT_GT(task_handler()->GetAdditionalBackoffSeconds(), 0); + + task_handler()->ResetBackoff(); + EXPECT_EQ(0, task_handler()->GetAdditionalBackoffSeconds()); +} + +} // namespace offline_pages
diff --git a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc index 470b30a..d4d7791 100644 --- a/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc +++ b/chrome/browser/offline_pages/prefetch/prefetch_service_factory.cc
@@ -12,8 +12,10 @@ #include "base/memory/singleton.h" #include "base/sequenced_task_runner.h" #include "base/task_scheduler/post_task.h" +#include "build/build_config.h" #include "chrome/browser/download/download_service_factory.h" #include "chrome/browser/offline_pages/prefetch/offline_metrics_collector_impl.h" +#include "chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.h" #include "chrome/browser/offline_pages/prefetch/prefetch_importer_impl.h" #include "chrome/browser/offline_pages/prefetch/prefetch_instance_id_proxy.h" #include "chrome/browser/profiles/profile.h" @@ -78,7 +80,6 @@ auto suggested_articles_observer = base::MakeUnique<SuggestedArticlesObserver>(); - auto prefetch_downloader = base::MakeUnique<PrefetchDownloader>( DownloadServiceFactory::GetForBrowserContext(context), chrome::GetChannel()); @@ -86,12 +87,18 @@ auto prefetch_importer = base::MakeUnique<PrefetchImporterImpl>(context, background_task_runner); + std::unique_ptr<PrefetchBackgroundTaskHandler> + prefetch_background_task_handler = + base::MakeUnique<PrefetchBackgroundTaskHandlerImpl>( + profile->GetPrefs()); + return new PrefetchServiceImpl( std::move(offline_metrics_collector), std::move(prefetch_dispatcher), std::move(prefetch_gcm_app_handler), std::move(prefetch_network_request_factory), std::move(prefetch_store), std::move(suggested_articles_observer), std::move(prefetch_downloader), - std::move(prefetch_importer)); + std::move(prefetch_importer), + std::move(prefetch_background_task_handler)); } } // namespace offline_pages
diff --git a/chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer_browsertest.cc index 87e1782c..ed17e5a2 100644 --- a/chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer_browsertest.cc +++ b/chrome/browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer_browsertest.cc
@@ -154,7 +154,7 @@ } void FakeUserMetricsUpload() { - metrics_provider_->ProvideGeneralMetrics(NULL); + metrics_provider_->ProvideCurrentSessionData(NULL); } protected:
diff --git a/chrome/browser/payments/android/journey_logger_android.cc b/chrome/browser/payments/android/journey_logger_android.cc index 55fbc28..5d50b9075 100644 --- a/chrome/browser/payments/android/journey_logger_android.cc +++ b/chrome/browser/payments/android/journey_logger_android.cc
@@ -80,12 +80,6 @@ journey_logger_.SetCanMakePaymentValue(jvalue); } -void JourneyLoggerAndroid::SetShowCalled( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& jcaller) { - journey_logger_.SetShowCalled(); -} - void JourneyLoggerAndroid::SetEventOccurred( JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller,
diff --git a/chrome/browser/payments/android/journey_logger_android.h b/chrome/browser/payments/android/journey_logger_android.h index c67871d7..4fbb5496 100644 --- a/chrome/browser/payments/android/journey_logger_android.h +++ b/chrome/browser/payments/android/journey_logger_android.h
@@ -43,8 +43,6 @@ JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller, jboolean jvalue); - void SetShowCalled(JNIEnv* env, - const base::android::JavaParamRef<jobject>& jcaller); void SetEventOccurred(JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller, jint jevent);
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 3730c9e..7e207ed 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -145,8 +145,8 @@ #endif // BUILDFLAG(ENABLE_EXTENSIONS) #if BUILDFLAG(ENABLE_OFFLINE_PAGES) -#include "chrome/browser/android/offline_pages/prefetch/prefetch_background_task.h" #include "chrome/browser/offline_pages/prefetch/offline_metrics_collector_impl.h" +#include "chrome/browser/offline_pages/prefetch/prefetch_background_task_handler_impl.h" #endif #if BUILDFLAG(ENABLE_PLUGINS)
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.html b/chrome/browser/resources/chromeos/login/custom_elements_login.html index 1e06aa3b..6a1fdf5 100644 --- a/chrome/browser/resources/chromeos/login/custom_elements_login.html +++ b/chrome/browser/resources/chromeos/login/custom_elements_login.html
@@ -27,5 +27,6 @@ <include src="oobe_voice_interaction_value_prop.html"> <include src="oobe_wait_for_container_ready.html"> <include src="encryption_migration.html"> +<include src="enrollment_license_card.html"> <script src="chrome://oobe/custom_elements.js"></script>
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.js b/chrome/browser/resources/chromeos/login/custom_elements_login.js index d07ff7cf..dc682da 100644 --- a/chrome/browser/resources/chromeos/login/custom_elements_login.js +++ b/chrome/browser/resources/chromeos/login/custom_elements_login.js
@@ -27,3 +27,4 @@ // <include src="encryption_migration.js"> // <include src="oobe_voice_interaction_value_prop.js"> // <include src="oobe_wait_for_container_ready.js"> +// <include src="enrollment_license_card.js"> \ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html index de4d1d5..e9d7197c 100644 --- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html +++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
@@ -32,5 +32,6 @@ <include src="offline_ad_login.html"> <include src="active_directory_password_change.html"> <include src="arc_terms_of_service.html"> +<include src="enrollment_license_card.html"> <script src="chrome://oobe/custom_elements.js"></script>
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js index b6e5cef4..8500404 100644 --- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js +++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
@@ -42,3 +42,4 @@ // <include src="arc_terms_of_service.js"> // <include src="oobe_voice_interaction_value_prop.js"> // <include src="oobe_wait_for_container_ready.js"> +// <include src="enrollment_license_card.js">
diff --git a/chrome/browser/resources/chromeos/login/enrollment_license_card.css b/chrome/browser/resources/chromeos/login/enrollment_license_card.css new file mode 100644 index 0000000..4c890901 --- /dev/null +++ b/chrome/browser/resources/chromeos/login/enrollment_license_card.css
@@ -0,0 +1,13 @@ +/* Copyright 2015 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + + +.gaia-radio-button { + display: block; +} + +.license-card-explanation { + font-size: 15px; + margin: 8px 0 0 0; +} \ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/login/enrollment_license_card.html b/chrome/browser/resources/chromeos/login/enrollment_license_card.html new file mode 100644 index 0000000..9883ea6e --- /dev/null +++ b/chrome/browser/resources/chromeos/login/enrollment_license_card.html
@@ -0,0 +1,61 @@ +<!-- Copyright 2017 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> + +<link rel="import" + href="chrome://resources/polymer/v1_0/paper-radio-button/paper-radio-button.html"> +<link rel="import" + href="chrome://resources/polymer/v1_0/paper-radio-group/paper-radio-group.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html"> + +<!-- + UI for the Enrollment license type selection. + + Example: + <enrollment-license-card> </enrollment-license-card> + + Attributes: + 'disabled' - Whether the UI disabled. Could be used to disable the UI + during blocking IO operations. + 'selected' - Selected license type. + 'buttonText' - Text on the confirmation button. + + Events: + 'buttonclick' - Fired when user confirms license type selection. +--> +<dom-module id="enrollment-license-card"> + <template> + <link rel="stylesheet" href="gaia_input_form.css"> + <link rel="stylesheet" href="enrollment_license_card.css"> + + <gaia-card id="license-selection-prompt-card" class="fit"> + <div class="header flex vertical layout end-justified"> + <h1 class="welcome-message" style="text-transform:capitalize" + i18n-content="licenseSelectionCardTitle"></h1> + </div> + <div class="footer flex vertical layout justified"> + <div> + <p class="license-card-explanation" + i18n-content="licenseSelectionCardExplanation"></p> + <paper-radio-group selected="{{selected}}"> + <template is="dom-repeat" items="[[licenses]]" + id="repeatTemplate"> + <paper-radio-button class="gaia-radio-button" + disabled$="[[or_(item.disabled, disabled)]]" + hidden$="[[item.hidden]]" name="[[item.id]]"> + [[formatTitle_(item)]] + </paper-radio-button> + </template> + </paper-radio-group> + </div> + <div class="horizontal-reverse justified layout center"> + <gaia-button id="submitButton" class="self-end" + disabled="[[disabled]]" + on-tap="buttonClicked_"> + <span>[[buttonText]]</span> + </gaia-button> + </div> + </div> + </gaia-card> + </template> +</dom-module>
diff --git a/chrome/browser/resources/chromeos/login/enrollment_license_card.js b/chrome/browser/resources/chromeos/login/enrollment_license_card.js new file mode 100644 index 0000000..6f3de855 --- /dev/null +++ b/chrome/browser/resources/chromeos/login/enrollment_license_card.js
@@ -0,0 +1,89 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Polymer element for displaying enrollment license selection + card. + */ + +Polymer({ + is: 'enrollment-license-card', + + behaviors: [I18nBehavior], + + properties: { + /** + * Whether the UI disabled. + */ + disabled: { + type: Boolean, + value: true, + }, + + /** + * Selected license type + */ + selected: { + type: String, + value: 'sometype', + }, + + /** + * Array with available license types. + */ + licenses: { + type: Array, + value: function() { + return [ + { + id: 'licenseType', + label: 'perpetualLicenseTypeTitle', + count: 123, + disabled: false, + hidden: false, + }, + ]; + }, + observer: 'licensesChanged_', + }, + }, + + get submitButton() { + return this.$.submitButton; + }, + + buttonClicked_: function() { + this.fire('buttonclick'); + }, + + licensesChanged_: function(newValue, oldValue) { + var firstSelection = ''; + for (var i = 0, item; item = this.licenses[i]; ++i) { + if (this.isSelectable_(item) && firstSelection == '') { + firstSelection = item.id; + break; + } + } + if (firstSelection != '') { + this.selected = firstSelection; + } else if (this.licenses[0]) { + this.selected = this.licenses[0].id; + } else { + this.selected = ''; + this.disabled = true; + } + }, + + isSelectable_: function(item) { + return item.count > 0 && !item.disabled && !item.hidden; + }, + + formatTitle_: function(item) { + return this.i18n('licenseCountTemplate', this.i18n(item.label), item.count); + }, + + or_: function(left, right) { + return left || right; + }, +});
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.css b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.css index 83e9bf5..f83cbc8 100644 --- a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.css +++ b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.css
@@ -38,6 +38,7 @@ #oauth-enroll-step-attribute-prompt-error, .oauth-enroll-state-signin #oauth-enroll-step-signin, .oauth-enroll-state-ad-join #oauth-enroll-step-ad-join, +.oauth-enroll-state-license #oauth-enroll-step-license, .oauth-enroll-state-attribute-prompt #oauth-enroll-step-attribute-prompt { display: block;
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.html b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.html index bd026bac..82c5077 100644 --- a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.html +++ b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.html
@@ -15,6 +15,11 @@ <throbber-notice class="fit" i18n-values="text:oauthEnrollWorking"> </throbber-notice> </div> + <div id="oauth-enroll-step-license"> + <enrollment-license-card id="oauth-enroll-license-ui" + i18n-values="button-text:oauthEnrollNextBtn"> + </enrollment-license-card> + </div> <div id="oauth-enroll-step-ad-join"> <offline-ad-login id="oauth-enroll-ad-join-ui" show-machine-input class="fit" i18n-values=
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js index 2b5f56c..6d95d3f1 100644 --- a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js +++ b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js
@@ -12,6 +12,7 @@ /** @const */ var STEP_SIGNIN = 'signin'; /** @const */ var STEP_AD_JOIN = 'ad-join'; + /** @const */ var STEP_LICENSE_TYPE = 'license'; /** @const */ var STEP_WORKING = 'working'; /** @const */ var STEP_ATTRIBUTE_PROMPT = 'attribute-prompt'; /** @const */ var STEP_ERROR = 'error'; @@ -30,6 +31,7 @@ 'showStep', 'showError', 'doReload', + 'setAvailableLicenseTypes', 'showAttributePromptStep', 'showAttestationBasedEnrollmentSuccess', 'invalidateAd', @@ -61,6 +63,13 @@ isManualEnrollment_: undefined, /** + * An element containing UI for picking license type. + * @type {EnrollmentLicenseCard} + * @private + */ + licenseUi_: undefined, + + /** * An element containg navigation buttons. */ navigation_: undefined, @@ -95,6 +104,7 @@ decorate: function() { this.navigation_ = $('oauth-enroll-navigation'); this.offlineAdUi_ = $('oauth-enroll-ad-join-ui'); + this.licenseUi_ = $('oauth-enroll-license-ui'); this.authenticator_ = new cr.login.Authenticator($('oauth-enroll-auth-view')); @@ -228,6 +238,11 @@ $('oauth-enroll-skip-button') .addEventListener('click', this.onSkipButtonClicked.bind(this)); + + this.licenseUi_.addEventListener('buttonclick', function() { + chrome.send('onLicenseTypeSelected', [this.licenseUi_.selected]); + }.bind(this)); + }, /** @@ -329,6 +344,39 @@ }, /** + * Updates the list of available license types in license selection dialog. + */ + setAvailableLicenseTypes: function(licenseTypes) { + var licenses = [ + { + id: 'perpetual', + label: 'perpetualLicenseTypeTitle', + }, + { + id: 'annual', + label: 'annualLicenseTypeTitle', + }, + { + id: 'kiosk', + label: 'kioskLicenseTypeTitle', + } + ]; + for (var i = 0, item; item = licenses[i]; ++i) { + if (item.id in licenseTypes) { + item.count = parseInt(licenseTypes[item.id]); + item.disabled = item.count == 0; + item.hidden = false; + } else { + item.count = 0; + item.disabled = true; + item.hidden = true; + } + } + this.licenseUi_.disabled = false; + this.licenseUi_.licenses = licenses; + }, + + /** * Switches between the different steps in the enrollment flow. * @param {string} step the steps to show, one of "signin", "working", * "attribute-prompt", "error", "success". @@ -339,6 +387,8 @@ if (step == STEP_SIGNIN) { $('oauth-enroll-auth-view').focus(); + } else if (step == STEP_LICENSE_TYPE) { + $('oauth-enroll-license-ui').submitButton.focus(); } else if (step == STEP_ERROR) { $('oauth-enroll-error-card').submitButton.focus(); } else if (step == STEP_SUCCESS) {
diff --git a/chrome/browser/resources/md_bookmarks/command_manager.js b/chrome/browser/resources/md_bookmarks/command_manager.js index dee208ef..0b7502c 100644 --- a/chrome/browser/resources/md_bookmarks/command_manager.js +++ b/chrome/browser/resources/md_bookmarks/command_manager.js
@@ -83,8 +83,8 @@ */ this.menuSource_ = MenuSource.NONE; - /** @private {Object<Command, cr.ui.KeyboardShortcutList>} */ - this.shortcuts_ = {}; + /** @private {!Map<Command, cr.ui.KeyboardShortcutList>} */ + this.shortcuts_ = new Map(); this.addShortcut_(Command.EDIT, 'F2', 'Enter'); this.addShortcut_(Command.DELETE, 'Delete', 'Delete Backspace'); @@ -351,6 +351,9 @@ default: assert(false); } + + bookmarks.util.recordEnumHistogram( + 'BookmarkManager.CommandExecuted', command, Command.MAX_VALUE); }, /** @@ -360,11 +363,16 @@ * shortcut. */ handleKeyEvent: function(e, itemIds) { - for (var commandName in this.shortcuts_) { - var shortcut = this.shortcuts_[commandName]; - if (shortcut.matchesEvent(e) && this.canExecute(commandName, itemIds)) { - this.handle(commandName, itemIds); + for (var commandTuple of this.shortcuts_) { + var command = /** @type {Command} */ (commandTuple[0]); + var shortcut = + /** @type {cr.ui.KeyboardShortcutList} */ (commandTuple[1]); + if (shortcut.matchesEvent(e) && this.canExecute(command, itemIds)) { + this.handle(command, itemIds); + bookmarks.util.recordEnumHistogram( + 'BookmarkManager.CommandExecutedFromKeyboard', command, + Command.MAX_VALUE); e.stopPropagation(); e.preventDefault(); return true; @@ -387,7 +395,7 @@ */ addShortcut_: function(command, shortcut, macShortcut) { var shortcut = (cr.isMac && macShortcut) ? macShortcut : shortcut; - this.shortcuts_[command] = new cr.ui.KeyboardShortcutList(shortcut); + this.shortcuts_.set(command, new cr.ui.KeyboardShortcutList(shortcut)); }, /** @@ -638,7 +646,9 @@ */ onCommandClick_: function(e) { this.handle( - e.currentTarget.getAttribute('command'), assert(this.menuIds_)); + /** @type {Command} */ ( + Number(e.currentTarget.getAttribute('command'))), + assert(this.menuIds_)); this.closeCommandMenu(); },
diff --git a/chrome/browser/resources/md_bookmarks/constants.js b/chrome/browser/resources/md_bookmarks/constants.js index 8ff285b..4741bef 100644 --- a/chrome/browser/resources/md_bookmarks/constants.js +++ b/chrome/browser/resources/md_bookmarks/constants.js
@@ -16,26 +16,31 @@ }; /** - * @enum {string} + * Commands which can be handled by the CommandManager. This enum is also used + * for metrics and should be kept in sync with BookmarkManagerCommand in + * enums.xml. + * @enum {number} * @const */ var Command = { - EDIT: 'edit', - COPY_URL: 'copy-url', - SHOW_IN_FOLDER: 'show-in-folder', - DELETE: 'delete', - OPEN_NEW_TAB: 'open-new-tab', - OPEN_NEW_WINDOW: 'open-new-window', - OPEN_INCOGNITO: 'open-incognito', - UNDO: 'undo', - REDO: 'redo', + EDIT: 0, + COPY_URL: 1, + SHOW_IN_FOLDER: 2, + DELETE: 3, + OPEN_NEW_TAB: 4, + OPEN_NEW_WINDOW: 5, + OPEN_INCOGNITO: 6, + UNDO: 7, + REDO: 8, // OPEN triggers when you double-click an item. - OPEN: 'open', - SELECT_ALL: 'select-all', - DESELECT_ALL: 'deselect-all', - COPY: 'copy', - CUT: 'cut', - PASTE: 'paste', + OPEN: 9, + SELECT_ALL: 10, + DESELECT_ALL: 11, + COPY: 12, + CUT: 13, + PASTE: 14, + // Append new values to the end of the enum. + MAX_VALUE: 15, }; /**
diff --git a/chrome/browser/resources/md_bookmarks/util.js b/chrome/browser/resources/md_bookmarks/util.js index 8ac5212..52f5dd2 100644 --- a/chrome/browser/resources/md_bookmarks/util.js +++ b/chrome/browser/resources/md_bookmarks/util.js
@@ -167,6 +167,15 @@ } /** + * @param {string} name + * @param {number} value + * @param {number} maxValue + */ + function recordEnumHistogram(name, value, maxValue) { + chrome.send('metricsHandler:recordInHistogram', [name, value, maxValue]); + } + + /** * @param {!Object<string, T>} map * @param {!Set<string>} ids * @return {!Object<string, T>} @@ -203,6 +212,7 @@ isShowingSearch: isShowingSearch, normalizeNode: normalizeNode, normalizeNodes: normalizeNodes, + recordEnumHistogram: recordEnumHistogram, removeIdsFromMap: removeIdsFromMap, removeIdsFromSet: removeIdsFromSet, };
diff --git a/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html b/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html index 21352c7..8d8847b 100644 --- a/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html +++ b/chrome/browser/resources/settings/bluetooth_page/bluetooth_subpage.html
@@ -35,7 +35,7 @@ } #onOff[on] { - color: var(--settings-toggle-color); + color: var(--cr-toggle-color); } </style>
diff --git a/chrome/browser/resources/settings/internet_page/internet_subpage.html b/chrome/browser/resources/settings/internet_page/internet_subpage.html index 4316d20..3d0116ab 100644 --- a/chrome/browser/resources/settings/internet_page/internet_subpage.html +++ b/chrome/browser/resources/settings/internet_page/internet_subpage.html
@@ -29,7 +29,7 @@ } #onOff[on] { - color: var(--settings-toggle-color); + color: var(--cr-toggle-color); } .vpn-header {
diff --git a/chrome/browser/resources/settings/on_startup_page/on_startup_page.html b/chrome/browser/resources/settings/on_startup_page/on_startup_page.html index e5cac20..6ee69b6 100644 --- a/chrome/browser/resources/settings/on_startup_page/on_startup_page.html +++ b/chrome/browser/resources/settings/on_startup_page/on_startup_page.html
@@ -20,7 +20,7 @@ aria-label="$i18n{onStartupManage}"></button> </div> </neon-animatable> - <template is="dom-if" route-path="/startupUrls"> + <template is="dom-if" route-path="/startupPages"> <settings-subpage associated-control="[[$$('#manage-startup-urls-subpage-trigger')]]" page-title="$i18n{onStartupManage}">
diff --git a/chrome/browser/resources/settings/on_startup_page/on_startup_page.js b/chrome/browser/resources/settings/on_startup_page/on_startup_page.js index 38cdc3cc..72b34de 100644 --- a/chrome/browser/resources/settings/on_startup_page/on_startup_page.js +++ b/chrome/browser/resources/settings/on_startup_page/on_startup_page.js
@@ -15,6 +15,6 @@ /** @private */ onManageStartupUrls_: function() { - settings.navigateTo(settings.routes.STARTUP_URLS); + settings.navigateTo(settings.routes.STARTUP_PAGES); }, });
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js index e90fb16..34b1932 100644 --- a/chrome/browser/resources/settings/route.js +++ b/chrome/browser/resources/settings/route.js
@@ -236,7 +236,7 @@ if (pageVisibility.onStartup !== false) { r.ON_STARTUP = r.BASIC.createSection('/onStartup', 'onStartup'); - r.STARTUP_URLS = r.ON_STARTUP.createChild('/startupUrls'); + r.STARTUP_PAGES = r.ON_STARTUP.createChild('/startupPages'); } if (pageVisibility.people !== false) {
diff --git a/chrome/browser/resources/settings/settings_page/settings_section.js b/chrome/browser/resources/settings/settings_page/settings_section.js index 9ee94f57..f3b06bd9 100644 --- a/chrome/browser/resources/settings/settings_page/settings_section.js +++ b/chrome/browser/resources/settings/settings_page/settings_section.js
@@ -97,6 +97,14 @@ }, /** + * Calling this method fires the 'settings-section-expanded event'. + */ + setExpanded_: function() { + this.classList.add('expanded'); + this.fire('settings-section-expanded'); + }, + + /** * @return {boolean} True if the section is currently rendered and not * already expanded or transitioning. */ @@ -113,7 +121,7 @@ this.$.card.top = containerTop + 'px'; this.$.card.height = 'calc(100% - ' + containerTop + 'px)'; - this.classList.add('expanded'); + this.setExpanded_(); }, /** @@ -146,12 +154,7 @@ var animation = this.animateCard_('fixed', startTop, endTop, startHeight, endHeight); - animation.finished - .then( - () => { - this.classList.add('expanded'); - }, - function() {}) + animation.finished.then(this.setExpanded_.bind(this), function() {}) .then(() => { // Unset these changes whether the animation finished or canceled. this.classList.remove('expanding');
diff --git a/chrome/browser/resources/settings/settings_shared_css.html b/chrome/browser/resources/settings/settings_shared_css.html index 279c57df..1510aecb 100644 --- a/chrome/browser/resources/settings/settings_shared_css.html +++ b/chrome/browser/resources/settings/settings_shared_css.html
@@ -1,4 +1,5 @@ <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/paper_toggle_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> <link rel="import" href="settings_icons_css.html"> @@ -7,7 +8,7 @@ <!-- Common styles for Material Design settings. --> <dom-module id="settings-shared"> <template> - <style include="settings-icons paper-button-style cr-shared-style"> + <style include="settings-icons paper-button-style paper-toggle-style cr-shared-style"> /* Prevent action-links from being selected to avoid accidental * selection when trying to click it. */ a[is=action-link] {
diff --git a/chrome/browser/resources/settings/settings_vars_css.html b/chrome/browser/resources/settings/settings_vars_css.html index 153aa5e..53fbc30 100644 --- a/chrome/browser/resources/settings/settings_vars_css.html +++ b/chrome/browser/resources/settings/settings_vars_css.html
@@ -55,24 +55,6 @@ --settings-title-bar-color: rgb(255, 255, 255); --settings-title-search-color: rgb(192, 199, 205); - --settings-toggle-bar-size: { - height: 12px; - left: 4px; - width: 28px; - }; - --settings-toggle-button-size: { - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4); - height: 16px; - top: -2px; - width: 16px; - }; - --settings-toggle-ink-size: { - height: 40px; - top: -12px; - left: -12px; - width: 40px; - }; - --settings-toggle-color: var(--google-blue-500); --checkbox-margin-start: 2px; --checkbox-size: 16px; @@ -116,18 +98,6 @@ --paper-radio-button-size: 16px; --paper-radio-group-item-padding: 0; - --paper-toggle-button-checked-bar: var(--settings-toggle-bar-size); - --paper-toggle-button-checked-bar-color: var(--settings-toggle-color); - --paper-toggle-button-checked-button: { - @apply(--settings-toggle-button-size); - transform: translate(18px, 0); - }; - --paper-toggle-button-checked-button-color: var(--settings-toggle-color); - --paper-toggle-button-label-spacing: 0; - --paper-toggle-button-unchecked-bar: var(--settings-toggle-bar-size); - --paper-toggle-button-unchecked-button: var(--settings-toggle-button-size); - --paper-toggle-button-unchecked-ink: var(--settings-toggle-ink-size); - --settings-input-underline: { border-color: var(--paper-grey-300); };
diff --git a/chrome/browser/safe_browsing/certificate_reporting_metrics_provider.cc b/chrome/browser/safe_browsing/certificate_reporting_metrics_provider.cc index 23dfccbc6..a528343 100644 --- a/chrome/browser/safe_browsing/certificate_reporting_metrics_provider.cc +++ b/chrome/browser/safe_browsing/certificate_reporting_metrics_provider.cc
@@ -13,11 +13,12 @@ CertificateReportingMetricsProvider::~CertificateReportingMetricsProvider() {} -void CertificateReportingMetricsProvider::ProvideGeneralMetrics( +void CertificateReportingMetricsProvider::ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* unused) { - // When ProvideGeneralMetrics is called, this class is being asked to provide - // metrics to the metrics service. It doesn't provide any metrics though, - // instead it tells CertificateReportingService to upload any pending reports. + // When ProvideCurrentSessionData is called, this class is being asked to + // provide metrics to the metrics service. It doesn't provide any metrics + // though, instead it tells CertificateReportingService to upload any pending + // reports. ProfileManager* profile_manager = g_browser_process->profile_manager(); if (!profile_manager) return;
diff --git a/chrome/browser/safe_browsing/certificate_reporting_metrics_provider.h b/chrome/browser/safe_browsing/certificate_reporting_metrics_provider.h index f22819e..4b0a5982 100644 --- a/chrome/browser/safe_browsing/certificate_reporting_metrics_provider.h +++ b/chrome/browser/safe_browsing/certificate_reporting_metrics_provider.h
@@ -21,7 +21,7 @@ ~CertificateReportingMetricsProvider() override; // metrics:MetricsProvider: - void ProvideGeneralMetrics( + void ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* unused) override; };
diff --git a/chrome/browser/signin/chrome_signin_status_metrics_provider_delegate.cc b/chrome/browser/signin/chrome_signin_status_metrics_provider_delegate.cc index 76ebab7..dca9427 100644 --- a/chrome/browser/signin/chrome_signin_status_metrics_provider_delegate.cc +++ b/chrome/browser/signin/chrome_signin_status_metrics_provider_delegate.cc
@@ -126,7 +126,7 @@ owner()->UpdateSigninStatus( SigninStatusMetricsProviderBase::MIXED_SIGNIN_STATUS); } else if (status == SigninStatusMetricsProviderBase::UNKNOWN_SIGNIN_STATUS) { - // If when function ProvideGeneralMetrics() is called, Chrome is + // If when function ProvideCurrentSessionData() is called, Chrome is // running in the background with no browser window opened, |signin_status_| // will be reset to |UNKNOWN_SIGNIN_STATUS|. Then this newly added browser // is the only opened browser/profile and its signin status represents
diff --git a/chrome/browser/signin/signin_status_metrics_provider_chromeos.cc b/chrome/browser/signin/signin_status_metrics_provider_chromeos.cc index fc8856e..dce97a1f 100644 --- a/chrome/browser/signin/signin_status_metrics_provider_chromeos.cc +++ b/chrome/browser/signin/signin_status_metrics_provider_chromeos.cc
@@ -31,7 +31,7 @@ SigninStatusMetricsProviderChromeOS::~SigninStatusMetricsProviderChromeOS() { } -void SigninStatusMetricsProviderChromeOS::ProvideGeneralMetrics( +void SigninStatusMetricsProviderChromeOS::ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) { // Compare the current logged-in status with the recorded one and compute // sign-in status that should be reported.
diff --git a/chrome/browser/signin/signin_status_metrics_provider_chromeos.h b/chrome/browser/signin/signin_status_metrics_provider_chromeos.h index e9ff719..7012b14 100644 --- a/chrome/browser/signin/signin_status_metrics_provider_chromeos.h +++ b/chrome/browser/signin/signin_status_metrics_provider_chromeos.h
@@ -22,7 +22,7 @@ ~SigninStatusMetricsProviderChromeOS() override; // SigninStatusMetricsProviderBase: - void ProvideGeneralMetrics( + void ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) override; private:
diff --git a/chrome/browser/translate/translate_ranker_metrics_provider.cc b/chrome/browser/translate/translate_ranker_metrics_provider.cc index cefb656..8f73761 100644 --- a/chrome/browser/translate/translate_ranker_metrics_provider.cc +++ b/chrome/browser/translate/translate_ranker_metrics_provider.cc
@@ -13,7 +13,7 @@ namespace translate { -void TranslateRankerMetricsProvider::ProvideGeneralMetrics( +void TranslateRankerMetricsProvider::ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) { std::vector<Profile*> loaded_profiles = g_browser_process->profile_manager()->GetLoadedProfiles();
diff --git a/chrome/browser/translate/translate_ranker_metrics_provider.h b/chrome/browser/translate/translate_ranker_metrics_provider.h index 65af105..4d38ee907 100644 --- a/chrome/browser/translate/translate_ranker_metrics_provider.h +++ b/chrome/browser/translate/translate_ranker_metrics_provider.h
@@ -17,7 +17,7 @@ ~TranslateRankerMetricsProvider() override {} // From metrics::MetricsProvider... - void ProvideGeneralMetrics( + void ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) override; void OnRecordingEnabled() override; void OnRecordingDisabled() override;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 445df80..6226e58 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1205,6 +1205,8 @@ "webui/chromeos/certificate_manager_dialog_ui.h", "webui/chromeos/proxy_settings_ui.cc", "webui/chromeos/proxy_settings_ui.h", + "webui/chromeos/user_image_source.cc", + "webui/chromeos/user_image_source.h", "webui/help/help_handler.cc", "webui/help/help_handler.h", "webui/help/help_utils_chromeos.cc", @@ -1249,8 +1251,6 @@ "webui/options/chromeos/stats_options_handler.h", "webui/options/chromeos/storage_manager_handler.cc", "webui/options/chromeos/storage_manager_handler.h", - "webui/options/chromeos/user_image_source.cc", - "webui/options/chromeos/user_image_source.h", "webui/options/clear_browser_data_handler.cc", "webui/options/clear_browser_data_handler.h", "webui/options/content_settings_handler.cc",
diff --git a/chrome/browser/ui/android/infobars/translate_compact_infobar.cc b/chrome/browser/ui/android/infobars/translate_compact_infobar.cc index 9ff8fccd..67e9820ff 100644 --- a/chrome/browser/ui/android/infobars/translate_compact_infobar.cc +++ b/chrome/browser/ui/android/infobars/translate_compact_infobar.cc
@@ -13,11 +13,27 @@ #include "base/memory/ptr_util.h" #include "chrome/browser/translate/android/translate_utils.h" #include "components/translate/core/browser/translate_infobar_delegate.h" +#include "components/variations/variations_associated_data.h" #include "jni/TranslateCompactInfoBar_jni.h" using base::android::JavaParamRef; using base::android::ScopedJavaLocalRef; +// Default values for the finch parameters. (used when the corresponding finch +// parameter does not exist.) +const int kDefaultAutoAlwaysThreshold = 5; +const int kDefaultAutoNeverThreshold = 10; +const int kDefaultMaxNumberOfAutoAlways = 2; +const int kDefaultMaxNumberOfAutoNever = 2; + +// Finch parameter names: +const char kTranslateAutoAlwaysThreshold[] = "translate_auto_always_threshold"; +const char kTranslateAutoNeverThreshold[] = "translate_auto_never_threshold"; +const char kTranslateMaxNumberOfAutoAlways[] = + "translate_max_number_of_auto_always"; +const char kTranslateMaxNumberOfAutoNever[] = + "translate_max_number_of_auto_never"; + // TranslateInfoBar ----------------------------------------------------------- TranslateCompactInfoBar::TranslateCompactInfoBar( @@ -144,8 +160,8 @@ bool TranslateCompactInfoBar::ShouldAutoAlwaysTranslate() { translate::TranslateInfoBarDelegate* delegate = GetDelegate(); - return (delegate->GetTranslationAcceptedCount() == kAcceptCountThreshold && - delegate->GetTranslationAutoAlwaysCount() < kMaxNumberOfAutoAlways); + return (delegate->GetTranslationAcceptedCount() == AutoAlwaysThreshold() && + delegate->GetTranslationAutoAlwaysCount() < MaxNumberOfAutoAlways()); } jboolean TranslateCompactInfoBar::ShouldAutoNeverTranslate( @@ -174,8 +190,8 @@ int off_by_one = auto_never_count == 0 ? 1 : 0; bool never_translate = (delegate->GetTranslationDeniedCount() + off_by_one == - kDeniedCountThreshold && - auto_never_count < kMaxNumberOfAutoNever); + AutoNeverThreshold() && + auto_never_count < MaxNumberOfAutoNever()); if (never_translate) { // Auto-never will be triggered. Need to increment the auto-never counter. delegate->IncrementTranslationAutoNeverCount(); @@ -185,6 +201,34 @@ return never_translate; } +int TranslateCompactInfoBar::GetParam(const std::string& paramName, + int default_value) { + std::map<std::string, std::string> params; + if (!variations::GetVariationParams(translate::kTranslateCompactUI.name, + ¶ms)) + return default_value; + int value = 0; + base::StringToInt(params[paramName], &value); + return value <= 0 ? default_value : value; +} + +int TranslateCompactInfoBar::AutoAlwaysThreshold() { + return GetParam(kTranslateAutoAlwaysThreshold, kDefaultAutoAlwaysThreshold); +} + +int TranslateCompactInfoBar::AutoNeverThreshold() { + return GetParam(kTranslateAutoNeverThreshold, kDefaultAutoNeverThreshold); +} + +int TranslateCompactInfoBar::MaxNumberOfAutoAlways() { + return GetParam(kTranslateMaxNumberOfAutoAlways, + kDefaultMaxNumberOfAutoAlways); +} + +int TranslateCompactInfoBar::MaxNumberOfAutoNever() { + return GetParam(kTranslateMaxNumberOfAutoNever, kDefaultMaxNumberOfAutoNever); +} + translate::TranslateInfoBarDelegate* TranslateCompactInfoBar::GetDelegate() { return delegate()->AsTranslateInfoBarDelegate(); }
diff --git a/chrome/browser/ui/android/infobars/translate_compact_infobar.h b/chrome/browser/ui/android/infobars/translate_compact_infobar.h index 0beca80f..a6f90e9 100644 --- a/chrome/browser/ui/android/infobars/translate_compact_infobar.h +++ b/chrome/browser/ui/android/infobars/translate_compact_infobar.h
@@ -63,6 +63,26 @@ void SetJavaInfoBar( const base::android::JavaRef<jobject>& java_info_bar) override; + // Get the value of a specified finch parameter in TranslateCompactUI. If the + // finch parameter does not exist, default_value will be returned. + int GetParam(const std::string& paramName, int default_value); + // Get the value of the finch parameter: translate_auto_always_threshold. + // If the number of times a user consecutively translates is equal to this + // number, infobar will automatically trigger "Always Translate". + int AutoAlwaysThreshold(); + // Get the value of the finch parameter: translate_auto_never_threshold. + // If the number of times a user consecutively denies the translate infobar is + // equal to this number, infobar will automatically trigger "Never Translate". + int AutoNeverThreshold(); + // Get the value of the finch parameter: translate_max_number_of_auto_always. + // This is the maximum number of times to trigger "Always Translate" + // automatically. + int MaxNumberOfAutoAlways(); + // Get the value of the finch parameter: translate_max_number_of_auto_never. + // This is the maximum number of times to trigger "Never Translate" + // automatically. + int MaxNumberOfAutoNever(); + translate::TranslateInfoBarDelegate* GetDelegate(); // Bits for trace user's affirmative actions. @@ -79,17 +99,6 @@ FLAG_EXPAND_MENU = 1 << 5, }; - // If number of consecutive translations is equal to this number, infobar will - // automatically trigger "Always Translate". - const int kAcceptCountThreshold = 5; - // If number of consecutive denied is equal to this number, infobar will - // automatically trigger "Never Translate Language". - const int kDeniedCountThreshold = 7; - // Maximum number of times to trigger "Always Translate" automatically. - const int kMaxNumberOfAutoAlways = 2; - // Maximum number of times to trigger "Never Translate" automatically. - const int kMaxNumberOfAutoNever = 2; - DISALLOW_COPY_AND_ASSIGN(TranslateCompactInfoBar); };
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc index 795460f..edf38d15 100644 --- a/chrome/browser/ui/tabs/tab_strip_model.cc +++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -430,17 +430,15 @@ int to_position, bool select_after_move) { DCHECK(ContainsIndex(index)); + + // Ensure pinned and non-pinned tabs do not mix. + const int first_non_pinned_tab = IndexOfFirstNonPinnedTab(); + to_position = IsTabPinned(index) + ? std::min(first_non_pinned_tab - 1, to_position) + : std::max(first_non_pinned_tab, to_position); if (index == to_position) return; - int first_non_pinned_tab = IndexOfFirstNonPinnedTab(); - if ((index < first_non_pinned_tab && to_position >= first_non_pinned_tab) || - (to_position < first_non_pinned_tab && index >= first_non_pinned_tab)) { - // This would result in pinned tabs mixed with non-pinned tabs. We don't - // allow that. - return; - } - MoveWebContentsAtImpl(index, to_position, select_after_move); }
diff --git a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc index af629584..84ddee06 100644 --- a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc +++ b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
@@ -2168,6 +2168,33 @@ EXPECT_TRUE(tab_strip_model_observer.empty()); } +// Ensure pinned tabs are not mixed with non-pinned tabs when using +// MoveWebContentsAt. +TEST_F(TabStripModelTest, MoveWebContentsAtWithPinned) { + TabStripDummyDelegate delegate; + TabStripModel strip(&delegate, profile()); + ASSERT_NO_FATAL_FAILURE(PrepareTabstripForSelectionTest(&strip, 6, 3, "0")); + EXPECT_EQ("0p 1p 2p 3 4 5", GetTabStripStateString(strip)); + + // Move middle tabs into the wrong area. + strip.MoveWebContentsAt(1, 5, true); + EXPECT_EQ("0p 2p 1p 3 4 5", GetTabStripStateString(strip)); + strip.MoveWebContentsAt(4, 1, true); + EXPECT_EQ("0p 2p 1p 4 3 5", GetTabStripStateString(strip)); + + // Test moving edge cases into the wrong area. + strip.MoveWebContentsAt(5, 0, true); + EXPECT_EQ("0p 2p 1p 5 4 3", GetTabStripStateString(strip)); + strip.MoveWebContentsAt(0, 5, true); + EXPECT_EQ("2p 1p 0p 5 4 3", GetTabStripStateString(strip)); + + // Test moving edge cases in the correct area. + strip.MoveWebContentsAt(3, 5, true); + EXPECT_EQ("2p 1p 0p 4 3 5", GetTabStripStateString(strip)); + strip.MoveWebContentsAt(2, 0, true); + EXPECT_EQ("0p 2p 1p 4 3 5", GetTabStripStateString(strip)); +} + TEST_F(TabStripModelTest, MoveSelectedTabsTo) { struct TestData { // Number of tabs the tab strip should have.
diff --git a/chrome/browser/ui/view_ids.h b/chrome/browser/ui/view_ids.h index aa0204a..9c55bbc2 100644 --- a/chrome/browser/ui/view_ids.h +++ b/chrome/browser/ui/view_ids.h
@@ -43,6 +43,8 @@ VIEW_ID_TAB_STRIP, + VIEW_ID_NEW_TAB_PROMO, + // Toolbar & toolbar elements. VIEW_ID_TOOLBAR = 1000, VIEW_ID_BACK_BUTTON,
diff --git a/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc index d97c555c..d3a753b8 100644 --- a/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc +++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc
@@ -7,6 +7,7 @@ #include <stddef.h> #include "base/strings/string16.h" +#include "build/build_config.h" #include "chrome/browser/permissions/permission_request.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/ui/browser.h" @@ -32,6 +33,10 @@ #include "ui/views/controls/label.h" #include "ui/views/layout/box_layout.h" +#if defined(OS_MACOSX) +#include "chrome/common/chrome_features.h" +#endif + namespace { // (Square) pixel size of icon. @@ -101,6 +106,14 @@ set_close_on_deactivate(false); set_arrow(kPermissionAnchorArrow); +#if defined(OS_MACOSX) + // On Mac, the browser UI flips depending on a runtime feature. TODO(tapted): + // Change the default in views::PlatformStyle when features::kMacRTL launches, + // and remove the following. + if (base::FeatureList::IsEnabled(features::kMacRTL)) + set_mirror_arrow_in_rtl(true); +#endif + ChromeLayoutProvider* provider = ChromeLayoutProvider::Get(); SetLayoutManager(new views::BoxLayout( views::BoxLayout::kVertical, gfx::Insets(),
diff --git a/chrome/browser/ui/views/tabs/new_tab_promo.cc b/chrome/browser/ui/views/tabs/new_tab_promo.cc index b3b8f6f..df2b9f3 100644 --- a/chrome/browser/ui/views/tabs/new_tab_promo.cc +++ b/chrome/browser/ui/views/tabs/new_tab_promo.cc
@@ -4,10 +4,12 @@ #include "chrome/browser/ui/views/tabs/new_tab_promo.h" -#include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/ui/view_ids.h" +#include "chrome/browser/ui/views/tabs/new_tab_button.h" #include "chrome/grit/generated_resources.h" +#include "components/variations/variations_associated_data.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/rect.h" @@ -28,7 +30,6 @@ // hovering it. Constant subject to change after interactive tests. constexpr base::TimeDelta kBubbleCloseDelayShort = base::TimeDelta::FromSecondsD(2.5); - } // namespace // static @@ -37,8 +38,31 @@ } NewTabPromo::NewTabPromo(const gfx::Rect& anchor_rect) { + set_id(VIEW_ID_NEW_TAB_PROMO); SetAnchorRect(anchor_rect); set_arrow(views::BubbleBorder::LEFT_TOP); + auto box_layout = base::MakeUnique<views::BoxLayout>( + views::BoxLayout::kVertical, gfx::Insets(), 0); + box_layout->set_main_axis_alignment( + views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER); + box_layout->set_cross_axis_alignment( + views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); + SetLayoutManager(box_layout.release()); + + // Display one of several different strings in the promo, depending on which + // variant Finch instructs us to use. + static constexpr int kTextIds[] = {IDS_NEWTAB_PROMO_0, IDS_NEWTAB_PROMO_1, + IDS_NEWTAB_PROMO_2}; + const std::string& str = variations::GetVariationParamValue( + "NewTabInProductHelp", "x_promo_string"); + size_t text_specifier; + if (!base::StringToSizeT(str, &text_specifier) || + text_specifier >= arraysize(kTextIds)) { + text_specifier = 0; + } + AddChildView( + new views::Label(l10n_util::GetStringUTF16(kTextIds[text_specifier]))); + views::Widget* new_tab_promo_widget = views::BubbleDialogDelegateView::CreateBubble(this); UseCompactMargins(); @@ -65,17 +89,6 @@ StartAutoCloseTimer(kBubbleCloseDelayShort); } -void NewTabPromo::Init() { - auto box_layout = base::MakeUnique<views::BoxLayout>( - views::BoxLayout::kVertical, gfx::Insets(), 0); - box_layout->set_main_axis_alignment( - views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER); - box_layout->set_cross_axis_alignment( - views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); - SetLayoutManager(box_layout.release()); - AddChildView(new views::Label(l10n_util::GetStringUTF16(IDS_NEWTAB_PROMO))); -} - void NewTabPromo::CloseBubble() { GetWidget()->Close(); }
diff --git a/chrome/browser/ui/views/tabs/new_tab_promo.h b/chrome/browser/ui/views/tabs/new_tab_promo.h index d915e4e..642fc164 100644 --- a/chrome/browser/ui/views/tabs/new_tab_promo.h +++ b/chrome/browser/ui/views/tabs/new_tab_promo.h
@@ -36,7 +36,6 @@ bool OnMousePressed(const ui::MouseEvent& event) override; void OnMouseEntered(const ui::MouseEvent& event) override; void OnMouseExited(const ui::MouseEvent& event) override; - void Init() override; // Closes the New Tab Promo. void CloseBubble();
diff --git a/chrome/browser/ui/views/tabs/new_tab_promo_dialog_browsertest.cc b/chrome/browser/ui/views/tabs/new_tab_promo_dialog_browsertest.cc index 210b86e2..b32a230 100644 --- a/chrome/browser/ui/views/tabs/new_tab_promo_dialog_browsertest.cc +++ b/chrome/browser/ui/views/tabs/new_tab_promo_dialog_browsertest.cc
@@ -23,8 +23,8 @@ }; // Test that calls ShowDialog("default"). Interactive when run via -// browser_tests --gtest_filter=BrowserDialogTest.Invoke --interactive -// --dialog=NewTabPromoDialogTest.InvokeDialog_NewTabPromo +// ../browser_tests --gtest_filter=BrowserDialogTest.Invoke +// --interactive --dialog=NewTabPromoDialogTest.InvokeDialog_NewTabPromo IN_PROC_BROWSER_TEST_F(NewTabPromoDialogTest, InvokeDialog_NewTabPromo) { RunDialog(); }
diff --git a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc index b6cc422..555e6f5 100644 --- a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.cc
@@ -44,6 +44,7 @@ // Enrollment step names. const char kEnrollmentStepSignin[] = "signin"; const char kEnrollmentStepAdJoin[] = "ad-join"; +const char kEnrollmentStepPickLicense[] = "license"; const char kEnrollmentStepSuccess[] = "success"; const char kEnrollmentStepWorking[] = "working"; @@ -149,6 +150,8 @@ &EnrollmentScreenHandler::HandleDeviceAttributesProvided); AddCallback("oauthEnrollOnLearnMore", &EnrollmentScreenHandler::HandleOnLearnMore); + AddCallback("onLicenseTypeSelected", + &EnrollmentScreenHandler::HandleLicenseTypeSelected); } // EnrollmentScreenHandler @@ -177,6 +180,12 @@ ShowStep(kEnrollmentStepSignin); } +void EnrollmentScreenHandler::ShowLicenseTypeSelectionScreen( + const base::DictionaryValue& license_types) { + CallJS("setAvailableLicenseTypes", license_types); + ShowStep(kEnrollmentStepPickLicense); +} + void EnrollmentScreenHandler::ShowAdJoin() { observe_network_failure_ = false; if (!authpolicy_login_helper_) @@ -358,6 +367,9 @@ ShowError(IDS_ENTERPRISE_ENROLLMENT_ERROR_SAVE_DEVICE_CONFIGURATION, false); return; + case policy::EnrollmentStatus::LICENSE_REQUEST_FAILED: + ShowError(IDS_ENTERPRISE_ENROLLMENT_ERROR_LICENSE_REQUEST, false); + return; } NOTREACHED(); } @@ -404,6 +416,18 @@ builder->Add("adLoginInvalidPassword", IDS_AD_INVALID_PASSWORD); builder->Add("adJoinErrorMachineNameInvalid", IDS_AD_MACHINENAME_INVALID); builder->Add("adJoinErrorMachineNameTooLong", IDS_AD_MACHINENAME_TOO_LONG); + builder->Add("licenseSelectionCardTitle", + IDS_ENTERPRISE_ENROLLMENT_LICENSE_SELECTION); + builder->Add("licenseSelectionCardExplanation", + IDS_ENTERPRISE_ENROLLMENT_LICENSE_SELECTION_EXPLANATION); + builder->Add("perpetualLicenseTypeTitle", + IDS_ENTERPRISE_ENROLLMENT_PERPETUAL_LICENSE_TYPE); + builder->Add("annualLicenseTypeTitle", + IDS_ENTERPRISE_ENROLLMENT_ANNUAL_LICENSE_TYPE); + builder->Add("kioskLicenseTypeTitle", + IDS_ENTERPRISE_ENROLLMENT_KIOSK_LICENSE_TYPE); + builder->Add("licenseCountTemplate", + IDS_ENTERPRISE_ENROLLMENT_LICENSES_REMAINING_TEMPLATE); } bool EnrollmentScreenHandler::IsOnEnrollmentScreen() const { @@ -619,6 +643,11 @@ help_app_->ShowHelpTopic(HelpAppLauncher::HELP_DEVICE_ATTRIBUTES); } +void EnrollmentScreenHandler::HandleLicenseTypeSelected( + const std::string& licenseType) { + controller_->OnLicenseTypeSelected(licenseType); +} + void EnrollmentScreenHandler::ShowStep(const char* step) { CallJS("showStep", std::string(step)); }
diff --git a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h index d8a0dee8..a13f4ae 100644 --- a/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/enrollment_screen_handler.h
@@ -55,6 +55,8 @@ void Show() override; void Hide() override; void ShowSigninScreen() override; + void ShowLicenseTypeSelectionScreen( + const base::DictionaryValue& license_types) override; void ShowAdJoin() override; void ShowAttributePromptScreen(const std::string& asset_id, const std::string& location) override; @@ -88,6 +90,7 @@ void HandleDeviceAttributesProvided(const std::string& asset_id, const std::string& location); void HandleOnLearnMore(); + void HandleLicenseTypeSelected(const std::string& licenseType); void UpdateStateInternal(NetworkError::ErrorReason reason, bool force_update); void SetupAndShowOfflineMessage(NetworkStateInformer::State state,
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc index 94f7115..b811e7a9 100644 --- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc +++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -63,7 +63,7 @@ #include "chrome/browser/ui/webui/chromeos/login/wait_for_container_ready_screen_handler.h" #include "chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.h" #include "chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h" -#include "chrome/browser/ui/webui/options/chromeos/user_image_source.h" +#include "chrome/browser/ui/webui/chromeos/user_image_source.h" #include "chrome/browser/ui/webui/test_files_request_filter.h" #include "chrome/browser/ui/webui/theme_source.h" #include "chrome/common/chrome_constants.h" @@ -361,8 +361,7 @@ network_element::AddLocalizedStrings(html_source); // Set up the chrome://userimage/ source. - options::UserImageSource* user_image_source = - new options::UserImageSource(); + UserImageSource* user_image_source = new UserImageSource(); content::URLDataSource::Add(profile, user_image_source); // TabHelper is required for OOBE webui to make webview working on it.
diff --git a/chrome/browser/ui/webui/options/chromeos/user_image_source.cc b/chrome/browser/ui/webui/chromeos/user_image_source.cc similarity index 96% rename from chrome/browser/ui/webui/options/chromeos/user_image_source.cc rename to chrome/browser/ui/webui/chromeos/user_image_source.cc index 400c1b5..ab566eef 100644 --- a/chrome/browser/ui/webui/options/chromeos/user_image_source.cc +++ b/chrome/browser/ui/webui/chromeos/user_image_source.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ui/webui/options/chromeos/user_image_source.h" +#include "chrome/browser/ui/webui/chromeos/user_image_source.h" #include "base/memory/ref_counted_memory.h" #include "base/message_loop/message_loop.h" @@ -75,7 +75,6 @@ } // namespace namespace chromeos { -namespace options { // Static. scoped_refptr<base::RefCountedMemory> UserImageSource::GetUserImage( @@ -83,8 +82,7 @@ return GetUserImageInternal(account_id); } -UserImageSource::UserImageSource() { -} +UserImageSource::UserImageSource() {} UserImageSource::~UserImageSource() {} @@ -109,5 +107,4 @@ return "image/png"; } -} // namespace options } // namespace chromeos
diff --git a/chrome/browser/ui/webui/options/chromeos/user_image_source.h b/chrome/browser/ui/webui/chromeos/user_image_source.h similarity index 84% rename from chrome/browser/ui/webui/options/chromeos/user_image_source.h rename to chrome/browser/ui/webui/chromeos/user_image_source.h index 6724abe..aabfe10 100644 --- a/chrome/browser/ui/webui/options/chromeos/user_image_source.h +++ b/chrome/browser/ui/webui/chromeos/user_image_source.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_USER_IMAGE_SOURCE_H_ -#define CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_USER_IMAGE_SOURCE_H_ +#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_USER_IMAGE_SOURCE_H_ +#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_USER_IMAGE_SOURCE_H_ #include <string> #include <vector> @@ -21,7 +21,6 @@ } namespace chromeos { -namespace options { // UserImageSource is the data source that serves user images for users that // have it. @@ -49,7 +48,6 @@ DISALLOW_COPY_AND_ASSIGN(UserImageSource); }; -} // namespace options } // namespace chromeos -#endif // CHROME_BROWSER_UI_WEBUI_OPTIONS_CHROMEOS_USER_IMAGE_SOURCE_H_ +#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_USER_IMAGE_SOURCE_H_
diff --git a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc index d688f7a7..d15ab16 100644 --- a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc +++ b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
@@ -12,6 +12,7 @@ #include "base/strings/string16.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/md_bookmarks/bookmarks_message_handler.h" +#include "chrome/browser/ui/webui/metrics_handler.h" #include "chrome/browser/ui/webui/plural_string_handler.h" #include "chrome/common/chrome_features.h" #include "chrome/common/url_constants.h" @@ -212,6 +213,7 @@ web_ui->AddMessageHandler(std::move(plural_string_handler)); web_ui->AddMessageHandler(base::MakeUnique<BookmarksMessageHandler>()); + web_ui->AddMessageHandler(base::MakeUnique<MetricsHandler>()); } // static
diff --git a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc index 3e370fc2..aef5d2b 100644 --- a/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc +++ b/chrome/browser/ui/webui/offline/offline_internals_ui_message_handler.cc
@@ -18,7 +18,7 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/values.h" -#include "chrome/browser/android/offline_pages/prefetch/prefetch_background_task.h" +#include "chrome/browser/offline_pages/android/prefetch_background_task_android.h" #include "chrome/browser/offline_pages/offline_page_model_factory.h" #include "chrome/browser/offline_pages/prefetch/prefetch_service_factory.h" #include "chrome/browser/offline_pages/request_coordinator_factory.h" @@ -295,8 +295,10 @@ const base::Value* callback_id; CHECK(args->Get(0, &callback_id)); - offline_pages::PrefetchBackgroundTask::Schedule(0); - + if (prefetch_service_) { + prefetch_service_->GetPrefetchBackgroundTaskHandler() + ->EnsureTaskScheduled(); + } ResolveJavascriptCallback(*callback_id, base::Value("Scheduled.")); } @@ -306,7 +308,10 @@ const base::Value* callback_id; CHECK(args->Get(0, &callback_id)); - offline_pages::PrefetchBackgroundTask::Cancel(); + if (prefetch_service_) { + prefetch_service_->GetPrefetchBackgroundTaskHandler() + ->CancelBackgroundTask(); + } ResolveJavascriptCallback(*callback_id, base::Value("Cancelled.")); }
diff --git a/chrome/browser/ui/webui/options/options_ui.cc b/chrome/browser/ui/webui/options/options_ui.cc index 5695a69..911a0ad 100644 --- a/chrome/browser/ui/webui/options/options_ui.cc +++ b/chrome/browser/ui/webui/options/options_ui.cc
@@ -83,6 +83,7 @@ #include "chrome/browser/browser_process_platform_part.h" #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #include "chrome/browser/chromeos/system/pointer_device_observer.h" +#include "chrome/browser/ui/webui/chromeos/user_image_source.h" #include "chrome/browser/ui/webui/options/chromeos/accounts_options_handler.h" #include "chrome/browser/ui/webui/options/chromeos/bluetooth_options_handler.h" #include "chrome/browser/ui/webui/options/chromeos/change_picture_options_handler.h" @@ -99,7 +100,6 @@ #include "chrome/browser/ui/webui/options/chromeos/proxy_handler.h" #include "chrome/browser/ui/webui/options/chromeos/stats_options_handler.h" #include "chrome/browser/ui/webui/options/chromeos/storage_manager_handler.h" -#include "chrome/browser/ui/webui/options/chromeos/user_image_source.h" #endif #if defined(USE_NSS_CERTS) @@ -538,8 +538,8 @@ #if defined(OS_CHROMEOS) // Set up the chrome://userimage/ source. - chromeos::options::UserImageSource* user_image_source = - new chromeos::options::UserImageSource(); + chromeos::UserImageSource* user_image_source = + new chromeos::UserImageSource(); content::URLDataSource::Add(profile, user_image_source); pointer_device_observer_.reset(
diff --git a/chrome/browser/ui/webui/settings/profile_info_handler.cc b/chrome/browser/ui/webui/settings/profile_info_handler.cc index 136bd07..304be42 100644 --- a/chrome/browser/ui/webui/settings/profile_info_handler.cc +++ b/chrome/browser/ui/webui/settings/profile_info_handler.cc
@@ -17,7 +17,7 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" -#include "chrome/browser/ui/webui/options/chromeos/user_image_source.h" +#include "chrome/browser/ui/webui/chromeos/user_image_source.h" #include "components/signin/core/account_id/account_id.h" #include "components/user_manager/user_manager.h" #include "content/public/browser/notification_service.h" @@ -48,8 +48,7 @@ callback_weak_ptr_factory_(this) { #if defined(OS_CHROMEOS) // Set up the chrome://userimage/ source. - content::URLDataSource::Add(profile, - new chromeos::options::UserImageSource()); + content::URLDataSource::Add(profile, new chromeos::UserImageSource()); #endif } @@ -186,7 +185,7 @@ // Get image as data URL instead of using chrome://userimage source to avoid // issues with caching. scoped_refptr<base::RefCountedMemory> image = - chromeos::options::UserImageSource::GetUserImage(user->GetAccountId()); + chromeos::UserImageSource::GetUserImage(user->GetAccountId()); icon_url = webui::GetPngDataUrl(image->front(), image->size()); #else // !defined(OS_CHROMEOS) ProfileAttributesEntry* entry;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 2d00f866..47ea381 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -3544,6 +3544,7 @@ "../browser/offline_pages/offline_page_tab_helper_unittest.cc", "../browser/offline_pages/offline_page_utils_unittest.cc", "../browser/offline_pages/prefetch/offline_metrics_collector_impl_unittest.cc", + "../browser/offline_pages/prefetch/prefetch_background_task_handler_impl_unittest.cc", "../browser/offline_pages/prefetch/prefetch_instance_id_proxy_unittest.cc", "../browser/offline_pages/prerender_adapter_unittest.cc", "../browser/offline_pages/prerendering_loader_unittest.cc",
diff --git a/chrome/test/data/favicon/page_with_location_override_to_other_page.html b/chrome/test/data/favicon/page_with_location_override_to_other_page.html new file mode 100644 index 0000000..4292eeb2 --- /dev/null +++ b/chrome/test/data/favicon/page_with_location_override_to_other_page.html
@@ -0,0 +1,11 @@ +<html> +<head> +<script> +window.onload = function() { + setTimeout(function() { + this.document.location.replace("/favicon/page_with_favicon.html"); + }, 0); +} +</script> +</head> +</html>
diff --git a/chrome/test/data/favicon/page_with_meta_refresh_tag.html b/chrome/test/data/favicon/page_with_meta_refresh_tag.html new file mode 100644 index 0000000..5a1f6df --- /dev/null +++ b/chrome/test/data/favicon/page_with_meta_refresh_tag.html
@@ -0,0 +1,5 @@ +<html> +<head> +<meta http-equiv="refresh" content="0; url=/favicon/page_with_favicon.html"> +</head> +</html>
diff --git a/chrome/test/data/favicon/page_with_meta_refresh_tag_to_server_redirect.html b/chrome/test/data/favicon/page_with_meta_refresh_tag_to_server_redirect.html new file mode 100644 index 0000000..de8f6172 --- /dev/null +++ b/chrome/test/data/favicon/page_with_meta_refresh_tag_to_server_redirect.html
@@ -0,0 +1,5 @@ +<html> +<head> +<meta http-equiv="refresh" content="0; url=/server-redirect?/favicon/page_with_favicon.html"> +</head> +</html>
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn index 9661fd7..54f24b5 100644 --- a/chrome/test/data/webui/BUILD.gn +++ b/chrome/test/data/webui/BUILD.gn
@@ -44,6 +44,7 @@ "../../../browser/ui/webui/sync_internals_browsertest.js", "../../../browser/ui/webui/sync_setup_browsertest.js", "../chromeos/oobe_webui_browsertest.js", + "//third_party/axe-core/axe.js", "about_invalidations_browsertest.js", "accessibility_audit_browsertest.js", "assertions.js", @@ -77,6 +78,7 @@ "ntp4.js", "polymer_browser_test_base.js", "sandboxstatus_browsertest.js", + "settings/accessibility_browsertest.js", "settings/advanced_page_browsertest.js", "settings/animation_browsertest.js", "settings/basic_page_browsertest.js",
diff --git a/chrome/test/data/webui/md_bookmarks/command_manager_test.js b/chrome/test/data/webui/md_bookmarks/command_manager_test.js index fa168fa..fc9294f 100644 --- a/chrome/test/data/webui/md_bookmarks/command_manager_test.js +++ b/chrome/test/data/webui/md_bookmarks/command_manager_test.js
@@ -94,7 +94,7 @@ store.notifyObservers(); MockInteractions.pressAndReleaseKeyOn(document.body, '', [], key); - commandManager.assertLastCommand('edit', ['13']); + commandManager.assertLastCommand(Command.EDIT, ['13']); // Doesn't trigger when multiple items are selected. store.data.selection.items = new Set(['11', '13']); @@ -116,7 +116,7 @@ store.notifyObservers(); MockInteractions.pressAndReleaseKeyOn(document.body, 46, '', 'Delete'); - commandManager.assertLastCommand('delete', ['12', '13']); + commandManager.assertLastCommand(Command.DELETE, ['12', '13']); }); test('copy command triggers', function() { @@ -126,7 +126,7 @@ store.notifyObservers(); MockInteractions.pressAndReleaseKeyOn(document.body, '', modifier, 'c'); - commandManager.assertLastCommand('copy', ['11', '13']); + commandManager.assertLastCommand(Command.COPY, ['11', '13']); }); test('cut/paste commands trigger', function() { @@ -158,11 +158,11 @@ MockInteractions.pressAndReleaseKeyOn( document.body, '', undoModifier, undoKey); - commandManager.assertLastCommand('undo'); + commandManager.assertLastCommand(Command.UNDO); MockInteractions.pressAndReleaseKeyOn( document.body, '', redoModifier, redoKey); - commandManager.assertLastCommand('redo'); + commandManager.assertLastCommand(Command.REDO); }); test('Show In Folder is only available during search', function() { @@ -172,8 +172,8 @@ commandManager.openCommandMenuAtPosition(0, 0, MenuSource.LIST); Polymer.dom.flush(); - var showInFolderItem = - commandManager.root.querySelector('[command=show-in-folder]'); + var showInFolderItem = commandManager.root.querySelector( + `[command='${Command.SHOW_IN_FOLDER}']`); // Show in folder hidden when search is inactive. assertTrue(showInFolderItem.hidden);
diff --git a/chrome/test/data/webui/settings/accessibility_browsertest.js b/chrome/test/data/webui/settings/accessibility_browsertest.js new file mode 100644 index 0000000..d5999e6 --- /dev/null +++ b/chrome/test/data/webui/settings/accessibility_browsertest.js
@@ -0,0 +1,91 @@ +// 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. + +/** @fileoverview Runs the Polymer Accessibility Settings tests. */ + +/** @const {string} Path to root from chrome/test/data/webui/settings/. */ +var ROOT_PATH = '../../../../../'; + +// Polymer BrowserTest fixture and aXe-core accessibility audit. +GEN_INCLUDE([ + ROOT_PATH + 'chrome/test/data/webui/polymer_browser_test_base.js', + ROOT_PATH + 'third_party/axe-core/axe.js', +]); + +/** + * @typedef {{ + * rules: { + * 'color_contrast': ({ enabled: boolean} | undefined), + * 'aria_valid_attr': ({ enabled: boolean} | undefined), + * } + * }} + * @see https://github.com/dequelabs/axe-core/blob/develop/doc/API.md#options-parameter + */ +function AccessibilityAuditConfig() {} + +/** + * Test fixture for Accessibility of Chrome Settings. + * @constructor + * @extends {PolymerTest} + */ +function SettingsAccessibilityTest() {} + +/** + * Run aXe-core accessibility audit, print console-friendly representation + * of violations to console, and fail the test. + * @param {AccessibilityAuditConfig} options Dictionary disabling specific + * audit rules. + * @return {Promise} A promise that will be resolved with the accessibility + * audit is complete. + */ +SettingsAccessibilityTest.runAudit = function(options) { + // Ignore iron-iconset-svg elements that have duplicate ids and result in + // false postives from the audit. + var context = { + exclude: ['iron-iconset-svg'] + }; + options = options || {}; + + return new Promise(function(resolve, reject) { + axe.run(context, options, function(err, results) { + if (err) reject(err); + + var violationCount = results.violations.length; + if (violationCount) { + // Pretty print out the violations detected by the audit. + console.log(JSON.stringify(results.violations, null, 4)); + reject('Found ' + violationCount + ' accessibility violations.'); + } else { + resolve(); + } + }); + }); +}; + +SettingsAccessibilityTest.prototype = { + __proto__: PolymerTest.prototype, + + /** @override */ + browsePreload: 'chrome://md-settings/', + + // Include files that define the mocha tests. + extraLibraries: PolymerTest.getLibraries(ROOT_PATH).concat([ + 'ensure_lazy_loaded.js', + 'passwords_and_autofill_fake_data.js', + 'passwords_a11y_test.js' + ]), + + // TODO(hcarmona): Remove once ADT is not longer in the testing infrastructure + runAccessibilityChecks: false, + + setUp: function() { + PolymerTest.prototype.setUp.call(this); + settings.ensureLazyLoaded(); + }, +}; + +TEST_F('SettingsAccessibilityTest', 'All', function() { + mocha.run(); +}); +
diff --git a/chrome/test/data/webui/settings/passwords_a11y_test.js b/chrome/test/data/webui/settings/passwords_a11y_test.js new file mode 100644 index 0000000..dc9ddf0f --- /dev/null +++ b/chrome/test/data/webui/settings/passwords_a11y_test.js
@@ -0,0 +1,76 @@ +// 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. + +/** @fileoverview Suite of accessibility tests for the passwords page. */ + +suite('SettingsPasswordsAccessibility', function() { + var passwordsSection = null; + var passwordManager = null; + + /** @type {AccessibilityAuditConfig} **/ + var auditOptions = { + 'rules': { + // Enable 'color-contrast' when failure is resolved. + // http://crbug.com/748608 + 'color-contrast': { enabled: false }, + // Enable when aXe-core supports exclusion of elements located in the + // shadow DOM and the advanced toggle button can be excluded. + // http://crbug.com/748632 + 'aria-valid-attr': { enabled: false } + } + }; + + setup(function() { + return new Promise(function(resolve) { + // Reset to a blank page. + PolymerTest.clearBody(); + + // Set the URL to be that of specific route to load upon injecting + // settings-ui. Simply calling settings.navigateTo(route) prevents + // use of mock APIs for fake data. + window.history.pushState( + 'object or string', 'Test', settings.routes.MANAGE_PASSWORDS.path); + + PasswordManagerImpl.instance_ = new TestPasswordManager(); + passwordManager = PasswordManagerImpl.instance_; + + var settingsUi = document.createElement('settings-ui'); + + // The settings section will expand to load the MANAGE_PASSWORDS route + // (based on the URL set above) once the settings-ui element is attached + settingsUi.addEventListener('settings-section-expanded', function () { + // Passwords section should be loaded before setup is complete. + passwordsSection = document.querySelector('* /deep/ passwords-section'); + assertTrue(!!passwordsSection); + + assertEquals(passwordManager, passwordsSection.passwordManager_); + + resolve(); + }); + + document.body.appendChild(settingsUi); + }); + }); + + test('Accessible with 0 passwords', function() { + assertEquals(0, passwordsSection.savedPasswords.length); + return SettingsAccessibilityTest.runAudit(auditOptions); + }); + + test('Accessible with 100 passwords', function() { + var fakePasswords = []; + for (var i = 0; i < 100; i++) { + fakePasswords.push(FakeDataMaker.passwordEntry()); + } + + // Set list of passwords. + passwordManager.lastCallback.addSavedPasswordListChangedListener( + fakePasswords); + Polymer.dom.flush(); + + assertEquals(100, passwordsSection.savedPasswords.length); + + return SettingsAccessibilityTest.runAudit(auditOptions); + }); +}); \ No newline at end of file
diff --git a/chrome/test/data/webui/settings/reset_page_test.js b/chrome/test/data/webui/settings/reset_page_test.js index 2c390135..66c7ba1 100644 --- a/chrome/test/data/webui/settings/reset_page_test.js +++ b/chrome/test/data/webui/settings/reset_page_test.js
@@ -42,7 +42,7 @@ teardown(function() { resetPage.remove(); }); /** - * @param {function(SettingsResetProfileDialogElemeent)} + * @param {function(SettingsResetProfileDialogElement)} * closeDialogFn A function to call for closing the dialog. * @return {!Promise} */ @@ -153,7 +153,7 @@ if (cr.isChromeOS) { /** - * @param {function(SettingsPowerwashDialogElemeent):!Element} + * @param {function(SettingsPowerwashDialogElement):!Element} * closeButtonFn A function that returns the button to be used for * closing the dialog. * @return {!Promise}
diff --git a/chromeos/components/tether/ble_advertiser.cc b/chromeos/components/tether/ble_advertiser.cc index b182441..76292a42 100644 --- a/chromeos/components/tether/ble_advertiser.cc +++ b/chromeos/components/tether/ble_advertiser.cc
@@ -26,9 +26,9 @@ // BleAdvertiser::IndividualAdvertisement::OnAdvertisementRegistered(). void OnAdvertisementUnregisteredAfterOwnerDestruction( const std::string& associated_device_id) { - PA_LOG(ERROR) << "Unregistered advertisement for device ID: " - << cryptauth::RemoteDevice::TruncateDeviceIdForLogs( - associated_device_id); + PA_LOG(INFO) << "Unregistered advertisement for device ID: " + << cryptauth::RemoteDevice::TruncateDeviceIdForLogs( + associated_device_id); } // Handles the failed unregistration of a BluetoothAdvertisement in the case
diff --git a/chromeos/components/tether/master_host_scan_cache.cc b/chromeos/components/tether/master_host_scan_cache.cc index 7ba9876..af44c89 100644 --- a/chromeos/components/tether/master_host_scan_cache.cc +++ b/chromeos/components/tether/master_host_scan_cache.cc
@@ -70,10 +70,10 @@ if (active_host_->GetTetherNetworkGuid() == tether_network_guid) { DCHECK(ExistsInCache(tether_network_guid)); - PA_LOG(ERROR) << "RemoveHostScanResult() called for Tether network with " - << "GUID " << tether_network_guid << ", but the " - << "corresponding device is the active host. Not removing " - << "this scan result from the cache."; + PA_LOG(INFO) << "RemoveHostScanResult() called for Tether network with " + << "GUID " << tether_network_guid << ", but the " + << "corresponding device is the active host. Not removing " + << "this scan result from the cache."; return false; }
diff --git a/components/cast_certificate/cast_cert_validator.cc b/components/cast_certificate/cast_cert_validator.cc index 0e41bc2..cddbe85b 100644 --- a/components/cast_certificate/cast_cert_validator.cc +++ b/components/cast_certificate/cast_cert_validator.cc
@@ -23,7 +23,7 @@ #include "net/cert/internal/parsed_certificate.h" #include "net/cert/internal/path_builder.h" #include "net/cert/internal/signature_algorithm.h" -#include "net/cert/internal/signature_policy.h" +#include "net/cert/internal/simple_path_builder_delegate.h" #include "net/cert/internal/trust_store_in_memory.h" #include "net/cert/internal/verify_certificate_chain.h" #include "net/cert/internal/verify_signed_data.h" @@ -93,19 +93,17 @@ // Cast certificates rely on RSASSA-PKCS#1 v1.5 with SHA-1 for signatures. // -// The following signature policy specifies which signature algorithms (and key -// sizes) are acceptable. It is used when verifying a chain of certificates, as -// well as when verifying digital signature using the target certificate's -// SPKI. +// The following delegate will allow signature algorithms of: // -// This particular policy allows for: // * ECDSA, RSA-SSA, and RSA-PSS // * Supported EC curves: P-256, P-384, P-521. // * Hashes: All SHA hashes including SHA-1 (despite being known weak). -// * RSA keys must have a modulus at least 2048-bits long. -std::unique_ptr<net::SignaturePolicy> CreateCastSignaturePolicy() { - return base::MakeUnique<net::SimpleSignaturePolicy>(2048); -} +// +// It will also require RSA keys have a modulus at least 2048-bits long. +class CastPathBuilderDelegate : public net::SimplePathBuilderDelegate { + public: + CastPathBuilderDelegate() : SimplePathBuilderDelegate(2048) {} +}; class CertVerificationContextImpl : public CertVerificationContext { public: @@ -123,16 +121,10 @@ auto signature_algorithm = net::SignatureAlgorithm::CreateRsaPkcs1(digest_algorithm); - // Use the same policy as was used for verifying signatures in - // certificates. This will ensure for instance that the key used is at - // least 2048-bits long. - auto signature_policy = CreateCastSignaturePolicy(); - - net::CertErrors errors; return net::VerifySignedData( *signature_algorithm, net::der::Input(data), net::der::BitString(net::der::Input(signature), 0), - net::der::Input(&spki_), signature_policy.get(), &errors); + net::der::Input(&spki_)); } std::string GetCommonName() const override { return common_name_; } @@ -298,8 +290,7 @@ intermediate_cert_issuer_source.AddCert(std::move(cert)); } - // Use a signature policy compatible with Cast's PKI. - auto signature_policy = CreateCastSignaturePolicy(); + CastPathBuilderDelegate path_builder_delegate; // Do path building and RFC 5280 compatible certificate verification using the // two Cast trust anchors and Cast signature policy. @@ -308,7 +299,7 @@ return CastCertError::ERR_UNEXPECTED; net::CertPathBuilder::Result result; net::CertPathBuilder path_builder( - target_cert.get(), trust_store, signature_policy.get(), verification_time, + target_cert.get(), trust_store, &path_builder_delegate, verification_time, net::KeyPurpose::CLIENT_AUTH, net::InitialExplicitPolicy::kFalse, {net::AnyPolicy()}, net::InitialPolicyMappingInhibit::kFalse, net::InitialAnyPolicyInhibit::kFalse, &result);
diff --git a/components/cast_certificate/cast_cert_validator_unittest.cc b/components/cast_certificate/cast_cert_validator_unittest.cc index b25f0ae..9fe86979 100644 --- a/components/cast_certificate/cast_cert_validator_unittest.cc +++ b/components/cast_certificate/cast_cert_validator_unittest.cc
@@ -542,138 +542,23 @@ TRUST_STORE_FROM_TEST_FILE, ""); } -// ------------------------------------------------------ -// Valid signature using 1024-bit RSA key -// ------------------------------------------------------ - -// This test vector comes from the NIST test vectors (pkcs1v15sign-vectors.txt), -// PKCS#1 v1.5 Signature Example 1.2. -// -// It is a valid signature using a 1024 bit key and SHA-1. - -const uint8_t kEx1Message[] = { - 0x85, 0x13, 0x84, 0xcd, 0xfe, 0x81, 0x9c, 0x22, 0xed, 0x6c, 0x4c, - 0xcb, 0x30, 0xda, 0xeb, 0x5c, 0xf0, 0x59, 0xbc, 0x8e, 0x11, 0x66, - 0xb7, 0xe3, 0x53, 0x0c, 0x4c, 0x23, 0x3e, 0x2b, 0x5f, 0x8f, 0x71, - 0xa1, 0xcc, 0xa5, 0x82, 0xd4, 0x3e, 0xcc, 0x72, 0xb1, 0xbc, 0xa1, - 0x6d, 0xfc, 0x70, 0x13, 0x22, 0x6b, 0x9e, -}; - -const uint8_t kEx1Signature[] = { - 0x84, 0xfd, 0x2c, 0xe7, 0x34, 0xec, 0x1d, 0xa8, 0x28, 0xd0, 0xf1, 0x5b, - 0xf4, 0x9a, 0x87, 0x07, 0xc1, 0x5d, 0x05, 0x94, 0x81, 0x36, 0xde, 0x53, - 0x7a, 0x3d, 0xb4, 0x21, 0x38, 0x41, 0x67, 0xc8, 0x6f, 0xae, 0x02, 0x25, - 0x87, 0xee, 0x9e, 0x13, 0x7d, 0xae, 0xe7, 0x54, 0x73, 0x82, 0x62, 0x93, - 0x2d, 0x27, 0x1c, 0x74, 0x4c, 0x6d, 0x3a, 0x18, 0x9a, 0xd4, 0x31, 0x1b, - 0xdb, 0x02, 0x04, 0x92, 0xe3, 0x22, 0xfb, 0xdd, 0xc4, 0x04, 0x06, 0xea, - 0x86, 0x0d, 0x4e, 0x8e, 0xa2, 0xa4, 0x08, 0x4a, 0xa9, 0x8b, 0x96, 0x22, - 0xa4, 0x46, 0x75, 0x6f, 0xdb, 0x74, 0x0d, 0xdb, 0x3d, 0x91, 0xdb, 0x76, - 0x70, 0xe2, 0x11, 0x66, 0x1b, 0xbf, 0x87, 0x09, 0xb1, 0x1c, 0x08, 0xa7, - 0x07, 0x71, 0x42, 0x2d, 0x1a, 0x12, 0xde, 0xf2, 0x9f, 0x06, 0x88, 0xa1, - 0x92, 0xae, 0xbd, 0x89, 0xe0, 0xf8, 0x96, 0xf8, -}; - -const uint8_t kEx1PublicKeySpki[] = { - 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, - 0x89, 0x02, 0x81, 0x81, 0x00, 0xa5, 0x6e, 0x4a, 0x0e, 0x70, 0x10, 0x17, - 0x58, 0x9a, 0x51, 0x87, 0xdc, 0x7e, 0xa8, 0x41, 0xd1, 0x56, 0xf2, 0xec, - 0x0e, 0x36, 0xad, 0x52, 0xa4, 0x4d, 0xfe, 0xb1, 0xe6, 0x1f, 0x7a, 0xd9, - 0x91, 0xd8, 0xc5, 0x10, 0x56, 0xff, 0xed, 0xb1, 0x62, 0xb4, 0xc0, 0xf2, - 0x83, 0xa1, 0x2a, 0x88, 0xa3, 0x94, 0xdf, 0xf5, 0x26, 0xab, 0x72, 0x91, - 0xcb, 0xb3, 0x07, 0xce, 0xab, 0xfc, 0xe0, 0xb1, 0xdf, 0xd5, 0xcd, 0x95, - 0x08, 0x09, 0x6d, 0x5b, 0x2b, 0x8b, 0x6d, 0xf5, 0xd6, 0x71, 0xef, 0x63, - 0x77, 0xc0, 0x92, 0x1c, 0xb2, 0x3c, 0x27, 0x0a, 0x70, 0xe2, 0x59, 0x8e, - 0x6f, 0xf8, 0x9d, 0x19, 0xf1, 0x05, 0xac, 0xc2, 0xd3, 0xf0, 0xcb, 0x35, - 0xf2, 0x92, 0x80, 0xe1, 0x38, 0x6b, 0x6f, 0x64, 0xc4, 0xef, 0x22, 0xe1, - 0xe1, 0xf2, 0x0d, 0x0c, 0xe8, 0xcf, 0xfb, 0x22, 0x49, 0xbd, 0x9a, 0x21, - 0x37, 0x02, 0x03, 0x01, 0x00, 0x01, -}; - -// Tests that a valid signature fails, because it uses a 1024-bit RSA key (too -// weak). -TEST(VerifyCastDeviceCertTest, VerifySignature1024BitRsa) { - auto context = - CertVerificationContextImplForTest(CreateString(kEx1PublicKeySpki)); - - EXPECT_FALSE(context->VerifySignatureOverData(CreateString(kEx1Signature), - CreateString(kEx1Message), - net::DigestAlgorithm::Sha1)); +// Tests verifying a certificate chain where the leaf certificate has a +// 1024-bit RSA key. Verification should fail since the target's key is +// too weak. +TEST(VerifyCastDeviceCertTest, DeviceCertHas1024BitRsaKey) { + RunTest(CastCertError::ERR_CERTS_VERIFY_GENERIC, "RSA 1024 Device Cert", + CastDeviceCertPolicy::NONE, "certificates/rsa1024_device_cert.pem", + AprilFirst2016(), TRUST_STORE_FROM_TEST_FILE, ""); } -// ------------------------------------------------------ -// Valid signature using 2048-bit RSA key -// ------------------------------------------------------ - -// This test vector was generated (using WebCrypto). It is a valid signature -// using a 2048-bit RSA key, RSASSA PKCS#1 v1.5 with SHA-1. - -const uint8_t kEx2Message[] = { - // "hello" - 0x68, 0x65, 0x6c, 0x6c, 0x6f, -}; - -const uint8_t kEx2Signature[] = { - 0xc1, 0x21, 0x84, 0xe1, 0x62, 0x0e, 0x59, 0x52, 0x5b, 0xa4, 0x10, 0x1e, - 0x11, 0x80, 0x5b, 0x9e, 0xcb, 0xa0, 0x20, 0x78, 0x29, 0xfc, 0xc0, 0x9a, - 0xd9, 0x48, 0x90, 0x81, 0x03, 0xa9, 0xc0, 0x2f, 0x0a, 0xc4, 0x20, 0x34, - 0xb5, 0xdb, 0x19, 0x04, 0xec, 0x94, 0x9b, 0xba, 0x48, 0x43, 0xf3, 0x5a, - 0x15, 0x56, 0xfc, 0x4a, 0x87, 0x79, 0xf8, 0x50, 0xff, 0x5d, 0x66, 0x25, - 0xdc, 0xa5, 0xd8, 0xe8, 0x9f, 0x5a, 0x73, 0x79, 0x6f, 0x5d, 0x99, 0xe0, - 0xd5, 0xa5, 0x84, 0x49, 0x20, 0x3c, 0xe2, 0xa3, 0xd0, 0x69, 0x31, 0x2c, - 0x13, 0xaf, 0x15, 0xd9, 0x10, 0x0d, 0x6f, 0xdd, 0x9d, 0x62, 0x5d, 0x7b, - 0xe1, 0x1a, 0x48, 0x59, 0xaf, 0xf7, 0xbe, 0x87, 0x92, 0x60, 0x5d, 0x1a, - 0xb5, 0xfe, 0x27, 0x38, 0x02, 0x20, 0xe9, 0xaf, 0x04, 0x57, 0xd3, 0x3b, - 0x70, 0x04, 0x63, 0x5b, 0xc6, 0x5d, 0x83, 0xe2, 0xaf, 0x02, 0xb4, 0xef, - 0x1c, 0x33, 0x54, 0x38, 0xf8, 0xb5, 0x19, 0xa8, 0x88, 0xdd, 0x1d, 0x96, - 0x1c, 0x5e, 0x54, 0x80, 0xde, 0x7b, 0xb6, 0x29, 0xb8, 0x6b, 0xea, 0x47, - 0xe5, 0xf1, 0x7e, 0xed, 0xe1, 0x91, 0xc8, 0xb8, 0x54, 0xd9, 0x1e, 0xfd, - 0x07, 0x10, 0xbd, 0xa9, 0xd4, 0x93, 0x5e, 0x65, 0x8b, 0x6b, 0x46, 0x93, - 0x4b, 0x60, 0x2a, 0x26, 0xf0, 0x1b, 0x4e, 0xca, 0x04, 0x82, 0xc0, 0x8d, - 0xb1, 0xa5, 0xa8, 0x70, 0xdd, 0x66, 0x68, 0x95, 0x09, 0xb4, 0x85, 0x62, - 0xf5, 0x17, 0x04, 0x48, 0xb4, 0x9d, 0x66, 0x2b, 0x25, 0x82, 0x7e, 0x99, - 0x3e, 0xa1, 0x11, 0x63, 0xc3, 0xdf, 0x10, 0x20, 0x52, 0x56, 0x32, 0x35, - 0xa9, 0x36, 0xde, 0x2a, 0xac, 0x10, 0x0d, 0x75, 0x21, 0xed, 0x5b, 0x38, - 0xb6, 0xb5, 0x1e, 0xb5, 0x5b, 0x9a, 0x72, 0xd5, 0xf8, 0x1a, 0xd3, 0x91, - 0xb8, 0x29, 0x0e, 0x58, -}; - -const uint8_t kEx2PublicKeySpki[] = { - 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, - 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xcf, 0xde, 0xa5, - 0x2e, 0x9d, 0x38, 0x62, 0x72, 0x47, 0x84, 0x8f, 0x2e, 0xa5, 0xe3, 0xd6, - 0x34, 0xb0, 0xf9, 0x79, 0xa9, 0x10, 0x63, 0xa9, 0x93, 0x5a, 0xa1, 0xb9, - 0xa3, 0x03, 0xd3, 0xcd, 0x9d, 0x84, 0x7d, 0xb6, 0x92, 0x47, 0xb4, 0x7d, - 0x4a, 0xe8, 0x3a, 0x4b, 0xc5, 0xf6, 0x35, 0x6f, 0x18, 0x72, 0xf3, 0xbc, - 0xd2, 0x1c, 0x7a, 0xd2, 0xe5, 0xdf, 0xcf, 0xb9, 0xac, 0x28, 0xd3, 0x49, - 0x2a, 0x4f, 0x08, 0x62, 0xb9, 0xf1, 0xaa, 0x3d, 0x76, 0xe3, 0xa9, 0x96, - 0x32, 0x24, 0x94, 0x9e, 0x88, 0xf8, 0x5e, 0xc3, 0x3c, 0x14, 0x32, 0x86, - 0x72, 0xa2, 0x34, 0x3d, 0x41, 0xd0, 0xb2, 0x01, 0x99, 0x01, 0xf3, 0x93, - 0xa3, 0x76, 0x5a, 0xff, 0x42, 0x28, 0x54, 0xe0, 0xcc, 0x4c, 0xcd, 0x2d, - 0x3b, 0x0b, 0x47, 0xcc, 0xc2, 0x75, 0x02, 0xc1, 0xb7, 0x0b, 0x37, 0x65, - 0xe6, 0x0d, 0xe4, 0xc3, 0x85, 0x86, 0x29, 0x3c, 0x77, 0xce, 0xb0, 0x34, - 0xa9, 0x03, 0xe9, 0x13, 0xbe, 0x97, 0x1e, 0xfd, 0xeb, 0x0d, 0x60, 0xc2, - 0xb3, 0x19, 0xa1, 0x75, 0x72, 0x57, 0x3f, 0x5d, 0x0e, 0x75, 0xac, 0x10, - 0x96, 0xad, 0x95, 0x67, 0x9f, 0xa2, 0x84, 0x15, 0x6a, 0x61, 0xb1, 0x47, - 0xd1, 0x24, 0x78, 0xb4, 0x40, 0x2b, 0xc3, 0x5c, 0x73, 0xd4, 0xc1, 0x8d, - 0x12, 0xf1, 0x3f, 0xb4, 0x93, 0x17, 0xfe, 0x5d, 0xbf, 0x39, 0xf2, 0x45, - 0xf9, 0xcf, 0x38, 0x44, 0x40, 0x5b, 0x47, 0x2a, 0xbf, 0xb9, 0xac, 0xa6, - 0x14, 0xb6, 0x1b, 0xe3, 0xa8, 0x14, 0xf8, 0xfe, 0x47, 0x67, 0xea, 0x90, - 0x51, 0x12, 0xcf, 0x5e, 0x28, 0xec, 0x92, 0x83, 0x7c, 0xc6, 0x29, 0x9f, - 0x12, 0x29, 0x88, 0x49, 0xf7, 0xb7, 0xed, 0x5e, 0x3a, 0x78, 0xd6, 0x8a, - 0xba, 0x42, 0x6e, 0x0a, 0xf4, 0x0d, 0xc1, 0xc0, 0x8f, 0xdb, 0x26, 0x41, - 0x57, 0x02, 0x03, 0x01, 0x00, 0x01, -}; - -// Tests that a valid signature using 2048-bit key succeeds. -TEST(VerifyCastDeviceCertTest, VerifySignature2048BitRsa) { - auto context = - CertVerificationContextImplForTest(CreateString(kEx2PublicKeySpki)); - - EXPECT_TRUE(context->VerifySignatureOverData(CreateString(kEx2Signature), - CreateString(kEx2Message), - net::DigestAlgorithm::Sha1)); +// Tests verifying a certificate chain where the leaf certificate has a +// 2048-bit RSA key, and then verifying signed data (both SHA1 and SHA256) +// for it. +TEST(VerifyCastDeviceCertTest, DeviceCertHas2048BitRsaKey) { + RunTest(CastCertError::OK, "RSA 2048 Device Cert", CastDeviceCertPolicy::NONE, + "certificates/rsa2048_device_cert.pem", AprilFirst2016(), + TRUST_STORE_FROM_TEST_FILE, + "signeddata/rsa2048_device_cert_data.pem"); } } // namespace
diff --git a/components/cast_certificate/cast_crl.cc b/components/cast_certificate/cast_crl.cc index 4f57a905..9401d7f9 100644 --- a/components/cast_certificate/cast_crl.cc +++ b/components/cast_certificate/cast_crl.cc
@@ -17,7 +17,7 @@ #include "net/cert/internal/parsed_certificate.h" #include "net/cert/internal/path_builder.h" #include "net/cert/internal/signature_algorithm.h" -#include "net/cert/internal/signature_policy.h" +#include "net/cert/internal/simple_path_builder_delegate.h" #include "net/cert/internal/trust_store_in_memory.h" #include "net/cert/internal/verify_certificate_chain.h" #include "net/cert/internal/verify_signed_data.h" @@ -87,13 +87,6 @@ generalized_time); } -// Specifies the signature verification policy. -// The required algorithms are: -// RSASSA PKCS#1 v1.5 with SHA-256, using RSA keys 2048-bits or longer. -std::unique_ptr<net::SignaturePolicy> CreateCastSignaturePolicy() { - return base::MakeUnique<net::SimpleSignaturePolicy>(2048); -} - // Verifies the CRL is signed by a trusted CRL authority at the time the CRL // was issued. Verifies the signature of |tbs_crl| is valid based on the // certificate and signature in |crl|. The validity of |tbs_crl| is verified @@ -122,16 +115,12 @@ net::der::Input(base::StringPiece(crl.signature())), 0); // Verify the signature. - auto signature_policy = CreateCastSignaturePolicy(); std::unique_ptr<net::SignatureAlgorithm> signature_algorithm_type = net::SignatureAlgorithm::CreateRsaPkcs1(net::DigestAlgorithm::Sha256); - net::CertErrors verify_errors; - if (!VerifySignedData(*signature_algorithm_type, - net::der::Input(&crl.tbs_crl()), - signature_value_bit_string, parsed_cert->tbs().spki_tlv, - signature_policy.get(), &verify_errors)) { - VLOG(2) << "CRL - Signature verification failed:\n" - << verify_errors.ToDebugString(); + if (!VerifySignedData( + *signature_algorithm_type, net::der::Input(&crl.tbs_crl()), + signature_value_bit_string, parsed_cert->tbs().spki_tlv)) { + VLOG(2) << "CRL - Signature verification failed"; return false; } @@ -141,9 +130,14 @@ VLOG(2) << "CRL - Unable to parse verification time."; return false; } + + // SimplePathBuilderDelegate will enforce required signature algorithms of + // RSASSA PKCS#1 v1.5 with SHA-256, and RSA keys 2048-bits or longer. + net::SimplePathBuilderDelegate path_builder_delegate(2048); + net::CertPathBuilder::Result result; net::CertPathBuilder path_builder( - parsed_cert.get(), trust_store, signature_policy.get(), verification_time, + parsed_cert.get(), trust_store, &path_builder_delegate, verification_time, net::KeyPurpose::ANY_EKU, net::InitialExplicitPolicy::kFalse, {net::AnyPolicy()}, net::InitialPolicyMappingInhibit::kFalse, net::InitialAnyPolicyInhibit::kFalse, &result);
diff --git a/components/component_updater/component_updater_service_unittest.cc b/components/component_updater/component_updater_service_unittest.cc index 4389e14..5e033c0 100644 --- a/components/component_updater/component_updater_service_unittest.cc +++ b/components/component_updater/component_updater_service_unittest.cc
@@ -126,10 +126,12 @@ private: base::test::ScopedTaskEnvironment scoped_task_environment_; base::RunLoop runloop_; - base::Closure quit_closure_; + const base::Closure quit_closure_ = runloop_.QuitClosure(); - scoped_refptr<TestConfigurator> config_; - scoped_refptr<MockUpdateClient> update_client_; + scoped_refptr<TestConfigurator> config_ = + base::MakeRefCounted<TestConfigurator>(); + scoped_refptr<MockUpdateClient> update_client_ = + base::MakeRefCounted<MockUpdateClient>(); std::unique_ptr<ComponentUpdateService> component_updater_; DISALLOW_COPY_AND_ASSIGN(ComponentUpdaterTest); @@ -181,16 +183,7 @@ return base::MakeUnique<CrxUpdateService>(config, new MockUpdateClient()); } -ComponentUpdaterTest::ComponentUpdaterTest() - : scoped_task_environment_( - base::test::ScopedTaskEnvironment::MainThreadType::UI) { - quit_closure_ = runloop_.QuitClosure(); - - config_ = base::MakeRefCounted<TestConfigurator>( - base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()}), - base::ThreadTaskRunnerHandle::Get()); - - update_client_ = base::MakeRefCounted<MockUpdateClient>(); +ComponentUpdaterTest::ComponentUpdaterTest() { EXPECT_CALL(update_client(), AddObserver(_)).Times(1); component_updater_ = base::MakeUnique<CrxUpdateService>(config_, update_client_);
diff --git a/components/component_updater/default_component_installer_unittest.cc b/components/component_updater/default_component_installer_unittest.cc index cd2fff2..4fa8646 100644 --- a/components/component_updater/default_component_installer_unittest.cc +++ b/components/component_updater/default_component_installer_unittest.cc
@@ -167,24 +167,17 @@ const scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get(); base::RunLoop runloop_; - base::Closure quit_closure_; + base::Closure quit_closure_ = runloop_.QuitClosure(); - scoped_refptr<TestConfigurator> config_; - scoped_refptr<MockUpdateClient> update_client_; + scoped_refptr<TestConfigurator> config_ = + base::MakeRefCounted<TestConfigurator>(); + scoped_refptr<MockUpdateClient> update_client_ = + base::MakeRefCounted<MockUpdateClient>(); std::unique_ptr<ComponentUpdateService> component_updater_; ComponentUnpacker::Result result_; }; -DefaultComponentInstallerTest::DefaultComponentInstallerTest() - : scoped_task_environment_( - base::test::ScopedTaskEnvironment::MainThreadType::UI) { - quit_closure_ = runloop_.QuitClosure(); - - config_ = base::MakeRefCounted<TestConfigurator>( - base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()}), - base::ThreadTaskRunnerHandle::Get()); - - update_client_ = base::MakeRefCounted<MockUpdateClient>(); +DefaultComponentInstallerTest::DefaultComponentInstallerTest() { EXPECT_CALL(update_client(), AddObserver(_)).Times(1); component_updater_ = base::MakeUnique<CrxUpdateService>(config_, update_client_);
diff --git a/components/feature_engagement_tracker/internal/stats.cc b/components/feature_engagement_tracker/internal/stats.cc index ac780251..79b78cf 100644 --- a/components/feature_engagement_tracker/internal/stats.cc +++ b/components/feature_engagement_tracker/internal/stats.cc
@@ -121,8 +121,10 @@ } // Histogram about the failure reasons. - if (!result.event_model_ready_ok) - LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE_MODEL_NOT_READY); + if (!result.event_model_ready_ok) { + LogTriggerHelpUIResult(name, + TriggerHelpUIResult::FAILURE_EVENT_MODEL_NOT_READY); + } if (!result.currently_showing_ok) { LogTriggerHelpUIResult(name, TriggerHelpUIResult::FAILURE_CURRENTLY_SHOWING);
diff --git a/components/feature_engagement_tracker/internal/stats.h b/components/feature_engagement_tracker/internal/stats.h index 7171a320..f1de6bd 100644 --- a/components/feature_engagement_tracker/internal/stats.h +++ b/components/feature_engagement_tracker/internal/stats.h
@@ -27,8 +27,8 @@ // The help UI is not triggered. FAILURE = 1, - // Data layer is not ready. - FAILURE_MODEL_NOT_READY = 2, + // Event model is not ready. + FAILURE_EVENT_MODEL_NOT_READY = 2, // Some other help UI is currently showing. FAILURE_CURRENTLY_SHOWING = 3, @@ -51,7 +51,7 @@ // Session rate does not meet the requirement. FAILURE_SESSION_RATE = 9, - // Availability mode is not ready. + // Availability model is not ready. FAILURE_AVAILABILITY_MODEL_NOT_READY = 10, // Availability precondition is not satisfied.
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc index bd7d01dc..75c16d9 100644 --- a/components/history/core/browser/history_backend.cc +++ b/components/history/core/browser/history_backend.cc
@@ -516,6 +516,15 @@ ui::PageTransition redirect_info = ui::PAGE_TRANSITION_CHAIN_START; RedirectList redirects = request.redirects; + // In the presence of client redirects, |request.redirects| can be a partial + // chain because previous calls to this function may have reported a + // redirect chain already. This is fine for the visits database where we'll + // just append data but insufficient for |recent_redirects_| + // (backpropagation of favicons and titles), where we'd like the full + // (extended) redirect chain. We use |extended_redirect_chain| to represent + // this. + RedirectList extended_redirect_chain; + if (redirects[0].SchemeIs(url::kAboutScheme)) { // When the redirect source + referrer is "about" we skip it. This // happens when a page opens a new frame/window to about:blank and then @@ -554,6 +563,8 @@ visit_row.transition & ~ui::PAGE_TRANSITION_CHAIN_END); db_->UpdateVisitRow(visit_row); } + + GetCachedRecentRedirects(request.referrer, &extended_redirect_chain); } } @@ -588,8 +599,12 @@ } // Last, save this redirect chain for later so we can set titles & favicons - // on the redirected pages properly. - recent_redirects_.Put(request.url, redirects); + // on the redirected pages properly. For this we use the extended redirect + // chain, which includes URLs from chained redirects. + extended_redirect_chain.insert(extended_redirect_chain.end(), + std::make_move_iterator(redirects.begin()), + std::make_move_iterator(redirects.end())); + recent_redirects_.Put(request.url, extended_redirect_chain); } // TODO(brettw) bug 1140015: Add an "add page" notification so the history @@ -2105,6 +2120,8 @@ if (page_url.has_ref()) { // Refs often gets added by Javascript, but the redirect chain is keyed to // the URL without a ref. + // TODO(crbug.com/746268): This can cause orphan favicons, i.e. without a + // matching history URL, which will never be cleaned up by the expirer. GURL::Replacements replacements; replacements.ClearRef(); GURL page_url_without_ref = page_url.ReplaceComponents(replacements);
diff --git a/components/history/core/browser/history_backend.h b/components/history/core/browser/history_backend.h index 85c5fb8..51df72c 100644 --- a/components/history/core/browser/history_backend.h +++ b/components/history/core/browser/history_backend.h
@@ -525,6 +525,8 @@ FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, SetFaviconMappingsForPageAndRedirectsWithFragment); FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, + RecentRedirectsForClientRedirects); + FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, SetFaviconMappingsForPageDuplicates); FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, SetFaviconsDeleteBitmaps); FRIEND_TEST_ALL_PREFIXES(HistoryBackendTest, SetFaviconsReplaceBitmapData);
diff --git a/components/history/core/browser/history_backend_unittest.cc b/components/history/core/browser/history_backend_unittest.cc index c92e97e..9cb50d9d 100644 --- a/components/history/core/browser/history_backend_unittest.cc +++ b/components/history/core/browser/history_backend_unittest.cc
@@ -367,8 +367,8 @@ // |did_replace| is true if the transition is non-user initiated and the // navigation entry for |url2| has replaced that for |url1|. The possibly // updated transition code of the visit records for |url1| and |url2| is - // returned by filling in |*transition1| and |*transition2|, respectively. - // |time| is a time of the redirect. + // returned by filling in |*transition1| and |*transition2|, respectively, + // unless null. |time| is a time of the redirect. void AddClientRedirect(const GURL& url1, const GURL& url2, bool did_replace, @@ -387,8 +387,11 @@ history::SOURCE_BROWSED, did_replace, true); backend_->AddPage(request); - *transition1 = GetTransition(url1); - *transition2 = GetTransition(url2); + if (transition1) + *transition1 = GetTransition(url1); + + if (transition2) + *transition2 = GetTransition(url2); } int GetTransition(const GURL& url) { @@ -1808,7 +1811,6 @@ EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::FAVICON)); } - // Test that SetFaviconMappingsForPageAndRedirects correctly updates icon // mappings when the final URL has a fragment. TEST_F(HistoryBackendTest, SetFaviconMappingsForPageAndRedirectsWithFragment) { @@ -1868,6 +1870,31 @@ EXPECT_EQ(1u, NumIconMappingsForPageURL(url3, favicon_base::FAVICON)); } +// Test that |recent_redirects_| stores the full redirect chain in case of +// client redirects. In this case, a server-side redirect is followed by a +// client-side one. +TEST_F(HistoryBackendTest, RecentRedirectsForClientRedirects) { + GURL server_redirect_url("http://google.com/a"); + GURL client_redirect_url("http://google.com/b"); + GURL landing_url("http://google.com/c"); + + // Page A is browsed by user and server redirects to B. + HistoryAddPageArgs request( + client_redirect_url, base::Time::Now(), NULL, 0, GURL(), + /*redirects=*/{server_redirect_url, client_redirect_url}, + ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED, false, true); + backend_->AddPage(request); + + // Client redirect to page C. + AddClientRedirect(client_redirect_url, landing_url, /*did_replace=*/false, + base::Time(), /*transition1=*/nullptr, + /*transition2=*/nullptr); + + EXPECT_THAT( + backend_->recent_redirects_.Get(landing_url)->second, + ElementsAre(server_redirect_url, client_redirect_url, landing_url)); +} + // Test that there is no churn in icon mappings from calling // SetFavicons() twice with the same |bitmaps| parameter. TEST_F(HistoryBackendTest, SetFaviconMappingsForPageDuplicates) {
diff --git a/components/metrics/call_stack_profile_metrics_provider.cc b/components/metrics/call_stack_profile_metrics_provider.cc index 472c6c5..3914b4c 100644 --- a/components/metrics/call_stack_profile_metrics_provider.cc +++ b/components/metrics/call_stack_profile_metrics_provider.cc
@@ -98,8 +98,8 @@ // Singleton class responsible for retaining profiles received via the callback // created by GetProfilerCallback(). These are then sent to UMA on the -// invocation of CallStackProfileMetricsProvider::ProvideGeneralMetrics(). We -// need to store the profiles outside of a CallStackProfileMetricsProvider +// invocation of CallStackProfileMetricsProvider::ProvideCurrentSessionData(). +// We need to store the profiles outside of a CallStackProfileMetricsProvider // instance since callers may start profiling before the // CallStackProfileMetricsProvider is created. // @@ -521,7 +521,7 @@ PendingProfiles::GetInstance()->SetCollectionEnabled(false); } -void CallStackProfileMetricsProvider::ProvideGeneralMetrics( +void CallStackProfileMetricsProvider::ProvideCurrentSessionData( ChromeUserMetricsExtension* uma_proto) { std::vector<ProfilesState> pending_profiles; PendingProfiles::GetInstance()->Swap(&pending_profiles);
diff --git a/components/metrics/call_stack_profile_metrics_provider.h b/components/metrics/call_stack_profile_metrics_provider.h index d5e4f7e..a8178bd 100644 --- a/components/metrics/call_stack_profile_metrics_provider.h +++ b/components/metrics/call_stack_profile_metrics_provider.h
@@ -73,7 +73,8 @@ // MetricsProvider: void OnRecordingEnabled() override; void OnRecordingDisabled() override; - void ProvideGeneralMetrics(ChromeUserMetricsExtension* uma_proto) override; + void ProvideCurrentSessionData( + ChromeUserMetricsExtension* uma_proto) override; protected: // base::Feature for reporting profiles. Provided here for test use.
diff --git a/components/metrics/call_stack_profile_metrics_provider_unittest.cc b/components/metrics/call_stack_profile_metrics_provider_unittest.cc index 11f2ddc..bf176e40 100644 --- a/components/metrics/call_stack_profile_metrics_provider_unittest.cc +++ b/components/metrics/call_stack_profile_metrics_provider_unittest.cc
@@ -344,7 +344,7 @@ CallStackProfileParams::MAY_SHUFFLE); AppendProfiles(¶ms, std::move(profiles)); ChromeUserMetricsExtension uma_proto; - provider.ProvideGeneralMetrics(&uma_proto); + provider.ProvideCurrentSessionData(&uma_proto); ASSERT_EQ(static_cast<int>(arraysize(expected_proto_profiles)), uma_proto.sampled_profile().size()); @@ -428,7 +428,7 @@ CallStackProfileParams::MAY_SHUFFLE); AppendProfiles(¶ms, std::move(profiles)); ChromeUserMetricsExtension uma_proto; - provider.ProvideGeneralMetrics(&uma_proto); + provider.ProvideCurrentSessionData(&uma_proto); ASSERT_EQ(static_cast<int>(arraysize(expected_proto_profiles)), uma_proto.sampled_profile().size()); @@ -514,7 +514,7 @@ CallStackProfileParams::PRESERVE_ORDER); AppendProfiles(¶ms, std::move(profiles)); ChromeUserMetricsExtension uma_proto; - provider.ProvideGeneralMetrics(&uma_proto); + provider.ProvideCurrentSessionData(&uma_proto); ASSERT_EQ(static_cast<int>(arraysize(expected_proto_profiles)), uma_proto.sampled_profile().size()); @@ -558,7 +558,7 @@ CallStackProfileParams::MAY_SHUFFLE); AppendProfiles(¶ms, std::move(profiles)); ChromeUserMetricsExtension uma_proto; - provider.ProvideGeneralMetrics(&uma_proto); + provider.ProvideCurrentSessionData(&uma_proto); ASSERT_EQ(static_cast<int>(arraysize(expected_proto_profiles)), uma_proto.sampled_profile().size()); @@ -569,8 +569,8 @@ } } -// Checks that pending profiles are only passed back to ProvideGeneralMetrics -// once. +// Checks that pending profiles are only passed back to +// ProvideCurrentSessionData once. TEST_F(CallStackProfileMetricsProviderTest, ProfilesProvidedOnlyOnce) { CallStackProfileMetricsProvider provider; for (int r = 0; r < 2; ++r) { @@ -591,7 +591,7 @@ CallStackProfileParams::MAY_SHUFFLE); AppendProfiles(¶ms, std::move(profiles)); ChromeUserMetricsExtension uma_proto; - provider.ProvideGeneralMetrics(&uma_proto); + provider.ProvideCurrentSessionData(&uma_proto); ASSERT_EQ(1, uma_proto.sampled_profile().size()); const SampledProfile& sampled_profile = uma_proto.sampled_profile().Get(0); @@ -603,7 +603,7 @@ } } -// Checks that pending profiles are provided to ProvideGeneralMetrics +// Checks that pending profiles are provided to ProvideCurrentSessionData // when collected before CallStackProfileMetricsProvider is instantiated. TEST_F(CallStackProfileMetricsProviderTest, ProfilesProvidedWhenCollectedBeforeInstantiation) { @@ -624,12 +624,12 @@ CallStackProfileMetricsProvider provider; provider.OnRecordingEnabled(); ChromeUserMetricsExtension uma_proto; - provider.ProvideGeneralMetrics(&uma_proto); + provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(1, uma_proto.sampled_profile_size()); } -// Checks that pending profiles are not provided to ProvideGeneralMetrics +// Checks that pending profiles are not provided to ProvideCurrentSessionData // while recording is disabled. TEST_F(CallStackProfileMetricsProviderTest, ProfilesNotProvidedWhileDisabled) { Profiles profiles = ProfilesFactory() @@ -648,12 +648,12 @@ CallStackProfileParams::MAY_SHUFFLE); AppendProfiles(¶ms, std::move(profiles)); ChromeUserMetricsExtension uma_proto; - provider.ProvideGeneralMetrics(&uma_proto); + provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(0, uma_proto.sampled_profile_size()); } -// Checks that pending profiles are not provided to ProvideGeneralMetrics +// Checks that pending profiles are not provided to ProvideCurrentSessionData // if recording is disabled while profiling. TEST_F(CallStackProfileMetricsProviderTest, ProfilesNotProvidedAfterChangeToDisabled) { @@ -674,12 +674,12 @@ .Build(); callback.Run(std::move(profiles)); ChromeUserMetricsExtension uma_proto; - provider.ProvideGeneralMetrics(&uma_proto); + provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(0, uma_proto.sampled_profile_size()); } -// Checks that pending profiles are not provided to ProvideGeneralMetrics if +// Checks that pending profiles are not provided to ProvideCurrentSessionData if // recording is enabled, but then disabled and reenabled while profiling. TEST_F(CallStackProfileMetricsProviderTest, ProfilesNotProvidedAfterChangeToDisabledThenEnabled) { @@ -701,12 +701,12 @@ .Build(); callback.Run(std::move(profiles)); ChromeUserMetricsExtension uma_proto; - provider.ProvideGeneralMetrics(&uma_proto); + provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(0, uma_proto.sampled_profile_size()); } -// Checks that pending profiles are not provided to ProvideGeneralMetrics +// Checks that pending profiles are not provided to ProvideCurrentSessionData // if recording is disabled, but then enabled while profiling. TEST_F(CallStackProfileMetricsProviderTest, ProfilesNotProvidedAfterChangeFromDisabled) { @@ -727,7 +727,7 @@ .Build(); callback.Run(std::move(profiles)); ChromeUserMetricsExtension uma_proto; - provider.ProvideGeneralMetrics(&uma_proto); + provider.ProvideCurrentSessionData(&uma_proto); EXPECT_EQ(0, uma_proto.sampled_profile_size()); } @@ -815,7 +815,7 @@ const base::TimeDelta max_expected_uptime = internal::GetUptime(); ChromeUserMetricsExtension uma_proto; - provider.ProvideGeneralMetrics(&uma_proto); + provider.ProvideCurrentSessionData(&uma_proto); // We expect duration_ms to be the process uptime. Check that it's within the // min/max boundary values that were retrieved earlier. Then, set the value
diff --git a/components/metrics/file_metrics_provider.cc b/components/metrics/file_metrics_provider.cc index d6165f2..b8bab8c2 100644 --- a/components/metrics/file_metrics_provider.cc +++ b/components/metrics/file_metrics_provider.cc
@@ -596,7 +596,7 @@ return false; } -bool FileMetricsProvider::HasInitialStabilityMetrics() { +bool FileMetricsProvider::HasPreviousSessionData() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // Measure the total time spent checking all sources as well as the time
diff --git a/components/metrics/file_metrics_provider.h b/components/metrics/file_metrics_provider.h index 66ce266..0581e99 100644 --- a/components/metrics/file_metrics_provider.h +++ b/components/metrics/file_metrics_provider.h
@@ -201,7 +201,7 @@ bool ProvideIndependentMetrics( SystemProfileProto* system_profile_proto, base::HistogramSnapshotManager* snapshot_manager) override; - bool HasInitialStabilityMetrics() override; + bool HasPreviousSessionData() override; void RecordInitialHistogramSnapshots( base::HistogramSnapshotManager* snapshot_manager) override;
diff --git a/components/metrics/file_metrics_provider_unittest.cc b/components/metrics/file_metrics_provider_unittest.cc index 183317d..b208198 100644 --- a/components/metrics/file_metrics_provider_unittest.cc +++ b/components/metrics/file_metrics_provider_unittest.cc
@@ -101,9 +101,7 @@ provider()->OnDidCreateMetricsLog(); } - bool HasInitialStabilityMetrics() { - return provider()->HasInitialStabilityMetrics(); - } + bool HasPreviousSessionData() { return provider()->HasPreviousSessionData(); } void MergeHistogramDeltas() { provider()->MergeHistogramDeltas(); @@ -420,7 +418,7 @@ kMetricsName); // Record embedded snapshots via snapshot-manager. - ASSERT_TRUE(HasInitialStabilityMetrics()); + ASSERT_TRUE(HasPreviousSessionData()); RunTasks(); { HistogramFlattenerDeltaRecorder flattener; @@ -524,7 +522,7 @@ kMetricsName); // Record embedded snapshots via snapshot-manager. - ASSERT_TRUE(HasInitialStabilityMetrics()); + ASSERT_TRUE(HasPreviousSessionData()); RunTasks(); { HistogramFlattenerDeltaRecorder flattener; @@ -565,7 +563,7 @@ kMetricsName); // Record embedded snapshots via snapshot-manager. - EXPECT_FALSE(HasInitialStabilityMetrics()); + EXPECT_FALSE(HasPreviousSessionData()); RunTasks(); { HistogramFlattenerDeltaRecorder flattener;
diff --git a/components/metrics/metrics_provider.cc b/components/metrics/metrics_provider.cc index 5cf6c56..b642258 100644 --- a/components/metrics/metrics_provider.cc +++ b/components/metrics/metrics_provider.cc
@@ -43,24 +43,18 @@ SystemProfileProto* system_profile_proto) { } -bool MetricsProvider::HasInitialStabilityMetrics() { +bool MetricsProvider::HasPreviousSessionData() { return false; } void MetricsProvider::ProvidePreviousSessionData( ChromeUserMetricsExtension* uma_proto) { - ProvideInitialStabilityMetrics(uma_proto->mutable_system_profile()); ProvideStabilityMetrics(uma_proto->mutable_system_profile()); } void MetricsProvider::ProvideCurrentSessionData( ChromeUserMetricsExtension* uma_proto) { ProvideStabilityMetrics(uma_proto->mutable_system_profile()); - ProvideGeneralMetrics(uma_proto); -} - -void MetricsProvider::ProvideInitialStabilityMetrics( - SystemProfileProto* system_profile_proto) { } void MetricsProvider::ProvideStabilityMetrics( @@ -70,10 +64,6 @@ void MetricsProvider::ClearSavedStabilityMetrics() { } -void MetricsProvider::ProvideGeneralMetrics( - ChromeUserMetricsExtension* uma_proto) { -} - void MetricsProvider::RecordHistogramSnapshots( base::HistogramSnapshotManager* snapshot_manager) { }
diff --git a/components/metrics/metrics_provider.h b/components/metrics/metrics_provider.h index 442c573..591c63ff 100644 --- a/components/metrics/metrics_provider.h +++ b/components/metrics/metrics_provider.h
@@ -65,7 +65,7 @@ // Returning true will trigger ProvidePreviousSessionData on all other // registered metrics providers. // Default implementation always returns false. - virtual bool HasInitialStabilityMetrics(); + virtual bool HasPreviousSessionData(); // Called when building a log about the previous session, so the provider // can provide data about it. Stability metrics can be provided @@ -78,13 +78,6 @@ // can provide data about it. virtual void ProvideCurrentSessionData(ChromeUserMetricsExtension* uma_proto); - // Called at most once at startup when an initial stability log is created. - // It provides critical statiblity metrics that need to be reported in an - // initial stability log. - // Default implementation is a no-op. - virtual void ProvideInitialStabilityMetrics( - SystemProfileProto* system_profile_proto); - // Provides additional stability metrics. Stability metrics can be provided // directly into |stability_proto| fields or by logging stability histograms // via the UMA_STABILITY_HISTOGRAM_ENUMERATION() macro. @@ -95,12 +88,6 @@ // because they are from an old version and should not be kept. virtual void ClearSavedStabilityMetrics(); - // Provides general metrics that are neither system profile nor stability - // metrics. May also be used to add histograms when final metrics are - // collected right before upload. - virtual void ProvideGeneralMetrics( - ChromeUserMetricsExtension* uma_proto); - // Called during regular collection to explicitly load histogram snapshots // using a snapshot manager. PrepareDeltas() will have already been called // and FinishDeltas() will be called later; calls to only PrepareDelta(),
diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc index 4da5ea8f..d90605d 100644 --- a/components/metrics/metrics_service.cc +++ b/components/metrics/metrics_service.cc
@@ -747,7 +747,7 @@ // the later call to RecordInitialHistogramSnapshots(). bool has_stability_metrics = false; for (auto& provider : metrics_providers_) - has_stability_metrics |= provider->HasInitialStabilityMetrics(); + has_stability_metrics |= provider->HasPreviousSessionData(); return has_stability_metrics; }
diff --git a/components/metrics/metrics_service_unittest.cc b/components/metrics/metrics_service_unittest.cc index 6c1497326..f2007926 100644 --- a/components/metrics/metrics_service_unittest.cc +++ b/components/metrics/metrics_service_unittest.cc
@@ -174,7 +174,7 @@ // No initial stability log should be generated. EXPECT_FALSE(service.has_unsent_logs()); - // Ensure that HasInitialStabilityMetrics() is always called on providers, + // Ensure that HasPreviousSessionData() is always called on providers, // for consistency, even if other conditions already indicate their presence. EXPECT_TRUE(test_provider->has_initial_stability_metrics_called()); @@ -218,7 +218,7 @@ EXPECT_TRUE(log_store->has_unsent_logs()); EXPECT_FALSE(log_store->has_staged_log()); - // Ensure that HasInitialStabilityMetrics() is always called on providers, + // Ensure that HasPreviousSessionData() is always called on providers, // for consistency, even if other conditions already indicate their presence. EXPECT_TRUE(test_provider->has_initial_stability_metrics_called()); @@ -284,7 +284,7 @@ EXPECT_TRUE(log_store->has_unsent_logs()); EXPECT_FALSE(log_store->has_staged_log()); - // Ensure that HasInitialStabilityMetrics() is always called on providers, + // Ensure that HasPreviousSessionData() is always called on providers, // for consistency, even if other conditions already indicate their presence. EXPECT_TRUE(test_provider->has_initial_stability_metrics_called());
diff --git a/components/metrics/net/network_metrics_provider.cc b/components/metrics/net/network_metrics_provider.cc index df7a8752..55ae90c 100644 --- a/components/metrics/net/network_metrics_provider.cc +++ b/components/metrics/net/network_metrics_provider.cc
@@ -185,11 +185,11 @@ } } -void NetworkMetricsProvider::ProvideGeneralMetrics( +void NetworkMetricsProvider::ProvideCurrentSessionData( ChromeUserMetricsExtension*) { DCHECK(thread_checker_.CalledOnValidThread()); - // ProvideGeneralMetrics is called on the main thread, at the time a metrics - // record is being finalized. + // ProvideCurrentSessionData is called on the main thread, at the time a + // metrics record is being finalized. net::NetworkChangeNotifier::FinalizingMetricsLogRecord(); LogAggregatedMetrics(); }
diff --git a/components/metrics/net/network_metrics_provider.h b/components/metrics/net/network_metrics_provider.h index 69d514a..55121aa 100644 --- a/components/metrics/net/network_metrics_provider.h +++ b/components/metrics/net/network_metrics_provider.h
@@ -66,7 +66,8 @@ class EffectiveConnectionTypeObserver; // MetricsProvider: - void ProvideGeneralMetrics(ChromeUserMetricsExtension* uma_proto) override; + void ProvideCurrentSessionData( + ChromeUserMetricsExtension* uma_proto) override; void ProvideSystemProfileMetrics(SystemProfileProto* system_profile) override; // ConnectionTypeObserver:
diff --git a/components/metrics/profiler/profiler_metrics_provider.cc b/components/metrics/profiler/profiler_metrics_provider.cc index 3c3ffbad..398626ae 100644 --- a/components/metrics/profiler/profiler_metrics_provider.cc +++ b/components/metrics/profiler/profiler_metrics_provider.cc
@@ -65,7 +65,7 @@ ProfilerMetricsProvider::~ProfilerMetricsProvider() { } -void ProfilerMetricsProvider::ProvideGeneralMetrics( +void ProfilerMetricsProvider::ProvideCurrentSessionData( ChromeUserMetricsExtension* uma_proto) { DCHECK_EQ(0, uma_proto->profiler_event_size());
diff --git a/components/metrics/profiler/profiler_metrics_provider.h b/components/metrics/profiler/profiler_metrics_provider.h index 8585fce..efb44c1 100644 --- a/components/metrics/profiler/profiler_metrics_provider.h +++ b/components/metrics/profiler/profiler_metrics_provider.h
@@ -28,7 +28,8 @@ ~ProfilerMetricsProvider() override; // MetricsDataProvider: - void ProvideGeneralMetrics(ChromeUserMetricsExtension* uma_proto) override; + void ProvideCurrentSessionData( + ChromeUserMetricsExtension* uma_proto) override; // Records the passed profiled data, which should be a snapshot of the // browser's profiled performance during startup for a single process. @@ -48,9 +49,9 @@ bool IsCellularLogicEnabled(); // Saved cache of generated Profiler event protos, to be copied into the UMA - // proto when ProvideGeneralMetrics() is called. The map is from a profiling - // phase id to the profiler event proto that represents profiler data for the - // profiling phase. + // proto when ProvideCurrentSessionData() is called. The map is from a + // profiling phase id to the profiler event proto that represents profiler + // data for the profiling phase. std::map<int, ProfilerEventProto> profiler_events_cache_; // Returns true if current connection type is cellular and user is assigned to
diff --git a/components/metrics/profiler/profiler_metrics_provider_unittest.cc b/components/metrics/profiler/profiler_metrics_provider_unittest.cc index 050a87ad..84def81 100644 --- a/components/metrics/profiler/profiler_metrics_provider_unittest.cc +++ b/components/metrics/profiler/profiler_metrics_provider_unittest.cc
@@ -145,7 +145,7 @@ // Capture the data and verify that it is as expected. ChromeUserMetricsExtension uma_proto; - profiler_metrics_provider.ProvideGeneralMetrics(&uma_proto); + profiler_metrics_provider.ProvideCurrentSessionData(&uma_proto); // Phase 0 ASSERT_EQ(2, uma_proto.profiler_event_size());
diff --git a/components/metrics/test_metrics_provider.cc b/components/metrics/test_metrics_provider.cc index ff420b8..1c7bb5eaf 100644 --- a/components/metrics/test_metrics_provider.cc +++ b/components/metrics/test_metrics_provider.cc
@@ -16,19 +16,20 @@ on_recording_disabled_called_ = true; } -bool TestMetricsProvider::HasInitialStabilityMetrics() { +bool TestMetricsProvider::HasPreviousSessionData() { has_initial_stability_metrics_called_ = true; return has_initial_stability_metrics_; } -void TestMetricsProvider::ProvideInitialStabilityMetrics( - SystemProfileProto* system_profile_proto) { +void TestMetricsProvider::ProvidePreviousSessionData( + ChromeUserMetricsExtension* uma_proto) { UMA_STABILITY_HISTOGRAM_ENUMERATION("TestMetricsProvider.Initial", 1, 2); provide_initial_stability_metrics_called_ = true; + ProvideCurrentSessionData(nullptr); } -void TestMetricsProvider::ProvideStabilityMetrics( - SystemProfileProto* system_profile_proto) { +void TestMetricsProvider::ProvideCurrentSessionData( + ChromeUserMetricsExtension* uma_proto) { UMA_STABILITY_HISTOGRAM_ENUMERATION("TestMetricsProvider.Regular", 1, 2); provide_stability_metrics_called_ = true; }
diff --git a/components/metrics/test_metrics_provider.h b/components/metrics/test_metrics_provider.h index 44d8f0c..2578954 100644 --- a/components/metrics/test_metrics_provider.h +++ b/components/metrics/test_metrics_provider.h
@@ -26,11 +26,11 @@ // MetricsProvider: void Init() override; void OnRecordingDisabled() override; - bool HasInitialStabilityMetrics() override; - void ProvideInitialStabilityMetrics( - SystemProfileProto* system_profile_proto) override; - void ProvideStabilityMetrics( - SystemProfileProto* system_profile_proto) override; + bool HasPreviousSessionData() override; + void ProvidePreviousSessionData( + ChromeUserMetricsExtension* uma_proto) override; + void ProvideCurrentSessionData( + ChromeUserMetricsExtension* uma_proto) override; void ProvideSystemProfileMetrics( SystemProfileProto* system_profile_proto) override;
diff --git a/components/offline_pages/core/prefetch/BUILD.gn b/components/offline_pages/core/prefetch/BUILD.gn index 63ec7359..2f251a2b 100644 --- a/components/offline_pages/core/prefetch/BUILD.gn +++ b/components/offline_pages/core/prefetch/BUILD.gn
@@ -20,6 +20,7 @@ "get_operation_task.cc", "get_operation_task.h", "offline_metrics_collector.h", + "prefetch_background_task_handler.h", "prefetch_dispatcher.h", "prefetch_dispatcher_impl.cc", "prefetch_dispatcher_impl.h",
diff --git a/components/offline_pages/core/prefetch/prefetch_background_task_handler.h b/components/offline_pages/core/prefetch/prefetch_background_task_handler.h new file mode 100644 index 0000000..6ef3e3f0 --- /dev/null +++ b/components/offline_pages/core/prefetch/prefetch_background_task_handler.h
@@ -0,0 +1,37 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_BACKGROUND_TASK_HANDLER_H_ +#define COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_BACKGROUND_TASK_HANDLER_H_ + +#include <memory> + +namespace offline_pages { + +// Interface for system-specific integrations with background task scheduling. +class PrefetchBackgroundTaskHandler { + public: + virtual ~PrefetchBackgroundTaskHandler() = default; + + // Stops any pending scheduled work. + virtual void CancelBackgroundTask() = 0; + + // Ensures that Chrome will be started using a background task at an + // appropriate time in the future. + virtual void EnsureTaskScheduled() = 0; + + // Requests that the network backoff be increased due to a server response. + virtual void Backoff() = 0; + + // Resets the backoff in case of a successful network attempt. + virtual void ResetBackoff() = 0; + + // Returns the number of seconds beyond the normal scheduling interval that + // the backoff should wait. + virtual int GetAdditionalBackoffSeconds() const = 0; +}; + +} // namespace offline_pages + +#endif // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_BACKGROUND_TASK_HANDLER_H_
diff --git a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc index 04b82549..cca42afe 100644 --- a/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc +++ b/components/offline_pages/core/prefetch/prefetch_dispatcher_impl.cc
@@ -17,6 +17,7 @@ #include "components/offline_pages/core/prefetch/add_unique_urls_task.h" #include "components/offline_pages/core/prefetch/generate_page_bundle_task.h" #include "components/offline_pages/core/prefetch/get_operation_task.h" +#include "components/offline_pages/core/prefetch/prefetch_background_task_handler.h" #include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h" #include "components/offline_pages/core/prefetch/prefetch_network_request_factory.h" #include "components/offline_pages/core/prefetch/prefetch_service.h" @@ -55,7 +56,7 @@ task_queue_.AddTask(std::move(add_task)); // TODO(dewittj): Remove when we have proper scheduling. - BeginBackgroundTask(nullptr); + service_->GetPrefetchBackgroundTaskHandler()->EnsureTaskScheduled(); } void PrefetchDispatcherImpl::RemoveAllUnprocessedPrefetchURLs(
diff --git a/components/offline_pages/core/prefetch/prefetch_service.h b/components/offline_pages/core/prefetch/prefetch_service.h index 974a3dc..fede4d7 100644 --- a/components/offline_pages/core/prefetch/prefetch_service.h +++ b/components/offline_pages/core/prefetch/prefetch_service.h
@@ -10,6 +10,7 @@ namespace offline_pages { class OfflineEventLogger; class OfflineMetricsCollector; +class PrefetchBackgroundTaskHandler; class PrefetchDispatcher; class PrefetchDownloader; class PrefetchGCMHandler; @@ -37,6 +38,7 @@ virtual PrefetchDownloader* GetPrefetchDownloader() = 0; virtual PrefetchStore* GetPrefetchStore() = 0; virtual PrefetchImporter* GetPrefetchImporter() = 0; + virtual PrefetchBackgroundTaskHandler* GetPrefetchBackgroundTaskHandler() = 0; // May be |nullptr| in tests. The PrefetchService does not depend on the // SuggestedArticlesObserver, it merely owns it for lifetime purposes.
diff --git a/components/offline_pages/core/prefetch/prefetch_service_impl.cc b/components/offline_pages/core/prefetch/prefetch_service_impl.cc index f8525be..24f4b53 100644 --- a/components/offline_pages/core/prefetch/prefetch_service_impl.cc +++ b/components/offline_pages/core/prefetch/prefetch_service_impl.cc
@@ -11,6 +11,7 @@ #include "components/offline_pages/core/client_id.h" #include "components/offline_pages/core/client_namespace_constants.h" #include "components/offline_pages/core/prefetch/offline_metrics_collector.h" +#include "components/offline_pages/core/prefetch/prefetch_background_task_handler.h" #include "components/offline_pages/core/prefetch/prefetch_dispatcher.h" #include "components/offline_pages/core/prefetch/prefetch_downloader.h" #include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h" @@ -29,7 +30,9 @@ std::unique_ptr<PrefetchStore> prefetch_store, std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer, std::unique_ptr<PrefetchDownloader> prefetch_downloader, - std::unique_ptr<PrefetchImporter> prefetch_importer) + std::unique_ptr<PrefetchImporter> prefetch_importer, + std::unique_ptr<PrefetchBackgroundTaskHandler> + prefetch_background_task_handler) : offline_metrics_collector_(std::move(offline_metrics_collector)), prefetch_dispatcher_(std::move(dispatcher)), prefetch_gcm_handler_(std::move(gcm_handler)), @@ -37,7 +40,9 @@ prefetch_store_(std::move(prefetch_store)), suggested_articles_observer_(std::move(suggested_articles_observer)), prefetch_downloader_(std::move(prefetch_downloader)), - prefetch_importer_(std::move(prefetch_importer)) { + prefetch_importer_(std::move(prefetch_importer)), + prefetch_background_task_handler_( + std::move(prefetch_background_task_handler)) { prefetch_dispatcher_->SetService(this); prefetch_gcm_handler_->SetService(this); suggested_articles_observer_->SetPrefetchService(this); @@ -86,6 +91,11 @@ return prefetch_importer_.get(); } +PrefetchBackgroundTaskHandler* +PrefetchServiceImpl::GetPrefetchBackgroundTaskHandler() { + return prefetch_background_task_handler_.get(); +} + void PrefetchServiceImpl::Shutdown() { suggested_articles_observer_.reset(); prefetch_downloader_.reset();
diff --git a/components/offline_pages/core/prefetch/prefetch_service_impl.h b/components/offline_pages/core/prefetch/prefetch_service_impl.h index e79935d75..a857ded2 100644 --- a/components/offline_pages/core/prefetch/prefetch_service_impl.h +++ b/components/offline_pages/core/prefetch/prefetch_service_impl.h
@@ -9,6 +9,7 @@ #include "base/macros.h" #include "components/offline_pages/core/offline_event_logger.h" +#include "components/offline_pages/core/prefetch/prefetch_background_task_handler.h" #include "components/offline_pages/core/prefetch/prefetch_service.h" #include "components/offline_pages/core/prefetch/prefetch_types.h" @@ -24,7 +25,9 @@ std::unique_ptr<PrefetchStore> prefetch_store, std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer, std::unique_ptr<PrefetchDownloader> prefetch_downloader, - std::unique_ptr<PrefetchImporter> prefetch_importer); + std::unique_ptr<PrefetchImporter> prefetch_importer, + std::unique_ptr<PrefetchBackgroundTaskHandler> background_task_handler); + ~PrefetchServiceImpl() override; // PrefetchService implementation: @@ -37,6 +40,7 @@ OfflineEventLogger* GetLogger() override; PrefetchDownloader* GetPrefetchDownloader() override; PrefetchImporter* GetPrefetchImporter() override; + PrefetchBackgroundTaskHandler* GetPrefetchBackgroundTaskHandler() override; // KeyedService implementation: void Shutdown() override; @@ -55,6 +59,8 @@ std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer_; std::unique_ptr<PrefetchDownloader> prefetch_downloader_; std::unique_ptr<PrefetchImporter> prefetch_importer_; + std::unique_ptr<PrefetchBackgroundTaskHandler> + prefetch_background_task_handler_; DISALLOW_COPY_AND_ASSIGN(PrefetchServiceImpl); };
diff --git a/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc b/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc index 5aaa109..c5068a8 100644 --- a/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc +++ b/components/offline_pages/core/prefetch/prefetch_service_test_taco.cc
@@ -6,9 +6,11 @@ #include <utility> +#include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/test/test_simple_task_runner.h" #include "components/offline_pages/core/prefetch/offline_metrics_collector.h" +#include "components/offline_pages/core/prefetch/prefetch_background_task_handler.h" #include "components/offline_pages/core/prefetch/prefetch_dispatcher.h" #include "components/offline_pages/core/prefetch/prefetch_downloader.h" #include "components/offline_pages/core/prefetch/prefetch_gcm_handler.h" @@ -26,7 +28,23 @@ namespace offline_pages { namespace { + const version_info::Channel kTestChannel = version_info::Channel::UNKNOWN; + +class StubPrefetchBackgroundTaskHandler : public PrefetchBackgroundTaskHandler { + public: + StubPrefetchBackgroundTaskHandler() = default; + ~StubPrefetchBackgroundTaskHandler() override = default; + void CancelBackgroundTask() override {} + void EnsureTaskScheduled() override {} + void Backoff() override {} + void ResetBackoff() override {} + int GetAdditionalBackoffSeconds() const override { return 0; } + + private: + DISALLOW_COPY_AND_ASSIGN(StubPrefetchBackgroundTaskHandler); +}; + } // namespace PrefetchServiceTestTaco::PrefetchServiceTestTaco() { @@ -46,6 +64,8 @@ // This sets up the testing articles as an empty vector, we can ignore the // result here. This allows us to not create a ContentSuggestionsService. suggested_articles_observer_->GetTestingArticles(); + prefetch_background_task_handler_ = + base::MakeUnique<StubPrefetchBackgroundTaskHandler>(); } PrefetchServiceTestTaco::~PrefetchServiceTestTaco() = default; @@ -98,6 +118,14 @@ prefetch_importer_ = std::move(prefetch_importer); } +void PrefetchServiceTestTaco::SetPrefetchBackgroundTaskHandler( + std::unique_ptr<PrefetchBackgroundTaskHandler> + prefetch_background_task_handler) { + CHECK(!prefetch_service_); + prefetch_background_task_handler_ = + std::move(prefetch_background_task_handler); +} + void PrefetchServiceTestTaco::CreatePrefetchService() { CHECK(metrics_collector_ && dispatcher_ && gcm_handler_ && network_request_factory_ && prefetch_store_sql_ && @@ -107,7 +135,8 @@ std::move(metrics_collector_), std::move(dispatcher_), std::move(gcm_handler_), std::move(network_request_factory_), std::move(prefetch_store_sql_), std::move(suggested_articles_observer_), - std::move(prefetch_downloader_), std::move(prefetch_importer_)); + std::move(prefetch_downloader_), std::move(prefetch_importer_), + std::move(prefetch_background_task_handler_)); } std::unique_ptr<PrefetchService>
diff --git a/components/offline_pages/core/prefetch/prefetch_service_test_taco.h b/components/offline_pages/core/prefetch/prefetch_service_test_taco.h index 3e15c51..ba43c372 100644 --- a/components/offline_pages/core/prefetch/prefetch_service_test_taco.h +++ b/components/offline_pages/core/prefetch/prefetch_service_test_taco.h
@@ -13,6 +13,7 @@ namespace offline_pages { class OfflineMetricsCollector; +class PrefetchBackgroundTaskHandler; class PrefetchDispatcher; class PrefetchDownloader; class PrefetchGCMHandler; @@ -53,6 +54,9 @@ void SetPrefetchDownloader( std::unique_ptr<PrefetchDownloader> prefetch_downloader); void SetPrefetchImporter(std::unique_ptr<PrefetchImporter> prefetch_importer); + void SetPrefetchBackgroundTaskHandler( + std::unique_ptr<PrefetchBackgroundTaskHandler> + prefetch_background_task_handler); // Creates and caches an instance of PrefetchService, using default or // overridden test dependencies. @@ -78,6 +82,8 @@ std::unique_ptr<SuggestedArticlesObserver> suggested_articles_observer_; std::unique_ptr<PrefetchDownloader> prefetch_downloader_; std::unique_ptr<PrefetchImporter> prefetch_importer_; + std::unique_ptr<PrefetchBackgroundTaskHandler> + prefetch_background_task_handler_; std::unique_ptr<PrefetchService> prefetch_service_; };
diff --git a/components/omnibox/browser/omnibox_metrics_provider.cc b/components/omnibox/browser/omnibox_metrics_provider.cc index c6b081a..1848e571 100644 --- a/components/omnibox/browser/omnibox_metrics_provider.cc +++ b/components/omnibox/browser/omnibox_metrics_provider.cc
@@ -98,7 +98,7 @@ subscription_.reset(); } -void OmniboxMetricsProvider::ProvideGeneralMetrics( +void OmniboxMetricsProvider::ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) { uma_proto->mutable_omnibox_event()->Swap( omnibox_events_cache.mutable_omnibox_event());
diff --git a/components/omnibox/browser/omnibox_metrics_provider.h b/components/omnibox/browser/omnibox_metrics_provider.h index 2cbfea7d..a344054 100644 --- a/components/omnibox/browser/omnibox_metrics_provider.h +++ b/components/omnibox/browser/omnibox_metrics_provider.h
@@ -23,7 +23,7 @@ // metrics::MetricsDataProvider: void OnRecordingEnabled() override; void OnRecordingDisabled() override; - void ProvideGeneralMetrics( + void ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) override; private: @@ -39,7 +39,7 @@ subscription_; // Saved cache of generated Omnibox event protos, to be copied into the UMA - // proto when ProvideGeneralMetrics() is called. + // proto when ProvideCurrentSessionData() is called. metrics::ChromeUserMetricsExtension omnibox_events_cache; // Callback passed in from the embedder that returns whether the user is
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc index e9198fba..9220764 100644 --- a/components/payments/content/payment_request.cc +++ b/components/payments/content/payment_request.cc
@@ -140,7 +140,6 @@ return; } - journey_logger_.SetShowCalled(); journey_logger_.SetEventOccurred(JourneyLogger::EVENT_SHOWN); journey_logger_.SetRequestedInformation( spec_->request_shipping(), spec_->request_payer_email(),
diff --git a/components/payments/core/journey_logger.cc b/components/payments/core/journey_logger.cc index ffdd5b837..15a9606 100644 --- a/components/payments/core/journey_logger.cc +++ b/components/payments/core/journey_logger.cc
@@ -72,7 +72,7 @@ ukm_recorder_(ukm_recorder) {} JourneyLogger::~JourneyLogger() { - if (was_show_called_) + if (was_payment_request_triggered_) DCHECK(has_recorded_); } @@ -105,12 +105,11 @@ could_make_payment_ |= value; } -void JourneyLogger::SetShowCalled() { - was_show_called_ = true; -} - void JourneyLogger::SetEventOccurred(Event event) { events_ |= event; + + if (event == EVENT_SHOWN || event == EVENT_SKIPPED_SHOW) + was_payment_request_triggered_ = true; } void JourneyLogger::SetSelectedPaymentMethod( @@ -139,8 +138,8 @@ } void JourneyLogger::SetAborted(AbortReason reason) { - // Don't log abort reasons if the Payment Request was not shown to the user. - if (was_show_called_) { + // Don't log abort reasons if the Payment Request was triggered. + if (was_payment_request_triggered_) { base::UmaHistogramEnumeration("PaymentRequest.CheckoutFunnel.Aborted", reason, ABORT_REASON_MAX); } @@ -171,8 +170,9 @@ RecordUrlKeyedMetrics(completion_status); RecordEventsMetric(completion_status); - // These following metrics only make sense if the UI was shown to the user. - if (was_show_called_) { + // These following metrics only make sense if the Payment Request was + // triggered. + if (was_payment_request_triggered_) { RecordPaymentMethodMetric(); RecordRequestedInformationMetrics(); RecordSectionSpecificStats(completion_status); @@ -298,19 +298,19 @@ if (!was_can_make_payments_used_) return; - int effect_on_show = 0; - if (was_show_called_) - effect_on_show |= CMP_EFFECT_ON_SHOW_DID_SHOW; + int effect_on_trigger = 0; + if (was_payment_request_triggered_) + effect_on_trigger |= CMP_EFFECT_ON_SHOW_DID_SHOW; if (could_make_payment_) - effect_on_show |= CMP_EFFECT_ON_SHOW_COULD_MAKE_PAYMENT; + effect_on_trigger |= CMP_EFFECT_ON_SHOW_COULD_MAKE_PAYMENT; UMA_HISTOGRAM_ENUMERATION("PaymentRequest.CanMakePayment.Used.EffectOnShow", - effect_on_show, CMP_EFFECT_ON_SHOW_MAX); + effect_on_trigger, CMP_EFFECT_ON_SHOW_MAX); } void JourneyLogger::RecordCanMakePaymentEffectOnCompletion( CompletionStatus completion_status) { - if (!was_show_called_) + if (!was_payment_request_triggered_) return; std::string histogram_name = "PaymentRequest.CanMakePayment.";
diff --git a/components/payments/core/journey_logger.h b/components/payments/core/journey_logger.h index 6db0e97..9e99819e3 100644 --- a/components/payments/core/journey_logger.h +++ b/components/payments/core/journey_logger.h
@@ -166,9 +166,6 @@ // return value. void SetCanMakePaymentValue(bool value); - // Records the fact that the Payment Request was shown to the user. - void SetShowCalled(); - // Records that an event occurred. void SetEventOccurred(Event event); @@ -266,7 +263,7 @@ bool has_recorded_ = false; bool was_can_make_payments_used_ = false; bool could_make_payment_ = false; - bool was_show_called_ = false; + bool was_payment_request_triggered_ = false; bool is_incognito_; // Accumulates the many events that have happened during the Payment Request.
diff --git a/components/payments/core/journey_logger_unittest.cc b/components/payments/core/journey_logger_unittest.cc index f6293107..708aac30 100644 --- a/components/payments/core/journey_logger_unittest.cc +++ b/components/payments/core/journey_logger_unittest.cc
@@ -53,7 +53,7 @@ // The merchant does not query CanMakePayment, show the PaymentRequest and the // user aborts it. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); logger.SetRequestedInformation(true, false, false, false); logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER); @@ -83,7 +83,7 @@ // The merchant does not query CanMakePayment, show the PaymentRequest and // there is an abort not initiated by the user. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); logger.SetRequestedInformation(true, false, false, false); logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER); @@ -113,7 +113,7 @@ // The merchant does not query CanMakePayment, show the PaymentRequest and the // user completes it. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); logger.SetRequestedInformation(true, false, false, false); logger.SetCompleted(); @@ -175,7 +175,7 @@ histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"), testing::ContainerEq(base::HistogramTester::CountsMap())); - // The user cannot make payment and the PaymentRequest is not shown. + // The user can make payment and the PaymentRequest is not shown. logger.SetCanMakePaymentValue(true); logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER); @@ -208,8 +208,9 @@ histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"), testing::ContainerEq(base::HistogramTester::CountsMap())); - // The user cannot make payment and the PaymentRequest is not shown. - logger.SetShowCalled(); + // The user cannot make payment, the Payment Request is shown but is aborted + // by the user. + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); logger.SetRequestedInformation(true, false, false, false); logger.SetCanMakePaymentValue(false); logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER); @@ -242,8 +243,8 @@ histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"), testing::ContainerEq(base::HistogramTester::CountsMap())); - // The user cannot make payment and the PaymentRequest is not shown. - logger.SetShowCalled(); + // The user cannot make payment, the Payment Request is shown but is aborted. + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); logger.SetRequestedInformation(true, false, false, false); logger.SetCanMakePaymentValue(false); logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER); @@ -276,8 +277,9 @@ histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"), testing::ContainerEq(base::HistogramTester::CountsMap())); - // The user cannot make payment and the PaymentRequest is not shown. - logger.SetShowCalled(); + // The user cannot make payment, the payment request is shown and is + // completed. + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); logger.SetRequestedInformation(true, false, false, false); logger.SetCanMakePaymentValue(false); logger.SetCompleted(); @@ -311,8 +313,9 @@ histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"), testing::ContainerEq(base::HistogramTester::CountsMap())); - // The user cannot make payment and the PaymentRequest is not shown. - logger.SetShowCalled(); + // The user can make payment, the Payment Request is shown and aborted by the + // user. + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); logger.SetRequestedInformation(true, false, false, false); logger.SetCanMakePaymentValue(true); logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER); @@ -347,8 +350,8 @@ histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"), testing::ContainerEq(base::HistogramTester::CountsMap())); - // The user cannot make payment and the PaymentRequest is not shown. - logger.SetShowCalled(); + // The user can make a payment, the request is shown but the user aborts. + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); logger.SetRequestedInformation(true, false, false, false); logger.SetCanMakePaymentValue(true); logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER); @@ -383,8 +386,9 @@ histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"), testing::ContainerEq(base::HistogramTester::CountsMap())); - // The user cannot make payment and the PaymentRequest is not shown. - logger.SetShowCalled(); + // The user can make a payment, the request is shown and the user completes + // the checkout. + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); logger.SetRequestedInformation(true, false, false, false); logger.SetCanMakePaymentValue(true); logger.SetCompleted(); @@ -419,8 +423,9 @@ histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"), testing::ContainerEq(base::HistogramTester::CountsMap())); - // The user cannot make payment and the PaymentRequest is not shown. - logger.SetShowCalled(); + // The user can make a payment, the request is shown and the user completes + // the checkout. + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); logger.SetRequestedInformation(true, false, false, false); logger.SetCanMakePaymentValue(true); logger.SetCompleted(); @@ -449,7 +454,7 @@ /*has_complete_suggestion=*/false); // Simulate that the Payment Request was shown to the user. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); // Simulate that the user completes the checkout. logger.SetCompleted(); @@ -496,7 +501,7 @@ /*has_complete_suggestion=*/false); // Simulate that the Payment Request was shown to the user. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); // Simulate that the user aborts the checkout. logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER); @@ -543,7 +548,7 @@ /*has_complete_suggestion=*/false); // Simulate that the Payment Request was shown to the user. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); // Simulate that the checkout is aborted. logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER); @@ -591,7 +596,7 @@ /*has_complete_suggestion=*/false); // Simulate that the Payment Request was shown to the user. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); // Simulate that the user completes the checkout. logger.SetCompleted(); @@ -638,7 +643,7 @@ /*has_complete_suggestion=*/false); // Simulate that the Payment Request was shown to the user. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); // Simulate that the user completes the checkout. logger.SetCompleted(); @@ -686,7 +691,7 @@ /*has_complete_suggestion=*/false); // Simulate that the Payment Request was shown to the user. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); // Simulate that the user aborts the checkout. logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER); @@ -734,7 +739,7 @@ /*has_complete_suggestion=*/false); // Simulate that the Payment Request was shown to the user. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); // Simulate that the the checkout is aborted. logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER); @@ -783,7 +788,7 @@ /*has_complete_suggestion=*/false); // Simulate that the Payment Request was shown to the user. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); // Simulate that the user aborts the checkout. logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER); @@ -833,7 +838,7 @@ /*has_complete_suggestion=*/false); // Simulate that the Payment Request was shown to the user. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); // Simulate that the the checkout is aborted. logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER); @@ -885,7 +890,7 @@ /*has_complete_suggestion=*/true); // Simulate that the Payment Request was shown to the user. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); // Simulate that the the checkout is aborted. logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER); @@ -937,7 +942,7 @@ /*has_complete_suggestion=*/true); // Simulate that the Payment Request was shown to the user. - logger.SetShowCalled(); + logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN); // Simulate that the the checkout is aborted. logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER); @@ -978,11 +983,11 @@ /*ukm_recorder=*/nullptr); // Make the two loggers have different data. - logger1.SetShowCalled(); + logger1.SetEventOccurred(JourneyLogger::EVENT_SHOWN); logger1.SetRequestedInformation( /*requested_shipping=*/true, /*requested_email=*/true, /*requested_phone=*/false, /*requested_name=*/false); - logger2.SetShowCalled(); + logger2.SetEventOccurred(JourneyLogger::EVENT_SHOWN); logger2.SetRequestedInformation( /*requested_shipping=*/true, /*requested_email=*/false, /*requested_phone=*/false, /*requested_name=*/false); @@ -1031,6 +1036,9 @@ base::HistogramTester histogram_tester; JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(test_url), /*ukm_recorder=*/&ukm_recorder); + logger.SetRequestedInformation( + /*requested_shipping=*/true, /*requested_email=*/true, + /*requested_phone=*/false, /*requested_name=*/false); // Simulate that the user aborts after being shown the Payment Request and // clicking pay. @@ -1072,6 +1080,9 @@ base::HistogramTester histogram_tester; JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(test_url), /*ukm_recorder=*/&ukm_recorder); + logger.SetRequestedInformation( + /*requested_shipping=*/true, /*requested_email=*/true, + /*requested_phone=*/false, /*requested_name=*/false); // Simulate that the user aborts after being shown the Payment Request. logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
diff --git a/components/policy/core/common/cloud/enterprise_metrics.h b/components/policy/core/common/cloud/enterprise_metrics.h index b61fe35..784b2174 100644 --- a/components/policy/core/common/cloud/enterprise_metrics.h +++ b/components/policy/core/common/cloud/enterprise_metrics.h
@@ -229,6 +229,8 @@ kMetricEnrollmentActiveDirectoryPolicyFetchFailed = 55, // Failed to store DM token into the local state. kMetricEnrollmentStoreDMTokenFailed = 56, + // Failed to get available licenses. + kMetricEnrollmentLicenseRequestFailed = 57, }; // Events related to policy refresh.
diff --git a/components/signin/core/browser/signin_status_metrics_provider.cc b/components/signin/core/browser/signin_status_metrics_provider.cc index 8ead61c..1b499cd3 100644 --- a/components/signin/core/browser/signin_status_metrics_provider.cc +++ b/components/signin/core/browser/signin_status_metrics_provider.cc
@@ -35,7 +35,7 @@ SigninStatusMetricsProvider::~SigninStatusMetricsProvider() {} -void SigninStatusMetricsProvider::ProvideGeneralMetrics( +void SigninStatusMetricsProvider::ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) { RecordSigninStatusHistogram(signin_status()); // After a histogram value is recorded, a new UMA session will be started, so
diff --git a/components/signin/core/browser/signin_status_metrics_provider.h b/components/signin/core/browser/signin_status_metrics_provider.h index ee6f0ad..73cd0a4 100644 --- a/components/signin/core/browser/signin_status_metrics_provider.h +++ b/components/signin/core/browser/signin_status_metrics_provider.h
@@ -33,7 +33,7 @@ ~SigninStatusMetricsProvider() override; // SigninStatusMetricsProviderBase: - void ProvideGeneralMetrics( + void ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) override; // Factory method, creates a new instance of this class.
diff --git a/components/sync/device_info/device_count_metrics_provider.cc b/components/sync/device_info/device_count_metrics_provider.cc index 1ed0506..7d35b58 100644 --- a/components/sync/device_info/device_count_metrics_provider.cc +++ b/components/sync/device_info/device_count_metrics_provider.cc
@@ -27,7 +27,7 @@ return max; } -void DeviceCountMetricsProvider::ProvideGeneralMetrics( +void DeviceCountMetricsProvider::ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) { UMA_HISTOGRAM_SPARSE_SLOWLY("Sync.DeviceCount", std::min(MaxActiveDeviceCount(), 100));
diff --git a/components/sync/device_info/device_count_metrics_provider.h b/components/sync/device_info/device_count_metrics_provider.h index f85d763..f0a7b21 100644 --- a/components/sync/device_info/device_count_metrics_provider.h +++ b/components/sync/device_info/device_count_metrics_provider.h
@@ -35,7 +35,7 @@ ~DeviceCountMetricsProvider() override; // MetricsProvider: - void ProvideGeneralMetrics( + void ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) override; private:
diff --git a/components/sync/device_info/device_count_metrics_provider_unittest.cc b/components/sync/device_info/device_count_metrics_provider_unittest.cc index 384f4c56..0ba2265b 100644 --- a/components/sync/device_info/device_count_metrics_provider_unittest.cc +++ b/components/sync/device_info/device_count_metrics_provider_unittest.cc
@@ -58,7 +58,7 @@ void TestProvider(int expected_device_count) { base::HistogramTester histogram_tester; - metrics_provider_.ProvideGeneralMetrics(nullptr); + metrics_provider_.ProvideCurrentSessionData(nullptr); histogram_tester.ExpectUniqueSample("Sync.DeviceCount", expected_device_count, 1); }
diff --git a/components/test/data/cast_certificate/certificates/create_signatures.py b/components/test/data/cast_certificate/certificates/create_signatures.py new file mode 100755 index 0000000..7e3ad106 --- /dev/null +++ b/components/test/data/cast_certificate/certificates/create_signatures.py
@@ -0,0 +1,77 @@ +#!/usr/bin/python +# Copyright (c) 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. + +""" +Helper code to generate SHA1 and SHA256 signatures given a private key. +Expects CWD to be the scripts directory. +""" + +import base64 +import os +import subprocess +import sys +sys.path += ['../../../../../net/data/verify_certificate_chain_unittest'] + +import common + + +def sign_data(key_path, data_to_sign, digest): + """Returns the signature of |data_to_sign| using the key at |key_path| and + the digest algorithm |digest|. The |digest| parameter should be either + "sha256" or "sha1""" + + data_to_sign_path = 'out/tmp_data_to_sign' + signed_data_path = 'out/tmp_signed_data' + + common.write_string_to_file(data_to_sign, data_to_sign_path) + + subprocess.check_call(['openssl', 'dgst', '-' + digest, + '-sign', key_path, + '-out', signed_data_path, + data_to_sign_path ]) + + signature = common.read_file_to_string(signed_data_path) + + # Delete the temporary files. + os.remove(data_to_sign_path) + os.remove(signed_data_path) + + return signature + + +def create_signed_data(key_path, signed_data_pem_path, cert_path): + # Use some random data as the message. + data_to_sign = os.urandom(256) + + sha1_signature = sign_data(key_path, data_to_sign, 'sha1') + sha256_signature = sign_data(key_path, data_to_sign, 'sha256') + + # Write a final PEM file which incorporates the message, and signatures. + signed_data_pem_data = """ +These signatures were generated using the device certificate key from: + %s + +The data being signed is a bunch of random data. + +-----BEGIN MESSAGE----- +%s +-----END MESSAGE----- + +Signature Algorithm: RSASSA PKCS#1 v1.5 with SHA1 + +-----BEGIN SIGNATURE SHA1----- +%s +-----END SIGNATURE SHA1----- + +Signature Algorithm: RSASSA PKCS#1 v1.5 with SHA256 + +-----BEGIN SIGNATURE SHA256----- +%s +-----END SIGNATURE SHA256----- """ % (cert_path, + base64.b64encode(data_to_sign), + base64.b64encode(sha1_signature), + base64.b64encode(sha256_signature)) + + common.write_string_to_file(signed_data_pem_data, signed_data_pem_path)
diff --git a/components/test/data/cast_certificate/certificates/generate_rsa_device_certs.py b/components/test/data/cast_certificate/certificates/generate_rsa_device_certs.py new file mode 100755 index 0000000..bd7b8ff8 --- /dev/null +++ b/components/test/data/cast_certificate/certificates/generate_rsa_device_certs.py
@@ -0,0 +1,56 @@ +#!/usr/bin/python +# Copyright (c) 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. + +""" +This python script generates test certificate chains for RSA device certs of +varying strengths (1024 and 2048 bits). + +Must be run from the current directory. +""" + +import sys +sys.path += ['../../../../../net/data/verify_certificate_chain_unittest'] + +import common +import create_signatures + + +def generate_rsa_cert(leaf_key_size): + JAN_2015 = '150101120000Z' + JAN_2018 = '180101120000Z' + + # Self-signed root certificate. + root = common.create_self_signed_root_certificate('Root') + root.set_validity_range(JAN_2015, JAN_2018) + + # Intermediate certificate. + intermediate = common.create_intermediate_certificate('Intermediate', root) + intermediate.set_validity_range(JAN_2015, JAN_2018) + + # Leaf certificate. + leaf = common.create_end_entity_certificate( + 'RSA %d Device Cert' % leaf_key_size, intermediate) + leaf.get_extensions().set_property('extendedKeyUsage', 'clientAuth') + device_key_path = common.create_key_path(leaf.name) + leaf.set_key(common.get_or_generate_rsa_key(leaf_key_size, device_key_path)) + leaf.set_validity_range(JAN_2015, JAN_2018) + + chain = [leaf, intermediate, root] + chain_description = """Cast certificate chain where device certificate uses a + %d-bit RSA key""" % leaf_key_size + + # Write the certificate chain. + chain_path ='rsa%d_device_cert.pem' % leaf_key_size + common.write_chain(chain_description, chain, chain_path) + + # Write the the signed data file. + create_signatures.create_signed_data( + device_key_path, + '../signeddata/rsa%d_device_cert_data.pem' % leaf_key_size, + '../certificates/' + chain_path) + + +generate_rsa_cert(1024) +generate_rsa_cert(2048)
diff --git a/components/test/data/cast_certificate/certificates/rsa1024_device_cert.pem b/components/test/data/cast_certificate/certificates/rsa1024_device_cert.pem new file mode 100644 index 0000000..9fe330f --- /dev/null +++ b/components/test/data/cast_certificate/certificates/rsa1024_device_cert.pem
@@ -0,0 +1,260 @@ +[Created by: ./generate_rsa_device_certs.py] + +Cast certificate chain where device certificate uses a + 1024-bit RSA key + +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Intermediate + Validity + Not Before: Jan 1 12:00:00 2015 GMT + Not After : Jan 1 12:00:00 2018 GMT + Subject: CN=RSA 1024 Device Cert + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:ea:1b:53:46:5f:1d:a2:d8:13:e6:e2:3d:4a:64: + 5e:fd:cf:72:63:78:be:3b:76:fe:29:ee:51:cd:86: + 25:72:de:12:8a:e2:fb:10:b8:90:c7:fa:e7:7a:2e: + 9a:4a:b6:7f:ac:d8:d2:fa:b5:c9:13:7f:31:4b:d7: + 24:52:c4:db:cf:75:56:11:0b:e4:1a:16:3a:0a:8f: + b3:52:8d:28:ed:a1:7b:ba:8f:a8:d4:d1:92:b7:bc: + 4e:bc:eb:bc:cd:91:3c:7c:95:48:c5:02:56:8d:79: + 17:74:24:dc:16:04:88:1f:a3:6f:56:5c:ee:0d:91: + 4f:81:e7:36:0d:42:0b:04:81 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + DE:16:32:07:B9:8D:5C:BC:0B:50:36:20:84:D6:71:94:7F:A4:79:76 + X509v3 Authority Key Identifier: + keyid:B7:38:59:7A:66:A9:B7:DE:6C:1E:81:28:0F:1F:AE:1E:A5:BF:44:8F + + Authority Information Access: + CA Issuers - URI:http://url-for-aia/Intermediate.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://url-for-crl/Intermediate.crl + + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Client Authentication + Signature Algorithm: sha256WithRSAEncryption + 2b:ba:14:d8:45:7f:f3:95:1f:c6:4b:0b:03:8e:7c:b4:a8:7e: + 71:f5:05:09:99:b5:b0:1a:13:e1:df:be:cd:9e:06:27:f4:e6: + 61:db:25:67:06:2e:d2:f1:2f:5c:be:2b:fe:ce:d4:a2:c9:a7: + b2:01:6c:f8:a7:b3:94:b8:bc:36:27:c7:ef:4c:7c:aa:d1:b4: + e7:a3:2a:ac:b9:f9:d1:bd:60:d2:ff:fa:4e:3c:0f:23:38:b5: + ab:82:12:ce:c9:7a:26:d8:a2:60:68:a5:d5:5f:27:d4:50:7c: + 48:72:b5:14:77:b6:8d:4b:a9:aa:58:6a:d3:a3:ff:07:29:6b: + 8e:6b:4f:8b:87:38:42:f5:1b:78:36:75:ea:51:ba:7b:75:4a: + c4:f9:e4:f8:2e:e3:ea:dd:b1:e9:1a:f6:02:33:99:1e:65:00: + a0:9a:63:82:dc:05:cd:40:39:2b:58:3c:e4:ff:80:63:79:65: + ce:0c:ce:96:c3:01:64:1c:76:fe:ac:c2:23:32:63:be:bd:eb: + 68:ba:91:34:20:26:b5:66:a8:f8:0c:6f:82:82:31:1e:1d:e3: + 51:a3:be:c9:10:d8:82:13:24:02:ee:c6:75:76:75:57:aa:5d: + 76:d9:5a:44:14:0d:ab:d3:90:93:8f:28:cc:53:6a:74:07:71: + ba:9f:2e:21 +-----BEGIN CERTIFICATE----- +MIIDDTCCAfWgAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl +cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTgwMTAxMTIwMDAwWjAfMR0wGwYD +VQQDDBRSU0EgMTAyNCBEZXZpY2UgQ2VydDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw +gYkCgYEA6htTRl8dotgT5uI9SmRe/c9yY3i+O3b+Ke5RzYYlct4SiuL7ELiQx/rn +ei6aSrZ/rNjS+rXJE38xS9ckUsTbz3VWEQvkGhY6Co+zUo0o7aF7uo+o1NGSt7xO +vOu8zZE8fJVIxQJWjXkXdCTcFgSIH6NvVlzuDZFPgec2DUILBIECAwEAAaOB3zCB +3DAdBgNVHQ4EFgQU3hYyB7mNXLwLUDYghNZxlH+keXYwHwYDVR0jBBgwFoAUtzhZ +emapt95sHoEoDx+uHqW/RI8wPwYIKwYBBQUHAQEEMzAxMC8GCCsGAQUFBzAChiNo +dHRwOi8vdXJsLWZvci1haWEvSW50ZXJtZWRpYXRlLmNlcjA0BgNVHR8ELTArMCmg +J6AlhiNodHRwOi8vdXJsLWZvci1jcmwvSW50ZXJtZWRpYXRlLmNybDAOBgNVHQ8B +Af8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEB +ACu6FNhFf/OVH8ZLCwOOfLSofnH1BQmZtbAaE+Hfvs2eBif05mHbJWcGLtLxL1y+ +K/7O1KLJp7IBbPins5S4vDYnx+9MfKrRtOejKqy5+dG9YNL/+k48DyM4tauCEs7J +eibYomBopdVfJ9RQfEhytRR3to1LqapYatOj/wcpa45rT4uHOEL1G3g2depRunt1 +SsT55Pgu4+rdseka9gIzmR5lAKCaY4LcBc1AOStYPOT/gGN5Zc4MzpbDAWQcdv6s +wiMyY76962i6kTQgJrVmqPgMb4KCMR4d41GjvskQ2IITJALuxnV2dVeqXXbZWkQU +DavTkJOPKMxTanQHcbqfLiE= +-----END CERTIFICATE----- + +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Root + Validity + Not Before: Jan 1 12:00:00 2015 GMT + Not After : Jan 1 12:00:00 2018 GMT + Subject: CN=Intermediate + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ac:8b:55:5c:b8:3c:26:2d:d8:fe:22:70:ef:15: + 38:a8:56:6d:c6:b7:d0:e1:a1:26:81:02:f8:97:f5: + 73:1b:d5:c6:1a:77:9f:ae:85:30:7c:6e:e0:03:a7: + 7f:e3:47:98:c2:d5:c3:6b:c2:cc:0d:0f:80:e1:c3: + 24:41:8f:21:10:cb:fe:ce:04:79:b6:1e:40:83:1a: + dd:44:3a:37:fb:42:8b:52:02:c6:6b:b8:47:58:bc: + 04:fc:8d:e7:fc:70:1f:07:c5:18:db:b2:6b:44:42: + 90:67:10:7f:83:38:47:4b:fd:94:cb:45:15:40:e5: + e8:2a:e1:2b:d0:f5:2e:cc:95:94:10:9c:da:b5:d4: + 47:5b:49:da:fe:c0:89:6e:7d:91:64:22:f9:fa:b5: + f4:ca:77:2e:f2:e6:cb:b3:4f:c6:67:40:f0:b9:ee: + 5c:ac:ed:cd:a6:73:b4:08:d5:76:7a:ae:91:1a:8a: + 5e:0e:e7:25:8c:82:7e:ad:d2:82:79:b1:ca:a3:77: + 1c:8f:71:68:f2:d8:ce:31:4f:db:b8:79:79:ad:2f: + d8:1d:9b:4c:c8:04:a9:de:ad:a2:68:f1:46:e4:f9: + f7:d1:8a:bd:1f:9a:d9:33:92:d3:c4:a4:a7:67:b2: + 5d:66:49:2a:15:8b:71:0d:10:1f:70:82:04:4b:70: + 4f:a5 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + B7:38:59:7A:66:A9:B7:DE:6C:1E:81:28:0F:1F:AE:1E:A5:BF:44:8F + X509v3 Authority Key Identifier: + keyid:94:38:F3:64:93:E5:2C:C9:0D:36:D1:16:21:13:90:2E:3E:E0:FA:94 + + Authority Information Access: + CA Issuers - URI:http://url-for-aia/Root.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://url-for-crl/Root.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE + Signature Algorithm: sha256WithRSAEncryption + 4f:3f:7a:d4:5d:59:e5:0e:d7:48:49:6f:40:ea:ce:95:87:76: + ae:58:fc:59:6b:78:88:33:17:65:79:a9:a1:63:93:6a:1c:5b: + 33:a2:7d:87:50:8b:47:35:3a:47:8e:0e:e9:3d:e8:1c:9c:a6: + ab:68:e2:62:20:09:e2:b3:16:f7:43:9d:e8:61:e8:1d:c0:ac: + 19:0c:ab:dd:06:5c:8c:ad:55:e3:7f:ba:20:ba:7b:1e:78:c7: + 40:78:1f:66:e0:db:a0:3b:cd:73:90:a5:6b:71:97:ef:16:ef: + a3:91:fa:0f:06:3e:4b:23:68:81:fc:25:de:fa:99:0b:f9:b9: + f5:81:15:59:b1:b1:41:42:1c:a5:17:cb:b5:ba:9f:cd:46:fe: + 22:c8:79:a8:95:03:70:e2:54:2c:58:1a:26:a9:6b:25:b4:ed: + 77:62:57:5f:e7:94:98:72:7d:a6:b3:4c:35:4e:54:68:85:34: + d0:f3:b8:f8:c1:36:94:db:8f:99:2e:fd:ea:68:47:e1:47:4f: + bb:0c:7b:dc:85:e1:e6:1c:71:00:5d:15:d3:17:b5:33:dc:a3: + 8b:2a:5e:16:e2:a5:f0:66:c3:5d:e0:f1:b4:59:df:1a:04:65: + 77:cb:0c:95:c2:fb:2d:66:12:0a:e6:49:3b:74:76:48:6f:7e: + 99:b9:02:45 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290 +MB4XDTE1MDEwMTEyMDAwMFoXDTE4MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50 +ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArItVXLg8 +Ji3Y/iJw7xU4qFZtxrfQ4aEmgQL4l/VzG9XGGnefroUwfG7gA6d/40eYwtXDa8LM +DQ+A4cMkQY8hEMv+zgR5th5AgxrdRDo3+0KLUgLGa7hHWLwE/I3n/HAfB8UY27Jr +REKQZxB/gzhHS/2Uy0UVQOXoKuEr0PUuzJWUEJzatdRHW0na/sCJbn2RZCL5+rX0 +yncu8ubLs0/GZ0Dwue5crO3NpnO0CNV2eq6RGopeDucljIJ+rdKCebHKo3ccj3Fo +8tjOMU/buHl5rS/YHZtMyASp3q2iaPFG5Pn30Yq9H5rZM5LTxKSnZ7JdZkkqFYtx +DRAfcIIES3BPpQIDAQABo4HLMIHIMB0GA1UdDgQWBBS3OFl6Zqm33mwegSgPH64e +pb9EjzAfBgNVHSMEGDAWgBSUOPNkk+UsyQ020RYhE5AuPuD6lDA3BggrBgEFBQcB +AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs +BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB +AE8/etRdWeUO10hJb0DqzpWHdq5Y/FlreIgzF2V5qaFjk2ocWzOifYdQi0c1OkeO +Duk96Bycpqto4mIgCeKzFvdDnehh6B3ArBkMq90GXIytVeN/uiC6ex54x0B4H2bg +26A7zXOQpWtxl+8W76OR+g8GPksjaIH8Jd76mQv5ufWBFVmxsUFCHKUXy7W6n81G +/iLIeaiVA3DiVCxYGiapayW07XdiV1/nlJhyfaazTDVOVGiFNNDzuPjBNpTbj5ku +/epoR+FHT7sMe9yF4eYccQBdFdMXtTPco4sqXhbipfBmw13g8bRZ3xoEZXfLDJXC ++y1mEgrmSTt0dkhvfpm5AkU= +-----END CERTIFICATE----- + +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Root + Validity + Not Before: Jan 1 12:00:00 2015 GMT + Not After : Jan 1 12:00:00 2018 GMT + Subject: CN=Root + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:df:f8:ef:9a:5c:9c:67:d8:0e:b6:38:1d:ee:7c: + 41:bb:b2:43:e1:3a:f6:6d:61:1c:68:3b:6d:b7:1d: + 1b:5c:89:52:d7:2c:1a:05:d8:a5:0f:80:cf:ff:c3: + e7:32:d1:75:ca:e0:23:4e:99:96:24:ff:d5:d8:50: + de:ef:a0:88:bb:e4:2b:a1:da:80:85:68:05:4b:04: + b6:29:be:04:8a:b2:fd:5b:c8:4e:6b:9b:ad:81:c0: + 25:05:7a:eb:16:ae:21:7d:1c:2a:74:7d:a9:7a:88: + 64:55:d1:0a:79:45:14:28:ba:25:e1:7f:55:df:22: + ee:4a:15:f4:03:11:8f:8f:b4:e4:8a:6d:4a:7b:93: + 9c:82:ef:f3:f6:ef:f9:10:8e:f5:f0:7b:77:01:40: + da:bd:c2:16:e0:53:7a:2d:c2:d1:bd:69:1b:2c:0a: + 51:c8:63:02:f7:dc:94:6c:19:66:ee:d8:1f:be:41: + 99:b4:4f:18:ca:41:44:43:8c:f1:95:d7:db:2c:df: + 6c:a4:b7:b4:24:26:2a:93:8b:c5:a9:e6:91:c2:d7: + 25:3e:af:bb:c0:b2:4e:3d:38:75:30:07:3b:d7:30: + 5e:b6:91:c1:de:9d:cb:54:ab:00:f6:2a:fb:a4:4a: + 9e:8c:27:08:66:35:37:a7:3e:82:50:5a:24:18:91: + ca:d7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 94:38:F3:64:93:E5:2C:C9:0D:36:D1:16:21:13:90:2E:3E:E0:FA:94 + X509v3 Authority Key Identifier: + keyid:94:38:F3:64:93:E5:2C:C9:0D:36:D1:16:21:13:90:2E:3E:E0:FA:94 + + Authority Information Access: + CA Issuers - URI:http://url-for-aia/Root.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://url-for-crl/Root.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE + Signature Algorithm: sha256WithRSAEncryption + 6b:95:68:f5:58:3c:9d:dc:7d:55:6f:fc:51:58:6b:85:87:c4: + 6a:fd:6d:d3:e3:15:95:61:17:ec:40:67:82:98:a6:d1:36:b3: + c3:6c:71:9f:8f:b8:7c:ad:e8:bf:ed:87:46:06:e8:86:94:50: + 99:db:86:56:5c:8e:45:9b:88:d5:e3:4d:fe:06:19:b3:55:7d: + 25:a9:a9:cc:b2:99:ad:49:31:0b:89:db:79:65:86:ed:c2:d3: + a9:44:68:d3:a4:d7:b0:40:14:d7:ba:f9:d3:b1:b7:57:86:e8: + 06:ab:8d:6c:fb:be:05:2e:fc:6a:44:8f:80:bd:2d:3c:25:18: + 2e:dd:28:82:b7:04:a1:d7:dd:99:37:21:c6:0e:8c:74:79:36: + f9:95:14:6e:11:7f:3e:91:6e:88:79:9b:f5:8a:e7:32:d3:24: + f5:64:60:e2:49:df:14:f0:5b:5a:47:0f:4f:a9:16:89:f2:42: + 04:d2:ab:fa:26:12:9a:4e:fb:c5:5d:49:a5:82:13:e2:71:80: + ca:97:dc:42:9b:72:50:72:0e:06:51:0b:f4:7f:81:43:d2:31: + 9c:5c:a9:b1:06:90:1e:eb:f7:60:4b:a7:e2:c4:1d:cd:b6:53: + e8:9e:11:13:d1:b2:19:25:d8:8c:4b:ac:31:63:13:f5:85:b9: + 59:14:92:92 +-----BEGIN CERTIFICATE----- +MIIDZTCCAk2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290 +MB4XDTE1MDEwMTEyMDAwMFoXDTE4MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN/475pcnGfYDrY4He58 +QbuyQ+E69m1hHGg7bbcdG1yJUtcsGgXYpQ+Az//D5zLRdcrgI06ZliT/1dhQ3u+g +iLvkK6HagIVoBUsEtim+BIqy/VvITmubrYHAJQV66xauIX0cKnR9qXqIZFXRCnlF +FCi6JeF/Vd8i7koV9AMRj4+05IptSnuTnILv8/bv+RCO9fB7dwFA2r3CFuBTei3C +0b1pGywKUchjAvfclGwZZu7YH75BmbRPGMpBREOM8ZXX2yzfbKS3tCQmKpOLxanm +kcLXJT6vu8CyTj04dTAHO9cwXraRwd6dy1SrAPYq+6RKnownCGY1N6c+glBaJBiR +ytcCAwEAAaOByzCByDAdBgNVHQ4EFgQUlDjzZJPlLMkNNtEWIROQLj7g+pQwHwYD +VR0jBBgwFoAUlDjzZJPlLMkNNtEWIROQLj7g+pQwNwYIKwYBBQUHAQEEKzApMCcG +CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw +IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE +AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBrlWj1WDyd +3H1Vb/xRWGuFh8Rq/W3T4xWVYRfsQGeCmKbRNrPDbHGfj7h8rei/7YdGBuiGlFCZ +24ZWXI5Fm4jV403+BhmzVX0lqanMspmtSTELidt5ZYbtwtOpRGjTpNewQBTXuvnT +sbdXhugGq41s+74FLvxqRI+AvS08JRgu3SiCtwSh192ZNyHGDox0eTb5lRRuEX8+ +kW6IeZv1iucy0yT1ZGDiSd8U8FtaRw9PqRaJ8kIE0qv6JhKaTvvFXUmlghPicYDK +l9xCm3JQcg4GUQv0f4FD0jGcXKmxBpAe6/dgS6fixB3NtlPonhET0bIZJdiMS6wx +YxP1hblZFJKS +-----END CERTIFICATE-----
diff --git a/components/test/data/cast_certificate/certificates/rsa2048_device_cert.pem b/components/test/data/cast_certificate/certificates/rsa2048_device_cert.pem new file mode 100644 index 0000000..c63e532b --- /dev/null +++ b/components/test/data/cast_certificate/certificates/rsa2048_device_cert.pem
@@ -0,0 +1,272 @@ +[Created by: ./generate_rsa_device_certs.py] + +Cast certificate chain where device certificate uses a + 2048-bit RSA key + +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Intermediate + Validity + Not Before: Jan 1 12:00:00 2015 GMT + Not After : Jan 1 12:00:00 2018 GMT + Subject: CN=RSA 2048 Device Cert + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:f6:7a:6f:33:8a:c9:b3:9d:db:a8:47:0c:2c:cd: + ff:27:db:37:7c:3f:8f:cf:6f:e4:f9:1c:77:6f:82: + 38:38:76:de:bb:2f:c5:df:47:15:3e:3d:f3:ee:d9: + 38:4e:c1:a6:2f:c4:dd:8e:ce:a9:a4:a6:4b:81:cb: + 0d:b2:89:cb:6f:a6:2c:83:cb:72:c8:26:b3:0d:d1: + b4:a1:66:f3:ca:d3:74:a9:6a:61:14:d7:6d:b0:0a: + 8f:a7:25:b5:d8:6a:0a:75:a3:e8:be:7e:6a:08:5f: + fc:31:46:2a:1d:e0:d3:21:6b:bf:1c:02:e8:b7:0a: + 6c:11:f1:69:50:32:15:59:04:c6:75:fe:2c:e7:c6: + cc:c8:89:7d:f7:16:da:89:16:b0:1f:10:b1:73:d1: + 00:06:c5:a5:e2:34:88:1f:8a:aa:d0:45:03:6e:82: + b5:ad:49:c7:ad:50:42:18:3a:35:35:88:90:68:98: + 02:bd:cc:d7:14:51:fe:86:bb:86:76:67:f2:8e:1f: + f9:3d:e1:e4:a3:dc:bd:b8:b0:6f:b6:14:b8:0b:a8: + 0d:24:cf:df:33:45:5a:0c:52:18:29:f9:94:4a:a2: + 14:c5:b8:90:6c:b6:fc:e1:a1:c5:d3:09:c0:f7:be: + 9b:be:84:e1:82:a9:58:7f:bc:7e:7a:0f:7d:40:e9: + 70:b5 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 1F:F8:92:E0:17:EB:C1:D9:58:25:A3:29:5B:7D:BF:F7:0E:3D:AE:1C + X509v3 Authority Key Identifier: + keyid:66:32:63:74:0F:08:DC:E5:56:9E:6A:77:1F:94:2F:F5:10:F0:87:67 + + Authority Information Access: + CA Issuers - URI:http://url-for-aia/Intermediate.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://url-for-crl/Intermediate.crl + + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Client Authentication + Signature Algorithm: sha256WithRSAEncryption + 5f:19:b5:37:ec:ec:0a:2a:6b:30:28:7c:bd:8a:b4:f9:c0:a3: + 64:ba:ce:4a:51:6b:10:09:dd:90:08:00:b8:8e:2d:37:1d:dd: + 62:3a:13:c9:2a:15:31:6e:37:b5:15:75:2f:15:72:f3:a7:30: + 8c:f1:d1:04:78:d0:6d:cd:68:22:e5:f0:45:7b:52:7d:cf:a5: + aa:cb:a0:b0:ee:e0:a9:fe:c9:f4:3b:df:0f:49:20:bf:c1:79: + 13:65:50:b2:32:b9:4d:14:2a:7f:55:ca:82:32:e5:6e:92:19: + 3e:c9:41:fa:9b:c0:f7:0a:a8:80:8c:ac:7f:45:79:8d:24:d9: + 0f:2d:9c:65:d7:e9:83:8e:61:b1:32:01:44:8f:09:8f:b6:b2: + aa:57:d2:e2:95:67:b7:b4:9b:ae:01:ac:3e:3e:27:d0:97:20: + 02:42:3b:47:0a:bf:a2:e6:10:a8:59:f2:df:26:30:88:29:b5: + a4:81:a2:2a:e8:c0:d8:b8:96:d3:15:88:30:bc:7a:f0:9a:a4: + a1:a6:49:b4:3d:e3:4b:24:9b:f7:52:50:70:74:f3:56:4f:4f: + e3:91:bc:80:28:3b:59:b8:df:e8:23:24:67:3a:c0:c1:29:b9: + c0:4a:ba:4b:41:35:f7:eb:6a:d7:65:b3:13:70:c6:08:74:5a: + ba:2d:b1:bb +-----BEGIN CERTIFICATE----- +MIIDkTCCAnmgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl +cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTgwMTAxMTIwMDAwWjAfMR0wGwYD +VQQDDBRSU0EgMjA0OCBEZXZpY2UgQ2VydDCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAPZ6bzOKybOd26hHDCzN/yfbN3w/j89v5Pkcd2+CODh23rsvxd9H +FT498+7ZOE7Bpi/E3Y7OqaSmS4HLDbKJy2+mLIPLcsgmsw3RtKFm88rTdKlqYRTX +bbAKj6cltdhqCnWj6L5+aghf/DFGKh3g0yFrvxwC6LcKbBHxaVAyFVkExnX+LOfG +zMiJffcW2okWsB8QsXPRAAbFpeI0iB+KqtBFA26Cta1Jx61QQhg6NTWIkGiYAr3M +1xRR/oa7hnZn8o4f+T3h5KPcvbiwb7YUuAuoDSTP3zNFWgxSGCn5lEqiFMW4kGy2 +/OGhxdMJwPe+m76E4YKpWH+8fnoPfUDpcLUCAwEAAaOB3zCB3DAdBgNVHQ4EFgQU +H/iS4BfrwdlYJaMpW32/9w49rhwwHwYDVR0jBBgwFoAUZjJjdA8I3OVWnmp3H5Qv +9RDwh2cwPwYIKwYBBQUHAQEEMzAxMC8GCCsGAQUFBzAChiNodHRwOi8vdXJsLWZv +ci1haWEvSW50ZXJtZWRpYXRlLmNlcjA0BgNVHR8ELTArMCmgJ6AlhiNodHRwOi8v +dXJsLWZvci1jcmwvSW50ZXJtZWRpYXRlLmNybDAOBgNVHQ8BAf8EBAMCBaAwEwYD +VR0lBAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBAF8ZtTfs7AoqazAo +fL2KtPnAo2S6zkpRaxAJ3ZAIALiOLTcd3WI6E8kqFTFuN7UVdS8VcvOnMIzx0QR4 +0G3NaCLl8EV7Un3PparLoLDu4Kn+yfQ73w9JIL/BeRNlULIyuU0UKn9VyoIy5W6S +GT7JQfqbwPcKqICMrH9FeY0k2Q8tnGXX6YOOYbEyAUSPCY+2sqpX0uKVZ7e0m64B +rD4+J9CXIAJCO0cKv6LmEKhZ8t8mMIgptaSBoirowNi4ltMViDC8evCapKGmSbQ9 +40skm/dSUHB081ZPT+ORvIAoO1m43+gjJGc6wMEpucBKuktBNffratdlsxNwxgh0 +Wrotsbs= +-----END CERTIFICATE----- + +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4 (0x4) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Root + Validity + Not Before: Jan 1 12:00:00 2015 GMT + Not After : Jan 1 12:00:00 2018 GMT + Subject: CN=Intermediate + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:da:bd:1b:16:83:47:79:63:b4:b1:d2:d8:a7:e8: + d2:bc:4d:20:70:fd:e2:aa:d3:c8:12:ea:b4:b1:8d: + df:ed:97:70:b7:96:41:80:c0:52:7c:62:49:93:3d: + 57:43:f2:c0:f9:17:cc:11:28:ee:49:3a:e7:56:54: + 3b:08:ee:c8:77:9a:85:be:9a:28:d1:ba:69:3f:57: + 5b:f1:6e:40:d0:78:22:b5:a4:41:b4:8a:00:2e:b1: + 83:93:fc:59:dd:39:e8:77:dd:0c:a3:9a:d1:ec:c2: + bc:cd:1b:ec:14:96:45:e9:33:de:e6:53:f6:3a:80: + 66:8e:b7:f2:78:7f:5a:e5:57:3e:cc:a9:12:4b:bf: + b6:02:30:85:1a:b7:65:6e:57:32:90:bd:64:13:c4: + 43:9e:4a:2f:05:3d:c5:61:bf:2f:d0:56:c9:75:8f: + 36:95:42:b4:3a:97:38:a8:41:26:34:cc:ec:41:97: + 52:10:f9:de:2c:21:b2:52:5d:28:07:75:3a:23:2b: + 4e:01:38:fd:f5:2d:15:f2:8c:f4:32:9e:ad:b1:da: + 2d:3c:82:68:7b:0c:9b:f9:c1:38:4f:81:6f:29:e0: + ae:a7:8d:6f:69:82:24:4a:24:74:a4:fa:09:8a:bf: + 16:5d:bc:f3:a8:6c:ec:9b:ed:1d:75:a9:f2:c4:ed: + ad:87 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 66:32:63:74:0F:08:DC:E5:56:9E:6A:77:1F:94:2F:F5:10:F0:87:67 + X509v3 Authority Key Identifier: + keyid:7E:4B:E4:D7:F7:28:D4:4D:56:94:D8:9F:0A:C2:A7:CC:AD:4A:BD:C1 + + Authority Information Access: + CA Issuers - URI:http://url-for-aia/Root.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://url-for-crl/Root.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE + Signature Algorithm: sha256WithRSAEncryption + 58:82:68:4d:2f:e7:a1:f5:cb:16:b6:bb:52:4a:0b:34:61:ca: + 5b:d1:57:fe:11:8b:86:54:5f:9a:92:bd:31:f9:7a:16:a3:3d: + 81:7f:c9:06:63:92:5d:91:29:a7:5b:13:22:fb:99:89:78:15: + 8f:bf:67:85:ce:06:98:e7:79:03:99:fb:06:4d:88:26:c0:28: + c5:46:91:92:94:1a:7e:2b:c2:4b:5a:b3:0c:d9:df:25:4d:ae: + b8:b6:10:5e:54:dc:26:60:6c:17:99:e7:6c:66:d2:cb:f3:a9: + c8:68:5d:5b:d4:71:b0:ea:35:c9:03:3f:32:d4:e2:1b:bc:05: + 36:05:62:0d:75:95:db:17:a2:a5:0b:3e:4d:b9:bf:bb:22:e0: + 4c:64:83:29:31:31:0a:e7:5a:a2:8b:07:30:1c:53:3a:f7:7d: + 12:1d:96:85:b8:f5:e2:a9:fa:36:ad:fb:5d:43:63:da:bc:68: + 01:cb:12:ff:5a:07:1d:72:4f:ad:56:f4:70:d6:44:de:80:cc: + e5:17:77:0b:94:1b:0f:f2:5f:2a:1a:97:c0:9c:7a:61:e2:43: + 80:86:5a:62:6b:3b:2d:f5:9f:c2:a1:52:33:8e:7a:33:c2:c9: + 79:21:a2:ec:38:25:8f:44:58:c7:2e:b5:29:ae:8b:94:ed:72: + 42:22:aa:32 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBBDANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290 +MB4XDTE1MDEwMTEyMDAwMFoXDTE4MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50 +ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2r0bFoNH +eWO0sdLYp+jSvE0gcP3iqtPIEuq0sY3f7Zdwt5ZBgMBSfGJJkz1XQ/LA+RfMESju +STrnVlQ7CO7Id5qFvpoo0bppP1db8W5A0HgitaRBtIoALrGDk/xZ3Tnod90Mo5rR +7MK8zRvsFJZF6TPe5lP2OoBmjrfyeH9a5Vc+zKkSS7+2AjCFGrdlblcykL1kE8RD +nkovBT3FYb8v0FbJdY82lUK0Opc4qEEmNMzsQZdSEPneLCGyUl0oB3U6IytOATj9 +9S0V8oz0Mp6tsdotPIJoewyb+cE4T4FvKeCup41vaYIkSiR0pPoJir8WXbzzqGzs +m+0ddanyxO2thwIDAQABo4HLMIHIMB0GA1UdDgQWBBRmMmN0Dwjc5VaeancflC/1 +EPCHZzAfBgNVHSMEGDAWgBR+S+TX9yjUTVaU2J8KwqfMrUq9wTA3BggrBgEFBQcB +AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs +BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB +AFiCaE0v56H1yxa2u1JKCzRhylvRV/4Ri4ZUX5qSvTH5ehajPYF/yQZjkl2RKadb +EyL7mYl4FY+/Z4XOBpjneQOZ+wZNiCbAKMVGkZKUGn4rwktaswzZ3yVNrri2EF5U +3CZgbBeZ52xm0svzqchoXVvUcbDqNckDPzLU4hu8BTYFYg11ldsXoqULPk25v7si +4ExkgykxMQrnWqKLBzAcUzr3fRIdloW49eKp+jat+11DY9q8aAHLEv9aBx1yT61W +9HDWRN6AzOUXdwuUGw/yXyoal8CcemHiQ4CGWmJrOy31n8KhUjOOejPCyXkhouw4 +JY9EWMcutSmui5TtckIiqjI= +-----END CERTIFICATE----- + +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Root + Validity + Not Before: Jan 1 12:00:00 2015 GMT + Not After : Jan 1 12:00:00 2018 GMT + Subject: CN=Root + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a8:ba:56:64:50:cb:eb:c8:c5:7a:6a:dc:72:24: + b9:09:22:03:5f:ca:55:51:15:eb:16:a9:4d:6c:a9: + a0:3d:26:88:5a:4c:0f:9c:b1:49:29:3b:ee:ad:c5: + 8c:11:9f:c3:75:44:e9:d1:25:80:02:ec:98:c5:0e: + 42:40:91:c3:85:cf:da:e2:98:0f:1d:66:87:c0:4a: + 46:4f:c5:ec:ac:27:82:2f:80:cb:78:e7:a7:a1:06: + c5:88:d3:b8:dd:82:d4:c7:7c:52:3c:01:7a:d7:ae: + cc:bd:0f:00:4b:8f:23:1c:c9:ee:d8:e8:b3:b7:c5: + c3:23:dd:85:2f:e9:aa:4d:b7:ef:5c:58:18:59:21: + 41:5c:40:fe:77:d9:0f:cd:5c:c8:2d:74:a2:98:c6: + 13:d4:a1:54:3a:a7:2a:e0:42:b8:4c:89:5c:b4:5c: + 34:7e:61:de:b2:5e:3f:1f:f2:5c:65:7c:53:14:94: + 76:33:36:c0:cf:16:7d:6d:52:71:20:6a:9a:7a:3d: + 0f:3d:12:ea:94:8b:eb:b0:07:61:d8:13:92:3e:e6: + ac:b8:0c:02:92:0b:a0:ca:9a:7b:52:08:05:cc:db: + 1b:40:3e:b1:30:b2:7d:18:f5:2e:e0:f2:69:df:b6: + a2:3d:94:04:47:3e:f4:63:b4:07:a1:94:30:43:02: + 7f:43 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 7E:4B:E4:D7:F7:28:D4:4D:56:94:D8:9F:0A:C2:A7:CC:AD:4A:BD:C1 + X509v3 Authority Key Identifier: + keyid:7E:4B:E4:D7:F7:28:D4:4D:56:94:D8:9F:0A:C2:A7:CC:AD:4A:BD:C1 + + Authority Information Access: + CA Issuers - URI:http://url-for-aia/Root.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://url-for-crl/Root.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE + Signature Algorithm: sha256WithRSAEncryption + 6b:18:91:92:32:ce:f7:0e:7d:d1:87:ce:95:80:b4:6d:c5:be: + e3:83:11:be:71:b5:0c:c0:b6:97:c8:e7:10:70:02:8a:2c:09: + 79:6a:25:42:03:ef:6b:88:07:81:8f:0f:a2:d2:d8:57:39:7e: + a8:8c:de:33:b3:3d:cf:dc:b9:26:ad:82:95:83:67:3e:a2:8b: + 93:43:1d:39:9a:3e:fb:1d:81:84:e9:bc:33:a7:80:13:97:fc: + a7:00:13:f4:44:aa:2c:f4:a6:1b:da:8c:fe:9d:e6:2e:04:b1: + 4d:68:cc:c6:b9:f3:52:6e:dd:ce:6a:86:ee:a1:fa:60:c7:fe: + e0:ce:1f:14:80:dd:02:e8:08:9c:b4:d5:e3:3a:5d:5c:44:c5: + 6e:cc:99:b0:27:94:c7:56:3a:60:d9:c3:bd:e7:4c:4a:6c:0f: + 77:53:67:5f:f6:bb:3b:b9:13:dc:4f:97:ee:86:8c:17:1b:8b: + d7:6b:2a:24:40:82:4a:5f:d6:96:cd:45:2a:c8:fc:c6:93:22: + 5d:c7:e6:bc:76:9f:1f:5a:1c:4a:64:6d:10:d9:61:28:11:11: + 7d:3b:74:2a:a9:af:77:5f:b4:02:5b:b7:18:c6:3c:cf:70:a1: + a2:89:5a:5f:6f:fb:44:70:ab:29:6a:66:07:8a:85:3e:5a:32: + 50:70:5c:59 +-----BEGIN CERTIFICATE----- +MIIDZTCCAk2gAwIBAgIBAzANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290 +MB4XDTE1MDEwMTEyMDAwMFoXDTE4MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKi6VmRQy+vIxXpq3HIk +uQkiA1/KVVEV6xapTWypoD0miFpMD5yxSSk77q3FjBGfw3VE6dElgALsmMUOQkCR +w4XP2uKYDx1mh8BKRk/F7Kwngi+Ay3jnp6EGxYjTuN2C1Md8UjwBeteuzL0PAEuP +IxzJ7tjos7fFwyPdhS/pqk2371xYGFkhQVxA/nfZD81cyC10opjGE9ShVDqnKuBC +uEyJXLRcNH5h3rJePx/yXGV8UxSUdjM2wM8WfW1ScSBqmno9Dz0S6pSL67AHYdgT +kj7mrLgMApILoMqae1IIBczbG0A+sTCyfRj1LuDyad+2oj2UBEc+9GO0B6GUMEMC +f0MCAwEAAaOByzCByDAdBgNVHQ4EFgQUfkvk1/co1E1WlNifCsKnzK1KvcEwHwYD +VR0jBBgwFoAUfkvk1/co1E1WlNifCsKnzK1KvcEwNwYIKwYBBQUHAQEEKzApMCcG +CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw +IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE +AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBrGJGSMs73 +Dn3Rh86VgLRtxb7jgxG+cbUMwLaXyOcQcAKKLAl5aiVCA+9riAeBjw+i0thXOX6o +jN4zsz3P3LkmrYKVg2c+oouTQx05mj77HYGE6bwzp4ATl/ynABP0RKos9KYb2oz+ +neYuBLFNaMzGufNSbt3Oaobuofpgx/7gzh8UgN0C6AictNXjOl1cRMVuzJmwJ5TH +Vjpg2cO950xKbA93U2df9rs7uRPcT5fuhowXG4vXayokQIJKX9aWzUUqyPzGkyJd +x+a8dp8fWhxKZG0Q2WEoERF9O3Qqqa93X7QCW7cYxjzPcKGiiVpfb/tEcKspamYH +ioU+WjJQcFxZ +-----END CERTIFICATE-----
diff --git a/components/test/data/cast_certificate/signeddata/rsa1024_device_cert_data.pem b/components/test/data/cast_certificate/signeddata/rsa1024_device_cert_data.pem new file mode 100644 index 0000000..acc8df6 --- /dev/null +++ b/components/test/data/cast_certificate/signeddata/rsa1024_device_cert_data.pem
@@ -0,0 +1,21 @@ + +These signatures were generated using the device certificate key from: + ../certificates/rsa1024_device_cert.pem + +The data being signed is a bunch of random data. + +-----BEGIN MESSAGE----- +Ch1+QBxhiVdsgHR3Ous2DiwXe/Pi3YDsKLh31LcmSrgm0uc5FFEjtPKXK9XIN/YD3qXmIaUPQ1PnucDOypOXgrJaj8lKKjSzjssEXAvC6TBaizWpsnTfZPi9/vVbaGyPJEjCvYoq2fuoSdnMaKUtQlEg6NZ4McKo33ptDRHOSuBLVxiSGQV7UXcH1IveLCTTWZFaaby8ZyztKIY8mQh+zXyrDyBQifFA2uHNKv96GYstyhINpS22Ff2lU1fVtZzFt8OEY0ckI7s6n/P72ZRCcCkbgiKHYt5G7C4BbQSbPLd3ALsXqfjDHkkxaFbFTZHeJWBTXoVXmJJRIbzEciyCCg== +-----END MESSAGE----- + +Signature Algorithm: RSASSA PKCS#1 v1.5 with SHA1 + +-----BEGIN SIGNATURE SHA1----- +niG0uROHa8H5xsZde/h81IDVva3GbZMeWpv5O8K8vZWEso6qMEmaPqSAzsnlHQmPqugU1H+PVJeTrhtvtCYcmkU1LYbuBbu6nqI/7nOma8AsLIt1JaFU5O/14vemMGXivXvkSqDvyH+LiGn6XsUWxLdnSdU4S3FfZqTCO2EMB4c= +-----END SIGNATURE SHA1----- + +Signature Algorithm: RSASSA PKCS#1 v1.5 with SHA256 + +-----BEGIN SIGNATURE SHA256----- +lD68BNx46OvTEHrW36s/075tEBmJJWpYeA/GGbXf3LqjrONpl3IrVQqzgCbJCzbq/V8fJS7kwHuhZneKeEO3ndu4gBCQIVpLqlc9GIuYU7dkq2YMGwIXR2SHLfua1sC6rVXZez1DQoLJyf8TCKU5SUOQJhjNMJiTkesj+Bihdqk= +-----END SIGNATURE SHA256-----
diff --git a/components/test/data/cast_certificate/signeddata/rsa2048_device_cert_data.pem b/components/test/data/cast_certificate/signeddata/rsa2048_device_cert_data.pem new file mode 100644 index 0000000..7c5f044 --- /dev/null +++ b/components/test/data/cast_certificate/signeddata/rsa2048_device_cert_data.pem
@@ -0,0 +1,21 @@ + +These signatures were generated using the device certificate key from: + ../certificates/rsa2048_device_cert.pem + +The data being signed is a bunch of random data. + +-----BEGIN MESSAGE----- +q0V/60EAZguRPs5f2LHzZAr2R5huGkCxiqpkYnPiP5sUHh/p4hNqq1iMDmTAPhC5QHNe9bZNtddEQ4KrI3nesDDgXDtNVRadNtS3QDYYR9Bgwi2+vp3F7ZqhuzLYUci5IuEAnWueWnimGlvGTnMdxYeBJumnLqpAKJ5+s0/IsL2/DQHT4nZjWbxDQgEt77XKL5yTmq8X1idV3e/80rDdgNwviEAJKaJXbdUD7lxW96IHNCfgExq3eZoH0EWQCks8HyzQlymKYMGAx/M7BNfeLET1zX+Nx80YYQj8mpNTfABqRgb7m9OtN3YSa4ZIqcbDLL2I3Jqxlp7oKp4xc4SBRg== +-----END MESSAGE----- + +Signature Algorithm: RSASSA PKCS#1 v1.5 with SHA1 + +-----BEGIN SIGNATURE SHA1----- +y+OUQTua2lFBmkPj7HyoCR8PfhdRcGe/Fv/l+uw1ZllnCQ5iBpstfgCWZi1PQcRQ0ut+eDhMDni3UZR+ORAy/e8SgeeiVdHOucpi/g9TMBl3W3Gkd3wS44cUHI0N+DvsoPdQcZsMpgH3n+Q6oa2AXAlC2vgl7pt/8T30XV4/zzkWeeaguqZaenEVCDmly9Gflm6KVYWUotry60zaII0+5r3PDureZpWWaJidIWiR2nPCL7dujMrShjPfJ+Qk8KdVDiYLjTzmerqxehj+0fY31c/25U6f8jb7VP/9Key/SrJP/Ty596Al9NOdRsO6kZR6UyBGn9C4xz3ARlwWXE7a3w== +-----END SIGNATURE SHA1----- + +Signature Algorithm: RSASSA PKCS#1 v1.5 with SHA256 + +-----BEGIN SIGNATURE SHA256----- +Qcr5c2gILxMnDZK+Lhs1lQnuqa6Yzf63doCsjoPvCynFXNqNVr9MQaXQDvNDoyq1S6BRW5WyL6kaCC3L0M8OW3TNGXYe0Mt9iyoXt+Eswh6X8N8pW2g8GB7sILyZ1iOYvVCmws04YyCCfK/RTcIFiQb0pfdAUIVcQUbEztasZSLi8tN0+jGAMVehVTVn1lch0c3bMxLENoC4hBxXXYeM5ypYAlazzF8RLBBsTg7NjNTCPuAfCECp8U9BEUUqRyPm8ZV3yaw3Obe1PDTcB+8yMZSA+tINtf6DtgA3zr+cvq1BMmRebM3Bth6CWoyNG1uN2CD11XjkTHPlBk7hAv6gYQ== +-----END SIGNATURE SHA256-----
diff --git a/components/update_client/component_unpacker_unittest.cc b/components/update_client/component_unpacker_unittest.cc index a8b2da4..65eca6d 100644 --- a/components/update_client/component_unpacker_unittest.cc +++ b/components/update_client/component_unpacker_unittest.cc
@@ -12,6 +12,7 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/macros.h" +#include "base/memory/ref_counted.h" #include "base/path_service.h" #include "base/run_loop.h" #include "base/task_scheduler/post_task.h" @@ -78,24 +79,14 @@ const scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_ = base::ThreadTaskRunnerHandle::Get(); base::RunLoop runloop_; - base::Closure quit_closure_; - - scoped_refptr<update_client::TestConfigurator> config_; + const base::Closure quit_closure_ = runloop_.QuitClosure(); ComponentUnpacker::Result result_; }; -ComponentUnpackerTest::ComponentUnpackerTest() - : scoped_task_environment_( - base::test::ScopedTaskEnvironment::MainThreadType::UI) { - quit_closure_ = runloop_.QuitClosure(); +ComponentUnpackerTest::ComponentUnpackerTest() = default; - config_ = new TestConfigurator( - base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()}), - base::ThreadTaskRunnerHandle::Get()); -} - -ComponentUnpackerTest::~ComponentUnpackerTest() {} +ComponentUnpackerTest::~ComponentUnpackerTest() = default; void ComponentUnpackerTest::RunThreads() { runloop_.Run();
diff --git a/components/update_client/configurator.h b/components/update_client/configurator.h index ddb5b5d..44a2d04 100644 --- a/components/update_client/configurator.h +++ b/components/update_client/configurator.h
@@ -15,7 +15,6 @@ class PrefService; namespace base { -class SequencedTaskRunner; class Version; } @@ -116,10 +115,6 @@ // True if signing of update checks is enabled. virtual bool EnabledCupSigning() const = 0; - // Gets a task runner to a blocking pool of threads suitable for worker jobs. - virtual scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() - const = 0; - // Returns a PrefService that the update_client can use to store persistent // update information. The PrefService must outlive the entire update_client, // and be safe to access from the thread the update_client is constructed
diff --git a/components/update_client/ping_manager_unittest.cc b/components/update_client/ping_manager_unittest.cc index 503a4d59..b883d376 100644 --- a/components/update_client/ping_manager_unittest.cc +++ b/components/update_client/ping_manager_unittest.cc
@@ -50,9 +50,8 @@ PingManagerTest::PingManagerTest() {} void PingManagerTest::SetUp() { - config_ = new TestConfigurator(base::ThreadTaskRunnerHandle::Get(), - base::ThreadTaskRunnerHandle::Get()); - ping_manager_.reset(new PingManager(config_)); + config_ = base::MakeRefCounted<TestConfigurator>(); + ping_manager_ = base::MakeUnique<PingManager>(config_); } void PingManagerTest::TearDown() {
diff --git a/components/update_client/request_sender_unittest.cc b/components/update_client/request_sender_unittest.cc index 82a07c2..ec79ea1d 100644 --- a/components/update_client/request_sender_unittest.cc +++ b/components/update_client/request_sender_unittest.cc
@@ -84,8 +84,7 @@ RequestSenderTest::~RequestSenderTest() {} void RequestSenderTest::SetUp() { - config_ = base::MakeRefCounted<TestConfigurator>( - base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get()); + config_ = base::MakeRefCounted<TestConfigurator>(); interceptor_factory_ = base::MakeUnique<InterceptorFactory>(base::ThreadTaskRunnerHandle::Get()); post_interceptor_1_ =
diff --git a/components/update_client/test_configurator.cc b/components/update_client/test_configurator.cc index ade005de..a61c093 100644 --- a/components/update_client/test_configurator.cc +++ b/components/update_client/test_configurator.cc
@@ -6,8 +6,7 @@ #include <utility> -#include "base/sequenced_task_runner.h" -#include "base/single_thread_task_runner.h" +#include "base/threading/thread_task_runner_handle.h" #include "base/version.h" #include "components/prefs/pref_service.h" #include "components/update_client/out_of_process_patcher.h" @@ -27,16 +26,14 @@ } // namespace -TestConfigurator::TestConfigurator( - const scoped_refptr<base::SequencedTaskRunner>& worker_task_runner, - const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner) - : worker_task_runner_(worker_task_runner), - brand_("TEST"), +TestConfigurator::TestConfigurator() + : brand_("TEST"), initial_time_(0), ondemand_time_(0), enabled_cup_signing_(false), enabled_component_updates_(true), - context_(new net::TestURLRequestContextGetter(network_task_runner)) {} + context_(base::MakeRefCounted<net::TestURLRequestContextGetter>( + base::ThreadTaskRunnerHandle::Get())) {} TestConfigurator::~TestConfigurator() { } @@ -163,12 +160,6 @@ ping_url_ = url; } -scoped_refptr<base::SequencedTaskRunner> -TestConfigurator::GetSequencedTaskRunner() const { - DCHECK(worker_task_runner_.get()); - return worker_task_runner_; -} - PrefService* TestConfigurator::GetPrefService() const { return nullptr; }
diff --git a/components/update_client/test_configurator.h b/components/update_client/test_configurator.h index fd0bc2f3..01a9674 100644 --- a/components/update_client/test_configurator.h +++ b/components/update_client/test_configurator.h
@@ -18,11 +18,6 @@ class PrefService; -namespace base { -class SequencedTaskRunner; -class SingleThreadTaskRunner; -} // namespace base - namespace net { class TestURLRequestContextGetter; class URLRequestContextGetter; @@ -61,9 +56,7 @@ class TestConfigurator : public Configurator { public: - TestConfigurator( - const scoped_refptr<base::SequencedTaskRunner>& worker_task_runner, - const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner); + TestConfigurator(); // Overrrides for Configurator. int InitialDelay() const override; @@ -86,8 +79,6 @@ bool EnabledComponentUpdates() const override; bool EnabledBackgroundDownloader() const override; bool EnabledCupSigning() const override; - scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() - const override; PrefService* GetPrefService() const override; bool IsPerUserInstall() const override; std::vector<uint8_t> GetRunActionKeyHash() const override; @@ -103,11 +94,8 @@ private: friend class base::RefCountedThreadSafe<TestConfigurator>; - ~TestConfigurator() override; - scoped_refptr<base::SequencedTaskRunner> worker_task_runner_; - std::string brand_; int initial_time_; int ondemand_time_;
diff --git a/components/update_client/update_checker_unittest.cc b/components/update_client/update_checker_unittest.cc index 033118b..ba63c7c4 100644 --- a/components/update_client/update_checker_unittest.cc +++ b/components/update_client/update_checker_unittest.cc
@@ -99,8 +99,7 @@ } void UpdateCheckerTest::SetUp() { - config_ = base::MakeRefCounted<TestConfigurator>( - base::ThreadTaskRunnerHandle::Get(), base::ThreadTaskRunnerHandle::Get()); + config_ = base::MakeRefCounted<TestConfigurator>(); pref_ = base::MakeUnique<TestingPrefServiceSimple>(); PersistedData::RegisterPrefs(pref_->registry()); metadata_ = base::MakeUnique<PersistedData>(pref_.get());
diff --git a/components/update_client/update_client_unittest.cc b/components/update_client/update_client_unittest.cc index fca7137..05b872f 100644 --- a/components/update_client/update_client_unittest.cc +++ b/components/update_client/update_client_unittest.cc
@@ -172,10 +172,12 @@ base::test::ScopedTaskEnvironment scoped_task_environment_; base::RunLoop runloop_; - base::Closure quit_closure_; + const base::Closure quit_closure_ = runloop_.QuitClosure(); - scoped_refptr<update_client::TestConfigurator> config_; - std::unique_ptr<TestingPrefServiceSimple> pref_; + scoped_refptr<update_client::TestConfigurator> config_ = + base::MakeRefCounted<TestConfigurator>(); + std::unique_ptr<TestingPrefServiceSimple> pref_ = + base::MakeUnique<TestingPrefServiceSimple>(); std::unique_ptr<update_client::PersistedData> metadata_; DISALLOW_COPY_AND_ASSIGN(UpdateClientTest); @@ -183,15 +185,7 @@ constexpr int UpdateClientTest::kNumWorkerThreads_; -UpdateClientTest::UpdateClientTest() - : scoped_task_environment_( - base::test::ScopedTaskEnvironment::MainThreadType::UI), - pref_(base::MakeUnique<TestingPrefServiceSimple>()) { - quit_closure_ = runloop_.QuitClosure(); - - config_ = base::MakeRefCounted<TestConfigurator>( - base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()}), - base::ThreadTaskRunnerHandle::Get()); +UpdateClientTest::UpdateClientTest() { PersistedData::RegisterPrefs(pref_->registry()); metadata_ = base::MakeUnique<PersistedData>(pref_.get()); }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 00faa958..ac490033 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -299,6 +299,10 @@ "android/scoped_surface_request_manager.h", "android/string_message_codec.cc", "android/string_message_codec.h", + "android/text_suggestion_host_android.cc", + "android/text_suggestion_host_android.h", + "android/text_suggestion_host_mojo_impl_android.cc", + "android/text_suggestion_host_mojo_impl_android.h", "android/url_request_content_job.cc", "android/url_request_content_job.h", "appcache/appcache.cc",
diff --git a/content/browser/DEPS b/content/browser/DEPS index 27702189..ba32d86 100644 --- a/content/browser/DEPS +++ b/content/browser/DEPS
@@ -84,6 +84,8 @@ "+third_party/WebKit/public/platform/WebSuddenTerminationDisablerType.h", "+third_party/WebKit/public/platform/WebTouchEvent.h", "+third_party/WebKit/public/platform/WebTextInputType.h", + "+third_party/WebKit/public/platform/input_host.mojom.h", + "+third_party/WebKit/public/platform/input_messages.mojom.h", "+third_party/WebKit/public/platform/mac/WebScrollbarTheme.h", "+third_party/WebKit/public/platform/mime_registry.mojom.h", "+third_party/WebKit/public/platform/modules/background_sync/background_sync.mojom.h",
diff --git a/content/browser/android/browser_jni_registrar.cc b/content/browser/android/browser_jni_registrar.cc index f657268..2d217f0 100644 --- a/content/browser/android/browser_jni_registrar.cc +++ b/content/browser/android/browser_jni_registrar.cc
@@ -24,6 +24,7 @@ #include "content/browser/android/load_url_params.h" #include "content/browser/android/selection_popup_controller.h" #include "content/browser/android/smart_selection_client.h" +#include "content/browser/android/text_suggestion_host_android.h" #include "content/browser/android/tracing_controller_android.h" #include "content/browser/android/web_contents_observer_proxy.h" #include "content/browser/child_process_launcher_helper_android.h" @@ -68,6 +69,7 @@ {"SmartSelectionClient", content::RegisterSmartSelectionClient}, {"SpeechRecognizerImplAndroid", content::SpeechRecognizerImplAndroid::RegisterSpeechRecognizer}, + {"TextSuggestionHostAndroid", content::RegisterTextSuggestionHost}, {"TracingControllerAndroid", content::RegisterTracingControllerAndroid}, {"WebContentsAccessibilityAndroid", content::RegisterWebContentsAccessibilityAndroid},
diff --git a/content/browser/android/text_suggestion_host_android.cc b/content/browser/android/text_suggestion_host_android.cc new file mode 100644 index 0000000..297c451 --- /dev/null +++ b/content/browser/android/text_suggestion_host_android.cc
@@ -0,0 +1,202 @@ +// 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 "content/browser/android/text_suggestion_host_android.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_array.h" +#include "base/android/jni_string.h" +#include "content/browser/android/text_suggestion_host_mojo_impl_android.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/web_contents.h" +#include "jni/TextSuggestionHost_jni.h" +#include "services/service_manager/public/cpp/interface_provider.h" +#include "ui/gfx/android/view_configuration.h" + +using base::android::AttachCurrentThread; +using base::android::ConvertUTF8ToJavaString; +using base::android::JavaParamRef; +using base::android::ScopedJavaLocalRef; +using base::android::ToJavaArrayOfStrings; + +namespace content { + +bool RegisterTextSuggestionHost(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +jlong Init(JNIEnv* env, + const JavaParamRef<jobject>& obj, + const JavaParamRef<jobject>& jweb_contents) { + WebContents* web_contents = WebContents::FromJavaWebContents(jweb_contents); + DCHECK(web_contents); + auto* text_suggestion_host = + new TextSuggestionHostAndroid(env, obj, web_contents); + text_suggestion_host->Initialize(); + return reinterpret_cast<intptr_t>(text_suggestion_host); +} + +TextSuggestionHostAndroid::TextSuggestionHostAndroid( + JNIEnv* env, + const JavaParamRef<jobject>& obj, + WebContents* web_contents) + : RenderWidgetHostConnector(web_contents), + WebContentsObserver(web_contents), + rwhva_(nullptr), + java_text_suggestion_host_(JavaObjectWeakGlobalRef(env, obj)), + spellcheck_menu_timeout_( + base::Bind(&TextSuggestionHostAndroid::OnSpellCheckMenuTimeout, + base::Unretained(this))) { + registry_.AddInterface(base::Bind(&TextSuggestionHostMojoImplAndroid::Create, + base::Unretained(this))); +} + +TextSuggestionHostAndroid::~TextSuggestionHostAndroid() { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = java_text_suggestion_host_.get(env); + if (!obj.is_null()) + Java_TextSuggestionHost_destroy(env, obj); +} + +void TextSuggestionHostAndroid::UpdateRenderProcessConnection( + RenderWidgetHostViewAndroid* old_rwhva, + RenderWidgetHostViewAndroid* new_rwhva) { + text_suggestion_backend_ = nullptr; + if (old_rwhva) + old_rwhva->set_text_suggestion_host(nullptr); + if (new_rwhva) + new_rwhva->set_text_suggestion_host(this); + rwhva_ = new_rwhva; +} + +void TextSuggestionHostAndroid::ApplySpellCheckSuggestion( + JNIEnv* env, + const JavaParamRef<jobject>&, + const base::android::JavaParamRef<jstring>& replacement) { + const blink::mojom::TextSuggestionBackendPtr& text_suggestion_backend = + GetTextSuggestionBackend(); + if (!text_suggestion_backend) + return; + text_suggestion_backend->ApplySpellCheckSuggestion( + ConvertJavaStringToUTF8(env, replacement)); +} + +void TextSuggestionHostAndroid::DeleteActiveSuggestionRange( + JNIEnv*, + const JavaParamRef<jobject>&) { + const blink::mojom::TextSuggestionBackendPtr& text_suggestion_backend = + GetTextSuggestionBackend(); + if (!text_suggestion_backend) + return; + text_suggestion_backend->DeleteActiveSuggestionRange(); +} + +void TextSuggestionHostAndroid::NewWordAddedToDictionary( + JNIEnv* env, + const JavaParamRef<jobject>&, + const base::android::JavaParamRef<jstring>& word) { + const blink::mojom::TextSuggestionBackendPtr& text_suggestion_backend = + GetTextSuggestionBackend(); + if (!text_suggestion_backend) + return; + text_suggestion_backend->NewWordAddedToDictionary( + ConvertJavaStringToUTF8(env, word)); +} + +void TextSuggestionHostAndroid::SuggestionMenuClosed( + JNIEnv*, + const JavaParamRef<jobject>&) { + const blink::mojom::TextSuggestionBackendPtr& text_suggestion_backend = + GetTextSuggestionBackend(); + if (!text_suggestion_backend) + return; + text_suggestion_backend->SuggestionMenuClosed(); +} + +void TextSuggestionHostAndroid::ShowSpellCheckSuggestionMenu( + double caret_x, + double caret_y, + const std::string& marked_text, + const std::vector<blink::mojom::SpellCheckSuggestionPtr>& suggestions) { + std::vector<std::string> suggestion_strings; + for (const auto& suggestion_ptr : suggestions) + suggestion_strings.push_back(suggestion_ptr->suggestion); + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = java_text_suggestion_host_.get(env); + if (obj.is_null()) + return; + + Java_TextSuggestionHost_showSpellCheckSuggestionMenu( + env, obj, caret_x, caret_y, ConvertUTF8ToJavaString(env, marked_text), + ToJavaArrayOfStrings(env, suggestion_strings)); +} + +void TextSuggestionHostAndroid::StartSpellCheckMenuTimer() { + spellcheck_menu_timeout_.Stop(); + spellcheck_menu_timeout_.Start(base::TimeDelta::FromMilliseconds( + gfx::ViewConfiguration::GetDoubleTapTimeoutInMs())); +} + +void TextSuggestionHostAndroid::OnKeyEvent() { + spellcheck_menu_timeout_.Stop(); + + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = java_text_suggestion_host_.get(env); + if (obj.is_null()) + return; + + Java_TextSuggestionHost_hidePopups(env, obj); +} + +void TextSuggestionHostAndroid::StopSpellCheckMenuTimer() { + spellcheck_menu_timeout_.Stop(); +} + +void TextSuggestionHostAndroid::OnInterfaceRequestFromFrame( + content::RenderFrameHost* render_frame_host, + const std::string& interface_name, + mojo::ScopedMessagePipeHandle* interface_pipe) { + registry_.TryBindInterface(interface_name, interface_pipe); +} + +RenderFrameHost* TextSuggestionHostAndroid::GetFocusedFrame() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + // We get the focused frame from the WebContents of the page. Although + // |rwhva_->GetFocusedWidget()| does a similar thing, there is no direct way + // to get a RenderFrameHost from its RWH. + if (!rwhva_) + return nullptr; + RenderWidgetHostImpl* rwh = + RenderWidgetHostImpl::From(rwhva_->GetRenderWidgetHost()); + if (!rwh || !rwh->delegate()) + return nullptr; + + if (auto* contents = rwh->delegate()->GetAsWebContents()) + return contents->GetFocusedFrame(); + + return nullptr; +} + +const blink::mojom::TextSuggestionBackendPtr& +TextSuggestionHostAndroid::GetTextSuggestionBackend() { + if (!text_suggestion_backend_) { + if (RenderFrameHost* rfh = GetFocusedFrame()) { + rfh->GetRemoteInterfaces()->GetInterface( + mojo::MakeRequest(&text_suggestion_backend_)); + } + } + return text_suggestion_backend_; +} + +void TextSuggestionHostAndroid::OnSpellCheckMenuTimeout() { + const blink::mojom::TextSuggestionBackendPtr& text_suggestion_backend = + GetTextSuggestionBackend(); + if (!text_suggestion_backend) + return; + text_suggestion_backend->SpellCheckMenuTimeoutCallback(); +} + +} // namespace content
diff --git a/content/browser/android/text_suggestion_host_android.h b/content/browser/android/text_suggestion_host_android.h new file mode 100644 index 0000000..9d7fb70 --- /dev/null +++ b/content/browser/android/text_suggestion_host_android.h
@@ -0,0 +1,102 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_ANDROID_TEXT_SUGGESTION_HOST_ANDROID_H_ +#define CONTENT_BROWSER_ANDROID_TEXT_SUGGESTION_HOST_ANDROID_H_ + +#include "content/browser/android/render_widget_host_connector.h" +#include "content/browser/renderer_host/input/timeout_monitor.h" +#include "services/service_manager/public/cpp/binder_registry.h" +#include "third_party/WebKit/public/platform/input_host.mojom.h" +#include "third_party/WebKit/public/platform/input_messages.mojom.h" + +namespace content { + +// This class, along with its Java counterpart TextSuggestionHost, is used to +// implement the Android text suggestion menu that appears when you tap a +// misspelled word. This class creates the Android implementation of +// mojom::TextSuggestionHost, which is used to communicate back-and-forth with +// Blink side code (these are separate classes due to lifecycle considerations; +// this class needs to be constructed from Java code, but Mojo code wants to +// take ownership of mojom::TextSuggestionHost). +class TextSuggestionHostAndroid : public RenderWidgetHostConnector, + public WebContentsObserver { + public: + TextSuggestionHostAndroid(JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj, + WebContents* web_contents); + ~TextSuggestionHostAndroid() override; + + // RenderWidgetHostConnector implementation. + void UpdateRenderProcessConnection( + RenderWidgetHostViewAndroid* old_rwhva, + RenderWidgetHostViewAndroid* new_rhwva) override; + + // Called from the Java text suggestion menu to have Blink apply a spell + // check suggestion. + void ApplySpellCheckSuggestion( + JNIEnv*, + const base::android::JavaParamRef<jobject>&, + const base::android::JavaParamRef<jstring>& replacement); + // Called from the Java text suggestion menu to have Blink delete the + // currently highlighted region of text that the open suggestion menu pertains + // to. + void DeleteActiveSuggestionRange(JNIEnv*, + const base::android::JavaParamRef<jobject>&); + // Called from the Java text suggestion menu to tell Blink that a word is + // being added to the dictionary (so Blink can clear the spell check markers + // for that word). + void NewWordAddedToDictionary( + JNIEnv*, + const base::android::JavaParamRef<jobject>&, + const base::android::JavaParamRef<jstring>& word); + // Called from the Java text suggestion menu to tell Blink that the user + // closed the menu without performing one of the available actions, so Blink + // can re-show the insertion caret and remove the suggestion range highlight. + void SuggestionMenuClosed(JNIEnv*, + const base::android::JavaParamRef<jobject>&); + // Called from Blink to tell the Java TextSuggestionHost to open the text + // suggestion menu. + void ShowSpellCheckSuggestionMenu( + double caret_x, + double caret_y, + const std::string& marked_text, + const std::vector<blink::mojom::SpellCheckSuggestionPtr>& suggestions); + + // Called by browser-side code in response to an input event to stop the + // spell check menu timer and close the suggestion menu (if open). + void OnKeyEvent(); + // Called by Blink when the user taps on a spell check marker and we might + // want to show the text suggestion menu after the double-tap timer expires. + void StartSpellCheckMenuTimer(); + // Called by browser-side code in response to an input event to stop the + // spell check menu timer. + void StopSpellCheckMenuTimer(); + + // WebContentsObserver overrides + void OnInterfaceRequestFromFrame( + content::RenderFrameHost* render_frame_host, + const std::string& interface_name, + mojo::ScopedMessagePipeHandle* interface_pipe) override; + + private: + RenderFrameHost* GetFocusedFrame(); + const blink::mojom::TextSuggestionBackendPtr& GetTextSuggestionBackend(); + // Used by the spell check menu timer to notify Blink that the timer has + // expired. + void OnSpellCheckMenuTimeout(); + + service_manager::BinderRegistry registry_; + // Current RenderWidgetHostView connected to this instance. Can be null. + RenderWidgetHostViewAndroid* rwhva_; + JavaObjectWeakGlobalRef java_text_suggestion_host_; + blink::mojom::TextSuggestionBackendPtr text_suggestion_backend_; + TimeoutMonitor spellcheck_menu_timeout_; +}; + +bool RegisterTextSuggestionHost(JNIEnv* env); + +} // namespace content + +#endif // CONTENT_BROWSER_ANDROID_TEXT_SUGGESTION_HOST_ANDROID_H_
diff --git a/content/browser/android/text_suggestion_host_mojo_impl_android.cc b/content/browser/android/text_suggestion_host_mojo_impl_android.cc new file mode 100644 index 0000000..6510499c --- /dev/null +++ b/content/browser/android/text_suggestion_host_mojo_impl_android.cc
@@ -0,0 +1,38 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/android/text_suggestion_host_mojo_impl_android.h" + +#include "content/browser/android/text_suggestion_host_android.h" +#include "mojo/public/cpp/bindings/strong_binding.h" + +namespace content { + +TextSuggestionHostMojoImplAndroid::TextSuggestionHostMojoImplAndroid( + TextSuggestionHostAndroid* text_suggestion_host) + : text_suggestion_host_(text_suggestion_host) {} + +// static +void TextSuggestionHostMojoImplAndroid::Create( + TextSuggestionHostAndroid* text_suggestion_host, + blink::mojom::TextSuggestionHostRequest request) { + mojo::MakeStrongBinding( + base::MakeUnique<TextSuggestionHostMojoImplAndroid>(text_suggestion_host), + std::move(request)); +} + +void TextSuggestionHostMojoImplAndroid::StartSpellCheckMenuTimer() { + text_suggestion_host_->StartSpellCheckMenuTimer(); +} + +void TextSuggestionHostMojoImplAndroid::ShowSpellCheckSuggestionMenu( + double caret_x, + double caret_y, + const std::string& marked_text, + std::vector<blink::mojom::SpellCheckSuggestionPtr> suggestions) { + text_suggestion_host_->ShowSpellCheckSuggestionMenu(caret_x, caret_y, + marked_text, suggestions); +} + +} // namespace content
diff --git a/content/browser/android/text_suggestion_host_mojo_impl_android.h b/content/browser/android/text_suggestion_host_mojo_impl_android.h new file mode 100644 index 0000000..4281948e --- /dev/null +++ b/content/browser/android/text_suggestion_host_mojo_impl_android.h
@@ -0,0 +1,39 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_ANDROID_TEXT_SUGGESTION_HOST_MOJO_IMPL_ANDROID_H_ +#define CONTENT_BROWSER_ANDROID_TEXT_SUGGESTION_HOST_MOJO_IMPL_ANDROID_H_ + +#include "third_party/WebKit/public/platform/input_host.mojom.h" + +namespace content { + +class TextSuggestionHostAndroid; + +// Android implementation of mojom::TextSuggestionHost. +class TextSuggestionHostMojoImplAndroid final + : public blink::mojom::TextSuggestionHost { + public: + explicit TextSuggestionHostMojoImplAndroid(TextSuggestionHostAndroid*); + + static void Create(TextSuggestionHostAndroid*, + blink::mojom::TextSuggestionHostRequest request); + + void StartSpellCheckMenuTimer() final; + + void ShowSpellCheckSuggestionMenu( + double caret_x, + double caret_y, + const std::string& marked_text, + std::vector<blink::mojom::SpellCheckSuggestionPtr> suggestions) final; + + private: + TextSuggestionHostAndroid* const text_suggestion_host_; + + DISALLOW_COPY_AND_ASSIGN(TextSuggestionHostMojoImplAndroid); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_ANDROID_TEXT_SUGGESTION_HOST_MOJO_IMPL_ANDROID_H_
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc index 591ca19d..c32a1d04 100644 --- a/content/browser/renderer_host/render_widget_host_view_android.cc +++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -43,6 +43,7 @@ #include "content/browser/android/overscroll_controller_android.h" #include "content/browser/android/selection_popup_controller.h" #include "content/browser/android/synchronous_compositor_host.h" +#include "content/browser/android/text_suggestion_host_android.h" #include "content/browser/compositor/surface_utils.h" #include "content/browser/devtools/render_frame_devtools_agent_host.h" #include "content/browser/gpu/browser_gpu_channel_host_factory.h" @@ -455,6 +456,7 @@ content_view_core_(nullptr), ime_adapter_android_(nullptr), selection_popup_controller_(nullptr), + text_suggestion_host_(nullptr), background_color_(SK_ColorWHITE), cached_background_color_(SK_ColorWHITE), view_(this), @@ -965,6 +967,11 @@ ComputeEventLatencyOSTouchHistograms(event); + // Receiving any other touch event before the double-tap timeout expires + // cancels opening the spellcheck menu. + if (text_suggestion_host_) + text_suggestion_host_->StopSpellCheckMenuTimer(); + // If a browser-based widget consumes the touch event, it's critical that // touch event interception be disabled. This avoids issues with // double-handling for embedder-detected gestures like side swipe. @@ -1826,6 +1833,11 @@ if (!target_host) return; + // Receiving a key event before the double-tap timeout expires cancels opening + // the spellcheck menu. If the suggestion menu is open, we close the menu. + if (text_suggestion_host_) + text_suggestion_host_->OnKeyEvent(); + ui::LatencyInfo latency_info; if (event.GetType() == blink::WebInputEvent::kRawKeyDown || event.GetType() == blink::WebInputEvent::kChar) {
diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h index a6872cda..58e7f70 100644 --- a/content/browser/renderer_host/render_widget_host_view_android.h +++ b/content/browser/renderer_host/render_widget_host_view_android.h
@@ -56,6 +56,7 @@ class SelectionPopupController; class SynchronousCompositorHost; class SynchronousCompositorClient; +class TextSuggestionHostAndroid; class TouchSelectionControllerClientManagerAndroid; class WebContentsAccessibilityAndroid; struct NativeWebKeyboardEvent; @@ -258,6 +259,10 @@ void set_selection_popup_controller(SelectionPopupController* controller) { selection_popup_controller_ = controller; } + void set_text_suggestion_host( + TextSuggestionHostAndroid* text_suggestion_host) { + text_suggestion_host_ = text_suggestion_host; + } base::WeakPtr<RenderWidgetHostViewAndroid> GetWeakPtrAndroid(); @@ -400,6 +405,7 @@ ImeAdapterAndroid* ime_adapter_android_; SelectionPopupController* selection_popup_controller_; + TextSuggestionHostAndroid* text_suggestion_host_; // The background color of the widget. SkColor background_color_;
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn index b6eac88..af2372e 100644 --- a/content/public/android/BUILD.gn +++ b/content/public/android/BUILD.gn
@@ -190,7 +190,9 @@ "java/src/org/chromium/content/browser/input/SelectPopupDialog.java", "java/src/org/chromium/content/browser/input/SelectPopupDropdown.java", "java/src/org/chromium/content/browser/input/SelectPopupItem.java", + "java/src/org/chromium/content/browser/input/SuggestionsPopupWindow.java", "java/src/org/chromium/content/browser/input/TextInputState.java", + "java/src/org/chromium/content/browser/input/TextSuggestionHost.java", "java/src/org/chromium/content/browser/input/ThreadedInputConnection.java", "java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java", "java/src/org/chromium/content/browser/input/ThreadedInputConnectionProxyView.java", @@ -355,6 +357,7 @@ "java/src/org/chromium/content/browser/input/DateTimeChooserAndroid.java", "java/src/org/chromium/content/browser/input/HandleViewResources.java", "java/src/org/chromium/content/browser/input/ImeAdapter.java", + "java/src/org/chromium/content/browser/input/TextSuggestionHost.java", "java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java", "java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java", "java/src/org/chromium/content_public/browser/LoadUrlParams.java",
diff --git a/content/public/android/java/res/drawable/floating_popup_background_light.xml b/content/public/android/java/res/drawable/floating_popup_background_light.xml new file mode 100644 index 0000000..c5e33bb --- /dev/null +++ b/content/public/android/java/res/drawable/floating_popup_background_light.xml
@@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2017 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@android:color/white" /> + <corners android:radius="2dp" /> +</shape>
diff --git a/content/public/android/java/res/layout/text_edit_suggestion_container.xml b/content/public/android/java/res/layout/text_edit_suggestion_container.xml new file mode 100644 index 0000000..b97a0807 --- /dev/null +++ b/content/public/android/java/res/layout/text_edit_suggestion_container.xml
@@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2017 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + <ListView + android:id="@+id/suggestionContainer" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> +</LinearLayout>
diff --git a/content/public/android/java/res/layout/text_edit_suggestion_item.xml b/content/public/android/java/res/layout/text_edit_suggestion_item.xml new file mode 100644 index 0000000..3026fce --- /dev/null +++ b/content/public/android/java/res/layout/text_edit_suggestion_item.xml
@@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2017 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:textSize="14sp" + android:fontFamily="sans-serif" + android:textColor="?android:attr/textColorPrimary" + android:drawablePadding="8dp" + android:gravity="start|center_vertical" + android:layout_gravity="start|center_vertical" + android:layout_height="@dimen/text_edit_suggestion_item_layout_height" + android:layout_width="match_parent" + android:paddingBottom="8dp" + android:paddingEnd="16dp" + android:paddingStart="16dp" + android:paddingTop="8dp" + android:singleLine="true" />
diff --git a/content/public/android/java/res/layout/text_edit_suggestion_list_footer.xml b/content/public/android/java/res/layout/text_edit_suggestion_list_footer.xml new file mode 100644 index 0000000..177739cd --- /dev/null +++ b/content/public/android/java/res/layout/text_edit_suggestion_list_footer.xml
@@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2017 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical"> + <View + android:id="@+id/divider" + android:background="?android:attr/listDivider" + android:layout_height="1dp" + android:layout_width="match_parent" /> + <TextView + style="@style/TextSuggestionButton" + android:id="@+id/addToDictionaryButton" + android:text="@string/add_to_dictionary" /> + <TextView + style="@style/TextSuggestionButton" + android:id="@+id/deleteButton" + android:text="@string/delete_suggestion_text" /> +</LinearLayout>
diff --git a/content/public/android/java/res/values-v17/styles.xml b/content/public/android/java/res/values-v17/styles.xml index 58d8ed4..5d0be61 100644 --- a/content/public/android/java/res/values-v17/styles.xml +++ b/content/public/android/java/res/values-v17/styles.xml
@@ -16,4 +16,21 @@ <style name="SelectActionMenuWebSearch"> <item name="android:icon">@drawable/ic_search</item> </style> + <style name="TextSuggestionButton" parent="RobotoMediumStyle"> + <item name="android:drawablePadding">8dp</item> + <!-- v21 uses sans-serif-medium --> + <item name="android:gravity">start|center_vertical</item> + <item name="android:layout_gravity">start|center_vertical</item> + <item name="android:layout_height">48dp</item> + <item name="android:layout_width">match_parent</item> + <item name="android:paddingBottom">8dp</item> + <item name="android:paddingEnd">16dp</item> + <item name="android:paddingStart">16dp</item> + <item name="android:paddingTop">8dp</item> + <item name="android:singleLine">true</item> + <!-- v21 uses android:attr/colorAccent --> + <item name="android:textColor">@color/google_blue_500</item> + <item name="android:textAllCaps">true</item> + <item name="android:textSize">14sp</item> + </style> </resources>
diff --git a/content/public/android/java/res/values-v21/styles.xml b/content/public/android/java/res/values-v21/styles.xml index a01b0c7..0e75d294 100644 --- a/content/public/android/java/res/values-v21/styles.xml +++ b/content/public/android/java/res/values-v21/styles.xml
@@ -10,5 +10,20 @@ <style name="SelectActionMenuWebSearch"> <item name="android:icon">?android:attr/actionModeWebSearchDrawable</item> </style> + <style name="TextSuggestionButton" parent="RobotoMediumStyle"> + <item name="android:drawablePadding">8dp</item> + <item name="android:gravity">start|center_vertical</item> + <item name="android:layout_gravity">start|center_vertical</item> + <item name="android:layout_height">48dp</item> + <item name="android:layout_width">match_parent</item> + <item name="android:paddingBottom">8dp</item> + <item name="android:paddingEnd">16dp</item> + <item name="android:paddingStart">16dp</item> + <item name="android:paddingTop">8dp</item> + <item name="android:singleLine">true</item> + <!-- v17 hard codes google_blue_500 --> + <item name="android:textColor">?android:attr/colorAccent</item> + <item name="android:textAllCaps">true</item> + <item name="android:textSize">14sp</item> + </style> </resources> -
diff --git a/content/public/android/java/res/values/colors.xml b/content/public/android/java/res/values/colors.xml new file mode 100644 index 0000000..d3a0c09 --- /dev/null +++ b/content/public/android/java/res/values/colors.xml
@@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2017 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> + +<resources> + <color name="google_blue_500">#4285f4</color> +</resources>
diff --git a/content/public/android/java/res/values/dimens.xml b/content/public/android/java/res/values/dimens.xml index 25ee103..f86b30d3 100644 --- a/content/public/android/java/res/values/dimens.xml +++ b/content/public/android/java/res/values/dimens.xml
@@ -8,4 +8,7 @@ <resources> <!-- Link Preview dimensions --> <dimen name="link_preview_overlay_radius">7dp</dimen> + <dimen name="text_edit_suggestion_item_layout_height">48dp</dimen> + <dimen name="text_suggestion_popup_elevation">2dp</dimen> + <dimen name="text_suggestion_popup_vertical_margin">20dp</dimen> </resources>
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java index 5651b61d..b77cc2da 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java +++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -48,6 +48,7 @@ import org.chromium.content.browser.input.SelectPopupDialog; import org.chromium.content.browser.input.SelectPopupDropdown; import org.chromium.content.browser.input.SelectPopupItem; +import org.chromium.content.browser.input.TextSuggestionHost; import org.chromium.content_public.browser.AccessibilitySnapshotCallback; import org.chromium.content_public.browser.AccessibilitySnapshotNode; import org.chromium.content_public.browser.ActionModeCallbackHelper; @@ -218,6 +219,8 @@ // Only valid when focused on a text / password field. private ImeAdapter mImeAdapter; + private TextSuggestionHost mTextSuggestionHost; + // Size of the viewport in physical pixels as set from onSizeChanged. private int mViewportWidthPix; private int mViewportHeightPix; @@ -369,6 +372,19 @@ mSelectionPopupController = actionMode; } + /** + * @return The TextSuggestionHost that handles displaying the text suggestion menu. + */ + @VisibleForTesting + public TextSuggestionHost getTextSuggestionHostForTesting() { + return mTextSuggestionHost; + } + + @VisibleForTesting + public void setTextSuggestionHostForTesting(TextSuggestionHost textSuggestionHost) { + mTextSuggestionHost = textSuggestionHost; + } + @Override public void addWindowAndroidChangedObserver(WindowAndroidChangedObserver observer) { mWindowAndroidChangedObservers.addObserver(observer); @@ -459,6 +475,8 @@ initPopupZoomer(mContext); mImeAdapter = new ImeAdapter( mWebContents, mContainerView, new InputMethodManagerWrapper(mContext)); + mTextSuggestionHost = new TextSuggestionHost( + mContext, mWebContents, mContainerView, this, mRenderCoordinates); mImeAdapter.addEventObserver(this); mSelectionPopupController = new SelectionPopupController( @@ -990,6 +1008,7 @@ destroyPastePopup(); hideSelectPopupWithCancelMessage(); mPopupZoomer.hide(false); + mTextSuggestionHost.hidePopups(); if (mWebContents != null) mWebContents.dismissTextHandles(); } @@ -999,6 +1018,7 @@ destroyPastePopup(); hideSelectPopupWithCancelMessage(); mPopupZoomer.hide(false); + mTextSuggestionHost.hidePopups(); } private void restoreSelectionPopupsIfNecessary() { @@ -2131,6 +2151,7 @@ hidePopupsAndPreserveSelection(); showSelectActionMode(); } + mTextSuggestionHost.hidePopups(); int rotationDegrees = 0; switch (rotation) {
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/SuggestionsPopupWindow.java b/content/public/android/java/src/org/chromium/content/browser/input/SuggestionsPopupWindow.java new file mode 100644 index 0000000..19d8d9c --- /dev/null +++ b/content/public/android/java/src/org/chromium/content/browser/input/SuggestionsPopupWindow.java
@@ -0,0 +1,380 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.content.browser.input; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.util.DisplayMetrics; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.BaseAdapter; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.PopupWindow; +import android.widget.PopupWindow.OnDismissListener; +import android.widget.TextView; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.base.VisibleForTesting; +import org.chromium.content.R; +import org.chromium.content.browser.WindowAndroidProvider; +import org.chromium.ui.UiUtils; + +/** + * Popup window that displays a menu for viewing and applying text replacement suggestions. + */ +public class SuggestionsPopupWindow + implements OnItemClickListener, OnDismissListener, View.OnClickListener { + private static final String ACTION_USER_DICTIONARY_INSERT = + "com.android.settings.USER_DICTIONARY_INSERT"; + private static final String USER_DICTIONARY_EXTRA_WORD = "word"; + + private final Context mContext; + private final TextSuggestionHost mTextSuggestionHost; + private final View mParentView; + private final WindowAndroidProvider mWindowAndroidProvider; + + private Activity mActivity; + private DisplayMetrics mDisplayMetrics; + private PopupWindow mPopupWindow; + private LinearLayout mContentView; + + private SuggestionAdapter mSuggestionsAdapter; + private String mHighlightedText; + private String[] mSpellCheckSuggestions = new String[0]; + private int mNumberOfSuggestionsToUse; + private TextView mAddToDictionaryButton; + private TextView mDeleteButton; + private ListView mSuggestionListView; + private LinearLayout mListFooter; + private View mDivider; + private int mPopupVerticalMargin; + + private boolean mDismissedByItemTap; + + /** + * @param context Android context to use. + * @param textSuggestionHost TextSuggestionHost instance (used to communicate with Blink). + * @param parentView The view used to attach the PopupWindow. + * @param windowAndroidProvider A WindowAndroidProvider instance used to get the window size. + */ + public SuggestionsPopupWindow(Context context, TextSuggestionHost textSuggestionHost, + View parentView, WindowAndroidProvider windowAndroidProvider) { + mContext = context; + mTextSuggestionHost = textSuggestionHost; + mParentView = parentView; + mWindowAndroidProvider = windowAndroidProvider; + + createPopupWindow(); + initContentView(); + + mPopupWindow.setContentView(mContentView); + } + + private void createPopupWindow() { + mPopupWindow = new PopupWindow(); + mPopupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT); + mPopupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // On Lollipop and later, we use elevation to create a drop shadow effect. + // On pre-Lollipop, we use a background image instead (in the layout file). + mPopupWindow.setElevation(mContext.getResources().getDimensionPixelSize( + R.dimen.text_suggestion_popup_elevation)); + } else { + // The PopupWindow does not properly dismiss on Jelly Bean without the following line. + mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + } + mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); + mPopupWindow.setFocusable(true); + mPopupWindow.setClippingEnabled(false); + mPopupWindow.setOnDismissListener(this); + } + + private void initContentView() { + final LayoutInflater inflater = + (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mContentView = + (LinearLayout) inflater.inflate(R.layout.text_edit_suggestion_container, null); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + mContentView.setBackground(ApiCompatibilityUtils.getDrawable( + mContext.getResources(), R.drawable.floating_popup_background_light)); + } else { + mContentView.setBackground(ApiCompatibilityUtils.getDrawable( + mContext.getResources(), R.drawable.dropdown_popup_background)); + } + + // mPopupVerticalMargin is the minimum amount of space we want to have between the popup + // and the top or bottom of the window. + mPopupVerticalMargin = mContext.getResources().getDimensionPixelSize( + R.dimen.text_suggestion_popup_vertical_margin); + + mSuggestionListView = (ListView) mContentView.findViewById(R.id.suggestionContainer); + // android:divider="@null" in the XML file crashes on Android N and O + // when running as a WebView (b/38346876). + mSuggestionListView.setDivider(null); + + mListFooter = + (LinearLayout) inflater.inflate(R.layout.text_edit_suggestion_list_footer, null); + mSuggestionListView.addFooterView(mListFooter, null, false); + + mSuggestionsAdapter = new SuggestionAdapter(); + mSuggestionListView.setAdapter(mSuggestionsAdapter); + mSuggestionListView.setOnItemClickListener(this); + + mDivider = mContentView.findViewById(R.id.divider); + + mAddToDictionaryButton = (TextView) mContentView.findViewById(R.id.addToDictionaryButton); + mAddToDictionaryButton.setOnClickListener(this); + + mDeleteButton = (TextView) mContentView.findViewById(R.id.deleteButton); + mDeleteButton.setOnClickListener(this); + } + + /** + * Dismisses the text suggestion menu (called by TextSuggestionHost when certain events occur, + * for example device rotation). + */ + public void dismiss() { + mPopupWindow.dismiss(); + } + + /** + * Used by TextSuggestionHost to determine if the text suggestion menu is currently visible. + */ + public boolean isShowing() { + return mPopupWindow.isShowing(); + } + + private void addToDictionary() { + final Intent intent = new Intent(ACTION_USER_DICTIONARY_INSERT); + intent.putExtra(USER_DICTIONARY_EXTRA_WORD, mHighlightedText); + intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + } + + private class SuggestionAdapter extends BaseAdapter { + private LayoutInflater mInflater = + (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + @Override + public int getCount() { + return mNumberOfSuggestionsToUse; + } + + @Override + public Object getItem(int position) { + return mSpellCheckSuggestions[position]; + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + TextView textView = (TextView) convertView; + if (textView == null) { + textView = (TextView) mInflater.inflate( + R.layout.text_edit_suggestion_item, parent, false); + } + final String suggestion = mSpellCheckSuggestions[position]; + textView.setText(suggestion); + return textView; + } + } + + private void measureContent() { + // Make the menu wide enough to fit its widest item. + int width = UiUtils.computeMaxWidthOfListAdapterItems(mSuggestionListView.getAdapter()); + width += mContentView.getPaddingLeft() + mContentView.getPaddingRight(); + + final int verticalMeasure = View.MeasureSpec.makeMeasureSpec( + mDisplayMetrics.heightPixels, View.MeasureSpec.AT_MOST); + mContentView.measure( + View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), verticalMeasure); + mPopupWindow.setWidth(width); + } + + private void updateDividerVisibility() { + // If we don't have any spell check suggestions, "Add to dictionary" will be the first menu + // item, and we shouldn't show a divider above it. + if (mNumberOfSuggestionsToUse == 0) { + mDivider.setVisibility(View.GONE); + } else { + mDivider.setVisibility(View.VISIBLE); + } + } + + /** + * Called by TextSuggestionHost to tell this class what text is currently highlighted (so it can + * be added to the dictionary if requested). + */ + public void setHighlightedText(String text) { + mHighlightedText = text; + } + + /** + * Called by TextSuggestionHost to set the list of spell check suggestions to show in the + * suggestion menu. + */ + public void setSpellCheckSuggestions(String[] suggestions) { + mSpellCheckSuggestions = suggestions.clone(); + mNumberOfSuggestionsToUse = mSpellCheckSuggestions.length; + } + + /** + * Shows the text suggestion menu at the specified coordinates (relative to the viewport). + */ + public void show(double caretX, double caretY) { + mSuggestionsAdapter.notifyDataSetChanged(); + + mActivity = mWindowAndroidProvider.getWindowAndroid().getActivity().get(); + // Note: the Activity can be null here if we're in a WebView that was created without + // using an Activity. So all code in this class should handle this case. + if (mActivity != null) { + mDisplayMetrics = mActivity.getResources().getDisplayMetrics(); + } else { + // Getting the DisplayMetrics from the passed-in context doesn't handle multi-window + // mode as well, but it's good enough for the "improperly-created WebView" case + mDisplayMetrics = mContext.getResources().getDisplayMetrics(); + } + + // In single-window mode, we need to get the status bar height to make sure we don't try to + // draw on top of it (we can't draw on top in older versions of Android). + // In multi-window mode, as of Android N, the behavior is as follows: + // + // Portrait mode, top window: the window height does not include the height of the status + // bar, but drawing at Y position 0 starts at the top of the status bar. + // + // Portrait mode, bottom window: the window height does not include the height of the status + // bar, and the status bar isn't touching the window, so we can't draw on it regardless. + // + // Landscape mode: the window height includes the whole height of the keyboard + // (Google-internal b/63405914), so we are unable to handle this case properly. + // + // For our purposes, we don't worry about if we're drawing over the status bar in + // multi-window mode, but we need to make sure we don't do it in single-window mode (in case + // we're on an old version of Android). + int statusBarHeight = 0; + if (mActivity != null + && (Build.VERSION.SDK_INT < Build.VERSION_CODES.N + || !mActivity.isInMultiWindowMode())) { + Rect rect = new Rect(); + mActivity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); + statusBarHeight = rect.top; + } + + // We determine the maximum number of suggestions we can show by taking the available + // height in the window, subtracting the height of the list footer (divider, add to + // dictionary button, delete button), and dividing by the height of a suggestion item. + mListFooter.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); + + final int verticalSpaceAvailableForSuggestions = mDisplayMetrics.heightPixels + - statusBarHeight - mListFooter.getMeasuredHeight() - 2 * mPopupVerticalMargin + - mContentView.getPaddingTop() - mContentView.getPaddingBottom(); + final int itemHeight = mContext.getResources().getDimensionPixelSize( + R.dimen.text_edit_suggestion_item_layout_height); + final int maxItemsToShow = verticalSpaceAvailableForSuggestions > 0 + ? verticalSpaceAvailableForSuggestions / itemHeight + : 0; + + mNumberOfSuggestionsToUse = Math.min(mNumberOfSuggestionsToUse, maxItemsToShow); + // If we're not showing any suggestions, hide the divider before "Add to dictionary" and + // "Delete". + updateDividerVisibility(); + measureContent(); + + final int width = mContentView.getMeasuredWidth(); + final int height = mContentView.getMeasuredHeight(); + + // Horizontally center the menu on the caret location, and vertically position the menu + // under the caret. + int positionX = (int) Math.round(caretX - width / 2.0f); + int positionY = (int) Math.round(caretY); + + // We get the insertion point coords relative to the viewport. + // We need to render the popup relative to the window. + final int[] positionInWindow = new int[2]; + mParentView.getLocationInWindow(positionInWindow); + + positionX += positionInWindow[0]; + positionY += positionInWindow[1]; + + // Subtract off the container's top padding to get the proper alignment with the caret. + // Note: there is no explicit padding set. On Android L and later, we use elevation to draw + // a drop shadow and there is no top padding. On pre-L, we instead use a background image, + // which results in some implicit padding getting added that we need to account for. + positionY -= mContentView.getPaddingTop(); + + // Horizontal clipping: if part of the menu (except the shadow) would fall off the left + // or right edge of the screen, shift the menu to keep it on-screen. + final int menuAtRightEdgeOfWindowPositionX = + mDisplayMetrics.widthPixels - width + mContentView.getPaddingRight(); + positionX = Math.min(menuAtRightEdgeOfWindowPositionX, positionX); + positionX = Math.max(-mContentView.getPaddingLeft(), positionX); + + // Vertical clipping: if part of the menu or its bottom margin would fall off the bottom of + // the screen, shift it up to keep it on-screen. + positionY = Math.min(positionY, + mDisplayMetrics.heightPixels - height - mContentView.getPaddingTop() + - mPopupVerticalMargin); + + mPopupWindow.showAtLocation(mParentView, Gravity.NO_GRAVITY, positionX, positionY); + } + + @Override + public void onClick(View v) { + if (v == mAddToDictionaryButton) { + addToDictionary(); + mTextSuggestionHost.newWordAddedToDictionary(mHighlightedText); + mDismissedByItemTap = true; + mPopupWindow.dismiss(); + } else if (v == mDeleteButton) { + mTextSuggestionHost.deleteActiveSuggestionRange(); + mDismissedByItemTap = true; + mPopupWindow.dismiss(); + } + } + + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + // Ignore taps somewhere in the list footer (divider, "Add to dictionary", "Delete") that + // don't get handled by a button. + if (position >= mNumberOfSuggestionsToUse) { + return; + } + + String suggestion = mSpellCheckSuggestions[position]; + mTextSuggestionHost.applySpellCheckSuggestion(suggestion); + mDismissedByItemTap = true; + mPopupWindow.dismiss(); + } + + @Override + public void onDismiss() { + mTextSuggestionHost.suggestionMenuClosed(mDismissedByItemTap); + mDismissedByItemTap = false; + } + + /** + * @return The popup's content view. + */ + @VisibleForTesting + public View getContentViewForTesting() { + return mContentView; + } +}
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/TextSuggestionHost.java b/content/public/android/java/src/org/chromium/content/browser/input/TextSuggestionHost.java new file mode 100644 index 0000000..4f5c529 --- /dev/null +++ b/content/public/android/java/src/org/chromium/content/browser/input/TextSuggestionHost.java
@@ -0,0 +1,123 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.content.browser.input; + +import android.content.Context; +import android.view.View; + +import org.chromium.base.VisibleForTesting; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; +import org.chromium.content.browser.RenderCoordinates; +import org.chromium.content.browser.WindowAndroidProvider; +import org.chromium.content_public.browser.WebContents; + +/** + * Handles displaying the Android spellcheck/text suggestion menu (provided by + * SuggestionsPopupWindow) when requested by the C++ class TextSuggestionHostAndroid and applying + * the commands in that menu (by calling back to the C++ class). + */ +@JNINamespace("content") +public class TextSuggestionHost { + private long mNativeTextSuggestionHost; + private final Context mContext; + private final WebContents mWebContents; + private final View mContainerView; + private final WindowAndroidProvider mWindowAndroidProvider; + private final RenderCoordinates mRenderCoordinates; + + private SuggestionsPopupWindow mSuggestionsPopupWindow; + + public TextSuggestionHost(Context context, WebContents webContents, View containerView, + WindowAndroidProvider windowAndroidProvider, RenderCoordinates renderCoordinates) { + mContext = context; + mWebContents = webContents; + mContainerView = containerView; + mWindowAndroidProvider = windowAndroidProvider; + mRenderCoordinates = renderCoordinates; + + mNativeTextSuggestionHost = nativeInit(webContents); + } + + @CalledByNative + private void showSpellCheckSuggestionMenu( + double caretX, double caretY, String markedText, String[] suggestions) { + if (mSuggestionsPopupWindow == null) { + mSuggestionsPopupWindow = new SuggestionsPopupWindow( + mContext, this, mContainerView, mWindowAndroidProvider); + } + + mSuggestionsPopupWindow.setHighlightedText(markedText); + mSuggestionsPopupWindow.setSpellCheckSuggestions(suggestions); + + float density = mRenderCoordinates.getDeviceScaleFactor(); + mSuggestionsPopupWindow.show( + density * caretX, density * caretY + mRenderCoordinates.getContentOffsetYPix()); + } + + /** + * Hides the text suggestion menu (and informs Blink that it was closed). + */ + @CalledByNative + public void hidePopups() { + if (mSuggestionsPopupWindow != null && mSuggestionsPopupWindow.isShowing()) { + mSuggestionsPopupWindow.dismiss(); + mSuggestionsPopupWindow = null; + } + } + + /** + * Tells Blink to replace the active suggestion range with the specified replacement. + */ + public void applySpellCheckSuggestion(String suggestion) { + nativeApplySpellCheckSuggestion(mNativeTextSuggestionHost, suggestion); + } + + /** + * Tells Blink to delete the active suggestion range. + */ + public void deleteActiveSuggestionRange() { + nativeDeleteActiveSuggestionRange(mNativeTextSuggestionHost); + } + + /** + * Tells Blink to remove spelling markers under all instances of the specified word. + */ + public void newWordAddedToDictionary(String word) { + nativeNewWordAddedToDictionary(mNativeTextSuggestionHost, word); + } + + /** + * Tells Blink the suggestion menu was closed (and also clears the reference to the + * SuggestionsPopupWindow instance so it can be garbage collected). + */ + public void suggestionMenuClosed(boolean dismissedByItemTap) { + if (!dismissedByItemTap) { + nativeSuggestionMenuClosed(mNativeTextSuggestionHost); + } + mSuggestionsPopupWindow = null; + } + + @CalledByNative + private void destroy() { + mNativeTextSuggestionHost = 0; + } + + /** + * @return The SuggestionsPopupWindow, if one exists. + */ + @VisibleForTesting + public SuggestionsPopupWindow getSuggestionsPopupWindowForTesting() { + return mSuggestionsPopupWindow; + } + + private native long nativeInit(WebContents webContents); + private native void nativeApplySpellCheckSuggestion( + long nativeTextSuggestionHostAndroid, String suggestion); + private native void nativeDeleteActiveSuggestionRange(long nativeTextSuggestionHostAndroid); + private native void nativeNewWordAddedToDictionary( + long nativeTextSuggestionHostAndroid, String word); + private native void nativeSuggestionMenuClosed(long nativeTextSuggestionHostAndroid); +}
diff --git a/content/public/android/java/strings/android_content_strings.grd b/content/public/android/java/strings/android_content_strings.grd index 8d61fc8..b9fb615d 100644 --- a/content/public/android/java/strings/android_content_strings.grd +++ b/content/public/android/java/strings/android_content_strings.grd
@@ -102,6 +102,12 @@ <message name="IDS_ACTIONBAR_TEXTSELECTION_TITLE" desc="Title of the text selection contextual action bar on tablets. [CHAR-LIMIT=30]"> Text selection </message> + <message name="IDS_ADD_TO_DICTIONARY" desc="Button that adds the currently highlighted word to the spellcheck dictionary"> + Add to dictionary + </message> + <message name="IDS_DELETE_SUGGESTION_TEXT" desc="Button that deletes the currently highlighted word(s) from a spellcheck or voice correction menu"> + Delete + </message> <message name="IDS_MEDIA_PLAYER_ERROR_TITLE" desc="Title of dialog explaining that a video cannot be played due to an error [CHAR-LIMIT=32]"> Cannot play video </message>
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/PopupZoomerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/PopupZoomerTest.java index 9fd15aa..7ec3e47 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/PopupZoomerTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/PopupZoomerTest.java
@@ -23,6 +23,7 @@ import org.chromium.base.ThreadUtils; import org.chromium.base.test.util.Feature; import org.chromium.content.browser.input.ImeAdapter; +import org.chromium.content.browser.input.TextSuggestionHost; import org.chromium.content.browser.test.ContentJUnit4ClassRunner; import org.chromium.content.browser.test.util.TestInputMethodManagerWrapper; import org.chromium.content_public.browser.WebContents; @@ -104,6 +105,9 @@ new TestInputMethodManagerWrapper(mContentViewCore))); mPopupZoomer = createPopupZoomerForTest(InstrumentationRegistry.getTargetContext()); mContentViewCore.setPopupZoomerForTest(mPopupZoomer); + mContentViewCore.setTextSuggestionHostForTesting(new TextSuggestionHost(context, + webContents, mActivityTestRule.getContentViewCore().getContainerView(), + mContentViewCore, mContentViewCore.getRenderCoordinates())); } }); }
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json index 3aa60a3..3f9eb20a2 100644 --- a/content/public/app/mojo/content_browser_manifest.json +++ b/content/public/app/mojo/content_browser_manifest.json
@@ -113,6 +113,7 @@ "blink::mojom::PermissionService", "blink::mojom::PresentationService", "blink::mojom::SensitiveInputVisibilityService", + "blink::mojom::TextSuggestionHost", "blink::mojom::WebBluetoothService", "blink::mojom::WebSocket",
diff --git a/content/public/app/mojo/content_renderer_manifest.json b/content/public/app/mojo/content_renderer_manifest.json index a850e125..24821a14 100644 --- a/content/public/app/mojo/content_renderer_manifest.json +++ b/content/public/app/mojo/content_renderer_manifest.json
@@ -41,6 +41,7 @@ "blink::mojom::EngagementClient", "blink::mojom::InstallationService", "blink::mojom::ManifestManager", + "blink::mojom::TextSuggestionBackend", "content::mojom::ImageDownloader", "content::mojom::FrameInputHandler", "content::mojom::Widget",
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc index e24b7aaf..8f8998e 100644 --- a/content/renderer/gpu/render_widget_compositor.cc +++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -11,6 +11,7 @@ #include <string> #include <utility> +#include "base/base_switches.h" #include "base/callback.h" #include "base/command_line.h" #include "base/location.h" @@ -447,6 +448,7 @@ #if defined(OS_ANDROID) bool using_synchronous_compositor = GetContentClient()->UsingSynchronousCompositing(); + bool using_low_memory_policy = base::SysInfo::IsLowEndDevice(); settings.use_stream_video_draw_quad = true; settings.using_synchronous_renderer_compositor = using_synchronous_compositor; @@ -462,19 +464,11 @@ settings.ignore_root_layer_flings = using_synchronous_compositor; // Memory policy on Android WebView does not depend on whether device is // low end, so always use default policy. - bool use_low_memory_policy = - base::SysInfo::IsLowEndDevice() && !using_synchronous_compositor; - if (use_low_memory_policy) { + if (using_low_memory_policy && !using_synchronous_compositor) { // On low-end we want to be very carefull about killing other // apps. So initially we use 50% more memory to avoid flickering // or raster-on-demand. settings.max_memory_for_prepaint_percentage = 67; - - // RGBA_4444 textures are only enabled by default for low end devices - // and are disabled for Android WebView as it doesn't support the format. - if (!cmd.HasSwitch(switches::kDisableRGBA4444Textures) && - base::SysInfo::AmountOfPhysicalMemoryMB() <= 512) - settings.preferred_tile_format = viz::RGBA_4444; } else { // On other devices we have increased memory excessively to avoid // raster-on-demand already, so now we reserve 50% _only_ to avoid @@ -482,16 +476,15 @@ settings.max_memory_for_prepaint_percentage = 50; } - if (base::SysInfo::IsLowEndDevice()) { - // When running on a low end device, we limit cached bytes to 2MB. - // This allows a typical page to fit its images in cache, but prevents - // most long-term caching. - settings.decoded_image_cache_budget_bytes = 2 * 1024 * 1024; - } - // TODO(danakj): Only do this on low end devices. settings.create_low_res_tiling = true; #else // defined(OS_ANDROID) + bool using_synchronous_compositor = false; // Only for Android WebView. + // On desktop, we never use the low memory policy unless we are simulating + // low-end mode via a switch. + bool using_low_memory_policy = + cmd.HasSwitch(switches::kEnableLowEndDeviceMode); + if (ui::IsOverlayScrollbarEnabled()) { settings.scrollbar_animator = cc::LayerTreeSettings::AURA_OVERLAY; settings.scrollbar_fade_delay = ui::kOverlayScrollbarFadeDelay; @@ -512,9 +505,25 @@ settings.decoded_image_cache_budget_bytes = 128 * 1024 * 1024; settings.decoded_image_working_set_budget_bytes = 128 * 1024 * 1024; } - #endif // defined(OS_ANDROID) + if (using_low_memory_policy) { + // RGBA_4444 textures are only enabled: + // - If the user hasn't explicitly disabled them + // - If system ram is <= 512MB (1GB devices are sometimes low-end). + // - If we are not running in a WebView, where 4444 isn't supported. + if (!cmd.HasSwitch(switches::kDisableRGBA4444Textures) && + base::SysInfo::AmountOfPhysicalMemoryMB() <= 512 && + !using_synchronous_compositor) { + settings.preferred_tile_format = viz::RGBA_4444; + } + + // When running on a low end device, we limit cached bytes to 2MB. + // This allows a typical page to fit its images in cache, but prevents + // most long-term caching. + settings.decoded_image_cache_budget_bytes = 2 * 1024 * 1024; + } + if (cmd.HasSwitch(switches::kEnableLowResTiling)) settings.create_low_res_tiling = true; if (cmd.HasSwitch(switches::kDisableLowResTiling))
diff --git a/content/shell/browser/layout_test/layout_test_permission_manager.cc b/content/shell/browser/layout_test/layout_test_permission_manager.cc index 8bdfb75..e777949e2 100644 --- a/content/shell/browser/layout_test/layout_test_permission_manager.cc +++ b/content/shell/browser/layout_test/layout_test_permission_manager.cc
@@ -166,11 +166,12 @@ void LayoutTestPermissionManager::SetPermission( PermissionType permission, blink::mojom::PermissionStatus status, - const GURL& origin, - const GURL& embedding_origin) { + const GURL& url, + const GURL& embedding_url) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - PermissionDescription description(permission, origin, embedding_origin); + PermissionDescription description(permission, url.GetOrigin(), + embedding_url.GetOrigin()); base::AutoLock lock(permissions_lock_);
diff --git a/content/shell/browser/layout_test/layout_test_permission_manager.h b/content/shell/browser/layout_test/layout_test_permission_manager.h index 8a135b4e..76391a4 100644 --- a/content/shell/browser/layout_test/layout_test_permission_manager.h +++ b/content/shell/browser/layout_test/layout_test_permission_manager.h
@@ -56,8 +56,8 @@ void SetPermission(PermissionType permission, blink::mojom::PermissionStatus status, - const GURL& origin, - const GURL& embedding_origin); + const GURL& url, + const GURL& embedding_url); void ResetPermissions(); private:
diff --git a/extensions/browser/extensions_browser_client.cc b/extensions/browser/extensions_browser_client.cc index 8e7fe3e..3624e86d 100644 --- a/extensions/browser/extensions_browser_client.cc +++ b/extensions/browser/extensions_browser_client.cc
@@ -8,7 +8,6 @@ #include "components/update_client/update_client.h" #include "extensions/browser/extension_api_frame_id_map.h" #include "extensions/browser/extension_error.h" -#include "extensions/browser/updater/update_client_config.h" namespace extensions {
diff --git a/extensions/browser/updater/BUILD.gn b/extensions/browser/updater/BUILD.gn index 91279bd..12590472 100644 --- a/extensions/browser/updater/BUILD.gn +++ b/extensions/browser/updater/BUILD.gn
@@ -23,8 +23,6 @@ "request_queue_impl.h", "safe_manifest_parser.cc", "safe_manifest_parser.h", - "update_client_config.cc", - "update_client_config.h", "update_data_provider.cc", "update_data_provider.h", "update_install_shim.cc",
diff --git a/extensions/browser/updater/update_client_config.cc b/extensions/browser/updater/update_client_config.cc deleted file mode 100644 index 8c21593c..0000000 --- a/extensions/browser/updater/update_client_config.cc +++ /dev/null
@@ -1,32 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "extensions/browser/updater/update_client_config.h" - -#include "base/sequenced_task_runner.h" -#include "base/task_scheduler/post_task.h" -#include "base/task_scheduler/task_traits.h" -#include "build/build_config.h" - -namespace extensions { - -UpdateClientConfig::UpdateClientConfig() {} - -scoped_refptr<base::SequencedTaskRunner> -UpdateClientConfig::GetSequencedTaskRunner() const { - constexpr base::TaskTraits traits = { - base::MayBlock(), base::TaskPriority::BACKGROUND, - base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}; -#if defined(OS_WIN) - // Use the COM STA task runner as the Windows background downloader requires - // COM initialization. - return base::CreateCOMSTATaskRunnerWithTraits(traits); -#else - return base::CreateSequencedTaskRunnerWithTraits(traits); -#endif -} - -UpdateClientConfig::~UpdateClientConfig() {} - -} // namespace extensions
diff --git a/extensions/browser/updater/update_client_config.h b/extensions/browser/updater/update_client_config.h deleted file mode 100644 index e54fde4b..0000000 --- a/extensions/browser/updater/update_client_config.h +++ /dev/null
@@ -1,38 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef EXTENSIONS_BROWSER_UPDATER_UPDATE_CLIENT_CONFIG_H_ -#define EXTENSIONS_BROWSER_UPDATER_UPDATE_CLIENT_CONFIG_H_ - -#include <string> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "components/update_client/configurator.h" - -namespace base { -class SequencedTaskRunner; -} - -namespace extensions { - -// Used to provide configuration settings to the UpdateClient. -class UpdateClientConfig : public update_client::Configurator { - public: - UpdateClientConfig(); - - scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() - const override; - - protected: - friend class base::RefCountedThreadSafe<UpdateClientConfig>; - ~UpdateClientConfig() override; - - private: - DISALLOW_COPY_AND_ASSIGN(UpdateClientConfig); -}; - -} // namespace extensions - -#endif // EXTENSIONS_BROWSER_UPDATER_UPDATE_CLIENT_CONFIG_H_
diff --git a/ios/chrome/browser/component_updater/ios_component_updater_configurator.cc b/ios/chrome/browser/component_updater/ios_component_updater_configurator.cc index 10fc8d9..484fe768 100644 --- a/ios/chrome/browser/component_updater/ios_component_updater_configurator.cc +++ b/ios/chrome/browser/component_updater/ios_component_updater_configurator.cc
@@ -9,7 +9,6 @@ #include <string> #include <vector> -#include "base/task_scheduler/post_task.h" #include "base/version.h" #include "components/component_updater/configurator_impl.h" #include "components/update_client/out_of_process_patcher.h" @@ -49,8 +48,6 @@ bool EnabledComponentUpdates() const override; bool EnabledBackgroundDownloader() const override; bool EnabledCupSigning() const override; - scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() - const override; PrefService* GetPrefService() const override; bool IsPerUserInstall() const override; std::vector<uint8_t> GetRunActionKeyHash() const override; @@ -155,13 +152,6 @@ return configurator_impl_.EnabledCupSigning(); } -scoped_refptr<base::SequencedTaskRunner> -IOSConfigurator::GetSequencedTaskRunner() const { - return base::CreateSequencedTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::BACKGROUND, - base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); -} - PrefService* IOSConfigurator::GetPrefService() const { return GetApplicationContext()->GetLocalState(); }
diff --git a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.h b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.h index 8255216..14736b2 100644 --- a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.h +++ b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.h
@@ -33,13 +33,13 @@ ~MobileSessionShutdownMetricsProvider() override; // metrics::MetricsProvider - bool HasInitialStabilityMetrics() override; - void ProvideInitialStabilityMetrics( - metrics::SystemProfileProto* system_profile_proto) override; + bool HasPreviousSessionData() override; + void ProvidePreviousSessionData( + metrics::ChromeUserMetricsExtension* uma_proto) override; protected: // Provides information on the last session environment, used to decide what - // stability metrics to provide in ProvideInitialStabilityMetrics. + // stability metrics to provide in ProvidePreviousSessionData. // These methods are virtual to be overridden in the tests. // The default implementations return the real values.
diff --git a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm index 1f076c8..87c8c61 100644 --- a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm +++ b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider.mm
@@ -35,12 +35,12 @@ MobileSessionShutdownMetricsProvider::~MobileSessionShutdownMetricsProvider() {} -bool MobileSessionShutdownMetricsProvider::HasInitialStabilityMetrics() { +bool MobileSessionShutdownMetricsProvider::HasPreviousSessionData() { return true; } -void MobileSessionShutdownMetricsProvider::ProvideInitialStabilityMetrics( - metrics::SystemProfileProto* system_profile_proto) { +void MobileSessionShutdownMetricsProvider::ProvidePreviousSessionData( + metrics::ChromeUserMetricsExtension* uma_proto) { // If this is the first launch after an upgrade, existing crash reports may // have been deleted before this code runs, so log this case in its own // bucket.
diff --git a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm index b99c59d..83c1a84 100644 --- a/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm +++ b/ios/chrome/browser/metrics/mobile_session_shutdown_metrics_provider_unittest.mm
@@ -172,7 +172,7 @@ // Now call the method under test and verify exactly one sample is written to // the expected bucket. - metrics_provider_->ProvideInitialStabilityMetrics(nullptr); + metrics_provider_->ProvidePreviousSessionData(nullptr); histogram_tester.ExpectUniqueSample("Stability.MobileSessionShutdownType", expected_buckets[GetParam()], 1); }
diff --git a/ios/chrome/browser/payments/BUILD.gn b/ios/chrome/browser/payments/BUILD.gn index c4ce1d4..9fd3a82a 100644 --- a/ios/chrome/browser/payments/BUILD.gn +++ b/ios/chrome/browser/payments/BUILD.gn
@@ -21,6 +21,8 @@ "payment_request_cache.mm", "payment_request_util.h", "payment_request_util.mm", + "payment_response_helper.h", + "payment_response_helper.mm", ] deps = [ "//base", @@ -47,6 +49,7 @@ testonly = true sources = [ "payment_request_unittest.mm", + "payment_response_helper_unittest.mm", ] deps = [ ":payments", @@ -58,6 +61,8 @@ "//components/payments/core", "//ios/chrome/browser", "//ios/chrome/browser/browser_state:test_support", + "//ios/chrome/browser/web:test_support", + "//ios/testing:ocmock_support", "//ios/web", "//ios/web/public/test/fakes", "//testing/gmock", @@ -80,6 +85,7 @@ "//components/autofill/core/browser", "//components/autofill/core/browser:test_support", "//components/payments/core", + "//components/payments/core:test_support", "//components/prefs", "//ios/web", ]
diff --git a/ios/chrome/browser/payments/payment_request.h b/ios/chrome/browser/payments/payment_request.h index 0fb989db..e372fe1 100644 --- a/ios/chrome/browser/payments/payment_request.h +++ b/ios/chrome/browser/payments/payment_request.h
@@ -21,6 +21,7 @@ #include "components/payments/core/payment_options_provider.h" #include "components/payments/core/payment_request_base_delegate.h" #include "components/payments/core/payments_profile_comparator.h" +#import "ios/chrome/browser/payments/payment_response_helper.h" #include "ios/web/public/payments/payment_request.h" namespace autofill { @@ -31,8 +32,8 @@ namespace payments { class AddressNormalizer; -class CurrencyFormatter; class AutofillPaymentInstrument; +class CurrencyFormatter; } // namespace payments namespace ios { @@ -132,9 +133,8 @@ // hence the CurrencyFormatter is cached here. CurrencyFormatter* GetOrCreateCurrencyFormatter(); - AddressNormalizationManager* address_normalization_manager() { - return &address_normalization_manager_; - } + // Returns the AddressNormalizationManager for this instance. + virtual AddressNormalizationManager* GetAddressNormalizationManager(); // Adds |profile| to the list of cached profiles, updates the list of // available shipping and contact profiles, and returns a reference to the @@ -242,6 +242,12 @@ // Returns whether the current PaymentRequest can be used to make a payment. bool CanMakePayment() const; + // Invokes the appropriate payment app for the selected payment method. + void InvokePaymentApp(id<PaymentResponseHelperConsumer> consumer); + + // Returns whether the payment app has been invoked. + bool IsPaymentAppInvoked() const; + // Record the use of the data models that were used in the Payment Request. void RecordUseStats(); @@ -346,6 +352,8 @@ // Keeps track of different stats during the lifetime of this object. JourneyLogger journey_logger_; + std::unique_ptr<PaymentResponseHelper> response_helper_; + DISALLOW_COPY_AND_ASSIGN(PaymentRequest); };
diff --git a/ios/chrome/browser/payments/payment_request.mm b/ios/chrome/browser/payments/payment_request.mm index 499e82c..7bcb836 100644 --- a/ios/chrome/browser/payments/payment_request.mm +++ b/ios/chrome/browser/payments/payment_request.mm
@@ -7,6 +7,7 @@ #include <algorithm> #include "base/containers/adapters.h" +#include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" @@ -228,6 +229,10 @@ return currency_formatter_.get(); } +AddressNormalizationManager* PaymentRequest::GetAddressNormalizationManager() { + return &address_normalization_manager_; +} + autofill::AutofillProfile* PaymentRequest::AddAutofillProfile( const autofill::AutofillProfile& profile) { profile_cache_.push_back( @@ -336,6 +341,17 @@ return false; } +void PaymentRequest::InvokePaymentApp( + id<PaymentResponseHelperConsumer> consumer) { + DCHECK(selected_payment_method()); + response_helper_ = base::MakeUnique<PaymentResponseHelper>(consumer, this); + selected_payment_method()->InvokePaymentApp(response_helper_.get()); +} + +bool PaymentRequest::IsPaymentAppInvoked() const { + return !!response_helper_; +} + void PaymentRequest::RecordUseStats() { if (request_shipping()) { DCHECK(selected_shipping_profile_);
diff --git a/ios/chrome/browser/payments/payment_response_helper.h b/ios/chrome/browser/payments/payment_response_helper.h new file mode 100644 index 0000000..42d87b4 --- /dev/null +++ b/ios/chrome/browser/payments/payment_response_helper.h
@@ -0,0 +1,70 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_PAYMENTS_PAYMENT_RESPONSE_HELPER_H_ +#define IOS_CHROME_BROWSER_PAYMENTS_PAYMENT_RESPONSE_HELPER_H_ + +#include <string> + +#import <UIKit/UIKit.h> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "components/autofill/core/browser/autofill_profile.h" +#include "components/payments/core/payment_instrument.h" +#include "ios/web/public/payments/payment_request.h" + +@protocol PaymentResponseHelperConsumer + +// Called when the payment method details have been successfully received. +- (void)paymentResponseHelperDidReceivePaymentMethodDetails; + +// Called when the payment method details have been successfully received and +// the shipping address and the contact info are normalized, if applicable. +- (void)paymentResponseHelperDidCompleteWithPaymentResponse: + (const web::PaymentResponse&)paymentResponse; + +@end + +namespace payments { + +class PaymentRequest; + +// A helper class to facilitate creation of the PaymentResponse. +class PaymentResponseHelper + : public PaymentInstrument::Delegate, + public base::SupportsWeakPtr<PaymentResponseHelper> { + public: + PaymentResponseHelper(id<PaymentResponseHelperConsumer> consumer, + PaymentRequest* payment_request); + ~PaymentResponseHelper() override; + + // PaymentInstrument::Delegate + void OnInstrumentDetailsReady( + const std::string& method_name, + const std::string& stringified_details) override; + void OnInstrumentDetailsError() override {} + + private: + // Called when the AddressNormalizationManager is done, whether any autofill + // profile is actually normalized. + void AddressNormalizationCompleted(); + + __weak id<PaymentResponseHelperConsumer> consumer_; + + // Owns this instance and is guaranteed to outlive it. + PaymentRequest* payment_request_; + + // Stored data to use in the payment response once normalization is complete. + std::string method_name_; + std::string stringified_details_; + autofill::AutofillProfile shipping_address_; + autofill::AutofillProfile contact_info_; + + DISALLOW_COPY_AND_ASSIGN(PaymentResponseHelper); +}; + +} // namespace payments + +#endif // IOS_CHROME_BROWSER_PAYMENTS_PAYMENT_RESPONSE_HELPER_H_
diff --git a/ios/chrome/browser/payments/payment_response_helper.mm b/ios/chrome/browser/payments/payment_response_helper.mm new file mode 100644 index 0000000..d108430d --- /dev/null +++ b/ios/chrome/browser/payments/payment_response_helper.mm
@@ -0,0 +1,106 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/chrome/browser/payments/payment_response_helper.h" + +#include "base/bind.h" +#include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/browser/autofill_country.h" +#include "components/autofill/core/browser/autofill_type.h" +#include "components/payments/core/address_normalization_manager.h" +#include "components/payments/core/journey_logger.h" +#include "components/payments/core/payment_request_data_util.h" +#include "ios/chrome/browser/payments/payment_request.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace payments { + +PaymentResponseHelper::PaymentResponseHelper( + id<PaymentResponseHelperConsumer> consumer, + PaymentRequest* payment_request) + : consumer_(consumer), payment_request_(payment_request) {} + +PaymentResponseHelper::~PaymentResponseHelper() {} + +void PaymentResponseHelper::OnInstrumentDetailsReady( + const std::string& method_name, + const std::string& stringified_details) { + method_name_ = method_name; + stringified_details_ = stringified_details; + + [consumer_ paymentResponseHelperDidReceivePaymentMethodDetails]; + + payment_request_->journey_logger().SetEventOccurred( + JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS); + + if (payment_request_->request_shipping()) { + DCHECK(payment_request_->selected_shipping_profile()); + shipping_address_ = *payment_request_->selected_shipping_profile(); + payment_request_->GetAddressNormalizationManager()->StartNormalizingAddress( + &shipping_address_); + } + + if (payment_request_->request_payer_name() || + payment_request_->request_payer_email() || + payment_request_->request_payer_phone()) { + DCHECK(payment_request_->selected_contact_profile()); + contact_info_ = *payment_request_->selected_contact_profile(); + payment_request_->GetAddressNormalizationManager()->StartNormalizingAddress( + &contact_info_); + } + + payment_request_->GetAddressNormalizationManager() + ->FinalizePendingRequestsWithCompletionCallback(base::Bind( + &PaymentResponseHelper::AddressNormalizationCompleted, AsWeakPtr())); +} + +void PaymentResponseHelper::AddressNormalizationCompleted() { + web::PaymentResponse response; + + response.payment_request_id = + payment_request_->web_payment_request().payment_request_id; + response.method_name = method_name_; + response.details = stringified_details_; + + if (payment_request_->request_shipping()) { + response.shipping_address = data_util::GetPaymentAddressFromAutofillProfile( + shipping_address_, payment_request_->GetApplicationLocale()); + + web::PaymentShippingOption* shippingOption = + payment_request_->selected_shipping_option(); + DCHECK(shippingOption); + response.shipping_option = shippingOption->id; + } + + if (payment_request_->request_payer_name()) { + response.payer_name = + contact_info_.GetInfo(autofill::AutofillType(autofill::NAME_FULL), + payment_request_->GetApplicationLocale()); + } + if (payment_request_->request_payer_email()) { + response.payer_email = contact_info_.GetRawInfo(autofill::EMAIL_ADDRESS); + } + if (payment_request_->request_payer_phone()) { + // Try to format the phone number to the E.164 format to send in the Payment + // Response, as defined in the Payment Request spec. If it's not possible, + // send the original. More info at: + // https://w3c.github.io/browser-payment-api/#paymentrequest-updated-algorithm + const std::string original_number = base::UTF16ToUTF8(contact_info_.GetInfo( + autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER), + payment_request_->GetApplicationLocale())); + + const std::string default_region_code = + autofill::AutofillCountry::CountryCodeForLocale( + payment_request_->GetApplicationLocale()); + response.payer_phone = base::UTF8ToUTF16(data_util::FormatPhoneForResponse( + original_number, default_region_code)); + } + + [consumer_ paymentResponseHelperDidCompleteWithPaymentResponse:response]; +} + +} // namespace payments
diff --git a/ios/chrome/browser/payments/payment_response_helper_unittest.mm b/ios/chrome/browser/payments/payment_response_helper_unittest.mm new file mode 100644 index 0000000..23fc7d5d --- /dev/null +++ b/ios/chrome/browser/payments/payment_response_helper_unittest.mm
@@ -0,0 +1,294 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/chrome/browser/payments/payment_response_helper.h" + +#include <string> + +#include "base/json/json_writer.h" +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_task_environment.h" +#include "components/autofill/core/browser/autofill_data_util.h" +#include "components/autofill/core/browser/autofill_profile.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/credit_card.h" +#include "components/autofill/core/browser/test_personal_data_manager.h" +#include "components/payments/core/basic_card_response.h" +#include "components/payments/core/payment_request_data_util.h" +#import "ios/chrome/browser/browser_state/test_chrome_browser_state.h" +#include "ios/chrome/browser/payments/payment_request_test_util.h" +#import "ios/chrome/browser/payments/test_payment_request.h" +#import "ios/chrome/browser/web/chrome_web_test.h" +#import "ios/testing/ocmock_complex_type_helper.h" +#import "ios/web/public/test/fakes/test_web_state.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" +#include "third_party/ocmock/OCMock/OCMock.h" +#include "third_party/ocmock/gtest_support.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@interface PaymentResponseHelperConsumerMock + : OCMockComplexTypeHelper<PaymentResponseHelperConsumer> +@end + +@implementation PaymentResponseHelperConsumerMock + +typedef void (^mock_payment_response_helper_did_complete_with_payment_response)( + const web::PaymentResponse&); + +- (void)paymentResponseHelperDidReceivePaymentMethodDetails { +} + +- (void)paymentResponseHelperDidCompleteWithPaymentResponse: + (const web::PaymentResponse&)paymentResponse { + return static_cast< + mock_payment_response_helper_did_complete_with_payment_response>( + [self blockForSelector:_cmd])(paymentResponse); +} + +@end + +namespace payments { + +class PaymentRequestPaymentResponseHelperTest : public PlatformTest { + protected: + PaymentRequestPaymentResponseHelperTest() + : profile_(autofill::test::GetFullProfile()), + credit_card_(autofill::test::GetCreditCard()), + chrome_browser_state_(TestChromeBrowserState::Builder().Build()) { + personal_data_manager_.AddTestingProfile(&profile_); + payment_request_ = base::MakeUnique<TestPaymentRequest>( + payment_request_test_util::CreateTestWebPaymentRequest(), + chrome_browser_state_.get(), &web_state_, &personal_data_manager_); + } + + std::string GetMethodName() { + return autofill::data_util::GetPaymentRequestData(credit_card_.network()) + .basic_card_issuer_network; + } + + std::string GetStringifiedDetails() { + autofill::AutofillProfile billing_address; + std::unique_ptr<base::DictionaryValue> response_value = + data_util::GetBasicCardResponseFromAutofillCreditCard( + credit_card_, base::ASCIIToUTF16("123"), billing_address, "en-US") + .ToDictionaryValue(); + std::string stringified_details; + base::JSONWriter::Write(*response_value, &stringified_details); + return stringified_details; + } + + TestPaymentRequest* payment_request() { return payment_request_.get(); } + + private: + base::test::ScopedTaskEnvironment scoped_task_evironment_; + + autofill::AutofillProfile profile_; + autofill::CreditCard credit_card_; + web::TestWebState web_state_; + autofill::TestPersonalDataManager personal_data_manager_; + std::unique_ptr<TestChromeBrowserState> chrome_browser_state_; + std::unique_ptr<TestPaymentRequest> payment_request_; +}; + +// Tests that calling the PaymentResponseHelper's delegate method which signals +// that the full payment method details have been successfully received, causes +// the PaymentResponseHelper's delegate method to get called with the +// appropriate payment response. +TEST_F(PaymentRequestPaymentResponseHelperTest, PaymentResponse) { + // Mock the consumer. + id consumer = + [OCMockObject mockForProtocol:@protocol(PaymentResponseHelperConsumer)]; + id consumer_mock([[PaymentResponseHelperConsumerMock alloc] + initWithRepresentedObject:consumer]); + SEL selector = + @selector(paymentResponseHelperDidCompleteWithPaymentResponse:); + [consumer_mock onSelector:selector + callBlockExpectation:^(const web::PaymentResponse& response) { + // Check if all the expected values were set. + EXPECT_EQ(GetMethodName(), response.method_name); + EXPECT_EQ(GetStringifiedDetails(), response.details); + + EXPECT_EQ(base::ASCIIToUTF16("US"), response.shipping_address.country); + ASSERT_EQ(2U, response.shipping_address.address_line.size()); + EXPECT_EQ(base::ASCIIToUTF16("666 Erebus St."), + response.shipping_address.address_line[0]); + EXPECT_EQ(base::ASCIIToUTF16("Apt 8"), + response.shipping_address.address_line[1]); + EXPECT_EQ(base::ASCIIToUTF16("CA"), response.shipping_address.region); + EXPECT_EQ(base::ASCIIToUTF16("Elysium"), + response.shipping_address.city); + EXPECT_EQ(base::string16(), + response.shipping_address.dependent_locality); + EXPECT_EQ(base::ASCIIToUTF16("91111"), + response.shipping_address.postal_code); + EXPECT_EQ(base::string16(), response.shipping_address.sorting_code); + EXPECT_EQ(base::string16(), response.shipping_address.language_code); + EXPECT_EQ(base::ASCIIToUTF16("Underworld"), + response.shipping_address.organization); + EXPECT_EQ(base::ASCIIToUTF16("John H. Doe"), + response.shipping_address.recipient); + EXPECT_EQ(base::ASCIIToUTF16("16502111111"), + response.shipping_address.phone); + + EXPECT_EQ(base::ASCIIToUTF16("John H. Doe"), response.payer_name); + EXPECT_EQ(base::ASCIIToUTF16("+16502111111"), response.payer_phone); + EXPECT_EQ(base::ASCIIToUTF16("johndoe@hades.com"), + response.payer_email); + }]; + + PaymentResponseHelper payment_response_helper(consumer_mock, + payment_request()); + payment_response_helper.OnInstrumentDetailsReady(GetMethodName(), + GetStringifiedDetails()); +} + +// Tests that the generated PaymentResponse has a shipping address only if one +// is requested. +TEST_F(PaymentRequestPaymentResponseHelperTest, PaymentResponseNoShipping) { + // Mock the consumer. + id consumer = + [OCMockObject mockForProtocol:@protocol(PaymentResponseHelperConsumer)]; + id consumer_mock([[PaymentResponseHelperConsumerMock alloc] + initWithRepresentedObject:consumer]); + SEL selector = + @selector(paymentResponseHelperDidCompleteWithPaymentResponse:); + [consumer_mock onSelector:selector + callBlockExpectation:^(const web::PaymentResponse& response) { + EXPECT_EQ(base::string16(), response.shipping_address.country); + EXPECT_TRUE(response.shipping_address.address_line.empty()); + EXPECT_EQ(base::string16(), response.shipping_address.region); + EXPECT_EQ(base::string16(), response.shipping_address.city); + EXPECT_EQ(base::string16(), + response.shipping_address.dependent_locality); + EXPECT_EQ(base::string16(), response.shipping_address.postal_code); + EXPECT_EQ(base::string16(), response.shipping_address.sorting_code); + EXPECT_EQ(base::string16(), response.shipping_address.language_code); + EXPECT_EQ(base::string16(), response.shipping_address.organization); + EXPECT_EQ(base::string16(), response.shipping_address.recipient); + EXPECT_EQ(base::string16(), response.shipping_address.phone); + + EXPECT_EQ(base::ASCIIToUTF16("John H. Doe"), response.payer_name); + EXPECT_EQ(base::ASCIIToUTF16("+16502111111"), response.payer_phone); + EXPECT_EQ(base::ASCIIToUTF16("johndoe@hades.com"), + response.payer_email); + }]; + + payment_request()->web_payment_request().options.request_shipping = false; + PaymentResponseHelper payment_response_helper(consumer_mock, + payment_request()); + payment_response_helper.OnInstrumentDetailsReady(GetMethodName(), + GetStringifiedDetails()); +} + +// Tests that the generated PaymentResponse has contact information only if it +// is requested. +TEST_F(PaymentRequestPaymentResponseHelperTest, PaymentResponseNoContact) { + // Mock the consumer. + id consumer = + [OCMockObject mockForProtocol:@protocol(PaymentResponseHelperConsumer)]; + id consumer_mock([[PaymentResponseHelperConsumerMock alloc] + initWithRepresentedObject:consumer]); + SEL selector = + @selector(paymentResponseHelperDidCompleteWithPaymentResponse:); + [consumer_mock onSelector:selector + callBlockExpectation:^(const web::PaymentResponse& response) { + EXPECT_EQ(base::string16(), response.payer_name); + EXPECT_EQ(base::string16(), response.payer_phone); + EXPECT_EQ(base::string16(), response.payer_email); + }]; + + payment_request()->web_payment_request().options.request_payer_name = false; + payment_request()->web_payment_request().options.request_payer_phone = false; + payment_request()->web_payment_request().options.request_payer_email = false; + PaymentResponseHelper payment_response_helper(consumer_mock, + payment_request()); + payment_response_helper.OnInstrumentDetailsReady(GetMethodName(), + GetStringifiedDetails()); +} + +// Tests that the generated PaymentResponse has contact information only if it +// is requested. +TEST_F(PaymentRequestPaymentResponseHelperTest, PaymentResponseOneContact) { + // Mock the consumer. + id consumer = + [OCMockObject mockForProtocol:@protocol(PaymentResponseHelperConsumer)]; + id consumer_mock([[PaymentResponseHelperConsumerMock alloc] + initWithRepresentedObject:consumer]); + SEL selector = + @selector(paymentResponseHelperDidCompleteWithPaymentResponse:); + [consumer_mock onSelector:selector + callBlockExpectation:^(const web::PaymentResponse& response) { + EXPECT_EQ(base::ASCIIToUTF16("John H. Doe"), response.payer_name); + EXPECT_EQ(base::string16(), response.payer_phone); + EXPECT_EQ(base::string16(), response.payer_email); + }]; + + payment_request()->web_payment_request().options.request_payer_phone = false; + payment_request()->web_payment_request().options.request_payer_email = false; + PaymentResponseHelper payment_response_helper(consumer_mock, + payment_request()); + payment_response_helper.OnInstrumentDetailsReady(GetMethodName(), + GetStringifiedDetails()); +} + +// Tests that the generated PaymentResponse has contact information only if it +// is requested. +TEST_F(PaymentRequestPaymentResponseHelperTest, PaymentResponseSomeContact) { + // Mock the consumer. + id consumer = + [OCMockObject mockForProtocol:@protocol(PaymentResponseHelperConsumer)]; + id consumer_mock([[PaymentResponseHelperConsumerMock alloc] + initWithRepresentedObject:consumer]); + SEL selector = + @selector(paymentResponseHelperDidCompleteWithPaymentResponse:); + [consumer_mock onSelector:selector + callBlockExpectation:^(const web::PaymentResponse& response) { + EXPECT_EQ(base::ASCIIToUTF16("John H. Doe"), response.payer_name); + EXPECT_EQ(base::ASCIIToUTF16("johndoe@hades.com"), + response.payer_email); + EXPECT_EQ(base::string16(), response.payer_phone); + }]; + + payment_request()->web_payment_request().options.request_payer_phone = false; + PaymentResponseHelper payment_response_helper(consumer_mock, + payment_request()); + payment_response_helper.OnInstrumentDetailsReady(GetMethodName(), + GetStringifiedDetails()); +} + +// Tests that the phone number in the contact information of the generated +// PaymentResponse is formatted. +TEST_F(PaymentRequestPaymentResponseHelperTest, + PaymentResponseContactPhoneIsFormatted) { + // Mock the consumer. + id consumer = + [OCMockObject mockForProtocol:@protocol(PaymentResponseHelperConsumer)]; + id consumer_mock([[PaymentResponseHelperConsumerMock alloc] + initWithRepresentedObject:consumer]); + SEL selector = + @selector(paymentResponseHelperDidCompleteWithPaymentResponse:); + [consumer_mock onSelector:selector + callBlockExpectation:^(const web::PaymentResponse& response) { + EXPECT_EQ(base::ASCIIToUTF16("+15151231234"), response.payer_phone); + }]; + + payment_request()->selected_contact_profile()->SetRawInfo( + autofill::PHONE_HOME_WHOLE_NUMBER, base::UTF8ToUTF16("(515) 123-1234")); + + payment_request()->web_payment_request().options.request_payer_name = false; + payment_request()->web_payment_request().options.request_payer_email = false; + PaymentResponseHelper payment_response_helper(consumer_mock, + payment_request()); + payment_response_helper.OnInstrumentDetailsReady(GetMethodName(), + GetStringifiedDetails()); +} + +} // payments
diff --git a/ios/chrome/browser/payments/test_payment_request.h b/ios/chrome/browser/payments/test_payment_request.h index b669e245..5aa9e8d 100644 --- a/ios/chrome/browser/payments/test_payment_request.h +++ b/ios/chrome/browser/payments/test_payment_request.h
@@ -6,6 +6,8 @@ #define IOS_CHROME_BROWSER_PAYMENTS_TEST_PAYMENT_REQUEST_H_ #include "base/macros.h" +#include "components/payments/core/address_normalization_manager.h" +#include "components/payments/core/test_address_normalizer.h" #include "ios/chrome/browser/payments/payment_request.h" namespace autofill { @@ -46,6 +48,7 @@ web_state, personal_data_manager, payment_request_ui_delegate), + address_normalization_manager_(&address_normalizer_, "US"), region_data_loader_(nullptr), pref_service_(nullptr), profile_comparator_(nullptr) {} @@ -93,11 +96,16 @@ } // PaymentRequest + AddressNormalizer* GetAddressNormalizer() override; + AddressNormalizationManager* GetAddressNormalizationManager() override; autofill::RegionDataLoader* GetRegionDataLoader() override; PrefService* GetPrefService() override; PaymentsProfileComparator* profile_comparator() override; private: + TestAddressNormalizer address_normalizer_; + AddressNormalizationManager address_normalization_manager_; + // Not owned and must outlive this object. autofill::RegionDataLoader* region_data_loader_;
diff --git a/ios/chrome/browser/payments/test_payment_request.mm b/ios/chrome/browser/payments/test_payment_request.mm index 95320576..0ffdd8b 100644 --- a/ios/chrome/browser/payments/test_payment_request.mm +++ b/ios/chrome/browser/payments/test_payment_request.mm
@@ -28,6 +28,15 @@ payment_methods_.clear(); } +AddressNormalizer* TestPaymentRequest::GetAddressNormalizer() { + return &address_normalizer_; +} + +AddressNormalizationManager* +TestPaymentRequest::GetAddressNormalizationManager() { + return &address_normalization_manager_; +} + autofill::RegionDataLoader* TestPaymentRequest::GetRegionDataLoader() { if (region_data_loader_) return region_data_loader_;
diff --git a/ios/chrome/browser/translate/translate_ranker_metrics_provider.cc b/ios/chrome/browser/translate/translate_ranker_metrics_provider.cc index bbcab3b..e0fc172 100644 --- a/ios/chrome/browser/translate/translate_ranker_metrics_provider.cc +++ b/ios/chrome/browser/translate/translate_ranker_metrics_provider.cc
@@ -15,7 +15,7 @@ namespace translate { -void TranslateRankerMetricsProvider::ProvideGeneralMetrics( +void TranslateRankerMetricsProvider::ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) { std::vector<ios::ChromeBrowserState*> browser_states = GetApplicationContext()
diff --git a/ios/chrome/browser/translate/translate_ranker_metrics_provider.h b/ios/chrome/browser/translate/translate_ranker_metrics_provider.h index 21c9d78..22c17965 100644 --- a/ios/chrome/browser/translate/translate_ranker_metrics_provider.h +++ b/ios/chrome/browser/translate/translate_ranker_metrics_provider.h
@@ -16,7 +16,7 @@ ~TranslateRankerMetricsProvider() override {} // From metrics::MetricsProvider... - void ProvideGeneralMetrics( + void ProvideCurrentSessionData( metrics::ChromeUserMetricsExtension* uma_proto) override; void OnRecordingEnabled() override; void OnRecordingDisabled() override;
diff --git a/ios/chrome/browser/ui/payments/full_card_requester.h b/ios/chrome/browser/ui/payments/full_card_requester.h index c6092c7..14fe406 100644 --- a/ios/chrome/browser/ui/payments/full_card_requester.h +++ b/ios/chrome/browser/ui/payments/full_card_requester.h
@@ -14,7 +14,6 @@ #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/payments/full_card_request.h" #include "components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h" -#include "components/payments/core/payment_instrument.h" namespace autofill { class AutofillManager; @@ -25,24 +24,12 @@ class ChromeBrowserState; } // namespace ios -@protocol FullCardRequesterConsumer - -// Called when a credit card has been successfully unmasked. Should be -// called wth method name (e.g., "visa") and json-serialized details. -- (void)fullCardRequestDidSucceedWithMethodName:(const std::string&)methodName - stringifiedDetails: - (const std::string&)stringifiedDetails; - -@end - // Receives the full credit card details. Also displays the unmask prompt UI. class FullCardRequester - : public payments::PaymentInstrument::Delegate, - public autofill::payments::FullCardRequest::UIDelegate, + : public autofill::payments::FullCardRequest::UIDelegate, public base::SupportsWeakPtr<FullCardRequester> { public: - FullCardRequester(id<FullCardRequesterConsumer> consumer, - UIViewController* base_view_controller, + FullCardRequester(UIViewController* base_view_controller, ios::ChromeBrowserState* browser_state); void GetFullCard( @@ -51,12 +38,6 @@ base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate> result_delegate); - // payments::PaymentInstrument::Delegate: - void OnInstrumentDetailsReady( - const std::string& method_name, - const std::string& stringified_details) override; - void OnInstrumentDetailsError() override{}; - // payments::FullCardRequest::UIDelegate: void ShowUnmaskPrompt( const autofill::CreditCard& card, @@ -66,7 +47,6 @@ autofill::AutofillClient::PaymentsRpcResult result) override; private: - __weak id<FullCardRequesterConsumer> consumer_; __weak UIViewController* base_view_controller_; autofill::CardUnmaskPromptControllerImpl unmask_controller_;
diff --git a/ios/chrome/browser/ui/payments/full_card_requester.mm b/ios/chrome/browser/ui/payments/full_card_requester.mm index 55e0995..8a8afd5 100644 --- a/ios/chrome/browser/ui/payments/full_card_requester.mm +++ b/ios/chrome/browser/ui/payments/full_card_requester.mm
@@ -42,11 +42,9 @@ } // namespace -FullCardRequester::FullCardRequester(id<FullCardRequesterConsumer> consumer, - UIViewController* base_view_controller, +FullCardRequester::FullCardRequester(UIViewController* base_view_controller, ios::ChromeBrowserState* browser_state) - : consumer_(consumer), - base_view_controller_(base_view_controller), + : base_view_controller_(base_view_controller), unmask_controller_(browser_state->GetPrefs(), browser_state->IsOffTheRecord()) {} @@ -62,13 +60,6 @@ result_delegate, AsWeakPtr()); } -void FullCardRequester::OnInstrumentDetailsReady( - const std::string& method_name, - const std::string& stringified_details) { - [consumer_ fullCardRequestDidSucceedWithMethodName:method_name - stringifiedDetails:stringified_details]; -} - void FullCardRequester::ShowUnmaskPrompt( const autofill::CreditCard& card, autofill::AutofillClient::UnmaskCardReason reason,
diff --git a/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm b/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm index 8abed84..8f57d1be 100644 --- a/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm +++ b/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm
@@ -16,7 +16,6 @@ #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/credit_card.h" #include "components/autofill/ios/browser/autofill_driver_ios.h" -#include "components/payments/core/autofill_payment_instrument.h" #include "components/payments/core/basic_card_response.h" #include "components/payments/core/payment_request_data_util.h" #import "ios/chrome/browser/autofill/autofill_agent.h" @@ -26,34 +25,13 @@ #include "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h" #import "ios/chrome/browser/web/chrome_web_test.h" #import "ios/chrome/test/scoped_key_window.h" -#import "ios/testing/ocmock_complex_type_helper.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/ocmock/OCMock/OCMock.h" #include "third_party/ocmock/gtest_support.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." #endif -@interface FullCardRequesterConsumerMock - : OCMockComplexTypeHelper<FullCardRequesterConsumer> -@end - -@implementation FullCardRequesterConsumerMock - -typedef void (^mock_full_card_request_did_succeed_with_method_name)( - const std::string&, - const std::string&); - -- (void)fullCardRequestDidSucceedWithMethodName:(const std::string&)methodName - stringifiedDetails: - (const std::string&)stringifiedDetails { - return static_cast<mock_full_card_request_did_succeed_with_method_name>( - [self blockForSelector:_cmd])(methodName, stringifiedDetails); -} - -@end - class FakeResultDelegate : public autofill::payments::FullCardRequest::ResultDelegate { public: @@ -119,7 +97,7 @@ ScopedKeyWindow scoped_key_window_; [scoped_key_window_.Get() setRootViewController:base_view_controller]; - FullCardRequester full_card_requester(nil, base_view_controller, + FullCardRequester full_card_requester(base_view_controller, chrome_browser_state_.get()); EXPECT_EQ(nil, base_view_controller.presentedViewController); @@ -146,42 +124,3 @@ }); EXPECT_EQ(nil, base_view_controller.presentedViewController); } - -// Tests that calling the FullCardRequester's delegate method which signals that -// the full credit card details have been successfully received, causes the -// FullCardRequester's delegate method to get called. -TEST_F(PaymentRequestFullCardRequesterTest, InstrumentDetailsReady) { - // Mock the consumer. - id consumer = - [OCMockObject mockForProtocol:@protocol(FullCardRequesterConsumer)]; - id consumer_mock([[FullCardRequesterConsumerMock alloc] - initWithRepresentedObject:consumer]); - SEL selector = - @selector(fullCardRequestDidSucceedWithMethodName:stringifiedDetails:); - [consumer_mock onSelector:selector - callBlockExpectation:^(const std::string& methodName, - const std::string& stringifiedDetails) { - EXPECT_EQ("visa", methodName); - - std::string cvc; - std::unique_ptr<base::DictionaryValue> detailsDict = - base::DictionaryValue::From( - base::JSONReader::Read(stringifiedDetails)); - detailsDict->GetString("cardSecurityCode", &cvc); - EXPECT_EQ("123", cvc); - }]; - - FullCardRequester full_card_requester(consumer_mock, nil, - chrome_browser_state_.get()); - - autofill::AutofillProfile billing_address; - - std::unique_ptr<base::DictionaryValue> response_value = - payments::data_util::GetBasicCardResponseFromAutofillCreditCard( - credit_card_, base::ASCIIToUTF16("123"), billing_address, "en-US") - .ToDictionaryValue(); - std::string stringifiedDetails; - base::JSONWriter::Write(*response_value, &stringifiedDetails); - - full_card_requester.OnInstrumentDetailsReady("visa", stringifiedDetails); -}
diff --git a/ios/chrome/browser/ui/payments/payment_request_coordinator.h b/ios/chrome/browser/ui/payments/payment_request_coordinator.h index b5d860a3..81b0330 100644 --- a/ios/chrome/browser/ui/payments/payment_request_coordinator.h +++ b/ios/chrome/browser/ui/payments/payment_request_coordinator.h
@@ -15,7 +15,6 @@ #import "ios/chrome/browser/ui/payments/contact_info_edit_coordinator.h" #import "ios/chrome/browser/ui/payments/contact_info_selection_coordinator.h" #import "ios/chrome/browser/ui/payments/credit_card_edit_coordinator.h" -#include "ios/chrome/browser/ui/payments/full_card_requester.h" #import "ios/chrome/browser/ui/payments/payment_items_display_coordinator.h" #import "ios/chrome/browser/ui/payments/payment_method_selection_coordinator.h" #include "ios/chrome/browser/ui/payments/payment_request_error_coordinator.h" @@ -45,6 +44,10 @@ // Delegate protocol for PaymentRequestCoordinator. @protocol PaymentRequestCoordinatorDelegate<NSObject> +// Notifies the delegate that the user has confirmed the payment request. +- (void)paymentRequestCoordinatorDidConfirm: + (PaymentRequestCoordinator*)coordinator; + // Notifies the delegate that the user has canceled the payment request. - (void)paymentRequestCoordinatorDidCancel: (PaymentRequestCoordinator*)coordinator; @@ -54,12 +57,6 @@ - (void)paymentRequestCoordinatorDidSelectSettings: (PaymentRequestCoordinator*)coordinator; -// Notifies the delegate that the full payment method name and details -// have been receieved. -- (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator - didReceiveFullMethodName:(const std::string&)methodName - stringifiedDetails:(const std::string&)stringifiedDetails; - // Notifies the delegate that the user has selected a shipping address. - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator didSelectShippingAddress: @@ -80,7 +77,6 @@ ContactInfoEditCoordinatorDelegate, ContactInfoSelectionCoordinatorDelegate, CreditCardEditCoordinatorDelegate, - FullCardRequesterConsumer, PaymentItemsDisplayCoordinatorDelegate, PaymentMethodSelectionCoordinatorDelegate, PaymentRequestErrorCoordinatorDelegate, @@ -116,6 +112,9 @@ // Whether or not the connection is secure. @property(nonatomic, assign, getter=isConnectionSecure) BOOL connectionSecure; +// Whether or not the PaymentRequest view controller is in a pending state. +@property(nonatomic, assign, getter=isPending) BOOL pending; + // The delegate to be notified when the user confirms or cancels the request. @property(nonatomic, weak) id<PaymentRequestCoordinatorDelegate> delegate;
diff --git a/ios/chrome/browser/ui/payments/payment_request_coordinator.mm b/ios/chrome/browser/ui/payments/payment_request_coordinator.mm index de84989..8570695 100644 --- a/ios/chrome/browser/ui/payments/payment_request_coordinator.mm +++ b/ios/chrome/browser/ui/payments/payment_request_coordinator.mm
@@ -9,7 +9,6 @@ #include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/credit_card.h" #include "components/payments/core/autofill_payment_instrument.h" -#include "components/payments/core/journey_logger.h" #include "components/payments/core/payment_address.h" #include "components/payments/core/payment_instrument.h" #include "components/payments/core/payment_request_data_util.h" @@ -17,6 +16,7 @@ #include "ios/chrome/browser/application_context.h" #include "ios/chrome/browser/payments/payment_request.h" #include "ios/chrome/browser/payments/payment_request_util.h" +#include "ios/chrome/browser/ui/payments/full_card_requester.h" #include "ios/chrome/browser/ui/payments/payment_request_mediator.h" #include "ui/base/l10n/l10n_util.h" @@ -62,6 +62,7 @@ @synthesize pageTitle = _pageTitle; @synthesize pageHost = _pageHost; @synthesize connectionSecure = _connectionSecure; +@synthesize pending = _pending; @synthesize delegate = _delegate; - (void)start { @@ -85,9 +86,6 @@ setModalTransitionStyle:UIModalTransitionStyleCoverVertical]; [_navigationController setNavigationBarHidden:YES]; - _fullCardRequester = base::MakeUnique<FullCardRequester>( - self, _navigationController, _browserState); - [[self baseViewController] presentViewController:_navigationController animated:YES completion:nil]; @@ -121,31 +119,28 @@ _navigationController = nil; } +#pragma mark - Setters + +- (void)setPending:(BOOL)pending { + _pending = pending; + _viewController.view.userInteractionEnabled = !pending; + [_viewController setPending:pending]; + [_viewController loadModel]; + [[_viewController collectionView] reloadData]; +} + +#pragma mark - Public methods + - (void) requestFullCreditCard:(const autofill::CreditCard&)card resultDelegate: (base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>) resultDelegate { + _fullCardRequester = + base::MakeUnique<FullCardRequester>(_navigationController, _browserState); _fullCardRequester->GetFullCard(card, _autofillManager, resultDelegate); } -#pragma mark - FullCardRequesterConsumer - -- (void)fullCardRequestDidSucceedWithMethodName:(const std::string&)methodName - stringifiedDetails: - (const std::string&)stringifiedDetails { - _viewController.view.userInteractionEnabled = NO; - [_viewController setPending:YES]; - [_viewController loadModel]; - [[_viewController collectionView] reloadData]; - - [_delegate paymentRequestCoordinator:self - didReceiveFullMethodName:methodName - stringifiedDetails:stringifiedDetails]; -} - -#pragma mark - Public methods - - (void)updatePaymentDetails:(web::PaymentDetails)paymentDetails { [_updatePaymentSummaryItemTimer invalidate]; @@ -218,12 +213,7 @@ - (void)paymentRequestViewControllerDidConfirm: (PaymentRequestViewController*)controller { - DCHECK(_paymentRequest->selected_payment_method()); - - [self recordPayButtonTapped]; - - _paymentRequest->selected_payment_method()->InvokePaymentApp( - _fullCardRequester.get()); + [_delegate paymentRequestCoordinatorDidConfirm:self]; } - (void)paymentRequestViewControllerDidSelectSettings: @@ -340,12 +330,7 @@ - (void)paymentItemsDisplayCoordinatorDidConfirm: (PaymentItemsDisplayCoordinator*)coordinator { - DCHECK(_paymentRequest->selected_payment_method()); - - [self recordPayButtonTapped]; - - _paymentRequest->selected_payment_method()->InvokePaymentApp( - _fullCardRequester.get()); + [_delegate paymentRequestCoordinatorDidConfirm:self]; } #pragma mark - ContactInfoSelectionCoordinatorDelegate @@ -476,16 +461,4 @@ _creditCardEditCoordinator = nil; } -#pragma mark - Helper methods - -- (void)recordPayButtonTapped { - _paymentRequest->journey_logger().SetEventOccurred( - payments::JourneyLogger::EVENT_PAY_CLICKED); - _paymentRequest->journey_logger().SetSelectedPaymentMethod( - _paymentRequest->selected_payment_method()->type() == - payments::PaymentInstrument::Type::AUTOFILL - ? payments::JourneyLogger::SELECTED_PAYMENT_METHOD_CREDIT_CARD - : payments::JourneyLogger::SELECTED_PAYMENT_METHOD_OTHER_PAYMENT_APP); -} - @end
diff --git a/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm b/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm index 2bd636ca..c2a740e 100644 --- a/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm +++ b/ios/chrome/browser/ui/payments/payment_request_coordinator_unittest.mm
@@ -36,10 +36,8 @@ @implementation PaymentRequestCoordinatorDelegateMock +typedef void (^mock_coordinator_confirm)(PaymentRequestCoordinator*); typedef void (^mock_coordinator_cancel)(PaymentRequestCoordinator*); -typedef void (^mock_coordinator_complete)(PaymentRequestCoordinator*, - const std::string&, - const std::string&); typedef void (^mock_coordinator_select_shipping_address)( PaymentRequestCoordinator*, const autofill::AutofillProfile&); @@ -47,6 +45,12 @@ PaymentRequestCoordinator*, const web::PaymentShippingOption&); +- (void)paymentRequestCoordinatorDidConfirm: + (PaymentRequestCoordinator*)coordinator { + return static_cast<mock_coordinator_confirm>([self blockForSelector:_cmd])( + coordinator); +} + - (void)paymentRequestCoordinatorDidCancel: (PaymentRequestCoordinator*)coordinator { return static_cast<mock_coordinator_cancel>([self blockForSelector:_cmd])( @@ -54,13 +58,6 @@ } - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator - didReceiveFullMethodName:(const std::string&)methodName - stringifiedDetails:(const std::string&)stringifiedDetails { - return static_cast<mock_coordinator_complete>([self blockForSelector:_cmd])( - coordinator, methodName, stringifiedDetails); -} - -- (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator didSelectShippingAddress: (const autofill::AutofillProfile&)shippingAddress { return static_cast<mock_coordinator_select_shipping_address>( @@ -128,57 +125,6 @@ EXPECT_EQ(nil, base_view_controller.presentedViewController); } -// Tests that calling the FullCardRequesterConsumer delegate method which -// notifies the coordinator about successful unmasking of a credit card invokes -// the appropriate coordinator delegate method with the expected information. -TEST_F(PaymentRequestCoordinatorTest, FullCardRequestDidSucceedWithMethodName) { - UIViewController* base_view_controller = [[UIViewController alloc] init]; - ScopedKeyWindow scoped_key_window_; - [scoped_key_window_.Get() setRootViewController:base_view_controller]; - - PaymentRequestCoordinator* coordinator = [[PaymentRequestCoordinator alloc] - initWithBaseViewController:base_view_controller]; - [coordinator setPaymentRequest:payment_request()]; - - id delegate = [OCMockObject - mockForProtocol:@protocol(PaymentMethodSelectionCoordinatorDelegate)]; - id delegate_mock([[PaymentRequestCoordinatorDelegateMock alloc] - initWithRepresentedObject:delegate]); - SEL selector = @selector - (paymentRequestCoordinator:didReceiveFullMethodName:stringifiedDetails:); - [delegate_mock onSelector:selector - callBlockExpectation:^(PaymentRequestCoordinator* callerCoordinator, - const std::string& methodName, - const std::string& stringifiedDetails) { - EXPECT_EQ("visa", methodName); - - std::string cvc; - std::unique_ptr<base::DictionaryValue> detailsDict = - base::DictionaryValue::From( - base::JSONReader::Read(stringifiedDetails)); - detailsDict->GetString("cardSecurityCode", &cvc); - EXPECT_EQ("123", cvc); - - EXPECT_EQ(coordinator, callerCoordinator); - }]; - [coordinator setDelegate:delegate_mock]; - - std::string methodName = "visa"; - std::string appLocale = ""; - - std::unique_ptr<base::DictionaryValue> response_value = - payments::data_util::GetBasicCardResponseFromAutofillCreditCard( - *credit_cards().back(), base::ASCIIToUTF16("123"), *profiles().back(), - appLocale) - .ToDictionaryValue(); - std::string stringifiedDetails; - base::JSONWriter::Write(*response_value, &stringifiedDetails); - - // Call the card unmasking delegate method. - [coordinator fullCardRequestDidSucceedWithMethodName:methodName - stringifiedDetails:stringifiedDetails]; -} - // Tests that calling the ShippingAddressSelectionCoordinator delegate method // which notifies the coordinator about selection of a shipping address invokes // the corresponding coordinator delegate method with the expected information. @@ -245,7 +191,6 @@ [coordinator setDelegate:delegate_mock]; // Call the ShippingOptionSelectionCoordinator delegate method. - [coordinator shippingOptionSelectionCoordinator:nil didSelectShippingOption:&shipping_option]; } @@ -282,3 +227,65 @@ // Call the controller delegate method. [coordinator paymentRequestViewControllerDidCancel:nil]; } + +// Tests that calling the view controller delegate method which notifies the +// coordinator about confirmation of the PaymentRequest invokes the +// corresponding coordinator delegate method. +TEST_F(PaymentRequestCoordinatorTest, DidConfirm) { + UIViewController* base_view_controller = [[UIViewController alloc] init]; + ScopedKeyWindow scoped_key_window_; + [scoped_key_window_.Get() setRootViewController:base_view_controller]; + + PaymentRequestCoordinator* coordinator = [[PaymentRequestCoordinator alloc] + initWithBaseViewController:base_view_controller]; + [coordinator setPaymentRequest:payment_request()]; + + // Mock the coordinator delegate. + id delegate = [OCMockObject + mockForProtocol:@protocol(PaymentMethodSelectionCoordinatorDelegate)]; + id delegate_mock([[PaymentRequestCoordinatorDelegateMock alloc] + initWithRepresentedObject:delegate]); + SEL selector = @selector(paymentRequestCoordinatorDidConfirm:); + [delegate_mock onSelector:selector + callBlockExpectation:^(PaymentRequestCoordinator* callerCoordinator) { + EXPECT_EQ(coordinator, callerCoordinator); + }]; + [coordinator setDelegate:delegate_mock]; + [coordinator setBrowserState:browser_state()]; + + [coordinator start]; + // Spin the run loop to trigger the animation. + base::test::ios::SpinRunLoopWithMaxDelay(base::TimeDelta::FromSecondsD(1)); + + // Call the controller delegate method. + [coordinator paymentRequestViewControllerDidConfirm:nil]; +} + +// Tests that calling the PaymentItemsDisplayCoordinatorDelegate delegate method +// which notifies the coordinator about confirmation of the PaymentRequest +// invokes the corresponding coordinator delegate method.. +TEST_F(PaymentRequestCoordinatorTest, + PaymentItemsDisplayCoordinatorDidConfirm) { + UIViewController* base_view_controller = [[UIViewController alloc] init]; + ScopedKeyWindow scoped_key_window_; + [scoped_key_window_.Get() setRootViewController:base_view_controller]; + + PaymentRequestCoordinator* coordinator = [[PaymentRequestCoordinator alloc] + initWithBaseViewController:base_view_controller]; + [coordinator setPaymentRequest:payment_request()]; + + // Mock the coordinator delegate. + id delegate = [OCMockObject + mockForProtocol:@protocol(PaymentMethodSelectionCoordinatorDelegate)]; + id delegate_mock([[PaymentRequestCoordinatorDelegateMock alloc] + initWithRepresentedObject:delegate]); + SEL selector = @selector(paymentRequestCoordinatorDidConfirm:); + [delegate_mock onSelector:selector + callBlockExpectation:^(PaymentRequestCoordinator* callerCoordinator) { + EXPECT_EQ(coordinator, callerCoordinator); + }]; + [coordinator setDelegate:delegate_mock]; + + // Call the PaymentItemsDisplayCoordinatorDelegate delegate method. + [coordinator paymentItemsDisplayCoordinatorDidConfirm:nil]; +}
diff --git a/ios/chrome/browser/ui/payments/payment_request_manager.mm b/ios/chrome/browser/ui/payments/payment_request_manager.mm index 37f01b4a..3449bf09 100644 --- a/ios/chrome/browser/ui/payments/payment_request_manager.mm +++ b/ios/chrome/browser/ui/payments/payment_request_manager.mm
@@ -39,6 +39,7 @@ #include "ios/chrome/browser/payments/ios_payment_request_cache_factory.h" #include "ios/chrome/browser/payments/payment_request.h" #import "ios/chrome/browser/payments/payment_request_cache.h" +#import "ios/chrome/browser/payments/payment_response_helper.h" #include "ios/chrome/browser/procedural_block_types.h" #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h" #import "ios/chrome/browser/ui/commands/generic_chrome_command.h" @@ -92,7 +93,8 @@ @interface PaymentRequestManager ()<CRWWebStateObserver, PaymentRequestCoordinatorDelegate, - PaymentRequestUIDelegate> { + PaymentRequestUIDelegate, + PaymentResponseHelperConsumer> { // View controller used to present the PaymentRequest view controller. __weak UIViewController* _baseViewController; @@ -211,11 +213,6 @@ // was rejected. - (void)setUpdateEventTimeoutTimer; -// Called when the relevant addresses from a Payment Request have been -// normalized. Resolves the request promise with a PaymentResponse. -- (void)paymentRequestAddressNormalizationDidCompleteForPaymentRequest: - (payments::PaymentRequest*)paymentRequest; - // Returns the instance of payments::PaymentRequest for self.activeWebState that // has the identifier |paymentRequestId|, if any. Otherwise returns nullptr. - (payments::PaymentRequest*)paymentRequestWithId:(std::string)paymentRequestId; @@ -494,7 +491,6 @@ _pendingPaymentRequest = paymentRequest; - paymentRequest->journey_logger().SetShowCalled(); paymentRequest->journey_logger().SetEventOccurred( payments::JourneyLogger::EVENT_SHOWN); paymentRequest->journey_logger().SetRequestedInformation( @@ -771,6 +767,21 @@ #pragma mark - PaymentRequestCoordinatorDelegate methods +- (void)paymentRequestCoordinatorDidConfirm: + (PaymentRequestCoordinator*)coordinator { + DCHECK(coordinator.paymentRequest->selected_payment_method()); + + coordinator.paymentRequest->journey_logger().SetEventOccurred( + payments::JourneyLogger::EVENT_PAY_CLICKED); + coordinator.paymentRequest->journey_logger().SetSelectedPaymentMethod( + coordinator.paymentRequest->selected_payment_method()->type() == + payments::PaymentInstrument::Type::AUTOFILL + ? payments::JourneyLogger::SELECTED_PAYMENT_METHOD_CREDIT_CARD + : payments::JourneyLogger::SELECTED_PAYMENT_METHOD_OTHER_PAYMENT_APP); + + coordinator.paymentRequest->InvokePaymentApp(self); +} + - (void)paymentRequestCoordinatorDidCancel: (PaymentRequestCoordinator*)coordinator { coordinator.paymentRequest->journey_logger().SetAborted( @@ -799,91 +810,6 @@ - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator didReceiveFullMethodName:(const std::string&)methodName stringifiedDetails:(const std::string&)stringifiedDetails { - coordinator.paymentRequest->journey_logger().SetEventOccurred( - payments::JourneyLogger::EVENT_RECEIVED_INSTRUMENT_DETAILS); - - _pendingPaymentResponse.methodName = methodName; - _pendingPaymentResponse.stringifiedDetails = stringifiedDetails; - - if (coordinator.paymentRequest->request_shipping()) { - // TODO(crbug.com/602666): User should get here only if they have selected - // a shipping address. - DCHECK(coordinator.paymentRequest->selected_shipping_profile()); - _pendingPaymentResponse.shippingAddress = - *coordinator.paymentRequest->selected_shipping_profile(); - coordinator.paymentRequest->address_normalization_manager() - ->StartNormalizingAddress(&_pendingPaymentResponse.shippingAddress); - } - - if (coordinator.paymentRequest->request_payer_name() || - coordinator.paymentRequest->request_payer_email() || - coordinator.paymentRequest->request_payer_phone()) { - // TODO(crbug.com/602666): User should get here only if they have selected - // a contact info. - DCHECK(coordinator.paymentRequest->selected_contact_profile()); - _pendingPaymentResponse.contactAddress = - *coordinator.paymentRequest->selected_contact_profile(); - coordinator.paymentRequest->address_normalization_manager() - ->StartNormalizingAddress(&_pendingPaymentResponse.contactAddress); - } - - __weak PaymentRequestManager* weakSelf = self; - __weak PaymentRequestCoordinator* weakCoordinator = coordinator; - coordinator.paymentRequest->address_normalization_manager() - ->FinalizePendingRequestsWithCompletionCallback(base::BindBlockArc(^() { - [weakSelf - paymentRequestAddressNormalizationDidCompleteForPaymentRequest: - weakCoordinator.paymentRequest]; - })); -} - -- (void)paymentRequestAddressNormalizationDidCompleteForPaymentRequest: - (payments::PaymentRequest*)paymentRequest { - web::PaymentResponse paymentResponse; - - paymentResponse.payment_request_id = - paymentRequest->web_payment_request().payment_request_id; - - paymentResponse.method_name = - base::ASCIIToUTF16(_pendingPaymentResponse.methodName); - - paymentResponse.details = _pendingPaymentResponse.stringifiedDetails; - - if (paymentRequest->request_shipping()) { - paymentResponse.shipping_address = - payments::data_util::GetPaymentAddressFromAutofillProfile( - _pendingPaymentResponse.shippingAddress, - paymentRequest->GetApplicationLocale()); - - web::PaymentShippingOption* shippingOption = - paymentRequest->selected_shipping_option(); - DCHECK(shippingOption); - paymentResponse.shipping_option = shippingOption->id; - } - - if (paymentRequest->request_payer_name()) { - paymentResponse.payer_name = _pendingPaymentResponse.contactAddress.GetInfo( - autofill::AutofillType(autofill::NAME_FULL), - paymentRequest->GetApplicationLocale()); - } - - if (paymentRequest->request_payer_email()) { - paymentResponse.payer_email = - _pendingPaymentResponse.contactAddress.GetRawInfo( - autofill::EMAIL_ADDRESS); - } - - if (paymentRequest->request_payer_phone()) { - paymentResponse.payer_phone = - _pendingPaymentResponse.contactAddress.GetRawInfo( - autofill::PHONE_HOME_WHOLE_NUMBER); - } - - [_paymentRequestJsManager - resolveRequestPromiseWithPaymentResponse:paymentResponse - completionHandler:nil]; - [self setUnblockEventQueueTimer]; - [self setPaymentResponseTimeoutTimer]; } - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator @@ -907,6 +833,21 @@ [self setUpdateEventTimeoutTimer]; } +#pragma mark - PaymentResponseHelperConsumer methods + +- (void)paymentResponseHelperDidReceivePaymentMethodDetails { + [_paymentRequestCoordinator setPending:YES]; +} + +- (void)paymentResponseHelperDidCompleteWithPaymentResponse: + (const web::PaymentResponse&)paymentResponse { + [_paymentRequestJsManager + resolveRequestPromiseWithPaymentResponse:paymentResponse + completionHandler:nil]; + [self setUnblockEventQueueTimer]; + [self setPaymentResponseTimeoutTimer]; +} + #pragma mark - CRWWebStateObserver methods - (void)webState:(web::WebState*)webState
diff --git a/ios/web/payments/payment_request_unittest.cc b/ios/web/payments/payment_request_unittest.cc index 1e2fee0e..f3c690e6 100644 --- a/ios/web/payments/payment_request_unittest.cc +++ b/ios/web/payments/payment_request_unittest.cc
@@ -354,7 +354,7 @@ PaymentResponse payment_response; payment_response.payment_request_id = "12345"; - payment_response.method_name = base::ASCIIToUTF16("American Express"); + payment_response.method_name = "American Express"; payments::BasicCardResponse payment_response_details; payment_response_details.card_number = @@ -695,11 +695,11 @@ PaymentResponse response2; EXPECT_EQ(response1, response2); - response1.method_name = base::ASCIIToUTF16("Visa"); + response1.method_name = "Visa"; EXPECT_NE(response1, response2); - response2.method_name = base::ASCIIToUTF16("Mastercard"); + response2.method_name = "Mastercard"; EXPECT_NE(response1, response2); - response2.method_name = base::ASCIIToUTF16("Visa"); + response2.method_name = "Visa"; EXPECT_EQ(response1, response2); payments::BasicCardResponse card_response1;
diff --git a/ios/web/public/payments/payment_request.h b/ios/web/public/payments/payment_request.h index 22ea8baa..cf619f06 100644 --- a/ios/web/public/payments/payment_request.h +++ b/ios/web/public/payments/payment_request.h
@@ -266,7 +266,7 @@ // The payment method identifier for the payment method that the user selected // to fulfil the transaction. - base::string16 method_name; + std::string method_name; // The json-serialized stringified details of the payment method. Used by the // merchant to process the transaction and determine successful fund transfer.
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm index 22cf740..52f5a12 100644 --- a/ios/web/web_state/ui/crw_web_controller.mm +++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -4835,10 +4835,8 @@ } if (!navigationWasCommitted && ![_pendingNavigationInfo cancelled]) { - // A fast back navigation caused by pushState does not call - // |didCommitNavigation:|, so signal page change explicitly. - // TODO(crbug.com/659816): Because back-forward navigation is disabled for - // pages that used push/replace State API, this code will not be executed. + // A fast back-forward navigation does not call |didCommitNavigation:|, so + // signal page change explicitly. DCHECK_EQ(_documentURL.GetOrigin(), webViewURL.GetOrigin()); BOOL isSameDocumentNavigation = [self isKVOChangePotentialSameDocumentNavigationToURL:webViewURL]; @@ -4847,12 +4845,11 @@ web::NavigationContextImpl* existingContext = [self contextForPendingNavigationWithURL:webViewURL]; - if (!existingContext && isSameDocumentNavigation) { - // This is a renderer-initiated same-document navigation, which needs to - // be registered. + if (!existingContext) { + // This URL was not seen before, so register new load request. std::unique_ptr<web::NavigationContextImpl> newContext = [self registerLoadRequestForURL:webViewURL]; - newContext->SetIsSameDocument(true); + newContext->SetIsSameDocument(isSameDocumentNavigation); _webStateImpl->OnNavigationFinished(newContext.get()); } else { // Same document navigation does not contain response headers.
diff --git a/media/blink/BUILD.gn b/media/blink/BUILD.gn index 2864b45f..fb8e8fa 100644 --- a/media/blink/BUILD.gn +++ b/media/blink/BUILD.gn
@@ -9,8 +9,6 @@ output_name = "media_blink" sources = [ - "active_loader.cc", - "active_loader.h", "buffered_data_source_host_impl.cc", "buffered_data_source_host_impl.h", "cache_util.cc",
diff --git a/media/blink/active_loader.cc b/media/blink/active_loader.cc deleted file mode 100644 index 9c93224..0000000 --- a/media/blink/active_loader.cc +++ /dev/null
@@ -1,26 +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 "media/blink/active_loader.h" - -#include <utility> - -#include "third_party/WebKit/public/web/WebAssociatedURLLoader.h" - -namespace media { - -ActiveLoader::ActiveLoader( - std::unique_ptr<blink::WebAssociatedURLLoader> loader) - : loader_(std::move(loader)), deferred_(false) {} - -ActiveLoader::~ActiveLoader() { - loader_->Cancel(); -} - -void ActiveLoader::SetDeferred(bool deferred) { - deferred_ = deferred; - loader_->SetDefersLoading(deferred); -} - -} // namespace media
diff --git a/media/blink/active_loader.h b/media/blink/active_loader.h deleted file mode 100644 index 76d1382e..0000000 --- a/media/blink/active_loader.h +++ /dev/null
@@ -1,44 +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. - -#ifndef MEDIA_BLINK_ACTIVE_LOADER_H_ -#define MEDIA_BLINK_ACTIVE_LOADER_H_ - -#include <memory> - -#include "base/macros.h" -#include "media/blink/media_blink_export.h" - -namespace blink { -class WebAssociatedURLLoader; -} - -namespace media { - -// Wraps an active WebURLLoader with some additional state. -// -// Handles deferring and deletion of loaders. -class MEDIA_BLINK_EXPORT ActiveLoader { - public: - // Creates an ActiveLoader with the given loader. It is assumed that the - // initial state of |loader| is loading and not deferred. - explicit ActiveLoader(std::unique_ptr<blink::WebAssociatedURLLoader> loader); - ~ActiveLoader(); - - // Starts or stops deferring the resource load. - void SetDeferred(bool deferred); - bool deferred() { return deferred_; } - - private: - friend class MultibufferDataSourceTest; - - std::unique_ptr<blink::WebAssociatedURLLoader> loader_; - bool deferred_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(ActiveLoader); -}; - -} // namespace media - -#endif // MEDIA_BLINK_ACTIVE_LOADER_H_
diff --git a/media/blink/multibuffer_data_source_unittest.cc b/media/blink/multibuffer_data_source_unittest.cc index 232ec554..32f2663 100644 --- a/media/blink/multibuffer_data_source_unittest.cc +++ b/media/blink/multibuffer_data_source_unittest.cc
@@ -49,7 +49,7 @@ class TestMultiBufferDataProvider : public ResourceMultiBufferDataProvider { public: TestMultiBufferDataProvider(UrlData* url_data, MultiBuffer::BlockId pos) - : ResourceMultiBufferDataProvider(url_data, pos), loading_(false) { + : ResourceMultiBufferDataProvider(url_data, pos), deferred_(false) { CHECK(test_data_providers.insert(this).second); } ~TestMultiBufferDataProvider() override { @@ -57,28 +57,22 @@ } void Start() override { // Create a mock active loader. - // Keep track of active loading state via loadAsynchronously() and cancel(). - NiceMock<MockWebAssociatedURLLoader>* url_loader = - new NiceMock<MockWebAssociatedURLLoader>(); - ON_CALL(*url_loader, Cancel()).WillByDefault(Invoke([this]() { - // Check that we have not been destroyed first. - if (test_data_providers.find(this) != test_data_providers.end()) { - this->loading_ = false; - } - })); - loading_ = true; - active_loader_.reset( - new ActiveLoader(std::unique_ptr<WebAssociatedURLLoader>(url_loader))); + active_loader_ = base::MakeUnique<NiceMock<MockWebAssociatedURLLoader>>(); if (!on_start_.is_null()) { on_start_.Run(); } } + void SetDeferred(bool defer) override { + deferred_ = defer; + ResourceMultiBufferDataProvider::SetDeferred(defer); + } - bool loading() const { return loading_; } + bool loading() const { return !!active_loader_; } + bool deferred() const { return deferred_; } void RunOnStart(base::Closure cb) { on_start_ = cb; } private: - bool loading_; + bool deferred_; base::Closure on_start_; }; @@ -303,17 +297,13 @@ } void Respond(const WebURLResponse& response) { - EXPECT_TRUE(url_loader()); - if (!active_loader()) - return; + EXPECT_TRUE(active_loader()); data_provider()->DidReceiveResponse(response); base::RunLoop().RunUntilIdle(); } void ReceiveDataLow(int size) { - EXPECT_TRUE(url_loader()); - if (!url_loader()) - return; + EXPECT_TRUE(active_loader()); std::unique_ptr<char[]> data(new char[size]); memset(data.get(), 0xA5, size); // Arbitrary non-zero value. @@ -326,9 +316,7 @@ } void FinishLoading() { - EXPECT_TRUE(url_loader()); - if (!url_loader()) - return; + EXPECT_TRUE(active_loader()); data_provider()->DidFinishLoading(0); base::RunLoop().RunUntilIdle(); } @@ -425,26 +413,27 @@ TestMultiBufferDataProvider* data_provider() { return multibuffer()->GetProvider(); } - ActiveLoader* active_loader() { + blink::WebAssociatedURLLoader* active_loader() { EXPECT_TRUE(data_provider()); if (!data_provider()) return nullptr; return data_provider()->active_loader_.get(); } - ActiveLoader* active_loader_allownull() { + blink::WebAssociatedURLLoader* active_loader_allownull() { TestMultiBufferDataProvider* data_provider = multibuffer()->GetProvider_allownull(); if (!data_provider) return nullptr; return data_provider->active_loader_.get(); } - WebAssociatedURLLoader* url_loader() { - EXPECT_TRUE(active_loader()); - if (!active_loader()) - return nullptr; - return active_loader()->loader_.get(); - } - + /* + WebAssociatedURLLoader* url_loader() { + EXPECT_TRUE(active_loader()); + if (!active_loader()) + return nullptr; + return active_loader()->loader_.get(); + } + */ bool loading() { return multibuffer()->loading(); } MultibufferDataSource::Preload preload() { return data_source_->preload_; } @@ -1206,7 +1195,7 @@ ReadAt(0); ASSERT_TRUE(active_loader()); - EXPECT_TRUE(active_loader()->deferred()); + EXPECT_TRUE(data_provider()->deferred()); } TEST_F(MultibufferDataSourceTest, @@ -1289,12 +1278,12 @@ ASSERT_TRUE(active_loader()); data_source_->OnBufferingHaveEnough(true); ASSERT_TRUE(active_loader()); - ASSERT_FALSE(active_loader()->deferred()); + ASSERT_FALSE(data_provider()->deferred()); // Deliver data until capacity is reached and verify deferral. int bytes_received = 0; EXPECT_CALL(host_, AddBufferedByteRange(_, _)).Times(testing::AtLeast(1)); - while (active_loader_allownull() && !active_loader()->deferred()) { + while (active_loader_allownull() && !data_provider()->deferred()) { ReceiveData(kDataSize); bytes_received += kDataSize; }
diff --git a/media/blink/resource_multibuffer_data_provider.cc b/media/blink/resource_multibuffer_data_provider.cc index 0a9f2601..839af73 100644 --- a/media/blink/resource_multibuffer_data_provider.cc +++ b/media/blink/resource_multibuffer_data_provider.cc
@@ -16,7 +16,6 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/threading/thread_task_runner_handle.h" -#include "media/blink/active_loader.h" #include "media/blink/cache_util.h" #include "media/blink/media_blink_export.h" #include "media/blink/url_index.h" @@ -131,7 +130,7 @@ // Start the resource loading. loader->LoadAsynchronously(request, this); - active_loader_.reset(new ActiveLoader(std::move(loader))); + active_loader_ = std::move(loader); } ResourceMultiBufferDataProvider::~ResourceMultiBufferDataProvider() {} @@ -171,9 +170,8 @@ } void ResourceMultiBufferDataProvider::SetDeferred(bool deferred) { - if (!active_loader_ || active_loader_->deferred() == deferred) - return; - active_loader_->SetDeferred(deferred); + if (active_loader_) + active_loader_->SetDefersLoading(deferred); } /////////////////////////////////////////////////////////////////////////////
diff --git a/media/blink/resource_multibuffer_data_provider.h b/media/blink/resource_multibuffer_data_provider.h index e3ed536a..fb54d653 100644 --- a/media/blink/resource_multibuffer_data_provider.h +++ b/media/blink/resource_multibuffer_data_provider.h
@@ -12,7 +12,6 @@ #include "base/callback.h" #include "base/memory/weak_ptr.h" -#include "media/blink/active_loader.h" #include "media/blink/media_blink_export.h" #include "media/blink/multibuffer.h" #include "media/blink/url_index.h" @@ -110,8 +109,8 @@ // const to make it obvious that redirects cannot change it. const GURL origin_; - // Keeps track of an active WebAssociatedURLLoader and associated state. - std::unique_ptr<ActiveLoader> active_loader_; + // Keeps track of an active WebAssociatedURLLoader. + std::unique_ptr<blink::WebAssociatedURLLoader> active_loader_; // Injected WebAssociatedURLLoader instance for testing purposes. std::unique_ptr<blink::WebAssociatedURLLoader> test_loader_;
diff --git a/media/blink/resource_multibuffer_data_provider_unittest.cc b/media/blink/resource_multibuffer_data_provider_unittest.cc index f7126ff0..8bab3bf 100644 --- a/media/blink/resource_multibuffer_data_provider_unittest.cc +++ b/media/blink/resource_multibuffer_data_provider_unittest.cc
@@ -195,8 +195,6 @@ } void StopWhenLoad() { - InSequence s; - EXPECT_CALL(*url_loader_, Cancel()); loader_ = nullptr; url_data_ = nullptr; }
diff --git a/net/BUILD.gn b/net/BUILD.gn index 4cd757c..b42b3b0 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -209,8 +209,8 @@ "cert/internal/path_builder.h", "cert/internal/signature_algorithm.cc", "cert/internal/signature_algorithm.h", - "cert/internal/signature_policy.cc", - "cert/internal/signature_policy.h", + "cert/internal/simple_path_builder_delegate.cc", + "cert/internal/simple_path_builder_delegate.h", "cert/internal/trust_store.cc", "cert/internal/trust_store.h", "cert/internal/trust_store_collection.cc", @@ -3793,6 +3793,8 @@ "data/verify_certificate_chain_unittest/target-eku-none/chain.pem", "data/verify_certificate_chain_unittest/target-eku-none/clientauth.test", "data/verify_certificate_chain_unittest/target-eku-none/serverauth.test", + "data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/chain.pem", + "data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/main.test", "data/verify_certificate_chain_unittest/target-has-keycertsign-but-not-ca/chain.pem", "data/verify_certificate_chain_unittest/target-has-keycertsign-but-not-ca/main.test", "data/verify_certificate_chain_unittest/target-has-pathlen-but-not-ca/chain.pem", @@ -4622,6 +4624,7 @@ "cert/internal/path_builder_unittest.cc", "cert/internal/path_builder_verify_certificate_chain_unittest.cc", "cert/internal/signature_algorithm_unittest.cc", + "cert/internal/simple_path_builder_delegate_unittest.cc", "cert/internal/test_helpers.cc", "cert/internal/test_helpers.h", "cert/internal/trust_store_collection_unittest.cc",
diff --git a/net/base/net_errors_posix.cc b/net/base/net_errors_posix.cc index 123f7e5..8af962f 100644 --- a/net/base/net_errors_posix.cc +++ b/net/base/net_errors_posix.cc
@@ -108,6 +108,8 @@ return ERR_INSUFFICIENT_RESOURCES; case EMFILE: // Too many open files. return ERR_INSUFFICIENT_RESOURCES; + case EIO: // Generic IO error. + return ERR_FAILED; case 0: return OK;
diff --git a/net/cert/cert_verify_proc_builtin.cc b/net/cert/cert_verify_proc_builtin.cc index 3261d2b..59ce58b 100644 --- a/net/cert/cert_verify_proc_builtin.cc +++ b/net/cert/cert_verify_proc_builtin.cc
@@ -21,7 +21,7 @@ #include "net/cert/internal/cert_issuer_source_static.h" #include "net/cert/internal/parsed_certificate.h" #include "net/cert/internal/path_builder.h" -#include "net/cert/internal/signature_policy.h" +#include "net/cert/internal/simple_path_builder_delegate.h" #include "net/cert/internal/system_trust_store.h" #include "net/cert/internal/verify_certificate_chain.h" #include "net/cert/x509_certificate.h" @@ -200,9 +200,7 @@ // keys, and separately cert_verify_proc.cc also checks the chains with its // own policy. These policies should be aligned, to give path building the // best chance of finding a good path. - // Another difference to resolve is the path building here does not check the - // target certificate's key strength, whereas cert_verify_proc.cc does. - SimpleSignaturePolicy signature_policy(1024); + SimplePathBuilderDelegate path_builder_delegate(1024); // Use the current time. der::GeneralizedTime verification_time; @@ -217,7 +215,7 @@ // Initialize the path builder. CertPathBuilder::Result result; CertPathBuilder path_builder( - target, ssl_trust_store->GetTrustStore(), &signature_policy, + target, ssl_trust_store->GetTrustStore(), &path_builder_delegate, verification_time, KeyPurpose::SERVER_AUTH, InitialExplicitPolicy::kFalse, {AnyPolicy()} /* user_initial_policy_set*/, InitialPolicyMappingInhibit::kFalse, InitialAnyPolicyInhibit::kFalse,
diff --git a/net/cert/internal/cert_errors.cc b/net/cert/internal/cert_errors.cc index 8d38e3c..12af2a7 100644 --- a/net/cert/internal/cert_errors.cc +++ b/net/cert/internal/cert_errors.cc
@@ -130,6 +130,12 @@ return &cert_errors_[cert_index]; } +const CertErrors* CertPathErrors::GetErrorsForCert(size_t cert_index) const { + if (cert_index >= cert_errors_.size()) + return nullptr; + return &cert_errors_[cert_index]; +} + CertErrors* CertPathErrors::GetOtherErrors() { return &other_errors_; }
diff --git a/net/cert/internal/cert_errors.h b/net/cert/internal/cert_errors.h index 438417fd..8a7b58d 100644 --- a/net/cert/internal/cert_errors.h +++ b/net/cert/internal/cert_errors.h
@@ -134,6 +134,11 @@ // index in a certificate chain (with 0 being the target). CertErrors* GetErrorsForCert(size_t cert_index); + // Const version of the above, with the difference that if there is no + // existing bucket for |cert_index| returns nullptr rather than lazyily + // creating one. + const CertErrors* GetErrorsForCert(size_t cert_index) const; + // Returns a bucket to put errors that are not associated with a particular // certificate. CertErrors* GetOtherErrors();
diff --git a/net/cert/internal/path_builder.cc b/net/cert/internal/path_builder.cc index cc25543..144654d3 100644 --- a/net/cert/internal/path_builder.cc +++ b/net/cert/internal/path_builder.cc
@@ -14,7 +14,6 @@ #include "net/cert/internal/certificate_policies.h" #include "net/cert/internal/parse_certificate.h" #include "net/cert/internal/parse_name.h" // For CertDebugString. -#include "net/cert/internal/signature_policy.h" #include "net/cert/internal/trust_store.h" #include "net/cert/internal/verify_certificate_chain.h" #include "net/cert/internal/verify_name_match.h" @@ -548,7 +547,7 @@ CertPathBuilder::CertPathBuilder( scoped_refptr<ParsedCertificate> cert, TrustStore* trust_store, - const SignaturePolicy* signature_policy, + CertPathBuilderDelegate* delegate, const der::GeneralizedTime& time, KeyPurpose key_purpose, InitialExplicitPolicy initial_explicit_policy, @@ -557,7 +556,7 @@ InitialAnyPolicyInhibit initial_any_policy_inhibit, Result* result) : cert_path_iter_(new CertPathIter(std::move(cert), trust_store)), - signature_policy_(signature_policy), + delegate_(delegate), time_(time), key_purpose_(key_purpose), initial_explicit_policy_(initial_explicit_policy), @@ -566,6 +565,7 @@ initial_any_policy_inhibit_(initial_any_policy_inhibit), next_state_(STATE_NONE), out_result_(result) { + DCHECK(delegate); result->Clear(); // The TrustStore also implements the CertIssuerSource interface. AddCertIssuerSource(trust_store); @@ -614,20 +614,25 @@ // Verify the entire certificate chain. auto result_path = base::MakeUnique<ResultPath>(); + result_path->path = next_path_; VerifyCertificateChain( - next_path_.certs, next_path_.last_cert_trust, signature_policy_, time_, - key_purpose_, initial_explicit_policy_, user_initial_policy_set_, + result_path->path.certs, result_path->path.last_cert_trust, delegate_, + time_, key_purpose_, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result_path->user_constrained_policy_set, &result_path->errors); - bool verify_result = !result_path->errors.ContainsHighSeverityErrors(); - DVLOG(1) << "CertPathBuilder VerifyCertificateChain result = " - << verify_result << "\n" - << result_path->errors.ToDebugString(next_path_.certs); - result_path->path = next_path_; + DVLOG(1) << "CertPathBuilder VerifyCertificateChain errors:\n" + << result_path->errors.ToDebugString(result_path->path.certs); + + // Give the delegate a chance to add errors to the path. + delegate_->CheckPathAfterVerification(result_path->path, + &result_path->errors); + + bool path_is_good = result_path->IsValid(); + AddResultPath(std::move(result_path)); - if (verify_result) { + if (path_is_good) { // Found a valid path, return immediately. // TODO(mattm): add debug/test mode that tries all possible paths. next_state_ = STATE_NONE;
diff --git a/net/cert/internal/path_builder.h b/net/cert/internal/path_builder.h index 2dcaa6836..ba53290a 100644 --- a/net/cert/internal/path_builder.h +++ b/net/cert/internal/path_builder.h
@@ -25,7 +25,6 @@ class CertPathIter; class CertIssuerSource; -class SignaturePolicy; // CertPath describes a chain of certificates in the "forward" direction. // @@ -57,6 +56,19 @@ const ParsedCertificate* GetTrustedCert() const; }; +// CertPathBuilderDelegate controls policies for certificate verification and +// path building. +class NET_EXPORT CertPathBuilderDelegate + : public VerifyCertificateChainDelegate { + public: + // This is called during path building on candidate paths which have already + // been run through RFC 5280 verification. |path| may already have errors + // and warnings set on it. Delegates can "reject" a candidate path from path + // building by adding high severity errors. + virtual void CheckPathAfterVerification(const CertPath& path, + CertPathErrors* errors) = 0; +}; + // Checks whether a certificate is trusted by building candidate paths to trust // anchors and verifying those paths according to RFC 5280. Each instance of // CertPathBuilder is used for a single verification. @@ -115,21 +127,24 @@ DISALLOW_COPY_AND_ASSIGN(Result); }; - // TODO(mattm): allow caller specified hook/callback to extend path - // verification. - // // Creates a CertPathBuilder that attempts to find a path from |cert| to a - // trust anchor in |trust_store|, which satisfies |signature_policy| and is - // valid at |time|. Details of attempted path(s) are stored in |*result|. + // trust anchor in |trust_store| and is valid at |time|. Details of attempted + // path(s) are stored in |*result|. // - // The caller must keep |trust_store|, |signature_policy|, and |*result| valid - // for the lifetime of the CertPathBuilder. + // The caller must keep |trust_store|, |delegate| and |*result| valid for the + // lifetime of the CertPathBuilder. // // See VerifyCertificateChain() for a more detailed explanation of the - // same-named parameters. + // same-named parameters not defined below. + // + // * |result|: Storage for the result of path building. + // * |delegate|: Must be non-null. The delegate is called at various points in + // path building to verify specific parts of certificates or the + // final chain. See CertPathBuilderDelegate and + // VerifyCertificateChainDelegate for more information. CertPathBuilder(scoped_refptr<ParsedCertificate> cert, TrustStore* trust_store, - const SignaturePolicy* signature_policy, + CertPathBuilderDelegate* delegate, const der::GeneralizedTime& time, KeyPurpose key_purpose, InitialExplicitPolicy initial_explicit_policy, @@ -168,7 +183,7 @@ void AddResultPath(std::unique_ptr<ResultPath> result_path); std::unique_ptr<CertPathIter> cert_path_iter_; - const SignaturePolicy* signature_policy_; + CertPathBuilderDelegate* delegate_; const der::GeneralizedTime time_; const KeyPurpose key_purpose_; const InitialExplicitPolicy initial_explicit_policy_;
diff --git a/net/cert/internal/path_builder_pkits_unittest.cc b/net/cert/internal/path_builder_pkits_unittest.cc index 3b2b622..d5fbe859 100644 --- a/net/cert/internal/path_builder_pkits_unittest.cc +++ b/net/cert/internal/path_builder_pkits_unittest.cc
@@ -8,7 +8,7 @@ #include "net/cert/internal/cert_issuer_source_static.h" #include "net/cert/internal/parse_certificate.h" #include "net/cert/internal/parsed_certificate.h" -#include "net/cert/internal/signature_policy.h" +#include "net/cert/internal/simple_path_builder_delegate.h" #include "net/cert/internal/trust_store_in_memory.h" #include "net/cert/internal/verify_certificate_chain.h" #include "net/der/input.h" @@ -56,11 +56,11 @@ scoped_refptr<ParsedCertificate> target_cert(certs.back()); - SimpleSignaturePolicy signature_policy(1024); + SimplePathBuilderDelegate path_builder_delegate(1024); CertPathBuilder::Result result; CertPathBuilder path_builder( - std::move(target_cert), &trust_store, &signature_policy, info.time, + std::move(target_cert), &trust_store, &path_builder_delegate, info.time, KeyPurpose::ANY_EKU, info.initial_explicit_policy, info.initial_policy_set, info.initial_policy_mapping_inhibit, info.initial_inhibit_any_policy, &result);
diff --git a/net/cert/internal/path_builder_unittest.cc b/net/cert/internal/path_builder_unittest.cc index 5035ac6..2d79909 100644 --- a/net/cert/internal/path_builder_unittest.cc +++ b/net/cert/internal/path_builder_unittest.cc
@@ -7,9 +7,10 @@ #include "base/base_paths.h" #include "base/files/file_util.h" #include "base/path_service.h" +#include "net/cert/internal/cert_error_params.h" #include "net/cert/internal/cert_issuer_source_static.h" #include "net/cert/internal/parsed_certificate.h" -#include "net/cert/internal/signature_policy.h" +#include "net/cert/internal/simple_path_builder_delegate.h" #include "net/cert/internal/test_helpers.h" #include "net/cert/internal/trust_store_collection.h" #include "net/cert/internal/trust_store_in_memory.h" @@ -115,7 +116,7 @@ class PathBuilderMultiRootTest : public ::testing::Test { public: - PathBuilderMultiRootTest() : signature_policy_(1024) {} + PathBuilderMultiRootTest() : delegate_(1024) {} void SetUp() override { ASSERT_TRUE(ReadTestCert("multi-root-A-by-B.pem", &a_by_b_)); @@ -132,7 +133,7 @@ scoped_refptr<ParsedCertificate> a_by_b_, b_by_c_, b_by_f_, c_by_d_, c_by_e_, d_by_d_, e_by_e_, f_by_e_; - SimpleSignaturePolicy signature_policy_; + SimplePathBuilderDelegate delegate_; der::GeneralizedTime time_ = {2017, 3, 1, 0, 0, 0}; const InitialExplicitPolicy initial_explicit_policy_ = @@ -158,7 +159,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - a_by_b_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); @@ -185,7 +186,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - a_by_b_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); @@ -216,8 +217,8 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - b_by_c_, &trust_store, &signature_policy_, expired_time, - KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, + b_by_c_, &trust_store, &delegate_, expired_time, KeyPurpose::ANY_EKU, + initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&sync_certs); @@ -243,7 +244,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - e_by_e_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + e_by_e_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); @@ -269,7 +270,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - a_by_b_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); @@ -298,7 +299,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - a_by_b_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&async_certs); @@ -328,7 +329,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - a_by_b_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&async_certs1); @@ -357,7 +358,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - a_by_b_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&sync_certs); @@ -392,7 +393,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - a_by_b_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&sync_certs); @@ -433,7 +434,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - a_by_b_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + a_by_b_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&sync_certs); @@ -454,7 +455,7 @@ class PathBuilderKeyRolloverTest : public ::testing::Test { public: - PathBuilderKeyRolloverTest() : signature_policy_(1024) {} + PathBuilderKeyRolloverTest() : delegate_(1024) {} void SetUp() override { ParsedCertificateList path; @@ -505,7 +506,7 @@ scoped_refptr<ParsedCertificate> newroot_; scoped_refptr<ParsedCertificate> newrootrollover_; - SimpleSignaturePolicy signature_policy_; + SimplePathBuilderDelegate delegate_; der::GeneralizedTime time_; const InitialExplicitPolicy initial_explicit_policy_ = @@ -532,7 +533,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - target_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&sync_certs); @@ -582,7 +583,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - target_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&sync_certs); @@ -619,7 +620,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - target_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); @@ -652,8 +653,8 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - target_, &trust_store_collection, &signature_policy_, time_, - KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, + target_, &trust_store_collection, &delegate_, time_, KeyPurpose::ANY_EKU, + initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&sync_certs); @@ -704,7 +705,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - target_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&sync_certs); @@ -763,8 +764,8 @@ CertPathBuilder::Result result; // Newintermediate is also the target cert. CertPathBuilder path_builder( - newintermediate_, &trust_store, &signature_policy_, time_, - KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, + newintermediate_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, + initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.Run(); @@ -789,7 +790,7 @@ CertPathBuilder::Result result; // Newroot is the target cert. CertPathBuilder path_builder( - newroot_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + newroot_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&sync_certs); @@ -812,7 +813,7 @@ CertPathBuilder::Result result; // Newroot is the target cert. CertPathBuilder path_builder( - newroot_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + newroot_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); @@ -862,7 +863,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - target_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&sync_certs1); @@ -918,7 +919,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - target_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&sync_certs); @@ -994,7 +995,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - target_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&cert_issuer_source); @@ -1075,7 +1076,7 @@ CertPathBuilder::Result result; CertPathBuilder path_builder( - target_, &trust_store, &signature_policy_, time_, KeyPurpose::ANY_EKU, + target_, &trust_store, &delegate_, time_, KeyPurpose::ANY_EKU, initial_explicit_policy_, user_initial_policy_set_, initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, &result); path_builder.AddCertIssuerSource(&cert_issuer_source); @@ -1154,11 +1155,9 @@ EXPECT_EQ(newroot_, path1.certs[2]); } -// Test fixture for running the path builder over a simple chain, while varying -// the trustedness of certain certificates. -class PathBuilderDistrustTest : public ::testing::Test { +class PathBuilderSimpleChainTest : public ::testing::Test { public: - PathBuilderDistrustTest() {} + PathBuilderSimpleChainTest() {} protected: void SetUp() override { @@ -1171,10 +1170,10 @@ } // Runs the path builder for the target certificate while |distrusted_cert| is - // blacklisted. - void RunPathBuilderWithDistrustedCert( - const scoped_refptr<ParsedCertificate>& distrusted_cert, - CertPathBuilder::Result* result) { + // blacklisted, and |delegate| if non-null. + void RunPathBuilder(const scoped_refptr<ParsedCertificate>& distrusted_cert, + CertPathBuilderDelegate* optional_delegate, + CertPathBuilder::Result* result) { ASSERT_EQ(3u, test_.chain.size()); // Set up the trust store such that |distrusted_cert| is blacklisted, and @@ -1189,7 +1188,9 @@ CertIssuerSourceStatic intermediates; intermediates.AddCert(test_.chain[1]); - SimpleSignaturePolicy signature_policy(1024); + SimplePathBuilderDelegate default_delegate(1024); + CertPathBuilderDelegate* delegate = + optional_delegate ? optional_delegate : &default_delegate; const InitialExplicitPolicy initial_explicit_policy = InitialExplicitPolicy::kFalse; @@ -1200,7 +1201,7 @@ InitialAnyPolicyInhibit::kFalse; CertPathBuilder path_builder( - test_.chain.front(), &trust_store, &signature_policy, test_.time, + test_.chain.front(), &trust_store, delegate, test_.time, KeyPurpose::ANY_EKU, initial_explicit_policy, user_initial_policy_set, initial_policy_mapping_inhibit, initial_any_policy_inhibit, result); path_builder.AddCertIssuerSource(&intermediates); @@ -1211,6 +1212,22 @@ VerifyCertChainTest test_; }; +// Test fixture for running the path builder over a simple chain, while varying +// the trustedness of certain certificates. +class PathBuilderDistrustTest : public PathBuilderSimpleChainTest { + public: + PathBuilderDistrustTest() {} + + protected: + // Runs the path builder for the target certificate while |distrusted_cert| is + // blacklisted. + void RunPathBuilderWithDistrustedCert( + const scoped_refptr<ParsedCertificate>& distrusted_cert, + CertPathBuilder::Result* result) { + RunPathBuilder(distrusted_cert, nullptr, result); + } +}; + // Tests that path building fails when the target, intermediate, or root are // distrusted (but the path is otherwise valid). TEST_F(PathBuilderDistrustTest, TargetIntermediateRoot) { @@ -1276,6 +1293,103 @@ } } +// Test fixture for running the path builder over a simple chain, while varying +// what CheckPathAfterVerification() does. +class PathBuilderCheckPathAfterVerificationTest + : public PathBuilderSimpleChainTest {}; + +class CertPathBuilderDelegateBase : public SimplePathBuilderDelegate { + public: + CertPathBuilderDelegateBase() : SimplePathBuilderDelegate(1024) {} + void CheckPathAfterVerification(const CertPath& path, + CertPathErrors* errors) override { + ADD_FAILURE() << "Tests must override this"; + } +}; + +class MockPathBuilderDelegate : public CertPathBuilderDelegateBase { + public: + MOCK_METHOD2(CheckPathAfterVerification, + void(const CertPath& path, CertPathErrors* errors)); +}; + +TEST_F(PathBuilderCheckPathAfterVerificationTest, NoOpToValidPath) { + CertPathBuilder::Result result; + + StrictMock<MockPathBuilderDelegate> delegate; + // Just verify that the hook is called. + EXPECT_CALL(delegate, CheckPathAfterVerification(_, _)); + + RunPathBuilder(nullptr, &delegate, &result); + EXPECT_TRUE(result.HasValidPath()); +} + +DEFINE_CERT_ERROR_ID(kWarningFromDelegate, "Warning from delegate"); + +class AddWarningPathBuilderDelegate : public CertPathBuilderDelegateBase { + public: + void CheckPathAfterVerification(const CertPath& path, + CertPathErrors* errors) override { + errors->GetErrorsForCert(1)->AddWarning(kWarningFromDelegate, nullptr); + } +}; + +TEST_F(PathBuilderCheckPathAfterVerificationTest, AddsWarningToValidPath) { + CertPathBuilder::Result result; + + AddWarningPathBuilderDelegate delegate; + RunPathBuilder(nullptr, &delegate, &result); + ASSERT_TRUE(result.HasValidPath()); + + // A warning should have been added to certificate at index 1 in the path. + const CertErrors* cert1_errors = + result.GetBestValidPath()->errors.GetErrorsForCert(1); + ASSERT_TRUE(cert1_errors); + EXPECT_TRUE(cert1_errors->ContainsError(kWarningFromDelegate)); +} + +DEFINE_CERT_ERROR_ID(kErrorFromDelegate, "Error from delegate"); + +class AddErrorPathBuilderDelegate : public CertPathBuilderDelegateBase { + public: + void CheckPathAfterVerification(const CertPath& path, + CertPathErrors* errors) override { + errors->GetErrorsForCert(2)->AddError(kErrorFromDelegate, nullptr); + } +}; + +TEST_F(PathBuilderCheckPathAfterVerificationTest, AddsErrorToValidPath) { + CertPathBuilder::Result result; + + AddErrorPathBuilderDelegate delegate; + RunPathBuilder(nullptr, &delegate, &result); + + // Verification failed. + ASSERT_FALSE(result.HasValidPath()); + + ASSERT_LT(result.best_result_index, result.paths.size()); + const CertPathBuilder::ResultPath* failed_path = + result.paths[result.best_result_index].get(); + ASSERT_TRUE(failed_path); + + // An error should have been added to certificate at index 2 in the path. + const CertErrors* cert2_errors = failed_path->errors.GetErrorsForCert(2); + ASSERT_TRUE(cert2_errors); + EXPECT_TRUE(cert2_errors->ContainsError(kErrorFromDelegate)); +} + +TEST_F(PathBuilderCheckPathAfterVerificationTest, NoopToAlreadyInvalidPath) { + CertPathBuilder::Result result; + + StrictMock<MockPathBuilderDelegate> delegate; + // Just verify that the hook is called (on an invalid path). + EXPECT_CALL(delegate, CheckPathAfterVerification(_, _)); + + // Run the pathbuilder with certificate at index 1 actively distrusted. + RunPathBuilder(test_.chain[1], &delegate, &result); + EXPECT_FALSE(result.HasValidPath()); +} + } // namespace } // namespace net
diff --git a/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc b/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc index 3de3cc81a..9ef6631 100644 --- a/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc +++ b/net/cert/internal/path_builder_verify_certificate_chain_unittest.cc
@@ -5,7 +5,7 @@ #include "net/cert/internal/path_builder.h" #include "net/cert/internal/cert_issuer_source_static.h" -#include "net/cert/internal/signature_policy.h" +#include "net/cert/internal/simple_path_builder_delegate.h" #include "net/cert/internal/trust_store_in_memory.h" #include "net/cert/internal/verify_certificate_chain_typed_unittest.h" @@ -13,11 +13,11 @@ namespace { -class PathBuilderDelegate { +class PathBuilderTestDelegate { public: static void Verify(const VerifyCertChainTest& test, const std::string& test_file_path) { - SimpleSignaturePolicy signature_policy(1024); + SimplePathBuilderDelegate path_builder_delegate(1024); ASSERT_FALSE(test.chain.empty()); TrustStoreInMemory trust_store; @@ -44,7 +44,7 @@ CertPathBuilder::Result result; // First cert in the |chain| is the target. CertPathBuilder path_builder( - test.chain.front(), &trust_store, &signature_policy, test.time, + test.chain.front(), &trust_store, &path_builder_delegate, test.time, test.key_purpose, test.initial_explicit_policy, test.user_initial_policy_set, test.initial_policy_mapping_inhibit, test.initial_any_policy_inhibit, &result); @@ -59,6 +59,6 @@ INSTANTIATE_TYPED_TEST_CASE_P(PathBuilder, VerifyCertificateChainSingleRootTest, - PathBuilderDelegate); + PathBuilderTestDelegate); } // namespace net
diff --git a/net/cert/internal/signature_policy.cc b/net/cert/internal/signature_policy.cc deleted file mode 100644 index d8a877c9..0000000 --- a/net/cert/internal/signature_policy.cc +++ /dev/null
@@ -1,112 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/cert/internal/signature_policy.h" - -#include "base/logging.h" -#include "net/cert/internal/cert_error_params.h" -#include "net/cert/internal/cert_errors.h" -#include "third_party/boringssl/src/include/openssl/nid.h" - -namespace net { - -DEFINE_CERT_ERROR_ID(kRsaModulusTooSmall, "RSA modulus too small"); - -namespace { - -DEFINE_CERT_ERROR_ID(kUnacceptableCurveForEcdsa, - "Only P-256, P-384, P-521 are supported for ECDSA"); - -bool IsModulusSizeGreaterOrEqual(size_t modulus_length_bits, - size_t min_length_bits, - CertErrors* errors) { - if (modulus_length_bits < min_length_bits) { - errors->AddError(kRsaModulusTooSmall, - CreateCertErrorParams2SizeT("actual", modulus_length_bits, - "minimum", min_length_bits)); - return false; - } - return true; -} - -// Whitelist of default permitted signature digest algorithms. -WARN_UNUSED_RESULT bool IsAcceptableDigest(DigestAlgorithm digest) { - switch (digest) { - case DigestAlgorithm::Md2: - case DigestAlgorithm::Md4: - case DigestAlgorithm::Md5: - return false; - - case DigestAlgorithm::Sha1: - case DigestAlgorithm::Sha256: - case DigestAlgorithm::Sha384: - case DigestAlgorithm::Sha512: - return true; - } - - return false; -} - -} // namespace - -bool SignaturePolicy::IsAcceptableSignatureAlgorithm( - const SignatureAlgorithm& algorithm, - CertErrors* errors) const { - // Whitelist default permitted signature algorithms to: - // - // RSA PKCS#1 v1.5 - // RSASSA-PSS - // ECDSA - // - // When used with digest algorithms: - // - // SHA1 - // SHA256 - // SHA384 - // SHA512 - switch (algorithm.algorithm()) { - case SignatureAlgorithmId::Dsa: - return false; - case SignatureAlgorithmId::Ecdsa: - case SignatureAlgorithmId::RsaPkcs1: - return IsAcceptableDigest(algorithm.digest()); - case SignatureAlgorithmId::RsaPss: - return IsAcceptableDigest(algorithm.digest()) && - IsAcceptableDigest(algorithm.ParamsForRsaPss()->mgf1_hash()); - } - - return false; -} - -bool SignaturePolicy::IsAcceptableCurveForEcdsa(int curve_nid, - CertErrors* errors) const { - // Whitelist default permitted named curves. - switch (curve_nid) { - case NID_X9_62_prime256v1: - case NID_secp384r1: - case NID_secp521r1: - return true; - } - - errors->AddError(kUnacceptableCurveForEcdsa); - return false; -} - -bool SignaturePolicy::IsAcceptableModulusLengthForRsa( - size_t modulus_length_bits, - CertErrors* errors) const { - return IsModulusSizeGreaterOrEqual(modulus_length_bits, 2048, errors); -} - -SimpleSignaturePolicy::SimpleSignaturePolicy(size_t min_rsa_modulus_length_bits) - : min_rsa_modulus_length_bits_(min_rsa_modulus_length_bits) {} - -bool SimpleSignaturePolicy::IsAcceptableModulusLengthForRsa( - size_t modulus_length_bits, - CertErrors* errors) const { - return IsModulusSizeGreaterOrEqual(modulus_length_bits, - min_rsa_modulus_length_bits_, errors); -} - -} // namespace net
diff --git a/net/cert/internal/signature_policy.h b/net/cert/internal/signature_policy.h deleted file mode 100644 index fbc94ad9..0000000 --- a/net/cert/internal/signature_policy.h +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef NET_CERT_INTERNAL_SIGNATURE_POLICY_H_ -#define NET_CERT_INTERNAL_SIGNATURE_POLICY_H_ - -#include <stddef.h> - -#include "base/compiler_specific.h" -#include "net/base/net_export.h" -#include "net/cert/internal/cert_errors.h" -#include "net/cert/internal/signature_algorithm.h" - -namespace net { - -class CertErrors; -class SignatureAlgorithm; - -// SignaturePolicy is an interface (and base implementation) for applying -// policies when verifying signed data. It lets callers override which -// algorithms, named curves, and key sizes to allow. -class NET_EXPORT SignaturePolicy { - public: - virtual ~SignaturePolicy() {} - - // Implementations should return true if |algorithm| is acceptable. For - // instance, implementations could reject any signature algorithms that used - // SHA-1. - // - // The default implementation accepts all signature algorithms. - virtual bool IsAcceptableSignatureAlgorithm( - const SignatureAlgorithm& algorithm, - CertErrors* errors) const; - - // Implementations should return true if |curve_nid| is an allowed - // elliptical curve. |curve_nid| is an object ID from BoringSSL (for example - // NID_secp384r1). - // - // The default implementation accepts secp256r1, secp384r1, secp521r1 only. - virtual bool IsAcceptableCurveForEcdsa(int curve_nid, - CertErrors* errors) const; - - // Implementations should return true if |modulus_length_bits| is an allowed - // RSA key size in bits. - // - // The default implementation accepts any modulus length >= 2048 bits. - virtual bool IsAcceptableModulusLengthForRsa(size_t modulus_length_bits, - CertErrors* errors) const; -}; - -// SimpleSignaturePolicy modifies the base SignaturePolicy by allowing the -// minimum RSA key length to be specified (rather than hard coded to 2048). -class NET_EXPORT SimpleSignaturePolicy : public SignaturePolicy { - public: - explicit SimpleSignaturePolicy(size_t min_rsa_modulus_length_bits); - - bool IsAcceptableModulusLengthForRsa(size_t modulus_length_bits, - CertErrors* errors) const override; - - private: - const size_t min_rsa_modulus_length_bits_; -}; - -// TODO(crbug.com/634443): Move exported errors to a central location? -extern CertErrorId kRsaModulusTooSmall; - -} // namespace net - -#endif // NET_CERT_INTERNAL_SIGNATURE_POLICY_H_
diff --git a/net/cert/internal/simple_path_builder_delegate.cc b/net/cert/internal/simple_path_builder_delegate.cc new file mode 100644 index 0000000..4652b49 --- /dev/null +++ b/net/cert/internal/simple_path_builder_delegate.cc
@@ -0,0 +1,141 @@ +// 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 "net/cert/internal/simple_path_builder_delegate.h" + +#include "base/logging.h" +#include "net/cert/internal/cert_error_params.h" +#include "net/cert/internal/cert_errors.h" +#include "net/cert/internal/signature_algorithm.h" +#include "net/cert/internal/verify_signed_data.h" +#include "third_party/boringssl/src/include/openssl/bn.h" +#include "third_party/boringssl/src/include/openssl/bytestring.h" +#include "third_party/boringssl/src/include/openssl/digest.h" +#include "third_party/boringssl/src/include/openssl/ec.h" +#include "third_party/boringssl/src/include/openssl/ec_key.h" +#include "third_party/boringssl/src/include/openssl/evp.h" +#include "third_party/boringssl/src/include/openssl/nid.h" +#include "third_party/boringssl/src/include/openssl/rsa.h" + +namespace net { + +DEFINE_CERT_ERROR_ID(kRsaModulusTooSmall, "RSA modulus too small"); + +namespace { + +DEFINE_CERT_ERROR_ID(kUnacceptableCurveForEcdsa, + "Only P-256, P-384, P-521 are supported for ECDSA"); + +// Whitelist of default permitted signature digest algorithms. +WARN_UNUSED_RESULT bool IsAcceptableDigest(DigestAlgorithm digest) { + switch (digest) { + case DigestAlgorithm::Md2: + case DigestAlgorithm::Md4: + case DigestAlgorithm::Md5: + return false; + + case DigestAlgorithm::Sha1: + case DigestAlgorithm::Sha256: + case DigestAlgorithm::Sha384: + case DigestAlgorithm::Sha512: + return true; + } + + return false; +} + +bool IsAcceptableCurveForEcdsa(int curve_nid) { + // Whitelist default permitted named curves. + switch (curve_nid) { + case NID_X9_62_prime256v1: + case NID_secp384r1: + case NID_secp521r1: + return true; + } + + return false; +} + +} // namespace + +SimplePathBuilderDelegate::SimplePathBuilderDelegate( + size_t min_rsa_modulus_length_bits) + : min_rsa_modulus_length_bits_(min_rsa_modulus_length_bits) {} + +void SimplePathBuilderDelegate::CheckPathAfterVerification( + const CertPath& path, + CertPathErrors* errors) { + // Do nothing - consider all candidate paths valid. +} + +bool SimplePathBuilderDelegate::IsSignatureAlgorithmAcceptable( + const SignatureAlgorithm& algorithm, + CertErrors* errors) { + // Whitelist default permitted signature algorithms to: + // + // RSA PKCS#1 v1.5 + // RSASSA-PSS + // ECDSA + // + // When used with digest algorithms: + // + // SHA1 + // SHA256 + // SHA384 + // SHA512 + switch (algorithm.algorithm()) { + case SignatureAlgorithmId::Dsa: + return false; + case SignatureAlgorithmId::Ecdsa: + case SignatureAlgorithmId::RsaPkcs1: + return IsAcceptableDigest(algorithm.digest()); + case SignatureAlgorithmId::RsaPss: + return IsAcceptableDigest(algorithm.digest()) && + IsAcceptableDigest(algorithm.ParamsForRsaPss()->mgf1_hash()); + } + + return false; +} + +bool SimplePathBuilderDelegate::IsPublicKeyAcceptable(EVP_PKEY* public_key, + CertErrors* errors) { + int pkey_id = EVP_PKEY_id(public_key); + if (pkey_id == EVP_PKEY_RSA) { + // Extract the modulus length from the key. + RSA* rsa = EVP_PKEY_get0_RSA(public_key); + if (!rsa) + return false; + unsigned int modulus_length_bits = BN_num_bits(rsa->n); + + if (modulus_length_bits < min_rsa_modulus_length_bits_) { + errors->AddError( + kRsaModulusTooSmall, + CreateCertErrorParams2SizeT("actual", modulus_length_bits, "minimum", + min_rsa_modulus_length_bits_)); + return false; + } + + return true; + } + + if (pkey_id == EVP_PKEY_EC) { + // Extract the curve name. + EC_KEY* ec = EVP_PKEY_get0_EC_KEY(public_key); + if (!ec) + return false; // Unexpected. + int curve_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); + + if (!IsAcceptableCurveForEcdsa(curve_nid)) { + errors->AddError(kUnacceptableCurveForEcdsa); + return false; + } + + return true; + } + + // Unexpected key type. + return false; +} + +} // namespace net
diff --git a/net/cert/internal/simple_path_builder_delegate.h b/net/cert/internal/simple_path_builder_delegate.h new file mode 100644 index 0000000..e5ec0875 --- /dev/null +++ b/net/cert/internal/simple_path_builder_delegate.h
@@ -0,0 +1,51 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_CERT_INTERNAL_SIMPLE_PATH_BUILDER_DELEGATE_H_ +#define NET_CERT_INTERNAL_SIMPLE_PATH_BUILDER_DELEGATE_H_ + +#include <stddef.h> + +#include "base/compiler_specific.h" +#include "net/base/net_export.h" +#include "net/cert/internal/path_builder.h" + +namespace net { + +class CertErrors; +class SignatureAlgorithm; + +// SimplePathBuilderDelegate is an implementation of CertPathBuilderDelegate +// that uses some default policies: +// +// * RSA public keys must be >= |min_rsa_modulus_length_bits|. +// * Signature algorithm can be RSA PKCS#1, RSASSA-PSS or ECDSA +// * Hash algorithm can be SHA1, SHA256, SHA348 or SHA512 +// * EC named curve can be P-256, P-384, P-521. +class NET_EXPORT SimplePathBuilderDelegate : public CertPathBuilderDelegate { + public: + explicit SimplePathBuilderDelegate(size_t min_rsa_modulus_length_bits); + + // Accepts RSA PKCS#1, RSASSA-PSS or ECDA using any of the SHA* digests + // (including SHA1). + bool IsSignatureAlgorithmAcceptable( + const SignatureAlgorithm& signature_algorithm, + CertErrors* errors) override; + + // Requires RSA keys be >= |min_rsa_modulus_length_bits_|. + bool IsPublicKeyAcceptable(EVP_PKEY* public_key, CertErrors* errors) override; + + // No-op implementation. + void CheckPathAfterVerification(const CertPath& path, + CertPathErrors* errors) override; + + private: + const size_t min_rsa_modulus_length_bits_; +}; + +extern CertErrorId kRsaModulusTooSmall; + +} // namespace net + +#endif // NET_CERT_INTERNAL_SIMPLE_PATH_BUILDER_DELEGATE_H_
diff --git a/net/cert/internal/simple_path_builder_delegate_unittest.cc b/net/cert/internal/simple_path_builder_delegate_unittest.cc new file mode 100644 index 0000000..507b115 --- /dev/null +++ b/net/cert/internal/simple_path_builder_delegate_unittest.cc
@@ -0,0 +1,111 @@ +// 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 "net/cert/internal/simple_path_builder_delegate.h" + +#include <memory> +#include <set> + +#include "net/cert/internal/cert_errors.h" +#include "net/cert/internal/signature_algorithm.h" +#include "net/cert/internal/test_helpers.h" +#include "net/cert/internal/verify_signed_data.h" +#include "net/der/input.h" +#include "net/der/parse_values.h" +#include "net/der/parser.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/boringssl/src/include/openssl/nid.h" + +namespace net { + +namespace { + +// Reads the public key and algorithm from the test data at |file_name|. +void ReadTestCase(const char* file_name, + std::unique_ptr<SignatureAlgorithm>* signature_algorithm, + bssl::UniquePtr<EVP_PKEY>* public_key) { + std::string path = + std::string("net/data/verify_signed_data_unittest/") + file_name; + + std::string public_key_str; + std::string algorithm_str; + + const PemBlockMapping mappings[] = { + {"PUBLIC KEY", &public_key_str}, {"ALGORITHM", &algorithm_str}, + }; + + ASSERT_TRUE(ReadTestDataFromPemFile(path, mappings)); + + CertErrors algorithm_errors; + *signature_algorithm = + SignatureAlgorithm::Create(der::Input(&algorithm_str), &algorithm_errors); + ASSERT_TRUE(*signature_algorithm) << algorithm_errors.ToDebugString(); + + ASSERT_TRUE(ParsePublicKey(der::Input(&public_key_str), public_key)); +} + +class SimplePathBuilderDelegate1024SuccessTest + : public ::testing::TestWithParam<const char*> {}; + +const char* kSuccess1024Filenames[] = { + "rsa-pkcs1-sha1.pem", + "rsa-pkcs1-sha256.pem", + "rsa2048-pkcs1-sha512.pem", + "ecdsa-secp384r1-sha256.pem", + "ecdsa-prime256v1-sha512.pem", + "rsa-pss-sha1-salt20.pem", + "rsa-pss-sha256-mgf1-sha512-salt33.pem", + "rsa-pss-sha256-salt10.pem", + "ecdsa-secp384r1-sha256.pem", + "ecdsa-prime256v1-sha512.pem", +}; + +INSTANTIATE_TEST_CASE_P(, + SimplePathBuilderDelegate1024SuccessTest, + ::testing::ValuesIn(kSuccess1024Filenames)); + +TEST_P(SimplePathBuilderDelegate1024SuccessTest, IsAcceptableSignatureAndKey) { + std::unique_ptr<SignatureAlgorithm> signature_algorithm; + bssl::UniquePtr<EVP_PKEY> public_key; + ReadTestCase(GetParam(), &signature_algorithm, &public_key); + ASSERT_TRUE(signature_algorithm); + ASSERT_TRUE(public_key); + + CertErrors errors; + SimplePathBuilderDelegate delegate(1024); + + EXPECT_TRUE( + delegate.IsSignatureAlgorithmAcceptable(*signature_algorithm, &errors)); + + EXPECT_TRUE(delegate.IsPublicKeyAcceptable(public_key.get(), &errors)); +} + +class SimplePathBuilderDelegate2048FailTest + : public ::testing::TestWithParam<const char*> {}; + +const char* kFail2048Filenames[] = {"rsa-pkcs1-sha1.pem", + "rsa-pkcs1-sha256.pem"}; + +INSTANTIATE_TEST_CASE_P(, + SimplePathBuilderDelegate2048FailTest, + ::testing::ValuesIn(kFail2048Filenames)); + +TEST_P(SimplePathBuilderDelegate2048FailTest, RsaKeySmallerThan2048) { + std::unique_ptr<SignatureAlgorithm> signature_algorithm; + bssl::UniquePtr<EVP_PKEY> public_key; + ReadTestCase(GetParam(), &signature_algorithm, &public_key); + ASSERT_TRUE(signature_algorithm); + ASSERT_TRUE(public_key); + + CertErrors errors; + SimplePathBuilderDelegate delegate(2048); + + EXPECT_TRUE( + delegate.IsSignatureAlgorithmAcceptable(*signature_algorithm, &errors)); + + EXPECT_FALSE(delegate.IsPublicKeyAcceptable(public_key.get(), &errors)); +} + +} // namespace + +} // namespace net
diff --git a/net/cert/internal/verify_certificate_chain.cc b/net/cert/internal/verify_certificate_chain.cc index 4f2c088b..db4b283 100644 --- a/net/cert/internal/verify_certificate_chain.cc +++ b/net/cert/internal/verify_certificate_chain.cc
@@ -15,7 +15,6 @@ #include "net/cert/internal/name_constraints.h" #include "net/cert/internal/parse_certificate.h" #include "net/cert/internal/signature_algorithm.h" -#include "net/cert/internal/signature_policy.h" #include "net/cert/internal/trust_store.h" #include "net/cert/internal/verify_signed_data.h" #include "net/der/input.h" @@ -67,6 +66,10 @@ DEFINE_CERT_ERROR_ID(kNoValidPolicy, "No valid policy"); DEFINE_CERT_ERROR_ID(kPolicyMappingAnyPolicy, "PolicyMappings must not map anyPolicy"); +DEFINE_CERT_ERROR_ID(kFailedParsingSpki, "Couldn't parse SubjectPublicKeyInfo"); +DEFINE_CERT_ERROR_ID(kUnacceptableSignatureAlgorithm, + "Unacceptable signature algorithm"); +DEFINE_CERT_ERROR_ID(kUnacceptablePublicKey, "Unacceptable public key"); bool IsHandledCriticalExtension(const ParsedExtension& extension) { if (extension.oid == BasicConstraintsOid()) @@ -437,7 +440,7 @@ // Same parameters and meaning as VerifyCertificateChain(). void Run(const ParsedCertificateList& certs, const CertificateTrust& last_cert_trust, - const SignaturePolicy* signature_policy, + VerifyCertificateChainDelegate* delegate, const der::GeneralizedTime& time, KeyPurpose required_key_purpose, InitialExplicitPolicy initial_explicit_policy, @@ -462,7 +465,6 @@ // Processing" procedure. void BasicCertificateProcessing(const ParsedCertificate& cert, bool is_target_cert, - const SignaturePolicy* signature_policy, const der::GeneralizedTime& time, KeyPurpose required_key_purpose, CertErrors* errors); @@ -491,6 +493,12 @@ KeyPurpose required_key_purpose, CertErrors* errors); + // Parses |spki| to an EVP_PKEY and checks whether the public key is accepted + // by |delegate_|. On failure parsing returns nullptr. If either parsing the + // key or key policy failed, adds a high-severity error to |errors|. + bssl::UniquePtr<EVP_PKEY> ParseAndCheckPublicKey(const der::Input& spki, + CertErrors* errors); + ValidPolicyTree valid_policy_tree_; // Will contain a NameConstraints for each previous cert in the chain which @@ -541,21 +549,22 @@ // otherwise the initial value is n+1. size_t policy_mapping_; - // |working_spki_| is an amalgamation of 3 separate variables from RFC 5280: + // |working_public_key_| is an amalgamation of 3 separate variables from RFC + // 5280: // * working_public_key // * working_public_key_algorithm // * working_public_key_parameters // // They are combined for simplicity since the signature verification takes an - // SPKI, and the parameter inheritence is not applicable for the supported - // key types. + // EVP_PKEY, and the parameter inheritence is not applicable for the supported + // key types. |working_public_key_| may be null if parsing failed. // - // An approximate explanation of |working_spki| is this description from RFC - // 5280 section 6.1.2: + // An approximate explanation of |working_public_key_| is this description + // from RFC 5280 section 6.1.2: // // working_public_key: the public key used to verify the // signature of a certificate. - der::Input working_spki_; + bssl::UniquePtr<EVP_PKEY> working_public_key_; // |working_normalized_issuer_name_| is the normalized value of the // working_issuer_name variable in RFC 5280 section 6.1.2: @@ -573,6 +582,8 @@ // field within the basic constraints extension of a CA // certificate. size_t max_path_length_; + + VerifyCertificateChainDelegate* delegate_; }; void PathVerifier::VerifyPolicies(const ParsedCertificate& cert, @@ -779,7 +790,6 @@ void PathVerifier::BasicCertificateProcessing( const ParsedCertificate& cert, bool is_target_cert, - const SignaturePolicy* signature_policy, const der::GeneralizedTime& time, KeyPurpose required_key_purpose, CertErrors* errors) { @@ -788,12 +798,20 @@ // sections 4.1.1.2 and 4.1.2.3. VerifySignatureAlgorithmsMatch(cert, errors); - // Verify the digital signature using the previous certificate's key (RFC - // 5280 section 6.1.3 step a.1). - if (!VerifySignedData(cert.signature_algorithm(), cert.tbs_certificate_tlv(), - cert.signature_value(), working_spki_, signature_policy, - errors)) { - errors->AddError(kVerifySignedDataFailed); + // Check whether this signature algorithm is allowed. + if (!delegate_->IsSignatureAlgorithmAcceptable(cert.signature_algorithm(), + errors)) { + errors->AddError(kUnacceptableSignatureAlgorithm); + } + + if (working_public_key_) { + // Verify the digital signature using the previous certificate's key (RFC + // 5280 section 6.1.3 step a.1). + if (!VerifySignedData(cert.signature_algorithm(), + cert.tbs_certificate_tlv(), cert.signature_value(), + working_public_key_.get())) { + errors->AddError(kVerifySignedDataFailed); + } } // Check the time range for the certificate's validity, ensuring it is valid @@ -844,7 +862,7 @@ // From RFC 5280 section 6.1.4 step d: // // Assign the certificate subjectPublicKey to working_public_key. - working_spki_ = cert.tbs().spki_tlv; + working_public_key_ = ParseAndCheckPublicKey(cert.tbs().spki_tlv, errors); // Note that steps e and f are omitted as they are handled by // the assignment to |working_spki| above. See the definition @@ -1048,6 +1066,11 @@ // The following check is NOT part of RFC 5280 6.1.5's "Wrap-Up Procedure", // however is implied by RFC 5280 section 4.2.1.9. VerifyTargetCertHasConsistentCaBits(cert, errors); + + // Check the public key for the target certificate. The public key for the + // other certificates is already checked by PrepareForNextCertificate(). + // Note that this step is not part of RFC 5280 6.1.5. + ParseAndCheckPublicKey(cert.tbs().spki_tlv, errors); } void PathVerifier::ApplyTrustAnchorConstraints(const ParsedCertificate& cert, @@ -1101,7 +1124,7 @@ // Use the certificate's SPKI and subject when verifying the next certificate. // Note this is initialized even in the case of untrusted roots (they already // emit an error for the distrust). - working_spki_ = cert.tbs().spki_tlv; + working_public_key_ = ParseAndCheckPublicKey(cert.tbs().spki_tlv, errors); working_normalized_issuer_name_ = cert.normalized_subject(); switch (trust.type) { @@ -1123,10 +1146,27 @@ } } +bssl::UniquePtr<EVP_PKEY> PathVerifier::ParseAndCheckPublicKey( + const der::Input& spki, + CertErrors* errors) { + // Parse the public key. + bssl::UniquePtr<EVP_PKEY> pkey; + if (!ParsePublicKey(spki, &pkey)) { + errors->AddError(kFailedParsingSpki); + return nullptr; + } + + // Check if the key is acceptable by the delegate. + if (!delegate_->IsPublicKeyAcceptable(pkey.get(), errors)) + errors->AddError(kUnacceptablePublicKey); + + return pkey; +} + void PathVerifier::Run( const ParsedCertificateList& certs, const CertificateTrust& last_cert_trust, - const SignaturePolicy* signature_policy, + VerifyCertificateChainDelegate* delegate, const der::GeneralizedTime& time, KeyPurpose required_key_purpose, InitialExplicitPolicy initial_explicit_policy, @@ -1137,9 +1177,11 @@ CertPathErrors* errors) { // This implementation is structured to mimic the description of certificate // path verification given by RFC 5280 section 6.1. - DCHECK(signature_policy); + DCHECK(delegate); DCHECK(errors); + delegate_ = delegate; + // An empty chain is necessarily invalid. if (certs.empty()) { errors->GetOtherErrors()->AddError(kChainIsEmpty); @@ -1223,8 +1265,8 @@ // * If it is the last certificate in the path (target certificate) // - Then run "Wrap up" // - Otherwise run "Prepare for Next cert" - BasicCertificateProcessing(cert, is_target_cert, signature_policy, time, - required_key_purpose, cert_errors); + BasicCertificateProcessing(cert, is_target_cert, time, required_key_purpose, + cert_errors); if (!is_target_cert) { PrepareForNextCertificate(cert, cert_errors); } else { @@ -1246,10 +1288,12 @@ } // namespace +VerifyCertificateChainDelegate::~VerifyCertificateChainDelegate() = default; + void VerifyCertificateChain( const ParsedCertificateList& certs, const CertificateTrust& last_cert_trust, - const SignaturePolicy* signature_policy, + VerifyCertificateChainDelegate* delegate, const der::GeneralizedTime& time, KeyPurpose required_key_purpose, InitialExplicitPolicy initial_explicit_policy, @@ -1259,10 +1303,10 @@ std::set<der::Input>* user_constrained_policy_set, CertPathErrors* errors) { PathVerifier verifier; - verifier.Run(certs, last_cert_trust, signature_policy, time, - required_key_purpose, initial_explicit_policy, - user_initial_policy_set, initial_policy_mapping_inhibit, - initial_any_policy_inhibit, user_constrained_policy_set, errors); + verifier.Run(certs, last_cert_trust, delegate, time, required_key_purpose, + initial_explicit_policy, user_initial_policy_set, + initial_policy_mapping_inhibit, initial_any_policy_inhibit, + user_constrained_policy_set, errors); } } // namespace net
diff --git a/net/cert/internal/verify_certificate_chain.h b/net/cert/internal/verify_certificate_chain.h index 5beafb6..ff3dc7b 100644 --- a/net/cert/internal/verify_certificate_chain.h +++ b/net/cert/internal/verify_certificate_chain.h
@@ -13,6 +13,7 @@ #include "net/cert/internal/cert_errors.h" #include "net/cert/internal/parsed_certificate.h" #include "net/der/input.h" +#include "third_party/boringssl/src/include/openssl/evp.h" namespace net { @@ -20,7 +21,6 @@ struct GeneralizedTime; } -class SignaturePolicy; struct CertificateTrust; // The key purpose (extended key usage) to check for during verification. @@ -45,6 +45,30 @@ kTrue, }; +// VerifyCertificateChainDelegate exposes delegate methods used when verifying a +// chain. +class NET_EXPORT VerifyCertificateChainDelegate { + public: + // Implementations should return true if |signature_algorithm| is allowed for + // certificate signing, false otherwise. When returning false implementations + // can optionally add high-severity errors to |errors| with details on why it + // was rejected. + virtual bool IsSignatureAlgorithmAcceptable( + const SignatureAlgorithm& signature_algorithm, + CertErrors* errors) = 0; + + // Implementations should return true if |public_key| is acceptable. This is + // called for each certificate in the chain, including the target certificate. + // When returning false implementations can optionally add high-severity + // errors to |errors| with details on why it was rejected. + // + // |public_key| can be assumed to be non-null. + virtual bool IsPublicKeyAcceptable(EVP_PKEY* public_key, + CertErrors* errors) = 0; + + virtual ~VerifyCertificateChainDelegate(); +}; + // VerifyCertificateChain() verifies an ordered certificate path in accordance // with RFC 5280's "Certification Path Validation" algorithm (section 6). // @@ -98,9 +122,10 @@ // similar role to "trust anchor information" defined in RFC 5280 // section 6.1.1.d. // -// signature_policy: -// The policy to use when verifying signatures (what hash algorithms are -// allowed, what length keys, what named curves, etc). +// delegate: +// |delegate| must be non-null. It is used to answer policy questions such +// as whether a signature algorithm is acceptable, or a public key is strong +// enough. // // time: // The UTC time to use for expiration checks. This is equivalent to @@ -201,7 +226,7 @@ NET_EXPORT void VerifyCertificateChain( const ParsedCertificateList& certs, const CertificateTrust& last_cert_trust, - const SignaturePolicy* signature_policy, + VerifyCertificateChainDelegate* delegate, const der::GeneralizedTime& time, KeyPurpose required_key_purpose, InitialExplicitPolicy initial_explicit_policy,
diff --git a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc index e9b64f24..4443dd7a 100644 --- a/net/cert/internal/verify_certificate_chain_pkits_unittest.cc +++ b/net/cert/internal/verify_certificate_chain_pkits_unittest.cc
@@ -5,7 +5,7 @@ #include "net/cert/internal/verify_certificate_chain.h" #include "net/cert/internal/parsed_certificate.h" -#include "net/cert/internal/signature_policy.h" +#include "net/cert/internal/simple_path_builder_delegate.h" #include "net/cert/internal/trust_store.h" #include "net/der/input.h" #include "third_party/boringssl/src/include/openssl/pool.h" @@ -42,13 +42,13 @@ << parsing_errors.ToDebugString(); } - SimpleSignaturePolicy signature_policy(1024); + SimplePathBuilderDelegate path_builder_delegate(1024); std::set<der::Input> user_constrained_policy_set; CertPathErrors path_errors; VerifyCertificateChain( - input_chain, CertificateTrust::ForTrustAnchor(), &signature_policy, + input_chain, CertificateTrust::ForTrustAnchor(), &path_builder_delegate, info.time, KeyPurpose::ANY_EKU, info.initial_explicit_policy, info.initial_policy_set, info.initial_policy_mapping_inhibit, info.initial_inhibit_any_policy, &user_constrained_policy_set,
diff --git a/net/cert/internal/verify_certificate_chain_typed_unittest.h b/net/cert/internal/verify_certificate_chain_typed_unittest.h index 42a4023..9540408 100644 --- a/net/cert/internal/verify_certificate_chain_typed_unittest.h +++ b/net/cert/internal/verify_certificate_chain_typed_unittest.h
@@ -71,7 +71,7 @@ this->RunTest("target-unknown-critical-extension/main.test"); } -TYPED_TEST_P(VerifyCertificateChainSingleRootTest, Md5) { +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, WeakSignature) { this->RunTest("target-signed-with-md5/main.test"); this->RunTest("intermediate-signed-with-md5/main.test"); } @@ -86,8 +86,9 @@ this->RunTest("target-and-intermediate/unspecified-trust-root.test"); } -TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetSignedBy512bitRsa) { +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, WeakPublicKey) { this->RunTest("target-signed-by-512bit-rsa/main.test"); + this->RunTest("target-has-512bit-rsa-key/main.test"); } TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetSignedUsingEcdsa) { @@ -174,10 +175,10 @@ BasicConstraintsCa, BasicConstraintsPathlen, UnknownExtension, - Md5, + WeakSignature, WrongSignature, LastCertificateNotTrusted, - TargetSignedBy512bitRsa, + WeakPublicKey, TargetSignedUsingEcdsa, Expired, TargetNotEndEntity,
diff --git a/net/cert/internal/verify_certificate_chain_unittest.cc b/net/cert/internal/verify_certificate_chain_unittest.cc index 9b1e3e8..70595839 100644 --- a/net/cert/internal/verify_certificate_chain_unittest.cc +++ b/net/cert/internal/verify_certificate_chain_unittest.cc
@@ -4,7 +4,7 @@ #include "net/cert/internal/verify_certificate_chain.h" -#include "net/cert/internal/signature_policy.h" +#include "net/cert/internal/simple_path_builder_delegate.h" #include "net/cert/internal/test_helpers.h" #include "net/cert/internal/trust_store.h" #include "net/cert/internal/verify_certificate_chain_typed_unittest.h" @@ -13,16 +13,16 @@ namespace { -class VerifyCertificateChainDelegate { +class VerifyCertificateChainTestDelegate { public: static void Verify(const VerifyCertChainTest& test, const std::string& test_file_path) { - SimpleSignaturePolicy signature_policy(1024); + SimplePathBuilderDelegate delegate(1024); CertPathErrors errors; // TODO(eroman): Check user_constrained_policy_set. VerifyCertificateChain( - test.chain, test.last_cert_trust, &signature_policy, test.time, + test.chain, test.last_cert_trust, &delegate, test.time, test.key_purpose, test.initial_explicit_policy, test.user_initial_policy_set, test.initial_policy_mapping_inhibit, test.initial_any_policy_inhibit, @@ -36,6 +36,6 @@ INSTANTIATE_TYPED_TEST_CASE_P(VerifyCertificateChain, VerifyCertificateChainSingleRootTest, - VerifyCertificateChainDelegate); + VerifyCertificateChainTestDelegate); } // namespace net
diff --git a/net/cert/internal/verify_signed_data.cc b/net/cert/internal/verify_signed_data.cc index bb700fe67..668e8cb 100644 --- a/net/cert/internal/verify_signed_data.cc +++ b/net/cert/internal/verify_signed_data.cc
@@ -10,15 +10,11 @@ #include "crypto/openssl_util.h" #include "net/cert/internal/cert_errors.h" #include "net/cert/internal/signature_algorithm.h" -#include "net/cert/internal/signature_policy.h" #include "net/der/input.h" #include "net/der/parse_values.h" #include "net/der/parser.h" -#include "third_party/boringssl/src/include/openssl/bn.h" #include "third_party/boringssl/src/include/openssl/bytestring.h" #include "third_party/boringssl/src/include/openssl/digest.h" -#include "third_party/boringssl/src/include/openssl/ec.h" -#include "third_party/boringssl/src/include/openssl/ec_key.h" #include "third_party/boringssl/src/include/openssl/evp.h" #include "third_party/boringssl/src/include/openssl/rsa.h" @@ -26,15 +22,6 @@ namespace { -DEFINE_CERT_ERROR_ID(kUnacceptableSignatureAlgorithm, - "Unacceptable signature algorithm"); -DEFINE_CERT_ERROR_ID(kUnacceptableRsaModulusLength, - "Unacceptable modulus length for RSA key"); -DEFINE_CERT_ERROR_ID(kUnacceptableEcdsaCurve, - "Unacceptable curve for ECDSA key"); -DEFINE_CERT_ERROR_ID(kSignatureVerificationFailed, - "Signature verification failed"); - // Converts a DigestAlgorithm to an equivalent EVP_MD*. WARN_UNUSED_RESULT bool GetDigest(DigestAlgorithm digest, const EVP_MD** out) { *out = nullptr; @@ -81,29 +68,10 @@ salt_length_bytes_int.ValueOrDie()); } -// TODO(eroman): This function is not strict enough. It accepts BER, other RSA -// OIDs, and does not check id-rsaEncryption parameters. -// See https://crbug.com/522228 and https://crbug.com/522232 -WARN_UNUSED_RESULT bool ImportPkeyFromSpki(const der::Input& spki, - int expected_pkey_id, - bssl::UniquePtr<EVP_PKEY>* pkey) { - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); +} // namespace - CBS cbs; - CBS_init(&cbs, spki.UnsafeData(), spki.Length()); - pkey->reset(EVP_parse_public_key(&cbs)); - if (!*pkey || CBS_len(&cbs) != 0 || - EVP_PKEY_id(pkey->get()) != expected_pkey_id) { - pkey->reset(); - return false; - } - - return true; -} - -// Parses an RSA public key from SPKI to an EVP_PKEY. -// -// Returns true on success. +// Parses an RSA public key or EC public key from SPKI to an EVP_PKEY. Returns +// true on success. // // There are two flavors of RSA public key that this function should recognize // from RFC 5912 (however note that pk-rsaSSA-PSS is not supported in the @@ -157,83 +125,9 @@ // have ASN.1 type NULL for this algorithm identifier. // // Following RFC 3279 in this case. -WARN_UNUSED_RESULT bool ParseRsaKeyFromSpki(const der::Input& public_key_spki, - bssl::UniquePtr<EVP_PKEY>* pkey, - const SignaturePolicy* policy, - CertErrors* errors) { - // TODO(crbug.com/634443): Add more specific errors. - if (!ImportPkeyFromSpki(public_key_spki, EVP_PKEY_RSA, pkey)) - return false; - - // Extract the modulus length from the key. - RSA* rsa = EVP_PKEY_get0_RSA(pkey->get()); - if (!rsa) - return false; - unsigned int modulus_length_bits = BN_num_bits(rsa->n); - - if (!policy->IsAcceptableModulusLengthForRsa(modulus_length_bits, errors)) { - errors->AddError(kUnacceptableRsaModulusLength); - return false; - } - - return true; -} - -// Does signature verification using either RSA or ECDSA. -WARN_UNUSED_RESULT bool DoVerify(const SignatureAlgorithm& algorithm, - const der::Input& signed_data, - const der::BitString& signature_value, - EVP_PKEY* public_key) { - switch (algorithm.algorithm()) { - case SignatureAlgorithmId::Dsa: - return false; - case SignatureAlgorithmId::RsaPkcs1: - case SignatureAlgorithmId::RsaPss: - case SignatureAlgorithmId::Ecdsa: { - // For the supported algorithms the signature value must be a whole - // number of bytes. - if (signature_value.unused_bits() != 0) - return false; - const der::Input& signature_value_bytes = signature_value.bytes(); - - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - - bssl::ScopedEVP_MD_CTX ctx; - EVP_PKEY_CTX* pctx = nullptr; // Owned by |ctx|. - - const EVP_MD* digest; - if (!GetDigest(algorithm.digest(), &digest)) - return false; - - if (!EVP_DigestVerifyInit(ctx.get(), &pctx, digest, nullptr, public_key)) - return false; - - // Set the RSASSA-PSS specific options. - if (algorithm.algorithm() == SignatureAlgorithmId::RsaPss && - !ApplyRsaPssOptions(algorithm.ParamsForRsaPss(), pctx)) { - return false; - } - - if (!EVP_DigestVerifyUpdate(ctx.get(), signed_data.UnsafeData(), - signed_data.Length())) { - return false; - } - - return 1 == EVP_DigestVerifyFinal(ctx.get(), - signature_value_bytes.UnsafeData(), - signature_value_bytes.Length()); - } - } - - return false; -} - -// Parses an EC public key from SPKI to an EVP_PKEY. // -// Returns true on success. -// -// RFC 5912 describes all the ECDSA signature algorithms as requiring a public -// key of type "pk-ec": +// In the case of parsing EC keys, RFC 5912 describes all the ECDSA +// signature algorithms as requiring a public key of type "pk-ec": // // pk-ec PUBLIC-KEY ::= { // IDENTIFIER id-ecPublicKey @@ -272,66 +166,89 @@ // { ID secp521r1 } | { ID sect571k1 } | { ID sect571r1 }, // ... -- Extensible // } -WARN_UNUSED_RESULT bool ParseEcKeyFromSpki(const der::Input& public_key_spki, - bssl::UniquePtr<EVP_PKEY>* pkey, - const SignaturePolicy* policy, - CertErrors* errors) { - // TODO(crbug.com/634443): Add more specific errors. - if (!ImportPkeyFromSpki(public_key_spki, EVP_PKEY_EC, pkey)) - return false; +bool ParsePublicKey(const der::Input& public_key_spki, + bssl::UniquePtr<EVP_PKEY>* public_key) { + // Parse the SPKI to an EVP_PKEY. + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - // Extract the curve name. - EC_KEY* ec = EVP_PKEY_get0_EC_KEY(pkey->get()); - if (!ec) - return false; // Unexpected. - int curve_nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); - - if (!policy->IsAcceptableCurveForEcdsa(curve_nid, errors)) { - errors->AddError(kUnacceptableEcdsaCurve); + // TODO(eroman): This is not strict enough. It accepts BER, other RSA + // OIDs, and does not check id-rsaEncryption parameters. + // See https://crbug.com/522228 and https://crbug.com/522232 + CBS cbs; + CBS_init(&cbs, public_key_spki.UnsafeData(), public_key_spki.Length()); + public_key->reset(EVP_parse_public_key(&cbs)); + if (!*public_key || CBS_len(&cbs) != 0) { + public_key->reset(); return false; } - return true; } -} // namespace - -bool VerifySignedData(const SignatureAlgorithm& signature_algorithm, +bool VerifySignedData(const SignatureAlgorithm& algorithm, const der::Input& signed_data, const der::BitString& signature_value, - const der::Input& public_key_spki, - const SignaturePolicy* policy, - CertErrors* errors) { - if (!policy->IsAcceptableSignatureAlgorithm(signature_algorithm, errors)) { - // TODO(crbug.com/634443): Include the DER for the AlgorithmIdentifier - errors->AddError(kUnacceptableSignatureAlgorithm); - return false; - } - - bssl::UniquePtr<EVP_PKEY> public_key; - - // Parse the SPKI to an EVP_PKEY appropriate for the signature algorithm. - switch (signature_algorithm.algorithm()) { + EVP_PKEY* public_key) { + // Check that the key type matches the signature algorithm. + int expected_pkey_id = -1; + switch (algorithm.algorithm()) { case SignatureAlgorithmId::Dsa: + // DSA is not supported. return false; case SignatureAlgorithmId::RsaPkcs1: case SignatureAlgorithmId::RsaPss: - if (!ParseRsaKeyFromSpki(public_key_spki, &public_key, policy, errors)) - return false; + expected_pkey_id = EVP_PKEY_RSA; break; case SignatureAlgorithmId::Ecdsa: - if (!ParseEcKeyFromSpki(public_key_spki, &public_key, policy, errors)) - return false; + expected_pkey_id = EVP_PKEY_EC; break; } - if (!DoVerify(signature_algorithm, signed_data, signature_value, - public_key.get())) { - errors->AddError(kSignatureVerificationFailed); + if (expected_pkey_id != EVP_PKEY_id(public_key)) + return false; + + // For the supported algorithms the signature value must be a whole + // number of bytes. + if (signature_value.unused_bits() != 0) + return false; + const der::Input& signature_value_bytes = signature_value.bytes(); + + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + bssl::ScopedEVP_MD_CTX ctx; + EVP_PKEY_CTX* pctx = nullptr; // Owned by |ctx|. + + const EVP_MD* digest; + if (!GetDigest(algorithm.digest(), &digest)) + return false; + + if (!EVP_DigestVerifyInit(ctx.get(), &pctx, digest, nullptr, public_key)) + return false; + + // Set the RSASSA-PSS specific options. + if (algorithm.algorithm() == SignatureAlgorithmId::RsaPss && + !ApplyRsaPssOptions(algorithm.ParamsForRsaPss(), pctx)) { return false; } - return true; + if (!EVP_DigestVerifyUpdate(ctx.get(), signed_data.UnsafeData(), + signed_data.Length())) { + return false; + } + + return 1 == EVP_DigestVerifyFinal(ctx.get(), + signature_value_bytes.UnsafeData(), + signature_value_bytes.Length()); +} + +bool VerifySignedData(const SignatureAlgorithm& algorithm, + const der::Input& signed_data, + const der::BitString& signature_value, + const der::Input& public_key_spki) { + bssl::UniquePtr<EVP_PKEY> public_key; + if (!ParsePublicKey(public_key_spki, &public_key)) + return false; + return VerifySignedData(algorithm, signed_data, signature_value, + public_key.get()); } } // namespace net
diff --git a/net/cert/internal/verify_signed_data.h b/net/cert/internal/verify_signed_data.h index 9c74912..5c1c735 100644 --- a/net/cert/internal/verify_signed_data.h +++ b/net/cert/internal/verify_signed_data.h
@@ -6,7 +6,9 @@ #define NET_CERT_INTERNAL_VERIFY_SIGNED_DATA_H_ #include "base/compiler_specific.h" +#include "crypto/openssl_util.h" #include "net/base/net_export.h" +#include "third_party/boringssl/src/include/openssl/evp.h" namespace net { @@ -15,31 +17,33 @@ class Input; } // namespace der -class CertErrors; class SignatureAlgorithm; -class SignaturePolicy; // Verifies that |signature_value| is a valid signature of |signed_data| using -// the algorithm |signature_algorithm| and the public key |public_key|. +// the algorithm |algorithm| and the public key |public_key|. // -// |signature_algorithm| - The parsed AlgorithmIdentifier +// |algorithm| - The parsed AlgorithmIdentifier // |signed_data| - The blob of data to verify // |signature_value| - The BIT STRING for the signature's value -// |public_key| - A DER-encoded SubjectPublicKeyInfo. -// |policy| - Instance of the policy to use. This will be queried to -// determine if: -// * The parsed RSA key is an adequate size. -// * The parsed EC key is for an allowed curve. -// * The signature algorithm and its parameters are acceptable. -// |errors| - Non-null destination for errors/warnings information. +// |public_key| - The parsed (non-null) public key. // // Returns true if verification was successful. -NET_EXPORT bool VerifySignedData(const SignatureAlgorithm& signature_algorithm, +NET_EXPORT bool VerifySignedData(const SignatureAlgorithm& algorithm, const der::Input& signed_data, const der::BitString& signature_value, - const der::Input& public_key, - const SignaturePolicy* policy, - CertErrors* errors) WARN_UNUSED_RESULT; + EVP_PKEY* public_key) WARN_UNUSED_RESULT; + +// Same as above overload, only the public key is inputted as an SPKI and will +// be parsed internally. +NET_EXPORT bool VerifySignedData(const SignatureAlgorithm& algorithm, + const der::Input& signed_data, + const der::BitString& signature_value, + const der::Input& public_key_spki) + WARN_UNUSED_RESULT; + +NET_EXPORT bool ParsePublicKey(const der::Input& public_key_spki, + bssl::UniquePtr<EVP_PKEY>* public_key) + WARN_UNUSED_RESULT; } // namespace net
diff --git a/net/cert/internal/verify_signed_data_unittest.cc b/net/cert/internal/verify_signed_data_unittest.cc index 70a9463..4c7cb182 100644 --- a/net/cert/internal/verify_signed_data_unittest.cc +++ b/net/cert/internal/verify_signed_data_unittest.cc
@@ -9,13 +9,11 @@ #include "net/cert/internal/cert_errors.h" #include "net/cert/internal/signature_algorithm.h" -#include "net/cert/internal/signature_policy.h" #include "net/cert/internal/test_helpers.h" #include "net/der/input.h" #include "net/der/parse_values.h" #include "net/der/parser.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/boringssl/src/include/openssl/nid.h" namespace net { @@ -27,16 +25,14 @@ }; // Reads test data from |file_name| and runs VerifySignedData() over its -// inputs, using |policy|. +// inputs. // // If expected_result was SUCCESS then the test will only succeed if // VerifySignedData() returns true. // // If expected_result was FAILURE then the test will only succeed if // VerifySignedData() returns false. -void RunTestCaseUsingPolicy(VerifyResult expected_result, - const char* file_name, - const SignaturePolicy* policy) { +void RunTestCase(VerifyResult expected_result, const char* file_name) { std::string path = std::string("net/data/verify_signed_data_unittest/") + file_name; @@ -66,23 +62,11 @@ bool expected_result_bool = expected_result == SUCCESS; - CertErrors verify_errors; bool result = VerifySignedData(*signature_algorithm, der::Input(&signed_data), - signature_value_bit_string, der::Input(&public_key), - policy, &verify_errors); - EXPECT_EQ(expected_result_bool, result); - // TODO(crbug.com/634443): Verify the returned errors. - // if (!result) - // EXPECT_FALSE(verify_errors.empty()); -} + signature_value_bit_string, der::Input(&public_key)); -// RunTestCase() is the same as RunTestCaseUsingPolicy(), only it uses a -// default policy. This policy will accept a basic profile of signature -// algorithms (including ANY sized RSA key >= 1024). -void RunTestCase(VerifyResult expected_result, const char* file_name) { - SimpleSignaturePolicy policy(1024); - return RunTestCaseUsingPolicy(expected_result, file_name, &policy); + EXPECT_EQ(expected_result_bool, result); } // Read the descriptions in the test files themselves for details on what is @@ -218,85 +202,10 @@ RunTestCase(FAILURE, "ecdsa-prime256v1-sha512-unused-bits-signature.pem"); } -// This policy rejects specifically secp384r1 curves. -class RejectSecp384r1Policy : public SignaturePolicy { - public: - bool IsAcceptableCurveForEcdsa(int curve_nid, - CertErrors* errors) const override { - if (curve_nid == NID_secp384r1) - return false; - return true; - } -}; - -TEST(VerifySignedDataTest, PolicyIsAcceptableCurveForEcdsa) { +TEST(VerifySignedDataTest, Ecdsa384) { // Using the regular policy both secp384r1 and secp256r1 should be accepted. RunTestCase(SUCCESS, "ecdsa-secp384r1-sha256.pem"); RunTestCase(SUCCESS, "ecdsa-prime256v1-sha512.pem"); - - // However when using a policy that specifically rejects secp384r1, only - // prime256v1 should be accepted. - RejectSecp384r1Policy policy; - RunTestCaseUsingPolicy(FAILURE, "ecdsa-secp384r1-sha256.pem", &policy); - RunTestCaseUsingPolicy(SUCCESS, "ecdsa-prime256v1-sha512.pem", &policy); -} - -TEST(VerifySignedDataTest, PolicyIsAcceptableModulusLengthForRsa) { - // Using the regular policy both 1024-bit and 2048-bit RSA keys should be - // accepted. - SimpleSignaturePolicy policy_1024(1024); - RunTestCaseUsingPolicy(SUCCESS, "rsa-pkcs1-sha256.pem", &policy_1024); - RunTestCaseUsingPolicy(SUCCESS, "rsa2048-pkcs1-sha512.pem", &policy_1024); - - // However when using a policy that rejects any keys less than 2048-bits, only - // one of the tests will pass. - SimpleSignaturePolicy policy_2048(2048); - RunTestCaseUsingPolicy(FAILURE, "rsa-pkcs1-sha256.pem", &policy_2048); - RunTestCaseUsingPolicy(SUCCESS, "rsa2048-pkcs1-sha512.pem", &policy_2048); -} - -// This policy rejects the use of SHA-512. -class RejectSha512 : public SignaturePolicy { - public: - RejectSha512() : SignaturePolicy() {} - - bool IsAcceptableSignatureAlgorithm(const SignatureAlgorithm& algorithm, - CertErrors* errors) const override { - if (algorithm.algorithm() == SignatureAlgorithmId::RsaPss && - algorithm.ParamsForRsaPss()->mgf1_hash() == DigestAlgorithm::Sha512) { - return false; - } - - return algorithm.digest() != DigestAlgorithm::Sha512; - } - - bool IsAcceptableModulusLengthForRsa(size_t modulus_length_bits, - CertErrors* errors) const override { - return true; - } -}; - -TEST(VerifySignedDataTest, PolicyIsAcceptableDigestAlgorithm) { - // Using the regular policy use of either SHA256 or SHA512 should work - // (whether as the main digest, or the MGF1 for RSASSA-PSS) - RunTestCase(SUCCESS, "rsa2048-pkcs1-sha512.pem"); - RunTestCase(SUCCESS, "ecdsa-prime256v1-sha512.pem"); - RunTestCase(SUCCESS, "ecdsa-secp384r1-sha256.pem"); - RunTestCase(SUCCESS, "rsa-pkcs1-sha256.pem"); - RunTestCase(SUCCESS, "rsa-pss-sha256-salt10.pem"); - // This one uses both SHA256 and SHA512 - RunTestCase(SUCCESS, "rsa-pss-sha256-mgf1-sha512-salt33.pem"); - - // The tests using SHA512 should fail when using a policy that rejects SHA512. - // Everything else should pass. - RejectSha512 policy; - RunTestCaseUsingPolicy(FAILURE, "rsa2048-pkcs1-sha512.pem", &policy); - RunTestCaseUsingPolicy(FAILURE, "ecdsa-prime256v1-sha512.pem", &policy); - RunTestCaseUsingPolicy(SUCCESS, "ecdsa-secp384r1-sha256.pem", &policy); - RunTestCaseUsingPolicy(SUCCESS, "rsa-pkcs1-sha256.pem", &policy); - RunTestCaseUsingPolicy(SUCCESS, "rsa-pss-sha256-salt10.pem", &policy); - RunTestCaseUsingPolicy(FAILURE, "rsa-pss-sha256-mgf1-sha512-salt33.pem", - &policy); } } // namespace
diff --git a/net/cert/x509_certificate_bytes.cc b/net/cert/x509_certificate_bytes.cc index 90d3813..44f0d67 100644 --- a/net/cert/x509_certificate_bytes.cc +++ b/net/cert/x509_certificate_bytes.cc
@@ -14,7 +14,7 @@ #include "net/cert/internal/name_constraints.h" #include "net/cert/internal/parse_name.h" #include "net/cert/internal/parsed_certificate.h" -#include "net/cert/internal/signature_policy.h" +#include "net/cert/internal/signature_algorithm.h" #include "net/cert/internal/verify_name_match.h" #include "net/cert/internal/verify_signed_data.h" #include "net/cert/x509_util.h" @@ -462,11 +462,10 @@ if (!signature_algorithm) return false; - SimpleSignaturePolicy signature_policy(1024); - CertErrors unused_errors; + // Don't enforce any minimum key size or restrict the algorithm, since when + // self signed not very relevant. return VerifySignedData(*signature_algorithm, tbs_certificate_tlv, - signature_value, tbs.spki_tlv, &signature_policy, - &unused_errors); + signature_value, tbs.spki_tlv); } // static
diff --git a/net/data/verify_certificate_chain_unittest/incorrect-trust-anchor/main.test b/net/data/verify_certificate_chain_unittest/incorrect-trust-anchor/main.test index 4b470c1..de73cb3 100644 --- a/net/data/verify_certificate_chain_unittest/incorrect-trust-anchor/main.test +++ b/net/data/verify_certificate_chain_unittest/incorrect-trust-anchor/main.test
@@ -4,7 +4,6 @@ key_purpose: SERVER_AUTH expected_errors: ----- Certificate i=1 (CN=Intermediate) ----- -ERROR: Signature verification failed ERROR: VerifySignedData failed ERROR: subject does not match issuer
diff --git a/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.2.txt b/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.2.txt index c5b2701..859d749 100644 --- a/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.2.txt +++ b/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.2.txt
@@ -1,4 +1,3 @@ ----- Certificate i=1 (CN=Bad Signed CA,O=Test Certificates 2011,C=US) ----- -ERROR: Signature verification failed ERROR: VerifySignedData failed
diff --git a/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.3.txt b/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.3.txt index 46ed3910..eca3c4e 100644 --- a/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.3.txt +++ b/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.3.txt
@@ -1,4 +1,3 @@ ----- Certificate i=0 (CN=Invalid EE Signature Test3,O=Test Certificates 2011,C=US) ----- -ERROR: Signature verification failed ERROR: VerifySignedData failed
diff --git a/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.4.txt b/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.4.txt index 649daa7..d4c5a90 100644 --- a/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.4.txt +++ b/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.4.txt
@@ -1,4 +1,8 @@ ----- Certificate i=0 (CN=Valid DSA Signatures EE Certificate Test4,O=Test Certificates 2011,C=US) ----- ERROR: Unacceptable signature algorithm ERROR: VerifySignedData failed +ERROR: Unacceptable public key + +----- Certificate i=1 (CN=DSA CA,O=Test Certificates 2011,C=US) ----- +ERROR: Unacceptable public key
diff --git a/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.5.txt b/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.5.txt index 6a4c943e..1554b6a5 100644 --- a/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.5.txt +++ b/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.5.txt
@@ -1,8 +1,13 @@ ----- Certificate i=0 (CN=Valid DSA Parameter Inheritance EE Certificate Test5,O=Test Certificates 2011,C=US) ----- ERROR: Unacceptable signature algorithm ERROR: VerifySignedData failed +ERROR: Unacceptable public key ----- Certificate i=1 (CN=DSA Parameters Inherited CA,O=Test Certificates 2011,C=US) ----- ERROR: Unacceptable signature algorithm ERROR: VerifySignedData failed +ERROR: Unacceptable public key + +----- Certificate i=2 (CN=DSA CA,O=Test Certificates 2011,C=US) ----- +ERROR: Unacceptable public key
diff --git a/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.6.txt b/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.6.txt index 26662b9..bd927bb9 100644 --- a/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.6.txt +++ b/net/data/verify_certificate_chain_unittest/pkits_errors/4.1.6.txt
@@ -1,4 +1,8 @@ ----- Certificate i=0 (CN=Invalid DSA Signature EE Certificate Test6,O=Test Certificates 2011,C=US) ----- ERROR: Unacceptable signature algorithm ERROR: VerifySignedData failed +ERROR: Unacceptable public key + +----- Certificate i=1 (CN=DSA CA,O=Test Certificates 2011,C=US) ----- +ERROR: Unacceptable public key
diff --git a/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/chain.pem b/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/chain.pem new file mode 100644 index 0000000..7efc922 --- /dev/null +++ b/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/chain.pem
@@ -0,0 +1,254 @@ +[Created by: ./generate-chains.py] + +Certificate chain where the target certificate is contains a public key with +only a 512-bit modulus. + +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Intermediate + Validity + Not Before: Jan 1 12:00:00 2015 GMT + Not After : Jan 1 12:00:00 2016 GMT + Subject: CN=Target + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (512 bit) + Modulus: + 00:d5:13:bb:52:bf:ca:19:1a:06:19:68:07:1d:e6: + 87:16:d3:f0:e0:12:ba:a2:b5:2a:3d:ed:b3:64:16: + 06:a3:50:fc:b0:a4:49:f2:f9:ab:34:ad:4f:db:0a: + 3d:2b:25:92:86:3f:94:df:fb:fc:54:f2:c7:6d:9e: + d2:10:e0:cd:0d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + E9:D8:44:8D:24:EE:A1:82:18:6F:21:FA:4E:EC:FB:DF:D1:91:57:9D + X509v3 Authority Key Identifier: + keyid:CC:47:D8:FF:8B:67:33:FB:F5:CD:D0:A7:B6:12:A0:19:27:27:D1:57 + + Authority Information Access: + CA Issuers - URI:http://url-for-aia/Intermediate.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://url-for-crl/Intermediate.crl + + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + Signature Algorithm: sha256WithRSAEncryption + 10:87:ef:fc:16:91:74:55:5e:0d:b3:52:66:70:12:07:a1:ca: + 7c:d6:f8:a2:ff:3c:01:4f:8b:c9:37:55:af:f2:f5:31:bc:89: + 93:3b:96:0f:31:72:b6:96:bb:b4:d6:db:0b:61:a1:27:b7:c1: + 80:dd:3d:40:73:69:84:86:a1:04:eb:53:cd:e8:85:b2:39:dd: + 11:4f:66:e2:39:6f:7c:c0:ef:0b:09:1e:e9:89:b1:60:44:51: + 35:20:64:94:2c:54:39:a9:0b:90:b1:32:a0:16:21:d7:97:70: + e3:8b:ab:a9:2f:39:5b:ac:c1:e9:16:c6:e9:e5:e6:8a:92:23: + c2:d6:90:83:48:94:1c:5d:b3:f1:c9:0b:0a:f0:ff:dd:f0:9a: + 42:a2:43:f7:92:1c:de:3d:14:ee:49:92:8d:af:c0:47:63:89: + af:b7:14:39:cc:55:4a:7e:89:77:41:aa:62:f9:cd:d1:f3:11: + ea:83:6f:85:48:5d:31:a8:2d:2b:b5:fd:05:46:cb:bc:ce:53: + a1:7a:b1:03:3b:85:f2:4e:93:61:c7:bd:e1:1e:15:c6:c6:2d: + 19:df:22:57:d1:b7:ff:5b:97:5d:53:f0:ab:78:b2:a6:21:09: + e1:56:2c:bb:ee:44:94:9d:59:29:f7:f4:ed:24:4f:cf:e9:a1: + 80:fb:82:ad +-----BEGIN CERTIFICATE----- +MIICxTCCAa2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxJbnRl +cm1lZGlhdGUwHhcNMTUwMTAxMTIwMDAwWhcNMTYwMTAxMTIwMDAwWjARMQ8wDQYD +VQQDDAZUYXJnZXQwXDANBgkqhkiG9w0BAQEFAANLADBIAkEA1RO7Ur/KGRoGGWgH +HeaHFtPw4BK6orUqPe2zZBYGo1D8sKRJ8vmrNK1P2wo9KyWShj+U3/v8VPLHbZ7S +EODNDQIDAQABo4HpMIHmMB0GA1UdDgQWBBTp2ESNJO6hghhvIfpO7Pvf0ZFXnTAf +BgNVHSMEGDAWgBTMR9j/i2cz+/XN0Ke2EqAZJyfRVzA/BggrBgEFBQcBAQQzMDEw +LwYIKwYBBQUHMAKGI2h0dHA6Ly91cmwtZm9yLWFpYS9JbnRlcm1lZGlhdGUuY2Vy +MDQGA1UdHwQtMCswKaAnoCWGI2h0dHA6Ly91cmwtZm9yLWNybC9JbnRlcm1lZGlh +dGUuY3JsMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB +BQUHAwIwDQYJKoZIhvcNAQELBQADggEBABCH7/wWkXRVXg2zUmZwEgehynzW+KL/ +PAFPi8k3Va/y9TG8iZM7lg8xcraWu7TW2wthoSe3wYDdPUBzaYSGoQTrU83ohbI5 +3RFPZuI5b3zA7wsJHumJsWBEUTUgZJQsVDmpC5CxMqAWIdeXcOOLq6kvOVuswekW +xunl5oqSI8LWkINIlBxds/HJCwrw/93wmkKiQ/eSHN49FO5Jko2vwEdjia+3FDnM +VUp+iXdBqmL5zdHzEeqDb4VIXTGoLSu1/QVGy7zOU6F6sQM7hfJOk2HHveEeFcbG +LRnfIlfRt/9bl11T8Kt4sqYhCeFWLLvuRJSdWSn39O0kT8/poYD7gq0= +-----END CERTIFICATE----- + +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Root + Validity + Not Before: Jan 1 12:00:00 2015 GMT + Not After : Jan 1 12:00:00 2016 GMT + Subject: CN=Intermediate + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:bf:5d:ac:94:a4:6d:90:41:e2:b8:a8:23:44:d3: + 0b:a0:16:0f:a9:d1:86:a6:7d:16:1b:0a:74:78:ef: + 7e:fd:66:74:8d:d7:9e:d4:6b:ec:82:a7:18:59:1b: + a6:36:7d:b3:69:ac:5d:70:74:88:37:49:2a:5b:d2: + eb:db:56:de:54:f7:35:5a:3c:b6:b2:69:23:c5:f6: + 73:95:78:80:8c:62:95:0d:2f:8d:0e:64:e2:66:2a: + 05:7e:fd:70:23:ad:00:39:6d:2d:33:a7:00:af:37: + 74:41:bf:67:49:bd:51:c1:54:c8:d3:1e:87:f1:87: + 13:7a:c0:e4:20:52:15:fc:f1:4c:9e:84:30:72:93: + df:0e:71:94:54:7c:12:4e:4a:31:a6:77:94:8b:98: + 35:02:fa:1f:aa:e8:51:c6:a6:6c:b9:40:3b:2d:a3: + 3f:01:33:7d:5b:60:b3:db:12:cc:1c:a1:7f:94:4b: + 3b:b8:2a:13:a2:da:d4:cb:5a:b7:a9:60:90:19:b5: + d2:83:c0:a2:84:52:44:61:1f:26:ff:3e:e2:43:32: + 15:a4:96:33:34:3b:7c:90:4b:f0:db:7d:e8:f4:34: + 65:60:e1:09:06:5a:1c:8a:89:6c:cc:cc:81:c7:e5: + d5:56:24:d6:6f:a1:2c:c8:9f:77:ab:8b:17:0b:98: + fe:c5 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + CC:47:D8:FF:8B:67:33:FB:F5:CD:D0:A7:B6:12:A0:19:27:27:D1:57 + X509v3 Authority Key Identifier: + keyid:BE:33:72:47:C2:B1:97:41:99:C0:31:57:52:56:0C:B5:53:78:5A:A4 + + Authority Information Access: + CA Issuers - URI:http://url-for-aia/Root.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://url-for-crl/Root.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE + Signature Algorithm: sha256WithRSAEncryption + bc:aa:2c:ff:8e:a8:35:b7:d2:b6:ea:6e:46:71:f0:e5:fb:86: + 87:16:10:c5:af:17:cb:97:22:57:bb:12:44:7f:a4:d8:6f:cd: + 9f:05:eb:fc:eb:fc:fc:fd:01:99:08:6a:43:33:29:46:6e:4e: + 7e:8d:82:98:ad:11:2a:02:d3:61:8f:68:aa:4e:f3:ab:f0:9a: + 3e:45:17:57:3b:26:ec:18:79:3d:a6:85:95:1c:0f:0f:e5:37: + 55:55:a8:e9:c2:a6:9b:5f:e3:f8:17:81:28:c0:f9:a5:28:a6: + 25:6b:d1:23:c4:eb:3d:7d:41:64:61:cc:99:da:1e:5e:02:86: + 4d:3d:66:e6:02:d0:d8:9c:f2:4b:87:24:bd:e1:7d:cb:49:ef: + 29:d9:5e:5e:4e:8f:cf:bc:08:09:e1:30:cd:89:33:0e:a1:89: + 7e:36:04:7c:47:3f:1b:ec:69:66:eb:90:0d:ca:98:ff:4c:1c: + d7:5b:05:61:04:b2:b6:ce:82:77:dc:d4:b4:61:81:79:9a:1d: + 10:e2:49:a6:ca:38:a9:38:44:5e:1b:0e:dd:4f:d2:7d:2f:c8: + ec:03:49:5d:83:bc:a1:31:80:75:ad:98:2b:67:1b:b6:9d:4a: + 33:1d:64:13:5b:b5:0f:ac:56:89:87:1e:57:34:6c:59:b1:48: + c3:e3:6d:cc +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290 +MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowFzEVMBMGA1UEAwwMSW50 +ZXJtZWRpYXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv12slKRt +kEHiuKgjRNMLoBYPqdGGpn0WGwp0eO9+/WZ0jdee1GvsgqcYWRumNn2zaaxdcHSI +N0kqW9Lr21beVPc1Wjy2smkjxfZzlXiAjGKVDS+NDmTiZioFfv1wI60AOW0tM6cA +rzd0Qb9nSb1RwVTI0x6H8YcTesDkIFIV/PFMnoQwcpPfDnGUVHwSTkoxpneUi5g1 +AvofquhRxqZsuUA7LaM/ATN9W2Cz2xLMHKF/lEs7uCoTotrUy1q3qWCQGbXSg8Ci +hFJEYR8m/z7iQzIVpJYzNDt8kEvw233o9DRlYOEJBlociolszMyBx+XVViTWb6Es +yJ93q4sXC5j+xQIDAQABo4HLMIHIMB0GA1UdDgQWBBTMR9j/i2cz+/XN0Ke2EqAZ +JyfRVzAfBgNVHSMEGDAWgBS+M3JHwrGXQZnAMVdSVgy1U3hapDA3BggrBgEFBQcB +AQQrMCkwJwYIKwYBBQUHMAKGG2h0dHA6Ly91cmwtZm9yLWFpYS9Sb290LmNlcjAs +BgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vdXJsLWZvci1jcmwvUm9vdC5jcmwwDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB +ALyqLP+OqDW30rbqbkZx8OX7hocWEMWvF8uXIle7EkR/pNhvzZ8F6/zr/Pz9AZkI +akMzKUZuTn6NgpitESoC02GPaKpO86vwmj5FF1c7JuwYeT2mhZUcDw/lN1VVqOnC +pptf4/gXgSjA+aUopiVr0SPE6z19QWRhzJnaHl4Chk09ZuYC0Nic8kuHJL3hfctJ +7ynZXl5Oj8+8CAnhMM2JMw6hiX42BHxHPxvsaWbrkA3KmP9MHNdbBWEEsrbOgnfc +1LRhgXmaHRDiSabKOKk4RF4bDt1P0n0vyOwDSV2DvKExgHWtmCtnG7adSjMdZBNb +tQ+sVomHHlc0bFmxSMPjbcw= +-----END CERTIFICATE----- + +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=Root + Validity + Not Before: Jan 1 12:00:00 2015 GMT + Not After : Jan 1 12:00:00 2016 GMT + Subject: CN=Root + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:ca:3f:f8:1f:42:8f:55:98:f8:9f:fb:94:03:42: + 2a:c1:42:3a:2b:2a:f3:54:14:f3:fe:67:25:24:d3: + 9a:7f:66:1a:60:0b:9d:d8:bd:65:71:b5:f5:d9:fe: + eb:f6:04:72:57:97:bc:23:b0:be:bd:ce:94:9e:58: + 1a:10:e7:33:09:0b:57:a8:1c:6f:fa:f7:ce:d1:31: + 34:90:1a:b4:60:2d:d2:7f:29:9b:4e:ec:f4:6e:99: + 21:6b:98:9c:90:09:fc:bd:2f:55:c3:34:38:48:4a: + 73:fe:58:e2:09:b9:d9:f9:53:f6:84:e2:5d:fc:eb: + 3c:ba:92:f5:bc:97:cc:ef:43:54:f7:4f:c9:b4:2c: + 86:95:32:a6:e8:91:f5:8e:31:f8:de:b5:d9:c9:3d: + 4d:d7:24:4c:8c:58:aa:8a:c5:79:ab:e7:cd:3b:5c: + 84:67:52:5a:88:33:c3:55:d5:a9:2e:c9:5b:61:7c: + 87:05:c1:0b:d7:19:4a:fe:bd:ba:af:d7:e5:70:d1: + a4:92:08:d2:f2:ca:2b:b1:94:d0:84:57:f9:30:92: + fc:3a:67:82:10:6e:e3:89:9f:b3:df:75:6e:99:46: + bd:ce:b1:e8:ac:a2:3b:21:80:da:11:13:bd:df:93: + 0e:0e:ee:5d:f5:39:a2:a8:f7:41:c8:cb:00:5c:ac: + ee:b1 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + BE:33:72:47:C2:B1:97:41:99:C0:31:57:52:56:0C:B5:53:78:5A:A4 + X509v3 Authority Key Identifier: + keyid:BE:33:72:47:C2:B1:97:41:99:C0:31:57:52:56:0C:B5:53:78:5A:A4 + + Authority Information Access: + CA Issuers - URI:http://url-for-aia/Root.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://url-for-crl/Root.crl + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE + Signature Algorithm: sha256WithRSAEncryption + 6a:f1:5f:9d:b3:dd:07:5a:5c:44:0a:17:df:04:6c:e5:17:03: + a6:ba:c1:85:f3:4f:ff:15:52:85:7c:98:aa:58:ab:39:b2:6d: + ae:71:ff:85:36:de:d6:72:c6:3f:7b:6e:e3:13:32:d5:cd:d8: + 22:c3:48:71:e7:ed:02:97:5a:b0:bd:e7:fd:d4:21:53:66:7e: + 17:df:97:cd:c0:75:18:f3:a8:6a:0c:bc:de:c3:02:36:17:eb: + 99:a4:b7:01:be:89:27:3c:43:9e:d4:e8:24:2a:81:0b:fa:32: + 74:90:53:5f:c1:3c:2e:cf:04:ec:90:5e:f4:20:8e:39:06:49: + ee:8d:69:1e:5f:7f:e0:90:ea:b3:cd:70:42:40:76:22:ec:53: + b4:c7:cd:bf:41:34:92:29:80:97:9a:28:f1:f4:8c:65:a2:74: + f3:79:a5:0a:fa:4f:a7:df:d2:c2:a8:23:9f:51:15:19:2c:40: + fd:67:75:3a:24:8c:5b:9a:71:df:02:92:90:d8:e2:58:22:79: + 44:10:e5:2c:fd:7e:25:6e:e2:42:ec:02:67:44:17:8a:ac:e5: + 9c:b2:0b:d3:22:f5:88:2f:53:e6:e8:a5:43:a4:65:97:a6:36: + f6:57:d3:4b:15:28:55:05:df:52:b5:19:c8:7e:a8:3a:4a:79: + 52:33:b9:52 +-----BEGIN CERTIFICATE----- +MIIDZTCCAk2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARSb290 +MB4XDTE1MDEwMTEyMDAwMFoXDTE2MDEwMTEyMDAwMFowDzENMAsGA1UEAwwEUm9v +dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMo/+B9Cj1WY+J/7lANC +KsFCOisq81QU8/5nJSTTmn9mGmALndi9ZXG19dn+6/YEcleXvCOwvr3OlJ5YGhDn +MwkLV6gcb/r3ztExNJAatGAt0n8pm07s9G6ZIWuYnJAJ/L0vVcM0OEhKc/5Y4gm5 +2flT9oTiXfzrPLqS9byXzO9DVPdPybQshpUypuiR9Y4x+N612ck9TdckTIxYqorF +eavnzTtchGdSWogzw1XVqS7JW2F8hwXBC9cZSv69uq/X5XDRpJII0vLKK7GU0IRX ++TCS/DpnghBu44mfs991bplGvc6x6KyiOyGA2hETvd+TDg7uXfU5oqj3QcjLAFys +7rECAwEAAaOByzCByDAdBgNVHQ4EFgQUvjNyR8Kxl0GZwDFXUlYMtVN4WqQwHwYD +VR0jBBgwFoAUvjNyR8Kxl0GZwDFXUlYMtVN4WqQwNwYIKwYBBQUHAQEEKzApMCcG +CCsGAQUFBzAChhtodHRwOi8vdXJsLWZvci1haWEvUm9vdC5jZXIwLAYDVR0fBCUw +IzAhoB+gHYYbaHR0cDovL3VybC1mb3ItY3JsL1Jvb3QuY3JsMA4GA1UdDwEB/wQE +AwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBq8V+ds90H +WlxEChffBGzlFwOmusGF80//FVKFfJiqWKs5sm2ucf+FNt7WcsY/e27jEzLVzdgi +w0hx5+0Cl1qwvef91CFTZn4X35fNwHUY86hqDLzewwI2F+uZpLcBvoknPEOe1Ogk +KoEL+jJ0kFNfwTwuzwTskF70II45BknujWkeX3/gkOqzzXBCQHYi7FO0x82/QTSS +KYCXmijx9IxlonTzeaUK+k+n39LCqCOfURUZLED9Z3U6JIxbmnHfApKQ2OJYInlE +EOUs/X4lbuJC7AJnRBeKrOWcsgvTIvWIL1Pm6KVDpGWXpjb2V9NLFShVBd9StRnI +fqg6SnlSM7lS +-----END CERTIFICATE-----
diff --git a/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/generate-chains.py b/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/generate-chains.py new file mode 100755 index 0000000..e0dc200 --- /dev/null +++ b/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/generate-chains.py
@@ -0,0 +1,26 @@ +#!/usr/bin/python +# Copyright (c) 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. + +"""Valid certificate chain where the target certificate contains a public key +with a 512-bit modulus (weak).""" + +import sys +sys.path += ['..'] + +import common + +# Self-signed root certificate. +root = common.create_self_signed_root_certificate('Root') + +# Intermediate +intermediate = common.create_intermediate_certificate('Intermediate', root) + +# Target certificate. +target = common.create_end_entity_certificate('Target', intermediate) +target.set_key(common.get_or_generate_rsa_key( + 512, common.create_key_path(target.name))) + +chain = [target, intermediate, root] +common.write_chain(__doc__, chain, 'chain.pem')
diff --git a/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/keys/Intermediate.key b/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/keys/Intermediate.key new file mode 100644 index 0000000..32e8c7a --- /dev/null +++ b/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/keys/Intermediate.key
@@ -0,0 +1,28 @@ +openssl genrsa 2048 +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAv12slKRtkEHiuKgjRNMLoBYPqdGGpn0WGwp0eO9+/WZ0jdee +1GvsgqcYWRumNn2zaaxdcHSIN0kqW9Lr21beVPc1Wjy2smkjxfZzlXiAjGKVDS+N +DmTiZioFfv1wI60AOW0tM6cArzd0Qb9nSb1RwVTI0x6H8YcTesDkIFIV/PFMnoQw +cpPfDnGUVHwSTkoxpneUi5g1AvofquhRxqZsuUA7LaM/ATN9W2Cz2xLMHKF/lEs7 +uCoTotrUy1q3qWCQGbXSg8CihFJEYR8m/z7iQzIVpJYzNDt8kEvw233o9DRlYOEJ +BlociolszMyBx+XVViTWb6EsyJ93q4sXC5j+xQIDAQABAoIBACi3TZjywz0GR67y +V061eKu/BeYj5npV8vYd61ov2t0fh30Ge4zGybOiydNrxpmhdSLuwZLDuJfKwXB4 +GCa6/OMnFfr1IAolxK7CGSWcVf2InB4KGAEQBfumxTSXx9xPWtTdHdj3l3WwXtP+ +XYOa/GIeH/yLanFBRCvCDsexr2v5rO/miB7hynZJ5YFZwtf99iqu8S1ULEE+UZ5c +1C1DhB/BzoUUB/g3DeNLGAxLq6UtsQqZFYL1ZmwVjeecrVIuYjn/LAHnNGkjwabN +tpf5b0HYS18LAH+JP6p2yLz/Ur0EeJ+I+U3MPKg8Q6vJ0u8nWb/W134aa05Uni31 +vGrZQwECgYEA6x0CKeyq0q62AFqiw+IquCk1M7vJXK8GWxgUgkrGBIdF5TCntIlf +26/KJeXPfPpK3sayUY5049d27Oi1IrybAdH7mFf2g1HW3dJMUJjXEnIolpKBFUJj +TOQrA7Q1+YZipbbyCPiQzcSy+bZITyg3e3u9aYT+yPS0JD4HIvIekW0CgYEA0F3B +CKB1lNFVI0O9UsvelyVV12gqBjMm+Vyo4HuWprP/p6uRZ9/6S0QGQ4EqMeQKI88G +67RNeAdjbb0lkIom2g9NbkqS7bHRasUtpyY4rfm6rTNz284fSZnMRMRvJ7+kaH6T +oTJBjUI47gUVoCxX+RQdREnrgcazuZquNZggI7kCgYAsBLxY+RRqaYdtvYpnvjpd +TGnHi8sBbUt1VqbQVguI4YK2jEt5w5aM3Pat7b7RGVNXLkBIgLFlzvtXE6KGJGWp +C5VdSmq+312pHixnkpYBwBnVRwyf3FQXG0jqYp0QYJari/r4rwD9ZWxU0EnteAwb +NGmcDehd22K2vl47rrUGaQKBgGux2YCs9rj5TSjR7TurFZxHdsvEEdxsedtu0fZc +ymvVIvE1kwz+Te6y9Q3U58srkzYY3fnbkiLUpsZkedLwJM6WFC5KKxDh1Fx8F3GK +Jsd9CMhWjK5yJeezr0lnwg/oVICR05oAULNDJAuZ4yiLYtjhVZMjJa9I1UG8OgiH +XS05AoGAFz3V0aPTlD9isEUbz5KYm5t7nr3GpCKy5Ss+hkmQWDCeJb78vgW3P+H1 +OwV7nvVmOSM7AYIngp2q7VsGRx8nmsVwm/KDRnbnPxmPPLsHQS+TKHwbcOG9LWb0 +R0TkmMFIdwJrtBO997T9/gCGDxmw86OwvzeqkbYW7/RtrwJFk8Y= +-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/keys/Root.key b/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/keys/Root.key new file mode 100644 index 0000000..3034b86 --- /dev/null +++ b/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/keys/Root.key
@@ -0,0 +1,28 @@ +openssl genrsa 2048 +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAyj/4H0KPVZj4n/uUA0IqwUI6KyrzVBTz/mclJNOaf2YaYAud +2L1lcbX12f7r9gRyV5e8I7C+vc6UnlgaEOczCQtXqBxv+vfO0TE0kBq0YC3Sfymb +Tuz0bpkha5ickAn8vS9VwzQ4SEpz/ljiCbnZ+VP2hOJd/Os8upL1vJfM70NU90/J +tCyGlTKm6JH1jjH43rXZyT1N1yRMjFiqisV5q+fNO1yEZ1JaiDPDVdWpLslbYXyH +BcEL1xlK/r26r9flcNGkkgjS8sorsZTQhFf5MJL8OmeCEG7jiZ+z33VumUa9zrHo +rKI7IYDaERO935MODu5d9TmiqPdByMsAXKzusQIDAQABAoIBAEiBrGtQimBOifuY +zpRoeTl1i7MEH93p8RsoUTmlnsLDkPsTzw/vvlmIuU3gxSkaqP9cB3foGkmjsMYf +oaCjsjkw1skPANpBUuTONiDfYgEFDGzINsSR0IOB5GhVevNskS4ltSJZK0BHaNQr +e0WvWkS3ZC55lOZiUxA0NWLaLP8tSXdWoaUK3XBwCi+yg5E+enM88Hx69kAY5Xxs +1D3GxPpCPXTT6xVuutA7eaozb7QQuMQro/T279YpFN7DblFs4CxYpGFJHsJ3+y9m +4jj1vDSA5lZvj20x4EhiS40rsF/5pjBq0SZG39Gv9cUwJjgbgwAPRjufv5Hjd4vo +b2X36v0CgYEA+mm1qlKHLEtnDVZK+6gX5gO6O7YF+u/Nj+jzsZ7/2/O0LMDs37kd +jq+8KJz/xsSyU0s+1r+ddY8GuIGHqyGiRVKyIUcA90vd9SrxYcRbbV7G0QZym+F4 +fuJ3jRbFK6fDz9BwkaE7/uOFsckWNiSH7ChSQss8nIQ8AdH4bBjyJ2cCgYEAzsMq +V2Pc9dXYsKQVoCbSvcWqizh0GrOoZUKfytBNnGacS9wU1mfdNknTS3fwLWfaBu7e +mS3MdFQoM0g4kOGEhv09vb1bNiAe+x37cXQvFzKeH9Aj3LTnJQHIbnEBnZGM+VMA +9e2TsS6b/L1L/4+zz6yCecZHpezpQjcslk8A4icCgYBgrHDS6Xt/8Tg+oOLf1twr +E6NRLAuQ/gU5GrECEKUscCBN6slH8bpkfJnCgCIKxaMmnvUKiP0sBmSM1Izg12JD +KxLT4AqSbjqpTMPVf63gQme1CK00Ws5fBeUrle/W07S3xPvAbSOxWnsh0MT/cAj9 +de+UE8w5jJ9yAHLMoLDT3wKBgDOUUlK8sdmOAGGIfXCXXslCr1nNuoESwnaIWU6C +Cmpy2pi+DWCzRmcNoa1Y/UyGdMh3/IXf+/olKGYqpRnXeHUoZaeYvlFRUAk7IIfc +AQdbdEDhbqDXbDY6LKMIg+un7LAh+cJgAxEXXIh/PJ9DXQr4sQ/p2+PTpxkCpJfW +m5TPAoGAYIdAPSVITlwvEJZHOy0TJ5/1f8oAcNp16hhhfmxN8NCad5JaW8G5QSDG +ck7RvwV6Zt3z5LaXYCyz8JbMxTGnCMdf5xKrRkTN+i515BGBYQqbkNqrdIdYKzmF +wDHaQFVtWi7cH1j6fygW9luZt3Jh0+euPEewMmLzZ1AglSYDwUQ= +-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/keys/Target.key b/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/keys/Target.key new file mode 100644 index 0000000..5f4989f7 --- /dev/null +++ b/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/keys/Target.key
@@ -0,0 +1,10 @@ +openssl genrsa 512 +-----BEGIN RSA PRIVATE KEY----- +MIIBOQIBAAJBANUTu1K/yhkaBhloBx3mhxbT8OASuqK1Kj3ts2QWBqNQ/LCkSfL5 +qzStT9sKPSslkoY/lN/7/FTyx22e0hDgzQ0CAwEAAQJAbZBW20b5QY0LI9dFCY/3 +WLqkemPHClFDplJq0wUsZp8XCIDjkx8RzNBOjN1i4WV25z5q5lOGeYopYgE4g5AQ +QQIhAO0ZkvzLX7A08d5guXp7AZYIbX3z43fzBaaAPkgRaC9RAiEA5g/uXR1+NxrK +a88zei/oADF6tixOoTI41Hkg3CCK6v0CIE6GPskcbfeEwWod7K/k1zSiW+jwAjDy +urdXF8l0gmXRAiBNtcnlKAYvJNyFCAsyVaY/EneJu3Of3W/2zSd9U3y5HQIgXa7O +cDEGpXaGf862y0kEzrHPnG8morJkL6zjUOtI6ZA= +-----END RSA PRIVATE KEY-----
diff --git a/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/main.test b/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/main.test new file mode 100644 index 0000000..4a21535 --- /dev/null +++ b/net/data/verify_certificate_chain_unittest/target-has-512bit-rsa-key/main.test
@@ -0,0 +1,11 @@ +chain: chain.pem +last_cert_trust: TRUSTED_ANCHOR +utc_time: 150302120000Z +key_purpose: SERVER_AUTH +expected_errors: +----- Certificate i=0 (CN=Target) ----- +ERROR: RSA modulus too small + actual: 512 + minimum: 1024 +ERROR: Unacceptable public key +
diff --git a/net/data/verify_certificate_chain_unittest/target-signed-by-512bit-rsa/main.test b/net/data/verify_certificate_chain_unittest/target-signed-by-512bit-rsa/main.test index d30b49c..edefccab 100644 --- a/net/data/verify_certificate_chain_unittest/target-signed-by-512bit-rsa/main.test +++ b/net/data/verify_certificate_chain_unittest/target-signed-by-512bit-rsa/main.test
@@ -3,10 +3,9 @@ utc_time: 150302120000Z key_purpose: SERVER_AUTH expected_errors: ------ Certificate i=0 (CN=Target) ----- +----- Certificate i=1 (CN=Intermediate) ----- ERROR: RSA modulus too small actual: 512 minimum: 1024 -ERROR: Unacceptable modulus length for RSA key -ERROR: VerifySignedData failed +ERROR: Unacceptable public key
diff --git a/net/data/verify_certificate_chain_unittest/target-wrong-signature/main.test b/net/data/verify_certificate_chain_unittest/target-wrong-signature/main.test index b4423e8e..244eef1 100644 --- a/net/data/verify_certificate_chain_unittest/target-wrong-signature/main.test +++ b/net/data/verify_certificate_chain_unittest/target-wrong-signature/main.test
@@ -4,6 +4,5 @@ key_purpose: SERVER_AUTH expected_errors: ----- Certificate i=0 (CN=Target) ----- -ERROR: Signature verification failed ERROR: VerifySignedData failed
diff --git a/net/tools/cert_verify_tool/verify_using_path_builder.cc b/net/tools/cert_verify_tool/verify_using_path_builder.cc index 7a85816..9c3d55e 100644 --- a/net/tools/cert_verify_tool/verify_using_path_builder.cc +++ b/net/tools/cert_verify_tool/verify_using_path_builder.cc
@@ -19,7 +19,7 @@ #include "net/cert/internal/parse_name.h" #include "net/cert/internal/parsed_certificate.h" #include "net/cert/internal/path_builder.h" -#include "net/cert/internal/signature_policy.h" +#include "net/cert/internal/simple_path_builder_delegate.h" #include "net/cert/internal/system_trust_store.h" #include "net/cert/x509_util.h" #include "net/cert_net/cert_net_fetcher_impl.h" @@ -206,10 +206,10 @@ return false; // Verify the chain. - net::SimpleSignaturePolicy signature_policy(2048); + net::SimplePathBuilderDelegate delegate(2048); net::CertPathBuilder::Result result; net::CertPathBuilder path_builder( - target_cert, ssl_trust_store->GetTrustStore(), &signature_policy, time, + target_cert, ssl_trust_store->GetTrustStore(), &delegate, time, net::KeyPurpose::SERVER_AUTH, net::InitialExplicitPolicy::kFalse, {net::AnyPolicy()}, net::InitialPolicyMappingInhibit::kFalse, net::InitialAnyPolicyInhibit::kFalse, &result);
diff --git a/testing/buildbot/filters/fuchsia.base_unittests.filter b/testing/buildbot/filters/fuchsia.base_unittests.filter index 8a0419139..484b815d 100644 --- a/testing/buildbot/filters/fuchsia.base_unittests.filter +++ b/testing/buildbot/filters/fuchsia.base_unittests.filter
@@ -74,13 +74,11 @@ -ProcessTest.WaitForExitWithTimeout -ProcessUtilTest.DelayedTermination -ProcessUtilTest.FDRemapping --ProcessUtilTest.GetAppOutputWithExitCode -ProcessUtilTest.GetTerminationStatusCrash -ProcessUtilTest.GetTerminationStatusSigKill -ProcessUtilTest.GetTerminationStatusSigTerm -ProcessUtilTest.ImmediateTermination -ProcessUtilTest.KillSlowChild --ProcessUtilTest.LaunchProcess -ProcessUtilTest.PreExecHook -ProcessUtilTest.SpawnChild -SysInfoTest.AmountOfFreeDiskSpace
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites index 07361018..ae16e63 100644 --- a/third_party/WebKit/LayoutTests/VirtualTestSuites +++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -113,6 +113,11 @@ "args": ["--stable-release-mode"] }, { + "prefix": "feature-policy-permissions", + "base": "http/tests/webmidi", + "args": ["--enable-features=UseFeaturePolicyForPermissions"] + }, + { "prefix": "off-main-thread-fetch", "base": "external/wpt/html/browsers/offline/appcache/workers", "args": ["--enable-features=OffMainThreadFetch"]
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/console-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/console-test.js index 318a4834..5fc47fb 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector/console-test.js +++ b/third_party/WebKit/LayoutTests/http/tests/inspector/console-test.js
@@ -229,8 +229,10 @@ InspectorTest.dumpConsoleClassesBrief = function() { var messageViews = Console.ConsoleView.instance()._visibleViewMessages; - for (var i = 0; i < messageViews.length; ++i) - InspectorTest.addResult(messageViews[i].toMessageElement().className); + for (var i = 0; i < messageViews.length; ++i) { + var repeatText = messageViews[i].repeatCount() > 1 ? (' x' + messageViews[i].repeatCount()) : ''; + InspectorTest.addResult(messageViews[i].toMessageElement().className + repeatText); + } } InspectorTest.dumpConsoleCounters = function()
diff --git a/third_party/WebKit/LayoutTests/http/tests/webmidi/midi-default-feature-policy.https.sub-expected.txt b/third_party/WebKit/LayoutTests/http/tests/webmidi/midi-default-feature-policy.https.sub-expected.txt new file mode 100644 index 0000000..2f20afdd --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/webmidi/midi-default-feature-policy.https.sub-expected.txt
@@ -0,0 +1,8 @@ +CONSOLE WARNING: line 13: requestMIDIAccess usage in cross-origin iframes is deprecated and will be disabled in M63, around December 2017. To continue to use this feature, it must be enabled by the embedding document using Feature Policy, e.g. <iframe allow="midi" ...>. See https://goo.gl/EuHzyv for more details. +This is a testharness.js-based test. +PASS Default "midi" feature policy ["self"] allows the top-level document. +PASS Default "midi" feature policy ["self"] allows same-origin iframes. +FAIL Default "midi" feature policy ["self"] disallows cross-origin iframes. assert_equals: expected "#SecurityError" but got "#OK" +PASS Feature policy "midi" can be enabled in cross-origin iframes using "allow" attribute. +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/LayoutTests/http/tests/webmidi/midi-default-feature-policy.https.sub.html b/third_party/WebKit/LayoutTests/http/tests/webmidi/midi-default-feature-policy.https.sub.html new file mode 100644 index 0000000..8994bad4 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/webmidi/midi-default-feature-policy.https.sub.html
@@ -0,0 +1,16 @@ +<!DOCTYPE html> +<body> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=resources/feature-policy-permissions-test.js></script> +<script> +'use strict'; + +run_permission_default_header_policy_tests( + location.protocol + '//localhost:' + location.port, + 'midi', + 'SecurityError', + function() { return navigator.requestMIDIAccess(); }); + +</script> +</body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/webmidi/resources/feature-policy-permissions-test.js b/third_party/WebKit/LayoutTests/http/tests/webmidi/resources/feature-policy-permissions-test.js new file mode 100644 index 0000000..fba857a3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/webmidi/resources/feature-policy-permissions-test.js
@@ -0,0 +1,78 @@ +function grant_permission(feature_name, url) { + if (window.testRunner) { + window.testRunner.setPermission( + feature_name, 'granted', url, location.origin); + } +} + +function assert_available_in_iframe( + feature_name, test, location, expected, allow_attributes) { + const frame = document.createElement('iframe'); + if (allow_attributes) + frame.allow = allow_attributes; + frame.src = location; + + grant_permission(feature_name, frame.src); + + window.addEventListener('message', test.step_func(evt => { + if (evt.source == frame.contentWindow) { + assert_equals(evt.data, expected); + document.body.removeChild(frame); + test.done(); + } + })); + + document.body.appendChild(frame); +} + +function run_permission_default_header_policy_tests( + cross_origin, feature_name, error_name, feature_promise_factory) { + // This may be the version of the page loaded up in an iframe. If so, just + // post the result of running the feature promise back to the parent. + if (location.hash == '#iframe') { + feature_promise_factory().then( + () => window.parent.postMessage('#OK', '*'), + error => window.parent.postMessage('#' + error.name, '*')); + return; + } + + grant_permission(feature_name, location.href); + + // Run the various tests. + // 1. Top level frame. + promise_test( + () => feature_promise_factory(), + 'Default "' + feature_name + + '" feature policy ["self"] allows the top-level document.'); + + // 2. Same-origin iframe. + // Append #iframe to the URL so we can detect the iframe'd version of the + // page. + const same_origin_frame_pathname = location.pathname + '#iframe'; + async_test( + t => { + assert_available_in_iframe( + feature_name, t, same_origin_frame_pathname, '#OK'); + }, + 'Default "' + feature_name + + '" feature policy ["self"] allows same-origin iframes.'); + + // 3. Cross-origin iframe. + const cross_origin_frame_url = cross_origin + same_origin_frame_pathname; + async_test( + t => { + assert_available_in_iframe( + feature_name, t, cross_origin_frame_url, '#' + error_name); + }, + 'Default "' + feature_name + + '" feature policy ["self"] disallows cross-origin iframes.'); + + // 4. Cross-origin iframe with "allow" attribute. + async_test( + t => { + assert_available_in_iframe( + feature_name, t, cross_origin_frame_url, '#OK', feature_name); + }, + 'Feature policy "' + feature_name + + '" can be enabled in cross-origin iframes using "allow" attribute.'); +} \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-repeat-count-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/console-repeat-count-expected.txt index 15d855bf..8e5698f 100644 --- a/third_party/WebKit/LayoutTests/inspector/console/console-repeat-count-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/console/console-repeat-count-expected.txt
@@ -1,17 +1,39 @@ -CONSOLE MESSAGE: line 10: Same message -CONSOLE MESSAGE: line 10: Same message -CONSOLE MESSAGE: line 10: Same message -CONSOLE MESSAGE: line 10: Same message -CONSOLE MESSAGE: line 10: Same message -CONSOLE MESSAGE: line 10: Same message -CONSOLE MESSAGE: line 10: Same message -CONSOLE MESSAGE: line 10: Same message -CONSOLE MESSAGE: line 10: Same message -CONSOLE MESSAGE: line 10: Same message +CONSOLE MESSAGE: line 10: Message +CONSOLE MESSAGE: line 10: Message +CONSOLE MESSAGE: line 13: Error: Message with error +CONSOLE MESSAGE: line 13: Error: Message with error +CONSOLE ERROR: line 16: [object Object] +CONSOLE ERROR: line 16: [object Object] +CONSOLE ERROR: line 26: Uncaught Primitive value +CONSOLE ERROR: line 26: Uncaught Primitive value +CONSOLE ERROR: line 21: Uncaught [object Object] +CONSOLE ERROR: line 21: Uncaught [object Object] Tests that repeat count is properly updated. -5console-repeat-count.html:6 Same message -"Different message" -"Different message" -5console-repeat-count.html:6 Same message +Message count: 7 +2console-repeat-count.html:6 Message +2console-repeat-count.html:9 Error: Message with error + at dumpMessages (console-repeat-count.html:9) + at <anonymous>:1:1 +console-repeat-count.html:12 {a: 1} +dumpMessages @ console-repeat-count.html:12 +(anonymous) @ VM:1 +console-repeat-count.html:12 {a: 1} +dumpMessages @ console-repeat-count.html:12 +(anonymous) @ VM:1 +2console-repeat-count.html:22 Uncaught Primitive value +setTimeout @ console-repeat-count.html:22 +setTimeout (async) +throwPrimitiveValues @ console-repeat-count.html:22 +(anonymous) @ VM:1 +console-repeat-count.html:17 Uncaught {a: 1} +setTimeout @ console-repeat-count.html:17 +setTimeout (async) +throwObjects @ console-repeat-count.html:17 +(anonymous) @ VM:1 +console-repeat-count.html:17 Uncaught {a: 1} +setTimeout @ console-repeat-count.html:17 +setTimeout (async) +throwObjects @ console-repeat-count.html:17 +(anonymous) @ VM:1
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-repeat-count.html b/third_party/WebKit/LayoutTests/inspector/console/console-repeat-count.html index a12207e7..c8d33ee9 100644 --- a/third_party/WebKit/LayoutTests/inspector/console/console-repeat-count.html +++ b/third_party/WebKit/LayoutTests/inspector/console/console-repeat-count.html
@@ -6,32 +6,38 @@ function dumpMessages() { - for (var i = 0; i < 5; ++i) - console.log("Same message"); + for (var i = 0; i < 2; ++i) + console.log("Message"); + + for (var i = 0; i < 2; ++i) + console.log(new Error("Message with error")); + + for (var i = 0; i < 2; ++i) + console.error({a: 1}); +} + +function throwObjects() { + for (var i = 0; i < 2; ++i) + setTimeout(() => { throw {a: 1}; }, 0); +} + +function throwPrimitiveValues() { + for (var i = 0; i < 2; ++i) + setTimeout(() => { throw "Primitive value"; }, 0); } //# sourceURL=console-repeat-count.html </script> <script> -function test() +async function test() { - InspectorTest.evaluateInPage("dumpMessages()", step1); - - function step1() - { - InspectorTest.evaluateInConsole("\"Different message\"", step2); - } - - function step2() - { - InspectorTest.evaluateInPage("dumpMessages()", step3); - } - - function step3() - { + await InspectorTest.evaluateInPagePromise("dumpMessages()"); + await InspectorTest.evaluateInPagePromise("throwPrimitiveValues()"); + await InspectorTest.evaluateInPagePromise("throwObjects()"); + InspectorTest.waitForConsoleMessages(7, () => { InspectorTest.dumpConsoleMessages(); InspectorTest.completeTest(); - } + }); } </script> </head>
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-expected.txt index 7f01e08..df548568 100644 --- a/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-expected.txt
@@ -9,9 +9,7 @@ Message added: error log 3 errors -console-message-wrapper console-error-level -console-message-wrapper console-error-level -console-message-wrapper console-error-level +console-message-wrapper console-error-level x3 Handling promise 1 error
diff --git a/third_party/WebKit/LayoutTests/virtual/feature-policy-permissions/http/tests/webmidi/README.txt b/third_party/WebKit/LayoutTests/virtual/feature-policy-permissions/http/tests/webmidi/README.txt new file mode 100644 index 0000000..72baed6 --- /dev/null +++ b/third_party/WebKit/LayoutTests/virtual/feature-policy-permissions/http/tests/webmidi/README.txt
@@ -0,0 +1 @@ +# These tests run with --enable-features=UseFeaturePolicyForPermissions
diff --git a/third_party/WebKit/LayoutTests/virtual/feature-policy-permissions/http/tests/webmidi/midi-default-feature-policy.https.sub-expected.txt b/third_party/WebKit/LayoutTests/virtual/feature-policy-permissions/http/tests/webmidi/midi-default-feature-policy.https.sub-expected.txt new file mode 100644 index 0000000..c0ac4537 --- /dev/null +++ b/third_party/WebKit/LayoutTests/virtual/feature-policy-permissions/http/tests/webmidi/midi-default-feature-policy.https.sub-expected.txt
@@ -0,0 +1,8 @@ +CONSOLE WARNING: line 13: requestMIDIAccess usage in cross-origin iframes is deprecated and will be disabled in M63, around December 2017. To continue to use this feature, it must be enabled by the embedding document using Feature Policy, e.g. <iframe allow="midi" ...>. See https://goo.gl/EuHzyv for more details. +This is a testharness.js-based test. +PASS Default "midi" feature policy ["self"] allows the top-level document. +PASS Default "midi" feature policy ["self"] allows same-origin iframes. +PASS Default "midi" feature policy ["self"] disallows cross-origin iframes. +PASS Feature policy "midi" can be enabled in cross-origin iframes using "allow" attribute. +Harness: the test ran to completion. +
diff --git a/third_party/WebKit/Source/build/scripts/keyword_utils.py b/third_party/WebKit/Source/build/scripts/keyword_utils.py new file mode 100644 index 0000000..2703425d --- /dev/null +++ b/third_party/WebKit/Source/build/scripts/keyword_utils.py
@@ -0,0 +1,25 @@ +import json5_generator + + +def sort_keyword_properties_by_canonical_order(css_properties, css_value_keywords_file, json5_file_parameters): + """Sort all keyword CSS properties by the order of the keyword in CSSValueKeywords.json5 + + Args: + css_properties: css_properties excluding extra fields. + css_value_keywords_file: file containing all css keywords. + json5_file_parameters: current context json5 parameters. + + Returns: + New css_properties object with sorted keywords. + """ + css_values_dictionary = json5_generator.Json5File.load_from_files( + [css_value_keywords_file], + default_parameters=json5_file_parameters + ).name_dictionaries + css_values_dictionary = [x['name'] for x in css_values_dictionary] + name_to_position_dictionary = dict(zip(css_values_dictionary, range(len(css_values_dictionary)))) + for css_property in css_properties: + if css_property['field_template'] == 'keyword' and len(css_property['include_paths']) == 0: + css_property['keywords'] = sorted(css_property['keywords'], key=lambda x: name_to_position_dictionary[x]) + + return css_properties
diff --git a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py index 8eb8aa59..e0e28435 100755 --- a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py +++ b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
@@ -8,6 +8,7 @@ import json5_generator import template_expander import make_style_builder +import keyword_utils from name_utilities import ( enum_for_css_keyword, enum_type_name, enum_value_name, class_member_name, method_name, @@ -307,7 +308,6 @@ if property_['field_template'] in ('keyword', 'multi_keyword') and len(property_['include_paths']) == 0: enum = Enum(property_['type_name'], property_['keywords'], is_set=(property_['field_template'] == 'multi_keyword')) - if property_['field_template'] == 'multi_keyword': assert property_['keywords'][0] == 'none', \ "First keyword in a 'multi_keyword' field must be 'none' in '{}'.".format(property_['name']) @@ -318,8 +318,8 @@ ("'" + property_['name'] + "' can't have type_name '" + enum.type_name + "' " "because it was used by a previous property, but with a different set of keywords. " "Either give it a different name or ensure the keywords are the same.") - - enums[enum.type_name] = enum + else: + enums[enum.type_name] = enum # Return the enums sorted by type name return list(sorted(enums.values(), key=lambda e: e.type_name)) @@ -514,6 +514,15 @@ "Shorthand '{}' cannot have a field_template.".format(property_['name']) css_properties = [value for value in self._properties.values() if not value['longhands']] + # We sort the enum values based on each value's position in + # the keywords as listed in CSSProperties.json5. This will ensure that if there is a continuous + # segment in CSSProperties.json5 matching the segment in this enum then + # the generated enum will have the same order and continuity as + # CSSProperties.json5 and we can get the longest continuous segment. + # Thereby reduce the switch case statement to the minimum. + css_properties = keyword_utils.sort_keyword_properties_by_canonical_order(css_properties, + json5_file_paths[3], + self.json5_file.parameters) for property_ in css_properties: # Set default values for extra parameters in ComputedStyleExtraFields.json5.
diff --git a/third_party/WebKit/Source/build/scripts/make_css_value_id_mappings.py b/third_party/WebKit/Source/build/scripts/make_css_value_id_mappings.py index a196dc1..24597236 100755 --- a/third_party/WebKit/Source/build/scripts/make_css_value_id_mappings.py +++ b/third_party/WebKit/Source/build/scripts/make_css_value_id_mappings.py
@@ -6,7 +6,7 @@ import json5_generator import template_expander import make_style_builder - +import keyword_utils from name_utilities import enum_for_css_keyword, enum_value_name @@ -103,6 +103,17 @@ 'CSSValueIDMappingsGenerated.h': self.generate_css_value_mappings, } self.css_values_dictionary_file = json5_file_path[1] + css_properties = [value for value in self._properties.values() if not value['longhands']] + # We sort the enum values based on each value's position in + # the keywords as listed in CSSProperties.json5. This will ensure that if there is a continuous + # segment in CSSProperties.json5 matching the segment in this enum then + # the generated enum will have the same order and continuity as + # CSSProperties.json5 and we can get the longest continuous segment. + # Thereby reduce the switch case statement to the minimum. + css_properties = keyword_utils.sort_keyword_properties_by_canonical_order(css_properties, + json5_file_path[1], + self.json5_file.parameters) + @template_expander.use_jinja('templates/CSSValueIDMappingsGenerated.h.tmpl') def generate_css_value_mappings(self):
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn index ca69225..d696d05 100644 --- a/third_party/WebKit/Source/core/BUILD.gn +++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -423,6 +423,7 @@ in_files = [ "css/ComputedStyleExtraFields.json5", "css/ComputedStyleDiffFunctions.json5", + "css/CSSValueKeywords.json5", ] other_inputs = [ "../build/scripts/templates/fields/field.tmpl", @@ -436,6 +437,7 @@ "../build/scripts/templates/ComputedStyleBase.h.tmpl", "../build/scripts/templates/ComputedStyleBase.cpp.tmpl", "../build/scripts/templates/ComputedStyleBaseConstants.h.tmpl", + "../build/scripts/keyword_utils.py", ] outputs = [ "$blink_core_output_dir/ComputedStyleBase.h", @@ -446,9 +448,13 @@ css_properties("make_core_generated_css_value_id_mappings") { script = "../build/scripts/make_css_value_id_mappings.py" - in_files = [ "css/CSSValueKeywords.json5" ] - other_inputs = - [ "../build/scripts/templates/CSSValueIDMappingsGenerated.h.tmpl" ] + in_files = [ + "css/CSSValueKeywords.json5" + ] + other_inputs = [ + "../build/scripts/templates/CSSValueIDMappingsGenerated.h.tmpl", + "../build/scripts/keyword_utils.py", + ] outputs = [ "$blink_core_output_dir/CSSValueIDMappingsGenerated.h", ]
diff --git a/third_party/WebKit/Source/core/editing/BUILD.gn b/third_party/WebKit/Source/core/editing/BUILD.gn index 6293673..c05459f 100644 --- a/third_party/WebKit/Source/core/editing/BUILD.gn +++ b/third_party/WebKit/Source/core/editing/BUILD.gn
@@ -281,6 +281,10 @@ "state_machines/StateMachineUtil.h", "state_machines/TextSegmentationMachineState.cpp", "state_machines/TextSegmentationMachineState.h", + "suggestion/TextSuggestionBackendImpl.cpp", + "suggestion/TextSuggestionBackendImpl.h", + "suggestion/TextSuggestionController.cpp", + "suggestion/TextSuggestionController.h", ] if (is_mac) { @@ -362,6 +366,7 @@ "state_machines/StateMachineTestUtil.cpp", "state_machines/StateMachineTestUtil.h", "state_machines/StateMachineUtilTest.cpp", + "suggestion/TextSuggestionControllerTest.cpp", ] configs += [
diff --git a/third_party/WebKit/Source/core/editing/Editor.cpp b/third_party/WebKit/Source/core/editing/Editor.cpp index cf15578..a7204284 100644 --- a/third_party/WebKit/Source/core/editing/Editor.cpp +++ b/third_party/WebKit/Source/core/editing/Editor.cpp
@@ -1415,15 +1415,20 @@ GetFrame().Selection().RevealSelection(alignment, reveal_extent_option); } -void Editor::Transpose() { - if (!CanEdit()) +// TODO(yosin): We should move |Transpose()| into |ExecuteTranspose()| in +// "EditorCommand.cpp" +void Transpose(LocalFrame& frame) { + Editor& editor = frame.GetEditor(); + if (!editor.CanEdit()) return; + Document* const document = frame.GetDocument(); + // TODO(editing-dev): The use of updateStyleAndLayoutIgnorePendingStylesheets // needs to be audited. see http://crbug.com/590369 for more details. - GetFrame().GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets(); + document->UpdateStyleAndLayoutIgnorePendingStylesheets(); - const EphemeralRange& range = ComputeRangeForTranspose(GetFrame()); + const EphemeralRange& range = ComputeRangeForTranspose(frame); if (range.IsNull()) return; @@ -1434,23 +1439,23 @@ const String& transposed = text.Right(1) + text.Left(1); if (DispatchBeforeInputInsertText( - EventTargetNodeForDocument(GetFrame().GetDocument()), transposed, + EventTargetNodeForDocument(document), transposed, InputEvent::InputType::kInsertTranspose, new StaticRangeVector(1, StaticRange::Create(range))) != DispatchEventResult::kNotCanceled) return; - // 'beforeinput' event handler may destroy document. - if (frame_->GetDocument()->GetFrame() != frame_) + // 'beforeinput' event handler may destroy document-> + if (frame.GetDocument() != document) return; // TODO(editing-dev): The use of updateStyleAndLayoutIgnorePendingStylesheets // needs to be audited. see http://crbug.com/590369 for more details. - GetFrame().GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets(); + document->UpdateStyleAndLayoutIgnorePendingStylesheets(); // 'beforeinput' event handler may change selection, we need to re-calculate // range. - const EphemeralRange& new_range = ComputeRangeForTranspose(GetFrame()); + const EphemeralRange& new_range = ComputeRangeForTranspose(frame); if (new_range.IsNull()) return; @@ -1464,12 +1469,12 @@ // Select the two characters. if (CreateVisibleSelection(new_selection) != - GetFrame().Selection().ComputeVisibleSelectionInDOMTree()) - GetFrame().Selection().SetSelection(new_selection); + frame.Selection().ComputeVisibleSelectionInDOMTree()) + frame.Selection().SetSelection(new_selection); // Insert the transposed characters. - ReplaceSelectionWithText(new_transposed, false, false, - InputEvent::InputType::kInsertTranspose); + editor.ReplaceSelectionWithText(new_transposed, false, false, + InputEvent::InputType::kInsertTranspose); } void Editor::AddToKillRing(const EphemeralRange& range) {
diff --git a/third_party/WebKit/Source/core/editing/Editor.h b/third_party/WebKit/Source/core/editing/Editor.h index c4284eb..94e15e8 100644 --- a/third_party/WebKit/Source/core/editing/Editor.h +++ b/third_party/WebKit/Source/core/editing/Editor.h
@@ -105,8 +105,6 @@ static void CountEvent(ExecutionContext*, const Event*); void CopyImage(const HitTestResult&); - void Transpose(); - void RespondToChangedContents(const Position&); bool SelectionStartHasStyle(CSSPropertyID, const String& value) const; @@ -392,6 +390,10 @@ typing_style_ = style; } +// TODO(yosin): We should move |Transpose()| into |ExecuteTranspose()| in +// "EditorCommand.cpp" +void Transpose(LocalFrame&); + } // namespace blink #endif // Editor_h
diff --git a/third_party/WebKit/Source/core/editing/SelectionController.cpp b/third_party/WebKit/Source/core/editing/SelectionController.cpp index f7cde35a..b74a0bb21 100644 --- a/third_party/WebKit/Source/core/editing/SelectionController.cpp +++ b/third_party/WebKit/Source/core/editing/SelectionController.cpp
@@ -38,6 +38,7 @@ #include "core/editing/VisibleSelection.h" #include "core/editing/iterators/TextIterator.h" #include "core/editing/markers/DocumentMarkerController.h" +#include "core/editing/suggestion/TextSuggestionController.h" #include "core/events/Event.h" #include "core/frame/LocalFrame.h" #include "core/frame/LocalFrameView.h" @@ -343,7 +344,8 @@ } bool is_handle_visible = false; - if (HasEditableStyle(*inner_node)) { + const bool has_editable_style = HasEditableStyle(*inner_node); + if (has_editable_style) { const bool is_text_box_empty = CreateVisibleSelection(SelectionInFlatTree::Builder() .SelectAllChildren(*inner_node) @@ -365,6 +367,12 @@ TextGranularity::kCharacter, is_handle_visible ? HandleVisibility::kVisible : HandleVisibility::kNotVisible); + + if (has_editable_style && event.Event().FromTouch()) { + frame_->GetTextSuggestionController().HandlePotentialMisspelledWordTap( + visible_pos.DeepEquivalent()); + } + return false; }
diff --git a/third_party/WebKit/Source/core/editing/SelectionModifier.cpp b/third_party/WebKit/Source/core/editing/SelectionModifier.cpp index caee47f..6ea84b75 100644 --- a/third_party/WebKit/Source/core/editing/SelectionModifier.cpp +++ b/third_party/WebKit/Source/core/editing/SelectionModifier.cpp
@@ -418,52 +418,51 @@ return AdjustForwardPositionForUserSelectAll(pos); } -VisiblePosition SelectionModifier::ModifyExtendingBackward( +VisiblePosition SelectionModifier::ModifyExtendingBackwardInternal( TextGranularity granularity) { - VisiblePosition pos = - CreateVisiblePosition(selection_.Extent(), selection_.Affinity()); - // Extending a selection backward by word or character from just after a table // selects the table. This "makes sense" from the user perspective, esp. when // deleting. It was done here instead of in VisiblePosition because we want // VPs to iterate over everything. switch (granularity) { case TextGranularity::kCharacter: - pos = PreviousPositionOf(pos, kCanSkipOverEditingBoundary); - break; + return PreviousPositionOf( + CreateVisiblePosition(selection_.Extent(), selection_.Affinity()), + kCanSkipOverEditingBoundary); case TextGranularity::kWord: - pos = PreviousWordPosition(pos); - break; + return PreviousWordPosition( + CreateVisiblePosition(selection_.Extent(), selection_.Affinity())); case TextGranularity::kSentence: - pos = PreviousSentencePosition(pos); - break; + return PreviousSentencePosition( + CreateVisiblePosition(selection_.Extent(), selection_.Affinity())); case TextGranularity::kLine: - pos = PreviousLinePosition( - pos, + return PreviousLinePosition( + CreateVisiblePosition(selection_.Extent(), selection_.Affinity()), LineDirectionPointForBlockDirectionNavigation(selection_.Extent())); - break; case TextGranularity::kParagraph: - pos = PreviousParagraphPosition( - pos, + return PreviousParagraphPosition( + CreateVisiblePosition(selection_.Extent(), selection_.Affinity()), LineDirectionPointForBlockDirectionNavigation(selection_.Extent())); - break; case TextGranularity::kSentenceBoundary: - pos = StartOfSentence(StartForPlatform()); - break; + return StartOfSentence(StartForPlatform()); case TextGranularity::kLineBoundary: - pos = LogicalStartOfLine(StartForPlatform()); - break; + return LogicalStartOfLine(StartForPlatform()); case TextGranularity::kParagraphBoundary: - pos = StartOfParagraph(StartForPlatform()); - break; - case TextGranularity::kDocumentBoundary: - pos = StartForPlatform(); + return StartOfParagraph(StartForPlatform()); + case TextGranularity::kDocumentBoundary: { + const VisiblePosition pos = StartForPlatform(); if (IsEditablePosition(pos.DeepEquivalent())) - pos = StartOfEditableContent(pos); - else - pos = StartOfDocument(pos); - break; + return StartOfEditableContent(pos); + return StartOfDocument(pos); + } } + NOTREACHED() << static_cast<int>(granularity); + return VisiblePosition(); +} + +VisiblePosition SelectionModifier::ModifyExtendingBackward( + TextGranularity granularity) { + const VisiblePosition pos = ModifyExtendingBackwardInternal(granularity); if (DirectionOfEnclosingBlock() == TextDirection::kLtr) return AdjustBackwardPositionForUserSelectAll(pos); return AdjustForwardPositionForUserSelectAll(pos);
diff --git a/third_party/WebKit/Source/core/editing/SelectionModifier.h b/third_party/WebKit/Source/core/editing/SelectionModifier.h index 82b43eb..f7b1f66c 100644 --- a/third_party/WebKit/Source/core/editing/SelectionModifier.h +++ b/third_party/WebKit/Source/core/editing/SelectionModifier.h
@@ -79,6 +79,7 @@ VisiblePosition ModifyMovingForward(TextGranularity); VisiblePosition ModifyExtendingLeft(TextGranularity); VisiblePosition ModifyExtendingBackward(TextGranularity); + VisiblePosition ModifyExtendingBackwardInternal(TextGranularity); VisiblePosition ModifyMovingLeft(TextGranularity); VisiblePosition ModifyMovingBackward(TextGranularity); VisiblePosition NextWordPositionForPlatform(const VisiblePosition&);
diff --git a/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp b/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp index 71fd901..d59e5de1 100644 --- a/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp +++ b/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp
@@ -1954,7 +1954,7 @@ Event*, EditorCommandSource, const String&) { - frame.GetEditor().Transpose(); + Transpose(frame); return true; }
diff --git a/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionBackendImpl.cpp b/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionBackendImpl.cpp new file mode 100644 index 0000000..3ac89240 --- /dev/null +++ b/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionBackendImpl.cpp
@@ -0,0 +1,47 @@ +// 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 "core/editing/suggestion/TextSuggestionBackendImpl.h" + +#include "core/editing/suggestion/TextSuggestionController.h" +#include "core/frame/LocalFrame.h" +#include "mojo/public/cpp/bindings/strong_binding.h" + +namespace blink { + +TextSuggestionBackendImpl::TextSuggestionBackendImpl(LocalFrame& frame) + : frame_(frame) {} + +// static +void TextSuggestionBackendImpl::Create( + LocalFrame* frame, + mojom::blink::TextSuggestionBackendRequest request) { + mojo::MakeStrongBinding(std::unique_ptr<TextSuggestionBackendImpl>( + new TextSuggestionBackendImpl(*frame)), + std::move(request)); +} + +void TextSuggestionBackendImpl::ApplySpellCheckSuggestion( + const WTF::String& suggestion) { + frame_->GetTextSuggestionController().ApplySpellCheckSuggestion(suggestion); +} + +void TextSuggestionBackendImpl::DeleteActiveSuggestionRange() { + frame_->GetTextSuggestionController().DeleteActiveSuggestionRange(); +} + +void TextSuggestionBackendImpl::NewWordAddedToDictionary( + const WTF::String& word) { + frame_->GetTextSuggestionController().NewWordAddedToDictionary(word); +} + +void TextSuggestionBackendImpl::SpellCheckMenuTimeoutCallback() { + frame_->GetTextSuggestionController().SpellCheckMenuTimeoutCallback(); +} + +void TextSuggestionBackendImpl::SuggestionMenuClosed() { + frame_->GetTextSuggestionController().SuggestionMenuClosed(); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionBackendImpl.h b/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionBackendImpl.h new file mode 100644 index 0000000..4b9164c8 --- /dev/null +++ b/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionBackendImpl.h
@@ -0,0 +1,38 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TextSuggestionBackendImpl_h +#define TextSuggestionBackendImpl_h + +#include "core/CoreExport.h" +#include "platform/heap/Persistent.h" +#include "public/platform/input_messages.mojom-blink.h" + +namespace blink { + +class LocalFrame; + +// Implementation of mojom::blink::TextSuggestionBackend +class CORE_EXPORT TextSuggestionBackendImpl final + : public mojom::blink::TextSuggestionBackend { + public: + static void Create(LocalFrame*, mojom::blink::TextSuggestionBackendRequest); + + void ApplySpellCheckSuggestion(const String& suggestion) final; + void DeleteActiveSuggestionRange() final; + void NewWordAddedToDictionary(const String& word) final; + void SpellCheckMenuTimeoutCallback() final; + void SuggestionMenuClosed() final; + + private: + explicit TextSuggestionBackendImpl(LocalFrame&); + + WeakPersistent<LocalFrame> frame_; + + DISALLOW_COPY_AND_ASSIGN(TextSuggestionBackendImpl); +}; + +} // namespace blink + +#endif // TextSuggestionBackendImpl_h
diff --git a/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionController.cpp b/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionController.cpp new file mode 100644 index 0000000..a696bac --- /dev/null +++ b/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionController.cpp
@@ -0,0 +1,337 @@ +// 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 "core/editing/suggestion/TextSuggestionController.h" + +#include "core/editing/EditingUtilities.h" +#include "core/editing/Editor.h" +#include "core/editing/FrameSelection.h" +#include "core/editing/PlainTextRange.h" +#include "core/editing/Position.h" +#include "core/editing/markers/DocumentMarkerController.h" +#include "core/editing/markers/SpellCheckMarker.h" +#include "core/editing/spellcheck/SpellChecker.h" +#include "core/frame/FrameView.h" +#include "core/frame/LocalFrame.h" +#include "core/layout/LayoutTheme.h" +#include "services/service_manager/public/cpp/interface_provider.h" + +namespace blink { + +namespace { + +bool ShouldDeleteNextCharacter(const Node& marker_text_node, + const DocumentMarker& marker) { + // If the character immediately following the range to be deleted is a space, + // delete it if either of these conditions holds: + // - We're deleting at the beginning of the editable text (to avoid ending up + // with a space at the beginning) + // - The character immediately before the range being deleted is also a space + // (to avoid ending up with two adjacent spaces) + const EphemeralRange next_character_range = + PlainTextRange(marker.EndOffset(), marker.EndOffset() + 1) + .CreateRange(*marker_text_node.parentNode()); + // No character immediately following the range (so it can't be a space) + if (next_character_range.IsNull()) + return false; + + const String next_character_str = + PlainText(next_character_range, TextIteratorBehavior::Builder().Build()); + const UChar next_character = next_character_str[0]; + // Character immediately following the range is not a space + if (next_character != kSpaceCharacter && + next_character != kNoBreakSpaceCharacter) + return false; + + // First case: we're deleting at the beginning of the editable text + if (marker.StartOffset() == 0) + return true; + + const EphemeralRange prev_character_range = + PlainTextRange(marker.StartOffset() - 1, marker.StartOffset()) + .CreateRange(*marker_text_node.parentNode()); + // Not at beginning, but there's no character immediately before the range + // being deleted (so it can't be a space) + if (prev_character_range.IsNull()) + return false; + + const String prev_character_str = + PlainText(prev_character_range, TextIteratorBehavior::Builder().Build()); + // Return true if the character immediately before the range is a space, false + // otherwise + const UChar prev_character = prev_character_str[0]; + return prev_character == kSpaceCharacter || + prev_character == kNoBreakSpaceCharacter; +} + +EphemeralRangeInFlatTree ComputeRangeSurroundingCaret( + const PositionInFlatTree& caret_position) { + const Node* const position_node = caret_position.ComputeContainerNode(); + const bool is_text_node = position_node->IsTextNode(); + const int position_offset_in_node = + caret_position.ComputeOffsetInContainerNode(); + + // If we're in the interior of a text node, we can avoid calling + // PreviousPositionOf/NextPositionOf for better efficiency. + if (is_text_node && position_offset_in_node != 0 && + position_offset_in_node != position_node->MaxCharacterOffset()) { + return EphemeralRangeInFlatTree( + PositionInFlatTree(position_node, position_offset_in_node - 1), + PositionInFlatTree(position_node, position_offset_in_node + 1)); + } + + const PositionInFlatTree& previous_position = + PreviousPositionOf(caret_position, PositionMoveType::kGraphemeCluster); + + const PositionInFlatTree& next_position = + NextPositionOf(caret_position, PositionMoveType::kGraphemeCluster); + + return EphemeralRangeInFlatTree( + previous_position.IsNull() ? caret_position : previous_position, + next_position.IsNull() ? caret_position : next_position); +} + +} // namespace + +TextSuggestionController::TextSuggestionController(LocalFrame& frame) + : is_suggestion_menu_open_(false), frame_(&frame) {} + +void TextSuggestionController::DocumentAttached(Document* document) { + DCHECK(document); + SetContext(document); +} + +bool TextSuggestionController::IsMenuOpen() const { + return is_suggestion_menu_open_; +} + +void TextSuggestionController::HandlePotentialMisspelledWordTap( + const PositionInFlatTree& caret_position) { + const EphemeralRangeInFlatTree& range_to_check = + ComputeRangeSurroundingCaret(caret_position); + + const std::pair<const Node*, const DocumentMarker*>& node_and_marker = + FirstMarkerIntersectingRange(range_to_check, + DocumentMarker::MisspellingMarkers()); + if (!node_and_marker.first) + return; + + if (!text_suggestion_host_) { + GetFrame().GetInterfaceProvider().GetInterface( + mojo::MakeRequest(&text_suggestion_host_)); + } + + text_suggestion_host_->StartSpellCheckMenuTimer(); +} + +DEFINE_TRACE(TextSuggestionController) { + visitor->Trace(frame_); + DocumentShutdownObserver::Trace(visitor); +} + +void TextSuggestionController::ApplySpellCheckSuggestion( + const String& suggestion) { + ReplaceSpellingMarkerTouchingSelectionWithText(suggestion); + SuggestionMenuClosed(); +} + +void TextSuggestionController::DeleteActiveSuggestionRange() { + AttemptToDeleteActiveSuggestionRange(); + SuggestionMenuClosed(); +} + +void TextSuggestionController::NewWordAddedToDictionary(const String& word) { + // Android pops up a dialog to let the user confirm they actually want to add + // the word to the dictionary; this method gets called as soon as the dialog + // is shown. So the word isn't actually in the dictionary here, even if the + // user will end up confirming the dialog, and we shouldn't try to re-run + // spellcheck here. + + // Note: this actually matches the behavior in native Android text boxes + GetDocument().Markers().RemoveSpellingMarkersUnderWords( + Vector<String>({word})); + SuggestionMenuClosed(); +} + +void TextSuggestionController::SpellCheckMenuTimeoutCallback() { + const std::pair<const Node*, const DocumentMarker*>& node_and_marker = + FirstMarkerTouchingSelection(DocumentMarker::MisspellingMarkers()); + if (!node_and_marker.first) + return; + + const Node* const marker_text_node = node_and_marker.first; + const SpellCheckMarker* const marker = + ToSpellCheckMarker(node_and_marker.second); + + const EphemeralRange marker_range = + EphemeralRange(Position(marker_text_node, marker->StartOffset()), + Position(marker_text_node, marker->EndOffset())); + const String& misspelled_word = PlainText(marker_range); + const String& description = marker->Description(); + + is_suggestion_menu_open_ = true; + GetFrame().Selection().SetCaretVisible(false); + GetDocument().Markers().AddActiveSuggestionMarker( + marker_range, SK_ColorTRANSPARENT, StyleableMarker::Thickness::kThin, + LayoutTheme::GetTheme().PlatformActiveSpellingMarkerHighlightColor()); + + Vector<String> suggestions; + description.Split('\n', suggestions); + + Vector<mojom::blink::SpellCheckSuggestionPtr> suggestion_ptrs; + for (const String& suggestion : suggestions) { + mojom::blink::SpellCheckSuggestionPtr info_ptr( + mojom::blink::SpellCheckSuggestion::New()); + info_ptr->suggestion = suggestion; + suggestion_ptrs.push_back(std::move(info_ptr)); + } + + const IntRect& absolute_bounds = GetFrame().Selection().AbsoluteCaretBounds(); + const IntRect& viewport_bounds = + GetFrame().View()->ContentsToViewport(absolute_bounds); + + text_suggestion_host_->ShowSpellCheckSuggestionMenu( + viewport_bounds.X(), viewport_bounds.MaxY(), std::move(misspelled_word), + std::move(suggestion_ptrs)); +} + +void TextSuggestionController::SuggestionMenuClosed() { + if (!IsAvailable()) + return; + + GetDocument().Markers().RemoveMarkersOfTypes( + DocumentMarker::kActiveSuggestion); + GetFrame().Selection().SetCaretVisible(true); + is_suggestion_menu_open_ = false; +} + +Document& TextSuggestionController::GetDocument() const { + DCHECK(IsAvailable()); + return *LifecycleContext(); +} + +bool TextSuggestionController::IsAvailable() const { + return LifecycleContext(); +} + +LocalFrame& TextSuggestionController::GetFrame() const { + DCHECK(frame_); + return *frame_; +} + +std::pair<const Node*, const DocumentMarker*> +TextSuggestionController::FirstMarkerIntersectingRange( + const EphemeralRangeInFlatTree& range, + DocumentMarker::MarkerTypes types) const { + const Node* const range_start_container = + range.StartPosition().ComputeContainerNode(); + const int range_start_offset = + range.StartPosition().ComputeOffsetInContainerNode(); + const Node* const range_end_container = + range.EndPosition().ComputeContainerNode(); + const int range_end_offset = + range.EndPosition().ComputeOffsetInContainerNode(); + + for (const Node& node : range.Nodes()) { + if (!node.IsTextNode()) + continue; + + const int start_offset = + node == range_start_container ? range_start_offset : 0; + const int end_offset = node == range_end_container + ? range_end_offset + : node.MaxCharacterOffset(); + + const DocumentMarker* const found_marker = + GetFrame().GetDocument()->Markers().FirstMarkerIntersectingOffsetRange( + ToText(node), start_offset, end_offset, types); + if (found_marker) + return std::make_pair(&node, found_marker); + } + + return {}; +} + +std::pair<const Node*, const DocumentMarker*> +TextSuggestionController::FirstMarkerTouchingSelection( + DocumentMarker::MarkerTypes types) const { + const VisibleSelectionInFlatTree& selection = + GetFrame().Selection().ComputeVisibleSelectionInFlatTree(); + if (selection.IsNone()) + return {}; + + const EphemeralRangeInFlatTree& range_to_check = + selection.IsRange() + ? EphemeralRangeInFlatTree(selection.Start(), selection.End()) + : ComputeRangeSurroundingCaret(selection.Start()); + + return FirstMarkerIntersectingRange(range_to_check, types); +} + +void TextSuggestionController::AttemptToDeleteActiveSuggestionRange() { + const std::pair<const Node*, const DocumentMarker*>& node_and_marker = + FirstMarkerTouchingSelection(DocumentMarker::kActiveSuggestion); + if (!node_and_marker.first) + return; + + const Node* const marker_text_node = node_and_marker.first; + const DocumentMarker* const marker = node_and_marker.second; + + const bool delete_next_char = + ShouldDeleteNextCharacter(*marker_text_node, *marker); + + const EphemeralRange range_to_delete = EphemeralRange( + Position(marker_text_node, marker->StartOffset()), + Position(marker_text_node, marker->EndOffset() + delete_next_char)); + ReplaceRangeWithText(range_to_delete, ""); +} + +void TextSuggestionController::ReplaceSpellingMarkerTouchingSelectionWithText( + const String& suggestion) { + const std::pair<const Node*, const DocumentMarker*>& node_and_marker = + FirstMarkerTouchingSelection(DocumentMarker::MisspellingMarkers()); + if (!node_and_marker.first) + return; + + const Node* const marker_text_node = node_and_marker.first; + const DocumentMarker* const marker = node_and_marker.second; + + const EphemeralRange range_to_replace( + Position(marker_text_node, marker->StartOffset()), + Position(marker_text_node, marker->EndOffset())); + ReplaceRangeWithText(range_to_replace, suggestion); +} + +void TextSuggestionController::ReplaceRangeWithText(const EphemeralRange& range, + const String& replacement) { + GetFrame().Selection().SetSelection( + SelectionInDOMTree::Builder().SetBaseAndExtent(range).Build()); + + // Dispatch 'beforeinput'. + Element* const target = GetFrame().GetEditor().FindEventTargetFromSelection(); + DataTransfer* const data_transfer = DataTransfer::Create( + DataTransfer::DataTransferType::kInsertReplacementText, + DataTransferAccessPolicy::kDataTransferReadable, + DataObject::CreateFromString(replacement)); + + const bool is_canceled = + DispatchBeforeInputDataTransfer( + target, InputEvent::InputType::kInsertReplacementText, + data_transfer) != DispatchEventResult::kNotCanceled; + + // 'beforeinput' event handler may destroy target frame. + if (!IsAvailable()) + return; + + // TODO(editing-dev): The use of updateStyleAndLayoutIgnorePendingStylesheets + // needs to be audited. See http://crbug.com/590369 for more details. + GetFrame().GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets(); + + if (is_canceled) + return; + GetFrame().GetEditor().ReplaceSelectionWithText( + replacement, false, false, InputEvent::InputType::kInsertReplacementText); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionController.h b/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionController.h new file mode 100644 index 0000000..3e84230 --- /dev/null +++ b/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionController.h
@@ -0,0 +1,71 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TextSuggestionController_h +#define TextSuggestionController_h + +#include "core/CoreExport.h" +#include "core/dom/DocumentShutdownObserver.h" +#include "core/editing/EphemeralRange.h" +#include "core/editing/VisiblePosition.h" +#include "core/editing/markers/DocumentMarker.h" +#include "platform/heap/Handle.h" +#include "public/platform/input_host.mojom-blink.h" + +namespace blink { + +class Document; +class LocalFrame; + +// This class handles functionality related to displaying a menu of text +// suggestions (e.g. from spellcheck), and performing actions relating to those +// suggestions. Android is currently the only platform that has such a menu. +class CORE_EXPORT TextSuggestionController final + : public GarbageCollectedFinalized<TextSuggestionController>, + public DocumentShutdownObserver { + USING_GARBAGE_COLLECTED_MIXIN(TextSuggestionController); + + public: + explicit TextSuggestionController(LocalFrame&); + + void DocumentAttached(Document*); + + bool IsMenuOpen() const; + + void HandlePotentialMisspelledWordTap( + const PositionInFlatTree& caret_position); + + void ApplySpellCheckSuggestion(const String& suggestion); + void DeleteActiveSuggestionRange(); + void NewWordAddedToDictionary(const String& word); + void SpellCheckMenuTimeoutCallback(); + void SuggestionMenuClosed(); + + DECLARE_TRACE(); + + private: + Document& GetDocument() const; + bool IsAvailable() const; + LocalFrame& GetFrame() const; + + std::pair<const Node*, const DocumentMarker*> FirstMarkerIntersectingRange( + const EphemeralRangeInFlatTree&, + DocumentMarker::MarkerTypes) const; + std::pair<const Node*, const DocumentMarker*> FirstMarkerTouchingSelection( + DocumentMarker::MarkerTypes) const; + + void AttemptToDeleteActiveSuggestionRange(); + void ReplaceSpellingMarkerTouchingSelectionWithText(const String&); + void ReplaceRangeWithText(const EphemeralRange&, const String& replacement); + + bool is_suggestion_menu_open_; + const Member<LocalFrame> frame_; + mojom::blink::TextSuggestionHostPtr text_suggestion_host_; + + DISALLOW_COPY_AND_ASSIGN(TextSuggestionController); +}; + +} // namespace blink + +#endif // TextSuggestionController_h
diff --git a/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionControllerTest.cpp b/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionControllerTest.cpp new file mode 100644 index 0000000..387b969 --- /dev/null +++ b/third_party/WebKit/Source/core/editing/suggestion/TextSuggestionControllerTest.cpp
@@ -0,0 +1,271 @@ +// 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 "core/editing/suggestion/TextSuggestionController.h" + +#include "core/editing/EditingTestBase.h" +#include "core/editing/FrameSelection.h" +#include "core/editing/markers/DocumentMarkerController.h" +#include "core/editing/spellcheck/SpellChecker.h" + +namespace blink { + +class TextSuggestionControllerTest : public EditingTestBase {}; + +TEST_F(TextSuggestionControllerTest, ApplySpellCheckSuggestion) { + SetBodyContent( + "<div contenteditable>" + "spllchck" + "</div>"); + Element* div = GetDocument().QuerySelector("div"); + Node* text = div->firstChild(); + + GetDocument().Markers().AddSpellingMarker( + EphemeralRange(Position(text, 0), Position(text, 8))); + // Select immediately before misspelling + GetDocument().GetFrame()->Selection().SetSelection( + SelectionInDOMTree::Builder() + .SetBaseAndExtent(Position(text, 0), Position(text, 0)) + .Build()); + GetDocument() + .GetFrame() + ->GetTextSuggestionController() + .ApplySpellCheckSuggestion("spellcheck"); + + EXPECT_EQ("spellcheck", text->textContent()); +} + +TEST_F(TextSuggestionControllerTest, DeleteActiveSuggestionRange_DeleteAtEnd) { + SetBodyContent( + "<div contenteditable>" + "word1 word2" + "</div>"); + Element* div = GetDocument().QuerySelector("div"); + Node* text = div->firstChild(); + + // Mark "word2" as the active suggestion range + GetDocument().Markers().AddActiveSuggestionMarker( + EphemeralRange(Position(text, 6), Position(text, 11)), Color::kBlack, + StyleableMarker::Thickness::kThin, Color::kBlack); + // Select immediately before word2 + GetDocument().GetFrame()->Selection().SetSelection( + SelectionInDOMTree::Builder() + .SetBaseAndExtent(Position(text, 6), Position(text, 6)) + .Build()); + GetDocument() + .GetFrame() + ->GetTextSuggestionController() + .DeleteActiveSuggestionRange(); + + EXPECT_EQ("word1\xA0", text->textContent()); +} + +TEST_F(TextSuggestionControllerTest, + DeleteActiveSuggestionRange_DeleteInMiddle) { + SetBodyContent( + "<div contenteditable>" + "word1 word2 word3" + "</div>"); + Element* div = GetDocument().QuerySelector("div"); + Node* text = div->firstChild(); + + // Mark "word2" as the active suggestion range + GetDocument().Markers().AddActiveSuggestionMarker( + EphemeralRange(Position(text, 6), Position(text, 11)), Color::kBlack, + StyleableMarker::Thickness::kThin, Color::kBlack); + // Select immediately before word2 + GetDocument().GetFrame()->Selection().SetSelection( + SelectionInDOMTree::Builder() + .SetBaseAndExtent(Position(text, 6), Position(text, 6)) + .Build()); + GetDocument() + .GetFrame() + ->GetTextSuggestionController() + .DeleteActiveSuggestionRange(); + + // One of the extra spaces around "word2" should have been removed + EXPECT_EQ("word1\xA0word3", text->textContent()); +} + +TEST_F(TextSuggestionControllerTest, + DeleteActiveSuggestionRange_DeleteAtBeginningWithSpaceAfter) { + SetBodyContent( + "<div contenteditable>" + "word1 word2" + "</div>"); + Element* div = GetDocument().QuerySelector("div"); + Node* text = div->firstChild(); + + // Mark "word1" as the active suggestion range + GetDocument().Markers().AddActiveSuggestionMarker( + EphemeralRange(Position(text, 0), Position(text, 5)), Color::kBlack, + StyleableMarker::Thickness::kThin, Color::kBlack); + // Select immediately before word1 + GetDocument().GetFrame()->Selection().SetSelection( + SelectionInDOMTree::Builder() + .SetBaseAndExtent(Position(text, 0), Position(text, 0)) + .Build()); + GetDocument() + .GetFrame() + ->GetTextSuggestionController() + .DeleteActiveSuggestionRange(); + + // The space after "word1" should have been removed (to avoid leaving an + // empty space at the beginning of the composition) + EXPECT_EQ("word2", text->textContent()); +} + +TEST_F(TextSuggestionControllerTest, + DeleteActiveSuggestionRange_DeleteEntireRange) { + SetBodyContent( + "<div contenteditable>" + "word1" + "</div>"); + Element* div = GetDocument().QuerySelector("div"); + Node* text = div->firstChild(); + + // Mark "word1" as the active suggestion range + GetDocument().Markers().AddActiveSuggestionMarker( + EphemeralRange(Position(text, 0), Position(text, 5)), Color::kBlack, + StyleableMarker::Thickness::kThin, Color::kBlack); + // Select immediately before word1 + GetDocument().GetFrame()->Selection().SetSelection( + SelectionInDOMTree::Builder() + .SetBaseAndExtent(Position(text, 0), Position(text, 0)) + .Build()); + GetDocument() + .GetFrame() + ->GetTextSuggestionController() + .DeleteActiveSuggestionRange(); + + EXPECT_EQ("", text->textContent()); +} + +// The following two cases test situations that probably shouldn't occur in +// normal use (spell check/suggestoin markers not spanning a whole word), but +// are included anyway to verify that DeleteActiveSuggestionRange() is +// well-behaved in these cases + +TEST_F(TextSuggestionControllerTest, + DeleteActiveSuggestionRange_DeleteRangeWithTextBeforeAndSpaceAfter) { + SetBodyContent( + "<div contenteditable>" + "word1word2 word3" + "</div>"); + Element* div = GetDocument().QuerySelector("div"); + Node* text = div->firstChild(); + + // Mark "word2" as the active suggestion range + GetDocument().Markers().AddActiveSuggestionMarker( + EphemeralRange(Position(text, 5), Position(text, 10)), Color::kBlack, + StyleableMarker::Thickness::kThin, Color::kBlack); + // Select immediately before word2 + GetDocument().GetFrame()->Selection().SetSelection( + SelectionInDOMTree::Builder() + .SetBaseAndExtent(Position(text, 5), Position(text, 5)) + .Build()); + GetDocument() + .GetFrame() + ->GetTextSuggestionController() + .DeleteActiveSuggestionRange(); + + EXPECT_EQ("word1\xA0word3", text->textContent()); +} + +TEST_F(TextSuggestionControllerTest, + DeleteActiveSuggestionRange_DeleteRangeWithSpaceBeforeAndTextAfter) { + SetBodyContent( + "<div contenteditable>" + "word1 word2word3" + "</div>"); + Element* div = GetDocument().QuerySelector("div"); + Node* text = div->firstChild(); + + // Mark "word2" as the active suggestion range + GetDocument().Markers().AddActiveSuggestionMarker( + EphemeralRange(Position(text, 6), Position(text, 11)), Color::kBlack, + StyleableMarker::Thickness::kThin, Color::kBlack); + // Select immediately before word2 + GetDocument().GetFrame()->Selection().SetSelection( + SelectionInDOMTree::Builder() + .SetBaseAndExtent(Position(text, 6), Position(text, 6)) + .Build()); + GetDocument() + .GetFrame() + ->GetTextSuggestionController() + .DeleteActiveSuggestionRange(); + + EXPECT_EQ("word1\xA0word3", text->textContent()); +} + +TEST_F(TextSuggestionControllerTest, + DeleteActiveSuggestionRange_DeleteAtBeginningWithTextAfter) { + SetBodyContent( + "<div contenteditable>" + "word1word2" + "</div>"); + Element* div = GetDocument().QuerySelector("div"); + Node* text = div->firstChild(); + + // Mark "word1" as the active suggestion range + GetDocument().Markers().AddActiveSuggestionMarker( + EphemeralRange(Position(text, 0), Position(text, 5)), Color::kBlack, + StyleableMarker::Thickness::kThin, Color::kBlack); + // Select immediately before word1 + GetDocument().GetFrame()->Selection().SetSelection( + SelectionInDOMTree::Builder() + .SetBaseAndExtent(Position(text, 0), Position(text, 0)) + .Build()); + GetDocument() + .GetFrame() + ->GetTextSuggestionController() + .DeleteActiveSuggestionRange(); + + EXPECT_EQ("word2", text->textContent()); +} + +TEST_F(TextSuggestionControllerTest, + DeleteActiveSuggestionRange_NewWordAddedToDictionary) { + SetBodyContent( + "<div contenteditable>" + "embiggen" + "</div>"); + Element* div = GetDocument().QuerySelector("div"); + Node* text = div->firstChild(); + + // Mark "embiggen" as misspelled + GetDocument().Markers().AddSpellingMarker( + EphemeralRange(Position(text, 0), Position(text, 8))); + // Select inside before "embiggen" + GetDocument().GetFrame()->Selection().SetSelection( + SelectionInDOMTree::Builder() + .SetBaseAndExtent(Position(text, 1), Position(text, 1)) + .Build()); + + // Add some other word to the dictionary + GetDocument() + .GetFrame() + ->GetTextSuggestionController() + .NewWordAddedToDictionary("cromulent"); + // Verify the spelling marker is still present + EXPECT_NE(nullptr, GetDocument() + .GetFrame() + ->GetSpellChecker() + .GetSpellCheckMarkerUnderSelection() + .first); + + // Add "embiggen" to the dictionary + GetDocument() + .GetFrame() + ->GetTextSuggestionController() + .NewWordAddedToDictionary("embiggen"); + // Verify the spelling marker is gone + EXPECT_EQ(nullptr, GetDocument() + .GetFrame() + ->GetSpellChecker() + .GetSpellCheckMarkerUnderSelection() + .first); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp index d3fafe5..0fb9749 100644 --- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp +++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -44,6 +44,7 @@ #include "core/editing/InputMethodController.h" #include "core/editing/serializers/Serialization.h" #include "core/editing/spellcheck/SpellChecker.h" +#include "core/editing/suggestion/TextSuggestionController.h" #include "core/events/Event.h" #include "core/frame/ContentSettingsClient.h" #include "core/frame/EventHandlerRegistry.h" @@ -222,6 +223,7 @@ visitor->Trace(console_); visitor->Trace(input_method_controller_); visitor->Trace(frame_resource_coordinator_); + visitor->Trace(text_suggestion_controller_); Frame::Trace(visitor); Supplementable<LocalFrame>::Trace(visitor); } @@ -384,6 +386,7 @@ Selection().DocumentAttached(GetDocument()); GetInputMethodController().DocumentAttached(GetDocument()); GetSpellChecker().DocumentAttached(GetDocument()); + GetTextSuggestionController().DocumentAttached(GetDocument()); } Frame* LocalFrame::FindFrameForNavigation(const AtomicString& name, @@ -744,6 +747,7 @@ event_handler_(new EventHandler(*this)), console_(FrameConsole::Create(*this)), input_method_controller_(InputMethodController::Create(*this)), + text_suggestion_controller_(new TextSuggestionController(*this)), navigation_disable_count_(0), page_zoom_factor_(ParentPageZoomFactor(this)), text_zoom_factor_(ParentTextZoomFactor(this)),
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.h b/third_party/WebKit/Source/core/frame/LocalFrame.h index 6fe9f42..d300c04 100644 --- a/third_party/WebKit/Source/core/frame/LocalFrame.h +++ b/third_party/WebKit/Source/core/frame/LocalFrame.h
@@ -81,6 +81,7 @@ class ResourceRequest; class ScriptController; class SpellChecker; +class TextSuggestionController; class WebFrameScheduler; class WebPluginContainerImpl; class WebTaskRunner; @@ -154,6 +155,7 @@ NavigationScheduler& GetNavigationScheduler() const; FrameSelection& Selection() const; InputMethodController& GetInputMethodController() const; + TextSuggestionController& GetTextSuggestionController() const; ScriptController& GetScriptController() const; SpellChecker& GetSpellChecker() const; FrameConsole& Console() const; @@ -301,6 +303,7 @@ const Member<EventHandler> event_handler_; const Member<FrameConsole> console_; const Member<InputMethodController> input_method_controller_; + const Member<TextSuggestionController> text_suggestion_controller_; int navigation_disable_count_; @@ -355,6 +358,11 @@ return *input_method_controller_; } +inline TextSuggestionController& LocalFrame::GetTextSuggestionController() + const { + return *text_suggestion_controller_; +} + inline bool LocalFrame::InViewSourceMode() const { return in_view_source_mode_; }
diff --git a/third_party/WebKit/Source/core/layout/BUILD.gn b/third_party/WebKit/Source/core/layout/BUILD.gn index 38459d6..5959317 100644 --- a/third_party/WebKit/Source/core/layout/BUILD.gn +++ b/third_party/WebKit/Source/core/layout/BUILD.gn
@@ -376,6 +376,7 @@ "ng/inline/ng_line_height_metrics.h", "ng/inline/ng_offset_mapping_builder.cc", "ng/inline/ng_offset_mapping_builder.h", + "ng/inline/ng_offset_mapping_result.cc", "ng/inline/ng_offset_mapping_result.h", "ng/inline/ng_physical_line_box_fragment.cc", "ng/inline/ng_physical_line_box_fragment.h",
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlock.cpp b/third_party/WebKit/Source/core/layout/LayoutBlock.cpp index dc11be0..dc2ec511 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBlock.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutBlock.cpp
@@ -907,11 +907,6 @@ void LayoutBlock::SetSelectionState(SelectionState state) { LayoutBox::SetSelectionState(state); - - if (InlineBoxWrapper() && CanUpdateSelectionOnRootLineBoxes()) { - InlineBoxWrapper()->Root().SetHasSelectedChildren(state != - SelectionState::kNone); - } } TrackedLayoutBoxListHashSet* LayoutBlock::PositionedObjectsInternal() const {
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlockFlowLine.cpp b/third_party/WebKit/Source/core/layout/LayoutBlockFlowLine.cpp index 1380f19d..862a25e3 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBlockFlowLine.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutBlockFlowLine.cpp
@@ -335,11 +335,6 @@ DCHECK(LastLineBox()); DCHECK(!LastLineBox()->IsConstructed()); - // Set the m_selectedChildren flag on the root inline box if one of the leaf - // inline box from the bidi runs walk above has a selection state. - if (root_has_selected_children) - LastLineBox()->Root().SetHasSelectedChildren(true); - // Set bits on our inline flow boxes that indicate which sides should // paint borders/margins/padding. This knowledge will ultimately be used when // we determine the horizontal positions and widths of all the inline boxes on
diff --git a/third_party/WebKit/Source/core/layout/LayoutListMarker.cpp b/third_party/WebKit/Source/core/layout/LayoutListMarker.cpp index 05bee86..e2700cb 100644 --- a/third_party/WebKit/Source/core/layout/LayoutListMarker.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutListMarker.cpp
@@ -483,11 +483,6 @@ // The selection state for our containing block hierarchy is updated by the // base class call. LayoutBox::SetSelectionState(state); - - if (InlineBoxWrapper() && CanUpdateSelectionOnRootLineBoxes()) { - InlineBoxWrapper()->Root().SetHasSelectedChildren(state != - SelectionState::kNone); - } } void LayoutListMarker::ListItemStyleDidChange() {
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h index 6bb039e..baa106d 100644 --- a/third_party/WebKit/Source/core/layout/LayoutObject.h +++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -2532,7 +2532,7 @@ } } void ClearPositionedState() { - positioned_state_ = static_cast<unsigned>(EPosition::kStatic); + positioned_state_ = kIsStaticallyPositioned; } ALWAYS_INLINE SelectionState GetSelectionState() const {
diff --git a/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp b/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp index 1d90c33..2afa4633 100644 --- a/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutReplaced.cpp
@@ -940,14 +940,6 @@ // The selection state for our containing block hierarchy is updated by the // base class call. LayoutBox::SetSelectionState(state); - - if (!InlineBoxWrapper()) - return; - - if (CanUpdateSelectionOnRootLineBoxes()) { - InlineBoxWrapper()->Root().SetHasSelectedChildren(state != - SelectionState::kNone); - } } void LayoutReplaced::IntrinsicSizingInfo::Transpose() {
diff --git a/third_party/WebKit/Source/core/layout/LayoutText.cpp b/third_party/WebKit/Source/core/layout/LayoutText.cpp index 48cd0ed..501345f 100644 --- a/third_party/WebKit/Source/core/layout/LayoutText.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutText.cpp
@@ -1446,33 +1446,6 @@ void LayoutText::SetSelectionState(SelectionState state) { LayoutObject::SetSelectionState(state); - if (CanUpdateSelectionOnRootLineBoxes()) { - if (state == SelectionState::kStart || state == SelectionState::kEnd || - state == SelectionState::kStartAndEnd) { - int start_pos, end_pos; - std::tie(start_pos, end_pos) = SelectionStartEnd(); - if (GetSelectionState() == SelectionState::kStart) { - end_pos = TextLength(); - - // to handle selection from end of text to end of line - if (start_pos && start_pos == end_pos) - start_pos = end_pos - 1; - } else if (GetSelectionState() == SelectionState::kEnd) { - start_pos = 0; - } - - for (InlineTextBox* box : InlineTextBoxesOf(*this)) { - if (box->IsSelected(start_pos, end_pos)) { - box->Root().SetHasSelectedChildren(true); - } - } - } else { - for (InlineTextBox* box : InlineTextBoxesOf(*this)) { - box->Root().SetHasSelectedChildren(state == SelectionState::kInside); - } - } - } - // The containing block can be null in case of an orphaned tree. LayoutBlock* containing_block = this->ContainingBlock(); if (containing_block && !containing_block->IsLayoutView())
diff --git a/third_party/WebKit/Source/core/layout/TextAutosizer.cpp b/third_party/WebKit/Source/core/layout/TextAutosizer.cpp index a7f828c..36548bc 100644 --- a/third_party/WebKit/Source/core/layout/TextAutosizer.cpp +++ b/third_party/WebKit/Source/core/layout/TextAutosizer.cpp
@@ -165,7 +165,8 @@ // the content is already overflowing before autosizing kicks in. for (; block; block = block->ContainingBlock()) { const ComputedStyle& style = block->StyleRef(); - if (style.OverflowY() >= EOverflow::kScroll) + if (style.OverflowY() != EOverflow::kVisible + && style.OverflowY() != EOverflow::kHidden) return false; if (style.Height().IsSpecified() || style.MaxHeight().IsSpecified() || block->IsOutOfFlowPositioned()) {
diff --git a/third_party/WebKit/Source/core/layout/line/InlineBox.h b/third_party/WebKit/Source/core/layout/line/InlineBox.h index 7714da90..83eb143 100644 --- a/third_party/WebKit/Source/core/layout/line/InlineBox.h +++ b/third_party/WebKit/Source/core/layout/line/InlineBox.h
@@ -432,7 +432,7 @@ has_virtual_logical_height_(false), is_horizontal_(is_horizontal), ends_with_break_(false), - has_selected_children_or_can_have_leading_expansion_(false), + can_have_leading_expansion_(false), known_to_have_no_overflow_(true), has_ellipsis_box_or_hyphen_(false), dir_override_(false), @@ -464,8 +464,7 @@ ADD_BOOLEAN_BITFIELD(ends_with_break_, EndsWithBreak); // Whether the line ends with a <br>. // shared between RootInlineBox and InlineTextBox - ADD_BOOLEAN_BITFIELD(has_selected_children_or_can_have_leading_expansion_, - HasSelectedChildrenOrCanHaveLeadingExpansion); + ADD_BOOLEAN_BITFIELD(can_have_leading_expansion_, CanHaveLeadingExpansion); // This boolean will never be set if there is potential for overflow, but it // will be eagerly cleared in the opposite case. As such, it's a @@ -506,13 +505,6 @@ bitfields_.SetEndsWithBreak(ends_with_break); } bool HasEllipsisBox() const { return bitfields_.HasEllipsisBoxOrHyphen(); } - bool HasSelectedChildren() const { - return bitfields_.HasSelectedChildrenOrCanHaveLeadingExpansion(); - } - void SetHasSelectedChildren(bool has_selected_children) { - bitfields_.SetHasSelectedChildrenOrCanHaveLeadingExpansion( - has_selected_children); - } void SetHasEllipsisBox(bool has_ellipsis_box) { bitfields_.SetHasEllipsisBoxOrHyphen(has_ellipsis_box); } @@ -523,11 +515,10 @@ bitfields_.SetHasEllipsisBoxOrHyphen(has_hyphen); } bool CanHaveLeadingExpansion() const { - return bitfields_.HasSelectedChildrenOrCanHaveLeadingExpansion(); + return bitfields_.CanHaveLeadingExpansion(); } void SetCanHaveLeadingExpansion(bool can_have_leading_expansion) { - bitfields_.SetHasSelectedChildrenOrCanHaveLeadingExpansion( - can_have_leading_expansion); + bitfields_.SetCanHaveLeadingExpansion(can_have_leading_expansion); } signed Expansion() { return bitfields_.Expansion(); } void SetExpansion(signed expansion) { bitfields_.SetExpansion(expansion); }
diff --git a/third_party/WebKit/Source/core/layout/line/RootInlineBox.h b/third_party/WebKit/Source/core/layout/line/RootInlineBox.h index 501ed63..7e92e602 100644 --- a/third_party/WebKit/Source/core/layout/line/RootInlineBox.h +++ b/third_party/WebKit/Source/core/layout/line/RootInlineBox.h
@@ -145,9 +145,6 @@ LayoutUnit line_top, LayoutUnit line_bottom) override; - using InlineBox::HasSelectedChildren; - using InlineBox::SetHasSelectedChildren; - SelectionState GetSelectionState() const final; InlineBox* FirstSelectedBox() const; InlineBox* LastSelectedBox() const;
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc index c0760fe..86839c9d 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc
@@ -674,4 +674,69 @@ return String::Format("NGInlineNode"); } +// static +Optional<NGInlineNode> GetNGInlineNodeFor(const Node& node) { + LayoutObject* layout_object = node.GetLayoutObject(); + if (!layout_object || !layout_object->IsInline()) + return WTF::nullopt; + LayoutBox* box = layout_object->EnclosingBox(); + if (!box->IsLayoutNGBlockFlow()) + return WTF::nullopt; + DCHECK(box); + DCHECK(box->ChildrenInline()); + return NGInlineNode(ToLayoutNGBlockFlow(box)); +} + +const NGOffsetMappingUnit* NGInlineNode::GetMappingUnitForDOMOffset( + const Node& node, + unsigned offset) { + // TODO(xiaochengh): Move/Reimplement AssociatedLayoutObjectOf in core/layout. + LayoutObject* layout_object = AssociatedLayoutObjectOf(node, offset); + if (!layout_object || !layout_object->IsText()) + return nullptr; + + DCHECK_EQ(layout_object->EnclosingBox(), GetLayoutBlockFlow()); + const auto& result = ComputeOffsetMappingIfNeeded(); + + // TODO(xiaochengh): Wrap the code below into a member function of + // NGOffsetMappingResult. + unsigned range_start; + unsigned range_end; + std::tie(range_start, range_end) = + result.ranges.at(ToLayoutText(layout_object)); + if (range_start == range_end || result.units[range_start].DOMStart() > offset) + return nullptr; + // Find the last unit where unit.dom_start <= offset + const NGOffsetMappingUnit* unit = std::prev(std::upper_bound( + result.units.begin() + range_start, result.units.begin() + range_end, + offset, [](unsigned offset, const NGOffsetMappingUnit& unit) { + return offset < unit.DOMStart(); + })); + if (unit->DOMEnd() < offset) + return nullptr; + return unit; +} + +size_t NGInlineNode::GetTextContentOffset(const Node& node, unsigned offset) { + const NGOffsetMappingUnit* unit = GetMappingUnitForDOMOffset(node, offset); + if (!unit) + return kNotFound; + + // TODO(xiaochengh): Wrap the code below into a member function of + // NGOffsetMappingUnit. + DCHECK_GE(offset, unit->DOMStart()); + DCHECK_LE(offset, unit->DOMEnd()); + // DOM start is always mapped to text content start. + if (offset == unit->DOMStart()) + return unit->TextContentStart(); + // DOM end is always mapped to text content end. + if (offset == unit->DOMEnd()) + return unit->TextContentEnd(); + // |unit| has collapsed mapping. + if (unit->TextContentStart() == unit->TextContentEnd()) + return unit->TextContentStart(); + // |unit| has identity mapping. + return offset - unit->DOMStart() + unit->TextContentStart(); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.h index 395ae91..8a3565e7 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.h +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.h
@@ -11,6 +11,7 @@ #include "core/layout/ng/layout_ng_block_flow.h" #include "core/layout/ng/ng_layout_input_node.h" #include "platform/heap/Handle.h" +#include "platform/wtf/Optional.h" #include "platform/wtf/text/WTFString.h" namespace blink { @@ -81,6 +82,17 @@ String ToString() const; + // ------ Offset Mapping APIs ----- + + // Returns the NGOffsetMappingUnit that contains the given offset in the DOM + // node. If there are multiple qualifying units, returns the last one. + const NGOffsetMappingUnit* GetMappingUnitForDOMOffset(const Node&, unsigned); + + // Returns the text content offset corresponding to the given DOM offset. + size_t GetTextContentOffset(const Node&, unsigned); + + // TODO(xiaochengh): Add APIs for reverse mapping. + protected: // Prepare inline and text content for layout. Must be called before // calling the Layout method. @@ -110,6 +122,10 @@ Data().items_[index].AssertEndOffset(offset); } +// If the given Node is laid out as an inline, returns the NGInlineNode that +// encloses it. Otherwise, returns null. +CORE_EXPORT Optional<NGInlineNode> GetNGInlineNodeFor(const Node&); + DEFINE_TYPE_CASTS(NGInlineNode, NGLayoutInputNode, node,
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node_offset_mapping_test.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node_offset_mapping_test.cc index 4ce92f2..7b72fbc 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node_offset_mapping_test.cc +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node_offset_mapping_test.cc
@@ -48,20 +48,30 @@ return ToLayoutText(parent->firstChild()->GetLayoutObject()); } + const NGOffsetMappingUnit* GetUnitForDOMOffset(const Node& node, + unsigned offset) const { + return NGInlineNode(layout_block_flow_) + .GetMappingUnitForDOMOffset(node, offset); + } + + size_t GetTextContentOffset(const Node& node, unsigned offset) const { + return NGInlineNode(layout_block_flow_).GetTextContentOffset(node, offset); + } + RefPtr<const ComputedStyle> style_; LayoutNGBlockFlow* layout_block_flow_ = nullptr; LayoutObject* layout_object_ = nullptr; FontCachePurgePreventer purge_preventer_; }; -#define TEST_UNIT(unit, _type, _owner, domstart, domend, textcontentstart, \ - textcontentend) \ - EXPECT_EQ(_type, unit.type); \ - EXPECT_EQ(_owner, unit.owner); \ - EXPECT_EQ(domstart, unit.dom_start); \ - EXPECT_EQ(domend, unit.dom_end); \ - EXPECT_EQ(textcontentstart, unit.text_content_start); \ - EXPECT_EQ(textcontentend, unit.text_content_end) +#define TEST_UNIT(unit, type, owner, dom_start, dom_end, text_content_start, \ + text_content_end) \ + EXPECT_EQ(type, unit.GetType()); \ + EXPECT_EQ(owner, unit.GetOwner()); \ + EXPECT_EQ(dom_start, unit.DOMStart()); \ + EXPECT_EQ(dom_end, unit.DOMEnd()); \ + EXPECT_EQ(text_content_start, unit.TextContentStart()); \ + EXPECT_EQ(text_content_end, unit.TextContentEnd()) #define TEST_RANGE(ranges, owner, start, end) \ ASSERT_TRUE(ranges.Contains(owner)); \ @@ -75,8 +85,27 @@ EXPECT_TRUE(IsOffsetMappingStored()); } +TEST_F(NGInlineNodeOffsetMappingTest, GetNGInlineNodeForText) { + SetupHtml("t", "<div id=t>foo</div>"); + Element* div = GetDocument().getElementById("t"); + Node* text = div->firstChild(); + + Optional<NGInlineNode> inline_node = GetNGInlineNodeFor(*text); + ASSERT_TRUE(inline_node.has_value()); + EXPECT_EQ(layout_block_flow_, inline_node->GetLayoutBlockFlow()); +} + +TEST_F(NGInlineNodeOffsetMappingTest, CantGetNGInlineNodeForBody) { + SetupHtml("t", "<div id=t>foo</div>"); + Element* div = GetDocument().getElementById("t"); + + Optional<NGInlineNode> inline_node = GetNGInlineNodeFor(*div); + EXPECT_FALSE(inline_node.has_value()); +} + TEST_F(NGInlineNodeOffsetMappingTest, OneTextNode) { SetupHtml("t", "<div id=t>foo</div>"); + const Node* foo_node = layout_object_->GetNode(); const NGOffsetMappingResult& result = GetOffsetMapping(); ASSERT_EQ(1u, result.units.size()); @@ -85,12 +114,24 @@ ASSERT_EQ(1u, result.ranges.size()); TEST_RANGE(result.ranges, ToLayoutText(layout_object_), 0u, 1u); + + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 0)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 1)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 2)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 3)); + + EXPECT_EQ(0u, GetTextContentOffset(*foo_node, 0)); + EXPECT_EQ(1u, GetTextContentOffset(*foo_node, 1)); + EXPECT_EQ(2u, GetTextContentOffset(*foo_node, 2)); + EXPECT_EQ(3u, GetTextContentOffset(*foo_node, 3)); } TEST_F(NGInlineNodeOffsetMappingTest, TwoTextNodes) { SetupHtml("t", "<div id=t>foo<span id=s>bar</span></div>"); const LayoutText* foo = ToLayoutText(layout_object_); const LayoutText* bar = GetLayoutTextUnder("s"); + const Node* foo_node = foo->GetNode(); + const Node* bar_node = bar->GetNode(); const NGOffsetMappingResult& result = GetOffsetMapping(); ASSERT_EQ(2u, result.units.size()); @@ -102,6 +143,24 @@ ASSERT_EQ(2u, result.ranges.size()); TEST_RANGE(result.ranges, foo, 0u, 1u); TEST_RANGE(result.ranges, bar, 1u, 2u); + + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 0)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 1)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 2)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 3)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*bar_node, 0)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*bar_node, 1)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*bar_node, 2)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*bar_node, 3)); + + EXPECT_EQ(0u, GetTextContentOffset(*foo_node, 0)); + EXPECT_EQ(1u, GetTextContentOffset(*foo_node, 1)); + EXPECT_EQ(2u, GetTextContentOffset(*foo_node, 2)); + EXPECT_EQ(3u, GetTextContentOffset(*foo_node, 3)); + EXPECT_EQ(3u, GetTextContentOffset(*bar_node, 0)); + EXPECT_EQ(4u, GetTextContentOffset(*bar_node, 1)); + EXPECT_EQ(5u, GetTextContentOffset(*bar_node, 2)); + EXPECT_EQ(6u, GetTextContentOffset(*bar_node, 3)); } TEST_F(NGInlineNodeOffsetMappingTest, BRBetweenTextNodes) { @@ -109,6 +168,8 @@ const LayoutText* foo = ToLayoutText(layout_object_); const LayoutText* br = ToLayoutText(foo->NextSibling()); const LayoutText* bar = ToLayoutText(br->NextSibling()); + const Node* foo_node = foo->GetNode(); + const Node* bar_node = bar->GetNode(); const NGOffsetMappingResult& result = GetOffsetMapping(); ASSERT_EQ(3u, result.units.size()); @@ -123,10 +184,29 @@ TEST_RANGE(result.ranges, foo, 0u, 1u); TEST_RANGE(result.ranges, br, 1u, 2u); TEST_RANGE(result.ranges, bar, 2u, 3u); + + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 0)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 1)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 2)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 3)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*bar_node, 0)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*bar_node, 1)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*bar_node, 2)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*bar_node, 3)); + + EXPECT_EQ(0u, GetTextContentOffset(*foo_node, 0)); + EXPECT_EQ(1u, GetTextContentOffset(*foo_node, 1)); + EXPECT_EQ(2u, GetTextContentOffset(*foo_node, 2)); + EXPECT_EQ(3u, GetTextContentOffset(*foo_node, 3)); + EXPECT_EQ(4u, GetTextContentOffset(*bar_node, 0)); + EXPECT_EQ(5u, GetTextContentOffset(*bar_node, 1)); + EXPECT_EQ(6u, GetTextContentOffset(*bar_node, 2)); + EXPECT_EQ(7u, GetTextContentOffset(*bar_node, 3)); } TEST_F(NGInlineNodeOffsetMappingTest, OneTextNodeWithCollapsedSpace) { SetupHtml("t", "<div id=t>foo bar</div>"); + const Node* node = layout_object_->GetNode(); const NGOffsetMappingResult& result = GetOffsetMapping(); ASSERT_EQ(3u, result.units.size()); @@ -139,6 +219,26 @@ ASSERT_EQ(1u, result.ranges.size()); TEST_RANGE(result.ranges, ToLayoutText(layout_object_), 0u, 3u); + + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*node, 0)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*node, 1)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*node, 2)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*node, 3)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*node, 4)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*node, 5)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*node, 6)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*node, 7)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*node, 8)); + + EXPECT_EQ(0u, GetTextContentOffset(*node, 0)); + EXPECT_EQ(1u, GetTextContentOffset(*node, 1)); + EXPECT_EQ(2u, GetTextContentOffset(*node, 2)); + EXPECT_EQ(3u, GetTextContentOffset(*node, 3)); + EXPECT_EQ(4u, GetTextContentOffset(*node, 4)); + EXPECT_EQ(4u, GetTextContentOffset(*node, 5)); + EXPECT_EQ(5u, GetTextContentOffset(*node, 6)); + EXPECT_EQ(6u, GetTextContentOffset(*node, 7)); + EXPECT_EQ(7u, GetTextContentOffset(*node, 8)); } TEST_F(NGInlineNodeOffsetMappingTest, FullyCollapsedWhiteSpaceNode) { @@ -151,6 +251,9 @@ const LayoutText* foo = GetLayoutTextUnder("s1"); const LayoutText* bar = GetLayoutTextUnder("s2"); const LayoutText* space = ToLayoutText(layout_object_->NextSibling()); + const Node* foo_node = foo->GetNode(); + const Node* bar_node = bar->GetNode(); + const Node* space_node = space->GetNode(); const NGOffsetMappingResult& result = GetOffsetMapping(); ASSERT_EQ(3u, result.units.size()); @@ -165,12 +268,38 @@ TEST_RANGE(result.ranges, foo, 0u, 1u); TEST_RANGE(result.ranges, space, 1u, 2u); TEST_RANGE(result.ranges, bar, 2u, 3u); + + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 0)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 1)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 2)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 3)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 4)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*space_node, 0)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*space_node, 1)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*bar_node, 0)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*bar_node, 1)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*bar_node, 2)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*bar_node, 3)); + + EXPECT_EQ(0u, GetTextContentOffset(*foo_node, 0)); + EXPECT_EQ(1u, GetTextContentOffset(*foo_node, 1)); + EXPECT_EQ(2u, GetTextContentOffset(*foo_node, 2)); + EXPECT_EQ(3u, GetTextContentOffset(*foo_node, 3)); + EXPECT_EQ(4u, GetTextContentOffset(*foo_node, 4)); + EXPECT_EQ(4u, GetTextContentOffset(*space_node, 0)); + EXPECT_EQ(4u, GetTextContentOffset(*space_node, 1)); + EXPECT_EQ(4u, GetTextContentOffset(*bar_node, 0)); + EXPECT_EQ(5u, GetTextContentOffset(*bar_node, 1)); + EXPECT_EQ(6u, GetTextContentOffset(*bar_node, 2)); + EXPECT_EQ(7u, GetTextContentOffset(*bar_node, 3)); } TEST_F(NGInlineNodeOffsetMappingTest, ReplacedElement) { SetupHtml("t", "<div id=t>foo <img> bar</div>"); const LayoutText* foo = ToLayoutText(layout_object_); const LayoutText* bar = ToLayoutText(foo->NextSibling()->NextSibling()); + const Node* foo_node = foo->GetNode(); + const Node* bar_node = bar->GetNode(); const NGOffsetMappingResult& result = GetOffsetMapping(); ASSERT_EQ(2u, result.units.size()); @@ -182,6 +311,28 @@ ASSERT_EQ(2u, result.ranges.size()); TEST_RANGE(result.ranges, foo, 0u, 1u); TEST_RANGE(result.ranges, bar, 1u, 2u); + + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 0)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 1)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 2)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 3)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 4)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*bar_node, 0)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*bar_node, 1)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*bar_node, 2)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*bar_node, 3)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*bar_node, 4)); + + EXPECT_EQ(0u, GetTextContentOffset(*foo_node, 0)); + EXPECT_EQ(1u, GetTextContentOffset(*foo_node, 1)); + EXPECT_EQ(2u, GetTextContentOffset(*foo_node, 2)); + EXPECT_EQ(3u, GetTextContentOffset(*foo_node, 3)); + EXPECT_EQ(4u, GetTextContentOffset(*foo_node, 4)); + EXPECT_EQ(5u, GetTextContentOffset(*bar_node, 0)); + EXPECT_EQ(6u, GetTextContentOffset(*bar_node, 1)); + EXPECT_EQ(7u, GetTextContentOffset(*bar_node, 2)); + EXPECT_EQ(8u, GetTextContentOffset(*bar_node, 3)); + EXPECT_EQ(9u, GetTextContentOffset(*bar_node, 4)); } TEST_F(NGInlineNodeOffsetMappingTest, FirstLetter) { @@ -196,6 +347,7 @@ ->GetFirstLetterPseudoElement() ->GetLayoutObject() ->SlowFirstChild()); + const Node* foo_node = div->firstChild(); const NGOffsetMappingResult& result = GetOffsetMapping(); ASSERT_EQ(2u, result.units.size()); @@ -207,6 +359,14 @@ ASSERT_EQ(2u, result.ranges.size()); TEST_RANGE(result.ranges, first_letter, 0u, 1u); TEST_RANGE(result.ranges, remaining_text, 1u, 2u); + + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 0)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*foo_node, 1)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*foo_node, 2)); + + EXPECT_EQ(0u, GetTextContentOffset(*foo_node, 0)); + EXPECT_EQ(1u, GetTextContentOffset(*foo_node, 1)); + EXPECT_EQ(2u, GetTextContentOffset(*foo_node, 2)); } TEST_F(NGInlineNodeOffsetMappingTest, FirstLetterWithLeadingSpace) { @@ -221,6 +381,7 @@ ->GetFirstLetterPseudoElement() ->GetLayoutObject() ->SlowFirstChild()); + const Node* foo_node = div->firstChild(); const NGOffsetMappingResult& result = GetOffsetMapping(); ASSERT_EQ(3u, result.units.size()); @@ -234,6 +395,18 @@ ASSERT_EQ(2u, result.ranges.size()); TEST_RANGE(result.ranges, first_letter, 0u, 2u); TEST_RANGE(result.ranges, remaining_text, 2u, 3u); + + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 0)); + EXPECT_EQ(&result.units[0], GetUnitForDOMOffset(*foo_node, 1)); + EXPECT_EQ(&result.units[1], GetUnitForDOMOffset(*foo_node, 2)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*foo_node, 3)); + EXPECT_EQ(&result.units[2], GetUnitForDOMOffset(*foo_node, 4)); + + EXPECT_EQ(0u, GetTextContentOffset(*foo_node, 0)); + EXPECT_EQ(0u, GetTextContentOffset(*foo_node, 1)); + EXPECT_EQ(0u, GetTextContentOffset(*foo_node, 2)); + EXPECT_EQ(1u, GetTextContentOffset(*foo_node, 3)); + EXPECT_EQ(2u, GetTextContentOffset(*foo_node, 4)); } } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping_builder.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping_builder.cc index 46c551c2..ee14ca2 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping_builder.cc +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping_builder.cc
@@ -126,16 +126,15 @@ continue; } - NGOffsetMappingUnit unit; - unsigned end; - std::tie(unit.type, end) = - GetMappingUnitTypeAndEnd(mapping_, annotation_, start); - unit.owner = current_node; - unit.dom_start = start - inline_start + current_node->TextStartOffset(); - unit.dom_end = end - inline_start + current_node->TextStartOffset(); - unit.text_content_start = mapping_[start]; - unit.text_content_end = mapping_[end]; - result.units.push_back(unit); + auto type_and_end = GetMappingUnitTypeAndEnd(mapping_, annotation_, start); + NGOffsetMappingUnitType type = type_and_end.first; + unsigned end = type_and_end.second; + unsigned dom_start = start - inline_start + current_node->TextStartOffset(); + unsigned dom_end = end - inline_start + current_node->TextStartOffset(); + unsigned text_content_start = mapping_[start]; + unsigned text_content_end = mapping_[end]; + result.units.emplace_back(type, current_node, dom_start, dom_end, + text_content_start, text_content_end); start = end; }
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping_result.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping_result.cc new file mode 100644 index 0000000..9814119 --- /dev/null +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping_result.cc
@@ -0,0 +1,22 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "core/layout/ng/inline/ng_offset_mapping_result.h" + +namespace blink { + +NGOffsetMappingUnit::NGOffsetMappingUnit(NGOffsetMappingUnitType type, + const LayoutText* owner, + unsigned dom_start, + unsigned dom_end, + unsigned text_content_start, + unsigned text_content_end) + : type_(type), + owner_(owner), + dom_start_(dom_start), + dom_end_(dom_end), + text_content_start_(text_content_start), + text_content_end_(text_content_end) {} + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping_result.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping_result.h index e3acef1..097850f 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping_result.h +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_offset_mapping_result.h
@@ -6,6 +6,7 @@ #define NGOffsetMappingResult_h #include "platform/wtf/Allocator.h" +#include "platform/wtf/HashMap.h" #include "platform/wtf/Vector.h" namespace blink { @@ -28,10 +29,26 @@ // |text_content_end > text_content_start + 1|, indicating that the character // in the dom range is expanded into multiple characters. // See design doc https://goo.gl/CJbxky for details. -struct NGOffsetMappingUnit { +class NGOffsetMappingUnit { DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); - NGOffsetMappingUnitType type = NGOffsetMappingUnitType::kIdentity; + public: + NGOffsetMappingUnit(NGOffsetMappingUnitType, + const LayoutText*, + unsigned dom_start, + unsigned dom_end, + unsigned text_content_start, + unsigned text_content_end); + + NGOffsetMappingUnitType GetType() const { return type_; } + const LayoutText* GetOwner() const { return owner_; } + unsigned DOMStart() const { return dom_start_; } + unsigned DOMEnd() const { return dom_end_; } + unsigned TextContentStart() const { return text_content_start_; } + unsigned TextContentEnd() const { return text_content_end_; } + + private: + const NGOffsetMappingUnitType type_ = NGOffsetMappingUnitType::kIdentity; // Ideally, we should store |Node| as owner, instead of |LayoutObject|. // However, we need to ensure the invariant that, units of the same owner are @@ -41,12 +58,12 @@ // the node. // TODO(xiaochengh): Figure out if this the issue really exists. If not, then // we should use |Node| as owner. - const LayoutText* owner = nullptr; + const LayoutText* const owner_; - unsigned dom_start = 0; - unsigned dom_end = 0; - unsigned text_content_start = 0; - unsigned text_content_end = 0; + const unsigned dom_start_; + const unsigned dom_end_; + const unsigned text_content_start_; + const unsigned text_content_end_; }; // An NGOffsetMappingResult stores the units of a LayoutNGBlockFlow in sorted
diff --git a/third_party/WebKit/Source/devtools/front_end/console_model/ConsoleModel.js b/third_party/WebKit/Source/devtools/front_end/console_model/ConsoleModel.js index d79b3503..0f7ea69 100644 --- a/third_party/WebKit/Source/devtools/front_end/console_model/ConsoleModel.js +++ b/third_party/WebKit/Source/devtools/front_end/console_model/ConsoleModel.js
@@ -543,9 +543,6 @@ if (!msg) return false; - if (this._exceptionId || msg._exceptionId) - return false; - if (!this._isEqualStackTraces(this.stackTrace, msg.stackTrace)) return false; @@ -554,9 +551,13 @@ return false; for (var i = 0; i < msg.parameters.length; ++i) { - // Never treat objects as equal - their properties might change over time. - if (this.parameters[i].type !== msg.parameters[i].type || msg.parameters[i].type === 'object' || - this.parameters[i].value !== msg.parameters[i].value) + // Never treat objects as equal - their properties might change over time. Errors can be treated as equal + // since they are always formatted as strings. + if (msg.parameters[i].type === 'object' && msg.parameters[i].subtype !== 'error') + return false; + if (this.parameters[i].type !== msg.parameters[i].type || + this.parameters[i].value !== msg.parameters[i].value || + this.parameters[i].description !== msg.parameters[i].description) return false; } }
diff --git a/third_party/WebKit/Source/modules/ModulesInitializer.cpp b/third_party/WebKit/Source/modules/ModulesInitializer.cpp index a2cb33e..9d6abec 100644 --- a/third_party/WebKit/Source/modules/ModulesInitializer.cpp +++ b/third_party/WebKit/Source/modules/ModulesInitializer.cpp
@@ -8,6 +8,7 @@ #include "core/EventTypeNames.h" #include "core/css/CSSPaintImageGenerator.h" #include "core/dom/Document.h" +#include "core/editing/suggestion/TextSuggestionBackendImpl.h" #include "core/exported/WebSharedWorkerImpl.h" #include "core/frame/LocalFrame.h" #include "core/frame/Settings.h" @@ -137,6 +138,8 @@ // frame()->document(). frame.GetInterfaceRegistry()->AddInterface(WTF::Bind( &AppBannerController::BindMojoRequest, WrapWeakPersistent(&frame))); + frame.GetInterfaceRegistry()->AddInterface(WTF::Bind( + &TextSuggestionBackendImpl::Create, WrapWeakPersistent(&frame))); } void ModulesInitializer::InstallSupplements(LocalFrame& frame) const {
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn index 93856b42..5f9d368 100644 --- a/third_party/WebKit/Source/platform/BUILD.gn +++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -1637,6 +1637,8 @@ "testing/HistogramTester.cpp", "testing/HistogramTester.h", "testing/MessageLoopForMojo.h", + "testing/MockWebCrypto.cpp", + "testing/MockWebCrypto.h", "testing/PaintPrinters.cpp", "testing/PaintPrinters.h", "testing/PaintPropertyTestHelpers.h",
diff --git a/third_party/WebKit/Source/platform/loader/BUILD.gn b/third_party/WebKit/Source/platform/loader/BUILD.gn index 005bd3e..63d23ba 100644 --- a/third_party/WebKit/Source/platform/loader/BUILD.gn +++ b/third_party/WebKit/Source/platform/loader/BUILD.gn
@@ -156,6 +156,7 @@ # Source files that can be called from blink_platform_unittests and # webkit_unit_tests. sources = [ + "testing/CryptoTestingPlatformSupport.h", "testing/FetchTestingPlatformSupport.cpp", "testing/FetchTestingPlatformSupport.h", "testing/MockFetchContext.h",
diff --git a/third_party/WebKit/Source/platform/loader/testing/CryptoTestingPlatformSupport.h b/third_party/WebKit/Source/platform/loader/testing/CryptoTestingPlatformSupport.h new file mode 100644 index 0000000..574a272c --- /dev/null +++ b/third_party/WebKit/Source/platform/loader/testing/CryptoTestingPlatformSupport.h
@@ -0,0 +1,51 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CryptoTestingPlatformSupport_h +#define CryptoTestingPlatformSupport_h + +#include <memory> +#include "platform/loader/testing/FetchTestingPlatformSupport.h" +#include "platform/testing/MockWebCrypto.h" +#include "platform/wtf/Allocator.h" + +namespace blink { + +class CryptoTestingPlatformSupport : public FetchTestingPlatformSupport { + public: + CryptoTestingPlatformSupport() {} + ~CryptoTestingPlatformSupport() override {} + + // Platform: + WebCrypto* Crypto() override { return mock_web_crypto_.get(); } + + class SetMockCryptoScope final { + STACK_ALLOCATED(); + + public: + explicit SetMockCryptoScope(CryptoTestingPlatformSupport& platform) + : platform_(platform) { + DCHECK(!platform_.Crypto()); + platform_.SetMockCrypto(MockWebCrypto::Create()); + } + ~SetMockCryptoScope() { platform_.SetMockCrypto(nullptr); } + MockWebCrypto& MockCrypto() { return *platform_.mock_web_crypto_; } + + private: + CryptoTestingPlatformSupport& platform_; + }; + + private: + void SetMockCrypto(std::unique_ptr<MockWebCrypto> crypto) { + mock_web_crypto_ = std::move(crypto); + } + + std::unique_ptr<MockWebCrypto> mock_web_crypto_; + + DISALLOW_COPY_AND_ASSIGN(CryptoTestingPlatformSupport); +}; + +} // namespace blink + +#endif
diff --git a/third_party/WebKit/Source/platform/testing/MockWebCrypto.cpp b/third_party/WebKit/Source/platform/testing/MockWebCrypto.cpp new file mode 100644 index 0000000..4a51c5a --- /dev/null +++ b/third_party/WebKit/Source/platform/testing/MockWebCrypto.cpp
@@ -0,0 +1,70 @@ +// 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 "platform/testing/MockWebCrypto.h" + +#include <cstring> +#include <memory> +#include <string> +#include "testing/gtest/include/gtest/gtest.h" + +namespace blink { + +using ::testing::_; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::SetArgReferee; + +// MemEq(p, len) expects memcmp(arg, p, len) == 0, where |arg| is the argument +// to be matched. +MATCHER_P2(MemEq, + p, + len, + std::string("pointing to memory") + (negation ? " not" : "") + + " equal to \"" + std::string(static_cast<const char*>(p), len) + + "\" (length=" + ::testing::PrintToString(len) + ")") { + return memcmp(arg, p, len) == 0; +} + +void MockWebCryptoDigestor::ExpectConsumeAndFinish(const void* input_data, + unsigned input_length, + void* output_data, + unsigned output_length) { + InSequence s; + + // Consume should be called with a memory region equal to |input_data|. + EXPECT_CALL(*this, Consume(MemEq(input_data, input_length), input_length)) + .WillOnce(Return(true)); + + // Finish(unsigned char*& result_data, unsigned& result_data_size) { + // result_data = output_data; + // result_data_size = output_length; + // return true; + // } + EXPECT_CALL(*this, Finish(_, _)) + .WillOnce( + DoAll(SetArgReferee<0>(static_cast<unsigned char*>(output_data)), + SetArgReferee<1>(output_length), Return(true))); +} + +MockWebCryptoDigestorFactory::MockWebCryptoDigestorFactory( + const void* input_data, + unsigned input_length, + void* output_data, + unsigned output_length) + : input_data_(input_data), + input_length_(input_length), + output_data_(output_data), + output_length_(output_length) {} + +MockWebCryptoDigestor* MockWebCryptoDigestorFactory::Create() { + std::unique_ptr<MockWebCryptoDigestor> digestor( + MockWebCryptoDigestor::Create()); + digestor->ExpectConsumeAndFinish(input_data_, input_length_, output_data_, + output_length_); + return digestor.release(); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/platform/testing/MockWebCrypto.h b/third_party/WebKit/Source/platform/testing/MockWebCrypto.h new file mode 100644 index 0000000..309aa038 --- /dev/null +++ b/third_party/WebKit/Source/platform/testing/MockWebCrypto.h
@@ -0,0 +1,156 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MockWebCrypto_h +#define MockWebCrypto_h + +#include <memory> +#include "platform/wtf/Allocator.h" +#include "public/platform/WebCrypto.h" +#include "public/platform/WebCryptoKeyAlgorithm.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace blink { + +class MockWebCrypto : public WebCrypto { + public: + ~MockWebCrypto() override {} + + static std::unique_ptr<MockWebCrypto> Create() { + return std::unique_ptr<MockWebCrypto>( + new ::testing::StrictMock<MockWebCrypto>()); + } + + MOCK_METHOD4(Encrypt, + void(const WebCryptoAlgorithm&, + const WebCryptoKey&, + WebVector<unsigned char>, + WebCryptoResult)); + MOCK_METHOD4(Decrypt, + void(const WebCryptoAlgorithm&, + const WebCryptoKey&, + WebVector<unsigned char>, + WebCryptoResult)); + MOCK_METHOD4(Sign, + void(const WebCryptoAlgorithm&, + const WebCryptoKey&, + WebVector<unsigned char>, + WebCryptoResult)); + MOCK_METHOD5(VerifySignature, + void(const WebCryptoAlgorithm&, + const WebCryptoKey&, + WebVector<unsigned char>, + WebVector<unsigned char>, + WebCryptoResult)); + MOCK_METHOD3(Digest, + void(const WebCryptoAlgorithm&, + WebVector<unsigned char>, + WebCryptoResult)); + MOCK_METHOD4(GenerateKey, + void(const WebCryptoAlgorithm&, + bool, + WebCryptoKeyUsageMask, + WebCryptoResult)); + MOCK_METHOD6(ImportKey, + void(WebCryptoKeyFormat, + WebVector<unsigned char>, + const WebCryptoAlgorithm&, + bool, + WebCryptoKeyUsageMask, + WebCryptoResult)); + MOCK_METHOD3(ExportKey, + void(WebCryptoKeyFormat, const WebCryptoKey&, WebCryptoResult)); + MOCK_METHOD5(WrapKey, + void(WebCryptoKeyFormat, + const WebCryptoKey&, + const WebCryptoKey&, + const WebCryptoAlgorithm&, + WebCryptoResult)); + MOCK_METHOD8(UnwrapKey, + void(WebCryptoKeyFormat, + WebVector<unsigned char>, + const WebCryptoKey&, + const WebCryptoAlgorithm&, + const WebCryptoAlgorithm&, + bool, + WebCryptoKeyUsageMask, + WebCryptoResult)); + MOCK_METHOD4(DeriveBits, + void(const WebCryptoAlgorithm&, + const WebCryptoKey&, + unsigned, + WebCryptoResult)); + MOCK_METHOD7(DeriveKey, + void(const WebCryptoAlgorithm&, + const WebCryptoKey&, + const WebCryptoAlgorithm&, + const WebCryptoAlgorithm&, + bool, + WebCryptoKeyUsageMask, + WebCryptoResult)); + MOCK_METHOD1(CreateDigestorProxy, WebCryptoDigestor*(WebCryptoAlgorithmId)); + MOCK_METHOD7(DeserializeKeyForClone, + bool(const WebCryptoKeyAlgorithm&, + WebCryptoKeyType, + bool, + WebCryptoKeyUsageMask, + const unsigned char*, + unsigned, + WebCryptoKey&)); + MOCK_METHOD2(SerializeKeyForClone, + bool(const WebCryptoKey&, WebVector<unsigned char>&)); + + protected: + MockWebCrypto() {} + + std::unique_ptr<WebCryptoDigestor> CreateDigestor( + WebCryptoAlgorithmId id) override { + return std::unique_ptr<WebCryptoDigestor>(CreateDigestorProxy(id)); + } + + DISALLOW_COPY_AND_ASSIGN(MockWebCrypto); +}; + +class MockWebCryptoDigestor : public WebCryptoDigestor { + public: + ~MockWebCryptoDigestor() override {} + + static MockWebCryptoDigestor* Create() { + return new ::testing::StrictMock<MockWebCryptoDigestor>(); + } + + void ExpectConsumeAndFinish(const void* input_data, + unsigned input_length, + void* output_data, + unsigned output_length); + + MOCK_METHOD2(Consume, bool(const unsigned char*, unsigned)); + MOCK_METHOD2(Finish, bool(unsigned char*&, unsigned&)); + + protected: + MockWebCryptoDigestor() {} + + DISALLOW_COPY_AND_ASSIGN(MockWebCryptoDigestor); +}; + +class MockWebCryptoDigestorFactory final { + STACK_ALLOCATED(); + + public: + MockWebCryptoDigestorFactory(const void* input_data, + unsigned input_length, + void* output_data, + unsigned output_length); + MockWebCryptoDigestor* Create(); + + private: + const void* const input_data_; + const unsigned input_length_; + void* const output_data_; + const unsigned output_length_; +}; + +} // namespace blink + +#endif
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn index 037f473..0d21c09 100644 --- a/third_party/WebKit/public/BUILD.gn +++ b/third_party/WebKit/public/BUILD.gn
@@ -781,6 +781,8 @@ visibility = [ ":mojo_bindings" ] visibility_blink = [ ":mojo_bindings_blink" ] sources = [ + "platform/input_host.mojom", + "platform/input_messages.mojom", "platform/modules/document_metadata/copyless_paste.mojom", "platform/modules/installation/installation.mojom", "platform/modules/installedapp/installed_app_provider.mojom",
diff --git a/third_party/WebKit/public/platform/input_host.mojom b/third_party/WebKit/public/platform/input_host.mojom new file mode 100644 index 0000000..7a4ae78 --- /dev/null +++ b/third_party/WebKit/public/platform/input_host.mojom
@@ -0,0 +1,14 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module blink.mojom; + +struct SpellCheckSuggestion { + string suggestion; +}; + +interface TextSuggestionHost { + StartSpellCheckMenuTimer(); + ShowSpellCheckSuggestionMenu(double caret_x, double caret_y, string marked_text, array<SpellCheckSuggestion> suggestions); +};
diff --git a/third_party/WebKit/public/platform/input_messages.mojom b/third_party/WebKit/public/platform/input_messages.mojom new file mode 100644 index 0000000..54c3fe8 --- /dev/null +++ b/third_party/WebKit/public/platform/input_messages.mojom
@@ -0,0 +1,13 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module blink.mojom; + +interface TextSuggestionBackend { + ApplySpellCheckSuggestion(string suggestion); + DeleteActiveSuggestionRange(); + NewWordAddedToDictionary(string suggestion); + SpellCheckMenuTimeoutCallback(); + SuggestionMenuClosed(); +};
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index d54d7a28..206bb21 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -2861,6 +2861,24 @@ <int value="6" label="Omnibox suggestion"/> </enum> +<enum name="BookmarkManagerCommand"> + <int value="0" label="Edit"/> + <int value="1" label="Copy URL"/> + <int value="2" label="Show in folder"/> + <int value="3" label="Delete"/> + <int value="4" label="Open in new tab"/> + <int value="5" label="Open in new window"/> + <int value="6" label="Open in incognito"/> + <int value="7" label="Undo"/> + <int value="8" label="Redo"/> + <int value="9" label="Open (double click/enter)"/> + <int value="10" label="Select all"/> + <int value="11" label="Deselect all"/> + <int value="12" label="Copy"/> + <int value="13" label="Cut"/> + <int value="14" label="Paste"/> +</enum> + <enum name="BookmarksEntryPoint"> <int value="0" label="Accelerator(Ctrl+D)"/> <int value="1" label="Gesture"/> @@ -37451,7 +37469,7 @@ <enum name="TriggerHelpUIResult"> <int value="0" label="SUCCESS"/> <int value="1" label="FAILURE"/> - <int value="2" label="FAILURE_MODEL_NOT_READY"/> + <int value="2" label="FAILURE_EVENT_MODEL_NOT_READY"/> <int value="3" label="FAILURE_CURRENTLY_SHOWING"/> <int value="4" label="FAILURE_FEATURE_DISABLED"/> <int value="5" label="FAILURE_CONFIG_INVALID"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index ab3d1c39..cf56add6 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -6767,6 +6767,23 @@ </summary> </histogram> +<histogram name="BookmarkManager.CommandExecuted" enum="BookmarkManagerCommand"> + <owner>calamity@chromium.org</owner> + <summary> + Logs when a user action triggers a command in the bookmark manager. Commands + can be triggered by keyboard shortcuts, menu items or other buttons in the + UI. + </summary> +</histogram> + +<histogram name="BookmarkManager.CommandExecutedFromKeyboard" + enum="BookmarkManagerCommand"> + <owner>calamity@chromium.org</owner> + <summary> + Logs when a keyboard shortcut triggers a command in the bookmark manager. + </summary> +</histogram> + <histogram name="BookmarkManager.NumDragged" units="bookmarks"> <owner>calamity@chromium.org</owner> <summary>
diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc index c681f4d..7e2a65b 100644 --- a/ui/app_list/views/app_list_view.cc +++ b/ui/app_list/views/app_list_view.cc
@@ -40,13 +40,11 @@ #include "ui/gfx/path.h" #include "ui/gfx/skia_util.h" #include "ui/views/bubble/bubble_frame_view.h" -#include "ui/views/bubble/bubble_window_targeter.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/views_delegate.h" #include "ui/views/widget/widget.h" -#include "ui/wm/core/masked_window_targeter.h" #include "ui/wm/core/shadow_types.h" namespace app_list { @@ -132,27 +130,6 @@ DISALLOW_COPY_AND_ASSIGN(AppListOverlayView); }; -// An event targeter for the search box widget which will ignore events that -// are on the search box's shadow. -class SearchBoxWindowTargeter : public wm::MaskedWindowTargeter { - public: - explicit SearchBoxWindowTargeter(views::View* search_box) - : wm::MaskedWindowTargeter(search_box->GetWidget()->GetNativeWindow()), - search_box_(search_box) {} - ~SearchBoxWindowTargeter() override {} - - private: - // wm::MaskedWindowTargeter: - bool GetHitTestMask(aura::Window* window, gfx::Path* mask) const override { - mask->addRect(gfx::RectToSkRect(search_box_->GetContentsBounds())); - return true; - } - - views::View* search_box_; - - DISALLOW_COPY_AND_ASSIGN(SearchBoxWindowTargeter); -}; - } // namespace // An animation observer to hide the view at the end of the animation. @@ -441,11 +418,6 @@ search_box_widget_->SetFocusTraversableParent( GetWidget()->GetFocusTraversable()); - // Mouse events on the search box shadow should not be captured. - aura::Window* window = search_box_widget_->GetNativeWindow(); - window->SetEventTargeter( - base::MakeUnique<SearchBoxWindowTargeter>(search_box_view_)); - app_list_main_view_->contents_view()->Layout(); } @@ -491,9 +463,6 @@ SetBubbleArrow(views::BubbleBorder::FLOAT); // We can now create the internal widgets. - aura::Window* window = GetWidget()->GetNativeWindow(); - window->SetEventTargeter(base::MakeUnique<views::BubbleWindowTargeter>(this)); - const int kOverlayCornerRadius = GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(); overlay_view_ = new AppListOverlayView(kOverlayCornerRadius);
diff --git a/ui/aura/window_targeter.cc b/ui/aura/window_targeter.cc index 2ad7266..cb78d8c 100644 --- a/ui/aura/window_targeter.cc +++ b/ui/aura/window_targeter.cc
@@ -27,6 +27,21 @@ EventLocationInsideBounds(window, event); } +bool WindowTargeter::GetHitTestRects(Window* window, + gfx::Rect* hit_test_rect_mouse, + gfx::Rect* hit_test_rect_touch) const { + DCHECK(hit_test_rect_mouse); + DCHECK(hit_test_rect_touch); + *hit_test_rect_mouse = *hit_test_rect_touch = gfx::Rect(window->bounds()); + + if (ShouldUseExtendedBounds(window)) { + hit_test_rect_mouse->Inset(mouse_extend_); + hit_test_rect_touch->Inset(touch_extend_); + } + + return true; +} + Window* WindowTargeter::FindTargetInRootWindow(Window* root_window, const ui::LocatedEvent& event) { DCHECK_EQ(root_window, root_window->GetRootWindow()); @@ -157,15 +172,19 @@ return mouse_rect.Contains(point); } -bool WindowTargeter::GetHitTestRects(Window* window, - gfx::Rect* hit_test_rect_mouse, - gfx::Rect* hit_test_rect_touch) const { - DCHECK(hit_test_rect_mouse); - DCHECK(hit_test_rect_touch); - *hit_test_rect_mouse = *hit_test_rect_touch = gfx::Rect(window->bounds()); +bool WindowTargeter::ShouldUseExtendedBounds(const aura::Window* window) const { return true; } +void WindowTargeter::OnSetInsets() {} + +void WindowTargeter::SetInsets(const gfx::Insets& mouse_extend, + const gfx::Insets& touch_extend) { + mouse_extend_ = mouse_extend; + touch_extend_ = touch_extend; + OnSetInsets(); +} + Window* WindowTargeter::FindTargetForKeyEvent(Window* window, const ui::KeyEvent& key) { Window* root_window = window->GetRootWindow();
diff --git a/ui/aura/window_targeter.h b/ui/aura/window_targeter.h index 3bb46776..a1c2c0b 100644 --- a/ui/aura/window_targeter.h +++ b/ui/aura/window_targeter.h
@@ -8,6 +8,7 @@ #include "base/macros.h" #include "ui/aura/aura_export.h" #include "ui/events/event_targeter.h" +#include "ui/gfx/geometry/insets.h" namespace gfx { class Rect; @@ -78,12 +79,30 @@ virtual bool EventLocationInsideBounds(Window* target, const ui::LocatedEvent& event) const; + // Returns true if the hit testing (GetHitTestRects()) should use the + // extended bounds. + virtual bool ShouldUseExtendedBounds(const aura::Window* window) const; + + // Called after the hit-test area has been extended with SetInsets(). + virtual void OnSetInsets(); + + // Sets additional mouse and touch insets that are factored into the hit-test + // regions returned by GetHitTestRects. + void SetInsets(const gfx::Insets& mouse_extend, + const gfx::Insets& touch_extend); + + const gfx::Insets& mouse_extend() const { return mouse_extend_; } + const gfx::Insets& touch_extend() const { return touch_extend_; } + private: Window* FindTargetForKeyEvent(Window* root_window, const ui::KeyEvent& event); Window* FindTargetForNonKeyEvent(Window* root_window, ui::Event* event); Window* FindTargetForLocatedEventRecursively(Window* root_window, ui::LocatedEvent* event); + gfx::Insets mouse_extend_; + gfx::Insets touch_extend_; + DISALLOW_COPY_AND_ASSIGN(WindowTargeter); };
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn index f08e2b9..76de06b 100644 --- a/ui/views/BUILD.gn +++ b/ui/views/BUILD.gn
@@ -542,7 +542,6 @@ "accessibility/ax_view_obj_wrapper.h", "accessibility/ax_widget_obj_wrapper.h", "accessibility/ax_window_obj_wrapper.h", - "bubble/bubble_window_targeter.h", "bubble/tray_bubble_view.h", "controls/menu/menu_pre_target_handler.h", "controls/native/native_view_host_aura.h", @@ -575,7 +574,6 @@ "accessibility/ax_view_obj_wrapper.cc", "accessibility/ax_widget_obj_wrapper.cc", "accessibility/ax_window_obj_wrapper.cc", - "bubble/bubble_window_targeter.cc", "bubble/tray_bubble_view.cc", "controls/menu/display_change_listener_aura.cc", "controls/menu/menu_pre_target_handler.cc", @@ -889,7 +887,6 @@ "bubble/bubble_border_unittest.cc", "bubble/bubble_dialog_delegate_unittest.cc", "bubble/bubble_frame_view_unittest.cc", - "bubble/bubble_window_targeter_unittest.cc", "cocoa/bridged_native_widget_unittest.mm", "cocoa/cocoa_mouse_capture_unittest.mm", "cocoa/drag_drop_client_mac_unittest.mm", @@ -1038,10 +1035,7 @@ if (is_mac) { # views_unittests not yet compiling on Mac. http://crbug.com/378134 - sources -= [ - "bubble/bubble_window_targeter_unittest.cc", - "controls/native/native_view_host_unittest.cc", - ] + sources -= [ "controls/native/native_view_host_unittest.cc" ] public_deps += [ "//ui/accelerated_widget_mac" ] } }
diff --git a/ui/views/bubble/bubble_window_targeter.cc b/ui/views/bubble/bubble_window_targeter.cc deleted file mode 100644 index 8fa0da8..0000000 --- a/ui/views/bubble/bubble_window_targeter.cc +++ /dev/null
@@ -1,29 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/bubble/bubble_window_targeter.h" - -#include "ui/aura/window.h" -#include "ui/gfx/path.h" -#include "ui/gfx/skia_util.h" -#include "ui/views/bubble/bubble_dialog_delegate.h" -#include "ui/views/bubble/bubble_frame_view.h" - -namespace views { - -BubbleWindowTargeter::BubbleWindowTargeter(BubbleDialogDelegateView* bubble) - : wm::MaskedWindowTargeter(bubble->GetWidget()->GetNativeView()), - bubble_(bubble) {} - -BubbleWindowTargeter::~BubbleWindowTargeter() { -} - -bool BubbleWindowTargeter::GetHitTestMask(aura::Window* window, - gfx::Path* mask) const { - mask->addRect( - gfx::RectToSkRect(bubble_->GetBubbleFrameView()->GetContentsBounds())); - return true; -} - -} // namespace views
diff --git a/ui/views/bubble/bubble_window_targeter.h b/ui/views/bubble/bubble_window_targeter.h deleted file mode 100644 index 7b7f87b..0000000 --- a/ui/views/bubble/bubble_window_targeter.h +++ /dev/null
@@ -1,34 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/macros.h" -#include "ui/views/views_export.h" -#include "ui/wm/core/masked_window_targeter.h" - -namespace aura { -class Window; -} - -namespace views { - -class BubbleDialogDelegateView; - -// A convenient window-targeter that uses a mask based on the content-bounds of -// the bubble-frame. -class VIEWS_EXPORT BubbleWindowTargeter - : public NON_EXPORTED_BASE(wm::MaskedWindowTargeter) { - public: - explicit BubbleWindowTargeter(BubbleDialogDelegateView* bubble); - ~BubbleWindowTargeter() override; - - private: - // wm::MaskedWindowTargeter: - bool GetHitTestMask(aura::Window* window, gfx::Path* mask) const override; - - views::BubbleDialogDelegateView* bubble_; - - DISALLOW_COPY_AND_ASSIGN(BubbleWindowTargeter); -}; - -} // namespace views
diff --git a/ui/views/bubble/bubble_window_targeter_unittest.cc b/ui/views/bubble/bubble_window_targeter_unittest.cc deleted file mode 100644 index 975d68e82..0000000 --- a/ui/views/bubble/bubble_window_targeter_unittest.cc +++ /dev/null
@@ -1,123 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/bubble/bubble_window_targeter.h" - -#include "base/macros.h" -#include "ui/aura/window.h" -#include "ui/aura/window_event_dispatcher.h" -#include "ui/aura/window_tree_host.h" -#include "ui/events/event_utils.h" -#include "ui/views/bubble/bubble_border.h" -#include "ui/views/bubble/bubble_dialog_delegate.h" -#include "ui/views/test/views_test_base.h" -#include "ui/views/widget/widget.h" - -namespace views { - -namespace { - -class WidgetOwnsNativeBubble : public BubbleDialogDelegateView { - public: - WidgetOwnsNativeBubble(View* content, BubbleBorder::Arrow arrow) - : BubbleDialogDelegateView(content, arrow) {} - - ~WidgetOwnsNativeBubble() override {} - - private: - // BubbleDialogDelegateView: - void OnBeforeBubbleWidgetInit(Widget::InitParams* params, - Widget* widget) const override { - params->ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - } - - DISALLOW_COPY_AND_ASSIGN(WidgetOwnsNativeBubble); -}; - -} // namespace - -class BubbleWindowTargeterTest : public ViewsTestBase { - public: - BubbleWindowTargeterTest() - : bubble_delegate_(NULL) { - } - ~BubbleWindowTargeterTest() override {} - - void SetUp() override { - ViewsTestBase::SetUp(); - CreateAnchorWidget(); - CreateBubbleWidget(); - - anchor_widget()->Show(); - bubble_widget()->Show(); - } - - void TearDown() override { - bubble_delegate_ = NULL; - bubble_widget_.reset(); - anchor_.reset(); - ViewsTestBase::TearDown(); - } - - Widget* anchor_widget() { return anchor_.get(); } - Widget* bubble_widget() { return bubble_widget_.get(); } - BubbleDialogDelegateView* bubble_delegate() { return bubble_delegate_; } - - private: - void CreateAnchorWidget() { - anchor_.reset(new Widget()); - Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); - params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - anchor_->Init(params); - } - - void CreateBubbleWidget() { - bubble_delegate_ = new WidgetOwnsNativeBubble( - anchor_->GetContentsView(), BubbleBorder::NONE); - bubble_delegate_->set_color(SK_ColorGREEN); - bubble_widget_.reset( - BubbleDialogDelegateView::CreateBubble(bubble_delegate_)); - } - - std::unique_ptr<Widget> anchor_; - std::unique_ptr<Widget> bubble_widget_; - BubbleDialogDelegateView* bubble_delegate_; - - DISALLOW_COPY_AND_ASSIGN(BubbleWindowTargeterTest); -}; - -TEST_F(BubbleWindowTargeterTest, HitTest) { - aura::Window* root = bubble_widget()->GetNativeWindow()->GetRootWindow(); - ui::EventTargeter* targeter = - root->GetHost()->dispatcher()->GetDefaultEventTargeter(); - aura::Window* bubble_window = bubble_widget()->GetNativeWindow(); - gfx::Rect bubble_bounds = bubble_window->GetBoundsInRootWindow(); - - { - bubble_delegate()->set_margins(gfx::Insets()); - ui::MouseEvent move1(ui::ET_MOUSE_MOVED, bubble_bounds.origin(), - bubble_bounds.origin(), ui::EventTimeForNow(), - ui::EF_NONE, ui::EF_NONE); - EXPECT_EQ(bubble_window, targeter->FindTargetForEvent(root, &move1)); - } - { - bubble_delegate()->set_margins(gfx::Insets(20)); - ui::MouseEvent move1(ui::ET_MOUSE_MOVED, bubble_bounds.origin(), - bubble_bounds.origin(), ui::EventTimeForNow(), - ui::EF_NONE, ui::EF_NONE); - EXPECT_EQ(bubble_window, targeter->FindTargetForEvent(root, &move1)); - } - - bubble_window->SetEventTargeter(std::unique_ptr<ui::EventTargeter>( - new BubbleWindowTargeter(bubble_delegate()))); - { - bubble_delegate()->set_margins(gfx::Insets(20)); - ui::MouseEvent move1(ui::ET_MOUSE_MOVED, bubble_bounds.origin(), - bubble_bounds.origin(), ui::EventTimeForNow(), - ui::EF_NONE, ui::EF_NONE); - EXPECT_NE(bubble_window, targeter->FindTargetForEvent(root, &move1)); - } -} - -} // namespace views
diff --git a/ui/views/bubble/tray_bubble_view.cc b/ui/views/bubble/tray_bubble_view.cc index 33d429f..3841b892 100644 --- a/ui/views/bubble/tray_bubble_view.cc +++ b/ui/views/bubble/tray_bubble_view.cc
@@ -24,7 +24,6 @@ #include "ui/gfx/path.h" #include "ui/gfx/skia_util.h" #include "ui/views/bubble/bubble_frame_view.h" -#include "ui/views/bubble/bubble_window_targeter.h" #include "ui/views/layout/box_layout.h" #include "ui/views/painter.h" #include "ui/views/widget/widget.h" @@ -206,8 +205,6 @@ layer()->parent()->SetMaskLayer(bubble_content_mask_->layer()); GetWidget()->Show(); - GetWidget()->GetNativeWindow()->SetEventTargeter( - std::unique_ptr<ui::EventTargeter>(new BubbleWindowTargeter(this))); UpdateBubble(); ++g_current_tray_bubble_showing_count_;
diff --git a/ui/views/touchui/touch_selection_controller_impl.cc b/ui/views/touchui/touch_selection_controller_impl.cc index 0846f6a..4fa1a8a 100644 --- a/ui/views/touchui/touch_selection_controller_impl.cc +++ b/ui/views/touchui/touch_selection_controller_impl.cc
@@ -10,6 +10,7 @@ #include "ui/aura/client/cursor_client.h" #include "ui/aura/env.h" #include "ui/aura/window.h" +#include "ui/aura/window_targeter.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/rect.h" @@ -20,7 +21,6 @@ #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" #include "ui/wm/core/coordinate_conversion.h" -#include "ui/wm/core/masked_window_targeter.h" namespace { @@ -206,19 +206,19 @@ typedef TouchSelectionControllerImpl::EditingHandleView EditingHandleView; -class TouchHandleWindowTargeter : public wm::MaskedWindowTargeter { +// A WindowTargeter that shifts the hit-test target down - away from the text +// cursor and expanding the hit-test area just below the visible drag handle. +class TouchHandleWindowTargeter : public aura::WindowTargeter { public: - TouchHandleWindowTargeter(aura::Window* window, - EditingHandleView* handle_view); + TouchHandleWindowTargeter() = default; + ~TouchHandleWindowTargeter() override = default; - ~TouchHandleWindowTargeter() override {} + void SetHitTestOffset(int offset) { + const gfx::Insets insets(offset, 0, -offset, 0); + SetInsets(insets, insets); + } private: - // wm::MaskedWindowTargeter: - bool GetHitTestMask(aura::Window* window, gfx::Path* mask) const override; - - EditingHandleView* handle_view_; - DISALLOW_COPY_AND_ASSIGN(TouchHandleWindowTargeter); }; @@ -237,8 +237,8 @@ widget_.reset(CreateTouchSelectionPopupWidget(context, this)); aura::Window* window = widget_->GetNativeWindow(); - window->SetEventTargeter(std::unique_ptr<ui::EventTargeter>( - new TouchHandleWindowTargeter(window, this))); + targeter_ = new TouchHandleWindowTargeter(); + window->SetEventTargeter(std::unique_ptr<ui::EventTargeter>(targeter_)); // We are owned by the TouchSelectionControllerImpl. set_owned_by_client(); @@ -251,20 +251,6 @@ } // Overridden from views::WidgetDelegateView: - bool WidgetHasHitTestMask() const override { return true; } - - void GetWidgetHitTestMask(gfx::Path* mask) const override { - gfx::Size image_size = image_->Size(); - mask->addRect( - SkIntToScalar(0), - SkIntToScalar(selection_bound_.GetHeight() + - kSelectionHandleVerticalVisualOffset), - SkIntToScalar(image_size.width()) + 2 * kSelectionHandleHorizPadding, - SkIntToScalar(selection_bound_.GetHeight() + - kSelectionHandleVerticalVisualOffset + - image_size.height() + kSelectionHandleVertPadding)); - } - void DeleteDelegate() override { // We are owned and deleted by TouchSelectionControllerImpl. } @@ -366,19 +352,20 @@ SchedulePaint(); } - if (!is_visible) - return; + if (is_visible) { + selection_bound_.SetEdge(bound.edge_top(), bound.edge_bottom()); - selection_bound_.SetEdge(bound.edge_top(), bound.edge_bottom()); + widget_->SetBounds(GetSelectionWidgetBounds(selection_bound_)); - widget_->SetBounds(GetSelectionWidgetBounds(selection_bound_)); - - aura::Window* window = widget_->GetNativeView(); - gfx::Point edge_top = selection_bound_.edge_top_rounded(); - gfx::Point edge_bottom = selection_bound_.edge_bottom_rounded(); - wm::ConvertPointFromScreen(window, &edge_top); - wm::ConvertPointFromScreen(window, &edge_bottom); - selection_bound_.SetEdge(gfx::PointF(edge_top), gfx::PointF(edge_bottom)); + aura::Window* window = widget_->GetNativeView(); + gfx::Point edge_top = selection_bound_.edge_top_rounded(); + gfx::Point edge_bottom = selection_bound_.edge_bottom_rounded(); + wm::ConvertPointFromScreen(window, &edge_top); + wm::ConvertPointFromScreen(window, &edge_bottom); + selection_bound_.SetEdge(gfx::PointF(edge_top), gfx::PointF(edge_bottom)); + } + targeter_->SetHitTestOffset(selection_bound_.GetHeight() + + kSelectionHandleVerticalVisualOffset); } void SetDrawInvisible(bool draw_invisible) { @@ -392,6 +379,12 @@ std::unique_ptr<Widget> widget_; TouchSelectionControllerImpl* controller_; + // A custom targeter that shifts the hit-test target below the apparent bounds + // to make dragging easier. The |widget_|'s NativeWindow takes ownership over + // the |targeter_| but since the |widget_|'s lifetime is known to this class, + // it can safely access the |targeter_|. + TouchHandleWindowTargeter* targeter_; + // In local coordinates gfx::SelectionBound selection_bound_; gfx::Image* image_; @@ -416,19 +409,6 @@ DISALLOW_COPY_AND_ASSIGN(EditingHandleView); }; -TouchHandleWindowTargeter::TouchHandleWindowTargeter( - aura::Window* window, - EditingHandleView* handle_view) - : wm::MaskedWindowTargeter(window), - handle_view_(handle_view) { -} - -bool TouchHandleWindowTargeter::GetHitTestMask(aura::Window* window, - gfx::Path* mask) const { - handle_view_->GetWidgetHitTestMask(mask); - return true; -} - TouchSelectionControllerImpl::TouchSelectionControllerImpl( ui::TouchEditable* client_view) : client_view_(client_view),
diff --git a/ui/webui/resources/cr_elements/cr_scrollable_behavior.js b/ui/webui/resources/cr_elements/cr_scrollable_behavior.js index 2a49780..9535c1a9 100644 --- a/ui/webui/resources/cr_elements/cr_scrollable_behavior.js +++ b/ui/webui/resources/cr_elements/cr_scrollable_behavior.js
@@ -63,7 +63,7 @@ */ updateScrollableContents: function() { if (this.intervalId_ !== null) - return; // notifyResize is arelady in progress. + return; // notifyResize is already in progress. this.requestUpdateScroll(); @@ -93,7 +93,7 @@ }, /** - * Setup the intial scrolling related classes for each scrollable container. + * Setup the initial scrolling related classes for each scrollable container. * Called from ready() and updateScrollableContents(). May also be called * directly when the contents change (e.g. when not using iron-list). */ @@ -119,7 +119,7 @@ this.async(function() { var scrollTop = list.savedScrollTops.shift(); // Ignore scrollTop of 0 in case it was intermittent (we do not need to - // explicity scroll to 0). + // explicitly scroll to 0). if (scrollTop != 0) list.scroll(0, scrollTop); }); @@ -136,7 +136,8 @@ }, /** - * This gets called once intially and any time a scrollable container scrolls. + * This gets called once initially and any time a scrollable container + * scrolls. * @param {!HTMLElement} scrollable * @private */
diff --git a/ui/webui/resources/cr_elements/paper_toggle_style_css.html b/ui/webui/resources/cr_elements/paper_toggle_style_css.html new file mode 100644 index 0000000..95581fd --- /dev/null +++ b/ui/webui/resources/cr_elements/paper_toggle_style_css.html
@@ -0,0 +1,42 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> + +<!-- Common paper-button styling for Material Design WebUI. --> +<dom-module id="paper-toggle-style"> + <template> + <style> + :root { + --cr-toggle-bar-size: { + height: 12px; + left: 4px; + width: 28px; + }; + --cr-toggle-button-size: { + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4); + height: 16px; + top: -2px; + width: 16px; + }; + --cr-toggle-ink-size: { + height: 40px; + top: -12px; + left: -12px; + width: 40px; + }; + + --paper-toggle-button-checked-bar: var(--cr-toggle-bar-size); + --paper-toggle-button-checked-bar-color: var(--cr-toggle-color); + --paper-toggle-button-checked-button: { + @apply(--cr-toggle-button-size); + transform: translate(18px, 0); + }; + --paper-toggle-button-checked-button-color: var(--cr-toggle-color); + --paper-toggle-button-label-spacing: 0; + --paper-toggle-button-unchecked-bar: var(--cr-toggle-bar-size); + --paper-toggle-button-unchecked-button: var(--cr-toggle-button-size); + --paper-toggle-button-unchecked-ink: var(--cr-toggle-ink-size); + }; + </style> + </template> +</dom-module>
diff --git a/ui/webui/resources/cr_elements/shared_vars_css.html b/ui/webui/resources/cr_elements/shared_vars_css.html index 224a9476..f7811802 100644 --- a/ui/webui/resources/cr_elements/shared_vars_css.html +++ b/ui/webui/resources/cr_elements/shared_vars_css.html
@@ -37,6 +37,8 @@ -webkit-margin-start: var(--cr-icon-button-margin-start); } + --cr-toggle-color: var(--google-blue-500); + --cr-selectable-focus: { background-color: var(--cr-focused-item-color); outline: none;
diff --git a/ui/webui/resources/cr_elements_resources.grdp b/ui/webui/resources/cr_elements_resources.grdp index d9db27b..ce441a59 100644 --- a/ui/webui/resources/cr_elements_resources.grdp +++ b/ui/webui/resources/cr_elements_resources.grdp
@@ -179,6 +179,9 @@ <structure name="IDR_CR_ELEMENTS_PAPER_BUTTON_STYLE_CSS_HTML" file="../../webui/resources/cr_elements/paper_button_style_css.html" type="chrome_html" /> + <structure name="IDR_CR_ELEMENTS_PAPER_TOGGLE_STYLE_CSS_HTML" + file="../../webui/resources/cr_elements/paper_toggle_style_css.html" + type="chrome_html" /> <structure name="IDR_CR_ELEMENTS_CR_SHARED_VARS_CSS_HTML" file="../../webui/resources/cr_elements/shared_vars_css.html" type="chrome_html" />
diff --git a/ui/wm/core/easy_resize_window_targeter.cc b/ui/wm/core/easy_resize_window_targeter.cc index 37cc15b7..d050c1b4 100644 --- a/ui/wm/core/easy_resize_window_targeter.cc +++ b/ui/wm/core/easy_resize_window_targeter.cc
@@ -28,32 +28,12 @@ EasyResizeWindowTargeter::~EasyResizeWindowTargeter() {} -void EasyResizeWindowTargeter::SetInsets(const gfx::Insets& mouse_extend, - const gfx::Insets& touch_extend) { - if (mouse_extend == mouse_extend_ && touch_extend_ == touch_extend) - return; - - mouse_extend_ = mouse_extend; - touch_extend_ = touch_extend; +void EasyResizeWindowTargeter::OnSetInsets() { if (aura::Env::GetInstance()->mode() != aura::Env::Mode::MUS) return; aura::WindowPortMus::Get(container_) - ->SetExtendedHitRegionForChildren(mouse_extend, touch_extend); -} - -bool EasyResizeWindowTargeter::GetHitTestRects(aura::Window* window, - gfx::Rect* rect_mouse, - gfx::Rect* rect_touch) const { - if (!ShouldUseExtendedBounds(window)) - return WindowTargeter::GetHitTestRects(window, rect_mouse, rect_touch); - - DCHECK(rect_mouse); - DCHECK(rect_touch); - *rect_mouse = *rect_touch = gfx::Rect(window->bounds()); - rect_mouse->Inset(mouse_extend_); - rect_touch->Inset(touch_extend_); - return true; + ->SetExtendedHitRegionForChildren(mouse_extend(), touch_extend()); } bool EasyResizeWindowTargeter::EventLocationInsideBounds(
diff --git a/ui/wm/core/easy_resize_window_targeter.h b/ui/wm/core/easy_resize_window_targeter.h index 32a61fbc..b277bd4 100644 --- a/ui/wm/core/easy_resize_window_targeter.h +++ b/ui/wm/core/easy_resize_window_targeter.h
@@ -26,17 +26,8 @@ ~EasyResizeWindowTargeter() override; protected: - // NOTE: the insets must be negative. - void SetInsets(const gfx::Insets& mouse_extend, - const gfx::Insets& touch_extend); - - const gfx::Insets& mouse_extend() const { return mouse_extend_; } - const gfx::Insets& touch_extend() const { return touch_extend_; } - // aura::WindowTargeter: - bool GetHitTestRects(aura::Window* window, - gfx::Rect* rect_mouse, - gfx::Rect* rect_touch) const override; + void OnSetInsets() override; private: // aura::WindowTargeter: @@ -46,11 +37,9 @@ // Returns true if the hit testing (GetHitTestRects()) should use the // extended bounds. - virtual bool ShouldUseExtendedBounds(const aura::Window* window) const; + bool ShouldUseExtendedBounds(const aura::Window* window) const override; aura::Window* container_; - gfx::Insets mouse_extend_; - gfx::Insets touch_extend_; DISALLOW_COPY_AND_ASSIGN(EasyResizeWindowTargeter); };