diff --git a/DEPS b/DEPS index d6d6b81..2eba0412 100644 --- a/DEPS +++ b/DEPS
@@ -127,11 +127,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '436d47d5bde9849dbec03579d8d99a6df33c380f', + 'skia_revision': 'e842e326a4489f993aa13a6472eaab24d991d540', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'f9d0763ad7b31ba60d0dfdbb4a21fdd317990900', + 'v8_revision': '6ce59d6e32ec4e7d35f6ea4539c4d8d51acc5442', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -139,11 +139,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '76bd848cabd52907e392151835c973ccab4f45c9', + 'angle_revision': '4d153383bd3a2359a44fb0c13a65119c0087702f', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. - 'swiftshader_revision': '37c024a6a7fd03883ef04bc5f8f70163f9833f77', + 'swiftshader_revision': 'a2f5fd82603145c6e770e284cf23fc2e495e2a21', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. @@ -182,7 +182,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling HarfBuzz # and whatever else without interference from each other. - 'harfbuzz_revision': 'fe532923101586e316b300d419a337d357cd93da', + 'harfbuzz_revision': '4f37ab63de9705d7bf74ee75364747e41b7c06a1', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Emoji Segmenter # and whatever else without interference from each other. @@ -190,7 +190,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '021adeeeb4081594190aa6c68ab6e8c64f71ac9b', + 'catapult_revision': 'ccde41b4c52b891812731deac21585def8737ddb', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -649,7 +649,7 @@ Var('chromium_git') + '/angle/angle.git' + '@' + Var('angle_revision'), 'src/third_party/dav1d/libdav1d': - Var('chromium_git') + '/external/github.com/videolan/dav1d.git' + '@' + '16ba83510c69271d4b635a3f5fd43b73dfd89d20', + Var('chromium_git') + '/external/github.com/videolan/dav1d.git' + '@' + '493155af054cf29a4dead7ffe92acd5d6b3d6a59', 'src/third_party/dawn': Var('dawn_git') + '/dawn.git' + '@' + Var('dawn_revision'), @@ -738,7 +738,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '88be5f464ddfe282f29d734e99b4eb187239f65f', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '0b2d9ee474e1226c75df8034b61870588daf6b14', 'condition': 'checkout_linux', }, @@ -749,11 +749,11 @@ Var('chromium_git') + '/external/colorama.git' + '@' + '799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8', 'src/third_party/crc32c/src': - Var('chromium_git') + '/external/github.com/google/crc32c.git' + '@' + 'e4dfc9f1b583c2f41fd547b783d4304d9b29a276', + Var('chromium_git') + '/external/github.com/google/crc32c.git' + '@' + '5998f8451548244de8cde7fab387a550e7c4497d', # For Linux and Chromium OS. 'src/third_party/cros_system_api': { - 'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'e415e09bd20aec1fec91ad5007eb9e55c03f0c1c', + 'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + 'de1a4c92ee70a7a94651c0eaeece59090619c09e', 'condition': 'checkout_linux', }, @@ -763,7 +763,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'a6d41e2396bddb611df0cbc41de557a585939ace', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'c903198758bdddd96d37fc5992d24b174623a2e5', 'src/third_party/devtools-node-modules': Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'), @@ -1276,7 +1276,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'a0f51b2e123f39c9ff12e621b0b47dd28dd64424', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'ed50e6c7596dbd02c3b01c93f73d880587183c22', + Var('webrtc_git') + '/src.git' + '@' + '7e215c65d411b39e980fa7a2710d4e13df42ecee', 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d', @@ -1317,7 +1317,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6ecc5174ac939c9f8534e836346ae773e04914bf', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@18eeaa27090cbf954417dabb98cef060a978bba7', 'condition': 'checkout_src_internal', },
diff --git a/android_webview/browser/gfx/surfaces_instance.cc b/android_webview/browser/gfx/surfaces_instance.cc index 2e9de86..dd46a4c7 100644 --- a/android_webview/browser/gfx/surfaces_instance.cc +++ b/android_webview/browser/gfx/surfaces_instance.cc
@@ -67,8 +67,8 @@ // Webview does not own the surface so should not clear it. settings.should_clear_root_render_pass = false; - settings.use_skia_renderer = - features::IsUsingSkiaRenderer() || features::IsUsingSkiaRendererNonDDL(); + settings.use_skia_renderer = features::IsUsingSkiaRenderer(); + settings.use_skia_renderer_non_ddl = features::IsUsingSkiaRendererNonDDL(); // The SharedBitmapManager is null as we do not support or use software // compositing on Android. @@ -85,7 +85,7 @@ std::unique_ptr<viz::OutputSurface> output_surface; viz::SkiaOutputSurface* skia_output_surface = nullptr; - if (settings.use_skia_renderer) { + if (settings.use_skia_renderer || settings.use_skia_renderer_non_ddl) { auto* task_executor = DeferredGpuCommandService::GetInstance(); if (!shared_context_state_) { auto surface = base::MakeRefCounted<AwGLSurface>(); @@ -103,11 +103,11 @@ .enabled_gpu_driver_bug_workarounds), nullptr /* gr_shader_cache */); } - if (features::IsUsingSkiaRendererNonDDL()) { + if (settings.use_skia_renderer_non_ddl) { output_surface = std::make_unique<viz::SkiaOutputSurfaceImplNonDDL>( base::MakeRefCounted<AwGLSurface>(), shared_context_state_, - task_executor->mailbox_manager(), - task_executor->sync_point_manager()); + task_executor->mailbox_manager(), task_executor->sync_point_manager(), + false /* need_swapbuffers_ack */); } else { output_surface = std::make_unique<viz::SkiaOutputSurfaceImpl>( task_executor, base::MakeRefCounted<AwGLSurface>(),
diff --git a/ash/app_list/views/assistant/assistant_main_view.cc b/ash/app_list/views/assistant/assistant_main_view.cc index d6028b73..af630734 100644 --- a/ash/app_list/views/assistant/assistant_main_view.cc +++ b/ash/app_list/views/assistant/assistant_main_view.cc
@@ -10,7 +10,6 @@ #include "ash/app_list/views/assistant/dialog_plate.h" #include "ash/assistant/ui/assistant_ui_constants.h" #include "ash/assistant/ui/assistant_view_delegate.h" -#include "ash/assistant/ui/dialog_plate/dialog_plate.h" #include "ui/views/layout/box_layout.h" namespace app_list { @@ -24,19 +23,9 @@ AssistantMainView::AssistantMainView(ash::AssistantViewDelegate* delegate) : delegate_(delegate) { InitLayout(); - - for (ash::DialogPlateObserver* observer : - delegate_->GetDialogPlateObservers()) { - dialog_plate_->AddObserver(observer); - } } -AssistantMainView::~AssistantMainView() { - for (ash::DialogPlateObserver* observer : - delegate_->GetDialogPlateObservers()) { - dialog_plate_->RemoveObserver(observer); - } -} +AssistantMainView::~AssistantMainView() = default; const char* AssistantMainView::GetClassName() const { return "AssistantMainView";
diff --git a/ash/app_list/views/assistant/dialog_plate.cc b/ash/app_list/views/assistant/dialog_plate.cc index 482d745e..97bf52c 100644 --- a/ash/app_list/views/assistant/dialog_plate.cc +++ b/ash/app_list/views/assistant/dialog_plate.cc
@@ -80,14 +80,6 @@ delegate_->RemoveInteractionModelObserver(this); } -void DialogPlate::AddObserver(ash::DialogPlateObserver* observer) { - observers_.AddObserver(observer); -} - -void DialogPlate::RemoveObserver(ash::DialogPlateObserver* observer) { - observers_.RemoveObserver(observer); -} - const char* DialogPlate::GetClassName() const { return "DialogPlate"; } @@ -122,9 +114,8 @@ // Only non-empty trimmed text is consider a valid contents commit. // Anything else will simply result in the DialogPlate being cleared. if (!trimmed_text.empty()) { - for (ash::DialogPlateObserver& observer : observers_) - observer.OnDialogPlateContentsCommitted( - base::UTF16ToUTF8(trimmed_text)); + delegate_->OnDialogPlateContentsCommitted( + base::UTF16ToUTF8(trimmed_text)); } textfield_->SetText(base::string16()); @@ -409,9 +400,7 @@ } void DialogPlate::OnButtonPressed(ash::AssistantButtonId id) { - for (ash::DialogPlateObserver& observer : observers_) - observer.OnDialogPlateButtonPressed(id); - + delegate_->OnDialogPlateButtonPressed(id); textfield_->SetText(base::string16()); }
diff --git a/ash/app_list/views/assistant/dialog_plate.h b/ash/app_list/views/assistant/dialog_plate.h index 8be87cf..6c09146 100644 --- a/ash/app_list/views/assistant/dialog_plate.h +++ b/ash/app_list/views/assistant/dialog_plate.h
@@ -14,7 +14,6 @@ #include "ash/assistant/ui/dialog_plate/action_view.h" #include "base/component_export.h" #include "base/macros.h" -#include "base/observer_list.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/textfield/textfield_controller.h" #include "ui/views/view.h" @@ -24,7 +23,6 @@ enum class AssistantButtonId; class AssistantViewDelegate; class BaseLogoView; -class DialogPlateObserver; } // namespace ash namespace ui { @@ -54,10 +52,6 @@ explicit DialogPlate(ash::AssistantViewDelegate* delegate); ~DialogPlate() override; - // Adds/removes the specified |observer|. - void AddObserver(ash::DialogPlateObserver* observer); - void RemoveObserver(ash::DialogPlateObserver* observer); - // views::View: const char* GetClassName() const override; gfx::Size CalculatePreferredSize() const override; @@ -106,8 +100,6 @@ std::unique_ptr<ui::CallbackLayerAnimationObserver> animation_observer_; std::unique_ptr<ash::AssistantQueryHistory::Iterator> query_history_iterator_; - base::ObserverList<ash::DialogPlateObserver> observers_; - DISALLOW_COPY_AND_ASSIGN(DialogPlate); };
diff --git a/ash/assistant/assistant_interaction_controller.cc b/ash/assistant/assistant_interaction_controller.cc index 55834e8..d4d8e2e 100644 --- a/ash/assistant/assistant_interaction_controller.cc +++ b/ash/assistant/assistant_interaction_controller.cc
@@ -89,9 +89,11 @@ void AssistantInteractionController::OnAssistantControllerConstructed() { assistant_controller_->ui_controller()->AddModelObserver(this); + assistant_controller_->view_delegate()->AddObserver(this); } void AssistantInteractionController::OnAssistantControllerDestroying() { + assistant_controller_->view_delegate()->RemoveObserver(this); assistant_controller_->ui_controller()->RemoveModelObserver(this); }
diff --git a/ash/assistant/assistant_interaction_controller.h b/ash/assistant/assistant_interaction_controller.h index 6e765bb..d8478a9 100644 --- a/ash/assistant/assistant_interaction_controller.h +++ b/ash/assistant/assistant_interaction_controller.h
@@ -14,7 +14,7 @@ #include "ash/assistant/model/assistant_interaction_model.h" #include "ash/assistant/model/assistant_interaction_model_observer.h" #include "ash/assistant/model/assistant_ui_model_observer.h" -#include "ash/assistant/ui/dialog_plate/dialog_plate.h" +#include "ash/assistant/ui/assistant_view_delegate.h" #include "ash/highlighter/highlighter_controller.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" @@ -33,8 +33,8 @@ public AssistantControllerObserver, public AssistantInteractionModelObserver, public AssistantUiModelObserver, - public HighlighterController::Observer, - public DialogPlateObserver { + public AssistantViewDelegateObserver, + public HighlighterController::Observer { public: using AssistantSuggestion = chromeos::assistant::mojom::AssistantSuggestion; using AssistantSuggestionPtr = @@ -100,7 +100,7 @@ void OnSpeechLevelUpdated(float speech_level) override; void OnTtsStarted(bool due_to_error) override; - // DialogPlateObserver: + // AssistantViewDelegateObserver: void OnDialogPlateButtonPressed(AssistantButtonId id) override; void OnDialogPlateContentsCommitted(const std::string& text) override;
diff --git a/ash/assistant/assistant_ui_controller.cc b/ash/assistant/assistant_ui_controller.cc index 5d9b0735..0c96365 100644 --- a/ash/assistant/assistant_ui_controller.cc +++ b/ash/assistant/assistant_ui_controller.cc
@@ -216,9 +216,11 @@ void AssistantUiController::OnAssistantControllerConstructed() { assistant_controller_->interaction_controller()->AddModelObserver(this); assistant_controller_->screen_context_controller()->AddModelObserver(this); + assistant_controller_->view_delegate()->AddObserver(this); } void AssistantUiController::OnAssistantControllerDestroying() { + assistant_controller_->view_delegate()->RemoveObserver(this); assistant_controller_->screen_context_controller()->RemoveModelObserver(this); assistant_controller_->interaction_controller()->RemoveModelObserver(this);
diff --git a/ash/assistant/assistant_ui_controller.h b/ash/assistant/assistant_ui_controller.h index eab37a5..6034e0fc 100644 --- a/ash/assistant/assistant_ui_controller.h +++ b/ash/assistant/assistant_ui_controller.h
@@ -16,6 +16,7 @@ #include "ash/assistant/model/assistant_ui_model.h" #include "ash/assistant/model/assistant_ui_model_observer.h" #include "ash/assistant/ui/assistant_mini_view.h" +#include "ash/assistant/ui/assistant_view_delegate.h" #include "ash/assistant/ui/caption_bar.h" #include "ash/assistant/ui/dialog_plate/dialog_plate.h" #include "ash/highlighter/highlighter_controller.h" @@ -53,8 +54,8 @@ public AssistantScreenContextModelObserver, public AssistantUiModelObserver, public AssistantMiniViewDelegate, + public AssistantViewDelegateObserver, public CaptionBarDelegate, - public DialogPlateObserver, public HighlighterController::Observer, public keyboard::KeyboardControllerObserver, public display::DisplayObserver, @@ -93,7 +94,7 @@ // CaptionBarDelegate: bool OnCaptionButtonPressed(AssistantButtonId id) override; - // DialogPlateObserver: + // AssistantViewDelegateObserver: void OnDialogPlateButtonPressed(AssistantButtonId id) override; // HighlighterController::Observer:
diff --git a/ash/assistant/assistant_view_delegate_impl.cc b/ash/assistant/assistant_view_delegate_impl.cc index 3d9fa1f..b811530 100644 --- a/ash/assistant/assistant_view_delegate_impl.cc +++ b/ash/assistant/assistant_view_delegate_impl.cc
@@ -39,6 +39,16 @@ return assistant_controller_->ui_controller()->model(); } +void AssistantViewDelegateImpl::AddObserver( + AssistantViewDelegateObserver* observer) { + view_delegate_observers_.AddObserver(observer); +} + +void AssistantViewDelegateImpl::RemoveObserver( + AssistantViewDelegateObserver* observer) { + view_delegate_observers_.RemoveObserver(observer); +} + void AssistantViewDelegateImpl::AddCacheModelObserver( AssistantCacheModelObserver* observer) { assistant_controller_->cache_controller()->AddModelObserver(observer); @@ -81,16 +91,6 @@ assistant_controller_->ui_controller()->RemoveModelObserver(observer); } -void AssistantViewDelegateImpl::AddViewDelegateObserver( - AssistantViewDelegateObserver* observer) { - view_delegate_observers_.AddObserver(observer); -} - -void AssistantViewDelegateImpl::RemoveViewDelegateObserver( - AssistantViewDelegateObserver* observer) { - view_delegate_observers_.RemoveObserver(observer); -} - void AssistantViewDelegateImpl::AddVoiceInteractionControllerObserver( DefaultVoiceInteractionObserver* observer) { Shell::Get()->voice_interaction_controller()->AddLocalObserver(observer); @@ -105,12 +105,6 @@ return assistant_controller_->ui_controller(); } -std::vector<DialogPlateObserver*> -AssistantViewDelegateImpl::GetDialogPlateObservers() { - return {assistant_controller_->interaction_controller(), - assistant_controller_->ui_controller()}; -} - AssistantMiniViewDelegate* AssistantViewDelegateImpl::GetMiniViewDelegate() { return assistant_controller_->ui_controller(); } @@ -151,6 +145,18 @@ ->IsTabletModeWindowManagerEnabled(); } +void AssistantViewDelegateImpl::OnDialogPlateButtonPressed( + AssistantButtonId id) { + for (auto& observer : view_delegate_observers_) + observer.OnDialogPlateButtonPressed(id); +} + +void AssistantViewDelegateImpl::OnDialogPlateContentsCommitted( + const std::string& text) { + for (auto& observer : view_delegate_observers_) + observer.OnDialogPlateContentsCommitted(text); +} + void AssistantViewDelegateImpl::OnNotificationButtonPressed( const std::string& notification_id, int notification_button_index) {
diff --git a/ash/assistant/assistant_view_delegate_impl.h b/ash/assistant/assistant_view_delegate_impl.h index 0ca5c071..212f16c 100644 --- a/ash/assistant/assistant_view_delegate_impl.h +++ b/ash/assistant/assistant_view_delegate_impl.h
@@ -27,6 +27,8 @@ const AssistantInteractionModel* GetInteractionModel() const override; const AssistantNotificationModel* GetNotificationModel() const override; const AssistantUiModel* GetUiModel() const override; + void AddObserver(AssistantViewDelegateObserver* observer) override; + void RemoveObserver(AssistantViewDelegateObserver* observer) override; void AddCacheModelObserver(AssistantCacheModelObserver* observer) override; void RemoveCacheModelObserver(AssistantCacheModelObserver* observer) override; void AddInteractionModelObserver( @@ -39,16 +41,11 @@ AssistantNotificationModelObserver* observer) override; void AddUiModelObserver(AssistantUiModelObserver* observer) override; void RemoveUiModelObserver(AssistantUiModelObserver* observer) override; - void AddViewDelegateObserver( - AssistantViewDelegateObserver* observer) override; - void RemoveViewDelegateObserver( - AssistantViewDelegateObserver* observer) override; void AddVoiceInteractionControllerObserver( DefaultVoiceInteractionObserver* observer) override; void RemoveVoiceInteractionControllerObserver( DefaultVoiceInteractionObserver* observer) override; CaptionBarDelegate* GetCaptionBarDelegate() override; - std::vector<DialogPlateObserver*> GetDialogPlateObservers() override; AssistantMiniViewDelegate* GetMiniViewDelegate() override; AssistantOptInDelegate* GetOptInDelegate() override; void DownloadImage( @@ -60,6 +57,8 @@ content::mojom::NavigableContentsFactoryRequest request) override; aura::Window* GetRootWindowForNewWindows() override; bool IsTabletMode() const override; + void OnDialogPlateButtonPressed(AssistantButtonId id) override; + void OnDialogPlateContentsCommitted(const std::string& text) override; void OnNotificationButtonPressed(const std::string& notification_id, int notification_button_index) override; void OnSuggestionChipPressed(const AssistantSuggestion* suggestion) override;
diff --git a/ash/assistant/ui/assistant_main_view.cc b/ash/assistant/ui/assistant_main_view.cc index af3d61b..29bbcae 100644 --- a/ash/assistant/ui/assistant_main_view.cc +++ b/ash/assistant/ui/assistant_main_view.cc
@@ -55,17 +55,11 @@ // Set delegate/observers. caption_bar_->set_delegate(delegate_->GetCaptionBarDelegate()); - for (DialogPlateObserver* observer : delegate_->GetDialogPlateObservers()) - dialog_plate_->AddObserver(observer); - // The AssistantViewDelegate should outlive AssistantMainView. delegate_->AddUiModelObserver(this); } AssistantMainView::~AssistantMainView() { - for (DialogPlateObserver* observer : delegate_->GetDialogPlateObservers()) - dialog_plate_->RemoveObserver(observer); - delegate_->RemoveUiModelObserver(this); }
diff --git a/ash/assistant/ui/assistant_view_delegate.h b/ash/assistant/ui/assistant_view_delegate.h index 6a2a72c9..53da6de 100644 --- a/ash/assistant/ui/assistant_view_delegate.h +++ b/ash/assistant/ui/assistant_view_delegate.h
@@ -7,7 +7,6 @@ #include <map> #include <string> -#include <vector> #include "ash/assistant/model/assistant_cache_model.h" #include "ash/assistant/model/assistant_cache_model_observer.h" @@ -45,6 +44,12 @@ virtual void OnDeepLinkReceived( assistant::util::DeepLinkType type, const std::map<std::string, std::string>& params) {} + + // Invoked when the dialog plate button identified by |id| is pressed. + virtual void OnDialogPlateButtonPressed(AssistantButtonId id) {} + + // Invoked when the dialog plate contents have been committed. + virtual void OnDialogPlateContentsCommitted(const std::string& text) {} }; // A delegate of views in assistant/ui that handles views related actions e.g. @@ -68,6 +73,10 @@ // Gets the ui model associated with the view delegate. virtual const AssistantUiModel* GetUiModel() const = 0; + // Adds/removes the specified view delegate observer. + virtual void AddObserver(AssistantViewDelegateObserver* observer) = 0; + virtual void RemoveObserver(AssistantViewDelegateObserver* observer) = 0; + // Adds/removes the cache model observer associated with the view delegate. virtual void AddCacheModelObserver(AssistantCacheModelObserver* observer) = 0; virtual void RemoveCacheModelObserver( @@ -91,12 +100,6 @@ virtual void AddUiModelObserver(AssistantUiModelObserver* observer) = 0; virtual void RemoveUiModelObserver(AssistantUiModelObserver* observer) = 0; - // Adds/removes the view delegate observer. - virtual void AddViewDelegateObserver( - AssistantViewDelegateObserver* observer) = 0; - virtual void RemoveViewDelegateObserver( - AssistantViewDelegateObserver* observer) = 0; - // Adds/removes the voice interaction controller observer associated with the // view delegate. virtual void AddVoiceInteractionControllerObserver( @@ -107,9 +110,6 @@ // Gets the caption bar delegate associated with the view delegate. virtual CaptionBarDelegate* GetCaptionBarDelegate() = 0; - // Gets the dialog plate observers associated with the view delegate. - virtual std::vector<DialogPlateObserver*> GetDialogPlateObservers() = 0; - // Gets the mini view delegate associated with the view delegate. virtual AssistantMiniViewDelegate* GetMiniViewDelegate() = 0; @@ -134,11 +134,18 @@ virtual void GetNavigableContentsFactoryForView( content::mojom::NavigableContentsFactoryRequest request) = 0; + // Returns the root window that newly created windows should be added to. virtual aura::Window* GetRootWindowForNewWindows() = 0; // Returns true if in tablet mode. virtual bool IsTabletMode() const = 0; + // Invoked when the dialog plate button identified by |id| is pressed. + virtual void OnDialogPlateButtonPressed(AssistantButtonId id) = 0; + + // Invoked when the dialog plate contents have been committed. + virtual void OnDialogPlateContentsCommitted(const std::string& text) = 0; + // Invoked when an in-Assistant notification button is pressed. virtual void OnNotificationButtonPressed(const std::string& notification_id, int notification_button_index) = 0;
diff --git a/ash/assistant/ui/assistant_web_view.cc b/ash/assistant/ui/assistant_web_view.cc index 08e07315..0e08cbef 100644 --- a/ash/assistant/ui/assistant_web_view.cc +++ b/ash/assistant/ui/assistant_web_view.cc
@@ -67,11 +67,11 @@ : delegate_(delegate), weak_factory_(this) { InitLayout(); - delegate_->AddViewDelegateObserver(this); + delegate_->AddObserver(this); } AssistantWebView::~AssistantWebView() { - delegate_->RemoveViewDelegateObserver(this); + delegate_->RemoveObserver(this); RemoveContents(); }
diff --git a/ash/assistant/ui/caption_bar.cc b/ash/assistant/ui/caption_bar.cc index b8a6c38..41cc3f8 100644 --- a/ash/assistant/ui/caption_bar.cc +++ b/ash/assistant/ui/caption_bar.cc
@@ -4,8 +4,6 @@ #include "ash/assistant/ui/caption_bar.h" -#include <memory> - #include "ash/assistant/model/assistant_ui_model.h" #include "ash/assistant/ui/assistant_ui_constants.h" #include "ash/assistant/ui/base/assistant_button.h" @@ -15,6 +13,7 @@ #include "ui/gfx/paint_vector_icon.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/controls/button/image_button.h" +#include "ui/views/event_monitor.h" #include "ui/views/layout/box_layout.h" #include "ui/views/widget/widget.h" #include "ui/views/window/vector_icons/vector_icons.h" @@ -53,31 +52,6 @@ return "CaptionBar"; } -bool CaptionBar::AcceleratorPressed(const ui::Accelerator& accelerator) { - switch (accelerator.key_code()) { - case ui::VKEY_BROWSER_BACK: - HandleButton(AssistantButtonId::kBack); - break; - case ui::VKEY_ESCAPE: - HandleButton(AssistantButtonId::kClose); - break; - case ui::VKEY_W: - if (accelerator.IsCtrlDown()) { - HandleButton(AssistantButtonId::kClose); - } else { - NOTREACHED(); - return false; - } - break; - default: - NOTREACHED(); - return false; - } - - // Don't let ClientView handle the accelerator. - return true; -} - gfx::Size CaptionBar::CalculatePreferredSize() const { return gfx::Size(INT_MAX, GetHeightForWidth(INT_MAX)); } @@ -86,9 +60,47 @@ return kPreferredHeightDip; } +void CaptionBar::VisibilityChanged(views::View* starting_from, bool visible) { + if (!IsDrawn()) { + event_monitor_.reset(); + return; + } + + views::Widget* widget = GetWidget(); + if (!widget) + return; + + // Only when the CaptionBar is drawn do we allow it to monitor key press + // events. We monitor key press events to handle hotkeys that behave the same + // as caption bar buttons. Note that we use an EventMonitor rather than adding + // accelerators so that we always receive key events, even if an embedded + // navigable contents in our view hierarchy has focus. + gfx::NativeWindow root_window = widget->GetNativeWindow()->GetRootWindow(); + event_monitor_ = views::EventMonitor::CreateWindowMonitor( + this, root_window, {ui::ET_KEY_PRESSED}); +} + void CaptionBar::ButtonPressed(views::Button* sender, const ui::Event& event) { - auto id = static_cast<AssistantButtonId>(sender->id()); - HandleButton(id); + HandleButton(static_cast<AssistantButtonId>(sender->id())); +} + +void CaptionBar::OnEvent(const ui::Event& event) { + const ui::KeyEvent& key_event = static_cast<const ui::KeyEvent&>(event); + switch (key_event.key_code()) { + case ui::VKEY_BROWSER_BACK: + HandleButton(AssistantButtonId::kBack); + break; + case ui::VKEY_ESCAPE: + HandleButton(AssistantButtonId::kClose); + break; + case ui::VKEY_W: + if (key_event.IsControlDown()) + HandleButton(AssistantButtonId::kClose); + break; + default: + // No action necessary. + break; + } } void CaptionBar::SetButtonVisible(AssistantButtonId id, bool visible) { @@ -107,10 +119,8 @@ views::BoxLayout::CrossAxisAlignment::CROSS_AXIS_ALIGNMENT_CENTER); // Back. - auto* back_button = - CreateCaptionButton(kWindowControlBackIcon, IDS_APP_LIST_BACK, - AssistantButtonId::kBack, this); - AddChildView(back_button); + AddChildView(CreateCaptionButton(kWindowControlBackIcon, IDS_APP_LIST_BACK, + AssistantButtonId::kBack, this)); // Spacer. views::View* spacer = new views::View(); @@ -119,21 +129,14 @@ layout_manager->SetFlexForView(spacer, 1); // Minimize. - auto* minimize_button = CreateCaptionButton( - views::kWindowControlMinimizeIcon, IDS_APP_ACCNAME_MINIMIZE, - AssistantButtonId::kMinimize, this); - AddChildView(minimize_button); + AddChildView(CreateCaptionButton(views::kWindowControlMinimizeIcon, + IDS_APP_ACCNAME_MINIMIZE, + AssistantButtonId::kMinimize, this)); // Close. - auto* close_button = - CreateCaptionButton(views::kWindowControlCloseIcon, IDS_APP_ACCNAME_CLOSE, - AssistantButtonId::kClose, this); - AddChildView(close_button); - - // Add accelerators for keyboard shortcuts that behave like caption buttons. - AddAccelerator(ui::Accelerator(ui::VKEY_BROWSER_BACK, ui::EF_NONE)); // Back - AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); // Close - AddAccelerator(ui::Accelerator(ui::VKEY_W, ui::EF_CONTROL_DOWN)); // Close + AddChildView(CreateCaptionButton(views::kWindowControlCloseIcon, + IDS_APP_ACCNAME_CLOSE, + AssistantButtonId::kClose, this)); } void CaptionBar::HandleButton(AssistantButtonId id) {
diff --git a/ash/assistant/ui/caption_bar.h b/ash/assistant/ui/caption_bar.h index aeb777e..a56eaed 100644 --- a/ash/assistant/ui/caption_bar.h +++ b/ash/assistant/ui/caption_bar.h
@@ -5,11 +5,18 @@ #ifndef ASH_ASSISTANT_UI_CAPTION_BAR_H_ #define ASH_ASSISTANT_UI_CAPTION_BAR_H_ +#include <memory> + #include "base/component_export.h" #include "base/macros.h" +#include "ui/events/event_observer.h" #include "ui/views/controls/button/button.h" #include "ui/views/view.h" +namespace views { +class EventMonitor; +} // namespace views + namespace ash { enum class AssistantButtonId; @@ -31,20 +38,28 @@ // CaptionBar ------------------------------------------------------------------ class COMPONENT_EXPORT(ASSISTANT_UI) CaptionBar : public views::View, - views::ButtonListener { + public views::ButtonListener, + public ui::EventObserver { public: + // This is necessary to inform clang that our overload of |OnEvent|, + // overridden from |ui::EventObserver|, is intentional. + using ui::EventHandler::OnEvent; + CaptionBar(); ~CaptionBar() override; // views::View: const char* GetClassName() const override; - bool AcceleratorPressed(const ui::Accelerator& accelerator) override; gfx::Size CalculatePreferredSize() const override; int GetHeightForWidth(int width) const override; + void VisibilityChanged(views::View* starting_from, bool visible) override; // views::ButtonListener: void ButtonPressed(views::Button* sender, const ui::Event& event) override; + // ui::EventObserver: + void OnEvent(const ui::Event& event) override; + void set_delegate(CaptionBarDelegate* delegate) { delegate_ = delegate; } // Sets visibility for the caption button identified by |id|. @@ -56,6 +71,8 @@ CaptionBarDelegate* delegate_ = nullptr; + std::unique_ptr<views::EventMonitor> event_monitor_; + DISALLOW_COPY_AND_ASSIGN(CaptionBar); };
diff --git a/ash/assistant/ui/dialog_plate/dialog_plate.cc b/ash/assistant/ui/dialog_plate/dialog_plate.cc index c454cb9..92c9eed6 100644 --- a/ash/assistant/ui/dialog_plate/dialog_plate.cc +++ b/ash/assistant/ui/dialog_plate/dialog_plate.cc
@@ -73,14 +73,6 @@ delegate_->RemoveInteractionModelObserver(this); } -void DialogPlate::AddObserver(DialogPlateObserver* observer) { - observers_.AddObserver(observer); -} - -void DialogPlate::RemoveObserver(DialogPlateObserver* observer) { - observers_.RemoveObserver(observer); -} - const char* DialogPlate::GetClassName() const { return "DialogPlate"; } @@ -115,9 +107,8 @@ // Only non-empty trimmed text is consider a valid contents commit. // Anything else will simply result in the DialogPlate being cleared. if (!trimmed_text.empty()) { - for (DialogPlateObserver& observer : observers_) - observer.OnDialogPlateContentsCommitted( - base::UTF16ToUTF8(trimmed_text)); + delegate_->OnDialogPlateContentsCommitted( + base::UTF16ToUTF8(trimmed_text)); } textfield_->SetText(base::string16()); @@ -388,9 +379,7 @@ } void DialogPlate::OnButtonPressed(AssistantButtonId id) { - for (DialogPlateObserver& observer : observers_) - observer.OnDialogPlateButtonPressed(id); - + delegate_->OnDialogPlateButtonPressed(id); textfield_->SetText(base::string16()); }
diff --git a/ash/assistant/ui/dialog_plate/dialog_plate.h b/ash/assistant/ui/dialog_plate/dialog_plate.h index 961c3ef..4fe9881 100644 --- a/ash/assistant/ui/dialog_plate/dialog_plate.h +++ b/ash/assistant/ui/dialog_plate/dialog_plate.h
@@ -14,7 +14,6 @@ #include "ash/assistant/ui/dialog_plate/action_view.h" #include "base/component_export.h" #include "base/macros.h" -#include "base/observer_list.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/textfield/textfield_controller.h" #include "ui/views/view.h" @@ -33,21 +32,6 @@ enum class AssistantButtonId; class AssistantViewDelegate; -// DialogPlateObserver --------------------------------------------------------- - -class COMPONENT_EXPORT(ASSISTANT_UI) DialogPlateObserver - : public base::CheckedObserver { - public: - // Invoked when the dialog plate button identified by |id| is pressed. - virtual void OnDialogPlateButtonPressed(AssistantButtonId id) {} - - // Invoked on dialog plate contents committed event. - virtual void OnDialogPlateContentsCommitted(const std::string& text) {} - - protected: - ~DialogPlateObserver() override = default; -}; - // DialogPlate ----------------------------------------------------------------- // DialogPlate is the child of AssistantMainView concerned with providing the @@ -65,10 +49,6 @@ explicit DialogPlate(AssistantViewDelegate* delegate); ~DialogPlate() override; - // Adds/removes the specified |observer|. - void AddObserver(DialogPlateObserver* observer); - void RemoveObserver(DialogPlateObserver* observer); - // views::View: const char* GetClassName() const override; gfx::Size CalculatePreferredSize() const override; @@ -122,8 +102,6 @@ std::unique_ptr<ui::CallbackLayerAnimationObserver> animation_observer_; std::unique_ptr<AssistantQueryHistory::Iterator> query_history_iterator_; - base::ObserverList<DialogPlateObserver> observers_; - DISALLOW_COPY_AND_ASSIGN(DialogPlate); };
diff --git a/ash/display/display_error_observer_unittest.cc b/ash/display/display_error_observer_unittest.cc index bffa7ce..72ea3362 100644 --- a/ash/display/display_error_observer_unittest.cc +++ b/ash/display/display_error_observer_unittest.cc
@@ -27,17 +27,17 @@ const gfx::Size& size, display::DisplayConnectionType type) { return std::make_unique<display::DisplaySnapshot>( - id, gfx::Point(0, 0) /* origin */, size, type, - false /* is_aspect_preserving_scaling */, false /* has_overscan */, - false /* has_color_correction_matrix */, - false /* color_correction_in_linear_space */, - gfx::ColorSpace() /* color_space */, std::string() /* display_name */, - base::FilePath() /* sys_path */, - display::DisplaySnapshot::DisplayModeList() /* modes */, - std::vector<uint8_t>() /* edid */, nullptr /* current_mode */, - nullptr /* native_mode */, 0 /* product_id */, - display::kInvalidYearOfManufacture, - gfx::Size() /* maximum_cursor_size */); + id, /*origin=*/gfx::Point(0, 0), size, type, + /*is_aspect_preserving_scaling=*/false, /*has_overscan=*/false, + /*has_color_correction_matrix=*/false, + /*color_correction_in_linear_space=*/false, + /*color_space=*/gfx::ColorSpace(), /*display_name=*/std::string(), + /*sys_path=*/base::FilePath(), + /*modes=*/display::DisplaySnapshot::DisplayModeList(), + /*edid=*/std::vector<uint8_t>(), /*current_mode=*/nullptr, + /*native_mode=*/nullptr, /*product_id=*/0, + display::kInvalidYearOfManufacture, /*maximum_cursor_size=*/gfx::Size(), + /*has_associated_crtc=*/true); } } // namespace
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc index 4ad8aba..45730d7 100644 --- a/ash/login/ui/lock_contents_view.cc +++ b/ash/login/ui/lock_contents_view.cc
@@ -391,25 +391,6 @@ Shell::Get()->system_tray_notifier()->AddSystemTrayFocusObserver(this); keyboard::KeyboardController::Get()->AddObserver(this); - auth_error_bubble_ = new LoginErrorBubble(); - AddChildView(auth_error_bubble_); - - supervised_user_deprecation_bubble_ = new LoginErrorBubble(); - supervised_user_deprecation_bubble_->SetPersistent(true); - AddChildView(supervised_user_deprecation_bubble_); - - detachable_base_error_bubble_ = new LoginErrorBubble(); - detachable_base_error_bubble_->SetPersistent(true); - AddChildView(detachable_base_error_bubble_); - - tooltip_bubble_ = new LoginTooltipView(base::UTF8ToUTF16("") /*message*/, - nullptr /*anchor_view*/); - AddChildView(tooltip_bubble_); - - warning_banner_bubble_ = new LoginErrorBubble(); - warning_banner_bubble_->SetPersistent(true); - AddChildView(warning_banner_bubble_); - // We reuse the focusable state on this view as a signal that focus should // switch to the system tray. LockContentsView should otherwise not be // focusable. @@ -448,6 +429,25 @@ expanded_view_->SetVisible(false); AddChildView(expanded_view_); + supervised_user_deprecation_bubble_ = new LoginErrorBubble(); + supervised_user_deprecation_bubble_->SetPersistent(true); + AddChildView(supervised_user_deprecation_bubble_); + + detachable_base_error_bubble_ = new LoginErrorBubble(); + detachable_base_error_bubble_->SetPersistent(true); + AddChildView(detachable_base_error_bubble_); + + tooltip_bubble_ = new LoginTooltipView(base::UTF8ToUTF16("") /*message*/, + nullptr /*anchor_view*/); + AddChildView(tooltip_bubble_); + + warning_banner_bubble_ = new LoginErrorBubble(); + warning_banner_bubble_->SetPersistent(true); + AddChildView(warning_banner_bubble_); + + auth_error_bubble_ = new LoginErrorBubble(); + AddChildView(auth_error_bubble_); + OnLockScreenNoteStateChanged(initial_note_action_state); chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver( this);
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc index 8d15f2f..c6a23afe 100644 --- a/ash/login/ui/lock_contents_view_unittest.cc +++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -868,6 +868,53 @@ EXPECT_FALSE(test_api.auth_error_bubble()->visible()); } +TEST_F(LockContentsViewUnitTest, AuthErrorButtonClickable) { + // Build lock screen with a single user. + auto* contents = new LockContentsView( + mojom::TrayActionState::kNotAvailable, LockScreen::ScreenType::kLock, + DataDispatcher(), + std::make_unique<FakeLoginDetachableBaseModel>(DataDispatcher())); + SetUserCount(1); + SetWidget(CreateWidgetWithContent(contents)); + + LockContentsView::TestApi test_api(contents); + + // Password submit runs mojo. + std::unique_ptr<MockLoginScreenClient> client = BindMockLoginScreenClient(); + client->set_authenticate_user_callback_result(false); + EXPECT_CALL(*client, + AuthenticateUserWithPasswordOrPin_( + users()[0]->basic_user_info->account_id, _, false, _)); + + // AuthErrorButton should not be visible yet. + EXPECT_FALSE(test_api.auth_error_bubble()->visible()); + + // Submit password. + ui::test::EventGenerator* generator = GetEventGenerator(); + generator->PressKey(ui::KeyboardCode::VKEY_A, 0); + generator->PressKey(ui::KeyboardCode::VKEY_RETURN, 0); + base::RunLoop().RunUntilIdle(); + + // Auth Error button should be visible as an incorrect password was given. + EXPECT_TRUE(test_api.auth_error_bubble()->visible()); + + // Find button in auth_error_bubble children. + views::View* button = FindTopButton(test_api.auth_error_bubble()); + ASSERT_TRUE(button); + + // Expect ShowAccountAccessHelp() to be called due to button click. + EXPECT_CALL(*client, ShowAccountAccessHelpApp()).Times(1); + + // Move mouse to AuthError's ShowAccountAccessHelp button and click it. + // Should result in ShowAccountAccessHelpApp(). + generator->MoveMouseTo(button->GetBoundsInScreen().CenterPoint()); + generator->ClickLeftButton(); + Shell::Get()->login_screen_controller()->FlushForTesting(); + + // AuthErrorButton should go away after button press. + EXPECT_FALSE(test_api.auth_error_bubble()->visible()); +} + // Gaia is never shown on lock, no mater how many times auth fails. TEST_F(LockContentsViewUnitTest, GaiaNeverShownOnLockAfterFailedAuth) { // Build lock screen with a single user.
diff --git a/ash/login/ui/login_test_utils.cc b/ash/login/ui/login_test_utils.cc index 64f47df..aa6a0d5 100644 --- a/ash/login/ui/login_test_utils.cc +++ b/ash/login/ui/login_test_utils.cc
@@ -105,4 +105,20 @@ return false; } +// Performs a DFS for the first button in the views hierarchy +// The last child is on the top of the z layer stack +views::View* FindTopButton(views::View* current_view) { + for (int i = current_view->child_count() - 1; i >= 0; i--) { + views::View* child = current_view->child_at(i); + if (views::Button::AsButton(child)) + return child; + if (child->has_children()) { + views::View* child_button = FindTopButton(child); + if (child_button) + return child_button; + } + } + return nullptr; +} + } // namespace ash
diff --git a/ash/login/ui/login_test_utils.h b/ash/login/ui/login_test_utils.h index e980a6e..406de7f 100644 --- a/ash/login/ui/login_test_utils.h +++ b/ash/login/ui/login_test_utils.h
@@ -48,6 +48,9 @@ views::View* view, bool reverse); +// Find the first button in the z layer stack of the given view +views::View* FindTopButton(views::View* current_view); + } // namespace ash #endif // ASH_LOGIN_UI_LOGIN_TEST_UTILS_H_
diff --git a/build/android/gyp/create_stub_manifest.py b/build/android/gyp/create_stub_manifest.py new file mode 100755 index 0000000..ecaca9d --- /dev/null +++ b/build/android/gyp/create_stub_manifest.py
@@ -0,0 +1,36 @@ +#!/usr/bin/env python +# +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Generates a manifest with reference attributes removed.""" + +import argparse +import re + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--input', required=True, help='Path to the input manifest.') + parser.add_argument( + '--output', required=True, help='Path to the output manifest.') + args = parser.parse_args() + + with open(args.input) as manifest_file: + stub_manifest = manifest_file.read() + + stub_manifest = re.sub( + r'<meta-data[^>]*android:resource[^>]*/>', + '', + stub_manifest, + flags=re.MULTILINE) + stub_manifest = re.sub(r'android:[^=]*=\s*"@[^"]+"', '', stub_manifest) + + with open(args.output, 'w') as out_file: + out_file.write(stub_manifest) + + +if __name__ == '__main__': + main()
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 3b5b6dd..19b332d 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -2417,6 +2417,7 @@ # asset information. # deps: Specifies the dependencies of this target. # dex_path: Path to classes.dex file to include (optional). + # exclude_dex: Omit .dex files from the .apk (optional). # packaged_resources_path: Path to .ap_ to use. # output_apk_path: Output path for the generated .apk. # native_lib_placeholders: List of placeholder filenames to add to the apk @@ -2453,6 +2454,7 @@ _secondary_native_lib_placeholders = invoker.secondary_native_lib_placeholders } + _exclude_dex = defined(invoker.exclude_dex) && invoker.exclude_dex script = "//build/android/gyp/apkbuilder.py" depfile = "$target_gen_dir/$target_name.d" @@ -2518,7 +2520,7 @@ if (defined(invoker.write_asset_list) && invoker.write_asset_list) { args += [ "--write-asset-list" ] } - if (defined(invoker.dex_path)) { + if (defined(invoker.dex_path) && !_exclude_dex) { _rebased_dex_path = rebase_path(invoker.dex_path, root_build_dir) args += [ "--dex-file=$_rebased_dex_path" ] } @@ -2600,6 +2602,7 @@ [ "apk_name", "assets_build_config", + "exclude_dex", "native_lib_placeholders", "native_libs_filearg", "packaged_resources_path",
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index 77596b9..b7ee1585 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -1962,6 +1962,7 @@ # resources with acceptable/non-acceptable optimizations. # verify_android_configuration: Enables verification of expected merged # manifest and proguard flags based on a golden file. + # exclude_dex: Omit .dex files (even if they exist) from the final APK. template("android_apk_or_module") { forward_variables_from(invoker, [ "testonly" ]) @@ -2698,6 +2699,7 @@ create_apk("$_create_apk_target") { forward_variables_from(invoker, [ + "exclude_dex", "native_lib_placeholders", "public_deps", "secondary_native_lib_placeholders", @@ -2966,6 +2968,7 @@ "emma_never_instrument", "enable_chromium_linker_tests", "enable_multidex", + "exclude_dex", "final_apk_path", "firebase_app_id", "generate_buildconfig_java", @@ -4240,6 +4243,46 @@ ] } } + + # Generate an APK stub that contains a stripped down version of the manifest, + # a resources.arsc containing only the package name string, and signing + # artifacts. + # + # Variables: + # android_manifest: Path to the android manifest file. + # android_manifest_dep: Target that generates the manifest (optional). + # apk_name: Name of the final .apk. + template("create_stub_apk") { + _stub_manifest = "$target_gen_dir/$target_name/AndroidManifest_stub.xml" + _stub_manifest_target = "${target_name}__stub_manifest" + action(_stub_manifest_target) { + script = "//build/android/gyp/create_stub_manifest.py" + inputs = [ + invoker.android_manifest, + ] + outputs = [ + _stub_manifest, + ] + args = [ + "--input", + rebase_path(invoker.android_manifest), + "--output", + rebase_path(_stub_manifest), + ] + if (defined(invoker.android_manifest_dep)) { + deps = [ + invoker.android_manifest_dep, + ] + } + } + + android_apk(target_name) { + apk_name = invoker.apk_name + android_manifest = _stub_manifest + android_manifest_dep = ":$_stub_manifest_target" + exclude_dex = true + } + } } # Generate an Android resources target that contains localized strings
diff --git a/build/config/fuchsia/build_symbol_archive.py b/build/config/fuchsia/build_symbol_archive.py deleted file mode 100755 index 092566d..0000000 --- a/build/config/fuchsia/build_symbol_archive.py +++ /dev/null
@@ -1,66 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2018 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Creates a compressed archive of binary symbols derived from the unstripped -executables and libraries cataloged by "ids.txt".""" - -import argparse -import os -import subprocess -import sys -import tarfile -import tempfile - - -def main(args): - parser = argparse.ArgumentParser() - parser.add_argument('ids_txt', type=str, nargs=1, - help='Path to the ids.txt file.') - parser.add_argument('output_tarball', nargs=1, type=str, - help='Path which the tarball will be written to.') - parser.add_argument('--eu-strip', required=True, type=str, - help='Path to the the eu-strip tool.') - args = parser.parse_args(args) - - - stripped_tempfile = tempfile.NamedTemporaryFile() - - ids_txt = args.ids_txt[0] - build_ids_archive = tarfile.open(args.output_tarball[0], 'w:bz2') - for line in open(ids_txt, 'r'): - # debug_tempfile: The path which debug symbols will be written to. - # stripped_tempfile: The path which the stripped executable will be written - # to. This file is ignored and immediately deleted. - with tempfile.NamedTemporaryFile() as debug_tempfile, \ - tempfile.NamedTemporaryFile() as stripped_tempfile: - build_id, binary_path = line.strip().split(' ') - binary_abspath = os.path.abspath( - os.path.join(os.path.dirname(ids_txt), binary_path)) - - # Extract debugging symbols from the binary into their own file. - # The stripped executable binary is written to |debug_tempfile| and - # deleted. Writing to /dev/null would be preferable, but eu-strip - # disallows writing output to /dev/null. - subprocess.check_call([args.eu_strip, '-g', binary_abspath, - '-f', debug_tempfile.name, - '-o', stripped_tempfile.name]) - - # An empty result means that the source binary (most likely a prebuilt) - # didn't have debugging data to begin with. - if os.path.getsize(debug_tempfile.name) == 0: - continue - - # Archive the debugging symbols, placing them in a hierarchy keyed to the - # GNU build ID. The symbols reside in directories whose names are the - # first two characters of the build ID, with the symbol files themselves - # named after the remaining characters of the build ID. So, a symbol file - # with the build ID "deadbeef" would be located at the path 'de/adbeef'. - build_ids_archive.add(debug_tempfile.name, - '%s/%s' % (build_id[:2], build_id[2:])) - - -if __name__ == '__main__': - sys.exit(main(sys.argv[1:]))
diff --git a/build/config/fuchsia/package.gni b/build/config/fuchsia/package.gni index 55b43c0..9df27e9 100644 --- a/build/config/fuchsia/package.gni +++ b/build/config/fuchsia/package.gni
@@ -212,19 +212,12 @@ copy(target_name) { forward_variables_from(invoker, [ "testonly" ]) - # Allows dependent targets to make use of "ids.txt". - public_deps = [ - ":$_write_manifest_target", - ] - deps = [ ":$_bundle_target", ] data = [ _final_far_file, - - # Files specified here so that they can be read by isolated testbots. _package_info_path, _build_ids_file, ]
diff --git a/build/config/fuchsia/symbol_archive.gni b/build/config/fuchsia/symbol_archive.gni deleted file mode 100644 index cc8b23f..0000000 --- a/build/config/fuchsia/symbol_archive.gni +++ /dev/null
@@ -1,39 +0,0 @@ -# Copyright 2019 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -assert(is_fuchsia) - -# Creates a tarball of binaries' debug data, structured according -# to the ".build_ids" convention used by the symbolizer and GNU GDB. -# -# Parameters: -# ids_txt: The "ids.txt" file which lists the relative paths to unstripped -# executables and libraries, along with their build IDs. -# archive_name: The path to the compressed tarball that will be -# generated. -template("symbol_archive") { - action(target_name) { - _ids_txt = invoker.ids_txt - _build_ids = invoker.archive_name - - script = "//build/config/fuchsia/build_symbol_archive.py" - - inputs = [ - _ids_txt, - ] - - outputs = [ - _build_ids, - ] - - deps = invoker.deps - - args = [ - rebase_path(_ids_txt), - rebase_path(_build_ids), - "--eu-strip", - rebase_path("//buildtools/third_party/eu-strip/bin/eu-strip"), - ] - } -}
diff --git a/build/sanitizers/lsan_suppressions.cc b/build/sanitizers/lsan_suppressions.cc index 9834eef..2d1dba1 100644 --- a/build/sanitizers/lsan_suppressions.cc +++ b/build/sanitizers/lsan_suppressions.cc
@@ -91,9 +91,6 @@ "leak:blink::DOMWrapperWorld::Create\n" "leak:blink::ScriptState::Create\n" - // https://crbug.com/795148 - "leak:third_party/fontconfig/\n" - // PLEASE READ ABOVE BEFORE ADDING NEW SUPPRESSIONS. // End of suppressions.
diff --git a/build/win/BUILD.gn b/build/win/BUILD.gn index 9479363..d2e911e 100644 --- a/build/win/BUILD.gn +++ b/build/win/BUILD.gn
@@ -82,12 +82,11 @@ } group("runtime_libs") { - if (is_component_build || current_cpu != "arm64") { + data = [] + if (is_component_build) { # Copy the VS runtime DLLs into the isolate so that they don't have to be # preinstalled on the target machine. The debug runtimes have a "d" at - # the end. The UCRT files are needed for all non-arm64 builds because - # d3dcompiler_47.dll depends on them and they are missing on some Windows - # 7 machines. + # the end. if (is_debug) { vcrt_suffix = "d" } else { @@ -96,11 +95,27 @@ # These runtime files are copied to the output directory by the # vs_toolchain script that runs as part of toolchain configuration. - data = [ + data += [ "$root_out_dir/msvcp140${vcrt_suffix}.dll", "$root_out_dir/vccorlib140${vcrt_suffix}.dll", "$root_out_dir/vcruntime140${vcrt_suffix}.dll", - + ] + if (is_debug) { + data += [ "$root_out_dir/ucrtbased.dll" ] + } + if (is_asan) { + if (current_cpu == "x64") { + data += [ "$clang_base_path/lib/clang/$clang_version/lib/windows/clang_rt.asan_dynamic-x86_64.dll" ] + } else { + data += [ "$clang_base_path/lib/clang/$clang_version/lib/windows/clang_rt.asan_dynamic-i386.dll" ] + } + } + } + if (current_cpu != "arm64") { + # The UCRT files are needed for all non-arm64 builds because + # d3dcompiler_47.dll depends on them and they are missing on some Windows + # 7 machines. + data += [ # Universal Windows 10 CRT files "$root_out_dir/api-ms-win-core-console-l1-1-0.dll", "$root_out_dir/api-ms-win-core-datetime-l1-1-0.dll", @@ -142,19 +157,8 @@ "$root_out_dir/api-ms-win-crt-string-l1-1-0.dll", "$root_out_dir/api-ms-win-crt-time-l1-1-0.dll", "$root_out_dir/api-ms-win-crt-utility-l1-1-0.dll", - "$root_out_dir/ucrtbase${vcrt_suffix}.dll", + "$root_out_dir/ucrtbase.dll", ] - if (is_debug) { - # For debug builds we also need ucrtbase.dll for d3dcompiler_47.dll - data += [ "$root_out_dir/ucrtbase.dll" ] - } - if (is_asan) { - if (current_cpu == "x64") { - data += [ "$clang_base_path/lib/clang/$clang_version/lib/windows/clang_rt.asan_dynamic-x86_64.dll" ] - } else { - data += [ "$clang_base_path/lib/clang/$clang_version/lib/windows/clang_rt.asan_dynamic-i386.dll" ] - } - } } } }
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc index 9004f5b..12ce2ec9 100644 --- a/cc/layers/layer_impl.cc +++ b/cc/layers/layer_impl.cc
@@ -158,10 +158,13 @@ visible_layer_rect(), layer_to_content_scale_x, layer_to_content_scale_y); scaled_visible_layer_rect.Intersect(gfx::Rect(scaled_bounds)); + EffectNode* effect_node = GetEffectTree().Node(effect_tree_index_); state->SetAll(scaled_draw_transform, gfx::Rect(scaled_bounds), scaled_visible_layer_rect, draw_properties().clip_rect, draw_properties().is_clipped, contents_opaque, - draw_properties().opacity, SkBlendMode::kSrcOver, + draw_properties().opacity, + effect_node->has_render_surface ? SkBlendMode::kSrcOver + : effect_node->blend_mode, GetSortingContextId()); }
diff --git a/cc/test/layer_tree_pixel_resource_test.cc b/cc/test/layer_tree_pixel_resource_test.cc index 0050c722..72c00d9 100644 --- a/cc/test/layer_tree_pixel_resource_test.cc +++ b/cc/test/layer_tree_pixel_resource_test.cc
@@ -116,6 +116,13 @@ RunPixelTest(test_type_, content_root, expected_bitmap); } +void LayerTreeHostPixelResourceTest::RunPixelResourceTestWithLayerList( + scoped_refptr<Layer> root_layer, + base::FilePath file_name, + PropertyTrees* property_trees) { + RunPixelTestWithLayerList(test_type_, root_layer, file_name, property_trees); +} + ParameterizedPixelResourceTest::ParameterizedPixelResourceTest() : LayerTreeHostPixelResourceTest(::testing::get<0>(GetParam()), ::testing::get<1>(GetParam())) {}
diff --git a/cc/test/layer_tree_pixel_resource_test.h b/cc/test/layer_tree_pixel_resource_test.h index 29de0644..55f8500 100644 --- a/cc/test/layer_tree_pixel_resource_test.h +++ b/cc/test/layer_tree_pixel_resource_test.h
@@ -31,6 +31,10 @@ void RunPixelResourceTest(scoped_refptr<Layer> content_root, const SkBitmap& expected_bitmap); + void RunPixelResourceTestWithLayerList(scoped_refptr<Layer> root_layer, + base::FilePath file_name, + PropertyTrees* property_trees); + protected: PixelResourceTestCase test_case_; Layer::LayerMaskType mask_type_;
diff --git a/cc/test/layer_tree_pixel_test.cc b/cc/test/layer_tree_pixel_test.cc index a8a89de..8526bd0 100644 --- a/cc/test/layer_tree_pixel_test.cc +++ b/cc/test/layer_tree_pixel_test.cc
@@ -17,6 +17,7 @@ #include "cc/test/pixel_test_output_surface.h" #include "cc/test/pixel_test_utils.h" #include "cc/test/test_in_process_context_provider.h" +#include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_impl.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_result.h" @@ -33,6 +34,7 @@ LayerTreePixelTest::LayerTreePixelTest() : pixel_comparator_(new ExactPixelComparator(true)), test_type_(PIXEL_TEST_GL), + property_trees_(nullptr), pending_texture_mailbox_callbacks_(0) {} LayerTreePixelTest::~LayerTreePixelTest() = default; @@ -118,7 +120,16 @@ void LayerTreePixelTest::BeginTest() { Layer* target = readback_target_ ? readback_target_ : layer_tree_host()->root_layer(); - target->RequestCopyOfOutput(CreateCopyOutputRequest()); + if (!property_trees_) { + target->RequestCopyOfOutput(CreateCopyOutputRequest()); + } else { + layer_tree_host()->property_trees()->effect_tree.AddCopyRequest( + target->effect_tree_index(), CreateCopyOutputRequest()); + layer_tree_host() + ->property_trees() + ->effect_tree.Node(target->effect_tree_index()) + ->has_copy_request = true; + } PostSetNeedsCommitToMainThread(); } @@ -150,6 +161,8 @@ layer->SetIsDrawable(true); layer->SetBounds(rect.size()); layer->SetPosition(gfx::PointF(rect.origin())); + layer->SetOffsetToTransformParent( + gfx::Vector2dF(rect.origin().x(), rect.origin().y())); layer->SetBackgroundColor(color); return layer; } @@ -226,6 +239,48 @@ RunTest(CompositorMode::THREADED); } +void LayerTreePixelTest::RunPixelTestWithLayerList( + PixelTestType test_type, + scoped_refptr<Layer> root_layer, + base::FilePath file_name, + PropertyTrees* property_trees) { + test_type_ = test_type; + content_root_ = root_layer; + property_trees_ = property_trees; + readback_target_ = nullptr; + ref_file_ = file_name; + RunTest(CompositorMode::THREADED); +} + +void LayerTreePixelTest::InitializeForLayerListMode( + scoped_refptr<Layer>* root_layer, + PropertyTrees* property_trees) { + ClipNode clip_node; + property_trees->clip_tree.Insert(clip_node, 0); + + EffectNode root_effect; + root_effect.clip_id = 1; + root_effect.stable_id = 1; + root_effect.transform_id = 1; + root_effect.has_render_surface = true; + property_trees->effect_tree.Insert(root_effect, 0); + + ScrollNode scroll_node; + property_trees->scroll_tree.Insert(scroll_node, 0); + + TransformNode transform_node; + property_trees->transform_tree.Insert(transform_node, 0); + + *root_layer = Layer::Create(); + (*root_layer)->SetBounds(gfx::Size(100, 100)); + (*root_layer)->SetEffectTreeIndex(1); + (*root_layer)->SetClipTreeIndex(1); + (*root_layer)->SetScrollTreeIndex(1); + (*root_layer)->SetTransformTreeIndex(1); + (*root_layer) + ->set_property_tree_sequence_number(property_trees->sequence_number); +} + void LayerTreePixelTest::RunSingleThreadedPixelTest( PixelTestType test_type, scoped_refptr<Layer> content_root, @@ -250,10 +305,15 @@ } void LayerTreePixelTest::SetupTree() { - scoped_refptr<Layer> root = Layer::Create(); - root->SetBounds(content_root_->bounds()); - root->AddChild(content_root_); - layer_tree_host()->SetRootLayer(root); + if (property_trees_) { + layer_tree_host()->SetRootLayer(content_root_); + layer_tree_host()->SetPropertyTreesForTesting(property_trees_); + } else { + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(content_root_->bounds()); + root->AddChild(content_root_); + layer_tree_host()->SetRootLayer(root); + } LayerTreeTest::SetupTree(); }
diff --git a/cc/test/layer_tree_pixel_test.h b/cc/test/layer_tree_pixel_test.h index 3c95b739..4c37dc66 100644 --- a/cc/test/layer_tree_pixel_test.h +++ b/cc/test/layer_tree_pixel_test.h
@@ -11,6 +11,10 @@ #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "cc/test/layer_tree_test.h" +#include "cc/trees/clip_node.h" +#include "cc/trees/effect_node.h" +#include "cc/trees/scroll_node.h" +#include "cc/trees/transform_node.h" #include "components/viz/common/resources/single_release_callback.h" #include "ui/gl/gl_implementation.h" @@ -71,6 +75,12 @@ int border_width, SkColor border_color); + // Initializes the root layer and root PropertyTrees for layer list mode. + // In this mode, all other layers are direct children of |root_layer| and + // any property nodes are descendants of node id 1 in the respective trees. + void InitializeForLayerListMode(scoped_refptr<Layer>* root_layer, + PropertyTrees* property_trees); + void RunPixelTest(PixelTestType type, scoped_refptr<Layer> content_root, base::FilePath file_name); @@ -78,6 +88,11 @@ scoped_refptr<Layer> content_root, const SkBitmap& expected_bitmap); + void RunPixelTestWithLayerList(PixelTestType type, + scoped_refptr<Layer> root_layer, + base::FilePath file_name, + PropertyTrees* property_trees); + void RunSingleThreadedPixelTest(PixelTestType test_type, scoped_refptr<Layer> content_root, base::FilePath file_name); @@ -111,6 +126,7 @@ std::unique_ptr<PixelComparator> pixel_comparator_; PixelTestType test_type_; scoped_refptr<Layer> content_root_; + PropertyTrees* property_trees_; Layer* readback_target_; base::FilePath ref_file_; SkBitmap expected_bitmap_;
diff --git a/cc/trees/image_animation_controller.cc b/cc/trees/image_animation_controller.cc index 94902afe..eb92e3b 100644 --- a/cc/trees/image_animation_controller.cc +++ b/cc/trees/image_animation_controller.cc
@@ -17,6 +17,20 @@ // desired frame. const base::TimeDelta kAnimationResyncCutoff = base::TimeDelta::FromMinutes(5); +// Given the |desired_frame_time|, returns the time of the tick it should be +// snapped to. +base::TimeTicks SnappedTickTimeFromFrameTime( + const viz::BeginFrameArgs& args, + base::TimeTicks desired_frame_time) { + auto snapped_tick_time = + desired_frame_time.SnappedToNextTick(args.frame_time, args.interval); + DCHECK_GE(snapped_tick_time, desired_frame_time); + + if (snapped_tick_time != desired_frame_time) + snapped_tick_time -= args.interval; + return snapped_tick_time; +} + } // namespace ImageAnimationController::ImageAnimationController( @@ -55,7 +69,9 @@ const PaintImageIdFlatSet& ImageAnimationController::AnimateForSyncTree( const viz::BeginFrameArgs& args) { - TRACE_EVENT0("cc", "ImageAnimationController::AnimateImagesForSyncTree"); + TRACE_EVENT1("cc", "ImageAnimationController::AnimateImagesForSyncTree", + "frame_time_from_now", + (base::TimeTicks::Now() - args.frame_time).InMillisecondsF()); DCHECK(images_animated_on_sync_tree_.empty()); scheduler_.WillAnimate(); @@ -68,14 +84,21 @@ // Is anyone still interested in animating this image? state.UpdateStateFromDrivers(); - if (!state.ShouldAnimate()) + if (!state.ShouldAnimate()) { + TRACE_EVENT_INSTANT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), + "ShouldAnimate - early out", + TRACE_EVENT_SCOPE_THREAD); continue; + } // If we were able to advance this animation, invalidate it on the sync // tree. if (state.AdvanceFrame(args, enable_image_animation_resync_)) images_animated_on_sync_tree_.insert(id); + TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), + "AnimationState", TRACE_EVENT_SCOPE_THREAD, "state", + state.ToString()); // Update the next invalidation time to the earliest time at which we need // a frame to animate an image. // Note its important to check ShouldAnimate() here again since advancing to @@ -84,11 +107,11 @@ if (!state.ShouldAnimate()) continue; - DCHECK_GT(state.next_desired_frame_time(), args.frame_time); + DCHECK_GT(state.next_desired_tick_time(), args.frame_time); if (!next_invalidation_time.has_value()) { - next_invalidation_time.emplace(state.next_desired_frame_time()); + next_invalidation_time.emplace(state.next_desired_tick_time()); } else { - next_invalidation_time = std::min(state.next_desired_frame_time(), + next_invalidation_time = std::min(state.next_desired_tick_time(), next_invalidation_time.value()); } } @@ -118,10 +141,10 @@ continue; if (!next_invalidation_time.has_value()) { - next_invalidation_time.emplace(state.next_desired_frame_time()); + next_invalidation_time.emplace(state.next_desired_tick_time()); } else { next_invalidation_time = std::min(next_invalidation_time.value(), - state.next_desired_frame_time()); + state.next_desired_tick_time()); } } @@ -229,67 +252,45 @@ // and loop back to the first frame. size_t last_frame_index = frames_.size() - 1; if (completion_state_ != PaintImage::CompletionState::DONE && - pending_index_ == last_frame_index) + pending_index_ == last_frame_index) { return false; + } return true; } +// SCHEDULING +// The rate at which the animation progresses is decided by the frame duration, +// the time for which a particular frame should be displayed, specified in the +// metadata for the image. The animation is assumed to start from the frame_time +// of the first BeginFrame after the animation is registered and is visible. +// From here on, the time at which a frame should be displayed is the sum of +// durations for all previous frames of the animation. +// But, in order to align the work for the animation update with an impl frame, +// invalidations are requested at the beginning of the frame boundary in which +// the frame should be displayed. bool ImageAnimationController::AnimationState::AdvanceFrame( const viz::BeginFrameArgs& args, bool enable_image_animation_resync) { - AdvanceFrameInternal(args, enable_image_animation_resync); - const bool needs_invalidation = pending_index_ != active_index_; - - // If the animation will no longer continue, |next_desired_frame_time_| is - // not needed and will not be updated. - if (!ShouldAnimate()) - return needs_invalidation; - - DCHECK_GT(next_desired_frame_time_, args.frame_time); - auto snapped_next_desired_frame_time = - next_desired_frame_time_.SnappedToNextTick(args.frame_time, - args.interval); - if (snapped_next_desired_frame_time != next_desired_frame_time_) { - // If the next desired frame time was past the next tick but did not lie - // exactly at the frame tick, then snapping it would have set it to the tick - // after we wanted to display it. So set it to the tick we want to target. - snapped_next_desired_frame_time -= args.interval; - } - - // In the common case, |next_desired_frame_time_| should be >= to the - // |next_tick_time|, since the catch up loop skips frames until it reaches the - // first frame which should be drawn at or after the next tick. - // But in some cases we decode to skip the catch up and restart the regular - // animation from the next frame, in which case just snap it to the next tick. - auto next_tick_time = args.frame_time + args.interval; - next_desired_frame_time_ = - std::max(snapped_next_desired_frame_time, next_tick_time); - - return needs_invalidation; -} - -void ImageAnimationController::AnimationState::AdvanceFrameInternal( - const viz::BeginFrameArgs& args, - bool enable_image_animation_resync) { DCHECK(ShouldAnimate()); + const base::TimeTicks next_tick_time = args.frame_time + args.interval; - // Start the animation from the first frame, if not yet started. We don't need - // an invalidation here if the pending and active tree are both displaying the - // first frame. Its possible for the 2 to be different if the animation was - // reset, in which case we are starting again from the first frame on the - // pending tree. + // Start the animation from the first frame, if not yet started. The code + // falls through to catching up if the duration for the first frame is less + // than the interval. if (!animation_started_) { DCHECK_EQ(pending_index_, 0u); + animation_started_time_ = args.frame_time; next_desired_frame_time_ = args.frame_time + frames_[0].duration; + next_desired_tick_time_ = + SnappedTickTimeFromFrameTime(args, next_desired_frame_time_); animation_started_ = true; - return; } // Don't advance the animation if its not time yet to move to the next frame. - if (args.frame_time < next_desired_frame_time_) - return; + if (args.frame_time < next_desired_tick_time_) + return needs_invalidation(); // If the animation is more than 5 min out of date, we don't bother catching // up and start again from the current frame. @@ -297,23 +298,27 @@ // is already displaying the current frame. if (enable_image_animation_resync && args.frame_time - next_desired_frame_time_ > kAnimationResyncCutoff) { + TRACE_EVENT_INSTANT0("cc", "Resync - early out", TRACE_EVENT_SCOPE_THREAD); DCHECK_EQ(pending_index_, active_index_); next_desired_frame_time_ = args.frame_time + frames_[pending_index_].duration; - return; + next_desired_tick_time_ = + std::max(SnappedTickTimeFromFrameTime(args, next_desired_frame_time_), + next_tick_time); + return needs_invalidation(); } // Keep catching up the animation until we reach the frame we should be // displaying now. - // TODO(khushalsagar): Avoid unnecessary iterations for skipping whole loops - // in the animations. - size_t last_frame_index = frames_.size() - 1; + const size_t last_frame_index = frames_.size() - 1; size_t num_of_frames_advanced = 0u; - - base::TimeTicks next_frame_time = args.frame_time + args.interval; - while (next_desired_frame_time_ < next_frame_time && ShouldAnimate()) { + while (next_desired_tick_time_ < next_tick_time && ShouldAnimate()) { num_of_frames_advanced++; size_t next_frame_index = NextFrameIndex(); + TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("cc.debug"), + "FrameDurationAndIndex", TRACE_EVENT_SCOPE_THREAD, + "frame_index", next_frame_index, "duration", + frames_[next_frame_index].duration.InMillisecondsF()); base::TimeTicks next_desired_frame_time = next_desired_frame_time_ + frames_[next_frame_index].duration; @@ -332,12 +337,17 @@ next_desired_frame_time <= args.frame_time) { pending_index_ = 0u; next_desired_frame_time_ = args.frame_time + frames_[0].duration; + next_desired_tick_time_ = + std::max(SnappedTickTimeFromFrameTime(args, next_desired_frame_time_), + next_tick_time); repetitions_completed_ = 0; break; } pending_index_ = next_frame_index; next_desired_frame_time_ = next_desired_frame_time; + next_desired_tick_time_ = + SnappedTickTimeFromFrameTime(args, next_desired_frame_time_); // If we are advancing to the last frame and the image has been completely // loaded (which means that the frame count is known to be accurate), we @@ -352,6 +362,7 @@ last_num_frames_skipped_ = num_of_frames_advanced - 1u; UMA_HISTOGRAM_COUNTS_100000("AnimatedImage.NumOfFramesSkipped.Compositor", last_num_frames_skipped_); + return needs_invalidation(); } void ImageAnimationController::AnimationState::UpdateMetadata( @@ -419,6 +430,21 @@ // Don't reset the |active_index_|, tiles on the active tree still need it. } +std::string ImageAnimationController::AnimationState::ToString() const { + std::ostringstream str; + str << "paint_image_id[" << paint_image_id_ << "]\nrequested_repetitions[" + << requested_repetitions_ << "]\nrepetitions_completed[" + << requested_repetitions_ << "]\ndrivers[" << drivers_.size() + << "]\nactive_index[" << active_index_ << "]\npending_index[" + << pending_index_ << "]\nnext_desired_frame_time[" + << (next_desired_frame_time_ - animation_started_time_).InMillisecondsF() + << "]\nnext_desired_tick_time[" + << (next_desired_tick_time_ - animation_started_time_).InMillisecondsF() + << "]\nshould_animate_from_drivers[" << should_animate_from_drivers_ + << "]\ncompletion_state[" << static_cast<int>(completion_state_) << "]"; + return str.str(); +} + size_t ImageAnimationController::AnimationState::NextFrameIndex() const { if (!animation_started_) return 0u; @@ -538,6 +564,9 @@ } void ImageAnimationController::InvalidationScheduler::RequestInvalidation() { + TRACE_EVENT0( + "cc", + "ImageAnimationController::InvalidationScheduler::RequestInvalidation"); DCHECK_NE(state_, InvalidationState::kIdle); DCHECK_NE(state_, InvalidationState::kPendingInvalidation);
diff --git a/cc/trees/image_animation_controller.h b/cc/trees/image_animation_controller.h index 5ecdf78..d6f6c3c 100644 --- a/cc/trees/image_animation_controller.h +++ b/cc/trees/image_animation_controller.h
@@ -150,8 +150,8 @@ size_t pending_index() const { return pending_index_; } size_t active_index() const { return active_index_; } - base::TimeTicks next_desired_frame_time() const { - return next_desired_frame_time_; + base::TimeTicks next_desired_tick_time() const { + return next_desired_tick_time_; } const base::flat_set<AnimationDriver*>& drivers_for_testing() const { return drivers_; @@ -159,15 +159,15 @@ size_t last_num_frames_skipped_for_testing() const { return last_num_frames_skipped_; } + std::string ToString() const; private: - void AdvanceFrameInternal(const viz::BeginFrameArgs& args, - bool enable_image_animation_resync); void ResetAnimation(); size_t NextFrameIndex() const; bool is_complete() const { return completion_state_ == PaintImage::CompletionState::DONE; } + bool needs_invalidation() const { return pending_index_ != active_index_; } PaintImage::Id paint_image_id_ = PaintImage::kInvalidId; @@ -196,15 +196,22 @@ // The time at which we would like to display the next frame. This can be in // the past, for instance, if we pause the animation from the image becoming - // invisible. + // invisible. This time is updated strictly based on the animation timeline + // provided by the image. base::TimeTicks next_desired_frame_time_; + // The time of the next tick at which we want to invalidate and update the + // current frame. + base::TimeTicks next_desired_tick_time_; + // Set if there is at least one driver interested in animating this image, // cached from the last update. bool should_animate_from_drivers_ = false; // Set if the animation has been started. bool animation_started_ = false; + // Used for tracing, the time at which the animation was started. + base::TimeTicks animation_started_time_; // The last synchronized sequence id for resetting this animation. PaintImage::AnimationSequenceId reset_animation_sequence_id_ = 0;
diff --git a/cc/trees/image_animation_controller_unittest.cc b/cc/trees/image_animation_controller_unittest.cc index 62cc545..60b5b447 100644 --- a/cc/trees/image_animation_controller_unittest.cc +++ b/cc/trees/image_animation_controller_unittest.cc
@@ -821,7 +821,7 @@ std::vector<base::TimeDelta> expected_delays = { base::TimeDelta::FromMilliseconds(2), - base::TimeDelta::FromMilliseconds(3), + base::TimeDelta::FromMilliseconds(4), base::TimeDelta::FromMilliseconds(4)}; LoopOnceNoDelay(data.paint_image_id, frames, frames.size(), 0, expected_delays); @@ -846,43 +846,11 @@ controller_->RegisterAnimationDriver(data.paint_image_id, &driver); controller_->UpdateStateFromDrivers(); - // Since the animation hasn't started yet, it wants to advance immediately. + // Animation starts at 10s, we jump directly to the third frame. task_runner_->VerifyDelay(base::TimeDelta()); - // The first pending tree is what starts the animation and will just snap to - // the next vsync. auto invalidated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, WhichTree::PENDING_TREE), - 0u); - controller_->DidActivate(); - AdvanceNow(interval_); - - // For the next frame, the catch up loop forces us to skip the second frame - // and jump directly to the third frame. - task_runner_->VerifyDelay(interval_); - invalidated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); - ASSERT_EQ(invalidated_images.size(), 1u); - EXPECT_EQ(invalidated_images.count(data.paint_image_id), 1u); - EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, - WhichTree::PENDING_TREE), - 2u); - EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, - WhichTree::ACTIVE_TREE), - 0u); - controller_->DidActivate(); - AdvanceNow(interval_); - - // The 4th and 5th frame add up to the duration of the |interval_|, so the - // catch up look jumps us to the 5th frame. - task_runner_->VerifyDelay(interval_); - invalidated_images = controller_->AnimateForSyncTree(BeginFrameArgs()); - ASSERT_EQ(invalidated_images.size(), 1u); - EXPECT_EQ(invalidated_images.count(data.paint_image_id), 1u); - EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, - WhichTree::PENDING_TREE), - 4u); - EXPECT_EQ(controller_->GetFrameIndexForImage(data.paint_image_id, - WhichTree::ACTIVE_TREE), 2u); controller_->DidActivate();
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc index 73f2e910..1113f891 100644 --- a/cc/trees/layer_tree_host.cc +++ b/cc/trees/layer_tree_host.cc
@@ -1845,6 +1845,11 @@ return LayerListReverseIterator<Layer>(nullptr); } +void LayerTreeHost::SetPropertyTreesForTesting( + const PropertyTrees* property_trees) { + property_trees_ = *property_trees; +} + void LayerTreeHost::SetNeedsDisplayOnAllLayers() { for (auto* layer : *this) layer->SetNeedsDisplay();
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h index 5155946..b7713f32 100644 --- a/cc/trees/layer_tree_host.h +++ b/cc/trees/layer_tree_host.h
@@ -484,6 +484,8 @@ PropertyTrees* property_trees() { return &property_trees_; } const PropertyTrees* property_trees() const { return &property_trees_; } + void SetPropertyTreesForTesting(const PropertyTrees* property_trees); + void SetNeedsDisplayOnAllLayers(); void RegisterLayer(Layer* layer);
diff --git a/cc/trees/layer_tree_host_pixeltest_masks.cc b/cc/trees/layer_tree_host_pixeltest_masks.cc index e58aadb..19e3e2a0 100644 --- a/cc/trees/layer_tree_host_pixeltest_masks.cc +++ b/cc/trees/layer_tree_host_pixeltest_masks.cc
@@ -93,6 +93,332 @@ base::FilePath(FILE_PATH_LITERAL("mask_of_layer.png"))); } +class LayerTreeHostLayerListPixelTest : public ParameterizedPixelResourceTest { + void InitializeSettings(LayerTreeSettings* settings) override { + settings->use_layer_lists = true; + } +}; + +INSTANTIATE_PIXEL_RESOURCE_TEST_SUITE_P(LayerTreeHostLayerListPixelTest); + +TEST_P(LayerTreeHostLayerListPixelTest, MaskWithEffect) { + PropertyTrees property_trees; + scoped_refptr<Layer> root_layer; + InitializeForLayerListMode(&root_layer, &property_trees); + + EffectNode isolation_effect; + isolation_effect.clip_id = 1; + isolation_effect.stable_id = 2; + isolation_effect.has_render_surface = true; + isolation_effect.transform_id = 1; + property_trees.effect_tree.Insert(isolation_effect, 1); + + EffectNode mask_effect; + mask_effect.clip_id = 1; + mask_effect.stable_id = 2; + mask_effect.transform_id = 1; + mask_effect.blend_mode = SkBlendMode::kDstIn; + property_trees.effect_tree.Insert(mask_effect, 2); + + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); + background->set_property_tree_sequence_number(property_trees.sequence_number); + background->SetClipTreeIndex(1); + background->SetEffectTreeIndex(1); + background->SetScrollTreeIndex(1); + background->SetTransformTreeIndex(1); + root_layer->AddChild(background); + + scoped_refptr<SolidColorLayer> green = + CreateSolidColorLayer(gfx::Rect(25, 25, 50, 50), kCSSGreen); + green->set_property_tree_sequence_number(property_trees.sequence_number); + green->SetClipTreeIndex(1); + green->SetEffectTreeIndex(2); + green->SetScrollTreeIndex(1); + green->SetTransformTreeIndex(1); + + root_layer->AddChild(green); + + gfx::Size mask_bounds(50, 50); + MaskContentLayerClient client(mask_bounds); + + scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); + mask->SetOffsetToTransformParent(gfx::Vector2dF(25, 25)); + mask->set_property_tree_sequence_number(property_trees.sequence_number); + mask->SetBounds(mask_bounds); + mask->SetIsDrawable(true); + mask->SetClipTreeIndex(1); + mask->SetEffectTreeIndex(3); + mask->SetScrollTreeIndex(1); + mask->SetTransformTreeIndex(1); + root_layer->AddChild(mask); + + RunPixelResourceTestWithLayerList( + root_layer, base::FilePath(FILE_PATH_LITERAL("mask_with_effect.png")), + &property_trees); +} + +// Tests a situation in which there is no other content in the target +// render surface that the mask applies to. In this situation, the mask +// should have no effect on the rendered output. +TEST_P(LayerTreeHostLayerListPixelTest, MaskWithEffectNoContentToMask) { + PropertyTrees property_trees; + scoped_refptr<Layer> root_layer; + InitializeForLayerListMode(&root_layer, &property_trees); + + EffectNode isolation_effect; + isolation_effect.clip_id = 1; + isolation_effect.stable_id = 2; + isolation_effect.has_render_surface = true; + isolation_effect.transform_id = 1; + property_trees.effect_tree.Insert(isolation_effect, 1); + + EffectNode mask_effect; + mask_effect.clip_id = 1; + mask_effect.stable_id = 2; + mask_effect.transform_id = 1; + mask_effect.blend_mode = SkBlendMode::kDstIn; + property_trees.effect_tree.Insert(mask_effect, 2); + + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorRED); + background->set_property_tree_sequence_number(property_trees.sequence_number); + background->SetClipTreeIndex(1); + background->SetEffectTreeIndex(1); + background->SetScrollTreeIndex(1); + background->SetTransformTreeIndex(1); + root_layer->AddChild(background); + + gfx::Size mask_bounds(50, 50); + MaskContentLayerClient client(mask_bounds); + + scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); + mask->SetOffsetToTransformParent(gfx::Vector2dF(0, 0)); + mask->set_property_tree_sequence_number(property_trees.sequence_number); + mask->SetBounds(mask_bounds); + mask->SetIsDrawable(true); + mask->SetClipTreeIndex(1); + mask->SetEffectTreeIndex(3); + mask->SetScrollTreeIndex(1); + mask->SetTransformTreeIndex(1); + root_layer->AddChild(mask); + + RunPixelResourceTestWithLayerList( + root_layer, + base::FilePath(FILE_PATH_LITERAL("mask_with_effect_no_content.png")), + &property_trees); +} + +TEST_P(LayerTreeHostLayerListPixelTest, ScaledMaskWithEffect) { + PropertyTrees property_trees; + scoped_refptr<Layer> root_layer; + InitializeForLayerListMode(&root_layer, &property_trees); + + EffectNode isolation_effect; + isolation_effect.clip_id = 1; + isolation_effect.stable_id = 2; + isolation_effect.has_render_surface = true; + isolation_effect.transform_id = 1; + property_trees.effect_tree.Insert(isolation_effect, 1); + + EffectNode mask_effect; + mask_effect.clip_id = 1; + mask_effect.stable_id = 2; + mask_effect.transform_id = 2; + mask_effect.blend_mode = SkBlendMode::kDstIn; + property_trees.effect_tree.Insert(mask_effect, 2); + + // Scale the mask with a non-integral transform. This will trigger the + // AA path in the renderer. + TransformNode transform; + transform.local = gfx::Transform(); + transform.local.Scale(1.5, 1.5); + property_trees.transform_tree.Insert(transform, 1); + + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); + background->set_property_tree_sequence_number(property_trees.sequence_number); + background->SetClipTreeIndex(1); + background->SetEffectTreeIndex(1); + background->SetScrollTreeIndex(1); + background->SetTransformTreeIndex(1); + root_layer->AddChild(background); + + scoped_refptr<SolidColorLayer> green = + CreateSolidColorLayer(gfx::Rect(25, 25, 50, 50), kCSSGreen); + green->set_property_tree_sequence_number(property_trees.sequence_number); + green->SetClipTreeIndex(1); + green->SetEffectTreeIndex(2); + green->SetScrollTreeIndex(1); + green->SetTransformTreeIndex(1); + + root_layer->AddChild(green); + + gfx::Size mask_bounds(50, 50); + MaskContentLayerClient client(mask_bounds); + + scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); + mask->SetOffsetToTransformParent(gfx::Vector2dF(25, 25)); + mask->set_property_tree_sequence_number(property_trees.sequence_number); + mask->SetBounds(mask_bounds); + mask->SetIsDrawable(true); + mask->SetClipTreeIndex(1); + mask->SetEffectTreeIndex(3); + mask->SetScrollTreeIndex(1); + mask->SetTransformTreeIndex(2); + root_layer->AddChild(mask); + + float percentage_pixels_large_error = 2.5f; // 2.5%, ~250px / (100*100) + float percentage_pixels_small_error = 0.0f; + float average_error_allowed_in_bad_pixels = 100.0f; + int large_error_allowed = 256; + int small_error_allowed = 0; + pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( + true, // discard_alpha + percentage_pixels_large_error, percentage_pixels_small_error, + average_error_allowed_in_bad_pixels, large_error_allowed, + small_error_allowed); + + RunPixelResourceTestWithLayerList( + root_layer, + base::FilePath(FILE_PATH_LITERAL("scaled_mask_with_effect.png")), + &property_trees); +} + +TEST_P(LayerTreeHostLayerListPixelTest, MaskWithEffectDifferentSize) { + PropertyTrees property_trees; + scoped_refptr<Layer> root_layer; + InitializeForLayerListMode(&root_layer, &property_trees); + + EffectNode isolation_effect; + isolation_effect.clip_id = 1; + isolation_effect.stable_id = 2; + isolation_effect.has_render_surface = true; + isolation_effect.transform_id = 1; + property_trees.effect_tree.Insert(isolation_effect, 1); + + EffectNode mask_effect; + mask_effect.clip_id = 1; + mask_effect.stable_id = 2; + mask_effect.transform_id = 1; + mask_effect.blend_mode = SkBlendMode::kDstIn; + property_trees.effect_tree.Insert(mask_effect, 2); + + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); + background->set_property_tree_sequence_number(property_trees.sequence_number); + background->SetClipTreeIndex(1); + background->SetEffectTreeIndex(1); + background->SetScrollTreeIndex(1); + background->SetTransformTreeIndex(1); + root_layer->AddChild(background); + + scoped_refptr<SolidColorLayer> green = + CreateSolidColorLayer(gfx::Rect(25, 25, 50, 50), kCSSGreen); + green->set_property_tree_sequence_number(property_trees.sequence_number); + green->SetClipTreeIndex(1); + green->SetEffectTreeIndex(2); + green->SetScrollTreeIndex(1); + green->SetTransformTreeIndex(1); + + root_layer->AddChild(green); + + gfx::Size mask_bounds(25, 25); + MaskContentLayerClient client(mask_bounds); + + scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); + mask->SetOffsetToTransformParent(gfx::Vector2dF(25, 25)); + mask->set_property_tree_sequence_number(property_trees.sequence_number); + mask->SetBounds(mask_bounds); + mask->SetIsDrawable(true); + mask->SetClipTreeIndex(1); + mask->SetEffectTreeIndex(3); + mask->SetScrollTreeIndex(1); + mask->SetTransformTreeIndex(1); + root_layer->AddChild(mask); + + // The mask is half the size of thing it's masking. In layer-list mode, + // the mask is not automatically scaled to match the other layer. + RunPixelResourceTestWithLayerList( + root_layer, + base::FilePath(FILE_PATH_LITERAL("mask_with_effect_different_size.png")), + &property_trees); +} + +TEST_P(LayerTreeHostLayerListPixelTest, ImageMaskWithEffect) { + PropertyTrees property_trees; + scoped_refptr<Layer> root_layer; + InitializeForLayerListMode(&root_layer, &property_trees); + + EffectNode isolation_effect; + isolation_effect.clip_id = 1; + isolation_effect.stable_id = 2; + isolation_effect.has_render_surface = true; + isolation_effect.transform_id = 1; + property_trees.effect_tree.Insert(isolation_effect, 1); + + EffectNode mask_effect; + mask_effect.clip_id = 1; + mask_effect.stable_id = 2; + mask_effect.transform_id = 1; + mask_effect.blend_mode = SkBlendMode::kDstIn; + property_trees.effect_tree.Insert(mask_effect, 2); + + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); + background->set_property_tree_sequence_number(property_trees.sequence_number); + background->SetClipTreeIndex(1); + background->SetEffectTreeIndex(1); + background->SetScrollTreeIndex(1); + background->SetTransformTreeIndex(1); + root_layer->AddChild(background); + + scoped_refptr<SolidColorLayer> green = + CreateSolidColorLayer(gfx::Rect(25, 25, 50, 50), kCSSGreen); + green->set_property_tree_sequence_number(property_trees.sequence_number); + green->SetClipTreeIndex(1); + green->SetEffectTreeIndex(2); + green->SetScrollTreeIndex(1); + green->SetTransformTreeIndex(1); + + root_layer->AddChild(green); + + gfx::Size mask_bounds(50, 50); + MaskContentLayerClient client(mask_bounds); + + scoped_refptr<PictureImageLayer> mask = PictureImageLayer::Create(); + mask->SetOffsetToTransformParent(gfx::Vector2dF(25, 25)); + mask->set_property_tree_sequence_number(property_trees.sequence_number); + mask->SetBounds(mask_bounds); + mask->SetIsDrawable(true); + mask->SetClipTreeIndex(1); + mask->SetEffectTreeIndex(3); + mask->SetScrollTreeIndex(1); + mask->SetTransformTreeIndex(1); + + sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(200, 200); + SkCanvas* canvas = surface->getCanvas(); + canvas->scale(SkIntToScalar(4), SkIntToScalar(4)); + scoped_refptr<DisplayItemList> mask_display_list = + client.PaintContentsToDisplayList( + ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); + mask_display_list->Raster(canvas); + mask->SetImage(PaintImageBuilder::WithDefault() + .set_id(PaintImage::GetNextId()) + .set_image(surface->makeImageSnapshot(), + PaintImage::GetNextContentId()) + .TakePaintImage(), + SkMatrix::I(), false); + root_layer->AddChild(mask); + + // The mask is half the size of thing it's masking. In layer-list mode, + // the mask is not automatically scaled to match the other layer. + RunPixelResourceTestWithLayerList( + root_layer, + base::FilePath(FILE_PATH_LITERAL("image_mask_with_effect.png")), + &property_trees); +} + TEST_P(LayerTreeHostMasksPixelTest, ImageMaskOfLayer) { scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE);
diff --git a/chrome/VERSION b/chrome/VERSION index 119dd0e..fd62bb8 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=74 MINOR=0 -BUILD=3722 +BUILD=3723 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java index 8b11a7e..60cbe63 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -201,6 +201,7 @@ "ContentSuggestionsScrollToLoad"; public static final String CONTENT_SUGGESTIONS_THUMBNAIL_DOMINANT_COLOR = "ContentSuggestionsThumbnailDominantColor"; + public static final String CONTEXTUAL_SEARCH_DEFINITIONS = "ContextualSearchDefinitions"; public static final String CONTEXTUAL_SEARCH_ML_TAP_SUPPRESSION = "ContextualSearchMlTapSuppression"; public static final String CONTEXTUAL_SEARCH_SECOND_TAP = "ContextualSearchSecondTap";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index 449e828..dccd87b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -100,6 +100,7 @@ import org.chromium.chrome.browser.metrics.LaunchMetrics; import org.chromium.chrome.browser.metrics.MainIntentBehaviorMetrics; import org.chromium.chrome.browser.modaldialog.TabModalLifetimeHandler; +import org.chromium.chrome.browser.modaldialog.TabModalPresenter; import org.chromium.chrome.browser.multiwindow.MultiInstanceChromeTabbedActivity; import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; import org.chromium.chrome.browser.native_page.NativePageAssassin; @@ -2281,7 +2282,7 @@ // If the current active tab is showing a tab modal dialog, an app menu shouldn't be shown // in any cases, e.g. when a hardware menu button is clicked. Tab tab = getActivityTab(); - if (tab != null && tab.isShowingTabModalDialog()) return false; + if (TabModalPresenter.isDialogShowing(tab)) return false; return super.shouldShowAppMenu(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java index e0c7821..2986804 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
@@ -21,7 +21,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.support.annotation.Nullable; @@ -333,23 +332,25 @@ } long id = ids[0]; - Uri uri = DownloadManagerDelegate.getContentUriFromDownloadManager(context, id); - if (uri == null) { - DownloadManagerService.openDownloadsPage(context); - return; - } + DownloadManagerBridge.queryDownloadResult(id, result -> { + if (result.contentUri == null) { + DownloadManagerService.openDownloadsPage(context); + return; + } - String downloadFilename = IntentUtils.safeGetStringExtra( - intent, DownloadNotificationService.EXTRA_DOWNLOAD_FILE_PATH); - boolean isSupportedMimeType = IntentUtils.safeGetBooleanExtra( - intent, DownloadNotificationService.EXTRA_IS_SUPPORTED_MIME_TYPE, false); - boolean isOffTheRecord = IntentUtils.safeGetBooleanExtra( - intent, DownloadNotificationService.EXTRA_IS_OFF_THE_RECORD, false); - String originalUrl = IntentUtils.safeGetStringExtra(intent, Intent.EXTRA_ORIGINATING_URI); - String referrer = IntentUtils.safeGetStringExtra(intent, Intent.EXTRA_REFERRER); - DownloadManagerService.openDownloadedContent(context, downloadFilename, isSupportedMimeType, - isOffTheRecord, contentId.id, id, originalUrl, referrer, - DownloadMetrics.DownloadOpenSource.NOTIFICATION); + String downloadFilename = IntentUtils.safeGetStringExtra( + intent, DownloadNotificationService.EXTRA_DOWNLOAD_FILE_PATH); + boolean isSupportedMimeType = IntentUtils.safeGetBooleanExtra( + intent, DownloadNotificationService.EXTRA_IS_SUPPORTED_MIME_TYPE, false); + boolean isOffTheRecord = IntentUtils.safeGetBooleanExtra( + intent, DownloadNotificationService.EXTRA_IS_OFF_THE_RECORD, false); + String originalUrl = + IntentUtils.safeGetStringExtra(intent, Intent.EXTRA_ORIGINATING_URI); + String referrer = IntentUtils.safeGetStringExtra(intent, Intent.EXTRA_REFERRER); + DownloadManagerService.openDownloadedContent(context, downloadFilename, + isSupportedMimeType, isOffTheRecord, contentId.id, id, originalUrl, referrer, + DownloadMetrics.DownloadOpenSource.NOTIFICATION); + }); } @Nullable
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java index b551d5c..764e012 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadController.java
@@ -245,7 +245,7 @@ * @param info Download information about the download. */ static void enqueueDownloadManagerRequest(final DownloadInfo info) { - DownloadManagerService.getDownloadManagerService().enqueueDownloadManagerRequest( + DownloadManagerService.getDownloadManagerService().enqueueNewDownload( new DownloadItem(true, info), true); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadItem.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadItem.java index 3195754..02f5fc9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadItem.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadItem.java
@@ -47,13 +47,6 @@ } /** - * @return whether the download item has a valid system download ID. - */ - public boolean hasSystemDownloadId() { - return mDownloadId != INVALID_DOWNLOAD_ID; - } - - /** * @return System download ID from the Android DownloadManager. */ public long getSystemDownloadId() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerBridge.java new file mode 100644 index 0000000..34703fd --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerBridge.java
@@ -0,0 +1,393 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.download; + +import android.app.DownloadManager; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.support.v4.app.NotificationManagerCompat; +import android.text.TextUtils; + +import org.chromium.base.Callback; +import org.chromium.base.ContextUtils; +import org.chromium.base.Log; +import org.chromium.base.ThreadUtils; +import org.chromium.base.task.AsyncTask; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * A wrapper for Android DownloadManager to provide utility functions. + */ +public class DownloadManagerBridge { + private static final String TAG = "DownloadDelegate"; + private static final String DOWNLOAD_DIRECTORY = "Download"; + private static final long INVALID_SYSTEM_DOWNLOAD_ID = -1; + private static final String DOWNLOAD_ID_MAPPINGS_FILE_NAME = "download_id_mappings"; + private static final Object sLock = new Object(); + + /** + * Result for querying the Android DownloadManager. + */ + public static class DownloadQueryResult { + public final long downloadId; + public int downloadStatus; + public String fileName; + public String mimeType; + public Uri contentUri; + public long lastModifiedTime; + public long bytesDownloaded; + public long bytesTotal; + public int failureReason; + + public DownloadQueryResult(long downloadId) { + this.downloadId = downloadId; + } + } + + /** + * Contains the request params associated with a call to {@link + * DownloadManagerBridge.enqueueNewDownload}. + */ + public static class DownloadEnqueueRequest { + public String url; + public String fileName; + public String description; + public String mimeType; + public String cookie; + public String referrer; + public String userAgent; + public boolean notifyCompleted; + } + + /** Contains the results from the call to {@link DownloadManagerBridge.enqueueNewDownload}. */ + public static class DownloadEnqueueResponse { + public long downloadId = INVALID_SYSTEM_DOWNLOAD_ID; + public boolean result; + public int failureReason; + public long startTime; + } + + /** + * Adds a download to the Android DownloadManager. + * @see android.app.DownloadManager#addCompletedDownload(String, String, boolean, String, + * String, long, boolean) + */ + public static long addCompletedDownload(String fileName, String description, String mimeType, + String filePath, long fileSizeBytes, String originalUrl, String referer, + String downloadGuid) { + assert !ThreadUtils.runningOnUiThread(); + DownloadManager manager = + (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE); + NotificationManagerCompat notificationManager = + NotificationManagerCompat.from(getContext()); + boolean useSystemNotification = !notificationManager.areNotificationsEnabled(); + long downloadId = -1; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { + Class<?> c = manager.getClass(); + try { + Class[] args = {String.class, String.class, boolean.class, String.class, + String.class, long.class, boolean.class, Uri.class, Uri.class}; + Method method = c.getMethod("addCompletedDownload", args); + // OriginalUri has to be null or non-empty http(s) scheme. + Uri originalUri = DownloadUtils.parseOriginalUrl(originalUrl); + Uri refererUri = TextUtils.isEmpty(referer) ? null : Uri.parse(referer); + downloadId = (Long) method.invoke(manager, fileName, description, true, mimeType, + filePath, fileSizeBytes, useSystemNotification, originalUri, refererUri); + } catch (SecurityException e) { + Log.e(TAG, "Cannot access the needed method."); + } catch (NoSuchMethodException e) { + Log.e(TAG, "Cannot find the needed method."); + } catch (InvocationTargetException e) { + Log.e(TAG, "Error calling the needed method."); + } catch (IllegalAccessException e) { + Log.e(TAG, "Error accessing the needed method."); + } + } else { + downloadId = manager.addCompletedDownload(fileName, description, true, mimeType, + filePath, fileSizeBytes, useSystemNotification); + } + addDownloadIdMapping(downloadId, downloadGuid); + return downloadId; + } + + /** + * Removes a download from Android DownloadManager. + * @param downloadGuid The GUID of the download. + * @param externallyRemoved If download is externally removed in other application. + */ + public static void removeCompletedDownload(String downloadGuid, boolean externallyRemoved) { + long downloadId = removeDownloadIdMapping(downloadGuid); + + // Let Android DownloadManager to remove download only if the user removed the file in + // Chrome. If the user renamed or moved the file, Chrome should keep it intact. + if (downloadId != INVALID_SYSTEM_DOWNLOAD_ID && !externallyRemoved) { + DownloadManager manager = + (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE); + manager.remove(downloadId); + } + } + + /** + * Sends the download request to Android download manager. If |notifyCompleted| is true, + * a notification will be sent to the user once download is complete and the downloaded + * content will be saved to the public directory on external storage. Otherwise, the + * download will be saved in the app directory and user will not get any notifications + * after download completion. + * This will be used by OMA downloads as we need Android DownloadManager to encrypt the content. + * + * @param request The download request params. + * @param callback The callback to be executed after the download request is enqueued. + */ + public static void enqueueNewDownload( + DownloadEnqueueRequest request, Callback<DownloadEnqueueResponse> callback) { + new EnqueueNewDownloadTask(request, callback) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + /** + * Query the Android DownloadManager for download status. + * @param downloadId The id of the download. + * @param callback Callback to be notified when query completes. + */ + public static void queryDownloadResult( + long downloadId, Callback<DownloadQueryResult> callback) { + DownloadQueryTask task = new DownloadQueryTask(downloadId, callback); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + /** + * Query the Android DownloadManager for download status. + * @param downloadId The id of the download. + */ + public static DownloadQueryResult queryDownloadResult(long downloadId) { + assert !ThreadUtils.runningOnUiThread(); + DownloadQueryResult result = new DownloadQueryResult(downloadId); + DownloadManager manager = + (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE); + Cursor c = manager.query(new DownloadManager.Query().setFilterById(downloadId)); + if (c == null) { + result.downloadStatus = DownloadManagerService.DownloadStatus.CANCELLED; + return result; + } + result.downloadStatus = DownloadManagerService.DownloadStatus.IN_PROGRESS; + if (c.moveToNext()) { + int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS)); + result.downloadStatus = getDownloadStatus(status); + result.fileName = c.getString(c.getColumnIndex(DownloadManager.COLUMN_TITLE)); + result.failureReason = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_REASON)); + result.lastModifiedTime = + c.getLong(c.getColumnIndex(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP)); + result.bytesDownloaded = + c.getLong(c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); + result.bytesTotal = + c.getLong(c.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); + } else { + result.downloadStatus = DownloadManagerService.DownloadStatus.CANCELLED; + } + c.close(); + + result.contentUri = manager.getUriForDownloadedFile(downloadId); + result.mimeType = manager.getMimeTypeForDownloadedFile(downloadId); + + return result; + } + + /** + * Inserts a new download ID mapping into the SharedPreferences + * @param downloadId system download ID from Android DownloadManager. + * @param downloadGuid Download GUID. + */ + private static void addDownloadIdMapping(long downloadId, String downloadGuid) { + synchronized (sLock) { + SharedPreferences sharedPrefs = getSharedPreferences(); + SharedPreferences.Editor editor = sharedPrefs.edit(); + editor.putLong(downloadGuid, downloadId); + editor.apply(); + } + } + + /** + * Removes a download Id mapping from the SharedPreferences given the download GUID. + * @param downloadGuid Download GUID. + * @return the Android DownloadManager's download ID that is removed, or + * INVALID_SYSTEM_DOWNLOAD_ID if it is not found. + */ + private static long removeDownloadIdMapping(String downloadGuid) { + long downloadId = INVALID_SYSTEM_DOWNLOAD_ID; + synchronized (sLock) { + SharedPreferences sharedPrefs = getSharedPreferences(); + downloadId = sharedPrefs.getLong(downloadGuid, INVALID_SYSTEM_DOWNLOAD_ID); + if (downloadId != INVALID_SYSTEM_DOWNLOAD_ID) { + SharedPreferences.Editor editor = sharedPrefs.edit(); + editor.remove(downloadGuid); + editor.apply(); + } + } + return downloadId; + } + + /** @return The android DownloadManager's download ID for the given download. */ + private static long getDownloadIdForDownloadGuid(String downloadGuid) { + return getSharedPreferences().getLong(downloadGuid, INVALID_SYSTEM_DOWNLOAD_ID); + } + + /** + * Lazily retrieve the SharedPreferences when needed. Since download operations are not very + * frequent, no need to load all SharedPreference entries into a hashmap in the memory. + * @return the SharedPreferences instance. + */ + private static SharedPreferences getSharedPreferences() { + return ContextUtils.getApplicationContext().getSharedPreferences( + DOWNLOAD_ID_MAPPINGS_FILE_NAME, Context.MODE_PRIVATE); + } + + private static Context getContext() { + return ContextUtils.getApplicationContext(); + } + + private static int getDownloadStatus(int downloadManagerStatus) { + switch (downloadManagerStatus) { + case DownloadManager.STATUS_SUCCESSFUL: + return DownloadManagerService.DownloadStatus.COMPLETE; + case DownloadManager.STATUS_FAILED: + return DownloadManagerService.DownloadStatus.FAILED; + default: + return DownloadManagerService.DownloadStatus.IN_PROGRESS; + } + } + + /** + * Async task to query download status from Android DownloadManager + */ + private static class DownloadQueryTask extends AsyncTask<DownloadQueryResult> { + private final long mDownloadId; + private final Callback<DownloadQueryResult> mCallback; + + public DownloadQueryTask(long downloadId, Callback<DownloadQueryResult> callback) { + mDownloadId = downloadId; + mCallback = callback; + } + + @Override + public DownloadQueryResult doInBackground() { + return queryDownloadResult(mDownloadId); + } + + @Override + protected void onPostExecute(DownloadQueryResult result) { + mCallback.onResult(result); + } + } + + /** + * Async task to enqueue a download request into DownloadManager. + */ + private static class EnqueueNewDownloadTask extends AsyncTask<Boolean> { + private final DownloadEnqueueRequest mEnqueueRequest; + private final Callback<DownloadEnqueueResponse> mCallback; + private long mDownloadId; + private int mFailureReason; + private long mStartTime; + + public EnqueueNewDownloadTask( + DownloadEnqueueRequest enqueueRequest, Callback<DownloadEnqueueResponse> callback) { + mEnqueueRequest = enqueueRequest; + mCallback = callback; + } + + @Override + public Boolean doInBackground() { + DownloadManager.Request request; + try { + request = new DownloadManager.Request(Uri.parse(mEnqueueRequest.url)); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Cannot download non http or https scheme"); + // Use ERROR_UNHANDLED_HTTP_CODE so that it will be treated as a server error. + mFailureReason = DownloadManager.ERROR_UNHANDLED_HTTP_CODE; + return false; + } + + request.setMimeType(mEnqueueRequest.mimeType); + try { + if (mEnqueueRequest.notifyCompleted) { + if (mEnqueueRequest.fileName != null) { + // Set downloaded file destination to /sdcard/Download or, should it be + // set to one of several Environment.DIRECTORY* dirs depending on mimetype? + request.setDestinationInExternalPublicDir( + Environment.DIRECTORY_DOWNLOADS, mEnqueueRequest.fileName); + } + } else { + File dir = new File(getContext().getExternalFilesDir(null), DOWNLOAD_DIRECTORY); + if (dir.mkdir() || dir.isDirectory()) { + File file = new File(dir, mEnqueueRequest.fileName); + request.setDestinationUri(Uri.fromFile(file)); + } else { + Log.e(TAG, "Cannot create download directory"); + mFailureReason = DownloadManager.ERROR_FILE_ERROR; + return false; + } + } + } catch (IllegalStateException e) { + Log.e(TAG, "Cannot create download directory"); + mFailureReason = DownloadManager.ERROR_FILE_ERROR; + return false; + } + + if (mEnqueueRequest.notifyCompleted) { + // Let this downloaded file be scanned by MediaScanner - so that it can + // show up in Gallery app, for example. + request.allowScanningByMediaScanner(); + request.setNotificationVisibility( + DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + } else { + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE); + } + String description = mEnqueueRequest.description; + if (TextUtils.isEmpty(description)) { + description = mEnqueueRequest.fileName; + } + request.setDescription(description); + request.setTitle(mEnqueueRequest.fileName); + request.addRequestHeader("Cookie", mEnqueueRequest.cookie); + request.addRequestHeader("referrer", mEnqueueRequest.referrer); + request.addRequestHeader("User-Agent", mEnqueueRequest.userAgent); + + DownloadManager manager = + (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE); + try { + mStartTime = System.currentTimeMillis(); + mDownloadId = manager.enqueue(request); + } catch (IllegalArgumentException e) { + // See crbug.com/143499 for more details. + Log.e(TAG, "Download failed: " + e); + mFailureReason = DownloadManager.ERROR_UNKNOWN; + return false; + } catch (RuntimeException e) { + // See crbug.com/490442 for more details. + Log.e(TAG, "Failed to create target file on the external storage: " + e); + mFailureReason = DownloadManager.ERROR_FILE_ERROR; + return false; + } + return true; + } + + @Override + protected void onPostExecute(Boolean result) { + DownloadEnqueueResponse enqueueResult = new DownloadEnqueueResponse(); + enqueueResult.result = result; + enqueueResult.failureReason = mFailureReason; + enqueueResult.downloadId = mDownloadId; + enqueueResult.startTime = mStartTime; + mCallback.onResult(enqueueResult); + } + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerDelegate.java deleted file mode 100644 index c436678..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerDelegate.java +++ /dev/null
@@ -1,411 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.download; - -import android.app.DownloadManager; -import android.content.Context; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.net.Uri; -import android.os.Build; -import android.os.Environment; -import android.support.v4.app.NotificationManagerCompat; -import android.text.TextUtils; - -import org.chromium.base.ContextUtils; -import org.chromium.base.Log; -import org.chromium.base.StrictModeContext; -import org.chromium.base.task.AsyncTask; - -import java.io.File; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -/** - * A wrapper for Android DownloadManager to provide utility functions. - */ -public class DownloadManagerDelegate { - private static final String TAG = "DownloadDelegate"; - private static final String DOWNLOAD_DIRECTORY = "Download"; - private static final long INVALID_SYSTEM_DOWNLOAD_ID = -1; - private static final String DOWNLOAD_ID_MAPPINGS_FILE_NAME = "download_id_mappings"; - private static final Object sLock = new Object(); - protected final Context mContext; - - public DownloadManagerDelegate(Context context) { - mContext = context; - } - - /** - * Inserts a new download ID mapping into the SharedPreferences - * @param downloadId system download ID from Android DownloadManager. - * @param downloadGuid Download GUID. - */ - private void addDownloadIdMapping(long downloadId, String downloadGuid) { - synchronized (sLock) { - SharedPreferences sharedPrefs = getSharedPreferences(); - SharedPreferences.Editor editor = sharedPrefs.edit(); - editor.putLong(downloadGuid, downloadId); - editor.apply(); - } - } - - /** - * Removes a download Id mapping from the SharedPreferences given the download GUID. - * @param guid Download GUID. - * @return the Android DownloadManager's download ID that is removed, or - * INVALID_SYSTEM_DOWNLOAD_ID if it is not found. - */ - private long removeDownloadIdMapping(String downloadGuid) { - long downloadId = INVALID_SYSTEM_DOWNLOAD_ID; - synchronized (sLock) { - SharedPreferences sharedPrefs = getSharedPreferences(); - downloadId = sharedPrefs.getLong(downloadGuid, INVALID_SYSTEM_DOWNLOAD_ID); - if (downloadId != INVALID_SYSTEM_DOWNLOAD_ID) { - SharedPreferences.Editor editor = sharedPrefs.edit(); - editor.remove(downloadGuid); - editor.apply(); - } - } - return downloadId; - } - - /** - * Lazily retrieve the SharedPreferences when needed. Since download operations are not very - * frequent, no need to load all SharedPreference entries into a hashmap in the memory. - * @return the SharedPreferences instance. - */ - private SharedPreferences getSharedPreferences() { - return ContextUtils.getApplicationContext().getSharedPreferences( - DOWNLOAD_ID_MAPPINGS_FILE_NAME, Context.MODE_PRIVATE); - } - - /** - * @see android.app.DownloadManager#addCompletedDownload(String, String, boolean, String, - * String, long, boolean) - */ - protected long addCompletedDownload(String fileName, String description, String mimeType, - String path, long length, String originalUrl, String referer, String downloadGuid) { - DownloadManager manager = - (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); - NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mContext); - boolean useSystemNotification = !notificationManager.areNotificationsEnabled(); - long downloadId = -1; - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { - Class<?> c = manager.getClass(); - try { - Class[] args = {String.class, String.class, boolean.class, String.class, - String.class, long.class, boolean.class, Uri.class, Uri.class}; - Method method = c.getMethod("addCompletedDownload", args); - // OriginalUri has to be null or non-empty http(s) scheme. - Uri originalUri = DownloadUtils.parseOriginalUrl(originalUrl); - Uri refererUri = TextUtils.isEmpty(referer) ? null : Uri.parse(referer); - downloadId = (Long) method.invoke(manager, fileName, description, true, mimeType, - path, length, useSystemNotification, originalUri, refererUri); - } catch (SecurityException e) { - Log.e(TAG, "Cannot access the needed method."); - } catch (NoSuchMethodException e) { - Log.e(TAG, "Cannot find the needed method."); - } catch (InvocationTargetException e) { - Log.e(TAG, "Error calling the needed method."); - } catch (IllegalAccessException e) { - Log.e(TAG, "Error accessing the needed method."); - } - } else { - downloadId = manager.addCompletedDownload(fileName, description, true, mimeType, path, - length, useSystemNotification); - } - addDownloadIdMapping(downloadId, downloadGuid); - return downloadId; - } - - /** - * Removes a download from Android DownloadManager. - * @param downloadGuid The GUID of the download. - * @param externallyRemoved If download is externally removed in other application. - */ - void removeCompletedDownload(String downloadGuid, boolean externallyRemoved) { - long downloadId = removeDownloadIdMapping(downloadGuid); - - // Let Android DownloadManager to remove download only if the user removed the file in - // Chrome. If the user renamed or moved the file, Chrome should keep it intact. - if (downloadId != INVALID_SYSTEM_DOWNLOAD_ID && !externallyRemoved) { - DownloadManager manager = - (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); - manager.remove(downloadId); - } - } - - /** - * Interface for returning the query result when it completes. - */ - public interface DownloadQueryCallback { - /** - * Callback function to return query result. - * @param result Query result from android DownloadManager. - * @param showNotifications Whether to show status notifications. - */ - public void onQueryCompleted(DownloadQueryResult result, boolean showNotifications); - } - - /** - * Result for querying the Android DownloadManager. - */ - static class DownloadQueryResult { - public final DownloadItem item; - public final int downloadStatus; - public final long downloadTimeInMilliseconds; - public final long bytesDownloaded; - public final boolean canResolve; - public final int failureReason; - - DownloadQueryResult(DownloadItem item, int downloadStatus, long downloadTimeInMilliseconds, - long bytesDownloaded, boolean canResolve, int failureReason) { - this.item = item; - this.downloadStatus = downloadStatus; - this.downloadTimeInMilliseconds = downloadTimeInMilliseconds; - this.canResolve = canResolve; - this.bytesDownloaded = bytesDownloaded; - this.failureReason = failureReason; - } - } - - /** - * Query the Android DownloadManager for download status. - * @param downloadItem Download item to query. - * @param showNotifications Whether to show status notifications. - * @param callback Callback to be notified when query completes. - */ - void queryDownloadResult( - DownloadItem downloadItem, boolean showNotifications, DownloadQueryCallback callback) { - DownloadQueryTask task = new DownloadQueryTask(downloadItem, showNotifications, callback); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - /** - * Async task to query download status from Android DownloadManager - */ - private class DownloadQueryTask extends AsyncTask<DownloadQueryResult> { - private final DownloadItem mDownloadItem; - private final boolean mShowNotifications; - private final DownloadQueryCallback mCallback; - - public DownloadQueryTask(DownloadItem downloadItem, boolean showNotifications, - DownloadQueryCallback callback) { - mDownloadItem = downloadItem; - mShowNotifications = showNotifications; - mCallback = callback; - } - - @Override - public DownloadQueryResult doInBackground() { - DownloadManager manager = - (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); - Cursor c = manager.query( - new DownloadManager.Query().setFilterById(mDownloadItem.getSystemDownloadId())); - if (c == null) { - return new DownloadQueryResult(mDownloadItem, - DownloadManagerService.DownloadStatus.CANCELLED, 0, 0, false, 0); - } - long bytesDownloaded = 0; - boolean canResolve = false; - @DownloadManagerService.DownloadStatus - int downloadStatus = DownloadManagerService.DownloadStatus.IN_PROGRESS; - int failureReason = 0; - long lastModifiedTime = 0; - if (c.moveToNext()) { - int statusIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS); - int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS)); - if (status == DownloadManager.STATUS_SUCCESSFUL) { - downloadStatus = DownloadManagerService.DownloadStatus.COMPLETE; - DownloadInfo.Builder builder = mDownloadItem.getDownloadInfo() == null - ? new DownloadInfo.Builder() - : DownloadInfo.Builder.fromDownloadInfo( - mDownloadItem.getDownloadInfo()); - builder.setFileName( - c.getString(c.getColumnIndex(DownloadManager.COLUMN_TITLE))); - mDownloadItem.setDownloadInfo(builder.build()); - if (mShowNotifications) { - canResolve = DownloadManagerService.isOMADownloadDescription( - mDownloadItem.getDownloadInfo()) - || DownloadManagerService.canResolveDownloadItem( - mContext, mDownloadItem, false); - } - } else if (status == DownloadManager.STATUS_FAILED) { - downloadStatus = DownloadManagerService.DownloadStatus.FAILED; - failureReason = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_REASON)); - } - lastModifiedTime = - c.getLong(c.getColumnIndex(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP)); - bytesDownloaded = - c.getLong(c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); - } else { - downloadStatus = DownloadManagerService.DownloadStatus.CANCELLED; - } - c.close(); - long totalTime = Math.max(0, lastModifiedTime - mDownloadItem.getStartTime()); - return new DownloadQueryResult(mDownloadItem, downloadStatus, totalTime, - bytesDownloaded, canResolve, failureReason); - } - - @Override - protected void onPostExecute(DownloadQueryResult result) { - mCallback.onQueryCompleted(result, mShowNotifications); - } - } - - static Uri getContentUriFromDownloadManager(Context context, long downloadId) { - DownloadManager manager = - (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); - Uri contentUri = null; - try { - try (StrictModeContext unused = StrictModeContext.allowDiskReads()) { - contentUri = manager.getUriForDownloadedFile(downloadId); - } - } catch (SecurityException e) { - Log.e(TAG, "unable to get content URI from DownloadManager"); - } - return contentUri; - } - - /** - * Sends the download request to Android download manager. If |notifyCompleted| is true, - * a notification will be sent to the user once download is complete and the downloaded - * content will be saved to the public directory on external storage. Otherwise, the - * download will be saved in the app directory and user will not get any notifications - * after download completion. - * This will be used by OMA downloads as we need Android DownloadManager to encrypt the content. - * - * @param item The associated {@link DownloadItem}. - * @param notifyCompleted Whether to notify the user after DownloadManager completes the - * download. - * @param callback The callback to be executed after the download request is enqueued. - */ - public void enqueueDownloadManagerRequest(final DownloadItem item, boolean notifyCompleted, - EnqueueDownloadRequestCallback callback) { - new EnqueueDownloadRequestTask(item, notifyCompleted, callback) - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - /** - * Interface for returning the enqueue result when it completes. - */ - public interface EnqueueDownloadRequestCallback { - /** - * Callback function to return result of enqueue download operation. - * @param result Result of enqueue operation on android DownloadManager. - * @param failureReason The reason for failure if any, provided by android DownloadManager. - * @param downloadItem The associated download item. - * @param downloadId The download id obtained from android DownloadManager as a result of - * this enqueue operation. - */ - public void onDownloadEnqueued( - boolean result, int failureReason, DownloadItem downloadItem, long downloadId); - } - - /** - * Async task to enqueue a download request into DownloadManager. - */ - private class EnqueueDownloadRequestTask extends AsyncTask<Boolean> { - private final DownloadItem mDownloadItem; - private final boolean mNotifyCompleted; - private final EnqueueDownloadRequestCallback mCallback; - private long mDownloadId; - private int mFailureReason; - private long mStartTime; - - public EnqueueDownloadRequestTask(DownloadItem downloadItem, Boolean notifyCompleted, - EnqueueDownloadRequestCallback callback) { - mDownloadItem = downloadItem; - mNotifyCompleted = notifyCompleted; - mCallback = callback; - } - - @Override - public Boolean doInBackground() { - Uri uri = Uri.parse(mDownloadItem.getDownloadInfo().getUrl()); - DownloadManager.Request request; - DownloadInfo info = mDownloadItem.getDownloadInfo(); - try { - request = new DownloadManager.Request(uri); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Cannot download non http or https scheme"); - // Use ERROR_UNHANDLED_HTTP_CODE so that it will be treated as a server error. - mFailureReason = DownloadManager.ERROR_UNHANDLED_HTTP_CODE; - return false; - } - - request.setMimeType(info.getMimeType()); - try { - if (mNotifyCompleted) { - if (info.getFileName() != null) { - // Set downloaded file destination to /sdcard/Download or, should it be - // set to one of several Environment.DIRECTORY* dirs depending on mimetype? - request.setDestinationInExternalPublicDir( - Environment.DIRECTORY_DOWNLOADS, info.getFileName()); - } - } else { - File dir = new File(mContext.getExternalFilesDir(null), DOWNLOAD_DIRECTORY); - if (dir.mkdir() || dir.isDirectory()) { - File file = new File(dir, info.getFileName()); - request.setDestinationUri(Uri.fromFile(file)); - } else { - Log.e(TAG, "Cannot create download directory"); - mFailureReason = DownloadManager.ERROR_FILE_ERROR; - return false; - } - } - } catch (IllegalStateException e) { - Log.e(TAG, "Cannot create download directory"); - mFailureReason = DownloadManager.ERROR_FILE_ERROR; - return false; - } - - if (mNotifyCompleted) { - // Let this downloaded file be scanned by MediaScanner - so that it can - // show up in Gallery app, for example. - request.allowScanningByMediaScanner(); - request.setNotificationVisibility( - DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); - } else { - request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE); - } - String description = info.getDescription(); - if (TextUtils.isEmpty(description)) { - description = info.getFileName(); - } - request.setDescription(description); - request.setTitle(info.getFileName()); - request.addRequestHeader("Cookie", info.getCookie()); - request.addRequestHeader("Referer", info.getReferrer()); - request.addRequestHeader("User-Agent", info.getUserAgent()); - - DownloadManager manager = - (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); - try { - mStartTime = System.currentTimeMillis(); - mDownloadId = manager.enqueue(request); - } catch (IllegalArgumentException e) { - // See crbug.com/143499 for more details. - Log.e(TAG, "Download failed: " + e); - mFailureReason = DownloadManager.ERROR_UNKNOWN; - return false; - } catch (RuntimeException e) { - // See crbug.com/490442 for more details. - Log.e(TAG, "Failed to create target file on the external storage: " + e); - mFailureReason = DownloadManager.ERROR_FILE_ERROR; - return false; - } - return true; - } - - @Override - protected void onPostExecute(Boolean result) { - mDownloadItem.setStartTime(mStartTime); - mCallback.onDownloadEnqueued(result, mFailureReason, mDownloadItem, mDownloadId); - mDownloadItem.setSystemDownloadId(mDownloadId); - } - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java index 96c5191..de76e0d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -36,6 +36,8 @@ import org.chromium.base.task.PostTask; import org.chromium.base.task.TaskTraits; import org.chromium.chrome.R; +import org.chromium.chrome.browser.download.DownloadManagerBridge.DownloadEnqueueRequest; +import org.chromium.chrome.browser.download.DownloadManagerBridge.DownloadEnqueueResponse; import org.chromium.chrome.browser.download.DownloadMetrics.DownloadOpenSource; import org.chromium.chrome.browser.download.DownloadNotificationUmaHelper.UmaBackgroundDownload; import org.chromium.chrome.browser.download.DownloadNotificationUmaHelper.UmaDownloadResumption; @@ -77,15 +79,13 @@ * Chrome implementation of the {@link DownloadController.DownloadNotificationService} interface. * This class is responsible for keeping track of which downloads are in progress. It generates * updates for progress of downloads and handles cleaning up of interrupted progress notifications. - * TODO(qinmin): move BroadcastReceiver inheritance into DownloadManagerDelegate, as it handles all - * Android DownloadManager interactions. And DownloadManagerService should not know download Id - * issued by Android DownloadManager. + * TODO(qinmin): move BroadcastReceiver inheritance into DownloadManagerBridge, as it + * handles all Android DownloadManager interactions. And DownloadManagerService should not know + * download Id issued by Android DownloadManager. */ public class DownloadManagerService implements DownloadController.DownloadNotificationService, - NetworkChangeNotifierAutoDetect.Observer, - DownloadManagerDelegate.DownloadQueryCallback, - DownloadManagerDelegate.EnqueueDownloadRequestCallback, DownloadServiceDelegate, + NetworkChangeNotifierAutoDetect.Observer, DownloadServiceDelegate, BackendProvider.DownloadDelegate, BrowserStartupController.StartupCallback { // Download status. @IntDef({DownloadStatus.IN_PROGRESS, DownloadStatus.COMPLETE, DownloadStatus.FAILED, @@ -168,7 +168,6 @@ private DownloadInfoBarController mInfoBarController; private DownloadInfoBarController mIncognitoInfoBarController; private long mNativeDownloadManagerService; - private DownloadManagerDelegate mDownloadManagerDelegate; private NetworkChangeNotifierAutoDetect mNetworkChangeNotifier; // Flag to track if we need to post a task to update download notifications. private boolean mIsUIUpdateScheduled; @@ -267,9 +266,8 @@ mUpdateDelayInMillis = updateDelayInMillis; mHandler = handler; mDownloadSnackbarController = new DownloadSnackbarController(); - mDownloadManagerDelegate = new DownloadManagerDelegate(applicationContext); - mOMADownloadHandler = new OMADownloadHandler( - applicationContext, mDownloadManagerDelegate, mDownloadSnackbarController); + mOMADownloadHandler = + new OMADownloadHandler(applicationContext, mDownloadSnackbarController); // Note that this technically leaks the native object, however, DownloadManagerService // is a singleton that lives forever and there's no clean shutdown of Chrome on Android. init(); @@ -287,8 +285,9 @@ while (iterator.hasNext()) { DownloadUmaStatsEntry entry = iterator.next(); if (entry.useDownloadManager) { - mDownloadManagerDelegate.queryDownloadResult( - entry.buildDownloadItem(), false, this); + DownloadItem item = entry.buildDownloadItem(); + DownloadManagerBridge.queryDownloadResult(item.getSystemDownloadId(), + result -> onQueryCompleted(item, false, result)); } else if (!entry.isPaused) { entry.isPaused = true; entry.numInterruptions++; @@ -531,18 +530,24 @@ private boolean updateDownloadSuccessNotification(DownloadProgress progress) { final boolean isSupportedMimeType = progress.mIsSupportedMimeType; final DownloadItem item = progress.mDownloadItem; + final DownloadInfo info = item.getDownloadInfo(); + AsyncTask<Pair<Boolean, Boolean>> task = new AsyncTask<Pair<Boolean, Boolean>>() { @Override public Pair<Boolean, Boolean> doInBackground() { boolean success = ContentUriUtils.isContentUri(item.getDownloadInfo().getFilePath()); if (!success) { - success = addCompletedDownload(item); + long systemDownloadId = DownloadManagerBridge.addCompletedDownload( + info.getFileName(), info.getDescription(), info.getMimeType(), + info.getFilePath(), info.getBytesReceived(), info.getOriginalUrl(), + info.getReferrer(), info.getDownloadGuid()); + success = systemDownloadId != DownloadItem.INVALID_DOWNLOAD_ID; + if (success) item.setSystemDownloadId(systemDownloadId); } boolean canResolve = success - && (isOMADownloadDescription(item.getDownloadInfo()) - || canResolveDownloadItem(ContextUtils.getApplicationContext(), - item, isSupportedMimeType)); + && (isOMADownloadDescription(item.getDownloadInfo().getMimeType()) + || canResolveDownloadItem(item, isSupportedMimeType)); return Pair.create(success, canResolve); } @@ -578,46 +583,13 @@ } /** - * Adds a completed download into Android DownloadManager. - * - * @param downloadItem Information of the downloaded. - * @return true if the download is added to the DownloadManager, or false otherwise. - */ - protected boolean addCompletedDownload(DownloadItem downloadItem) { - assert !ThreadUtils.runningOnUiThread(); - DownloadInfo downloadInfo = downloadItem.getDownloadInfo(); - String description = downloadInfo.getDescription(); - if (TextUtils.isEmpty(description)) description = downloadInfo.getFileName(); - try { - // Exceptions can be thrown when calling this, although it is not - // documented on Android SDK page. - long downloadId = mDownloadManagerDelegate.addCompletedDownload( - downloadInfo.getFileName(), description, downloadInfo.getMimeType(), - downloadInfo.getFilePath(), downloadInfo.getBytesReceived(), - downloadInfo.getOriginalUrl(), downloadInfo.getReferrer(), - downloadInfo.getDownloadGuid()); - downloadItem.setSystemDownloadId(downloadId); - return true; - } catch (RuntimeException e) { - Log.w(TAG, "Failed to add the download item to DownloadManager: ", e); - if (downloadInfo.getFilePath() != null) { - File file = new File(downloadInfo.getFilePath()); - if (!file.delete()) { - Log.w(TAG, "Failed to remove the unsuccessful download"); - } - } - } - return false; - } - - /** * Handle auto opennable files after download completes. - * TODO(qinmin): move this to DownloadManagerDelegate. + * TODO(qinmin): move this to DownloadManagerBridge. * * @param download A download item. */ private void handleAutoOpenAfterDownload(DownloadItem download) { - if (isOMADownloadDescription(download.getDownloadInfo())) { + if (isOMADownloadDescription(download.getDownloadInfo().getMimeType())) { mOMADownloadHandler.handleOMADownload( download.getDownloadInfo(), download.getSystemDownloadId()); return; @@ -763,84 +735,83 @@ mOMADownloadHandler = omaDownloadHandler; } - /** See {@link DownloadManagerDelegate.EnqueueDownloadRequestTask}. */ - public void enqueueDownloadManagerRequest(final DownloadItem item, boolean notifyCompleted) { + /** See {@link DownloadManagerBridge.enqueueNewDownload}. */ + public void enqueueNewDownload(final DownloadItem item, boolean notifyCompleted) { if (mDownloadManagerRequestInterceptor != null) { mDownloadManagerRequestInterceptor.interceptDownloadRequest(item, notifyCompleted); return; } - mDownloadManagerDelegate.enqueueDownloadManagerRequest(item, notifyCompleted, this); + DownloadEnqueueRequest request = new DownloadEnqueueRequest(); + request.url = item.getDownloadInfo().getUrl(); + request.fileName = item.getDownloadInfo().getFileName(); + request.description = item.getDownloadInfo().getDescription(); + request.mimeType = item.getDownloadInfo().getMimeType(); + request.cookie = item.getDownloadInfo().getCookie(); + request.referrer = item.getDownloadInfo().getReferrer(); + request.userAgent = item.getDownloadInfo().getUserAgent(); + request.notifyCompleted = notifyCompleted; + DownloadManagerBridge.enqueueNewDownload( + request, response -> { onDownloadEnqueued(item, response); }); } - @Override - public void onDownloadEnqueued( - boolean result, int failureReason, DownloadItem downloadItem, long downloadId) { - if (!result) { - onDownloadFailed(downloadItem, failureReason); + public void onDownloadEnqueued(DownloadItem downloadItem, DownloadEnqueueResponse response) { + downloadItem.setStartTime(response.startTime); + downloadItem.setSystemDownloadId(response.downloadId); + if (!response.result) { + onDownloadFailed(downloadItem, response.failureReason); recordDownloadCompletionStats( true, DownloadManagerService.DownloadStatus.FAILED, 0, 0, 0, 0); return; } DownloadUtils.showDownloadStartToast(ContextUtils.getApplicationContext()); - addUmaStatsEntry(new DownloadUmaStatsEntry( - String.valueOf(downloadId), downloadItem.getStartTime(), 0, false, true, 0, 0)); + addUmaStatsEntry(new DownloadUmaStatsEntry(String.valueOf(response.downloadId), + downloadItem.getStartTime(), 0, false, true, 0, 0)); } /** * Determines if the download should be immediately opened after * downloading. * - * @param downloadInfo Information about the download. + * @param mimeType The mime type of the download. + * @param hasUserGesture Whether the download is associated with an user gesture. * @return true if the downloaded content should be opened, or false otherwise. */ @VisibleForTesting - static boolean shouldOpenAfterDownload(DownloadInfo downloadInfo) { - String type = downloadInfo.getMimeType(); - return downloadInfo.hasUserGesture() && MIME_TYPES_TO_OPEN.contains(type); + static boolean shouldOpenAfterDownload(String mimeType, boolean hasUserGesture) { + return hasUserGesture && MIME_TYPES_TO_OPEN.contains(mimeType); } /** * Returns true if the download is for OMA download description file. * - * @param downloadInfo Information about the download. + * @param mimeType The mime type of the download. * @return true if the downloaded is OMA download description, or false otherwise. */ - static boolean isOMADownloadDescription(DownloadInfo downloadInfo) { - return OMADownloadHandler.OMA_DOWNLOAD_DESCRIPTOR_MIME.equalsIgnoreCase( - downloadInfo.getMimeType()); + static boolean isOMADownloadDescription(String mimeType) { + return OMADownloadHandler.OMA_DOWNLOAD_DESCRIPTOR_MIME.equalsIgnoreCase(mimeType); } - /** - * Return the intent to launch for a given download item. - * - * @param context Context of the app. - * @param filePath Path to the file. - * @param downloadId ID of the download item in DownloadManager. - * @param isSupportedMimeType Whether the MIME type is supported by browser. - * @param downloadId ID of the download item in DownloadManager. - * @param originalUrl The original url of the downloaded file - * @param referrer Referrer of the downloaded file. - * @return the intent to launch for the given download item. - */ @Nullable - static Intent getLaunchIntentForDownload(Context context, @Nullable String filePath, - long downloadId, boolean isSupportedMimeType, String originalUrl, String referrer) { + static Intent getLaunchIntentForDownload(@Nullable String filePath, long downloadId, + boolean isSupportedMimeType, String originalUrl, String referrer) { assert !ThreadUtils.runningOnUiThread(); if (downloadId == DownloadItem.INVALID_DOWNLOAD_ID) { if (!ContentUriUtils.isContentUri(filePath)) return null; return getLaunchIntentFromDownloadUri( - context, filePath, isSupportedMimeType, originalUrl, referrer); + filePath, isSupportedMimeType, originalUrl, referrer); } + + DownloadManagerBridge.DownloadQueryResult queryResult = + DownloadManagerBridge.queryDownloadResult(downloadId); + String mimeType = queryResult.mimeType; + Uri contentUri = filePath == null - ? DownloadManagerDelegate.getContentUriFromDownloadManager(context, downloadId) + ? queryResult.contentUri : ApiCompatibilityUtils.getUriForDownloadedFile(new File(filePath)); if (contentUri == null) return null; - DownloadManager manager = - (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); - String mimeType = manager.getMimeTypeForDownloadedFile(downloadId); Uri fileUri = filePath == null ? contentUri : Uri.fromFile(new File(filePath)); return createLaunchIntent( fileUri, contentUri, mimeType, isSupportedMimeType, originalUrl, referrer); @@ -857,13 +828,14 @@ * @return the intent to launch for the given download item. */ @Nullable - private static Intent getLaunchIntentFromDownloadUri(Context context, String contentUri, - boolean isSupportedMimeType, String originalUrl, String referrer) { + private static Intent getLaunchIntentFromDownloadUri( + String contentUri, boolean isSupportedMimeType, String originalUrl, String referrer) { assert !ThreadUtils.runningOnUiThread(); assert ContentUriUtils.isContentUri(contentUri); Uri uri = Uri.parse(contentUri); - try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) { + try (Cursor cursor = ContextUtils.getApplicationContext().getContentResolver().query( + uri, null, null, null, null)) { if (cursor.getCount() == 0) return null; cursor.moveToNext(); String mimeType = cursor.getString(cursor.getColumnIndex(MediaColumns.MIME_TYPE)); @@ -901,14 +873,12 @@ * @param isSupportedMimeType Whether the MIME type is supported by browser. * @return true if the download item can be resolved, or false otherwise. */ - static boolean canResolveDownloadItem(Context context, DownloadItem download, - boolean isSupportedMimeType) { + static boolean canResolveDownloadItem(DownloadItem download, boolean isSupportedMimeType) { assert !ThreadUtils.runningOnUiThread(); - Intent intent = - getLaunchIntentForDownload(context, download.getDownloadInfo().getFilePath(), - download.getSystemDownloadId(), isSupportedMimeType, null, null); - return (intent == null) - ? false : ExternalNavigationDelegateImpl.resolveIntent(intent, true); + Intent intent = getLaunchIntentForDownload(download.getDownloadInfo().getFilePath(), + download.getSystemDownloadId(), isSupportedMimeType, null, null); + return (intent == null) ? false + : ExternalNavigationDelegateImpl.resolveIntent(intent, true); } /** See {@link #openDownloadedContent(Context, String, boolean, boolean, String, long)}. */ @@ -922,7 +892,7 @@ /** * Launch the intent for a given download item, or Download Home if that's not possible. - * TODO(qinmin): Move this to DownloadManagerDelegate. + * TODO(qinmin): Move this to DownloadManagerBridge. * * @param context Context to use. * @param filePath Path to the downloaded item. @@ -942,7 +912,7 @@ @Override public Intent doInBackground() { return getLaunchIntentForDownload( - context, filePath, downloadId, isSupportedMimeType, originalUrl, referrer); + filePath, downloadId, isSupportedMimeType, originalUrl, referrer); } @Override @@ -1142,7 +1112,7 @@ }); PostTask.postTask(TaskTraits.BEST_EFFORT_MAY_BLOCK, () -> { - mDownloadManagerDelegate.removeCompletedDownload(downloadGuid, externallyRemoved); + DownloadManagerBridge.removeCompletedDownload(downloadGuid, externallyRemoved); }); } @@ -1217,7 +1187,7 @@ */ public void onSuccessNotificationShown( DownloadInfo info, boolean canResolve, int notificationId, long systemDownloadId) { - if (canResolve && shouldOpenAfterDownload(info)) { + if (canResolve && shouldOpenAfterDownload(info.getMimeType(), info.hasUserGesture())) { DownloadItem item = new DownloadItem(false, info); item.setSystemDownloadId(systemDownloadId); handleAutoOpenAfterDownload(item); @@ -1321,33 +1291,61 @@ ConversionUtils.KILOBYTES_PER_GIGABYTE, 50); } - @Override - public void onQueryCompleted( - DownloadManagerDelegate.DownloadQueryResult result, boolean showNotification) { + /** + * Used only for android DownloadManager associated downloads. + * @param item The associated download item. + * @param showNotification Whether to show notification for this download. + * @param result The query result about the download. + */ + public void onQueryCompleted(DownloadItem item, boolean showNotification, + DownloadManagerBridge.DownloadQueryResult result) { + DownloadInfo.Builder builder = item.getDownloadInfo() == null + ? new DownloadInfo.Builder() + : DownloadInfo.Builder.fromDownloadInfo(item.getDownloadInfo()); + builder.setBytesTotalSize(result.bytesTotal); + builder.setBytesReceived(result.bytesDownloaded); + if (!TextUtils.isEmpty(result.fileName)) builder.setFileName(result.fileName); + if (!TextUtils.isEmpty(result.mimeType)) builder.setMimeType(result.mimeType); + item.setDownloadInfo(builder.build()); + if (result.downloadStatus == DownloadStatus.IN_PROGRESS) return; if (showNotification) { switch (result.downloadStatus) { case DownloadStatus.COMPLETE: - if (shouldOpenAfterDownload(result.item.getDownloadInfo()) - && result.canResolve) { - handleAutoOpenAfterDownload(result.item); - } else { - mDownloadSnackbarController.onDownloadSucceeded( - result.item.getDownloadInfo(), - DownloadSnackbarController.INVALID_NOTIFICATION_ID, - result.item.getSystemDownloadId(), result.canResolve, true); - } + new AsyncTask<Boolean>() { + @Override + protected Boolean doInBackground() { + return canResolveDownloadItem(item, + isSupportedMimeType(item.getDownloadInfo().getMimeType())); + } + + @Override + protected void onPostExecute(Boolean canResolve) { + if (shouldOpenAfterDownload( + result.mimeType, item.getDownloadInfo().hasUserGesture()) + && canResolve) { + handleAutoOpenAfterDownload(item); + } else { + mDownloadSnackbarController.onDownloadSucceeded( + item.getDownloadInfo(), + DownloadSnackbarController.INVALID_NOTIFICATION_ID, + item.getSystemDownloadId(), canResolve, true); + } + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); break; case DownloadStatus.FAILED: - onDownloadFailed(result.item, result.failureReason); + onDownloadFailed(item, result.failureReason); break; default: break; } } - recordDownloadCompletionStats(true, result.downloadStatus, - result.downloadTimeInMilliseconds, result.bytesDownloaded, 0, 0); - removeUmaStatsEntry(result.item.getId()); + + long totalTime = Math.max(0, result.lastModifiedTime - item.getStartTime()); + recordDownloadCompletionStats( + true, result.downloadStatus, totalTime, result.bytesDownloaded, 0, 0); + removeUmaStatsEntry(item.getId()); } /** @@ -1762,12 +1760,12 @@ AsyncTask<Boolean> task = new AsyncTask<Boolean>() { @Override public Boolean doInBackground() { - boolean isSupportedMimeType = - isSupportedMimeType(downloadItem.getDownloadInfo().getMimeType()); - boolean canResolve = isOMADownloadDescription(downloadItem.getDownloadInfo()) - || canResolveDownloadItem(ContextUtils.getApplicationContext(), - downloadItem, isSupportedMimeType); - return canResolve && shouldOpenAfterDownload(downloadItem.getDownloadInfo()); + DownloadInfo info = downloadItem.getDownloadInfo(); + boolean isSupportedMimeType = isSupportedMimeType(info.getMimeType()); + boolean canResolve = isOMADownloadDescription(info.getMimeType()) + || canResolveDownloadItem(downloadItem, isSupportedMimeType); + return canResolve + && shouldOpenAfterDownload(info.getMimeType(), info.hasUserGesture()); } @Override protected void onPostExecute(Boolean result) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java index 260787e..50677da 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
@@ -13,7 +13,6 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.database.Cursor; import android.net.Uri; import android.os.Environment; import android.os.ParcelFileDescriptor; @@ -22,7 +21,6 @@ import android.text.TextUtils; import android.util.Log; import android.util.LongSparseArray; -import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.webkit.URLUtil; @@ -34,9 +32,12 @@ import org.chromium.base.ApplicationStatus; import org.chromium.base.ContextUtils; +import org.chromium.base.ObserverList; import org.chromium.base.VisibleForTesting; import org.chromium.base.task.AsyncTask; import org.chromium.chrome.browser.content.ContentUtils; +import org.chromium.chrome.browser.download.DownloadManagerBridge.DownloadEnqueueRequest; +import org.chromium.chrome.browser.download.DownloadManagerBridge.DownloadEnqueueResponse; import org.chromium.chrome.download.R; import java.io.DataOutputStream; @@ -73,8 +74,10 @@ * be saved to the app directory first. If step 6 completes successfully, the content will * be moved to the public external storage. Otherwise, it will be removed from the device. */ -public class OMADownloadHandler extends BroadcastReceiver - implements DownloadManagerDelegate.EnqueueDownloadRequestCallback { +public class OMADownloadHandler extends BroadcastReceiver { + /** Alerted about changes to internal state. */ + public interface TestObserver { void onDownloadEnqueued(long downloadId); } + private static final String TAG = "OMADownloadHandler"; private static final String PENDING_OMA_DOWNLOADS = "PendingOMADownloads"; @@ -119,7 +122,7 @@ private final LongSparseArray<OMAInfo> mPendingOMADownloads = new LongSparseArray<OMAInfo>(); private final DownloadSnackbarController mDownloadSnackbarController; - private final DownloadManagerDelegate mDownloadManagerDelegate; + private final ObserverList<TestObserver> mObservers = new ObserverList<>(); /** * Information about the OMA content. The object is parsed from the download @@ -244,11 +247,10 @@ } } - public OMADownloadHandler(Context context, DownloadManagerDelegate delegate, - DownloadSnackbarController downloadSnackbarController) { + public OMADownloadHandler( + Context context, DownloadSnackbarController downloadSnackbarController) { mContext = context; mSharedPrefs = ContextUtils.getAppSharedPreferences(); - mDownloadManagerDelegate = delegate; mDownloadSnackbarController = downloadSnackbarController; } @@ -263,6 +265,11 @@ task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } + @VisibleForTesting + void addObserverForTest(TestObserver testObserver) { + mObservers.addObserver(testObserver); + } + /** * Async task to parse an OMA download descriptor. */ @@ -350,8 +357,10 @@ DownloadItem downloadItem = mSystemDownloadIdMap.get(downloadId); if (downloadItem != null) { - mDownloadManagerDelegate.queryDownloadResult( - downloadItem, true, DownloadManagerService.getDownloadManagerService()); + DownloadManagerBridge.queryDownloadResult(downloadId, (result) -> { + DownloadManagerService.getDownloadManagerService().onQueryCompleted( + downloadItem, true, result); + }); removeFromSystemDownloadIdMap(downloadId); } } @@ -695,8 +704,18 @@ // Don't show complete notification until that happens. DownloadItem item = new DownloadItem(true, newInfo); item.setSystemDownloadId(downloadId); - mDownloadManagerDelegate.enqueueDownloadManagerRequest( - item, omaInfo.isValueEmpty(OMA_INSTALL_NOTIFY_URI), this); + + DownloadEnqueueRequest enqueueRequest = new DownloadEnqueueRequest(); + enqueueRequest.fileName = fileName; + enqueueRequest.url = url; + enqueueRequest.mimeType = mimeType; + enqueueRequest.description = omaInfo.getValue(OMA_DESCRIPTION); + enqueueRequest.cookie = newInfo.getCookie(); + enqueueRequest.referrer = newInfo.getReferrer(); + enqueueRequest.userAgent = newInfo.getUserAgent(); + enqueueRequest.notifyCompleted = omaInfo.isValueEmpty(OMA_INSTALL_NOTIFY_URI); + DownloadManagerBridge.enqueueNewDownload( + enqueueRequest, response -> { onDownloadEnqueued(item, response); }); mPendingOMADownloads.put(downloadId, omaInfo); } @@ -722,13 +741,13 @@ mPendingOMADownloads.put(newDownloadId, omaInfo); } - @Override - public void onDownloadEnqueued( - boolean result, int failureReason, DownloadItem downloadItem, long downloadId) { - boolean isPendingOMADownload = isPendingOMADownload(downloadItem.getSystemDownloadId()); - if (!result) { + private void onDownloadEnqueued(DownloadItem downloadItem, DownloadEnqueueResponse response) { + long oldDownloadId = downloadItem.getSystemDownloadId(); + downloadItem.setSystemDownloadId(response.downloadId); + boolean isPendingOMADownload = isPendingOMADownload(oldDownloadId); + if (!response.result) { if (isPendingOMADownload) { - onDownloadFailed(downloadItem.getDownloadInfo(), downloadItem.getSystemDownloadId(), + onDownloadFailed(downloadItem.getDownloadInfo(), oldDownloadId, DownloadManager.ERROR_UNKNOWN, null); } return; @@ -738,119 +757,81 @@ mContext.registerReceiver( this, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); } - mSystemDownloadIdMap.put(downloadId, downloadItem); + mSystemDownloadIdMap.put(response.downloadId, downloadItem); if (isPendingOMADownload) { // A new downloadId is generated, needs to update the OMADownloadHandler // about this. - updateDownloadInfo(downloadItem.getSystemDownloadId(), downloadId); + updateDownloadInfo(oldDownloadId, response.downloadId); // TODO(qinmin): use a file instead of shared prefs to save the // OMA information in case chrome is killed. This will allow us to // save more information like cookies and user agent. - String notifyUri = getInstallNotifyInfo(downloadId); + String notifyUri = getInstallNotifyInfo(response.downloadId); if (!TextUtils.isEmpty(notifyUri)) { - OMAEntry entry = new OMAEntry(downloadId, notifyUri); + OMAEntry entry = new OMAEntry(response.downloadId, notifyUri); addOMADownloadToSharedPrefs(entry.generateSharedPrefsString()); } } DownloadManagerService.getDownloadManagerService().onDownloadEnqueued( - result, failureReason, downloadItem, downloadId); + downloadItem, response); + for (TestObserver observer : mObservers) observer.onDownloadEnqueued(response.downloadId); } /** - * Async task to clear the pending OMA download from SharedPrefs and inform - * the OMADownloadHandler about download status. - */ - protected class ClearPendingOMADownloadTask extends AsyncTask<Pair<Integer, Boolean>> { - private final DownloadItem mDownloadItem; - private final String mInstallNotifyURI; - private DownloadInfo mDownloadInfo; - private int mFailureReason; - - public ClearPendingOMADownloadTask(DownloadItem downloadItem, String installNotifyURI) { - mDownloadItem = downloadItem; - mInstallNotifyURI = installNotifyURI; - mDownloadInfo = downloadItem.getDownloadInfo(); - } - - @Override - public Pair<Integer, Boolean> doInBackground() { - DownloadManager manager = - (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); - Cursor c = manager.query( - new DownloadManager.Query().setFilterById(mDownloadItem.getSystemDownloadId())); - int statusIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS); - int reasonIndex = c.getColumnIndex(DownloadManager.COLUMN_REASON); - int titleIndex = c.getColumnIndex(DownloadManager.COLUMN_TITLE); - int status = DownloadManager.STATUS_FAILED; - Boolean canResolve = false; - if (c.moveToNext()) { - status = c.getInt(statusIndex); - String title = c.getString(titleIndex); - if (mDownloadInfo == null) { - // Chrome has been killed, reconstruct a DownloadInfo. - mDownloadInfo = - new DownloadInfo.Builder() - .setFileName(title) - .setDescription(c.getString( - c.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION))) - .setMimeType(c.getString( - c.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE))) - .setBytesReceived(Long.parseLong(c.getString(c.getColumnIndex( - DownloadManager.COLUMN_TOTAL_SIZE_BYTES)))) - .build(); - } - if (status == DownloadManager.STATUS_SUCCESSFUL) { - mDownloadInfo = DownloadInfo.Builder.fromDownloadInfo(mDownloadInfo) - .setFileName(title) - .build(); - mDownloadItem.setDownloadInfo(mDownloadInfo); - canResolve = DownloadManagerService.canResolveDownloadItem( - mContext, mDownloadItem, false); - } else if (status == DownloadManager.STATUS_FAILED) { - mFailureReason = c.getInt(reasonIndex); - manager.remove(mDownloadItem.getSystemDownloadId()); - } - } - c.close(); - return Pair.create(status, canResolve); - } - - @Override - protected void onPostExecute(Pair<Integer, Boolean> result) { - long downloadId = mDownloadItem.getSystemDownloadId(); - if (result.first == DownloadManager.STATUS_SUCCESSFUL) { - onDownloadCompleted(mDownloadInfo, downloadId, mInstallNotifyURI); - removeOMADownloadFromSharedPrefs(downloadId); - mDownloadSnackbarController.onDownloadSucceeded(mDownloadInfo, - DownloadSnackbarController.INVALID_NOTIFICATION_ID, downloadId, - result.second, true); - } else if (result.first == DownloadManager.STATUS_FAILED) { - onDownloadFailed(mDownloadInfo, downloadId, mFailureReason, mInstallNotifyURI); - removeOMADownloadFromSharedPrefs(downloadId); - if (mDownloadInfo != null) { - String fileName = mDownloadInfo.getFileName(); - DownloadManagerService.getDownloadManagerService().onDownloadFailed( - mDownloadItem, mFailureReason); - } - } - } - } - - /** - * Clear pending OMA downloads for a particular download ID. + * Clears any pending OMA downloads for a particular download ID. If the download has been + * already completed, notifies the user through appropriate UI. * * @param downloadId Download identifier from Android DownloadManager. * @param installNotifyURI URI to notify after installation. */ private void clearPendingOMADownload(long downloadId, String installNotifyURI) { - DownloadItem item = mSystemDownloadIdMap.get(downloadId); - if (item == null) { - item = new DownloadItem(true, null); - item.setSystemDownloadId(downloadId); - } - ClearPendingOMADownloadTask task = new ClearPendingOMADownloadTask(item, installNotifyURI); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + DownloadManagerBridge.queryDownloadResult(downloadId, result -> { + DownloadItem item = mSystemDownloadIdMap.get(downloadId); + if (item == null) { + item = new DownloadItem(true, null); + item.setSystemDownloadId(downloadId); + } + + DownloadInfo.Builder builder = item.getDownloadInfo() == null + ? new DownloadInfo.Builder() + : DownloadInfo.Builder.fromDownloadInfo(item.getDownloadInfo()); + builder.setBytesReceived(result.bytesDownloaded); + builder.setBytesTotalSize(result.bytesTotal); + if (!TextUtils.isEmpty(result.fileName)) builder.setFileName(result.fileName); + if (!TextUtils.isEmpty(result.mimeType)) builder.setMimeType(result.mimeType); + item.setDownloadInfo(builder.build()); + + showDownloadsUi(downloadId, item, result, installNotifyURI); + }); + } + + private void showDownloadsUi(long downloadId, DownloadItem item, + DownloadManagerBridge.DownloadQueryResult result, String installNotifyURI) { + new AsyncTask<Boolean>() { + @Override + protected Boolean doInBackground() { + boolean canResolve = DownloadManagerService.canResolveDownloadItem( + item, DownloadManagerService.isSupportedMimeType(result.mimeType)); + return canResolve; + } + + @Override + protected void onPostExecute(Boolean canResolve) { + if (result.downloadStatus == DownloadManagerService.DownloadStatus.COMPLETE) { + onDownloadCompleted(item.getDownloadInfo(), downloadId, installNotifyURI); + removeOMADownloadFromSharedPrefs(downloadId); + mDownloadSnackbarController.onDownloadSucceeded(item.getDownloadInfo(), + DownloadSnackbarController.INVALID_NOTIFICATION_ID, downloadId, + canResolve, true); + } else if (result.downloadStatus == DownloadManagerService.DownloadStatus.FAILED) { + onDownloadFailed(item.getDownloadInfo(), downloadId, result.failureReason, + installNotifyURI); + removeOMADownloadFromSharedPrefs(downloadId); + DownloadManagerService.getDownloadManagerService().onDownloadFailed( + item, result.failureReason); + } + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java index 65a5ca48..467f2b7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java
@@ -22,6 +22,8 @@ import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel; import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.TabAttributeKeys; +import org.chromium.chrome.browser.tab.TabAttributes; import org.chromium.chrome.browser.tab.TabBrowserControlsOffsetHelper; import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet; import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver; @@ -332,8 +334,12 @@ } } + public static boolean isDialogShowing(Tab tab) { + return TabAttributes.from(tab).get(TabAttributeKeys.MODAL_DIALOG_SHOWING, false); + } + private void onTabModalDialogStateChanged(boolean isShowing) { - mActiveTab.onTabModalDialogStateChanged(isShowing); + TabAttributes.from(mActiveTab).set(TabAttributeKeys.MODAL_DIALOG_SHOWING, isShowing); // Also need to update browser control state after dismissal to refresh the constraints. TabBrowserControlsOffsetHelper offsetHelper = getControlsOffsetHelper();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java index 4cb20a88..50245f7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -181,14 +181,8 @@ */ private int mRootId; - /** - * Whether the tab should be grouped with its parent tab. - */ - private boolean mGroupedWithParent = true; - private boolean mIsClosing; private boolean mIsShowingErrorPage; - private boolean mIsShowingTabModalDialog; private Bitmap mFavicon; private int mFaviconWidth; @@ -603,13 +597,6 @@ } /** - * @return Whether a tab modal dialog is showing. - */ - public boolean isShowingTabModalDialog() { - return mIsShowingTabModalDialog; - } - - /** * @return Whether the {@link Tab} is currently showing an error page. */ public boolean isShowingErrorPage() { @@ -1836,23 +1823,6 @@ return mParentId; } - /** - * @return Whether the tab should be grouped with its parent tab (true by default). - */ - public boolean isGroupedWithParent() { - return mGroupedWithParent; - } - - /** - * Sets whether the tab should be grouped with its parent tab. - * - * @param groupedWithParent The new value. - * @see #isGroupedWithParent - */ - public void setGroupedWithParent(boolean groupedWithParent) { - mGroupedWithParent = groupedWithParent; - } - private void destroyNativePageInternal(NativePage nativePage) { if (nativePage == null) return; assert nativePage != mNativePage : "Attempting to destroy active page."; @@ -2607,14 +2577,6 @@ } /** - * Handle browser controls when a tab modal dialog is shown. - * @param isShowing Whether a tab modal dialog is showing. - */ - public void onTabModalDialogStateChanged(boolean isShowing) { - mIsShowingTabModalDialog = isShowing; - } - - /** * @return Whether input events from the renderer are ignored on the browser side. */ public boolean areRendererInputEventsIgnored() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabAttributeKeys.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabAttributeKeys.java new file mode 100644 index 0000000..5141d9af --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabAttributeKeys.java
@@ -0,0 +1,23 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tab; + +import android.support.annotation.StringDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * List of attributes used for {@link TabAttributes}. + */ +@StringDef({TabAttributeKeys.GROUPED_WITH_PARENT, TabAttributeKeys.MODAL_DIALOG_SHOWING}) +@Retention(RetentionPolicy.SOURCE) +public @interface TabAttributeKeys { + /** Whether the tab should be grouped with its parent tab. True by default. */ + public static final String GROUPED_WITH_PARENT = "isTabGroupedWithParent"; + + /** Whether tab modal dialog is showing or not. */ + public static final String MODAL_DIALOG_SHOWING = "isTabModalDialogShowing"; +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabAttributes.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabAttributes.java new file mode 100644 index 0000000..669f64a --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabAttributes.java
@@ -0,0 +1,81 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tab; + +import android.support.annotation.Nullable; + +import org.chromium.base.UserData; +import org.chromium.base.UserDataHost; + +import java.util.HashMap; +import java.util.Map; + +/** + * Per-tab storage that holds a map of attributes. Allows other classes to access + * the attribute without having it directly hang on to a tab. + */ +public class TabAttributes implements UserData { + private static final Class<TabAttributes> USER_DATA_KEY = TabAttributes.class; + + private final Map<String, Object> mAttributes = new HashMap<>(); + + // Null object used to differentiate the uninitialized attributes from those explicitly + // set to |null|. + private static final Object NULL_VALUE = new Object(); + + public static TabAttributes from(Tab tab) { + UserDataHost host = tab.getUserDataHost(); + TabAttributes attrs = host.getUserData(USER_DATA_KEY); + return attrs != null ? attrs : host.setUserData(USER_DATA_KEY, new TabAttributes()); + } + + private TabAttributes() {} + + /** + * Gets the attribute of the Tab. + * @param key Name of the attribute. + */ + @Nullable + @SuppressWarnings("unchecked") + public <T> T get(@TabAttributeKeys String key) { + Object value = mAttributes.get(key); + return value != NULL_VALUE ? (T) value : null; + } + + /** + * Gets the attribute of the Tab. + * @param key Name of the attribute. + * @param defaultValue Default attribute to return if the attribute has not been set. + * Note that the attribute that has been set to {@code null} is also regarded + * as <b>set</b>, therefore returns {@code null} not the default value. + */ + @Nullable + public <T> T get(@TabAttributeKeys String key, T defaultValue) { + return mAttributes.containsKey(key) ? get(key) : defaultValue; + } + + /** + * Sets the attribute of the Tab. + * <p> + * Note that {@code value} can be {@code null}, which will make the attribute have + * an explicit {@code null}. {@link getValue(String, T)} will start returning + * {@code null} rather than the passed default value. + * @param key Name of the attribute. + * @param value Attribute to set. + */ + public <T> void set(@TabAttributeKeys String key, @Nullable T value) { + mAttributes.put(key, value == null ? NULL_VALUE : value); + } + + /** + * Clears the attribute of the Tab. {@link get()} returns {@code null} after the attribute + * is cleared. + * <p> + * @param key Name of the attribute. + */ + public void clear(@TabAttributeKeys String key) { + mAttributes.remove(key); + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java index b44ca18..f6c8cefa 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabStateBrowserControlsVisibilityDelegate.java
@@ -10,6 +10,7 @@ import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.device.DeviceClassManager; +import org.chromium.chrome.browser.modaldialog.TabModalPresenter; import org.chromium.chrome.browser.tab.Tab.TabHidingType; import org.chromium.chrome.browser.util.AccessibilityUtil; import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils; @@ -152,7 +153,7 @@ enableHidingBrowserControls &= (mTab.getFullscreenManager() != null); enableHidingBrowserControls &= DeviceClassManager.enableFullscreen(); enableHidingBrowserControls &= !mIsFullscreenWaitingForLoad; - enableHidingBrowserControls &= !mTab.isShowingTabModalDialog(); + enableHidingBrowserControls &= !TabModalPresenter.isDialogShowing(mTab); return enableHidingBrowserControls; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java index 3bbe3e8..1aaf279 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelOrderController.java
@@ -5,6 +5,8 @@ package org.chromium.chrome.browser.tabmodel; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.TabAttributeKeys; +import org.chromium.chrome.browser.tab.TabAttributes; /** * This class acts as a controller for determining where tabs should be inserted @@ -95,7 +97,8 @@ int count = currentModel.getCount(); for (int i = count - 1; i >= startIndex; i--) { Tab tab = currentModel.getTabAt(i); - if (tab.getParentId() == openerId && tab.isGroupedWithParent()) { + if (tab.getParentId() == openerId + && TabAttributes.from(tab).get(TabAttributeKeys.GROUPED_WITH_PARENT, true)) { return i; } } @@ -109,7 +112,8 @@ TabModel currentModel = mTabModelSelector.getCurrentModel(); int count = currentModel.getCount(); for (int i = 0; i < count; i++) { - currentModel.getTabAt(i).setGroupedWithParent(false); + TabAttributes.from(currentModel.getTabAt(i)) + .set(TabAttributeKeys.GROUPED_WITH_PARENT, false); } }
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index 319d0772..41ae9192 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -550,7 +550,7 @@ "java/src/org/chromium/chrome/browser/download/DownloadItem.java", "java/src/org/chromium/chrome/browser/download/DownloadLocationCustomView.java", "java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java", - "java/src/org/chromium/chrome/browser/download/DownloadManagerDelegate.java", + "java/src/org/chromium/chrome/browser/download/DownloadManagerBridge.java", "java/src/org/chromium/chrome/browser/download/DownloadManagerService.java", "java/src/org/chromium/chrome/browser/download/DownloadMediaData.java", "java/src/org/chromium/chrome/browser/download/DownloadMediaParserBridge.java", @@ -1521,6 +1521,8 @@ "java/src/org/chromium/chrome/browser/tab/SadTab.java", "java/src/org/chromium/chrome/browser/tab/SadTabView.java", "java/src/org/chromium/chrome/browser/tab/Tab.java", + "java/src/org/chromium/chrome/browser/tab/TabAttributeKeys.java", + "java/src/org/chromium/chrome/browser/tab/TabAttributes.java", "java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java", "java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java", "java/src/org/chromium/chrome/browser/tab/TabContextMenuPopulator.java", @@ -2542,6 +2544,7 @@ "junit/src/org/chromium/chrome/browser/suggestions/ImageFetcherTest.java", "junit/src/org/chromium/chrome/browser/suggestions/TileGroupUnitTest.java", "junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java", + "junit/src/org/chromium/chrome/browser/tab/TabAttributesTest.java", "junit/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java", "junit/src/org/chromium/chrome/browser/tabstate/TabStateUnitTest.java", "junit/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java index 925f31a9..25bcc3f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadManagerServiceTest.java
@@ -26,6 +26,7 @@ import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.RetryOnFailure; +import org.chromium.base.test.util.UrlUtils; import org.chromium.chrome.browser.download.DownloadInfo.Builder; import org.chromium.chrome.browser.download.DownloadManagerServiceTest.MockDownloadNotifier.MethodID; import org.chromium.chrome.browser.preferences.ChromePreferenceManager; @@ -256,12 +257,6 @@ } @Override - protected boolean addCompletedDownload(DownloadItem downloadItem) { - downloadItem.setSystemDownloadId(1L); - return true; - } - - @Override protected void init() {} @Override @@ -311,6 +306,10 @@ return new Builder() .setBytesReceived(100) .setDownloadGuid(UUID.randomUUID().toString()) + .setFileName("test") + .setDescription("test") + .setFilePath(UrlUtils.getIsolatedTestFilePath( + "chrome/test/data/android/download/download.txt")) .build(); } @@ -543,46 +542,20 @@ @Feature({"Download"}) public void testShouldOpenAfterDownload() { // Should not open any download type MIME types. - Assert.assertFalse(DownloadManagerService.shouldOpenAfterDownload( - new DownloadInfo.Builder() - .setMimeType("application/download") - .setHasUserGesture(true) - .build())); - Assert.assertFalse(DownloadManagerService.shouldOpenAfterDownload( - new DownloadInfo.Builder() - .setMimeType("application/x-download") - .setHasUserGesture(true) - .build())); - Assert.assertFalse(DownloadManagerService.shouldOpenAfterDownload( - new DownloadInfo.Builder() - .setMimeType("application/octet-stream") - .setHasUserGesture(true) - .build())); - - // Should open PDFs. - Assert.assertTrue(DownloadManagerService.shouldOpenAfterDownload( - new DownloadInfo.Builder() - .setMimeType("application/pdf") - .setHasUserGesture(true) - .build())); - Assert.assertTrue(DownloadManagerService.shouldOpenAfterDownload( - new DownloadInfo.Builder() - .setContentDisposition("filename=test.pdf") - .setMimeType("application/pdf") - .setHasUserGesture(true) - .build())); - - // Require user gesture. - Assert.assertFalse(DownloadManagerService.shouldOpenAfterDownload( - new DownloadInfo.Builder() - .setMimeType("application/pdf") - .setHasUserGesture(false) - .build())); - Assert.assertFalse(DownloadManagerService.shouldOpenAfterDownload( - new DownloadInfo.Builder() - .setContentDisposition("filename=test.pdf") - .setMimeType("application/pdf") - .setHasUserGesture(false) - .build())); + Assert.assertFalse( + DownloadManagerService.shouldOpenAfterDownload("application/download", true)); + Assert.assertFalse( + DownloadManagerService.shouldOpenAfterDownload("application/x-download", true)); + Assert.assertFalse( + DownloadManagerService.shouldOpenAfterDownload("application/octet-stream", true)); + Assert.assertTrue(DownloadManagerService.shouldOpenAfterDownload("application/pdf", true)); + Assert.assertFalse( + DownloadManagerService.shouldOpenAfterDownload("application/pdf", false)); + Assert.assertFalse( + DownloadManagerService.shouldOpenAfterDownload("application/x-download", true)); + Assert.assertFalse( + DownloadManagerService.shouldOpenAfterDownload("application/x-download", true)); + Assert.assertFalse( + DownloadManagerService.shouldOpenAfterDownload("application/x-download", true)); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java index acff78c..3b33473 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java
@@ -19,13 +19,13 @@ import org.junit.runner.RunWith; import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.base.Callback; import org.chromium.base.ContextUtils; import org.chromium.base.test.util.AdvancedMockContext; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.RetryOnFailure; import org.chromium.base.test.util.UrlUtils; -import org.chromium.chrome.browser.download.DownloadManagerDelegate.DownloadQueryCallback; -import org.chromium.chrome.browser.download.DownloadManagerDelegate.DownloadQueryResult; +import org.chromium.chrome.browser.download.DownloadManagerBridge.DownloadQueryResult; import org.chromium.chrome.browser.download.OMADownloadHandler.OMAInfo; import org.chromium.chrome.browser.test.ChromeBrowserTestRule; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; @@ -86,9 +86,10 @@ public String mNofityURI; public long mDownloadId; - public OMADownloadHandlerForTest(Context context, DownloadManagerDelegate delegate, - DownloadSnackbarController downloadSnackbarController) { - super(context, delegate, downloadSnackbarController); + public OMADownloadHandlerForTest( + Context context, DownloadSnackbarController downloadSnackbarController) { + super(context, downloadSnackbarController); + addObserverForTest(downloadId -> { mDownloadId = downloadId; }); } @Override @@ -97,17 +98,10 @@ mNofityURI = omaInfo.getValue(OMA_INSTALL_NOTIFY_URI); return true; } - - @Override - public void onDownloadEnqueued( - boolean result, int failureReason, DownloadItem downloadItem, long downloadId) { - super.onDownloadEnqueued(result, failureReason, downloadItem, downloadId); - mDownloadId = downloadId; - } } /** Helper class to verify the result of {@DownloadManagerDelegate.queryDownloadResult}. */ - private static class DownloadQueryResultVerifier implements DownloadQueryCallback { + private static class DownloadQueryResultVerifier implements Callback<DownloadQueryResult> { private int mExpectedDownloadStatus; public boolean mQueryCompleted; @@ -117,7 +111,7 @@ } @Override - public void onQueryCompleted(DownloadQueryResult result, boolean showNotifications) { + public void onResult(DownloadQueryResult result) { mQueryCompleted = true; Assert.assertEquals(mExpectedDownloadStatus, result.downloadStatus); } @@ -273,8 +267,8 @@ } /** - * Test to make sure {@link DownloadManagerDelegate#queryDownloadResult} will report correctly - * about the status of completed downloads and removed downloads. + * Test to make sure {@link DownloadManagerBridge#queryDownloadResult} will report + * correctly about the status of completed downloads and removed downloads. */ @Test @MediumTest @@ -287,19 +281,14 @@ UrlUtils.getIsolatedTestFilePath("chrome/test/data/android/download/download.txt"), 4, true); - DownloadItem downloadItem = new DownloadItem(true, new DownloadInfo.Builder().build()); - downloadItem.setSystemDownloadId(downloadId1); - - DownloadManagerDelegate downloadManagerDelegate = new DownloadManagerDelegate(context); DownloadQueryResultVerifier verifier = new DownloadQueryResultVerifier(DownloadManagerService.DownloadStatus.COMPLETE); - downloadManagerDelegate.queryDownloadResult(downloadItem, false, verifier); + DownloadManagerBridge.queryDownloadResult(downloadId1, verifier); waitForQueryCompletion(verifier); manager.remove(downloadId1); - downloadItem.setSystemDownloadId(downloadId1); verifier = new DownloadQueryResultVerifier(DownloadManagerService.DownloadStatus.CANCELLED); - downloadManagerDelegate.queryDownloadResult(downloadItem, false, verifier); + DownloadManagerBridge.queryDownloadResult(downloadId1, verifier); waitForQueryCompletion(verifier); } @@ -319,11 +308,10 @@ UrlUtils.getIsolatedTestFilePath("chrome/test/data/android/download/download.txt"), 4, true); - DownloadManagerDelegate downloadManagerDelegate = new DownloadManagerDelegate(context); final MockDownloadSnackbarController snackbarController = new MockDownloadSnackbarController(); final OMADownloadHandlerForTest omaHandler = - new OMADownloadHandlerForTest(context, downloadManagerDelegate, snackbarController); + new OMADownloadHandlerForTest(context, snackbarController); // Write a few pending downloads into shared preferences. Set<String> pendingOmaDownloads = new HashSet<>(); @@ -375,12 +363,10 @@ try { DownloadInfo info = new DownloadInfo.Builder().build(); - final DownloadManagerDelegate downloadManagerDelegate = - new DownloadManagerDelegate(context); final MockDownloadSnackbarController snackbarController = new MockDownloadSnackbarController(); final OMADownloadHandlerForTest omaHandler = new OMADownloadHandlerForTest( - context, downloadManagerDelegate, snackbarController) { + context, snackbarController) { @Override public void onReceive(Context context, Intent intent) { // Ignore all the broadcasts.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserDialogTest.java index f5f4bab..53d835b6d 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserDialogTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserDialogTest.java
@@ -168,7 +168,7 @@ @Feature({"Browser", "RenderTest"}) public void testMicrophonePermissionPrompt() throws InterruptedException, TimeoutException, IOException { - testMicrophonePermissionPromptImpl(false); + testMicrophonePermissionPromptImpl(false, false); } /** @@ -181,13 +181,14 @@ throws InterruptedException, TimeoutException, IOException { // Create an incognito tab mVrBrowserTestFramework.openIncognitoTab("about:blank"); - testMicrophonePermissionPromptImpl(true); + testMicrophonePermissionPromptImpl(true, false); } - private void testMicrophonePermissionPromptImpl(boolean incognito) + private void testMicrophonePermissionPromptImpl(boolean incognito, boolean reposition) throws InterruptedException, TimeoutException, IOException { permissionPromptTestImpl("navigator.getUserMedia({audio: true}, onGranted, onDenied)", - "microphone_permission_prompt" + (incognito ? "_incognito" : ""), + "microphone_permission_prompt" + (incognito ? "_incognito" : "") + + (reposition ? "_reposition" : ""), UserFriendlyElementName.MICROPHONE_PERMISSION_INDICATOR, true /* grant */); // Additionally, make sure that the permission indicator reacts properly to a hover. NativeUiUtils.hoverElement( @@ -195,11 +196,35 @@ NativeUiUtils.waitForUiQuiescence(); RenderTestUtils.dumpAndCompare(NativeUiUtils.FRAME_BUFFER_SUFFIX_BROWSER_UI, "microphone_permission_indicator_hover" + (incognito ? "_incognito" : "") - + "_browser_ui", + + (reposition ? "_reposition" : "") + "_browser_ui", mRenderTestRule); } /** + * Tests that permission prompts and permission usage indicators properly follow the content + * quad when it's repositioned and resized. + */ + @Test + @LargeTest + @Feature({"Browser", "RenderTest"}) + public void testMicrophonePermissionPromptRepositionResize() + throws InterruptedException, TimeoutException, IOException { + VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS); + // Move the content quad a bit up and to the right and make it smaller. + NativeUiUtils.selectRepositionBar(); + NativeUiUtils.hoverElement(UserFriendlyElementName.CONTENT_QUAD, new PointF(0.5f, 1.0f)); + NativeUiUtils.scrollFling(NativeUiUtils.ScrollDirection.DOWN); + // We need to ensure that the scroll has finished, but we can't use waitForUiQuiescence() + // because the UI is never quiescent while the reposition bar is being used. So, wait a + // suitable number of frames. + NativeUiUtils.waitNumFrames(2 * NativeUiUtils.NUM_STEPS_FLING_SCROLL); + NativeUiUtils.deselectRepositionBar(); + NativeUiUtils.waitForUiQuiescence(); + + testMicrophonePermissionPromptImpl(false, true); + } + + /** * Test navigate to 2D page and launch the Camera dialog. Not valid on standalones because * there is no camera permission. */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNativeUiTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNativeUiTest.java index 471829b..13c15c3a 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNativeUiTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNativeUiTest.java
@@ -402,7 +402,8 @@ NativeUiUtils.clickContentNode( "textfield", new PointF(), 1 /* numClicks */, mVrBrowserTestFramework); NativeUiUtils.waitForUiQuiescence(); - NativeUiUtils.hoverElement(UserFriendlyElementName.CONTENT_QUAD, new PointF(0.0f, 0.55f)); + NativeUiUtils.hoverElement( + UserFriendlyElementName.CONTENT_QUAD, NativeUiUtils.REPOSITION_BAR_COORDINATES); NativeUiUtils.waitForUiQuiescence(); // Due to the way the repositioner works, the reposition bar is technically always visible // in the element hierarchy, so we can't just assert that it's invisible. Instead, we have @@ -593,4 +594,109 @@ RenderTestUtils.dumpAndCompareWithCrop(NativeUiUtils.FRAME_BUFFER_SUFFIX_BROWSER_UI, "suggestion_clicking_bottom", cropBounds, mRenderTestRule); } + + /** + * Tests that scrolling while holding the reposition bar causes the content window to be + * resized and that the resize doesn't affect the dimensions reported to the webpage. + */ + @Test + @MediumTest + @Feature({"Browser", "RenderTest"}) + public void testScrollResizing() throws InterruptedException, TimeoutException, IOException { + mVrBrowserTestFramework.loadUrlAndAwaitInitialization( + VrBrowserTestFramework.getFileUrlForHtmlTestFile( + "test_content_resizing_does_not_affect_webpage"), + PAGE_LOAD_TIMEOUT_S); + mVrBrowserTestFramework.executeStepAndWait("stepGetInitialDimensions()"); + NativeUiUtils.selectRepositionBar(); + NativeUiUtils.scrollFling(NativeUiUtils.ScrollDirection.DOWN); + // We need to ensure that the scroll has finished, but we can't use waitForUiQuiescence() + // because the UI is never quiescent while the reposition bar is being used. So, wait a + // suitable number of frames. + NativeUiUtils.waitNumFrames(2 * NativeUiUtils.NUM_STEPS_FLING_SCROLL); + NativeUiUtils.deselectRepositionBar(); + NativeUiUtils.waitForUiQuiescence(); + RenderTestUtils.dumpAndCompare( + NativeUiUtils.FRAME_BUFFER_SUFFIX_BROWSER_UI, "scroll_resizing", mRenderTestRule); + mVrBrowserTestFramework.executeStepAndWait("stepCheckDimensionsAfterResize()"); + mVrBrowserTestFramework.endTest(); + } + + /** + * Tests that the overflow menu and keyboard properly follow the content quad when it is + * repositioned. + */ + @Test + @MediumTest + @Feature({"Browser", "RenderTest"}) + public void testOverflowAndKeyboardFollowContentQuad() + throws InterruptedException, TimeoutException, IOException { + mVrTestRule.loadUrl( + VrBrowserTestFramework.getFileUrlForHtmlTestFile("generic_text_entry_page"), + PAGE_LOAD_TIMEOUT_S); + // Drag the content quad up and to the left. + NativeUiUtils.selectRepositionBar(); + NativeUiUtils.hoverElement(UserFriendlyElementName.CONTENT_QUAD, new PointF(-0.5f, 1.0f)); + NativeUiUtils.deselectRepositionBar(); + // Click coordinates are determined when we queue the command, not when it's actually + // executed, so ensure the quad has moved before attempting to click. + NativeUiUtils.waitForUiQuiescence(); + NativeUiUtils.clickElementAndWaitForUiQuiescence( + UserFriendlyElementName.OVERFLOW_MENU, new PointF()); + RenderTestUtils.dumpAndCompare(NativeUiUtils.FRAME_BUFFER_SUFFIX_BROWSER_UI, + "repositioned_overflow_menu", mRenderTestRule); + NativeUiUtils.clickElementAndWaitForUiQuiescence( + UserFriendlyElementName.OVERFLOW_MENU, new PointF()); + NativeUiUtils.clickContentNode( + "textfield", new PointF(), 1 /* numClicks */, mVrBrowserTestFramework); + NativeUiUtils.waitForUiQuiescence(); + RenderTestUtils.dumpAndCompare(NativeUiUtils.FRAME_BUFFER_SUFFIX_BROWSER_UI, + "repositioned_keyboard", mRenderTestRule); + } + + /** + * Tests that window and WebXR rAFs continue to fire while repositioning the content quad. + */ + @Test + @MediumTest + @CommandLineFlags.Add({"enable-features=WebXR"}) + public void testRAFsFireWhileRepositioning() + throws InterruptedException, TimeoutException, IOException { + mVrBrowserTestFramework.loadUrlAndAwaitInitialization( + VrBrowserTestFramework.getFileUrlForHtmlTestFile( + "test_rafs_fire_while_repositioning"), + PAGE_LOAD_TIMEOUT_S); + NativeUiUtils.selectRepositionBar(); + mVrBrowserTestFramework.executeStepAndWait("stepCheckForRafs()"); + mVrBrowserTestFramework.endTest(); + } + + /** + * Tests that the reposition bar is not active while a permission prompt is displayed. + */ + @Test + @MediumTest + @Feature({"Browser", "RenderTest"}) + public void testRepositionBarDoesNotAppearWithPermissionPromptVisible() + throws InterruptedException, TimeoutException, IOException { + // We don't need to actually accept the prompt, so we don't need to use the local server. + mVrBrowserTestFramework.loadUrlAndAwaitInitialization( + VrBrowserTestFramework.getFileUrlForHtmlTestFile("2d_permission_page"), + PAGE_LOAD_TIMEOUT_S); + NativeUiUtils.enableMockedInput(); + NativeUiUtils.waitForUiQuiescence(); + NativeUiUtils.performActionAndWaitForUiQuiescence(() -> { + NativeUiUtils.performActionAndWaitForVisibilityStatus( + UserFriendlyElementName.BROWSING_DIALOG, true /* visible */, () -> { + mVrBrowserTestFramework.runJavaScriptOrFail( + "navigator.getUserMedia({audio: true}, onGranted, onDenied)", + POLL_TIMEOUT_LONG_MS); + }); + }); + NativeUiUtils.hoverElement( + UserFriendlyElementName.CONTENT_QUAD, NativeUiUtils.REPOSITION_BAR_COORDINATES); + NativeUiUtils.waitForUiQuiescence(); + RenderTestUtils.dumpAndCompare(NativeUiUtils.FRAME_BUFFER_SUFFIX_BROWSER_UI, + "reposition_bar_permission_prompt_open", mRenderTestRule); + } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/NativeUiUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/NativeUiUtils.java index cbdc575b..636d722 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/NativeUiUtils.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/NativeUiUtils.java
@@ -71,6 +71,8 @@ public static final String FRAME_BUFFER_SUFFIX_WEB_XR_CONTENT = "_WebXrContent"; public static final String FRAME_BUFFER_SUFFIX_BROWSER_UI = "_BrowserUi"; public static final String FRAME_BUFFER_SUFFIX_BROWSER_CONTENT = "_BrowserContent"; + // Valid position to click on the content quad in order to select the reposition bar. + public static final PointF REPOSITION_BAR_COORDINATES = new PointF(0.0f, 0.55f); // Arbitrary but reasonable amount of time to expect the UI to stop updating after interacting // with an element. @@ -104,8 +106,30 @@ * unit square centered at (0, 0). */ public static void clickElement(int elementName, PointF position) { + clickDown(elementName, position); + clickUp(elementName, position); + } + + /** + * Moves to the given position in the given element and presses the touchpad down. + * + * @param elementName The UserFriendlyElementName that will be clicked on. + * @param position A PointF specifying where on the element to send the click relative to a + * unit square centered at (0, 0). + */ + public static void clickDown(int elementName, PointF position) { TestVrShellDelegate.getInstance().performControllerActionForTesting( elementName, VrControllerTestAction.CLICK_DOWN, position); + } + + /** + * Moves to the given position in the given element and unpresses the touchpad. + * + * @param elementName The UserFriendlyElementName that will be unclicked on. + * @param position A PointF specifying where on the element to send the click relative to a + * unit square centered at (0, 0). + */ + public static void clickUp(int elementName, PointF position) { TestVrShellDelegate.getInstance().performControllerActionForTesting( elementName, VrControllerTestAction.CLICK_UP, position); } @@ -197,6 +221,36 @@ } /** + * Helper function to click the reposition bar to select it. + */ + public static void selectRepositionBar() { + // We need to ensure that the reposition bar is at least partially visible before trying + // to click it, so hover it for a frame. + hoverElement(UserFriendlyElementName.CONTENT_QUAD, REPOSITION_BAR_COORDINATES); + clickElement(UserFriendlyElementName.CONTENT_QUAD, REPOSITION_BAR_COORDINATES); + } + + /** + * An alias to click in place in order to deslect the reposition bar. + */ + public static void deselectRepositionBar() { + clickElement(UserFriendlyElementName.CURRENT_POSITION, new PointF()); + } + + /** + * Touches the touchpad at the given coordinates, keeping whatever button states and direction + * are already present. + * + * @param position A PointF specifying where on the touchpad to touch, each axis in the range + * [-1, 1]. + */ + public static void touchDown(PointF position) { + TestVrShellDelegate.getInstance().performControllerActionForTesting( + UserFriendlyElementName.NONE /* unused */, VrControllerTestAction.TOUCH_DOWN, + position); + } + + /** * Helper function for performing a non-fling scroll. * * @param direction the ScrollDirection to scroll in. @@ -244,14 +298,10 @@ PointF stepIncrement = new PointF((end.x - start.x) / numSteps, (end.y - start.y) / numSteps); PointF currentPosition = new PointF(start.x, start.y); - TestVrShellDelegate.getInstance().performControllerActionForTesting( - UserFriendlyElementName.NONE /* unused */, VrControllerTestAction.TOUCH_DOWN, - currentPosition); + touchDown(currentPosition); for (int i = 0; i < numSteps; ++i) { currentPosition.offset(stepIncrement.x, stepIncrement.y); - TestVrShellDelegate.getInstance().performControllerActionForTesting( - UserFriendlyElementName.NONE /* unused */, VrControllerTestAction.TOUCH_DOWN, - currentPosition); + touchDown(currentPosition); } if (delayTouchUp) { waitNumFrames(NUM_FRAMES_DELAY_TO_PREVENT_FLING);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabAttributesTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabAttributesTest.java new file mode 100644 index 0000000..a3934fc6 --- /dev/null +++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/TabAttributesTest.java
@@ -0,0 +1,75 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tab; + +import static org.mockito.Mockito.when; + +import android.support.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import org.chromium.base.UserDataHost; +import org.chromium.base.test.BaseRobolectricTestRunner; + +/** + * Tests for {@link TabAttributes}. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class TabAttributesTest { + private static final String ATTR1 = "attr1"; + + @Mock + private Tab mTab; + + private final UserDataHost mUserDataHost = new UserDataHost(); + + // User-defined class used for attribute type. + private static class TestObject {} + + @Before + public void setUp() throws InterruptedException { + MockitoAnnotations.initMocks(this); + when(mTab.getUserDataHost()).thenReturn(mUserDataHost); + } + + @Test + @SmallTest + public void testBasicGetAndSetOperation() throws Exception { + // |get| for an uninitialized attribute returns null. + Assert.assertNull(TabAttributes.from(mTab).get(ATTR1)); + + // |get| with a default value returns the given default. + Assert.assertFalse(TabAttributes.from(mTab).get(ATTR1, Boolean.FALSE)); + + // |get| returns the stored attribute. + TabAttributes.from(mTab).set(ATTR1, true); + Assert.assertTrue(TabAttributes.from(mTab).get(ATTR1)); + + // |get| returns null after cleared. + TabAttributes.from(mTab).clear(ATTR1); + Assert.assertNull(TabAttributes.from(mTab).get(ATTR1)); + } + + @Test + @SmallTest + public void testGetWithDefaultReturnsNullForAttributeExplicitlySetToNull() throws Exception { + TestObject defaultValue = new TestObject(); + + // The attribute is not set by default, therefore default value is returned. + Assert.assertEquals(defaultValue, TabAttributes.from(mTab).get(ATTR1, defaultValue)); + + // Explicitly set the attribute to null. Now |get| should return null, + // disregarding the default value. + TabAttributes.from(mTab).set(ATTR1, null); + Assert.assertNull(TabAttributes.from(mTab).get(ATTR1, defaultValue)); + } +}
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc index e48c490..4f07270 100644 --- a/chrome/app/chrome_main_delegate.cc +++ b/chrome/app/chrome_main_delegate.cc
@@ -545,7 +545,12 @@ version_info::Channel channel = chrome::GetChannel(); bool is_canary_dev = (channel == version_info::Channel::CANARY || channel == version_info::Channel::DEV); - gwp_asan::EnableForMalloc(is_canary_dev); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + std::string process_type = + command_line.GetSwitchValueASCII(switches::kProcessType); + bool is_browser_process = process_type.empty(); + gwp_asan::EnableForMalloc(is_canary_dev, is_browser_process); #endif }
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp index 189a8dd..07a8688 100644 --- a/chrome/app/settings_strings.grdp +++ b/chrome/app/settings_strings.grdp
@@ -478,35 +478,35 @@ </if> <if expr="chromeos"> - <!-- Contained Shell page. Note that this experience is currently a prototype and is not shown + <!-- Kiosk Next Shell page. Note that this experience is currently a prototype and is not shown to end users at this point. TODO(922687): Change these strings to the final ones when our internal repo is ready.--> - <message name="IDS_SETTINGS_CONTAINED_SHELL_TITLE" desc="The title of Contained Shell section." translateable="false"> - Contained Shell (Dev) + <message name="IDS_SETTINGS_KIOSK_NEXT_SHELL_TITLE" desc="The title of Kiosk Next Shell section." translateable="false"> + Kiosk Next Shell (Dev) </message> - <message name="IDS_SETTINGS_CONTAINED_SHELL_LABEL" desc="The text associated with the contained shell section setting." translateable="false"> - Contained Shell + <message name="IDS_SETTINGS_KIOSK_NEXT_SHELL_LABEL" desc="The text associated with the contained shell section setting." translateable="false"> + Kiosk Next Shell </message> - <message name="IDS_SETTINGS_CONTAINED_SHELL_SUBTEXT_ENABLE" desc="Text shown when displaying the enable Contained Shell button." translateable="false"> - Enables the Contained Shell for the next sign in. + <message name="IDS_SETTINGS_KIOSK_NEXT_SHELL_SUBTEXT_ENABLE" desc="Text shown when displaying the enable Kiosk Next Shell button." translateable="false"> + Enables the Kiosk Next Shell for the next sign in. </message> - <message name="IDS_SETTINGS_CONTAINED_SHELL_SUBTEXT_DISABLE" desc="Text shown when displaying the disable Contained Shell button." translateable="false"> - Disable the Contained Shell and return to the normal Chrome OS experience. + <message name="IDS_SETTINGS_KIOSK_NEXT_SHELL_SUBTEXT_DISABLE" desc="Text shown when displaying the disable Kiosk Next Shell button." translateable="false"> + Disable the Kiosk Next Shell and return to the normal Chrome OS experience. </message> - <message name="IDS_SETTINGS_CONTAINED_SHELL_TURN_OFF" desc="Label for the button that disables the Contained Shell." translateable="false"> + <message name="IDS_SETTINGS_KIOSK_NEXT_SHELL_TURN_OFF" desc="Label for the button that disables the Kiosk Next Shell." translateable="false"> Turn off </message> - <message name="IDS_SETTINGS_CONTAINED_SHELL_ENABLED_DIALOG_TITLE" desc="Title for the confirmation dialog when disabling the Contained Shell." translateable="false"> - Turn off the Contained Shell + <message name="IDS_SETTINGS_KIOSK_NEXT_SHELL_ENABLED_DIALOG_TITLE" desc="Title for the confirmation dialog when disabling the Kiosk Next Shell." translateable="false"> + Turn off the Kiosk Next Shell </message> - <message name="IDS_SETTINGS_CONTAINED_SHELL_DISABLED_DIALOG_TITLE" desc="Title for the confirmation dialog when enabling the Contained Shell." translateable="false"> - Turn on the Contained Shell + <message name="IDS_SETTINGS_KIOSK_NEXT_SHELL_DISABLED_DIALOG_TITLE" desc="Title for the confirmation dialog when enabling the Kiosk Next Shell." translateable="false"> + Turn on the Kiosk Next Shell </message> - <message name="IDS_SETTINGS_CONTAINED_SHELL_ENABLED_DIALOG_BODY" desc="Body text for the confirmation dialog when disabling the Contained Shell." translateable="false"> - This will turn off the Contained Shell, sign you out and return you to the normal Chrome OS experience. + <message name="IDS_SETTINGS_KIOSK_NEXT_SHELL_ENABLED_DIALOG_BODY" desc="Body text for the confirmation dialog when disabling the Kiosk Next Shell." translateable="false"> + This will turn off the Kiosk Next Shell, sign you out and return you to the normal Chrome OS experience. </message> - <message name="IDS_SETTINGS_CONTAINED_SHELL_DISABLED_DIALOG_BODY" desc="Body text for the confirmation dialog when enabling the Contained Shell." translateable="false"> - This will sign you out and turn on the Contained Shell experience. + <message name="IDS_SETTINGS_KIOSK_NEXT_SHELL_DISABLED_DIALOG_BODY" desc="Body text for the confirmation dialog when enabling the Kiosk Next Shell." translateable="false"> + This will sign you out and turn on the Kiosk Next Shell. </message> <!-- Crostini Page --> <message name="IDS_SETTINGS_CROSTINI_TITLE" desc="The title of Crostini section.">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 4e9e26e..f973effb 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -1210,6 +1210,10 @@ flag_descriptions::kDisableIpcFloodingProtectionDescription, kOsAll, SINGLE_VALUE_TYPE(switches::kDisableIpcFloodingProtection)}, #if defined(OS_ANDROID) + {"contextual-search-definitions", + flag_descriptions::kContextualSearchDefinitionsName, + flag_descriptions::kContextualSearchDefinitionsDescription, kOsAndroid, + FEATURE_VALUE_TYPE(chrome::android::kContextualSearchDefinitions)}, {"contextual-search-ml-tap-suppression", flag_descriptions::kContextualSearchMlTapSuppressionName, flag_descriptions::kContextualSearchMlTapSuppressionDescription,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc index 04a2c24..736422791 100644 --- a/chrome/browser/android/chrome_feature_list.cc +++ b/chrome/browser/android/chrome_feature_list.cc
@@ -115,6 +115,7 @@ &kCommandLineOnNonRooted, &kContentSuggestionsScrollToLoad, &kContentSuggestionsThumbnailDominantColor, + &kContextualSearchDefinitions, &kContextualSearchMlTapSuppression, &kContextualSearchSecondTap, &kContextualSearchTapDisableOverride, @@ -314,6 +315,9 @@ "ContentSuggestionsThumbnailDominantColor", base::FEATURE_ENABLED_BY_DEFAULT}; +const base::Feature kContextualSearchDefinitions{ + "ContextualSearchDefinitions", base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kContextualSearchMlTapSuppression{ "ContextualSearchMlTapSuppression", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h index be27a30..1c0ba3e 100644 --- a/chrome/browser/android/chrome_feature_list.h +++ b/chrome/browser/android/chrome_feature_list.h
@@ -44,6 +44,7 @@ extern const base::Feature kCommandLineOnNonRooted; extern const base::Feature kContentSuggestionsScrollToLoad; extern const base::Feature kContentSuggestionsThumbnailDominantColor; +extern const base::Feature kContextualSearchDefinitions; extern const base::Feature kContextualSearchMlTapSuppression; extern const base::Feature kContextualSearchSecondTap; extern const base::Feature kContextualSearchTapDisableOverride;
diff --git a/chrome/browser/android/compositor/layer/tab_layer.cc b/chrome/browser/android/compositor/layer/tab_layer.cc index 9a8a7bb..53b2300 100644 --- a/chrome/browser/android/compositor/layer/tab_layer.cc +++ b/chrome/browser/android/compositor/layer/tab_layer.cc
@@ -439,7 +439,6 @@ close_button_->SetUIResourceId(close_btn_resource->ui_resource()->id()); if (!back_visible) { - float inset_diff = inset_border ? border_padding.y() : 0.f; gfx::Rect rounded_descaled_content_area( round(descaled_local_content_area.x()), round(descaled_local_content_area.y()), @@ -448,8 +447,8 @@ SetContentProperties( id, ids, can_use_live_layer, static_to_view_blend, true, alpha, - saturation, true, rounded_descaled_content_area, width, height, - inset_diff, border_inner_shadow_resource, border_inner_shadow_alpha); + saturation, true, rounded_descaled_content_area, + border_inner_shadow_resource, border_inner_shadow_alpha); } else if (back_logo_resource) { back_logo_->SetUIResourceId(back_logo_resource->ui_resource()->id()); @@ -694,9 +693,6 @@ float saturation, bool should_clip, const gfx::Rect& clip, - float width, - float height, - int inset_diff, ui::NinePatchResource* inner_shadow_resource, float inner_shadow_alpha) { if (tab_ids.size() == 0) { @@ -714,8 +710,7 @@ tabgroup_content_layer->SetProperties( id, tab_ids, can_use_live_layer, static_to_view_blend, should_override_content_alpha, content_alpha_override, saturation, - should_clip, clip, width, height, inset_diff, inner_shadow_resource, - inner_shadow_alpha); + should_clip, clip, inner_shadow_resource, inner_shadow_alpha); front_border_inner_shadow_->SetIsDrawable(false); }
diff --git a/chrome/browser/android/compositor/layer/tab_layer.h b/chrome/browser/android/compositor/layer/tab_layer.h index b54bfaa8..cc77e8e7 100644 --- a/chrome/browser/android/compositor/layer/tab_layer.h +++ b/chrome/browser/android/compositor/layer/tab_layer.h
@@ -129,9 +129,6 @@ float saturation, bool should_clip, const gfx::Rect& clip, - float width, - float height, - int inset_diff, ui::NinePatchResource* inner_shadow_resource, float inner_shadow_alpha);
diff --git a/chrome/browser/android/compositor/layer/tabgroup_content_layer.cc b/chrome/browser/android/compositor/layer/tabgroup_content_layer.cc index d6bbaf8..4873333 100644 --- a/chrome/browser/android/compositor/layer/tabgroup_content_layer.cc +++ b/chrome/browser/android/compositor/layer/tabgroup_content_layer.cc
@@ -6,19 +6,22 @@ #include <vector> -#include "base/lazy_instance.h" #include "cc/layers/layer.h" -#include "cc/layers/layer_collections.h" -#include "cc/layers/nine_patch_layer.h" -#include "cc/paint/filter_operations.h" -#include "chrome/browser/android/compositor/layer/thumbnail_layer.h" +#include "chrome/browser/android/compositor/layer/tabgroup_tab_content_layer.h" #include "chrome/browser/android/compositor/tab_content_manager.h" -#include "content/public/browser/android/compositor.h" #include "ui/android/resources/nine_patch_resource.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/size.h" namespace android { +// row x column of the tab group content grid is 2 x 2. +const int THUMBNAIL_ROWS = 2; +const int THUMBNAIL_COLS = THUMBNAIL_ROWS; +const float DESIRED_SCALE = 0.475; +const float GAP_SCALE = 0.05; + // static scoped_refptr<TabGroupContentLayer> TabGroupContentLayer::Create( TabContentManager* tab_content_manager) { @@ -35,12 +38,53 @@ float saturation, bool should_clip, const gfx::Rect& clip, - float width, - float height, - int inset_diff, ui::NinePatchResource* inner_shadow_resource, float inner_shadow_alpha) { - // TODO(meiliang): Override here; + if (group_tab_content_layers_.size() == 0) { + for (int i = 0; i < 4; i++) { + group_tab_content_layers_.emplace_back( + TabGroupTabContentLayer::Create(tab_content_manager_)); + layer_->AddChild(group_tab_content_layers_.back()->layer()); + } + } + + const gfx::RectF border_inner_shadow_padding( + inner_shadow_resource->padding()); + + const gfx::Size border_inner_shadow_padding_size( + inner_shadow_resource->size().width() - + border_inner_shadow_padding.width(), + inner_shadow_resource->size().height() - + border_inner_shadow_padding.height()); + + int size = tab_ids.size(); + for (int i = 0; i < size; i++) { + group_tab_content_layers_[i]->SetProperties( + tab_ids[i], can_use_live_layer, static_to_view_blend, + should_override_content_alpha, content_alpha_override, saturation, + should_clip, clip, inner_shadow_resource, tab_ids, inner_shadow_alpha); + + gfx::Transform transform; + transform.Scale(DESIRED_SCALE, DESIRED_SCALE); + group_tab_content_layers_[i]->layer()->SetTransform(transform); + + int height_offset_factor = i / THUMBNAIL_ROWS; + int width_offset_factor = i % THUMBNAIL_COLS; + + float position_x = + clip.x() + + width_offset_factor * + (clip.width() * DESIRED_SCALE + clip.width() * GAP_SCALE - + border_inner_shadow_padding_size.width()); + + float position_y = + clip.y() + + height_offset_factor * + (clip.height() * DESIRED_SCALE + clip.height() * GAP_SCALE - + border_inner_shadow_padding_size.height()); + gfx::PointF position(position_x, position_y); + group_tab_content_layers_[i]->layer()->SetPosition(position); + } } TabGroupContentLayer::TabGroupContentLayer(
diff --git a/chrome/browser/android/compositor/layer/tabgroup_content_layer.h b/chrome/browser/android/compositor/layer/tabgroup_content_layer.h index 95d97d7..c7670a7 100644 --- a/chrome/browser/android/compositor/layer/tabgroup_content_layer.h +++ b/chrome/browser/android/compositor/layer/tabgroup_content_layer.h
@@ -10,15 +10,13 @@ #include "base/macros.h" #include "chrome/browser/android/compositor/layer/content_layer.h" -#include "chrome/browser/android/compositor/layer/layer.h" -#include "chrome/browser/android/compositor/layer/tabgroup_tab_content_layer.h" #include "ui/android/resources/nine_patch_resource.h" #include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/size.h" namespace android { class TabContentManager; +class TabGroupTabContentLayer; // Sub layer tree representation of the contents of a // set of four tabs within the group. @@ -36,9 +34,6 @@ float saturation, bool should_clip, const gfx::Rect& clip, - float width, - float height, - int inset_diff, ui::NinePatchResource* inner_shadow_resource, float inner_shadow_alpha);
diff --git a/chrome/browser/android/compositor/layer/tabgroup_tab_content_layer.cc b/chrome/browser/android/compositor/layer/tabgroup_tab_content_layer.cc index 5950292c4..6acb3ec4 100644 --- a/chrome/browser/android/compositor/layer/tabgroup_tab_content_layer.cc +++ b/chrome/browser/android/compositor/layer/tabgroup_tab_content_layer.cc
@@ -6,14 +6,13 @@ #include <vector> -#include "base/lazy_instance.h" #include "cc/layers/layer.h" #include "cc/layers/nine_patch_layer.h" -#include "cc/paint/filter_operations.h" -#include "chrome/browser/android/compositor/layer/thumbnail_layer.h" +#include "chrome/browser/android/compositor/layer/content_layer.h" #include "chrome/browser/android/compositor/tab_content_manager.h" -#include "content/public/browser/android/compositor.h" #include "ui/android/resources/nine_patch_resource.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/size.h" namespace android { @@ -34,13 +33,16 @@ bool should_clip, const gfx::Rect& clip, ui::NinePatchResource* border_inner_shadow_resource, - float width, - float height, const std::vector<int>& tab_ids, - float border_inner_shadow_alpha, - int inset_diff) { - // TODO(meiliang) : should call content_->SetProperties() and create - // set border_inner_shadow to front_border_inner_shadow_. + float border_inner_shadow_alpha) { + content_->SetProperties(id, can_use_live_layer, static_to_view_blend, + should_override_content_alpha, content_alpha_override, + saturation, should_clip, clip); + + setBorderProperties(border_inner_shadow_resource, clip, + border_inner_shadow_alpha); + + layer_->SetBounds(front_border_inner_shadow_->bounds()); } scoped_refptr<cc::Layer> TabGroupTabContentLayer::layer() { @@ -60,4 +62,37 @@ TabGroupTabContentLayer::~TabGroupTabContentLayer() {} +void TabGroupTabContentLayer::setBorderProperties( + ui::NinePatchResource* border_inner_shadow_resource, + const gfx::Rect& clip, + float border_inner_shadow_alpha) { + // precalculate helper values + const gfx::RectF border_inner_shadow_padding( + border_inner_shadow_resource->padding()); + + const gfx::Size border_inner_shadow_padding_size( + border_inner_shadow_resource->size().width() - + border_inner_shadow_padding.width(), + border_inner_shadow_resource->size().height() - + border_inner_shadow_padding.height()); + + gfx::Size border_inner_shadow_size(clip.size()); + border_inner_shadow_size.Enlarge(border_inner_shadow_padding_size.width(), + border_inner_shadow_padding_size.height()); + + front_border_inner_shadow_->SetUIResourceId( + border_inner_shadow_resource->ui_resource()->id()); + front_border_inner_shadow_->SetAperture( + border_inner_shadow_resource->aperture()); + front_border_inner_shadow_->SetBorder( + border_inner_shadow_resource->Border(border_inner_shadow_size)); + + gfx::PointF border_inner_shadow_position( + ScalePoint(border_inner_shadow_padding.origin(), -1)); + + front_border_inner_shadow_->SetPosition(border_inner_shadow_position); + front_border_inner_shadow_->SetBounds(border_inner_shadow_size); + front_border_inner_shadow_->SetOpacity(border_inner_shadow_alpha); +} + } // namespace android
diff --git a/chrome/browser/android/compositor/layer/tabgroup_tab_content_layer.h b/chrome/browser/android/compositor/layer/tabgroup_tab_content_layer.h index 6cdefc10..e12bbfc 100644 --- a/chrome/browser/android/compositor/layer/tabgroup_tab_content_layer.h +++ b/chrome/browser/android/compositor/layer/tabgroup_tab_content_layer.h
@@ -9,14 +9,18 @@ #include <vector> #include "base/macros.h" -#include "chrome/browser/android/compositor/layer/content_layer.h" #include "chrome/browser/android/compositor/layer/layer.h" #include "ui/android/resources/nine_patch_resource.h" #include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/size.h" + +namespace cc { +class Layer; +class NinePatchLayer; +} // namespace cc namespace android { +class ContentLayer; class TabContentManager; // Sub layer tree representation of the contents of a @@ -35,11 +39,8 @@ bool should_clip, const gfx::Rect& clip, ui::NinePatchResource* border_inner_shadow_resource, - float width, - float height, const std::vector<int>& tab_ids, - float border_inner_shadow_alpha, - int inset_diff); + float border_inner_shadow_alpha); scoped_refptr<cc::Layer> layer() override; @@ -48,6 +49,9 @@ ~TabGroupTabContentLayer() override; private: + void setBorderProperties(ui::NinePatchResource* border_inner_shadow_resource, + const gfx::Rect& clip, + float border_inner_shadow_alpha); scoped_refptr<cc::Layer> layer_; scoped_refptr<ContentLayer> content_; scoped_refptr<cc::NinePatchLayer> front_border_inner_shadow_;
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc index 670d397a..131d440 100644 --- a/chrome/browser/apps/app_service/arc_apps.cc +++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -174,9 +174,7 @@ int32_t size_hint_in_dip, bool allow_placeholder_icon, LoadIconCallback callback) { - if (!icon_key.is_null() && - (icon_key->icon_type == apps::mojom::IconType::kArc) && - !icon_key->s_key.empty()) { + if (!icon_key.is_null() && !icon_key->s_key.empty()) { // Treat the Play Store as a special case, loading an icon defined by a // resource instead of asking the Android VM (or the cache of previous // responses from the Android VM). Presumably this is for bootstrapping: @@ -332,7 +330,8 @@ apps::mojom::AppPtr app = apps::mojom::App::New(); app->app_type = apps::mojom::AppType::kArc; app->app_id = app_id; - app->icon_key = NewIconKey(app_id); + app->icon_key = + icon_key_factory_.MakeIconKey(apps::mojom::AppType::kArc, app_id); Publish(std::move(app)); } @@ -446,7 +445,8 @@ app->name = app_info.name; app->short_name = app->name; - app->icon_key = NewIconKey(app_id); + app->icon_key = + icon_key_factory_.MakeIconKey(apps::mojom::AppType::kArc, app_id); app->last_launch_time = app_info.last_launch_time; app->install_time = app_info.install_time; @@ -475,11 +475,6 @@ return app; } -apps::mojom::IconKeyPtr ArcApps::NewIconKey(const std::string& app_id) { - return icon_key_factory_.MakeIconKey(apps::mojom::AppType::kArc, - apps::mojom::IconType::kArc, app_id); -} - void ArcApps::Publish(apps::mojom::AppPtr app) { subscribers_.ForAllPtrs([&app](apps::mojom::Subscriber* subscriber) { std::vector<apps::mojom::AppPtr> apps;
diff --git a/chrome/browser/apps/app_service/arc_apps.h b/chrome/browser/apps/app_service/arc_apps.h index 76e3373..2e40e4e 100644 --- a/chrome/browser/apps/app_service/arc_apps.h +++ b/chrome/browser/apps/app_service/arc_apps.h
@@ -92,7 +92,6 @@ apps::mojom::AppPtr Convert(const std::string& app_id, const ArcAppListPrefs::AppInfo& app_info); - apps::mojom::IconKeyPtr NewIconKey(const std::string& app_id); void Publish(apps::mojom::AppPtr app); void ConvertAndPublishPackageApps( const arc::mojom::ArcPackageInfo& package_info);
diff --git a/chrome/browser/apps/app_service/built_in_chromeos_apps.cc b/chrome/browser/apps/app_service/built_in_chromeos_apps.cc index 9eda6f9..35deb570 100644 --- a/chrome/browser/apps/app_service/built_in_chromeos_apps.cc +++ b/chrome/browser/apps/app_service/built_in_chromeos_apps.cc
@@ -38,10 +38,9 @@ l10n_util::GetStringUTF8(internal_app.searchable_string_resource_id)); } - app->icon_key = apps::mojom::IconKey::New(); - app->icon_key->app_type = apps::mojom::AppType::kBuiltIn; - app->icon_key->icon_type = apps::mojom::IconType::kResource; - app->icon_key->u_key = static_cast<uint64_t>(internal_app.icon_resource_id); + app->icon_key = apps::mojom::IconKey::New( + apps::mojom::AppType::kBuiltIn, + static_cast<uint64_t>(internal_app.icon_resource_id), std::string()); app->last_launch_time = base::Time(); app->install_time = base::Time(); @@ -118,9 +117,8 @@ bool allow_placeholder_icon, LoadIconCallback callback) { constexpr bool is_placeholder_icon = false; - if (!icon_key.is_null() && - (icon_key->icon_type == apps::mojom::IconType::kResource) && - (icon_key->u_key != 0) && (icon_key->u_key <= INT_MAX)) { + if (!icon_key.is_null() && (icon_key->u_key != 0) && + (icon_key->u_key <= INT_MAX)) { int resource_id = static_cast<int>(icon_key->u_key); LoadIconFromResource(icon_compression, size_hint_in_dip, resource_id, is_placeholder_icon, std::move(callback));
diff --git a/chrome/browser/apps/app_service/built_in_chromeos_apps.h b/chrome/browser/apps/app_service/built_in_chromeos_apps.h index 6bc59ef..41360057 100644 --- a/chrome/browser/apps/app_service/built_in_chromeos_apps.h +++ b/chrome/browser/apps/app_service/built_in_chromeos_apps.h
@@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_APPS_APP_SERVICE_BUILT_IN_CHROMEOS_APPS_H_ #define CHROME_BROWSER_APPS_APP_SERVICE_BUILT_IN_CHROMEOS_APPS_H_ +#include <string> + #include "base/macros.h" #include "chrome/services/app_service/public/mojom/app_service.mojom.h" #include "mojo/public/cpp/bindings/binding.h"
diff --git a/chrome/browser/apps/app_service/crostini_apps.cc b/chrome/browser/apps/app_service/crostini_apps.cc index 72dce42e..7d1e885 100644 --- a/chrome/browser/apps/app_service/crostini_apps.cc +++ b/chrome/browser/apps/app_service/crostini_apps.cc
@@ -67,16 +67,12 @@ bool allow_placeholder_icon, LoadIconCallback callback) { if (!icon_key.is_null()) { - if ((icon_key->icon_type == apps::mojom::IconType::kResource) && - (icon_key->u_key != 0) && (icon_key->u_key <= INT_MAX)) { - int resource_id = static_cast<int>(icon_key->u_key); - constexpr bool is_placeholder_icon = false; - LoadIconFromResource(icon_compression, size_hint_in_dip, resource_id, - is_placeholder_icon, std::move(callback)); - return; - } - - if (icon_key->icon_type == apps::mojom::IconType::kCrostini) { + // A non-empty s_key means that the icon is provided by the Crostini VM + // (and possibly cached on disk). + // + // An empty s_key means that the icon is a resource built into the Chrome + // OS binary. + if (!icon_key->s_key.empty()) { auto scale_factor = apps_util::GetPrimaryDisplayUIScaleFactor(); // Try loading the icon from an on-disk cache. If that fails, fall back @@ -90,6 +86,13 @@ icon_compression, size_hint_in_dip, allow_placeholder_icon, scale_factor)); return; + + } else if ((icon_key->u_key != 0) && (icon_key->u_key <= INT_MAX)) { + int resource_id = static_cast<int>(icon_key->u_key); + constexpr bool is_placeholder_icon = false; + LoadIconFromResource(icon_compression, size_hint_in_dip, resource_id, + is_placeholder_icon, std::move(callback)); + return; } } @@ -216,6 +219,8 @@ } apps::mojom::IconKeyPtr CrostiniApps::NewIconKey(const std::string& app_id) { + DCHECK(!app_id.empty()); + // Treat the Crostini Terminal as a special case, loading an icon defined by // a resource instead of asking the Crostini VM (or the cache of previous // responses from the Crostini VM). Presumably this is for bootstrapping: the @@ -223,16 +228,11 @@ // should be showable even before the user has installed their first Crostini // app and before bringing up an Crostini VM for the first time. if (app_id == crostini::kCrostiniTerminalId) { - auto icon_key = apps::mojom::IconKey::New(); - icon_key->app_type = apps::mojom::AppType::kCrostini; - icon_key->icon_type = apps::mojom::IconType::kResource; - icon_key->u_key = IDR_LOGO_CROSTINI_TERMINAL; - return icon_key; + return apps::mojom::IconKey::New(apps::mojom::AppType::kCrostini, + IDR_LOGO_CROSTINI_TERMINAL, std::string()); } - return icon_key_factory_.MakeIconKey(apps::mojom::AppType::kCrostini, - apps::mojom::IconType::kCrostini, - app_id); + return icon_key_factory_.MakeIconKey(apps::mojom::AppType::kCrostini, app_id); } void CrostiniApps::PublishAppID(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/extension_apps.cc b/chrome/browser/apps/app_service/extension_apps.cc index 77b8d8d..89fca9a 100644 --- a/chrome/browser/apps/app_service/extension_apps.cc +++ b/chrome/browser/apps/app_service/extension_apps.cc
@@ -128,9 +128,7 @@ int32_t size_hint_in_dip, bool allow_placeholder_icon, LoadIconCallback callback) { - if (!icon_key.is_null() && - (icon_key->icon_type == apps::mojom::IconType::kExtension) && - !icon_key->s_key.empty()) { + if (!icon_key.is_null() && !icon_key->s_key.empty()) { LoadIconFromExtension(icon_compression, size_hint_in_dip, profile_, icon_key->s_key, std::move(callback)); return; @@ -425,8 +423,7 @@ app->name = extension->name(); app->short_name = extension->short_name(); - app->icon_key = icon_key_factory_.MakeIconKey( - app_type_, apps::mojom::IconType::kExtension, extension->id()); + app->icon_key = icon_key_factory_.MakeIconKey(app_type_, extension->id()); if (profile_) { auto* prefs = extensions::ExtensionPrefs::Get(profile_);
diff --git a/chrome/browser/apps/app_service/icon_key_util.cc b/chrome/browser/apps/app_service/icon_key_util.cc index f7ff4cd..0a24c3f6 100644 --- a/chrome/browser/apps/app_service/icon_key_util.cc +++ b/chrome/browser/apps/app_service/icon_key_util.cc
@@ -10,18 +10,13 @@ apps::mojom::IconKeyPtr IncrementingIconKeyFactory::MakeIconKey( apps::mojom::AppType app_type, - apps::mojom::IconType icon_type, const std::string& s_key, uint8_t flags) { // The flags occupy the low 8 bits. u_key_ += 1 << 8; - auto icon_key = apps::mojom::IconKey::New(); - icon_key->app_type = app_type; - icon_key->icon_type = icon_type; - icon_key->s_key = s_key; - icon_key->u_key = u_key_ | static_cast<uint64_t>(flags); - return icon_key; + return apps::mojom::IconKey::New( + app_type, u_key_ | static_cast<uint64_t>(flags), s_key); } } // namespace apps_util
diff --git a/chrome/browser/apps/app_service/icon_key_util.h b/chrome/browser/apps/app_service/icon_key_util.h index b807d6b4..4d2b434 100644 --- a/chrome/browser/apps/app_service/icon_key_util.h +++ b/chrome/browser/apps/app_service/icon_key_util.h
@@ -31,7 +31,6 @@ IncrementingIconKeyFactory(); apps::mojom::IconKeyPtr MakeIconKey(apps::mojom::AppType app_type, - apps::mojom::IconType icon_type, const std::string& s_key, uint8_t flags = 0);
diff --git a/chrome/browser/chromeos/android_sms/streaming_connection_establisher.cc b/chrome/browser/chromeos/android_sms/streaming_connection_establisher.cc index 0c9216e..20f0823 100644 --- a/chrome/browser/chromeos/android_sms/streaming_connection_establisher.cc +++ b/chrome/browser/chromeos/android_sms/streaming_connection_establisher.cc
@@ -27,7 +27,7 @@ StreamingConnectionEstablisher::StreamingConnectionEstablisher( base::Clock* clock) - : clock_(clock) {} + : clock_(clock), weak_ptr_factory_(this) {} StreamingConnectionEstablisher::~StreamingConnectionEstablisher() = default; void StreamingConnectionEstablisher::EstablishConnection( @@ -38,7 +38,7 @@ FROM_HERE, {content::BrowserThread::IO}, base::BindOnce(&StreamingConnectionEstablisher:: SendStartStreamingMessageIfNotConnected, - base::Unretained(this), url, connection_mode, + weak_ptr_factory_.GetWeakPtr(), url, connection_mode, service_worker_context)); } @@ -78,7 +78,7 @@ service_worker_context->StartServiceWorkerAndDispatchLongRunningMessage( url, std::move(msg), base::BindOnce(&StreamingConnectionEstablisher::OnMessageDispatchResult, - base::Unretained(this))); + weak_ptr_factory_.GetWeakPtr())); } void StreamingConnectionEstablisher::OnMessageDispatchResult(bool status) {
diff --git a/chrome/browser/chromeos/android_sms/streaming_connection_establisher.h b/chrome/browser/chromeos/android_sms/streaming_connection_establisher.h index a5917b9..b0e4490 100644 --- a/chrome/browser/chromeos/android_sms/streaming_connection_establisher.h +++ b/chrome/browser/chromeos/android_sms/streaming_connection_establisher.h
@@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_CHROMEOS_ANDROID_SMS_STREAMING_CONNECTION_ESTABLISHER_H_ #define CHROME_BROWSER_CHROMEOS_ANDROID_SMS_STREAMING_CONNECTION_ESTABLISHER_H_ +#include "base/memory/weak_ptr.h" #include "base/time/clock.h" #include "chrome/browser/chromeos/android_sms/connection_establisher.h" @@ -48,6 +49,8 @@ base::Clock* clock_; bool is_connected_ = false; base::Time start_connection_message_time_; + base::WeakPtrFactory<StreamingConnectionEstablisher> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(StreamingConnectionEstablisher); };
diff --git a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc index 93d794f4..45e2a4c1 100644 --- a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc +++ b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
@@ -221,7 +221,7 @@ QueryResult* result) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); base::ScopedBlockingCall scoped_blocking_call( - base::BlockingType::MAY_BLOCK); + FROM_HERE, base::BlockingType::MAY_BLOCK); result->success = cups_connection_.GetJobs(printer_ids, &result->queues); } @@ -230,7 +230,7 @@ void CancelJobImpl(const std::string& printer_id, const int job_id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); base::ScopedBlockingCall scoped_blocking_call( - base::BlockingType::MAY_BLOCK); + FROM_HERE, base::BlockingType::MAY_BLOCK); std::unique_ptr<::printing::CupsPrinter> printer = cups_connection_.GetPrinter(printer_id);
diff --git a/chrome/browser/conflicts/incompatible_applications_updater_win.cc b/chrome/browser/conflicts/incompatible_applications_updater_win.cc index d7b95eb4..37bfeb1 100644 --- a/chrome/browser/conflicts/incompatible_applications_updater_win.cc +++ b/chrome/browser/conflicts/incompatible_applications_updater_win.cc
@@ -213,11 +213,13 @@ ModuleDatabaseEventSource* module_database_event_source, const CertificateInfo& exe_certificate_info, scoped_refptr<ModuleListFilter> module_list_filter, - const InstalledApplications& installed_applications) + const InstalledApplications& installed_applications, + bool module_analysis_disabled) : module_database_event_source_(module_database_event_source), exe_certificate_info_(exe_certificate_info), module_list_filter_(std::move(module_list_filter)), - installed_applications_(installed_applications) { + installed_applications_(installed_applications), + module_analysis_disabled_(module_analysis_disabled) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); module_database_event_source_->AddObserver(this); } @@ -294,6 +296,13 @@ return; } + // New modules should not cause a warning when the module analysis is + // disabled. + if (module_analysis_disabled_) { + warning_decision = ModuleWarningDecision::kNotAnalyzed; + return; + } + // First check if this module is a part of Chrome. // Explicitly whitelist modules whose signing cert's Subject field matches the @@ -432,3 +441,7 @@ DCHECK(it != module_warning_decisions_.end()); return it->second; } + +void IncompatibleApplicationsUpdater::DisableModuleAnalysis() { + module_analysis_disabled_ = true; +}
diff --git a/chrome/browser/conflicts/incompatible_applications_updater_win.h b/chrome/browser/conflicts/incompatible_applications_updater_win.h index 0cb9b3e4..7c01a1f 100644 --- a/chrome/browser/conflicts/incompatible_applications_updater_win.h +++ b/chrome/browser/conflicts/incompatible_applications_updater_win.h
@@ -53,6 +53,9 @@ kAllowedMicrosoft, // Explicitly whitelisted by the Module List component. kAllowedWhitelisted, + // Module analysis was interrupted using DisableModuleAnalysis(). No warning + // will be emitted for that module. + kNotAnalyzed, // This module is already going to be blocked on next browser launch, so // don't warn about it. kAddedToBlacklist, @@ -83,7 +86,8 @@ ModuleDatabaseEventSource* module_database_event_source, const CertificateInfo& exe_certificate_info, scoped_refptr<ModuleListFilter> module_list_filter, - const InstalledApplications& installed_applications); + const InstalledApplications& installed_applications, + bool module_analysis_disabled); ~IncompatibleApplicationsUpdater() override; static void RegisterLocalStatePrefs(PrefRegistrySimple* registry); @@ -111,6 +115,10 @@ ModuleWarningDecision GetModuleWarningDecision( const ModuleInfoKey& module_key) const; + // Disables the analysis of newly found modules. This is a one way switch that + // will apply until Chrome is restarted. + void DisableModuleAnalysis(); + private: ModuleDatabaseEventSource* const module_database_event_source_; @@ -128,6 +136,10 @@ base::flat_map<ModuleInfoKey, ModuleWarningDecision> module_warning_decisions_; + // Indicates if the analysis of newly found modules is disabled. Used as a + // workaround for https://crbug.com/892294. + bool module_analysis_disabled_; + DISALLOW_COPY_AND_ASSIGN(IncompatibleApplicationsUpdater); };
diff --git a/chrome/browser/conflicts/incompatible_applications_updater_win_unittest.cc b/chrome/browser/conflicts/incompatible_applications_updater_win_unittest.cc index ce364cc..388ddb2 100644 --- a/chrome/browser/conflicts/incompatible_applications_updater_win_unittest.cc +++ b/chrome/browser/conflicts/incompatible_applications_updater_win_unittest.cc
@@ -149,7 +149,7 @@ CreateIncompatibleApplicationsUpdater() { return std::make_unique<IncompatibleApplicationsUpdater>( this, exe_certificate_info_, module_list_filter_, - installed_applications_); + installed_applications_, false); } // ModuleDatabaseEventSource: @@ -441,3 +441,25 @@ IncompatibleApplicationsUpdater::GetCachedApplications(); ASSERT_EQ(0u, application_names.size()); } + +TEST_F(IncompatibleApplicationsUpdaterTest, DisableModuleAnalysis) { + AddIncompatibleApplication(dll1_, L"Foo", Option::ADD_REGISTRY_ENTRY); + + auto incompatible_applications_updater = + CreateIncompatibleApplicationsUpdater(); + + incompatible_applications_updater->DisableModuleAnalysis(); + + // Simulate the module loading into the process. + ModuleInfoKey module_key(dll1_, 0, 0); + incompatible_applications_updater->OnNewModuleFound( + module_key, CreateLoadedModuleInfoData()); + incompatible_applications_updater->OnModuleDatabaseIdle(); + + // The module does not cause a warning. + EXPECT_FALSE(IncompatibleApplicationsUpdater::HasCachedApplications()); + EXPECT_TRUE(IncompatibleApplicationsUpdater::GetCachedApplications().empty()); + EXPECT_EQ( + incompatible_applications_updater->GetModuleWarningDecision(module_key), + IncompatibleApplicationsUpdater::ModuleWarningDecision::kNotAnalyzed); +}
diff --git a/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc b/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc index f47b7e49..3f3c115d 100644 --- a/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc +++ b/chrome/browser/conflicts/module_blacklist_cache_updater_win.cc
@@ -161,6 +161,7 @@ case ModuleBlockingDecision::kAllowedMicrosoft: case ModuleBlockingDecision::kAllowedWhitelisted: case ModuleBlockingDecision::kTolerated: + case ModuleBlockingDecision::kNotAnalyzed: return false; // The following are reasons for the module to be blocked. @@ -184,7 +185,8 @@ scoped_refptr<ModuleListFilter> module_list_filter, const std::vector<third_party_dlls::PackedListModule>& initial_blacklisted_modules, - OnCacheUpdatedCallback on_cache_updated_callback) + OnCacheUpdatedCallback on_cache_updated_callback, + bool module_analysis_disabled) : module_database_event_source_(module_database_event_source), exe_certificate_info_(exe_certificate_info), module_list_filter_(std::move(module_list_filter)), @@ -193,6 +195,7 @@ background_sequence_(base::CreateSequencedTaskRunnerWithTraits( {base::MayBlock(), base::TaskPriority::BEST_EFFORT, base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})), + module_analysis_disabled_(module_analysis_disabled), weak_ptr_factory_(this) { DCHECK(module_list_filter_); module_database_event_source_->AddObserver(this); @@ -305,6 +308,10 @@ return it->second; } +void ModuleBlacklistCacheUpdater::DisableModuleAnalysis() { + module_analysis_disabled_ = true; +} + void ModuleBlacklistCacheUpdater::OnTimerExpired() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -333,6 +340,11 @@ const ModuleInfoData& module_data) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // New modules should not be added to the cache when the module analysis is + // disabled. + if (module_analysis_disabled_) + return ModuleBlockingDecision::kNotAnalyzed; + // First check if this module is a part of Chrome's installation. This can // override explicit directions in the module list. This prevents us from // shooting ourselves in the foot by accidentally issuing a blacklisting
diff --git a/chrome/browser/conflicts/module_blacklist_cache_updater_win.h b/chrome/browser/conflicts/module_blacklist_cache_updater_win.h index 273c497..03c709b 100644 --- a/chrome/browser/conflicts/module_blacklist_cache_updater_win.h +++ b/chrome/browser/conflicts/module_blacklist_cache_updater_win.h
@@ -97,6 +97,9 @@ kAllowedMicrosoft, // Explicitly whitelisted by the Module List component. kAllowedWhitelisted, + // Module analysis was interrupted using DisableModuleAnalysis(). This + // module will not be added to the cache. + kNotAnalyzed, // New "allowed" reasons should be added here! // Unwanted, but allowed to load by the Module List component. This is @@ -160,7 +163,8 @@ scoped_refptr<ModuleListFilter> module_list_filter, const std::vector<third_party_dlls::PackedListModule>& initial_blacklisted_modules, - OnCacheUpdatedCallback on_cache_updated_callback); + OnCacheUpdatedCallback on_cache_updated_callback, + bool module_analysis_disabled); ~ModuleBlacklistCacheUpdater() override; // Returns true if the blocking of third-party modules is enabled. The return @@ -185,6 +189,10 @@ const ModuleBlockingState& GetModuleBlockingState( const ModuleInfoKey& module_key) const; + // Disables the analysis of newly found modules. This is a one way switch that + // will apply until Chrome is restarted. + void DisableModuleAnalysis(); + private: // The state of the module with respect to the ModuleList. enum class ModuleListState { @@ -244,6 +252,10 @@ // Holds the blocking state for all known modules. base::flat_map<ModuleInfoKey, ModuleBlockingState> module_blocking_states_; + // Indicates if the analysis of newly found modules is disabled. Used as a + // workaround for https://crbug.com/892294. + bool module_analysis_disabled_; + SEQUENCE_CHECKER(sequence_checker_); base::WeakPtrFactory<ModuleBlacklistCacheUpdater> weak_ptr_factory_;
diff --git a/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc b/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc index b703797..753e950 100644 --- a/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc +++ b/chrome/browser/conflicts/module_blacklist_cache_updater_win_unittest.cc
@@ -122,7 +122,8 @@ initial_blacklisted_modules_, base::BindRepeating( &ModuleBlacklistCacheUpdaterTest::OnModuleBlacklistCacheUpdated, - base::Unretained(this))); + base::Unretained(this)), + false); } void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); } @@ -358,3 +359,35 @@ EXPECT_TRUE(internal::ModuleEqual()(expected, blacklisted_modules[0])); } + +TEST_F(ModuleBlacklistCacheUpdaterTest, DisableModuleAnalysis) { + EXPECT_FALSE(base::PathExists(module_blacklist_cache_path())); + + auto module_blacklist_cache_updater = CreateModuleBlacklistCacheUpdater(); + module_blacklist_cache_updater->DisableModuleAnalysis(); + + // Simulate some arbitrary module loading into the process. + ModuleInfoKey module_key(dll1_, 0, 0); + module_blacklist_cache_updater->OnNewModuleFound( + module_key, CreateLoadedModuleInfoData()); + module_blacklist_cache_updater->OnModuleDatabaseIdle(); + + RunUntilIdle(); + EXPECT_TRUE(base::PathExists(module_blacklist_cache_path())); + EXPECT_TRUE(on_cache_updated_callback_invoked()); + EXPECT_TRUE(RegistryKeyExists()); + + // Check the cache. + third_party_dlls::PackedListMetadata metadata; + std::vector<third_party_dlls::PackedListModule> blacklisted_modules; + base::MD5Digest md5_digest; + EXPECT_EQ(ReadResult::kSuccess, + ReadModuleBlacklistCache(module_blacklist_cache_path(), &metadata, + &blacklisted_modules, &md5_digest)); + + // The module is not added to the blacklist. + EXPECT_EQ(0u, blacklisted_modules.size()); + ASSERT_EQ(ModuleBlacklistCacheUpdater::ModuleBlockingDecision::kNotAnalyzed, + module_blacklist_cache_updater->GetModuleBlockingState(module_key) + .blocking_decision); +}
diff --git a/chrome/browser/conflicts/module_database_win.cc b/chrome/browser/conflicts/module_database_win.cc index db1275c..bf69e1a 100644 --- a/chrome/browser/conflicts/module_database_win.cc +++ b/chrome/browser/conflicts/module_database_win.cc
@@ -273,6 +273,12 @@ // Immediately disable the hook. DisableHook() can be called concurrently. DisableHook(); + // Stop analyzing those modules. + ThirdPartyConflictsManager* third_party_conflicts_manager = + ModuleDatabase::GetInstance()->third_party_conflicts_manager_.get(); + if (third_party_conflicts_manager) + third_party_conflicts_manager->DisableModuleAnalysis(); + // Notify the ThirdPartyMetricsRecorder instance that the hook is disabled. // Since this is meant for a heartbeat metric, the small latency introduced // with the thread-hopping is perfectly acceptable.
diff --git a/chrome/browser/conflicts/module_database_win.h b/chrome/browser/conflicts/module_database_win.h index 00f3670a..e5949fe8 100644 --- a/chrome/browser/conflicts/module_database_win.h +++ b/chrome/browser/conflicts/module_database_win.h
@@ -146,7 +146,7 @@ // printing code that may require that third-party DLLs be successfully // loaded into the process to work correctly. // TODO(pmonette): Remove this workaround when printing is moved to a utility - // process. See https://crbug.com/809738. + // process. See https://crbug.com/892294. static void DisableThirdPartyBlocking(); // Accessor for the third party conflicts manager.
diff --git a/chrome/browser/conflicts/third_party_conflicts_manager_win.cc b/chrome/browser/conflicts/third_party_conflicts_manager_win.cc index 69b370f..4bb29ad 100644 --- a/chrome/browser/conflicts/third_party_conflicts_manager_win.cc +++ b/chrome/browser/conflicts/third_party_conflicts_manager_win.cc
@@ -333,6 +333,14 @@ SetTerminalState(State::kNoModuleListAvailableFailure); } +void ThirdPartyConflictsManager::DisableModuleAnalysis() { + module_analysis_disabled_ = true; + if (incompatible_applications_updater_) + incompatible_applications_updater_->DisableModuleAnalysis(); + if (incompatible_applications_updater_) + incompatible_applications_updater_->DisableModuleAnalysis(); +} + void ThirdPartyConflictsManager::OnModuleListFilterCreated( scoped_refptr<ModuleListFilter> module_list_filter) { module_list_filter_ = std::move(module_list_filter); @@ -400,7 +408,8 @@ module_list_filter_, *initial_blacklisted_modules_, base::BindRepeating( &ThirdPartyConflictsManager::OnModuleBlacklistCacheUpdated, - base::Unretained(this))); + base::Unretained(this)), + module_analysis_disabled_); } // The |incompatible_applications_updater_| instance must be created last so @@ -412,7 +421,8 @@ incompatible_applications_updater_ = std::make_unique<IncompatibleApplicationsUpdater>( module_database_event_source_, *exe_certificate_info_, - module_list_filter_, *installed_applications_); + module_list_filter_, *installed_applications_, + module_analysis_disabled_); } if (!incompatible_applications_updater_) {
diff --git a/chrome/browser/conflicts/third_party_conflicts_manager_win.h b/chrome/browser/conflicts/third_party_conflicts_manager_win.h index 08c8d86..3425c20 100644 --- a/chrome/browser/conflicts/third_party_conflicts_manager_win.h +++ b/chrome/browser/conflicts/third_party_conflicts_manager_win.h
@@ -140,6 +140,9 @@ return module_blacklist_cache_updater_.get(); } + // Disables the analysis of newly found modules. + void DisableModuleAnalysis(); + private: // Called when |module_list_filter_| finishes its initialization. void OnModuleListFilterCreated( @@ -235,6 +238,10 @@ // The callback that is invoked when |state_| changes. OnInitializationCompleteCallback on_initialization_complete_callback_; + // Indicates if the analysis of newly found modules is disabled. Used as a + // workaround for https://crbug.com/892294. + bool module_analysis_disabled_ = false; + base::WeakPtrFactory<ThirdPartyConflictsManager> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(ThirdPartyConflictsManager);
diff --git a/chrome/browser/devtools/devtools_eye_dropper.cc b/chrome/browser/devtools/devtools_eye_dropper.cc index b1ce3098..66528e57 100644 --- a/chrome/browser/devtools/devtools_eye_dropper.cc +++ b/chrome/browser/devtools/devtools_eye_dropper.cc
@@ -266,7 +266,6 @@ void DevToolsEyeDropper::OnFrameCaptured( base::ReadOnlySharedMemoryRegion data, ::media::mojom::VideoFrameInfoPtr info, - const gfx::Rect& update_rect, const gfx::Rect& content_rect, viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) { gfx::Size view_size = host_->GetView()->GetViewBounds().size();
diff --git a/chrome/browser/devtools/devtools_eye_dropper.h b/chrome/browser/devtools/devtools_eye_dropper.h index 5c0c293..8aa5b6c 100644 --- a/chrome/browser/devtools/devtools_eye_dropper.h +++ b/chrome/browser/devtools/devtools_eye_dropper.h
@@ -46,7 +46,6 @@ void OnFrameCaptured( base::ReadOnlySharedMemoryRegion data, ::media::mojom::VideoFrameInfoPtr info, - const gfx::Rect& update_rect, const gfx::Rect& content_rect, viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) override; void OnStopped() override;
diff --git a/chrome/browser/extensions/extension_functional_browsertest.cc b/chrome/browser/extensions/extension_functional_browsertest.cc index 3e4c631..9674a0f 100644 --- a/chrome/browser/extensions/extension_functional_browsertest.cc +++ b/chrome/browser/extensions/extension_functional_browsertest.cc
@@ -19,6 +19,7 @@ #include "content/public/browser/notification_service.h" #include "content/public/browser/render_frame_host.h" #include "content/public/test/browser_test_utils.h" +#include "content/public/test/download_test_observer.h" #include "content/public/test/test_utils.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" @@ -177,4 +178,29 @@ testing::ElementsAre(base::Bucket(false, 1))); } +IN_PROC_BROWSER_TEST_F(ExtensionFunctionalTest, DownloadExtensionResource) { + auto* download_manager = + content::BrowserContext::GetDownloadManager(profile()); + content::DownloadTestObserverTerminal download_observer( + download_manager, 1, + content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT); + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("download"))); + download_observer.WaitForFinished(); + + std::vector<download::DownloadItem*> download_items; + download_manager->GetAllDownloads(&download_items); + + base::ScopedAllowBlockingForTesting allow_blocking; + auto file_path = download_items[0]->GetTargetFilePath(); + + base::FilePath expected_path = ui_test_utils::GetTestFilePath( + base::FilePath(), + base::FilePath().AppendASCII("extensions/download/download.dat")); + + std::string actual_contents, expected_contents; + ASSERT_TRUE(base::ReadFileToString(file_path, &actual_contents)); + ASSERT_TRUE(base::ReadFileToString(expected_path, &expected_contents)); + ASSERT_EQ(expected_contents, actual_contents); +} + } // namespace extensions
diff --git a/chrome/browser/extensions/service_worker_apitest.cc b/chrome/browser/extensions/service_worker_apitest.cc index a26166d..8a0d4f3 100644 --- a/chrome/browser/extensions/service_worker_apitest.cc +++ b/chrome/browser/extensions/service_worker_apitest.cc
@@ -8,7 +8,6 @@ #include "base/bind_helpers.h" #include "base/json/json_reader.h" #include "base/macros.h" -#include "base/optional.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" @@ -36,14 +35,12 @@ #include "components/version_info/version_info.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_entry.h" -#include "content/public/browser/render_process_host.h" #include "content/public/browser/service_worker_context.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_switches.h" #include "content/public/common/origin_util.h" #include "content/public/common/page_type.h" -#include "content/public/common/result_codes.h" #include "content/public/test/background_sync_test_util.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/service_worker_test_helpers.h" @@ -249,25 +246,6 @@ ServiceWorkerTest::SetUpOnMainThread(); } - // Returns the only running worker id for |extension_id|. - // Returns base::nullopt if there isn't any worker running or more than one - // worker is running for |extension_id|. - base::Optional<WorkerId> GetUniqueRunningWorkerId( - const ExtensionId& extension_id) { - ProcessManager* process_manager = ProcessManager::Get(profile()); - std::vector<WorkerId> all_workers = - process_manager->GetAllWorkersIdsForTesting(); - base::Optional<WorkerId> running_worker_id; - for (const WorkerId& worker_id : all_workers) { - if (worker_id.extension_id == extension_id) { - if (running_worker_id) // More than one worker present. - return base::nullopt; - running_worker_id = worker_id; - } - } - return running_worker_id; - } - private: DISALLOW_COPY_AND_ASSIGN(ServiceWorkerBasedBackgroundTest); }; @@ -1585,68 +1563,6 @@ EXPECT_TRUE(worker_filtered_event_listener.WaitUntilSatisfied()); } -IN_PROC_BROWSER_TEST_P(ServiceWorkerBasedBackgroundTest, - ProcessManagerRegistrationOnShutdown) { - // Note that StopServiceWorkerForScope call below expects the worker to be - // completely installed, so wait for the |extension| worker to see "install" - // event. - ExtensionTestMessageListener install_listener("WORKER_INSTALLED", false); - const Extension* extension = LoadExtension(test_data_dir_.AppendASCII( - "service_worker/worker_based_background/process_manager")); - ASSERT_TRUE(extension); - EXPECT_TRUE(install_listener.WaitUntilSatisfied()); - - base::Optional<WorkerId> worker_id = - GetUniqueRunningWorkerId(extension->id()); - ASSERT_TRUE(worker_id); - { - // Shutdown the worker. - // TODO(lazyboy): Ideally we'd want to test worker shutdown on idle, do that - // once //content API allows to override test timeouts for Service Workers. - base::RunLoop run_loop; - content::StoragePartition* storage_partition = - content::BrowserContext::GetDefaultStoragePartition( - browser()->profile()); - GURL scope = extension->url(); - content::StopServiceWorkerForScope( - storage_partition->GetServiceWorkerContext(), - // The service worker is registered at the top level scope. - extension->url(), run_loop.QuitClosure()); - run_loop.Run(); - } - - EXPECT_FALSE(ProcessManager::Get(profile())->HasServiceWorker(*worker_id)); -} - -IN_PROC_BROWSER_TEST_P(ServiceWorkerBasedBackgroundTest, - ProcessManagerRegistrationOnTerminate) { - // NOTE: It is not necessary to wait for "install" event from the worker - // for this test, but we're lazily reusing the extension from - // ProcessManagerRegistrationOnShutdown test. - ExtensionTestMessageListener install_listener("WORKER_INSTALLED", false); - const Extension* extension = LoadExtension(test_data_dir_.AppendASCII( - "service_worker/worker_based_background/process_manager")); - ASSERT_TRUE(extension); - EXPECT_TRUE(install_listener.WaitUntilSatisfied()); - - base::Optional<WorkerId> worker_id = - GetUniqueRunningWorkerId(extension->id()); - ASSERT_TRUE(worker_id); - { - // Terminate worker's RenderProcessHost. - content::RenderProcessHost* worker_render_process_host = - content::RenderProcessHost::FromID(worker_id->render_process_id); - ASSERT_TRUE(worker_render_process_host); - content::RenderProcessHostWatcher process_exit_observer( - worker_render_process_host, - content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT); - worker_render_process_host->Shutdown(content::RESULT_CODE_KILLED); - process_exit_observer.Wait(); - } - - EXPECT_FALSE(ProcessManager::Get(profile())->HasServiceWorker(*worker_id)); -} - // Run with both native and JS-based bindings. This ensures that both stable // (JS) and experimental (native) phases work correctly with worker scripts // while we launch native bindings to stable.
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 94e0d599..5569aac 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -417,6 +417,11 @@ "expiry_milestone": 76 }, { + "name": "contextual-search-definitions", + "owners": [ "//chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/OWNERS" ], + "expiry_milestone": 77 + }, + { "name": "contextual-search-ranker-query", "owners": [ "//chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/OWNERS" ], "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index ce24d2a..a610c14 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -2254,6 +2254,11 @@ "Enable content suggestions debug log accessible through " "snippets-internals."; +const char kContextualSearchDefinitionsName[] = "Contextual Search definitions"; +const char kContextualSearchDefinitionsDescription[] = + "Enables tap-activated contextual definitions of words on a page to be " + "presented in the caption of the Tap to Search Bar."; + const char kContextualSearchMlTapSuppressionName[] = "Contextual Search ML tap suppression"; const char kContextualSearchMlTapSuppressionDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index bc02ffe..0f316d5 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -1334,6 +1334,9 @@ extern const char kContentSuggestionsDebugLogName[]; extern const char kContentSuggestionsDebugLogDescription[]; +extern const char kContextualSearchDefinitionsName[]; +extern const char kContextualSearchDefinitionsDescription[]; + extern const char kContextualSearchMlTapSuppressionName[]; extern const char kContextualSearchMlTapSuppressionDescription[];
diff --git a/chrome/browser/metrics/chrome_metrics_service_accessor_unittest.cc b/chrome/browser/metrics/chrome_metrics_service_accessor_unittest.cc index 6da420e..c90cefd 100644 --- a/chrome/browser/metrics/chrome_metrics_service_accessor_unittest.cc +++ b/chrome/browser/metrics/chrome_metrics_service_accessor_unittest.cc
@@ -4,8 +4,6 @@ #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" -#include "base/base_switches.h" -#include "base/command_line.h" #include "base/macros.h" #include "build/build_config.h" #include "chrome/common/pref_names.h" @@ -46,16 +44,6 @@ GetLocalState()->ClearPref(pref); EXPECT_FALSE( ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled()); - - // If field trials are forced, metrics should always be disabled, regardless - // of the value of the pref. - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kForceFieldTrials); - EXPECT_FALSE( - ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled()); - GetLocalState()->SetBoolean(pref, true); - EXPECT_FALSE( - ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled()); #else // Metrics Reporting is never enabled when GOOGLE_CHROME_BUILD is undefined. EXPECT_FALSE(
diff --git a/chrome/browser/previews/previews_lite_page_browsertest.cc b/chrome/browser/previews/previews_lite_page_browsertest.cc index e06622a..5b1811b 100644 --- a/chrome/browser/previews/previews_lite_page_browsertest.cc +++ b/chrome/browser/previews/previews_lite_page_browsertest.cc
@@ -1006,9 +1006,6 @@ IN_PROC_BROWSER_TEST_P(PreviewsLitePageServerBrowserTest, DISABLE_ON_WIN_MAC(LitePagePreviewsReloadDisabled)) { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitWithFeatures( - {previews::features::kPreviewsDisallowedOnReloads}, {}); // Start with a non-preview load. g_browser_process->network_quality_tracker() ->ReportEffectiveConnectionTypeForTesting(
diff --git a/chrome/browser/printing/print_browsertest.cc b/chrome/browser/printing/print_browsertest.cc index eca14d3..c84e4ea 100644 --- a/chrome/browser/printing/print_browsertest.cc +++ b/chrome/browser/printing/print_browsertest.cc
@@ -13,6 +13,7 @@ #include "base/threading/thread_restrictions.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/printing/print_view_manager_common.h" +#include "chrome/browser/printing/printing_message_filter.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" @@ -197,14 +198,26 @@ frame_host->GetProcess()->AddFilter(filter.get()); } - PrintMsg_PrintFrame_Params GetDefaultPrintParams() { + static PrintMsg_PrintFrame_Params GetDefaultPrintFrameParams() { PrintMsg_PrintFrame_Params params; - gfx::Rect area(800, 600); - params.printable_area = area; + params.printable_area = gfx::Rect(800, 600); params.document_cookie = kDefaultDocumentCookie; return params; } + static PrintMsg_Print_Params GetNupPrintParams() { + PrintMsg_Print_Params params; + params.page_size = gfx::Size(612, 792); + params.content_size = gfx::Size(612, 792); + params.printable_area = gfx::Rect(612, 792); + params.dpi = gfx::Size(72, 72); + params.document_cookie = kDefaultDocumentCookie; + params.pages_per_sheet = 4; + params.printed_doc_type = + IsOopifEnabled() ? SkiaDocumentType::MSKP : SkiaDocumentType::PDF; + return params; + } + private: unsigned int num_expected_messages_; unsigned int num_received_messages_; @@ -316,7 +329,7 @@ AddFilterForFrame(rfh); rfh->Send(new PrintMsg_PrintFrameContent(rfh->GetRoutingID(), - GetDefaultPrintParams())); + GetDefaultPrintFrameParams())); // The printed result will be received and checked in // TestPrintFrameContentMsgFilter. @@ -340,8 +353,8 @@ AddFilterForFrame(test_frame); - test_frame->Send(new PrintMsg_PrintFrameContent(test_frame->GetRoutingID(), - GetDefaultPrintParams())); + test_frame->Send(new PrintMsg_PrintFrameContent( + test_frame->GetRoutingID(), GetDefaultPrintFrameParams())); // The printed result will be received and checked in // TestPrintFrameContentMsgFilter. @@ -385,8 +398,8 @@ AddFilterForFrame(grandchild_frame); } - main_frame->Send(new PrintMsg_PrintFrameContent(main_frame->GetRoutingID(), - GetDefaultPrintParams())); + main_frame->Send(new PrintMsg_PrintFrameContent( + main_frame->GetRoutingID(), GetDefaultPrintFrameParams())); // The printed result will be received and checked in // TestPrintFrameContentMsgFilter. @@ -429,8 +442,8 @@ if (oopif_enabled) AddFilterForFrame(child_frame); - main_frame->Send(new PrintMsg_PrintFrameContent(main_frame->GetRoutingID(), - GetDefaultPrintParams())); + main_frame->Send(new PrintMsg_PrintFrameContent( + main_frame->GetRoutingID(), GetDefaultPrintFrameParams())); // The printed result will be received and checked in // TestPrintFrameContentMsgFilter. @@ -573,4 +586,25 @@ PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false); } +// Printing frame content for the main frame of a generic webpage with N-up +// priting. This is a regression test for https://crbug.com/937247 +IN_PROC_BROWSER_TEST_F(PrintBrowserTest, PrintNup) { + ASSERT_TRUE(embedded_test_server()->Started()); + GURL url(embedded_test_server()->GetURL("/printing/test1.html")); + ui_test_utils::NavigateToURL(browser(), url); + + PrintingMessageFilter::SetTestUpdatePrintSettingsReply(GetNupPrintParams()); + PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false); +} + +// Site per process version of PrintBrowserTest.PrintNup. +IN_PROC_BROWSER_TEST_F(SitePerProcessPrintBrowserTest, PrintNup) { + ASSERT_TRUE(embedded_test_server()->Started()); + GURL url(embedded_test_server()->GetURL("/printing/test1.html")); + ui_test_utils::NavigateToURL(browser(), url); + + PrintingMessageFilter::SetTestUpdatePrintSettingsReply(GetNupPrintParams()); + PrintAndWaitUntilPreviewIsReady(/*print_only_selection=*/false); +} + } // namespace printing
diff --git a/chrome/browser/printing/printing_init.cc b/chrome/browser/printing/printing_init.cc index 6cb834a9..085798e 100644 --- a/chrome/browser/printing/printing_init.cc +++ b/chrome/browser/printing/printing_init.cc
@@ -23,6 +23,7 @@ #if BUILDFLAG(ENABLE_PRINT_PREVIEW) printing::PrintViewManager::CreateForWebContents(web_contents); printing::PrintPreviewMessageHandler::CreateForWebContents(web_contents); + printing::PdfNupConverterClient::CreateForWebContents(web_contents); #else printing::PrintViewManagerBasic::CreateForWebContents(web_contents); #endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
diff --git a/chrome/browser/printing/printing_message_filter.cc b/chrome/browser/printing/printing_message_filter.cc index a864d0b..3f6007d 100644 --- a/chrome/browser/printing/printing_message_filter.cc +++ b/chrome/browser/printing/printing_message_filter.cc
@@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/memory/singleton.h" +#include "base/no_destructor.h" #include "base/task/post_task.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" @@ -41,6 +42,11 @@ namespace { +PrintMsg_Print_Params& GetTestUpdatePrintSettingsReply() { + static base::NoDestructor<PrintMsg_Print_Params> params; + return *params; +} + class PrintingMessageFilterShutdownNotifierFactory : public BrowserContextKeyedServiceShutdownNotifierFactory { public: @@ -82,6 +88,13 @@ } // namespace +// static +void PrintingMessageFilter::SetTestUpdatePrintSettingsReply( + const PrintMsg_Print_Params& print_params) { + auto& test_params = GetTestUpdatePrintSettingsReply(); + test_params = print_params; +} + PrintingMessageFilter::PrintingMessageFilter(int render_process_id, Profile* profile) : BrowserMessageFilter(PrintMsgStart), @@ -268,6 +281,11 @@ this, routing_id)); } #endif + + auto& test_params = GetTestUpdatePrintSettingsReply(); + if (test_params.document_cookie > 0) + params.params = test_params; + PrintHostMsg_UpdatePrintSettings::WriteReplyParams(reply_msg, params, canceled); Send(reply_msg);
diff --git a/chrome/browser/printing/printing_message_filter.h b/chrome/browser/printing/printing_message_filter.h index c4d586e6..4607c9a 100644 --- a/chrome/browser/printing/printing_message_filter.h +++ b/chrome/browser/printing/printing_message_filter.h
@@ -21,6 +21,7 @@ struct PrintHostMsg_PreviewIds; struct PrintHostMsg_ScriptedPrint_Params; +struct PrintMsg_Print_Params; class Profile; namespace printing { @@ -32,6 +33,10 @@ // renderer process on the IPC thread. class PrintingMessageFilter : public content::BrowserMessageFilter { public: + // Sets a global override for print params in OnUpdatePrintSettingsReply(). + static void SetTestUpdatePrintSettingsReply( + const PrintMsg_Print_Params& print_params); + PrintingMessageFilter(int render_process_id, Profile* profile); // content::BrowserMessageFilter:
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn index 2c94443..89b9888 100644 --- a/chrome/browser/resources/BUILD.gn +++ b/chrome/browser/resources/BUILD.gn
@@ -68,7 +68,7 @@ } grit("net_internals_resources") { - source = "net_internals_resources.grd" + source = "net_internals/net_internals_resources.grd" defines = chrome_grit_defines outputs = [ "grit/net_internals_resources.h", @@ -78,7 +78,7 @@ } grit("quota_internals_resources") { - source = "quota_internals_resources.grd" + source = "quota_internals/quota_internals_resources.grd" defines = chrome_grit_defines outputs = [ "grit/quota_internals_resources.h", @@ -88,7 +88,7 @@ } grit("translate_internals_resources") { - source = "translate_internals_resources.grd" + source = "translate_internals/translate_internals_resources.grd" defines = chrome_grit_defines outputs = [ "grit/translate_internals_resources.h", @@ -98,7 +98,7 @@ } grit("webapks_ui_resources") { - source = "webapks_ui_resources.grd" + source = "webapks/webapks_ui_resources.grd" defines = chrome_grit_defines outputs = [ "grit/webapks_ui_resources.h", @@ -125,7 +125,7 @@ } grit("local_ntp_resources") { - source = "local_ntp_resources.grd" + source = "local_ntp/local_ntp_resources.grd" defines = chrome_grit_defines outputs = [ "grit/local_ntp_resources.h", @@ -208,7 +208,8 @@ } grit("sync_file_system_internals_resources") { - source = "sync_file_system_internals_resources.grd" + source = + "sync_file_system_internals/sync_file_system_internals_resources.grd" defines = chrome_grit_defines outputs = [ "grit/sync_file_system_internals_resources.h",
diff --git a/chrome/browser/resources/OWNERS b/chrome/browser/resources/OWNERS index d7b19b2..59d0ab0 100644 --- a/chrome/browser/resources/OWNERS +++ b/chrome/browser/resources/OWNERS
@@ -1,9 +1,7 @@ file://ui/webui/PLATFORM_OWNERS per-file component_extension_resources.grd=dgozman@chromium.org -per-file local_ntp_resources.grd=file://components/search/OWNERS per-file profile_signin_confirmation*=achuith@chromium.org per-file snippets_internals*=file://components/ntp_snippets/OWNERS -per-file sync_file_system_internals_resources.*=tzik@chromium.org # COMPONENT: UI
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 a49dc36..05a9355a 100644 --- a/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js +++ b/chrome/browser/resources/chromeos/login/oobe_screen_oauth_enrollment.js
@@ -252,7 +252,6 @@ * URL. */ onBeforeShow: function(data) { - chrome.send('showGuestInOobe', [true]); if (Oobe.getInstance().forceKeyboardFlow) { // We run the tab remapping logic inside of the webview so that the // simulated tab events will use the webview tab-stops. Simulated tab
diff --git a/chrome/browser/resources/feed_internals/feed_internals.html b/chrome/browser/resources/feed_internals/feed_internals.html index 735dc83..9a3c114 100644 --- a/chrome/browser/resources/feed_internals/feed_internals.html +++ b/chrome/browser/resources/feed_internals/feed_internals.html
@@ -9,7 +9,9 @@ <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Feed Internals</title> + <link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> <link rel="stylesheet" href="feed_internals.css"> @@ -22,29 +24,48 @@ <body> <h2>Properties</h2> - <table id="tblProperties"> + <table> <tr> <td>Is Feed Enabled</td> - <td id="isFeedEnabled"></td> + <td id="is-feed-enabled"></td> </tr> </table> <h2>User Classifier</h2> - <table id="tblUserClass"> + <table> <tr> <td>User Class Description</td> - <td id="userClassDescription"></td> + <td id="user-class-description"></td> </tr> <tr> <td>Average Hours Between Suggestion Views</td> - <td id="avgHoursBetweenViews"></td> + <td id="avg-hours-between-views"></td> </tr> <tr> <td>Average Hours Between Suggestion Uses</td> - <td id="avgHoursBetweenUses"></td> + <td id="avg-hours-between-uses"></td> </tr> </table> - <button id="btnClearUserClassification"> + <button id="clear-user-classification"> Clear User Classification </button> + <h2>Feed Library Actions</h2> + <button id="clear-cached-data"> + Clear Cache & Refresh Feed + </button> + <h2>Last Fetch</h2> + <table> + <tr> + <td>Last Fetch Status</td> + <td id="last-fetch-status"></td> + </tr> + <tr> + <td>Last Fetch Time</td> + <td id="last-fetch-time"></td> + </tr> + <tr> + <td>Refresh Suppress Time</td> + <td id="refresh-suppress-time"></td> + </tr> + </table> </body> </html>
diff --git a/chrome/browser/resources/feed_internals/feed_internals.js b/chrome/browser/resources/feed_internals/feed_internals.js index bba1724e..7e64c0b5 100644 --- a/chrome/browser/resources/feed_internals/feed_internals.js +++ b/chrome/browser/resources/feed_internals/feed_internals.js
@@ -4,34 +4,81 @@ 'use strict'; -// Reference to the backend. +/** + * Reference to the backend. + * @type {feedInternals.mojom.PageHandlerProxy} + */ let pageHandler = null; (function() { -// Get and display general properties. + +/** + * Get and display general properties. + */ function updatePageWithProperties() { pageHandler.getGeneralProperties().then(response => { + /** @type {!feedInternals.mojom.Properties} */ const properties = response.properties; - $('isFeedEnabled').textContent = properties.isFeedEnabled; + $('is-feed-enabled').textContent = properties.isFeedEnabled; }); } -// Get and display user classifier properties. +/** + * Get and display user classifier properties. + */ function updatePageWithUserClass() { pageHandler.getUserClassifierProperties().then(response => { + /** @type {!feedInternals.mojom.UserClassifier} */ const properties = response.properties; - $('userClassDescription').textContent = properties.userClassDescription; - $('avgHoursBetweenViews').textContent = properties.avgHoursBetweenViews; - $('avgHoursBetweenUses').textContent = properties.avgHoursBetweenUses; + $('user-class-description').textContent = properties.userClassDescription; + $('avg-hours-between-views').textContent = properties.avgHoursBetweenViews; + $('avg-hours-between-uses').textContent = properties.avgHoursBetweenUses; }); } -// Hook up buttons to event listeners. +/** + * Get and display last fetch data. + */ +function updatePageWithLastFetchProperties() { + pageHandler.getLastFetchProperties().then(response => { + /** @type {!feedInternals.mojom.LastFetchProperties} */ + const properties = response.properties; + $('last-fetch-status').textContent = properties.lastFetchStatus; + $('last-fetch-time').textContent = toDateString(properties.lastFetchTime); + $('refresh-suppress-time').textContent = + toDateString(properties.refreshSuppressTime); + }); +} + +/** + * Convert time to string for display. + * + * @param {feedInternals.mojom.Time|undefined} time + * @return {string} + */ +function toDateString(time) { + return time == null ? '' : new Date(time.msSinceEpoch).toLocaleString(); +} + +/** + * Hook up buttons to event listeners. + */ function setupEventListeners() { - $('btnClearUserClassification').addEventListener('click', function() { + $('clear-user-classification').addEventListener('click', function() { pageHandler.clearUserClassifierProperties(); updatePageWithUserClass(); }); + + $('clear-cached-data').addEventListener('click', function() { + pageHandler.clearCachedDataAndRefreshFeed(); + + // TODO(chouinard): Investigate whether the Feed library's + // AppLifecycleListener.onClearAll methods could accept a callback to notify + // when cache clear and Feed refresh operations are complete. If not, + // consider adding backend->frontend mojo communication to listen for + // updates, rather than waiting an arbitrary period of time. + setTimeout(updatePageWithLastFetchProperties, 1000); + }); } document.addEventListener('DOMContentLoaded', function() { @@ -40,6 +87,8 @@ updatePageWithProperties(); updatePageWithUserClass(); + updatePageWithLastFetchProperties(); + setupEventListeners(); }); })();
diff --git a/chrome/browser/resources/local_ntp/OWNERS b/chrome/browser/resources/local_ntp/OWNERS index d66146c..81e7a847 100644 --- a/chrome/browser/resources/local_ntp/OWNERS +++ b/chrome/browser/resources/local_ntp/OWNERS
@@ -5,5 +5,7 @@ ramyan@chromium.org treib@chromium.org +per-file local_ntp_resources.grd=file://components/search/OWNERS + # TEAM: ntp-dev@chromium.org # COMPONENT: UI>Browser>NewTabPage
diff --git a/chrome/browser/resources/local_ntp/local_ntp_resources.grd b/chrome/browser/resources/local_ntp/local_ntp_resources.grd new file mode 100644 index 0000000..f4da3f37 --- /dev/null +++ b/chrome/browser/resources/local_ntp/local_ntp_resources.grd
@@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> + <outputs> + <output filename="grit/local_ntp_resources.h" type="rc_header"> + <emit emit_type='prepend'></emit> + </output> + <output filename="local_ntp_resources.pak" type="data_package" /> + </outputs> + <release seq="1"> + <includes> + <include name="IDR_INSTANT_IFRAME_VALIDATION_JS" file="instant_iframe_validation.js" type="BINDATA" /> + <include name="IDR_CUSTOM_LINKS_ADD_SVG" file="icons\add_link.svg" type="BINDATA" /> + <include name="IDR_CUSTOM_LINKS_ADD_WHITE_SVG" file="icons\add_link_white.svg" type="BINDATA" /> + <include name="IDR_CUSTOM_LINKS_EDIT_HTML" file="custom_links_edit.html" type="BINDATA" /> + <include name="IDR_CUSTOM_LINKS_EDIT_CSS" file="custom_links_edit.css" type="BINDATA" /> + <include name="IDR_CUSTOM_LINKS_EDIT_JS" file="custom_links_edit.js" type="BINDATA" /> + <include name="IDR_CUSTOM_LINKS_EDIT_MENU_SVG" file="icons\edit_menu.svg" type="BINDATA" /> + <include name="IDR_LOCAL_NTP_CONSTANTS_CSS" file="constants.css" type="BINDATA" /> + <include name="IDR_LOCAL_NTP_ANIMATIONS_CSS" file="animations.css" flattenhtml="true" type="BINDATA" /> + <include name="IDR_LOCAL_NTP_ANIMATIONS_JS" file="animations.js" flattenhtml="true" type="BINDATA" /> + <include name="IDR_LOCAL_NTP_CSS" file="local_ntp.css" flattenhtml="true" type="BINDATA" /> + <include name="IDR_LOCAL_NTP_CUSTOM_BACKGROUNDS_CSS" file="custom_backgrounds.css" flattenhtml="true" type="BINDATA" /> + <include name="IDR_LOCAL_NTP_CUSTOM_BACKGROUNDS_JS" file="custom_backgrounds.js" flattenhtml="true" type="BINDATA" /> + <include name="IDR_LOCAL_NTP_DOODLES_CSS" file="doodles.css" flattenhtml="true" type="BINDATA" /> + <include name="IDR_LOCAL_NTP_DOODLES_JS" file="doodles.js" flattenhtml="true" type="BINDATA" /> + <include name="IDR_LOCAL_NTP_HTML" file="local_ntp.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> + <include name="IDR_LOCAL_NTP_JS" file="local_ntp.js" flattenhtml="true" type="BINDATA" /> + <include name="IDR_LOCAL_NTP_UTILS_JS" file="utils.js" flattenhtml="true" type="BINDATA" /> + <include name="IDR_LOCAL_NTP_VOICE_CSS" file="voice.css" flattenhtml="true" type="BINDATA" /> + <include name="IDR_LOCAL_NTP_VOICE_JS" file="voice.js" flattenhtml="true" type="BINDATA" /> + <include name="IDR_MOST_VISITED_IFRAME_CSS" file="most_visited_iframe.css" type="BINDATA" /> + <include name="IDR_MOST_VISITED_SINGLE_CSS" file="most_visited_single.css" type="BINDATA" /> + <include name="IDR_MOST_VISITED_SINGLE_HTML" file="most_visited_single.html" type="BINDATA" /> + <include name="IDR_MOST_VISITED_SINGLE_JS" file="most_visited_single.js" type="BINDATA" /> + <include name="IDR_MOST_VISITED_TITLE_CSS" file="most_visited_title.css" type="BINDATA" /> + <include name="IDR_MOST_VISITED_TITLE_HTML" file="most_visited_title.html" type="BINDATA" /> + <include name="IDR_MOST_VISITED_TITLE_JS" file="most_visited_title.js" type="BINDATA" /> + <include name="IDR_MOST_VISITED_UTIL_JS" file="most_visited_util.js" type="BINDATA" flattenhtml="true" /> + </includes> + </release> +</grit>
diff --git a/chrome/browser/resources/local_ntp_resources.grd b/chrome/browser/resources/local_ntp_resources.grd deleted file mode 100644 index a7828b51..0000000 --- a/chrome/browser/resources/local_ntp_resources.grd +++ /dev/null
@@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> - <outputs> - <output filename="grit/local_ntp_resources.h" type="rc_header"> - <emit emit_type='prepend'></emit> - </output> - <output filename="local_ntp_resources.pak" type="data_package" /> - </outputs> - <release seq="1"> - <includes> - <include name="IDR_INSTANT_IFRAME_VALIDATION_JS" file="local_ntp\instant_iframe_validation.js" type="BINDATA" /> - <include name="IDR_CUSTOM_LINKS_ADD_SVG" file="local_ntp\icons\add_link.svg" type="BINDATA" /> - <include name="IDR_CUSTOM_LINKS_ADD_WHITE_SVG" file="local_ntp\icons\add_link_white.svg" type="BINDATA" /> - <include name="IDR_CUSTOM_LINKS_EDIT_HTML" file="local_ntp\custom_links_edit.html" type="BINDATA" /> - <include name="IDR_CUSTOM_LINKS_EDIT_CSS" file="local_ntp\custom_links_edit.css" type="BINDATA" /> - <include name="IDR_CUSTOM_LINKS_EDIT_JS" file="local_ntp\custom_links_edit.js" type="BINDATA" /> - <include name="IDR_CUSTOM_LINKS_EDIT_MENU_SVG" file="local_ntp\icons\edit_menu.svg" type="BINDATA" /> - <include name="IDR_LOCAL_NTP_CONSTANTS_CSS" file="local_ntp\constants.css" type="BINDATA" /> - <include name="IDR_LOCAL_NTP_ANIMATIONS_CSS" file="local_ntp\animations.css" flattenhtml="true" type="BINDATA" /> - <include name="IDR_LOCAL_NTP_ANIMATIONS_JS" file="local_ntp\animations.js" flattenhtml="true" type="BINDATA" /> - <include name="IDR_LOCAL_NTP_CSS" file="local_ntp\local_ntp.css" flattenhtml="true" type="BINDATA" /> - <include name="IDR_LOCAL_NTP_CUSTOM_BACKGROUNDS_CSS" file="local_ntp\custom_backgrounds.css" flattenhtml="true" type="BINDATA" /> - <include name="IDR_LOCAL_NTP_CUSTOM_BACKGROUNDS_JS" file="local_ntp\custom_backgrounds.js" flattenhtml="true" type="BINDATA" /> - <include name="IDR_LOCAL_NTP_DOODLES_CSS" file="local_ntp\doodles.css" flattenhtml="true" type="BINDATA" /> - <include name="IDR_LOCAL_NTP_DOODLES_JS" file="local_ntp\doodles.js" flattenhtml="true" type="BINDATA" /> - <include name="IDR_LOCAL_NTP_HTML" file="local_ntp\local_ntp.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> - <include name="IDR_LOCAL_NTP_JS" file="local_ntp\local_ntp.js" flattenhtml="true" type="BINDATA" /> - <include name="IDR_LOCAL_NTP_UTILS_JS" file="local_ntp\utils.js" flattenhtml="true" type="BINDATA" /> - <include name="IDR_LOCAL_NTP_VOICE_CSS" file="local_ntp\voice.css" flattenhtml="true" type="BINDATA" /> - <include name="IDR_LOCAL_NTP_VOICE_JS" file="local_ntp\voice.js" flattenhtml="true" type="BINDATA" /> - <include name="IDR_MOST_VISITED_IFRAME_CSS" file="local_ntp\most_visited_iframe.css" type="BINDATA" /> - <include name="IDR_MOST_VISITED_SINGLE_CSS" file="local_ntp\most_visited_single.css" type="BINDATA" /> - <include name="IDR_MOST_VISITED_SINGLE_HTML" file="local_ntp\most_visited_single.html" type="BINDATA" /> - <include name="IDR_MOST_VISITED_SINGLE_JS" file="local_ntp\most_visited_single.js" type="BINDATA" /> - <include name="IDR_MOST_VISITED_TITLE_CSS" file="local_ntp\most_visited_title.css" type="BINDATA" /> - <include name="IDR_MOST_VISITED_TITLE_HTML" file="local_ntp\most_visited_title.html" type="BINDATA" /> - <include name="IDR_MOST_VISITED_TITLE_JS" file="local_ntp\most_visited_title.js" type="BINDATA" /> - <include name="IDR_MOST_VISITED_UTIL_JS" file="local_ntp\most_visited_util.js" type="BINDATA" flattenhtml="true" /> - </includes> - </release> -</grit>
diff --git a/chrome/browser/resources/md_extensions/install_warnings_dialog.html b/chrome/browser/resources/md_extensions/install_warnings_dialog.html index 66289b98..19b213005 100644 --- a/chrome/browser/resources/md_extensions/install_warnings_dialog.html +++ b/chrome/browser/resources/md_extensions/install_warnings_dialog.html
@@ -19,6 +19,12 @@ padding-inline-end: 10px; padding-top: 10px; } + + :host-context([dark]) div[slot='body'] ul { + /* TODO(dbeam): merge with --cr-input-background-color? */ + background-color: rgba(0, 0, 0, .3); + color: var(--error-color); + } </style> <cr-dialog id="dialog" close-text="$i18n{close}"> <div slot="title">$i18n{installWarnings}</div>
diff --git a/chrome/browser/resources/net_internals_resources.grd b/chrome/browser/resources/net_internals/net_internals_resources.grd similarity index 65% rename from chrome/browser/resources/net_internals_resources.grd rename to chrome/browser/resources/net_internals/net_internals_resources.grd index 5a00818..dddecdb 100644 --- a/chrome/browser/resources/net_internals_resources.grd +++ b/chrome/browser/resources/net_internals/net_internals_resources.grd
@@ -10,8 +10,8 @@ </outputs> <release seq="1"> <includes> - <include name="IDR_NET_INTERNALS_INDEX_HTML" file="net_internals/index.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" /> - <include name="IDR_NET_INTERNALS_INDEX_JS" file="net_internals/index.js" flattenhtml="true" compress="gzip" type="BINDATA" /> + <include name="IDR_NET_INTERNALS_INDEX_HTML" file="index.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" /> + <include name="IDR_NET_INTERNALS_INDEX_JS" file="index.js" flattenhtml="true" compress="gzip" type="BINDATA" /> </includes> </release> </grit>
diff --git a/chrome/browser/resources/print_preview/new/other_options_settings.html b/chrome/browser/resources/print_preview/new/other_options_settings.html index 72ef4a6c..1b5d66e2 100644 --- a/chrome/browser/resources/print_preview/new/other_options_settings.html +++ b/chrome/browser/resources/print_preview/new/other_options_settings.html
@@ -6,6 +6,7 @@ <link rel="import" href="print_preview_shared_css.html"> <link rel="import" href="settings_behavior.html"> <link rel="import" href="settings_section.html"> +<link rel="import" href="strings.html"> <dom-module id="print-preview-other-options-settings"> <template>
diff --git a/chrome/browser/resources/quota_internals_resources.grd b/chrome/browser/resources/quota_internals/quota_internals_resources.grd similarity index 64% rename from chrome/browser/resources/quota_internals_resources.grd rename to chrome/browser/resources/quota_internals/quota_internals_resources.grd index f75b4f3..c5b1cca 100644 --- a/chrome/browser/resources/quota_internals_resources.grd +++ b/chrome/browser/resources/quota_internals/quota_internals_resources.grd
@@ -8,9 +8,9 @@ </outputs> <release seq="1"> <includes> - <include name="IDR_QUOTA_INTERNALS_MAIN_HTML" file="quota_internals/main.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip"/> - <include name="IDR_QUOTA_INTERNALS_EVENT_HANDLER_JS" file="quota_internals/event_handler.js" type="BINDATA" compress="gzip"/> - <include name="IDR_QUOTA_INTERNALS_MESSAGE_DISPATCHER_JS" file="quota_internals/message_dispatcher.js" type="BINDATA" compress="gzip"/> + <include name="IDR_QUOTA_INTERNALS_MAIN_HTML" file="main.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip"/> + <include name="IDR_QUOTA_INTERNALS_EVENT_HANDLER_JS" file="event_handler.js" type="BINDATA" compress="gzip"/> + <include name="IDR_QUOTA_INTERNALS_MESSAGE_DISPATCHER_JS" file="message_dispatcher.js" type="BINDATA" compress="gzip"/> </includes> </release> </grit>
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn index 0c79689..b1c488c 100644 --- a/chrome/browser/resources/settings/BUILD.gn +++ b/chrome/browser/resources/settings/BUILD.gn
@@ -106,11 +106,11 @@ deps += [ "android_apps_page:closure_compile", "bluetooth_page:closure_compile", - "contained_shell_page:closure_compile", "crostini_page:closure_compile", "date_time_page:closure_compile", "device_page:closure_compile", "internet_page:closure_compile", + "kiosk_next_shell_page:closure_compile", "multidevice_page:closure_compile", ] }
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html index e1c8cf4..fc4a25d 100644 --- a/chrome/browser/resources/settings/basic_page/basic_page.html +++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -19,9 +19,9 @@ <link rel="import" href="../android_apps_page/android_apps_browser_proxy.html"> <link rel="import" href="../android_apps_page/android_apps_page.html"> <link rel="import" href="../bluetooth_page/bluetooth_page.html"> -<link rel="import" href="../contained_shell_page/contained_shell_page.html"> +<link rel="import" href="../kiosk_next_shell_page/kiosk_next_shell_page.html"> <link rel="import" href="../crostini_page/crostini_page.html"> -<link rel="import" href="../contained_shell_page/contained_shell_page.html"> +<link rel="import" href="../kiosk_next_shell_page/kiosk_next_shell_page.html"> <link rel="import" href="../device_page/device_page.html"> <link rel="import" href="../internet_page/internet_page.html"> <link rel="import" href="../multidevice_page/multidevice_page.html"> @@ -198,12 +198,12 @@ </settings-android-apps-page> </settings-section> </template> - <template is="dom-if" if="[[showContainedShell]]" restamp> + <template is="dom-if" if="[[showKioskNextShell]]" restamp> <settings-section - page-title="$i18n{containedShellPageTitle}" - section="contained-shell"> - <settings-contained-shell-page prefs="{{prefs}}"> - </settings-contained-shell-page> + page-title="$i18n{kioskNextShellPageTitle}" + section="kiosk-next-shell"> + <settings-kiosk-next-shell-page prefs="{{prefs}}"> + </settings-kiosk-next-shell-page> </settings-section> </template> <template is="dom-if" if="[[showCrostini]]" restamp>
diff --git a/chrome/browser/resources/settings/contained_shell_page/contained_shell_confirmation_dialog.js b/chrome/browser/resources/settings/contained_shell_page/contained_shell_confirmation_dialog.js deleted file mode 100644 index 5969f7b7..0000000 --- a/chrome/browser/resources/settings/contained_shell_page/contained_shell_confirmation_dialog.js +++ /dev/null
@@ -1,78 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview - * 'settings-contained-shell-confirmation-dialog' is a dialog shown to confirm - * if a Contained Shell change is really wanted. Since enabling/disabling the - * shell requires a sign out, we need to provide this dialog to avoid surprising - * users. - */ -Polymer({ - is: 'settings-contained-shell-confirmation-dialog', - - behaviors: [I18nBehavior, PrefsBehavior], - - properties: { - /** Preferences state. */ - prefs: { - type: Object, - notify: true, - }, - }, - - /** @override */ - attached: function() { - this.$.dialog.showModal(); - }, - - /** - * @param {!Event} event - * @private - */ - onCancelTap_: function(event) { - this.$.dialog.close(); - event.stopPropagation(); - }, - - /** - * @param {!Event} event - * @private - */ - onConfirmTap_: function(event) { - const prefPath = 'ash.kiosk_next_shell.enabled'; - this.setPrefValue(prefPath, !this.getPref(prefPath).value); - settings.LifetimeBrowserProxyImpl.getInstance().signOutAndRestart(); - event.stopPropagation(); - }, - - /** - * @private - * @return {string} - */ - getTitleText_: function(containedShellEnabled) { - return containedShellEnabled ? - this.i18n('containedShellEnabledDialogTitle') : - this.i18n('containedShellDisabledDialogTitle'); - }, - - /** - * @private - * @return {string} - */ - getBodyText_: function(containedShellEnabled) { - return containedShellEnabled ? - this.i18n('containedShellEnabledDialogBody') : - this.i18n('containedShellDisabledDialogBody'); - }, - - /** - * @private - * @return {string} - */ - getConfirmationText_: function(containedShellEnabled) { - return containedShellEnabled ? this.i18n('containedShellTurnOff') : - this.i18n('containedShellTurnOn'); - }, -});
diff --git a/chrome/browser/resources/settings/contained_shell_page/contained_shell_page.js b/chrome/browser/resources/settings/contained_shell_page/contained_shell_page.js deleted file mode 100644 index 46998c8..0000000 --- a/chrome/browser/resources/settings/contained_shell_page/contained_shell_page.js +++ /dev/null
@@ -1,64 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview - * 'settings-contained-shell-page' is the settings page for enabling the - * Contained Shell. - */ -Polymer({ - is: 'settings-contained-shell-page', - - behaviors: [I18nBehavior, PrefsBehavior], - - properties: { - /** Preferences state. */ - prefs: { - type: Object, - notify: true, - }, - - showConfirmationDialog_: Boolean, - }, - - /** - * @private - * @param {!Event} event - */ - onToggleButtonPressed_: function(event) { - this.showConfirmationDialog_ = true; - event.stopPropagation(); - }, - - /** - * @private - * @param {!Event} event - */ - onConfirmationDialogClose_: function(event) { - this.showConfirmationDialog_ = false; - event.stopPropagation(); - }, - - /** - * @private - * @param {boolean} containedShellEnabled - * @return {string} - */ - getSubtextLabel_: function(containedShellEnabled) { - return containedShellEnabled - ? this.i18n('containedShellPageSubtextDisable') - : this.i18n('containedShellPageSubtextEnable'); - }, - - /** - * @private - * @param {boolean} containedShellEnabled - * @return {string} - */ - getButtonLabel_: function(containedShellEnabled) { - return containedShellEnabled - ? this.i18n('containedShellTurnOff') - : this.i18n('containedShellTurnOn'); - } -});
diff --git a/chrome/browser/resources/settings/controls/settings_slider.html b/chrome/browser/resources/settings/controls/settings_slider.html index 872792b..8b874fc4 100644 --- a/chrome/browser/resources/settings/controls/settings_slider.html +++ b/chrome/browser/resources/settings/controls/settings_slider.html
@@ -3,6 +3,7 @@ <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_behavior.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/cr_elements/cr_slider/cr_slider.html"> +<link rel="import" href="../settings_vars_css.html"> <dom-module id="settings-slider"> <template> @@ -17,10 +18,14 @@ margin-inline-start: var(--settings-controlled-by-spacing); } - #labels[disabled] { + :host-context(html:not([dark])) #labels[disabled] { color: var(--paper-grey-400); } + :host-context([dark]) #labels[disabled] { + opacity: var(--settings-disabled-opacity); + } + div.outer { align-items: stretch; display: flex;
diff --git a/chrome/browser/resources/settings/contained_shell_page/BUILD.gn b/chrome/browser/resources/settings/kiosk_next_shell_page/BUILD.gn similarity index 74% rename from chrome/browser/resources/settings/contained_shell_page/BUILD.gn rename to chrome/browser/resources/settings/kiosk_next_shell_page/BUILD.gn index ecb3ed80..b382f65 100644 --- a/chrome/browser/resources/settings/contained_shell_page/BUILD.gn +++ b/chrome/browser/resources/settings/kiosk_next_shell_page/BUILD.gn
@@ -6,19 +6,19 @@ js_type_check("closure_compile") { deps = [ - ":contained_shell_confirmation_dialog", - ":contained_shell_page", + ":kiosk_next_shell_confirmation_dialog", + ":kiosk_next_shell_page", ] } -js_library("contained_shell_page") { +js_library("kiosk_next_shell_page") { deps = [ "../prefs:prefs_behavior", "//ui/webui/resources/js:i18n_behavior", ] } -js_library("contained_shell_confirmation_dialog") { +js_library("kiosk_next_shell_confirmation_dialog") { deps = [ "..:lifetime_browser_proxy", "../prefs:prefs_behavior",
diff --git a/chrome/browser/resources/settings/contained_shell_page/contained_shell_confirmation_dialog.html b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.html similarity index 90% rename from chrome/browser/resources/settings/contained_shell_page/contained_shell_confirmation_dialog.html rename to chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.html index e1b32ac..9038ff8 100644 --- a/chrome/browser/resources/settings/contained_shell_page/contained_shell_confirmation_dialog.html +++ b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.html
@@ -6,7 +6,7 @@ <link rel="import" href="../prefs/prefs_behavior.html"> <link rel="import" href="../settings_shared_css.html"> -<dom-module id="settings-contained-shell-confirmation-dialog"> +<dom-module id="settings-kiosk-next-shell-confirmation-dialog"> <template> <style include="settings-shared"> </style> @@ -31,5 +31,5 @@ </div> </cr-dialog> </template> - <script src="contained_shell_confirmation_dialog.js"></script> + <script src="kiosk_next_shell_confirmation_dialog.js"></script> </dom-module>
diff --git a/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.js b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.js new file mode 100644 index 0000000..8d60ea4a --- /dev/null +++ b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.js
@@ -0,0 +1,78 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview + * 'settings-kiosk-next-shell-confirmation-dialog' is a dialog shown to confirm + * if a Kiosk Next Shell change is really wanted. Since enabling/disabling the + * shell requires a sign out, we need to provide this dialog to avoid surprising + * users. + */ +Polymer({ + is: 'settings-kiosk-next-shell-confirmation-dialog', + + behaviors: [I18nBehavior, PrefsBehavior], + + properties: { + /** Preferences state. */ + prefs: { + type: Object, + notify: true, + }, + }, + + /** @override */ + attached: function() { + this.$.dialog.showModal(); + }, + + /** + * @param {!Event} event + * @private + */ + onCancelTap_: function(event) { + this.$.dialog.close(); + event.stopPropagation(); + }, + + /** + * @param {!Event} event + * @private + */ + onConfirmTap_: function(event) { + const prefPath = 'ash.kiosk_next_shell.enabled'; + this.setPrefValue(prefPath, !this.getPref(prefPath).value); + settings.LifetimeBrowserProxyImpl.getInstance().signOutAndRestart(); + event.stopPropagation(); + }, + + /** + * @private + * @return {string} + */ + getTitleText_: function(kioskNextShellEnabled) { + return kioskNextShellEnabled ? + this.i18n('kioskNextShellEnabledDialogTitle') : + this.i18n('kioskNextShellDisabledDialogTitle'); + }, + + /** + * @private + * @return {string} + */ + getBodyText_: function(kioskNextShellEnabled) { + return kioskNextShellEnabled ? + this.i18n('kioskNextShellEnabledDialogBody') : + this.i18n('kioskNextShellDisabledDialogBody'); + }, + + /** + * @private + * @return {string} + */ + getConfirmationText_: function(kioskNextShellEnabled) { + return kioskNextShellEnabled ? this.i18n('kioskNextShellTurnOff') : + this.i18n('kioskNextShellTurnOn'); + }, +});
diff --git a/chrome/browser/resources/settings/contained_shell_page/contained_shell_page.html b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.html similarity index 70% rename from chrome/browser/resources/settings/contained_shell_page/contained_shell_page.html rename to chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.html index 7f996c3b..1ee63eb3 100644 --- a/chrome/browser/resources/settings/contained_shell_page/contained_shell_page.html +++ b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.html
@@ -5,20 +5,20 @@ <link rel="import" href="../i18n_setup.html"> <link rel="import" href="../prefs/prefs_behavior.html"> <link rel="import" href="../settings_shared_css.html"> -<link rel="import" href="contained_shell_confirmation_dialog.html"> +<link rel="import" href="kiosk_next_shell_confirmation_dialog.html"> -<dom-module id="settings-contained-shell-page"> +<dom-module id="settings-kiosk-next-shell-page"> <template> <style include="settings-shared"></style> <template is="dom-if" if="[[showConfirmationDialog_]]" restamp> - <settings-contained-shell-confirmation-dialog + <settings-kiosk-next-shell-confirmation-dialog on-close="onConfirmationDialogClose_" prefs="{{prefs}}"> - </settings-contained-shell-confirmation-dialog> + </settings-kiosk-next-shell-confirmation-dialog> </template> - <div id="contained-shell" class="settings-box two-line first"> + <div id="kiosk-next-shell" class="settings-box two-line first"> <div class="start"> - $i18n{containedShellPageLabel} + $i18n{kioskNextShellPageLabel} <div class="secondary" id="secondaryText"> [[getSubtextLabel_(prefs.ash.kiosk_next_shell.enabled.value)]] </div> @@ -26,11 +26,11 @@ <div class="separator"></div> <paper-button id="enable" on-click="onToggleButtonPressed_" - aria-label="$i18n{containedShellPageTitle}" + aria-label="$i18n{kioskNextShellPageTitle}" aria-describedby="secondaryText"> [[getButtonLabel_(prefs.ash.kiosk_next_shell.enabled.value)]] </paper-button> </div> </template> - <script src="contained_shell_page.js"></script> + <script src="kiosk_next_shell_page.js"></script> </dom-module>
diff --git a/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.js b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.js new file mode 100644 index 0000000..4b685acd6 --- /dev/null +++ b/chrome/browser/resources/settings/kiosk_next_shell_page/kiosk_next_shell_page.js
@@ -0,0 +1,64 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview + * 'settings-kiosk-next-shell-page' is the settings page for enabling the + * Kiosk Next Shell. + */ +Polymer({ + is: 'settings-kiosk-next-shell-page', + + behaviors: [I18nBehavior, PrefsBehavior], + + properties: { + /** Preferences state. */ + prefs: { + type: Object, + notify: true, + }, + + showConfirmationDialog_: Boolean, + }, + + /** + * @private + * @param {!Event} event + */ + onToggleButtonPressed_: function(event) { + this.showConfirmationDialog_ = true; + event.stopPropagation(); + }, + + /** + * @private + * @param {!Event} event + */ + onConfirmationDialogClose_: function(event) { + this.showConfirmationDialog_ = false; + event.stopPropagation(); + }, + + /** + * @private + * @param {boolean} kioskNextShellEnabled + * @return {string} + */ + getSubtextLabel_: function(kioskNextShellEnabled) { + return kioskNextShellEnabled + ? this.i18n('kioskNextShellPageSubtextDisable') + : this.i18n('kioskNextShellPageSubtextEnable'); + }, + + /** + * @private + * @param {boolean} kioskNextShellEnabled + * @return {string} + */ + getButtonLabel_: function(kioskNextShellEnabled) { + return kioskNextShellEnabled + ? this.i18n('kioskNextShellTurnOff') + : this.i18n('kioskNextShellTurnOn'); + } +});
diff --git a/chrome/browser/resources/settings/settings_main/settings_main.html b/chrome/browser/resources/settings/settings_main/settings_main.html index 2c0066a..1e1fc08b 100644 --- a/chrome/browser/resources/settings/settings_main/settings_main.html +++ b/chrome/browser/resources/settings/settings_main/settings_main.html
@@ -64,7 +64,7 @@ <settings-basic-page prefs="{{prefs}}" page-visibility="[[pageVisibility]]" show-android-apps="[[showAndroidApps]]" - show-contained-shell="[[showContainedShell]]" + show-kiosk-next-shell="[[showKioskNextShell]]" show-crostini="[[showCrostini]]" have-play-store-app="[[havePlayStoreApp]]" on-showing-section="onShowingSection_"
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd index 97edaa8..dbff3ba 100644 --- a/chrome/browser/resources/settings/settings_resources.grd +++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -1259,17 +1259,17 @@ type="chrome_html" preprocess="true" allowexternalscript="true" /> - <structure name="IDR_SETTINGS_CONTAINED_SHELL_PAGE_HTML" - file="contained_shell_page/contained_shell_page.html" + <structure name="IDR_SETTINGS_KIOSK_NEXT_SHELL_PAGE_HTML" + file="kiosk_next_shell_page/kiosk_next_shell_page.html" type="chrome_html" /> - <structure name="IDR_SETTINGS_CONTAINED_SHELL_PAGE_JS" - file="contained_shell_page/contained_shell_page.js" + <structure name="IDR_SETTINGS_KIOSK_NEXT_SHELL_PAGE_JS" + file="kiosk_next_shell_page/kiosk_next_shell_page.js" type="chrome_html" /> - <structure name="IDR_SETTINGS_CONTAINED_SHELL_CONFIRMATION_DIALOG_HTML" - file="contained_shell_page/contained_shell_confirmation_dialog.html" + <structure name="IDR_SETTINGS_KIOSK_NEXT_SHELL_CONFIRMATION_DIALOG_HTML" + file="kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.html" type="chrome_html" /> - <structure name="IDR_SETTINGS_CONTAINED_SHELL_CONFIRMATION_DIALOG_JS" - file="contained_shell_page/contained_shell_confirmation_dialog.js" + <structure name="IDR_SETTINGS_KIOSK_NEXT_SHELL_CONFIRMATION_DIALOG_JS" + file="kiosk_next_shell_page/kiosk_next_shell_confirmation_dialog.js" type="chrome_html" /> <structure name="IDR_SETTINGS_BLUETOOTH_DEVICE_LIST_ITEM_HTML" file="bluetooth_page/bluetooth_device_list_item.html"
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html index 00f4b74..519cbc5 100644 --- a/chrome/browser/resources/settings/settings_ui/settings_ui.html +++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -84,7 +84,7 @@ toolbar-spinner-active="{{toolbarSpinnerActive_}}" page-visibility="[[pageVisibility_]]" show-android-apps="[[showAndroidApps_]]" - show-contained-shell="[[showContainedShell_]]" + show-kiosk-next-shell="[[showKioskNextShell_]]" show-crostini="[[showCrostini_]]" show-multidevice="[[showMultidevice_]]" have-play-store-app="[[havePlayStoreApp_]]"
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js index 2f716c5f..9b8e869 100644 --- a/chrome/browser/resources/settings/settings_ui/settings_ui.js +++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -54,7 +54,7 @@ showAndroidApps_: Boolean, /** @private */ - showContainedShell_: Boolean, + showKioskNextShell_: Boolean, /** @private */ showCrostini_: Boolean, @@ -139,8 +139,8 @@ this.showAndroidApps_ = loadTimeData.valueExists('androidAppsVisible') && loadTimeData.getBoolean('androidAppsVisible'); - this.showContainedShell_ = loadTimeData.valueExists('showContainedShell') && - loadTimeData.getBoolean('showContainedShell'); + this.showKioskNextShell_ = loadTimeData.valueExists('showKioskNextShell') && + loadTimeData.getBoolean('showKioskNextShell'); this.showCrostini_ = loadTimeData.valueExists('showCrostini') && loadTimeData.getBoolean('showCrostini'); this.havePlayStoreApp_ = loadTimeData.valueExists('havePlayStoreApp') &&
diff --git a/chrome/browser/resources/sync_file_system_internals/OWNERS b/chrome/browser/resources/sync_file_system_internals/OWNERS index 8fb67bb4..ae057bc 100644 --- a/chrome/browser/resources/sync_file_system_internals/OWNERS +++ b/chrome/browser/resources/sync_file_system_internals/OWNERS
@@ -4,5 +4,7 @@ pwnall@chomium.org tzik@chromium.org +per-file sync_file_system_internals_resources.*=tzik@chromium.org + # TEAM: storage-dev@chromium.org # COMPONENT: Blink>Storage>FileSystem
diff --git a/chrome/browser/resources/sync_file_system_internals_resources.grd b/chrome/browser/resources/sync_file_system_internals/sync_file_system_internals_resources.grd similarity index 62% rename from chrome/browser/resources/sync_file_system_internals_resources.grd rename to chrome/browser/resources/sync_file_system_internals/sync_file_system_internals_resources.grd index 6c730ce..d1c4ab6 100644 --- a/chrome/browser/resources/sync_file_system_internals_resources.grd +++ b/chrome/browser/resources/sync_file_system_internals/sync_file_system_internals_resources.grd
@@ -8,13 +8,13 @@ </outputs> <release seq="1"> <includes> - <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_UTILS_JS" file="sync_file_system_internals/utils.js" type="BINDATA" /> - <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_EXTENSION_STATUSES_JS" file="sync_file_system_internals/extension_statuses.js" type="BINDATA" /> - <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_FILE_METADATA_JS" file="sync_file_system_internals/file_metadata.js" type="BINDATA" /> - <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_DUMP_DATABASE_JS" file="sync_file_system_internals/dump_database.js" type="BINDATA" /> - <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_MAIN_HTML" file="sync_file_system_internals/main.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> - <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_SYNC_SERVICE_JS" file="sync_file_system_internals/sync_service.js" type="BINDATA" /> - <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_TASK_LOG_JS" file="sync_file_system_internals/task_log.js" type="BINDATA" /> + <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_UTILS_JS" file="utils.js" type="BINDATA" /> + <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_EXTENSION_STATUSES_JS" file="extension_statuses.js" type="BINDATA" /> + <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_FILE_METADATA_JS" file="file_metadata.js" type="BINDATA" /> + <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_DUMP_DATABASE_JS" file="dump_database.js" type="BINDATA" /> + <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_MAIN_HTML" file="main.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> + <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_SYNC_SERVICE_JS" file="sync_service.js" type="BINDATA" /> + <include name="IDR_SYNC_FILE_SYSTEM_INTERNALS_TASK_LOG_JS" file="task_log.js" type="BINDATA" /> </includes> </release> </grit>
diff --git a/chrome/browser/resources/translate_internals_resources.grd b/chrome/browser/resources/translate_internals/translate_internals_resources.grd similarity index 71% rename from chrome/browser/resources/translate_internals_resources.grd rename to chrome/browser/resources/translate_internals/translate_internals_resources.grd index 02cc177..3026b89 100644 --- a/chrome/browser/resources/translate_internals_resources.grd +++ b/chrome/browser/resources/translate_internals/translate_internals_resources.grd
@@ -8,8 +8,8 @@ </outputs> <release seq="1"> <includes> - <include name="IDR_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_HTML" file="translate_internals/translate_internals.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" /> - <include name="IDR_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_JS" file="translate_internals/translate_internals.js" compress="gzip" type="BINDATA" /> + <include name="IDR_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_HTML" file="translate_internals.html" flattenhtml="true" allowexternalscript="true" compress="gzip" type="BINDATA" /> + <include name="IDR_TRANSLATE_INTERNALS_TRANSLATE_INTERNALS_JS" file="translate_internals.js" compress="gzip" type="BINDATA" /> </includes> </release> </grit>
diff --git a/chrome/browser/resources/webapks/webapks_ui_resources.grd b/chrome/browser/resources/webapks/webapks_ui_resources.grd new file mode 100644 index 0000000..a744953f --- /dev/null +++ b/chrome/browser/resources/webapks/webapks_ui_resources.grd
@@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> + <outputs> + <output filename="grit/webapks_ui_resources.h" type="rc_header"> + <emit emit_type='prepend'></emit> + </output> + <output filename="webapks_ui_resources.pak" type="data_package" /> + </outputs> + <release seq="1"> + <includes> + <include name="IDR_WEBAPKS_UI_CSS" file="about_webapks.css" type="BINDATA" compress="gzip" /> + <include name="IDR_WEBAPKS_UI_HTML" file="about_webapks.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> + <include name="IDR_WEBAPKS_UI_JS" file="about_webapks.js" type="BINDATA" compress="gzip" /> + </includes> + </release> +</grit>
diff --git a/chrome/browser/resources/webapks_ui_resources.grd b/chrome/browser/resources/webapks_ui_resources.grd deleted file mode 100644 index 063053a..0000000 --- a/chrome/browser/resources/webapks_ui_resources.grd +++ /dev/null
@@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> - <outputs> - <output filename="grit/webapks_ui_resources.h" type="rc_header"> - <emit emit_type='prepend'></emit> - </output> - <output filename="webapks_ui_resources.pak" type="data_package" /> - </outputs> - <release seq="1"> - <includes> - <include name="IDR_WEBAPKS_UI_CSS" file="webapks/about_webapks.css" type="BINDATA" compress="gzip" /> - <include name="IDR_WEBAPKS_UI_HTML" file="webapks/about_webapks.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> - <include name="IDR_WEBAPKS_UI_JS" file="webapks/about_webapks.js" type="BINDATA" compress="gzip" /> - </includes> - </release> -</grit>
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc index 87318f2..f9c3dd30 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -1187,10 +1187,10 @@ app_updaters_.push_back(std::move(crostini_app_updater)); } - app_list::AppListSyncableService* app_service = + app_list::AppListSyncableService* app_list_syncable_service = app_list::AppListSyncableServiceFactory::GetForProfile(profile()); - if (app_service) - app_service->AddObserverAndStart(this); + if (app_list_syncable_service) + app_list_syncable_service->AddObserverAndStart(this); PrefServiceSyncableFromProfile(profile())->AddObserver(this); } @@ -1203,10 +1203,10 @@ pref_change_registrar_.RemoveAll(); - app_list::AppListSyncableService* app_service = + app_list::AppListSyncableService* app_list_syncable_service = app_list::AppListSyncableServiceFactory::GetForProfile(profile()); - if (app_service) - app_service->RemoveObserver(this); + if (app_list_syncable_service) + app_list_syncable_service->RemoveObserver(this); PrefServiceSyncableFromProfile(profile())->RemoveObserver(this); }
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc index 7134bad..535cdfb 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -496,9 +496,10 @@ extension_system->ready().Post(FROM_HERE, run_loop.QuitClosure()); run_loop.Run(); - app_service_ = + app_list_syncable_service_ = app_list::AppListSyncableServiceFactory::GetForProfile(profile()); - StartAppSyncService(app_service_->GetAllSyncData(syncer::APP_LIST)); + StartAppSyncService( + app_list_syncable_service_->GetAllSyncData(syncer::APP_LIST)); std::string error; extension_chrome_ = Extension::Create(base::FilePath(), Manifest::UNPACKED, @@ -672,14 +673,17 @@ } void StartAppSyncService(const syncer::SyncDataList& init_sync_list) { - app_service_->MergeDataAndStartSyncing( + app_list_syncable_service_->MergeDataAndStartSyncing( syncer::APP_LIST, init_sync_list, std::make_unique<syncer::FakeSyncChangeProcessor>(), std::make_unique<syncer::SyncErrorFactoryMock>()); - EXPECT_EQ(init_sync_list.size(), app_service_->sync_items().size()); + EXPECT_EQ(init_sync_list.size(), + app_list_syncable_service_->sync_items().size()); } - void StopAppSyncService() { app_service_->StopSyncing(syncer::APP_LIST); } + void StopAppSyncService() { + app_list_syncable_service_->StopSyncing(syncer::APP_LIST); + } sync_preferences::PrefModelAssociator* GetPrefSyncService() { sync_preferences::PrefServiceSyncable* pref_sync = @@ -725,7 +729,7 @@ } void InsertRemoveAllPinsChange(syncer::SyncChangeList* list) { - for (const auto& sync_peer : app_service_->sync_items()) { + for (const auto& sync_peer : app_list_syncable_service_->sync_items()) { sync_pb::EntitySpecifics specifics; sync_pb::AppListSpecifics* app_list_specifics = specifics.mutable_app_list(); @@ -797,26 +801,28 @@ syncer::SyncChangeList sync_list; InsertRemoveAllPinsChange(&sync_list); InsertAddPinChange(&sync_list, 0, kDummyAppId); - app_service_->ProcessSyncChanges(FROM_HERE, sync_list); + app_list_syncable_service_->ProcessSyncChanges(FROM_HERE, sync_list); } void SendPinChanges(const syncer::SyncChangeList& sync_list, bool reset_pin_model) { if (!reset_pin_model) { - app_service_->ProcessSyncChanges(FROM_HERE, sync_list); + app_list_syncable_service_->ProcessSyncChanges(FROM_HERE, sync_list); } else { syncer::SyncChangeList combined_sync_list; InsertRemoveAllPinsChange(&combined_sync_list); combined_sync_list.insert(combined_sync_list.end(), sync_list.begin(), sync_list.end()); - app_service_->ProcessSyncChanges(FROM_HERE, combined_sync_list); + app_list_syncable_service_->ProcessSyncChanges(FROM_HERE, + combined_sync_list); } } // Set the index at which the chrome icon should be. void SetShelfChromeIconIndex(int index) { DCHECK( - app_service_->GetPinPosition(extension_misc::kChromeAppId).IsValid()); + app_list_syncable_service_->GetPinPosition(extension_misc::kChromeAppId) + .IsValid()); syncer::StringOrdinal chrome_position; chrome_position = index == 0 ? GeneratePinPosition(0).CreateBefore() : GeneratePinPosition(index - 1).CreateBetween( @@ -833,7 +839,7 @@ extension_misc::kChromeAppId, "Test", specifics); sync_list.push_back(syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data)); - app_service_->ProcessSyncChanges(FROM_HERE, sync_list); + app_list_syncable_service_->ProcessSyncChanges(FROM_HERE, sync_list); } // Gets the IDs of the currently pinned app items. @@ -1087,7 +1093,7 @@ extensions::ExtensionService* extension_service_ = nullptr; - app_list::AppListSyncableService* app_service_ = nullptr; + app_list::AppListSyncableService* app_list_syncable_service_ = nullptr; private: TestBrowserWindow* CreateTestBrowserWindowAura() { @@ -1424,12 +1430,12 @@ // Persist pin state, we don't have active pin for ARC apps yet, but pin // model should have it. syncer::SyncDataList copy_sync_list = - app_service_->GetAllSyncData(syncer::APP_LIST); + app_list_syncable_service_->GetAllSyncData(syncer::APP_LIST); ResetLauncherController(); SendPinChanges(syncer::SyncChangeList(), true); StopAppSyncService(); - EXPECT_EQ(0U, app_service_->sync_items().size()); + EXPECT_EQ(0U, app_list_syncable_service_->sync_items().size()); // Move to ARC enabled platform, restart syncing with stored data. StartAppSyncService(copy_sync_list); @@ -1455,14 +1461,14 @@ EXPECT_EQ("Back, AppList, App2, Fake App 1, Chrome, App1, Fake App 0, Gmail", GetPinnedAppStatus()); - copy_sync_list = app_service_->GetAllSyncData(syncer::APP_LIST); + copy_sync_list = app_list_syncable_service_->GetAllSyncData(syncer::APP_LIST); ResetLauncherController(); ResetPinModel(); SendPinChanges(syncer::SyncChangeList(), true); StopAppSyncService(); - EXPECT_EQ(0U, app_service_->sync_items().size()); + EXPECT_EQ(0U, app_list_syncable_service_->sync_items().size()); // Move back to ARC disabled platform. EnablePlayStore(false); @@ -4261,13 +4267,13 @@ EXPECT_EQ("Back, AppList, Chrome, App1, App2, Gmail", GetPinnedAppStatus()); const syncer::StringOrdinal position_chrome = - app_service_->GetPinPosition(extension_misc::kChromeAppId); + app_list_syncable_service_->GetPinPosition(extension_misc::kChromeAppId); const syncer::StringOrdinal position_1 = - app_service_->GetPinPosition(extension1_->id()); + app_list_syncable_service_->GetPinPosition(extension1_->id()); const syncer::StringOrdinal position_2 = - app_service_->GetPinPosition(extension2_->id()); + app_list_syncable_service_->GetPinPosition(extension2_->id()); const syncer::StringOrdinal position_3 = - app_service_->GetPinPosition(extensionGmailApp_->id()); + app_list_syncable_service_->GetPinPosition(extensionGmailApp_->id()); EXPECT_TRUE(position_chrome.LessThan(position_1)); EXPECT_TRUE(position_1.Equals(position_2)); EXPECT_TRUE(position_2.Equals(position_3)); @@ -4280,16 +4286,16 @@ // Expect sync positions for only Chrome is updated and its resolution is // after all duplicated ordinals. - EXPECT_TRUE(position_3.LessThan( - app_service_->GetPinPosition(extension_misc::kChromeAppId))); - EXPECT_TRUE( - position_1.Equals(app_service_->GetPinPosition(extension1_->id()))); - EXPECT_TRUE( - position_1.Equals(app_service_->GetPinPosition(extension1_->id()))); - EXPECT_TRUE( - position_2.Equals(app_service_->GetPinPosition(extension2_->id()))); + EXPECT_TRUE(position_3.LessThan(app_list_syncable_service_->GetPinPosition( + extension_misc::kChromeAppId))); + EXPECT_TRUE(position_1.Equals( + app_list_syncable_service_->GetPinPosition(extension1_->id()))); + EXPECT_TRUE(position_1.Equals( + app_list_syncable_service_->GetPinPosition(extension1_->id()))); + EXPECT_TRUE(position_2.Equals( + app_list_syncable_service_->GetPinPosition(extension2_->id()))); EXPECT_TRUE(position_3.Equals( - app_service_->GetPinPosition(extensionGmailApp_->id()))); + app_list_syncable_service_->GetPinPosition(extensionGmailApp_->id()))); } // Test the case when sync app is turned off and we need to use local copy to @@ -4309,9 +4315,9 @@ EXPECT_EQ("Back, AppList, Chrome, App1, App2", GetPinnedAppStatus()); syncer::SyncDataList copy_sync_list = - app_service_->GetAllSyncData(syncer::APP_LIST); + app_list_syncable_service_->GetAllSyncData(syncer::APP_LIST); - app_service_->StopSyncing(syncer::APP_LIST); + app_list_syncable_service_->StopSyncing(syncer::APP_LIST); RecreateLauncherController()->Init(); // Pinned state should not change.
diff --git a/chrome/browser/ui/toolbar/chrome_location_bar_model_delegate.cc b/chrome/browser/ui/toolbar/chrome_location_bar_model_delegate.cc index e6b0ed0..36faa4d1 100644 --- a/chrome/browser/ui/toolbar/chrome_location_bar_model_delegate.cc +++ b/chrome/browser/ui/toolbar/chrome_location_bar_model_delegate.cc
@@ -39,7 +39,7 @@ #include "extensions/browser/extension_registry.h" // Id for extension that enables users to report sites to Safe Browsing. -const char kPreventElisionExtensionId[] = "ekpgepffboojnckiahkpangdldnjafnj"; +const char kPreventElisionExtensionId[] = "jknemblkbdhdcpllfgbfekkdciegfboi"; #endif // BUILDFLAG(ENABLE_EXTENSIONS) ChromeLocationBarModelDelegate::ChromeLocationBarModelDelegate() {}
diff --git a/chrome/browser/ui/webui/conflicts/conflicts_handler.cc b/chrome/browser/ui/webui/conflicts/conflicts_handler.cc index c2e6807..f0ed130 100644 --- a/chrome/browser/ui/webui/conflicts/conflicts_handler.cc +++ b/chrome/browser/ui/webui/conflicts/conflicts_handler.cc
@@ -38,6 +38,8 @@ constexpr char kAllowedMatchingCertificate[] = "Allowed - Matching certificate"; constexpr char kAllowedMicrosoftModule[] = "Allowed - Microsoft module"; constexpr char kAllowedWhitelisted[] = "Allowed - Whitelisted"; +constexpr char kNotAnalyzed[] = + "Tolerated - Not analyzed (See https://crbug.com/892294)"; constexpr char kAllowedSameDirectory[] = #if defined(OFFICIAL_BUILD) // In official builds, modules in the Chrome directory are blocked but they @@ -100,6 +102,8 @@ return kAllowedMicrosoftModule; case BlockingDecision::kAllowedWhitelisted: return kAllowedWhitelisted; + case BlockingDecision::kNotAnalyzed: + return kNotAnalyzed; case BlockingDecision::kTolerated: // This is a module explicitly allowed to load by the Module List // component. But it is still valid for a potential warning, and so the @@ -143,6 +147,8 @@ return kAllowedMicrosoftModule; case WarningDecision::kAllowedWhitelisted: return kAllowedWhitelisted; + case WarningDecision::kNotAnalyzed: + return kNotAnalyzed; case WarningDecision::kNoTiedApplication: return "Tolerated - Could not tie to an installed application"; case WarningDecision::kIncompatible:
diff --git a/chrome/browser/ui/webui/feed_internals/feed_internals.mojom b/chrome/browser/ui/webui/feed_internals/feed_internals.mojom index d7bb4e7b..35f6b28 100644 --- a/chrome/browser/ui/webui/feed_internals/feed_internals.mojom +++ b/chrome/browser/ui/webui/feed_internals/feed_internals.mojom
@@ -23,6 +23,23 @@ float avg_hours_between_uses; }; +struct LastFetchProperties { + // Last fetch status. + int32 last_fetch_status; + + // Last fetch time. + Time? last_fetch_time; + + // Time until which the scheduler will stop requesting refreshes, unless there + // is direct user interaction. + Time? refresh_suppress_time; +}; + +// Time wrapper to allow for nullable objects. +struct Time { + double ms_since_epoch; +}; + // Browser interface for the page. Consists of calls for data and hooks for // interactivity. interface PageHandler { @@ -34,4 +51,11 @@ // Clear stored properties for the user classifier. ClearUserClassifierProperties(); + + // Get last fetch data. + GetLastFetchProperties() => (LastFetchProperties properties); + + // Clear all data cached by the Feed library. Also triggers a refresh of the + // Feed. + ClearCachedDataAndRefreshFeed(); };
diff --git a/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.cc b/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.cc index 7770d18..f8075e5 100644 --- a/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.cc +++ b/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.cc
@@ -7,16 +7,31 @@ #include <utility> #include "base/feature_list.h" +#include "base/time/time.h" +#include "chrome/browser/android/feed/feed_lifecycle_bridge.h" #include "components/feed/content/feed_host_service.h" #include "components/feed/core/feed_scheduler_host.h" +#include "components/feed/core/pref_names.h" #include "components/feed/core/user_classifier.h" #include "components/feed/feed_feature_list.h" +#include "components/prefs/pref_service.h" + +namespace { + +feed_internals::mojom::TimePtr ToMojoTime(base::Time time) { + return time.is_null() ? nullptr + : feed_internals::mojom::Time::New(time.ToJsTime()); +} + +} // namespace FeedInternalsPageHandler::FeedInternalsPageHandler( feed_internals::mojom::PageHandlerRequest request, - feed::FeedHostService* feed_host_service) + feed::FeedHostService* feed_host_service, + PrefService* pref_service) : binding_(this, std::move(request)), - feed_scheduler_host_(feed_host_service->GetSchedulerHost()) {} + feed_scheduler_host_(feed_host_service->GetSchedulerHost()), + pref_service_(pref_service) {} FeedInternalsPageHandler::~FeedInternalsPageHandler() = default; @@ -35,7 +50,7 @@ auto properties = feed_internals::mojom::UserClassifier::New(); feed::UserClassifier* user_classifier = - feed_scheduler_host_->user_classifier(); + feed_scheduler_host_->GetUserClassifierForDebugging(); properties->user_class_description = user_classifier->GetUserClassDescriptionForDebugging(); @@ -47,6 +62,25 @@ std::move(callback).Run(std::move(properties)); } +void FeedInternalsPageHandler::GetLastFetchProperties( + GetLastFetchPropertiesCallback callback) { + auto properties = feed_internals::mojom::LastFetchProperties::New(); + + properties->last_fetch_status = + feed_scheduler_host_->GetLastFetchStatusForDebugging(); + properties->last_fetch_time = + ToMojoTime(pref_service_->GetTime(feed::prefs::kLastFetchAttemptTime)); + properties->refresh_suppress_time = + ToMojoTime(feed_scheduler_host_->GetSuppressRefreshesUntilForDebugging()); + + std::move(callback).Run(std::move(properties)); +} + void FeedInternalsPageHandler::ClearUserClassifierProperties() { - feed_scheduler_host_->user_classifier()->ClearClassificationForDebugging(); + feed_scheduler_host_->GetUserClassifierForDebugging() + ->ClearClassificationForDebugging(); +} + +void FeedInternalsPageHandler::ClearCachedDataAndRefreshFeed() { + feed::FeedLifecycleBridge::ClearCachedData(); }
diff --git a/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.h b/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.h index e51220cd..e422b8f 100644 --- a/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.h +++ b/chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.h
@@ -9,6 +9,8 @@ #include "chrome/browser/ui/webui/feed_internals/feed_internals.mojom.h" #include "mojo/public/cpp/bindings/binding.h" +class PrefService; + namespace feed { class FeedHostService; class FeedSchedulerHost; @@ -18,7 +20,8 @@ class FeedInternalsPageHandler : public feed_internals::mojom::PageHandler { public: FeedInternalsPageHandler(feed_internals::mojom::PageHandlerRequest request, - feed::FeedHostService* feed_host_service); + feed::FeedHostService* feed_host_service, + PrefService* pref_service); ~FeedInternalsPageHandler() override; // feed_internals::mojom::PageHandler @@ -26,6 +29,8 @@ void GetUserClassifierProperties( GetUserClassifierPropertiesCallback) override; void ClearUserClassifierProperties() override; + void GetLastFetchProperties(GetLastFetchPropertiesCallback) override; + void ClearCachedDataAndRefreshFeed() override; private: // Binding from the mojo interface to concrete implementation. @@ -33,6 +38,7 @@ // Services that provide the data and functionality. feed::FeedSchedulerHost* feed_scheduler_host_; + PrefService* pref_service_; DISALLOW_COPY_AND_ASSIGN(FeedInternalsPageHandler); };
diff --git a/chrome/browser/ui/webui/feed_internals/feed_internals_ui.cc b/chrome/browser/ui/webui/feed_internals/feed_internals_ui.cc index 2ebf8e4..32658cd 100644 --- a/chrome/browser/ui/webui/feed_internals/feed_internals_ui.cc +++ b/chrome/browser/ui/webui/feed_internals/feed_internals_ui.cc
@@ -15,7 +15,7 @@ #include "content/public/browser/web_ui_data_source.h" FeedInternalsUI::FeedInternalsUI(content::WebUI* web_ui) - : ui::MojoWebUIController(web_ui) { + : ui::MojoWebUIController(web_ui), profile_(Profile::FromWebUI(web_ui)) { content::WebUIDataSource* source = content::WebUIDataSource::Create(chrome::kChromeUISnippetsInternalsHost); @@ -26,11 +26,7 @@ source->SetDefaultResource(IDR_FEED_INTERNALS_HTML); source->UseGzip(); - Profile* profile = Profile::FromWebUI(web_ui); - feed_host_service_ = - feed::FeedHostServiceFactory::GetForBrowserContext(profile); - - content::WebUIDataSource::Add(profile, source); + content::WebUIDataSource::Add(profile_, source); // This class is the caller of the callback when an observer interface is // triggered. So this base::Unretained is safe. AddHandlerToRegistry(base::BindRepeating( @@ -42,5 +38,7 @@ void FeedInternalsUI::BindFeedInternalsPageHandler( feed_internals::mojom::PageHandlerRequest request) { page_handler_ = std::make_unique<FeedInternalsPageHandler>( - std::move(request), feed_host_service_); + std::move(request), + feed::FeedHostServiceFactory::GetForBrowserContext(profile_), + profile_->GetPrefs()); }
diff --git a/chrome/browser/ui/webui/feed_internals/feed_internals_ui.h b/chrome/browser/ui/webui/feed_internals/feed_internals_ui.h index 23282c95..8d6077f 100644 --- a/chrome/browser/ui/webui/feed_internals/feed_internals_ui.h +++ b/chrome/browser/ui/webui/feed_internals/feed_internals_ui.h
@@ -8,10 +8,11 @@ #include <memory> #include "base/macros.h" -#include "chrome/browser/ui/webui/feed_internals/feed_internals.mojom.h" #include "chrome/browser/ui/webui/feed_internals/feed_internals_page_handler.h" #include "ui/webui/mojo_web_ui_controller.h" +class Profile; + // During the interim migration to Feed, this page will be co-located with // snippets-internals. Once migration is complete, and snippets-internals is // removed, this page will be moved to chrome://feed-internals. @@ -28,7 +29,7 @@ void BindFeedInternalsPageHandler( feed_internals::mojom::PageHandlerRequest request); - feed::FeedHostService* feed_host_service_; + Profile* profile_; std::unique_ptr<FeedInternalsPageHandler> page_handler_;
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc index 4e2f605..6bee934 100644 --- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -492,24 +492,24 @@ base::FeatureList::IsEnabled(chromeos::features::kCrostiniUsbSupport)); } -void AddContainedShellStrings(content::WebUIDataSource* html_source) { +void AddKioskNextShellStrings(content::WebUIDataSource* html_source) { static constexpr LocalizedString kLocalizedStrings[] = { - {"containedShellPageTitle", IDS_SETTINGS_CONTAINED_SHELL_TITLE}, - {"containedShellPageLabel", IDS_SETTINGS_CONTAINED_SHELL_LABEL}, - {"containedShellPageSubtextEnable", - IDS_SETTINGS_CONTAINED_SHELL_SUBTEXT_ENABLE}, - {"containedShellPageSubtextDisable", - IDS_SETTINGS_CONTAINED_SHELL_SUBTEXT_DISABLE}, - {"containedShellTurnOn", IDS_SETTINGS_TURN_ON}, - {"containedShellTurnOff", IDS_SETTINGS_CONTAINED_SHELL_TURN_OFF}, - {"containedShellEnabledDialogTitle", - IDS_SETTINGS_CONTAINED_SHELL_ENABLED_DIALOG_TITLE}, - {"containedShellDisabledDialogTitle", - IDS_SETTINGS_CONTAINED_SHELL_DISABLED_DIALOG_TITLE}, - {"containedShellEnabledDialogBody", - IDS_SETTINGS_CONTAINED_SHELL_ENABLED_DIALOG_BODY}, - {"containedShellDisabledDialogBody", - IDS_SETTINGS_CONTAINED_SHELL_DISABLED_DIALOG_BODY}, + {"kioskNextShellPageTitle", IDS_SETTINGS_KIOSK_NEXT_SHELL_TITLE}, + {"kioskNextShellPageLabel", IDS_SETTINGS_KIOSK_NEXT_SHELL_LABEL}, + {"kioskNextShellPageSubtextEnable", + IDS_SETTINGS_KIOSK_NEXT_SHELL_SUBTEXT_ENABLE}, + {"kioskNextShellPageSubtextDisable", + IDS_SETTINGS_KIOSK_NEXT_SHELL_SUBTEXT_DISABLE}, + {"kioskNextShellTurnOn", IDS_SETTINGS_TURN_ON}, + {"kioskNextShellTurnOff", IDS_SETTINGS_KIOSK_NEXT_SHELL_TURN_OFF}, + {"kioskNextShellEnabledDialogTitle", + IDS_SETTINGS_KIOSK_NEXT_SHELL_ENABLED_DIALOG_TITLE}, + {"kioskNextShellDisabledDialogTitle", + IDS_SETTINGS_KIOSK_NEXT_SHELL_DISABLED_DIALOG_TITLE}, + {"kioskNextShellEnabledDialogBody", + IDS_SETTINGS_KIOSK_NEXT_SHELL_ENABLED_DIALOG_BODY}, + {"kioskNextShellDisabledDialogBody", + IDS_SETTINGS_KIOSK_NEXT_SHELL_DISABLED_DIALOG_BODY}, }; AddLocalizedStringsBulk(html_source, kLocalizedStrings, base::size(kLocalizedStrings)); @@ -2860,7 +2860,7 @@ #if defined(OS_CHROMEOS) AddCrostiniStrings(html_source, profile); - AddContainedShellStrings(html_source); + AddKioskNextShellStrings(html_source); AddAndroidAppStrings(html_source); AddBluetoothStrings(html_source); AddChromeOSUserStrings(html_source, profile);
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc index b6efd86..99f3ab4 100644 --- a/chrome/browser/ui/webui/settings/md_settings_ui.cc +++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -325,9 +325,8 @@ html_source->AddBoolean("hasInternalStylus", ash::stylus_utils::HasInternalStylus()); - // TODO(ltenorio): Rename showContainedShell to showKioskNextShell. html_source->AddBoolean( - "showContainedShell", + "showKioskNextShell", base::FeatureList::IsEnabled(ash::features::kKioskNextShell)); html_source->AddBoolean("showCrostini",
diff --git a/chrome/browser/vr/input_delegate_for_testing.cc b/chrome/browser/vr/input_delegate_for_testing.cc index 79463420..4e60f4a 100644 --- a/chrome/browser/vr/input_delegate_for_testing.cc +++ b/chrome/browser/vr/input_delegate_for_testing.cc
@@ -59,13 +59,19 @@ ControllerModel controller_model; if (controller_input.element_name == UserFriendlyElementName::kNone) { controller_model.laser_direction = kForwardVector; + SetOriginAndTransform(&controller_model); + } else if (controller_input.element_name == + UserFriendlyElementName::kCurrentPosition) { + controller_model.transform = GetMostRecentModel().transform; + controller_model.laser_direction = GetMostRecentModel().laser_direction; + controller_model.laser_origin = GetMostRecentModel().laser_origin; } else { auto target_point = ui_->GetTargetPointForTesting( controller_input.element_name, controller_input.position); auto direction = (target_point - kStartControllerPosition) - kOrigin; direction.GetNormalized(&controller_model.laser_direction); + SetOriginAndTransform(&controller_model); } - SetOriginAndTransform(&controller_model); switch (controller_input.action) { case VrControllerTestAction::kHover: @@ -96,11 +102,14 @@ break; case VrControllerTestAction::kTouchDown: // Use whatever the most recent direction is and interpret the provided - // point as the point on the touchpad. + // point as the point on the touchpad. Also keep whatever the last + // touchpad button state was. controller_model.laser_direction = GetMostRecentModel().laser_direction; SetOriginAndTransform(&controller_model); controller_model.touching_touchpad = true; controller_model.touchpad_touch_position = controller_input.position; + controller_model.touchpad_button_state = + GetMostRecentModel().touchpad_button_state; controller_model_queue_.push(controller_model); break; case VrControllerTestAction::kTouchUp: @@ -108,6 +117,8 @@ SetOriginAndTransform(&controller_model); controller_model.touching_touchpad = false; controller_model.touchpad_touch_position = controller_input.position; + controller_model.touchpad_button_state = + GetMostRecentModel().touchpad_button_state; controller_model_queue_.push(controller_model); break; default:
diff --git a/chrome/browser/vr/ui_test_input.h b/chrome/browser/vr/ui_test_input.h index 211a43705..9ef40eb7 100644 --- a/chrome/browser/vr/ui_test_input.h +++ b/chrome/browser/vr/ui_test_input.h
@@ -16,6 +16,8 @@ enum class UserFriendlyElementName : int { kNone = 0, // A special "element" that causes the controller to point // straight forward. + kCurrentPosition, // A special "element" that causes the controller to + // remain where it is. kUrl, // URL bar kBackButton, // Back button on the URL bar kForwardButton, // Forward button in the overflow menu
diff --git a/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc b/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc index 4b0d910..ee4de102 100644 --- a/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc +++ b/chrome/browser/vr/win/vr_browser_renderer_thread_win.cc
@@ -63,20 +63,16 @@ void VRBrowserRendererThreadWin::SetVisibleExternalPromptNotification( ExternalPromptNotificationType prompt) { - bool currently_showing_ui = ShouldPauseWebXrAndDrawUI(); - current_external_prompt_notification_type_ = prompt; ui_->SetVisibleExternalPromptNotification(prompt); - bool show_ui = ShouldPauseWebXrAndDrawUI(); - if (!show_ui && currently_showing_ui) { - // Draw WebXR instead of UI. - overlay_->SetOverlayAndWebXRVisibility(false, true); - } else if (!currently_showing_ui && show_ui) { - // Draw UI instead of WebXR. - overlay_->SetOverlayAndWebXRVisibility(true, false); + if (!draw_state_.SetPrompt(prompt)) + return; + + overlay_->SetOverlayAndWebXRVisibility(draw_state_.ShouldDrawUI(), + draw_state_.ShouldDrawWebXR()); + if (draw_state_.ShouldDrawUI()) overlay_->RequestNextOverlayPose(base::BindOnce( &VRBrowserRendererThreadWin::OnPose, base::Unretained(this))); - } } VRBrowserRendererThreadWin* @@ -178,9 +174,9 @@ std::move(initializing_graphics_), std::move(input_delegate), browser_renderer_interface, kSlidingAverageSize); - bool draw_ui = ShouldPauseWebXrAndDrawUI(); - overlay_->SetOverlayAndWebXRVisibility(draw_ui, !draw_ui); - if (draw_ui) { + overlay_->SetOverlayAndWebXRVisibility(draw_state_.ShouldDrawUI(), + draw_state_.ShouldDrawWebXR()); + if (draw_state_.ShouldDrawUI()) { overlay_->RequestNextOverlayPose(base::BindOnce( &VRBrowserRendererThreadWin::OnPose, base::Unretained(this))); } @@ -188,14 +184,17 @@ } void VRBrowserRendererThreadWin::StopOverlay() { - overlay_->SetOverlayAndWebXRVisibility(false, true); + draw_state_.StopOverlay(); + overlay_->SetOverlayAndWebXRVisibility(draw_state_.ShouldDrawUI(), + draw_state_.ShouldDrawWebXR()); CleanUp(); } void VRBrowserRendererThreadWin::OnPose(device::mojom::XRFrameDataPtr data) { - if (!ShouldPauseWebXrAndDrawUI()) { + if (!draw_state_.ShouldDrawUI()) { // We shouldn't be showing UI. - overlay_->SetOverlayAndWebXRVisibility(false, true); + overlay_->SetOverlayAndWebXRVisibility(draw_state_.ShouldDrawUI(), + draw_state_.ShouldDrawWebXR()); graphics_->ResetMemoryBuffer(); return; } @@ -227,7 +226,7 @@ // calling the callback if we are destroyed. scheduler_->OnPose(base::BindOnce(&VRBrowserRendererThreadWin::SubmitFrame, base::Unretained(this), std::move(data)), - head_from_world, ShouldPauseWebXrAndDrawUI()); + head_from_world, draw_state_.ShouldDrawUI()); } void VRBrowserRendererThreadWin::SubmitFrame( @@ -251,9 +250,17 @@ } } -bool VRBrowserRendererThreadWin::ShouldPauseWebXrAndDrawUI() { - return current_external_prompt_notification_type_ != - ExternalPromptNotificationType::kPromptNone; +// VRBrowserRendererThreadWin::DrawContentType functions. +void VRBrowserRendererThreadWin::DrawState::StopOverlay() { + prompt_ = ExternalPromptNotificationType::kPromptNone; +} + +bool VRBrowserRendererThreadWin::DrawState::ShouldDrawUI() { + return prompt_ != ExternalPromptNotificationType::kPromptNone; +} + +bool VRBrowserRendererThreadWin::DrawState::ShouldDrawWebXR() { + return prompt_ == ExternalPromptNotificationType::kPromptNone; } } // namespace vr
diff --git a/chrome/browser/vr/win/vr_browser_renderer_thread_win.h b/chrome/browser/vr/win/vr_browser_renderer_thread_win.h index 4fa8e053..7476326 100644 --- a/chrome/browser/vr/win/vr_browser_renderer_thread_win.h +++ b/chrome/browser/vr/win/vr_browser_renderer_thread_win.h
@@ -37,16 +37,29 @@ BrowserRenderer* GetBrowserRendererForTesting(); private: + class DrawState { + public: + // State changing methods. + bool SetPrompt(ExternalPromptNotificationType prompt) { + auto old = prompt_; + prompt_ = prompt; + return prompt_ != old; + } + void StopOverlay(); + + // State querying methods. + bool ShouldDrawUI(); + bool ShouldDrawWebXR(); + + private: + ExternalPromptNotificationType prompt_ = + ExternalPromptNotificationType::kPromptNone; + }; + void CleanUp(); void OnPose(device::mojom::XRFrameDataPtr data); void SubmitResult(bool success); void SubmitFrame(device::mojom::XRFrameDataPtr data); - // If there is fullscreen UI in-headset, we won't composite WebXR content or - // render WebXR Overlays. We can even avoid giving out poses to avoid spending - // resources drawing things that won't be shown. - // When we return false, we tell the Ui to DrawWebVR. When we return true, we - // tell the Ui to DrawUI. - bool ShouldPauseWebXrAndDrawUI(); // We need to do some initialization of GraphicsDelegateWin before // browser_renderer_, so we first store it in a unique_ptr, then transition @@ -60,8 +73,8 @@ GraphicsDelegateWin* graphics_ = nullptr; SchedulerDelegateWin* scheduler_ = nullptr; BrowserUiInterface* ui_ = nullptr; - ExternalPromptNotificationType current_external_prompt_notification_type_ = - ExternalPromptNotificationType::kPromptNone; + + DrawState draw_state_; device::mojom::ImmersiveOverlayPtr overlay_; device::mojom::VRDisplayInfoPtr display_info_;
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json index a5c0d276..065a299 100644 --- a/chrome/common/extensions/api/_api_features.json +++ b/chrome/common/extensions/api/_api_features.json
@@ -297,7 +297,7 @@ "dashboardPrivate": [{ "channel": "stable", "contexts": ["blessed_web_page", "web_page"], - "matches": ["https://chrome.google.com/*"] + "matches": ["https://chrome.google.com/webstore/*"] }, { "channel": "stable", "contexts": ["blessed_extension"],
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json index 6508988..3452d45 100644 --- a/chrome/common/extensions/api/_permission_features.json +++ b/chrome/common/extensions/api/_permission_features.json
@@ -671,6 +671,7 @@ "channel": "stable", "extension_types": ["extension"], "whitelist": [ + "B281B98DDD6A379EF50D59BB1135419536C1C353", // Report site to Safe Browsing Extension, crbug.com/932161 "FD15C63ABA854733FDCBC1D4D34A71E963A12ABD", // Chrome Reporting Extension "08455FA7CB8734168378F731B00B354CEEE0088F", // Chrome Reporting Ext. Beta "86C81CACF5E2273044C491F1949E928E25F28E0A" // https://crbug.com/922140 Cloud Reporting
diff --git a/chrome/services/app_service/README.md b/chrome/services/app_service/README.md index 1f248ae..3b73e36a 100644 --- a/chrome/services/app_service/README.md +++ b/chrome/services/app_service/README.md
@@ -255,20 +255,17 @@ `App` type. `Publisher`s will generally serve icon data lazily, on demand, especially as the desired icon resolutions (e.g. 64dip or 256dip) aren't known up-front. Instead of sending an icon at all possible resolutions, the -`Publisher` sends an `IconKey`: enough information (when combined with the -`AppType app_type` and `string app_id`) to load the icon at given resoultions. -The `IconKey` is an `IconType icon_type` plus additional data (`uint64 u_key` -and `string s_key`) whose semantics depend on the `icon_type`. +`Publisher` sends an `IconKey`: enough information to load the icon at given +resoultions. The `IconKey` is an `AppType app_type` plus additional data +(`uint64 u_key` and `string s_key`) whose semantics depend on the `app_type`. For example, some icons are statically built into the Chrome or Chrome OS binary, as PNG-formatted resources, and can be loaded (synchronously, without sandboxing). They can be loaded from a `u_key` resource ID. Some icons are dynamically (and asynchronously) loaded from the extension database on disk. -They can be loaded from the `app_id` alone. - -TBD: for extension-backed icons, some sort of timestamp or version in the -`IconKey` (probably encoded in the `u_key`) so that an icon update results in a -different `IconKey`. +They can be loaded from the `s_key` app ID, with the `u_key` serving as a +(monotonically increasing) epoch number so that an icon update results in a +different `u_key` and hence a different `IconKey`. Consumers (via the `AppServiceProxy`) can always ask the `AppService` to load an icon. As an optimization, if the `AppServiceProxy` knows how to load an icon @@ -298,17 +295,8 @@ // Some additional methods; not App Icon Factory related. }; - enum IconType { - kUnknown, - kArc, - kCrostini, - kExtension, - kResource, - }; - struct IconKey { AppType app_type; - IconType icon_type; // The semantics of u_key and s_key depend on the app_type. uint64 u_key; string s_key; @@ -417,4 +405,4 @@ --- -Updated on 2019-03-01. +Updated on 2019-03-02.
diff --git a/chrome/services/app_service/app_service_impl_unittest.cc b/chrome/services/app_service/app_service_impl_unittest.cc index 4a7b6b6..15659cb 100644 --- a/chrome/services/app_service/app_service_impl_unittest.cc +++ b/chrome/services/app_service/app_service_impl_unittest.cc
@@ -196,9 +196,7 @@ pub0.load_icon_s_key = "-"; pub1.load_icon_s_key = "-"; pub2.load_icon_s_key = "-"; - auto icon_key = apps::mojom::IconKey::New(); - icon_key->app_type = app_type; - icon_key->s_key = "o"; + auto icon_key = apps::mojom::IconKey::New(app_type, 0, "o"); constexpr bool allow_placeholder_icon = false; impl.LoadIcon( std::move(icon_key), apps::mojom::IconCompression::kUncompressed,
diff --git a/chrome/services/app_service/public/cpp/app_update_unittest.cc b/chrome/services/app_service/public/cpp/app_update_unittest.cc index 26bbb447..79466d0 100644 --- a/chrome/services/app_service/public/cpp/app_update_unittest.cc +++ b/chrome/services/app_service/public/cpp/app_update_unittest.cc
@@ -241,8 +241,7 @@ // IconKey tests. if (state) { - auto x = apps::mojom::IconKey::New( - app_type, apps::mojom::IconType::kResource, 100, std::string()); + auto x = apps::mojom::IconKey::New(app_type, 100, std::string()); state->icon_key = x.Clone(); expect_icon_key_ = x.Clone(); expect_icon_key_changed_ = false; @@ -250,8 +249,7 @@ } if (delta) { - auto x = apps::mojom::IconKey::New( - app_type, apps::mojom::IconType::kExtension, 0, "one hundred"); + auto x = apps::mojom::IconKey::New(app_type, 0, "one hundred"); delta->icon_key = x.Clone(); expect_icon_key_ = x.Clone(); expect_icon_key_changed_ = true;
diff --git a/chrome/services/app_service/public/mojom/types.mojom b/chrome/services/app_service/public/mojom/types.mojom index 035c361..d6a8bc16 100644 --- a/chrome/services/app_service/public/mojom/types.mojom +++ b/chrome/services/app_service/public/mojom/types.mojom
@@ -84,18 +84,8 @@ kTrue, }; -enum IconType { - kUnknown, - kArc, - kCrostini, - kExtension, - kResource, -}; - struct IconKey { AppType app_type; - // TODO(nigeltao): remove IconType enum and field; update README.md. - IconType icon_type; // The semantics of u_key and s_key depend on the app_type. uint64 u_key; string s_key;
diff --git a/chrome/services/cups_ipp_parser/public/cpp/ipp_converter.cpp b/chrome/services/cups_ipp_parser/public/cpp/ipp_converter.cpp index 2654ff6..dda0e97 100644 --- a/chrome/services/cups_ipp_parser/public/cpp/ipp_converter.cpp +++ b/chrome/services/cups_ipp_parser/public/cpp/ipp_converter.cpp
@@ -42,7 +42,7 @@ uint8_t* src = static_cast<uint8_t*>(source); size_t num_to_write = std::min(dst->size(), bytes); - std::copy(src, src + bytes, dst->begin()); + std::copy(src, src + num_to_write, dst->begin()); *dst = dst->subspan(num_to_write); return num_to_write; @@ -161,6 +161,9 @@ {term.first, kHeaderDelimiter, term.second, kCarriage}); } + // End-of-headers sentinel is a double carriage return; add the second one. + headers += kCarriage; + std::vector<uint8_t> ret; std::copy(headers.begin(), headers.end(), std::back_inserter(ret)); return ret; @@ -189,11 +192,17 @@ base::Optional<std::vector<uint8_t>> BuildIppMessage(ipp_t* ipp) { std::vector<uint8_t> request(ippLength(ipp)); + // Need to start in idle state for reading/writing. + if (!ippSetState(ipp, IPP_STATE_IDLE)) { + return base::nullopt; + } + // Casting IppWrite callback to correct internal CUPS type // Note: This is safe since we essentially only cast the first argument from - // vector<const uint8_t> to base::span<const uint8_t>, which is well defined. - auto ret = ippWriteIO(&request, reinterpret_cast<ipp_iocb_t>(IppWrite), 1, - nullptr, ipp); + // base::span<uint8_t> to void* and back, only accessing it from the former. + base::span<uint8_t> request_view(request); + auto ret = ippWriteIO(&request_view, reinterpret_cast<ipp_iocb_t>(IppWrite), + 1, nullptr, ipp); if (ret == IPP_STATE_ERROR) { // Write failed
diff --git a/chrome/test/chromedriver/client/chromedriver.py b/chrome/test/chromedriver/client/chromedriver.py index bc81d5898c..16299e4 100644 --- a/chrome/test/chromedriver/client/chromedriver.py +++ b/chrome/test/chromedriver/client/chromedriver.py
@@ -14,9 +14,6 @@ ELEMENT_KEY = "ELEMENT" MAX_RETRY_COUNT = 3 -# Temp code for debugging https://crbug.com/chromedriver/2778. -enable_core_dump = False - class ChromeDriverException(Exception): pass class NoSuchElement(ChromeDriverException): @@ -136,35 +133,6 @@ if not e.message.startswith('timed out'): raise else: - # Temp code for debugging https://crbug.com/chromedriver/2778. - # If enabled, trigger a core dump on first timeout. - # The code is only intended for Linux, where the above bug is observed. - global enable_core_dump - if enable_core_dump and util.GetPlatformName() == 'linux': - enable_core_dump = False - import psutil - import signal - this_process = psutil.Process() - chromedriver_process = this_process.children()[0] - if chromedriver_process.name() == 'chromedriver': - chrome_processes = chromedriver_process.children() - if len(chrome_processes) == 1: - # Remove core file size limit, then use SIGABRT to dump core. - # Newer versions of psutil.Process have rlimit method, while older - # versions have set_rlimit method. - if hasattr(chrome_processes[0], 'rlimit'): - rlimit_method = chrome_processes[0].rlimit - else: - rlimit_method = chrome_processes[0].set_rlimit - rlimit_method( - psutil.RLIMIT_CORE, - (psutil.RLIM_INFINITY, psutil.RLIM_INFINITY)) - chrome_processes[0].send_signal(signal.SIGABRT) - else: - print 'Skipping core dump as ChromeDriver has unexpected children' - else: - print 'Unable to find ChromeDriver process, skipping core dump' - if ChromeDriver.retry_count < MAX_RETRY_COUNT: ChromeDriver.retry_count = ChromeDriver.retry_count + 1 ChromeDriver.retried_tests.append(kwargs.get('test_name'))
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py index e7249be..8cc42e6 100755 --- a/chrome/test/chromedriver/test/run_py_tests.py +++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -3295,10 +3295,6 @@ options.filter = '*-' + ':__main__.'.join([''] + negative_filter) - # Temp code for debugging https://crbug.com/chromedriver/2778. - if options.test_type is None and util.GetPlatformName() == 'linux': - chromedriver.enable_core_dump = True - all_tests_suite = unittest.defaultTestLoader.loadTestsFromModule( sys.modules[__name__]) tests = unittest_util.FilterTestSuite(all_tests_suite, options.filter)
diff --git a/chrome/test/data/extensions/api_test/service_worker/worker_based_background/process_manager/manifest.json b/chrome/test/data/extensions/api_test/service_worker/worker_based_background/process_manager/manifest.json deleted file mode 100644 index a1699a6..0000000 --- a/chrome/test/data/extensions/api_test/service_worker/worker_based_background/process_manager/manifest.json +++ /dev/null
@@ -1,7 +0,0 @@ -{ - "name": "Service Worker based background script", - "version": "0.1", - "manifest_version": 2, - "description": "Tests launching ProcessManager behavior on worker shutdown", - "background": {"service_worker_script": "service_worker_background.js"} -}
diff --git a/chrome/test/data/extensions/api_test/service_worker/worker_based_background/process_manager/service_worker_background.js b/chrome/test/data/extensions/api_test/service_worker/worker_based_background/process_manager/service_worker_background.js deleted file mode 100644 index 3c999b5..0000000 --- a/chrome/test/data/extensions/api_test/service_worker/worker_based_background/process_manager/service_worker_background.js +++ /dev/null
@@ -1,11 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// browser_test waits for worker installation to complete before it shuts down -// this worker. //content API (StopServiceWorkerForScope) used for shutdown -// DCHECKs if the worker hasn't finished installation. -this.addEventListener('install', function(e) { - console.log('install'); - chrome.test.sendMessage('WORKER_INSTALLED'); -});
diff --git a/chrome/test/data/extensions/download/background.js b/chrome/test/data/extensions/download/background.js new file mode 100644 index 0000000..00528db --- /dev/null +++ b/chrome/test/data/extensions/download/background.js
@@ -0,0 +1,6 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +url = chrome.runtime.getURL('test_download.html'); +chrome.tabs.create({'url': url});
diff --git a/chrome/test/data/extensions/download/download.dat b/chrome/test/data/extensions/download/download.dat new file mode 100644 index 0000000..8b32c5b --- /dev/null +++ b/chrome/test/data/extensions/download/download.dat
@@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent justo sapien, aliquet eget euismod non, laoreet vitae orci. Sed nec placerat nibh. Vivamus semper, lorem at egestas imperdiet, turpis nulla ullamcorper ex, quis condimentum mauris nulla sit amet leo. Duis pellentesque mollis odio cursus tempus. Nulla facilisi. Aliquam eu mi ullamcorper, faucibus augue vitae, placerat est. Vivamus facilisis, arcu quis fringilla ultricies, massa mauris tincidunt erat, nec varius velit dui sed tortor. Praesent sollicitudin gravida diam nec finibus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque non pellentesque sem. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Pellentesque fermentum lectus eget ullamcorper interdum. \ No newline at end of file
diff --git a/chrome/test/data/extensions/download/download.js b/chrome/test/data/extensions/download/download.js new file mode 100644 index 0000000..583e313 --- /dev/null +++ b/chrome/test/data/extensions/download/download.js
@@ -0,0 +1,14 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +window.onload = function() { + // Begin the download (Code based on IE Tab extension) + var anchorObj = document.body.children.namedItem('helper-download'); + anchorObj.href = chrome.runtime.getURL('download.dat'); + // Raise a fake click on the .dat file link, which will rename it to .exe + var evt = document.createEvent("MouseEvents"); + evt.initMouseEvent("click", true, true, window, + 0, 0, 0, 0, 0, false, false, false, false, 0, null); + var allowDefault = anchorObj.dispatchEvent(evt); +};
diff --git a/chrome/test/data/extensions/download/manifest.json b/chrome/test/data/extensions/download/manifest.json new file mode 100644 index 0000000..beab4eb --- /dev/null +++ b/chrome/test/data/extensions/download/manifest.json
@@ -0,0 +1,9 @@ +{ + "name": "download", + "version": "1.0", + "manifest_version": 2, + "description": "Test downloading from extension", + "background": { + "scripts": [ "background.js" ] + } +}
diff --git a/chrome/test/data/extensions/download/test_download.html b/chrome/test/data/extensions/download/test_download.html new file mode 100644 index 0000000..33cff6a --- /dev/null +++ b/chrome/test/data/extensions/download/test_download.html
@@ -0,0 +1,4 @@ +<html> +<a id="helper-download" download="download.exe"/> +<script type="text/javascript" src = "download.js"></script> +</html>
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js index 679d13e..595d839 100644 --- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js +++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -95,10 +95,6 @@ settings_sections_tests.TestNames.SettingsSectionsVisibilityChange); }); -TEST_F('PrintPreviewSettingsSectionsTest', 'Other', function() { - this.runMochaTest(settings_sections_tests.TestNames.Other); -}); - TEST_F('PrintPreviewSettingsSectionsTest', 'SetLayout', function() { this.runMochaTest(settings_sections_tests.TestNames.SetLayout); }); @@ -115,10 +111,6 @@ this.runMochaTest(settings_sections_tests.TestNames.SetPagesPerSheet); }); -TEST_F('PrintPreviewSettingsSectionsTest', 'SetOther', function() { - this.runMochaTest(settings_sections_tests.TestNames.SetOther); -}); - TEST_F('PrintPreviewSettingsSectionsTest', 'PresetCopies', function() { this.runMochaTest(settings_sections_tests.TestNames.PresetCopies); }); @@ -1349,3 +1341,23 @@ TEST_F('PrintPreviewDpiSettingsTest', 'All', function() { mocha.run(); }); + +PrintPreviewOtherOptionsSettingsTest = class extends NewPrintPreviewTest { + /** @override */ + get browsePreload() { + return 'chrome://print/new/other_options_settings.html'; + } + + /** @override */ + get extraLibraries() { + return super.extraLibraries.concat([ + '../settings/test_util.js', + 'print_preview_test_utils.js', + 'other_options_settings_test.js', + ]); + } +}; + +TEST_F('PrintPreviewOtherOptionsSettingsTest', 'All', function() { + mocha.run(); +});
diff --git a/chrome/test/data/webui/print_preview/other_options_settings_test.js b/chrome/test/data/webui/print_preview/other_options_settings_test.js new file mode 100644 index 0000000..083aaa3 --- /dev/null +++ b/chrome/test/data/webui/print_preview/other_options_settings_test.js
@@ -0,0 +1,122 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('other_options_settings_test', function() { + suite('OtherOptionsSettingsTest', function() { + /** @type {?PrintPreviewOtherOptionsSettingsElement} */ + let otherOptionsSection = null; + + /** @override */ + setup(function() { + PolymerTest.clearBody(); + otherOptionsSection = + document.createElement('print-preview-other-options-settings'); + otherOptionsSection.settings = { + duplex: { + value: true, + unavailableValue: false, + valid: true, + available: true, + setByPolicy: false, + key: 'isDuplexEnabled', + }, + cssBackground: { + value: true, + unavailableValue: false, + valid: true, + available: true, + setByPolicy: false, + key: 'isCssBackgroundEnabled', + }, + selectionOnly: { + value: true, + unavailableValue: false, + valid: true, + available: true, + setByPolicy: false, + key: '', + }, + headerFooter: { + value: true, + unavailableValue: false, + valid: true, + available: true, + setByPolicy: false, + key: 'isHeaderFooterEnabled', + }, + rasterize: { + value: true, + unavailableValue: false, + valid: true, + available: true, + setByPolicy: false, + key: '', + }, + }; + otherOptionsSection.disabled = false; + document.body.appendChild(otherOptionsSection); + Polymer.dom.flush(); + }); + + /** + * @param {!CrCheckboxElement} checkbox The checkbox to check + * @return {boolean} Whether the checkbox's parent section is hidden. + */ + function isSectionHidden(checkbox) { + return checkbox.parentNode.parentNode.hidden; + } + + // Verifies that the correct checkboxes are hidden when different settings + // are not available. + test('checkbox visibility', function() { + ['headerFooter', 'duplex', 'cssBackground', 'rasterize', 'selectionOnly'] + .forEach(setting => { + const checkbox = otherOptionsSection.$$(`#${setting}`); + // Show, hide and reset. + [true, false, true].forEach(value => { + otherOptionsSection.set(`settings.${setting}.available`, value); + // Element expected to be visible when available. + assertEquals(!value, isSectionHidden(checkbox)); + }); + }); + }); + + test('set with checkbox', async () => { + const testOptionCheckbox = (settingName) => { + const element = otherOptionsSection.$$('#' + settingName); + const optionSetting = otherOptionsSection.getSetting(settingName); + assertFalse(isSectionHidden(element)); + assertTrue(element.checked); + assertTrue(optionSetting.value); + element.checked = false; + element.dispatchEvent(new CustomEvent('change')); + return test_util + .eventToPromise('update-checkbox-setting', otherOptionsSection) + .then(function(event) { + assertEquals(element.id, event.detail); + assertFalse(optionSetting.value); + }); + }; + + await testOptionCheckbox('headerFooter'); + await testOptionCheckbox('duplex'); + await testOptionCheckbox('cssBackground'); + await testOptionCheckbox('rasterize'); + await testOptionCheckbox('selectionOnly'); + }); + + test('update from setting', function() { + ['headerFooter', 'duplex', 'cssBackground', 'rasterize', 'selectionOnly'] + .forEach(setting => { + const checkbox = otherOptionsSection.$$(`#${setting}`); + // Set false and then true. + [true, false].forEach(value => { + otherOptionsSection.setSetting(setting, value); + // Element expected to be visible when available. + assertEquals(value, checkbox.checked); + }); + }); + }); + }); +});
diff --git a/chrome/test/data/webui/print_preview/settings_section_test.js b/chrome/test/data/webui/print_preview/settings_section_test.js index a580b5c..83c010a6 100644 --- a/chrome/test/data/webui/print_preview/settings_section_test.js +++ b/chrome/test/data/webui/print_preview/settings_section_test.js
@@ -6,12 +6,10 @@ /** @enum {string} */ const TestNames = { SettingsSectionsVisibilityChange: 'settings sections visibility change', - Other: 'other', SetLayout: 'set layout', SetColor: 'set color', SetMargins: 'set margins', SetPagesPerSheet: 'set pages per sheet', - SetOther: 'set other', PresetCopies: 'preset copies', PresetDuplex: 'preset duplex', DisableMarginsByPagesPerSheet: 'disable margins by pages per sheet', @@ -107,27 +105,6 @@ }); }); - /** - * @param {!CrCheckboxElement} checkbox The checkbox to check - * @return {boolean} Whether the checkbox's parent section is hidden. - */ - function isSectionHidden(checkbox) { - return checkbox.parentNode.parentNode.hidden; - } - - test(assert(TestNames.Other), function() { - const optionsElement = page.$$('print-preview-other-options-settings'); - ['headerFooter', 'duplex', 'cssBackground', 'rasterize', 'selectionOnly'] - .forEach(setting => { - page.set(`settings.${setting}.available`, true); - const section = optionsElement.$$(`#${setting}`); - assertFalse(isSectionHidden(section)); - - page.set(`settings.${setting}.available`, false); - assertTrue(isSectionHidden(section)); - }); - }); - test(assert(TestNames.SetLayout), function() { const layoutElement = page.$$('print-preview-layout-settings'); assertFalse(layoutElement.hidden); @@ -252,51 +229,6 @@ }); }); - test(assert(TestNames.SetOther), function() { - toggleMoreSettings(); - const optionsElement = page.$$('print-preview-other-options-settings'); - assertFalse(optionsElement.hidden); - - // HTML - Header/footer, duplex, and CSS background. Also add selection. - initDocumentInfo(false, true); - - const testOptionCheckbox = (settingName, defaultValue) => { - const element = optionsElement.$$('#' + settingName); - const optionSetting = page.settings[settingName]; - assertFalse(isSectionHidden(element)); - assertEquals(defaultValue, element.checked); - assertEquals(defaultValue, optionSetting.value); - element.checked = !defaultValue; - element.dispatchEvent(new CustomEvent('change')); - return test_util - .eventToPromise('update-checkbox-setting', optionsElement) - .then(function(event) { - assertEquals(element.id, event.detail); - assertEquals(!defaultValue, optionSetting.value); - }); - }; - - return testOptionCheckbox('headerFooter', true) - .then(function() { - // Duplex defaults to false, since the printer sets no duplex as the - // default in the CDD (see print_preview_test_utils.js). - return testOptionCheckbox('duplex', false); - }) - .then(function() { - return testOptionCheckbox('cssBackground', false); - }) - .then(function() { - return testOptionCheckbox('selectionOnly', false); - }) - .then(function() { - // Set PDF to test rasterize - if (!cr.isWindows && !cr.isMac) { - initDocumentInfo(true, false); - return testOptionCheckbox('rasterize', false); - } - }); - }); - test(assert(TestNames.PresetCopies), function() { const copiesElement = page.$$('print-preview-copies-settings'); assertFalse(copiesElement.hidden); @@ -480,7 +412,8 @@ assertEquals( subtestParams.expectedValue, page.getSettingValue('duplex')); assertEquals( - subtestParams.expectedHidden, isSectionHidden(duplexElement)); + subtestParams.expectedHidden, + duplexElement.parentNode.parentNode.hidden); if (!subtestParams.expectedHidden) { assertEquals(subtestParams.expectedValue, duplexElement.checked); assertEquals(subtestParams.expectedManaged, duplexElement.disabled);
diff --git a/chrome/test/data/xr/e2e_test_files/html/test_content_resizing_does_not_affect_webpage.html b/chrome/test/data/xr/e2e_test_files/html/test_content_resizing_does_not_affect_webpage.html new file mode 100644 index 0000000..46a72c0c --- /dev/null +++ b/chrome/test/data/xr/e2e_test_files/html/test_content_resizing_does_not_affect_webpage.html
@@ -0,0 +1,29 @@ +<!doctype html> +<!-- +Tests that resizing the VR Browser content quad does not resize the dimensions +that are reported to the webpage. +--> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../resources/webxr_e2e.css"> + </head> + <body> + <script src="../../../../../../third_party/blink/web_tests/resources/testharness.js"></script> + <script src="../resources/webxr_e2e.js"></script> + <script> + var initialWidth = 0; + var initialHeight = 0; + function stepGetInitialDimensions() { + initialWidth = window.innerWidth; + initialHeight = window.innerHeight + finishJavaScriptStep(); + } + + function stepCheckDimensionsAfterResize() { + assert_equals(window.innerWidth, initialWidth, 'Resize affected width'); + assert_equals(window.innerHeight, initialHeight, 'Resize affected height'); + done(); + } + </script> + </body> +</html> \ No newline at end of file
diff --git a/chrome/test/data/xr/e2e_test_files/html/test_rafs_fire_while_repositioning.html b/chrome/test/data/xr/e2e_test_files/html/test_rafs_fire_while_repositioning.html new file mode 100644 index 0000000..f777e09 --- /dev/null +++ b/chrome/test/data/xr/e2e_test_files/html/test_rafs_fire_while_repositioning.html
@@ -0,0 +1,38 @@ +<!doctype html> +<!-- +Tests that window and WebXR rAFs continue to fire while repositioning the +content quad. +--> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../resources/webxr_e2e.css"> + </head> + <body> + <canvas id="webgl-canvas"></canvas> + <script src="../../../../../../third_party/blink/web_tests/resources/testharness.js"></script> + <script src="../resources/webxr_e2e.js"></script> + <script src="../resources/webxr_boilerplate.js"></script> + <script> + var numRafs = 0; + function stepCheckForRafs() { + window.requestAnimationFrame(onWindowRaf); + } + + function onWindowRaf() { + numRafs++; + if (numRafs == 3) { + onMagicWindowXRFrameCallback = onXrRaf; + } else { + window.requestAnimationFrame(onWindowRaf); + } + } + + function onXrRaf() { + numRafs++; + if (numRafs == 6) { + done(); + } + } + </script> + </body> +</html> \ No newline at end of file
diff --git a/chrome/updater/configurator.cc b/chrome/updater/configurator.cc index a5f9a9a..d705f26 100644 --- a/chrome/updater/configurator.cc +++ b/chrome/updater/configurator.cc
@@ -76,7 +76,7 @@ } std::string Configurator::GetLang() const { - return {}; + return "en-US"; } std::string Configurator::GetOSLongName() const { @@ -85,7 +85,7 @@ base::flat_map<std::string, std::string> Configurator::ExtraRequestParams() const { - return {}; + return {{"testrequest", "1"}, {"testsource", "dev"}}; } std::string Configurator::GetDownloadPreference() const { @@ -96,8 +96,7 @@ Configurator::GetNetworkFetcherFactory() { #if defined(OS_WIN) if (!network_fetcher_factory_) { - network_fetcher_factory_ = - base::MakeRefCounted<NetworkFetcherWinHTTPFactory>(); + network_fetcher_factory_ = base::MakeRefCounted<NetworkFetcherFactory>(); } return network_fetcher_factory_; #else
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc index b914069..d93c092 100644 --- a/chrome/updater/updater.cc +++ b/chrome/updater/updater.cc
@@ -4,6 +4,7 @@ #include "chrome/updater/updater.h" +#include <iterator> #include <memory> #include <string> #include <utility> @@ -23,6 +24,7 @@ #include "base/task/task_scheduler/task_scheduler.h" #include "base/task_runner.h" #include "base/threading/platform_thread.h" +#include "base/threading/thread_restrictions.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "chrome/updater/configurator.h" @@ -43,10 +45,12 @@ namespace { -const uint8_t jebg_hash[] = {0x94, 0x16, 0x0b, 0x6d, 0x41, 0x75, 0xe9, 0xec, - 0x8e, 0xd5, 0xfa, 0x54, 0xb0, 0xd2, 0xdd, 0xa5, - 0x6e, 0x05, 0x6b, 0xe8, 0x73, 0x47, 0xf6, 0xc4, - 0x11, 0x9f, 0xbc, 0xb3, 0x09, 0xb3, 0x5b, 0x40}; +// For now, use the Flash CRX for testing. +// CRX id is mimojjlkmoijpicakmndhoigimigcmbb. +const uint8_t mimo_hash[] = {0xc8, 0xce, 0x99, 0xba, 0xce, 0x89, 0xf8, 0x20, + 0xac, 0xd3, 0x7e, 0x86, 0x8c, 0x86, 0x2c, 0x11, + 0xb9, 0x40, 0xc5, 0x55, 0xaf, 0x08, 0x63, 0x70, + 0x54, 0xf9, 0x56, 0xd3, 0xe7, 0x88, 0xba, 0x8c}; void TaskSchedulerStart() { base::TaskScheduler::Create("Updater"); @@ -128,16 +132,31 @@ DISALLOW_COPY_AND_ASSIGN(Observer); }; +void InitLogging(const base::CommandLine& command_line) { + logging::LoggingSettings settings; + settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; + logging::InitLogging(settings); + logging::SetLogItems(true, // enable_process_id + true, // enable_thread_id + true, // enable_timestamp + false); // enable_tickcount +} + } // namespace int UpdaterMain(int argc, const char* const* argv) { base::CommandLine::Init(argc, argv); - const base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch("test")) return 0; + base::DisallowBlocking(); + + InitLogging(*command_line); + + base::PlatformThread::SetName("UpdaterMain"); + base::AtExitManager exit_manager; mojo::core::Init(); @@ -145,10 +164,8 @@ TaskSchedulerStart(); base::MessageLoopForUI message_loop; - DCHECK(base::ThreadTaskRunnerHandle::IsSet()); - base::PlatformThread::SetName("UpdaterMain"); - base::RunLoop runloop; + DCHECK(base::ThreadTaskRunnerHandle::IsSet()); auto embedder_service = CreateEmbedderService(); auto config = @@ -158,17 +175,18 @@ Observer observer(update_client); update_client->AddObserver(&observer); - const std::vector<std::string> ids = {"jebgalgnebhfojomionfpkfelancnnkf"}; + const std::vector<std::string> ids = {"mimojjlkmoijpicakmndhoigimigcmbb"}; update_client->Update( ids, base::BindOnce( [](const std::vector<std::string>& ids) -> std::vector<base::Optional<update_client::CrxComponent>> { update_client::CrxComponent component; - component.name = "jebg"; - component.pk_hash.assign(jebg_hash, - jebg_hash + base::size(jebg_hash)); + component.name = "mimo"; + component.pk_hash.assign(std::begin(mimo_hash), + std::end(mimo_hash)); component.version = base::Version("0.0"); + component.requires_network_encryption = false; return {component}; }), true, @@ -177,7 +195,7 @@ base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&QuitLoop, std::move(closure))); }, - runloop.QuitClosure())); + runloop.QuitWhenIdleClosure())); runloop.Run();
diff --git a/chrome/updater/win/BUILD.gn b/chrome/updater/win/BUILD.gn index d5e222e..8a86844 100644 --- a/chrome/updater/win/BUILD.gn +++ b/chrome/updater/win/BUILD.gn
@@ -37,10 +37,16 @@ source_set("code") { sources = [ + "net/net_util.cc", + "net/net_util.h", "net/network.h", + "net/network_fetcher.cc", + "net/network_fetcher.h", "net/network_winhttp.cc", "net/network_winhttp.h", "net/scoped_hinternet.h", + "util.cc", + "util.h", ] deps = [ @@ -54,6 +60,7 @@ sources = [ "net/network_unittest.cc", + "util_unittest.cc", ] deps = [
diff --git a/chrome/updater/win/net/net_util.cc b/chrome/updater/win/net/net_util.cc new file mode 100644 index 0000000..ba26d4b --- /dev/null +++ b/chrome/updater/win/net/net_util.cc
@@ -0,0 +1,49 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/updater/win/net/net_util.h" + +#include <vector> +#include "base/strings/string_piece.h" + +namespace updater { + +HRESULT QueryHeadersString(HINTERNET request_handle, + uint32_t info_level, + base::StringPiece16 name, + base::string16* value) { + DWORD num_bytes = 0; + ::WinHttpQueryHeaders(request_handle, info_level, name.data(), + WINHTTP_NO_OUTPUT_BUFFER, &num_bytes, + WINHTTP_NO_HEADER_INDEX); + auto hr = HRESULTFromLastError(); + if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) + return hr; + std::vector<base::char16> buffer(num_bytes / sizeof(base::char16)); + if (!::WinHttpQueryHeaders(request_handle, info_level, name.data(), + &buffer.front(), &num_bytes, + WINHTTP_NO_HEADER_INDEX)) { + return HRESULTFromLastError(); + } + DCHECK_EQ(0u, num_bytes % sizeof(base::char16)); + buffer.resize(num_bytes / sizeof(base::char16)); + value->assign(buffer.begin(), buffer.end()); + return S_OK; +} + +HRESULT QueryHeadersInt(HINTERNET request_handle, + uint32_t info_level, + base::StringPiece16 name, + int* value) { + info_level |= WINHTTP_QUERY_FLAG_NUMBER; + DWORD num_bytes = sizeof(*value); + if (!::WinHttpQueryHeaders(request_handle, info_level, name.data(), value, + &num_bytes, WINHTTP_NO_HEADER_INDEX)) { + return HRESULTFromLastError(); + } + + return S_OK; +} + +} // namespace updater
diff --git a/chrome/updater/win/net/net_util.h b/chrome/updater/win/net/net_util.h new file mode 100644 index 0000000..cf0d3091 --- /dev/null +++ b/chrome/updater/win/net/net_util.h
@@ -0,0 +1,54 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_UPDATER_WIN_NET_NET_UTIL_H_ +#define CHROME_UPDATER_WIN_NET_NET_UTIL_H_ + +#include <windows.h> +#include <winhttp.h> + +#include <stdint.h> + +#include <string> + +#include "base/logging.h" +#include "base/strings/string_piece_forward.h" +#include "chrome/updater/win/util.h" + +namespace updater { + +HRESULT QueryHeadersString(HINTERNET request_handle, + uint32_t info_level, + base::StringPiece16 name, + base::string16* value); + +HRESULT QueryHeadersInt(HINTERNET request_handle, + uint32_t info_level, + base::StringPiece16 name, + int* value); + +// Queries WinHTTP options for the given |handle|. Returns S_OK if the call +// is successful. +template <typename T> +HRESULT QueryOption(HINTERNET handle, uint32_t option, T* value) { + auto num_bytes = sizeof(*value); + if (!::WinHttpQueryOption(handle, option, value, &num_bytes)) { + DCHECK_EQ(sizeof(*value), num_bytes); + return HRESULTFromLastError(); + } + return S_OK; +} + +// Sets WinHTTP options for the given |handle|. Returns S_OK if the call +// is successful. +template <typename T> +HRESULT SetOption(HINTERNET handle, uint32_t option, T value) { + if (!::WinHttpSetOption(handle, option, &value, sizeof(value))) + return HRESULTFromLastError(); + return S_OK; +} + +} // namespace updater + +#endif // CHROME_UPDATER_WIN_NET_NET_UTIL_H_
diff --git a/chrome/updater/win/net/network.h b/chrome/updater/win/net/network.h index 5fea779..d6eeb72 100644 --- a/chrome/updater/win/net/network.h +++ b/chrome/updater/win/net/network.h
@@ -9,25 +9,27 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" #include "chrome/updater/win/net/scoped_hinternet.h" #include "components/update_client/network.h" namespace updater { -class NetworkFetcherWinHTTPFactory - : public update_client::NetworkFetcherFactory { +// Network fetcher factory for WinHTTP. +class NetworkFetcherFactory : public update_client::NetworkFetcherFactory { public: - NetworkFetcherWinHTTPFactory(); + NetworkFetcherFactory(); std::unique_ptr<update_client::NetworkFetcher> Create() const override; protected: - ~NetworkFetcherWinHTTPFactory() override; + ~NetworkFetcherFactory() override; private: - ScopedHInternet session_handle_; + THREAD_CHECKER(thread_checker_); + scoped_hinternet session_handle_; - DISALLOW_COPY_AND_ASSIGN(NetworkFetcherWinHTTPFactory); + DISALLOW_COPY_AND_ASSIGN(NetworkFetcherFactory); }; } // namespace updater
diff --git a/chrome/updater/win/net/network_fetcher.cc b/chrome/updater/win/net/network_fetcher.cc new file mode 100644 index 0000000..2508641 --- /dev/null +++ b/chrome/updater/win/net/network_fetcher.cc
@@ -0,0 +1,88 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/updater/win/net/network_fetcher.h" + +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/callback_helpers.h" +#include "base/threading/thread_task_runner_handle.h" +#include "chrome/updater/win/net/network.h" +#include "chrome/updater/win/net/network_winhttp.h" + +namespace updater { + +NetworkFetcher::NetworkFetcher(const HINTERNET& session_handle) + : network_fetcher_( + base::MakeRefCounted<NetworkFetcherWinHTTP>(session_handle)), + main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) {} + +NetworkFetcher::~NetworkFetcher() { + network_fetcher_->Close(); +} + +void NetworkFetcher::PostRequest( + const GURL& url, + const std::string& post_data, + const base::flat_map<std::string, std::string>& post_additional_headers, + ResponseStartedCallback response_started_callback, + PostRequestCompleteCallback post_request_complete_callback) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + post_request_complete_callback_ = std::move(post_request_complete_callback); + network_fetcher_->PostRequest( + url, post_data, post_additional_headers, + std::move(response_started_callback), + base::BindOnce(&NetworkFetcher::PostRequestComplete, + base::Unretained(this))); +} + +void NetworkFetcher::DownloadToFile( + const GURL& url, + const base::FilePath& file_path, + ResponseStartedCallback response_started_callback, + DownloadToFileCompleteCallback download_to_file_complete_callback) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + download_to_file_complete_callback_ = + std::move(download_to_file_complete_callback); + network_fetcher_->DownloadToFile( + url, file_path, std::move(response_started_callback), + base::BindOnce(&NetworkFetcher::DownloadToFileComplete, + base::Unretained(this))); +} + +void NetworkFetcher::PostRequestComplete() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + std::move(post_request_complete_callback_) + .Run(std::make_unique<std::string>(network_fetcher_->GetResponseBody()), + network_fetcher_->GetNetError(), network_fetcher_->GetHeaderETag(), + network_fetcher_->GetXHeaderRetryAfterSec()); +} + +void NetworkFetcher::DownloadToFileComplete() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + std::move(download_to_file_complete_callback_) + .Run(network_fetcher_->GetFilePath(), network_fetcher_->GetNetError(), + network_fetcher_->GetContentSize()); +} + +NetworkFetcherFactory::NetworkFetcherFactory() + : session_handle_(::WinHttpOpen(L"Chrome Updater", + WINHTTP_ACCESS_TYPE_NO_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, + WINHTTP_FLAG_ASYNC)) {} +NetworkFetcherFactory::~NetworkFetcherFactory() = default; + +std::unique_ptr<update_client::NetworkFetcher> NetworkFetcherFactory::Create() + const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return session_handle_.get() + ? std::make_unique<NetworkFetcher>(session_handle_.get()) + : nullptr; +} + +} // namespace updater
diff --git a/chrome/updater/win/net/network_fetcher.h b/chrome/updater/win/net/network_fetcher.h new file mode 100644 index 0000000..2cd1091 --- /dev/null +++ b/chrome/updater/win/net/network_fetcher.h
@@ -0,0 +1,73 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_UPDATER_WIN_NET_NETWORK_FETCHER_H_ +#define CHROME_UPDATER_WIN_NET_NETWORK_FETCHER_H_ + +#include <windows.h> + +#include <stdint.h> + +#include <string> + +#include "base/callback.h" +#include "base/containers/flat_map.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" +#include "components/update_client/network.h" +#include "url/gurl.h" + +namespace base { +class FilePath; +class SingleThreadTaskRunner; +} // namespace base + +namespace updater { + +class NetworkFetcherWinHTTP; + +class NetworkFetcher : public update_client::NetworkFetcher { + public: + using ResponseStartedCallback = + update_client::NetworkFetcher::ResponseStartedCallback; + using PostRequestCompleteCallback = + update_client::NetworkFetcher::PostRequestCompleteCallback; + using DownloadToFileCompleteCallback = + update_client::NetworkFetcher::DownloadToFileCompleteCallback; + + explicit NetworkFetcher(const HINTERNET& session_handle_); + ~NetworkFetcher() override; + + // NetworkFetcher overrides. + void PostRequest( + const GURL& url, + const std::string& post_data, + const base::flat_map<std::string, std::string>& post_additional_headers, + ResponseStartedCallback response_started_callback, + PostRequestCompleteCallback post_request_complete_callback) override; + void DownloadToFile(const GURL& url, + const base::FilePath& file_path, + ResponseStartedCallback response_started_callback, + DownloadToFileCompleteCallback + download_to_file_complete_callback) override; + + private: + THREAD_CHECKER(thread_checker_); + + void PostRequestComplete(); + void DownloadToFileComplete(); + + scoped_refptr<NetworkFetcherWinHTTP> network_fetcher_; + scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_; + + DownloadToFileCompleteCallback download_to_file_complete_callback_; + PostRequestCompleteCallback post_request_complete_callback_; + + DISALLOW_COPY_AND_ASSIGN(NetworkFetcher); +}; + +} // namespace updater + +#endif // CHROME_UPDATER_WIN_NET_NETWORK_FETCHER_H_
diff --git a/chrome/updater/win/net/network_unittest.cc b/chrome/updater/win/net/network_unittest.cc index b550968..66d2e8a5 100644 --- a/chrome/updater/win/net/network_unittest.cc +++ b/chrome/updater/win/net/network_unittest.cc
@@ -4,12 +4,15 @@ #include "chrome/updater/win/net/network.h" #include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "testing/gtest/include/gtest/gtest.h" namespace updater { TEST(UpdaterTestNetwork, NetworkFetcherWinHTTPFactory) { - auto fetcher = base::MakeRefCounted<NetworkFetcherWinHTTPFactory>()->Create(); + base::MessageLoopForUI message_loop; + auto fetcher = base::MakeRefCounted<NetworkFetcherFactory>()->Create(); EXPECT_NE(nullptr, fetcher.get()); }
diff --git a/chrome/updater/win/net/network_winhttp.cc b/chrome/updater/win/net/network_winhttp.cc index cda07053..dd7612f 100644 --- a/chrome/updater/win/net/network_winhttp.cc +++ b/chrome/updater/win/net/network_winhttp.cc
@@ -4,40 +4,544 @@ #include "chrome/updater/win/net/network_winhttp.h" +#include <limits> +#include <utility> + +#include "base/bind.h" #include "base/callback.h" +#include "base/callback_helpers.h" +#include "base/files/file.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/numerics/safe_math.h" +#include "base/strings/strcat.h" +#include "base/strings/string16.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "base/task/post_task.h" +#include "base/threading/thread_task_runner_handle.h" +#include "chrome/updater/win/net/net_util.h" #include "chrome/updater/win/net/network.h" #include "chrome/updater/win/net/scoped_hinternet.h" +#include "chrome/updater/win/util.h" +#include "url/url_constants.h" namespace updater { -NetworkFetcherWinHTTP::NetworkFetcherWinHTTP() = default; -NetworkFetcherWinHTTP::~NetworkFetcherWinHTTP() = default; +namespace { + +void CrackUrl(const GURL& url, + bool* is_https, + std::string* host, + int* port, + std::string* path_for_request) { + if (is_https) + *is_https = url.SchemeIs(url::kHttpsScheme); + if (host) + *host = url.host(); + if (port) + *port = url.EffectiveIntPort(); + if (path_for_request) + *path_for_request = url.PathForRequest(); +} + +} // namespace + +NetworkFetcherWinHTTP::NetworkFetcherWinHTTP(const HINTERNET& session_handle) + : main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), + session_handle_(session_handle) {} + +NetworkFetcherWinHTTP::~NetworkFetcherWinHTTP() {} + +void NetworkFetcherWinHTTP::Close() { + request_handle_.reset(); +} + +std::string NetworkFetcherWinHTTP::GetResponseBody() const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return post_response_body_; +} + +HRESULT NetworkFetcherWinHTTP::GetNetError() const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return net_error_; +} + +std::string NetworkFetcherWinHTTP::GetHeaderETag() const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return etag_; +} + +int64_t NetworkFetcherWinHTTP::GetXHeaderRetryAfterSec() const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return xheader_retry_after_sec_; +} + +base::FilePath NetworkFetcherWinHTTP::GetFilePath() const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return file_path_; +} + +int64_t NetworkFetcherWinHTTP::GetContentSize() const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return content_size_; +} void NetworkFetcherWinHTTP::PostRequest( const GURL& url, const std::string& post_data, const base::flat_map<std::string, std::string>& post_additional_headers, - ResponseStartedCallback response_started_callback, - PostRequestCompleteCallback post_request_complete_callback) {} + FetchStartedCallback fetch_started_callback, + FetchCompleteCallback fetch_complete_callback) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + url_ = url; + fetch_started_callback_ = std::move(fetch_started_callback); + fetch_complete_callback_ = std::move(fetch_complete_callback); + + DCHECK(url.SchemeIsHTTPOrHTTPS()); + CrackUrl(url, &is_https_, &host_, &port_, &path_for_request_); + + verb_ = L"POST"; + content_type_ = L"Content-Type: application/json\r\n"; + write_data_callback_ = base::BindRepeating( + &NetworkFetcherWinHTTP::WriteDataToMemory, base::Unretained(this)); + + net_error_ = BeginFetch(post_data, post_additional_headers); + if (FAILED(net_error_)) + std::move(fetch_complete_callback_).Run(); +} void NetworkFetcherWinHTTP::DownloadToFile( const GURL& url, const base::FilePath& file_path, - ResponseStartedCallback response_started_callback, - DownloadToFileCompleteCallback download_to_file_complete_callback) {} + FetchStartedCallback fetch_started_callback, + FetchCompleteCallback fetch_complete_callback) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); -NetworkFetcherWinHTTPFactory::NetworkFetcherWinHTTPFactory() - : session_handle_(WinHttpOpen(L"Chrome Updater", - WINHTTP_ACCESS_TYPE_NO_PROXY, - WINHTTP_NO_PROXY_NAME, - WINHTTP_NO_PROXY_BYPASS, - WINHTTP_FLAG_ASYNC)) {} -NetworkFetcherWinHTTPFactory::~NetworkFetcherWinHTTPFactory() = default; + url_ = url; + file_path_ = file_path; + fetch_started_callback_ = std::move(fetch_started_callback); + fetch_complete_callback_ = std::move(fetch_complete_callback); -std::unique_ptr<update_client::NetworkFetcher> -NetworkFetcherWinHTTPFactory::Create() const { - return session_handle_.get() ? std::make_unique<NetworkFetcherWinHTTP>() - : nullptr; + DCHECK(url.SchemeIsHTTPOrHTTPS()); + CrackUrl(url, &is_https_, &host_, &port_, &path_for_request_); + + verb_ = L"GET"; + write_data_callback_ = base::BindRepeating( + &NetworkFetcherWinHTTP::WriteDataToFile, base::Unretained(this)); + + net_error_ = BeginFetch({}, {}); + if (FAILED(net_error_)) + std::move(fetch_complete_callback_).Run(); +} + +HRESULT NetworkFetcherWinHTTP::BeginFetch( + const std::string& data, + const base::flat_map<std::string, std::string>& additional_headers) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + connect_handle_ = Connect(); + if (!connect_handle_.get()) + return HRESULTFromLastError(); + + request_handle_ = OpenRequest(); + if (!request_handle_.get()) + return HRESULTFromLastError(); + + const auto winhttp_callback = ::WinHttpSetStatusCallback( + request_handle_.get(), &NetworkFetcherWinHTTP::WinHttpStatusCallback, + WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0); + if (winhttp_callback == WINHTTP_INVALID_STATUS_CALLBACK) + return HRESULTFromLastError(); + + auto hr = + SetOption(request_handle_.get(), WINHTTP_OPTION_CONTEXT_VALUE, context()); + if (FAILED(hr)) + return hr; + + self_ = this; + + // Disables both saving and sending cookies. + hr = SetOption(request_handle_.get(), WINHTTP_OPTION_DISABLE_FEATURE, + WINHTTP_DISABLE_COOKIES); + if (FAILED(hr)) + return hr; + + if (!content_type_.empty()) { + ::WinHttpAddRequestHeaders( + request_handle_.get(), content_type_.data(), content_type_.size(), + WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE); + } + for (const auto& header : additional_headers) { + const auto raw_header = base::SysUTF8ToWide( + base::StrCat({header.first, ": ", header.second, "\r\n"})); + ::WinHttpAddRequestHeaders( + request_handle_.get(), raw_header.c_str(), raw_header.size(), + WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE); + } + + hr = SendRequest(data); + if (FAILED(hr)) + return hr; + + return S_OK; +} + +scoped_hinternet NetworkFetcherWinHTTP::Connect() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + return scoped_hinternet(::WinHttpConnect( + session_handle_, base::SysUTF8ToWide(host_).c_str(), port_, 0)); +} + +scoped_hinternet NetworkFetcherWinHTTP::OpenRequest() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + uint32_t flags = WINHTTP_FLAG_REFRESH; + if (is_https_) + flags |= WINHTTP_FLAG_SECURE; + return scoped_hinternet(::WinHttpOpenRequest( + connect_handle_.get(), verb_.data(), + base::SysUTF8ToWide(path_for_request_).c_str(), nullptr, + WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, flags)); +} + +HRESULT NetworkFetcherWinHTTP::SendRequest(const std::string& data) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + const uint32_t bytes_to_send = base::saturated_cast<uint32_t>(data.size()); + void* request_body = + bytes_to_send ? const_cast<char*>(data.c_str()) : WINHTTP_NO_REQUEST_DATA; + if (!::WinHttpSendRequest(request_handle_.get(), + WINHTTP_NO_ADDITIONAL_HEADERS, 0, request_body, + bytes_to_send, bytes_to_send, context())) { + return HRESULTFromLastError(); + } + + return S_OK; +} + +void NetworkFetcherWinHTTP::SendRequestComplete() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + base::string16 all; + QueryHeadersString( + request_handle_.get(), + WINHTTP_QUERY_RAW_HEADERS_CRLF | WINHTTP_QUERY_FLAG_REQUEST_HEADERS, + WINHTTP_HEADER_NAME_BY_INDEX, &all); + VLOG(1) << "request headers: " << all; + + net_error_ = ReceiveResponse(); + if (FAILED(net_error_)) + std::move(fetch_complete_callback_).Run(); +} + +HRESULT NetworkFetcherWinHTTP::ReceiveResponse() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (!::WinHttpReceiveResponse(request_handle_.get(), nullptr)) + return HRESULTFromLastError(); + return S_OK; +} + +void NetworkFetcherWinHTTP::ReceiveResponseComplete() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + base::string16 all; + QueryHeadersString(request_handle_.get(), WINHTTP_QUERY_RAW_HEADERS_CRLF, + WINHTTP_HEADER_NAME_BY_INDEX, &all); + VLOG(1) << "response headers: " << all; + + int response_code = 0; + net_error_ = QueryHeadersInt(request_handle_.get(), WINHTTP_QUERY_STATUS_CODE, + WINHTTP_HEADER_NAME_BY_INDEX, &response_code); + if (FAILED(net_error_)) { + std::move(fetch_complete_callback_).Run(); + return; + } + + int content_length = 0; + net_error_ = + QueryHeadersInt(request_handle_.get(), WINHTTP_QUERY_CONTENT_LENGTH, + WINHTTP_HEADER_NAME_BY_INDEX, &content_length); + if (FAILED(net_error_)) { + std::move(fetch_complete_callback_).Run(); + return; + } + + base::string16 etag; + if (SUCCEEDED(QueryHeadersString(request_handle_.get(), WINHTTP_QUERY_ETAG, + WINHTTP_HEADER_NAME_BY_INDEX, &etag))) { + etag_ = base::SysWideToUTF8(etag); + } + + int xheader_retry_after_sec = 0; + if (SUCCEEDED(QueryHeadersInt( + request_handle_.get(), WINHTTP_QUERY_CUSTOM, + base::SysUTF8ToWide( + update_client::NetworkFetcher::kHeaderXRetryAfter), + &xheader_retry_after_sec))) { + xheader_retry_after_sec_ = xheader_retry_after_sec; + } + + std::move(fetch_started_callback_) + .Run(final_url_.is_valid() ? final_url_ : url_, response_code, + content_length); + + net_error_ = QueryDataAvailable(); + if (FAILED(net_error_)) + std::move(fetch_complete_callback_).Run(); +} + +HRESULT NetworkFetcherWinHTTP::QueryDataAvailable() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (!::WinHttpQueryDataAvailable(request_handle_.get(), nullptr)) + return HRESULTFromLastError(); + return S_OK; +} + +void NetworkFetcherWinHTTP::QueryDataAvailableComplete( + size_t num_bytes_available) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + net_error_ = ReadData(num_bytes_available); + if (FAILED(net_error_)) + std::move(fetch_complete_callback_).Run(); +} + +HRESULT NetworkFetcherWinHTTP::ReadData(size_t num_bytes_available) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + const int num_bytes_to_read = base::saturated_cast<int>(num_bytes_available); + read_buffer_.resize(num_bytes_to_read); + + if (!::WinHttpReadData(request_handle_.get(), &read_buffer_.front(), + read_buffer_.size(), nullptr)) { + return HRESULTFromLastError(); + } + return S_OK; +} + +void NetworkFetcherWinHTTP::ReadDataComplete(size_t num_bytes_read) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + read_buffer_.resize(num_bytes_read); + write_data_callback_.Run(); +} + +void NetworkFetcherWinHTTP::RequestError(const WINHTTP_ASYNC_RESULT* result) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + net_error_ = HRESULTFromUpdaterError(result->dwError); + std::move(fetch_complete_callback_).Run(); +} + +void NetworkFetcherWinHTTP::WriteDataToFile() { + constexpr base::TaskTraits kTaskTraits = { + base::MayBlock(), base::TaskPriority::BEST_EFFORT, + base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}; + + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, kTaskTraits, + base::BindOnce(&NetworkFetcherWinHTTP::WriteDataToFileBlocking, + base::Unretained(this)), + base::BindOnce(&NetworkFetcherWinHTTP::WriteDataToFileComplete, + base::Unretained(this))); +} + +bool NetworkFetcherWinHTTP::WriteDataToFileBlocking() { + if (read_buffer_.empty()) { + file_.Close(); + net_error_ = S_OK; + return true; + } + + if (!file_.IsValid()) { + file_.Initialize(file_path_, base::File::Flags::FLAG_CREATE_ALWAYS | + base::File::Flags::FLAG_WRITE | + base::File::Flags::FLAG_SEQUENTIAL_SCAN); + if (!file_.IsValid()) { + net_error_ = HRESULTFromUpdaterError(file_.error_details()); + return false; + } + } + + DCHECK(file_.IsValid()); + if (file_.WriteAtCurrentPos(&read_buffer_.front(), read_buffer_.size()) == + -1) { + net_error_ = HRESULTFromUpdaterError(base::File::GetLastFileError()); + file_.Close(); + base::DeleteFile(file_path_, false); + return false; + } + + content_size_ += read_buffer_.size(); + return false; +} + +void NetworkFetcherWinHTTP::WriteDataToFileComplete(bool is_eof) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + if (is_eof || FAILED(net_error_)) { + std::move(fetch_complete_callback_).Run(); + return; + } + + net_error_ = QueryDataAvailable(); + if (FAILED(net_error_)) + std::move(fetch_complete_callback_).Run(); +} + +void NetworkFetcherWinHTTP::WriteDataToMemory() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + if (read_buffer_.empty()) { + VLOG(1) << post_response_body_; + net_error_ = S_OK; + std::move(fetch_complete_callback_).Run(); + return; + } + + post_response_body_.append(read_buffer_.begin(), read_buffer_.end()); + content_size_ += read_buffer_.size(); + + net_error_ = QueryDataAvailable(); + if (FAILED(net_error_)) + std::move(fetch_complete_callback_).Run(); +} + +void __stdcall NetworkFetcherWinHTTP::WinHttpStatusCallback(HINTERNET handle, + DWORD_PTR context, + DWORD status, + void* info, + DWORD info_len) { + DCHECK(handle); + DCHECK(context); + NetworkFetcherWinHTTP* network_fetcher = + reinterpret_cast<NetworkFetcherWinHTTP*>(context); + network_fetcher->main_thread_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&NetworkFetcherWinHTTP::StatusCallback, + base::Unretained(network_fetcher), handle, + status, info, info_len)); +} + +void NetworkFetcherWinHTTP::StatusCallback(HINTERNET handle, + uint32_t status, + void* info, + uint32_t info_len) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + base::StringPiece status_string; + base::string16 info_string; + switch (status) { + case WINHTTP_CALLBACK_STATUS_HANDLE_CREATED: + status_string = "handle created"; + break; + case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING: + status_string = "handle closing"; + break; + case WINHTTP_CALLBACK_STATUS_RESOLVING_NAME: + status_string = "resolving"; + info_string.assign(static_cast<base::char16*>(info), info_len); // host. + break; + case WINHTTP_CALLBACK_STATUS_NAME_RESOLVED: + status_string = "resolved"; + break; + case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER: + status_string = "connecting"; + info_string.assign(static_cast<base::char16*>(info), info_len); // IP. + break; + case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER: + status_string = "connected"; + break; + case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST: + status_string = "sending"; + break; + case WINHTTP_CALLBACK_STATUS_REQUEST_SENT: + status_string = "sent"; + break; + case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE: + status_string = "receiving response"; + break; + case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED: + status_string = "response received"; + break; + case WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION: + status_string = "connection closing"; + break; + case WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED: + status_string = "connection closed"; + break; + case WINHTTP_CALLBACK_STATUS_REDIRECT: + status_string = "redirect"; + info_string.assign(static_cast<base::char16*>(info), info_len); // URL. + break; + case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: + status_string = "data available"; + DCHECK_EQ(info_len, sizeof(uint32_t)); + info_string = base::StringPrintf(L"%lu", *static_cast<uint32_t*>(info)); + break; + case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: + status_string = "headers available"; + break; + case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: + status_string = "read complete"; + info_string = base::StringPrintf(L"%lu", info_len); + break; + case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE: + status_string = "send request complete"; + break; + case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE: + status_string = "write complete"; + break; + case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: + status_string = "request error"; + break; + case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE: + status_string = "https failure"; + DCHECK(info); + DCHECK_EQ(info_len, sizeof(uint32_t)); + info_string = base::StringPrintf(L"%#x", *static_cast<uint32_t*>(info)); + break; + default: + status_string = "unknown callback"; + break; + } + + std::string msg; + if (!status_string.empty()) + base::StringAppendF(&msg, "status=%s", status_string.data()); + else + base::StringAppendF(&msg, "status=%#x", status); + if (!info_string.empty()) + base::StringAppendF(&msg, ", info=%s", + base::SysWideToUTF8(info_string).c_str()); + + VLOG(1) << "WinHttp status callback:" + << " handle=" << handle << ", " << msg; + + switch (status) { + case WINHTTP_CALLBACK_STATUS_REDIRECT: + final_url_ = GURL(static_cast<base::char16*>(info)); + break; + case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING: + self_ = nullptr; + break; + case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE: + SendRequestComplete(); + break; + case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: + ReceiveResponseComplete(); + break; + case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: + DCHECK_EQ(info_len, sizeof(uint32_t)); + QueryDataAvailableComplete(*static_cast<uint32_t*>(info)); + break; + case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: + DCHECK_EQ(info, &read_buffer_.front()); + ReadDataComplete(info_len); + break; + case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: + RequestError(static_cast<const WINHTTP_ASYNC_RESULT*>(info)); + break; + default: + break; + } } } // namespace updater
diff --git a/chrome/updater/win/net/network_winhttp.h b/chrome/updater/win/net/network_winhttp.h index ba21b8480..09423fb 100644 --- a/chrome/updater/win/net/network_winhttp.h +++ b/chrome/updater/win/net/network_winhttp.h
@@ -5,41 +5,138 @@ #ifndef CHROME_UPDATER_WIN_NET_NETWORK_WINHTTP_H_ #define CHROME_UPDATER_WIN_NET_NETWORK_WINHTTP_H_ +#include <windows.h> + +#include <stdint.h> + #include <memory> #include <string> +#include <vector> +#include "base/callback.h" +#include "base/files/file.h" +#include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/strings/string_piece_forward.h" +#include "base/threading/thread_checker.h" +#include "chrome/updater/win/net/scoped_hinternet.h" #include "components/update_client/network.h" +#include "url/gurl.h" + +namespace base { +class SingleThreadTaskRunner; +} namespace updater { -class NetworkFetcherWinHTTP : public update_client::NetworkFetcher { +// Implements a network fetcher in terms of WinHTTP. The class is ref-counted +// as it is accessed from both the main thread and the worker threads in +// WinHTTP. +class NetworkFetcherWinHTTP + : public base::RefCountedThreadSafe<NetworkFetcherWinHTTP> { public: - using ResponseStartedCallback = - update_client::NetworkFetcher::ResponseStartedCallback; - using PostRequestCompleteCallback = - update_client::NetworkFetcher::PostRequestCompleteCallback; - using DownloadToFileCompleteCallback = - update_client::NetworkFetcher::DownloadToFileCompleteCallback; + using FetchCompleteCallback = base::OnceCallback<void()>; + using FetchStartedCallback = base::OnceCallback< + void(const GURL& final_url, int response_code, int64_t content_length)>; - NetworkFetcherWinHTTP(); - ~NetworkFetcherWinHTTP() override; + explicit NetworkFetcherWinHTTP(const HINTERNET& session_handle_); - // NetworkFetcher overrides. + void Close(); + void PostRequest( const GURL& url, const std::string& post_data, const base::flat_map<std::string, std::string>& post_additional_headers, - ResponseStartedCallback response_started_callback, - PostRequestCompleteCallback post_request_complete_callback) override; + FetchStartedCallback fetch_started_callback, + FetchCompleteCallback fetch_complete_callback); void DownloadToFile(const GURL& url, const base::FilePath& file_path, - ResponseStartedCallback response_started_callback, - DownloadToFileCompleteCallback - download_to_file_complete_callback) override; + FetchStartedCallback fetch_started_callback, + FetchCompleteCallback fetch_complete_callback); + + std::string GetResponseBody() const; + HRESULT GetNetError() const; + std::string GetHeaderETag() const; + int64_t GetXHeaderRetryAfterSec() const; + base::FilePath GetFilePath() const; + int64_t GetContentSize() const; private: + friend class base::RefCountedThreadSafe<NetworkFetcherWinHTTP>; + using WriteDataCallback = base::RepeatingCallback<void()>; + + ~NetworkFetcherWinHTTP(); + + static void __stdcall WinHttpStatusCallback(HINTERNET handle, + DWORD_PTR context, + DWORD status, + void* info, + DWORD info_len); + + DWORD_PTR context() const { return reinterpret_cast<DWORD_PTR>(this); } + + void StatusCallback(HINTERNET handle, + uint32_t status, + void* info, + uint32_t info_len); + + HRESULT BeginFetch( + const std::string& data, + const base::flat_map<std::string, std::string>& additional_headers); + scoped_hinternet Connect(); + scoped_hinternet OpenRequest(); + HRESULT SendRequest(const std::string& data); + void SendRequestComplete(); + HRESULT ReceiveResponse(); + void ReceiveResponseComplete(); + HRESULT QueryDataAvailable(); + void QueryDataAvailableComplete(size_t num_bytes_available); + HRESULT ReadData(size_t num_bytes_available); + void ReadDataComplete(size_t num_bytes_read); + void RequestError(const WINHTTP_ASYNC_RESULT* result); + + void WriteDataToMemory(); + void WriteDataToFile(); + bool WriteDataToFileBlocking(); + void WriteDataToFileComplete(bool is_eof); + + THREAD_CHECKER(thread_checker_); + scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_; + + const HINTERNET& session_handle_; // Owned by NetworkFetcherWinHTTPFactory. + scoped_hinternet connect_handle_; + scoped_hinternet request_handle_; + + // Keeps an outstanding reference count on itself as long as there is a + // valid request handle and the context for the handle is set to this + // instance. + scoped_refptr<NetworkFetcherWinHTTP> self_; + + GURL url_; + bool is_https_ = false; + std::string host_; + int port_ = 0; + std::string path_for_request_; + + GURL final_url_; + base::StringPiece16 verb_; + base::StringPiece16 content_type_; + WriteDataCallback write_data_callback_; + HRESULT net_error_ = S_OK; + std::string etag_; + int64_t xheader_retry_after_sec_ = -1; + std::vector<char> read_buffer_; + std::string post_response_body_; + base::FilePath file_path_; + base::File file_; + int64_t content_size_ = 0; + + FetchStartedCallback fetch_started_callback_; + FetchCompleteCallback fetch_complete_callback_; + + scoped_refptr<update_client::NetworkFetcherFactory> network_fetcher_factory_; + DISALLOW_COPY_AND_ASSIGN(NetworkFetcherWinHTTP); };
diff --git a/chrome/updater/win/net/scoped_hinternet.h b/chrome/updater/win/net/scoped_hinternet.h index ca4ca6b..235547e 100644 --- a/chrome/updater/win/net/scoped_hinternet.h +++ b/chrome/updater/win/net/scoped_hinternet.h
@@ -25,12 +25,8 @@ } // namespace internal // Manages the lifetime of HINTERNET handles allocated by WinHTTP. -class ScopedHInternet - : public base::ScopedGeneric<HINTERNET, - updater::internal::ScopedHInternetTraits> { - public: - explicit ScopedHInternet(HINTERNET handle) : ScopedGeneric(handle) {} -}; +using scoped_hinternet = + base::ScopedGeneric<HINTERNET, updater::internal::ScopedHInternetTraits>; } // namespace updater
diff --git a/chrome/updater/win/util.cc b/chrome/updater/win/util.cc new file mode 100644 index 0000000..821c729 --- /dev/null +++ b/chrome/updater/win/util.cc
@@ -0,0 +1,16 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/updater/win/util.h" + +#include <windows.h> + +namespace updater { + +HRESULT HRESULTFromLastError() { + const auto error_code = ::GetLastError(); + return (error_code != NO_ERROR) ? HRESULT_FROM_WIN32(error_code) : E_FAIL; +} + +} // namespace updater
diff --git a/chrome/updater/win/util.h b/chrome/updater/win/util.h new file mode 100644 index 0000000..00627248 --- /dev/null +++ b/chrome/updater/win/util.h
@@ -0,0 +1,30 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_UPDATER_WIN_UTIL_H_ +#define CHROME_UPDATER_WIN_UTIL_H_ + +#include "base/win/windows_types.h" + +namespace updater { + +// Returns the last error as an HRESULT or E_FAIL if last error is NO_ERROR. +// This is not a drop in replacement for the HRESULT_FROM_WIN32 macro. +// The macro maps a NO_ERROR to S_OK, whereas the HRESULTFromLastError maps a +// NO_ERROR to E_FAIL. +HRESULT HRESULTFromLastError(); + +// Returns an HRESULT with a custom facility code representing an updater error. +template <typename Error> +HRESULT HRESULTFromUpdaterError(Error error) { + constexpr ULONG kCustomerBit = 0x20000000; + constexpr ULONG kFacilityOmaha = 67; + return static_cast<HRESULT>(static_cast<ULONG>(SEVERITY_ERROR) | + kCustomerBit | (kFacilityOmaha << 16) | + static_cast<ULONG>(error)); +} + +} // namespace updater + +#endif // CHROME_UPDATER_WIN_UTIL_H_
diff --git a/chrome/updater/win/util_unittest.cc b/chrome/updater/win/util_unittest.cc new file mode 100644 index 0000000..e62b1b4e --- /dev/null +++ b/chrome/updater/win/util_unittest.cc
@@ -0,0 +1,20 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/updater/win/util.h" + +#include <windows.h> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace updater { + +TEST(UpdaterTestUtil, HRESULTFromLastError) { + ::SetLastError(ERROR_ACCESS_DENIED); + EXPECT_EQ(E_ACCESSDENIED, HRESULTFromLastError()); + ::SetLastError(ERROR_SUCCESS); + EXPECT_EQ(E_FAIL, HRESULTFromLastError()); +} + +} // namespace updater
diff --git a/chromeos/constants/chromeos_switches.cc b/chromeos/constants/chromeos_switches.cc index 9bfa57c..07dd60f 100644 --- a/chromeos/constants/chromeos_switches.cc +++ b/chromeos/constants/chromeos_switches.cc
@@ -125,6 +125,11 @@ // to the temporary directory. const char kArcPackagesCacheMode[] = "arc-packages-cache-mode"; +// Used in autotest to forces Play Store auto-update state. Can be +// on - auto-update is forced on. +// off - auto-update is forced off. +const char kArcPlayStoreAutoUpdate[] = "arc-play-store-auto-update"; + // Defines how to start ARC. This can take one of the following values: // - always-start automatically start with Play Store UI support. // - always-start-with-no-play-store automatically start without Play Store UI.
diff --git a/chromeos/constants/chromeos_switches.h b/chromeos/constants/chromeos_switches.h index bf25ff08..204021e 100644 --- a/chromeos/constants/chromeos_switches.h +++ b/chromeos/constants/chromeos_switches.h
@@ -39,6 +39,7 @@ CHROMEOS_EXPORT extern const char kArcForceCacheAppIcons[]; CHROMEOS_EXPORT extern const char kArcForceShowOptInUi[]; CHROMEOS_EXPORT extern const char kArcPackagesCacheMode[]; +CHROMEOS_EXPORT extern const char kArcPlayStoreAutoUpdate[]; CHROMEOS_EXPORT extern const char kArcStartMode[]; CHROMEOS_EXPORT extern const char kArcTransitionMigrationRequired[]; CHROMEOS_EXPORT extern const char kArtifactsDir[];
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc index 137c8c5..eff6ca8 100644 --- a/chromeos/services/assistant/assistant_manager_service_impl.cc +++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -216,6 +216,11 @@ void AssistantManagerServiceImpl::EnableHotword(bool enable) { platform_api_->OnHotwordEnabled(enable); + + if (base::FeatureList::IsEnabled(assistant::features::kAssistantVoiceMatch) && + state_ == State::RUNNING) { + assistant_settings_manager_->SyncSpeakerIdEnrollmentStatus(); + } } AssistantSettingsManager*
diff --git a/components/arc/arc_session_impl.cc b/components/arc/arc_session_impl.cc index 60fb9bf2..3dc198a1 100644 --- a/components/arc/arc_session_impl.cc +++ b/components/arc/arc_session_impl.cc
@@ -44,6 +44,9 @@ constexpr char kArcBridgeSocketPath[] = "/run/chrome/arc_bridge.sock"; constexpr char kArcBridgeSocketGroup[] = "arc-bridge"; +constexpr char kOn[] = "on"; +constexpr char kOff[] = "off"; + std::string GenerateRandomToken() { char random_bytes[16]; base::RandBytes(random_bytes, 16); @@ -372,6 +375,27 @@ base::FeatureList::IsEnabled(arc::kFilePickerExperimentFeature)); request.set_lcd_density(lcd_density); + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + chromeos::switches::kArcPlayStoreAutoUpdate)) { + const std::string value = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + chromeos::switches::kArcPlayStoreAutoUpdate); + if (value == kOn) { + request.set_play_store_auto_update( + login_manager:: + StartArcMiniContainerRequest_PlayStoreAutoUpdate_AUTO_UPDATE_ON); + VLOG(1) << "Play Store auto-update is forced on"; + } else if (value == kOff) { + request.set_play_store_auto_update( + login_manager:: + StartArcMiniContainerRequest_PlayStoreAutoUpdate_AUTO_UPDATE_OFF); + VLOG(1) << "Play Store auto-update is forced off"; + } else { + LOG(ERROR) << "Invalid parameter " << value << " for " + << chromeos::switches::kArcPlayStoreAutoUpdate; + } + } + VLOG(1) << "Starting ARC mini instance with lcd_density=" << request.lcd_density();
diff --git a/components/content_capture/common/content_capture_features.cc b/components/content_capture/common/content_capture_features.cc index d9eb4359..01ab6a9 100644 --- a/components/content_capture/common/content_capture_features.cc +++ b/components/content_capture/common/content_capture_features.cc
@@ -21,9 +21,9 @@ false); } -int TaskLongDelayInSeconds() { +int TaskLongDelayInMilliseconds() { return base::GetFieldTrialParamByFeatureAsInt( - kContentCapture, "task_long_delay_in_seconds", 5); + kContentCapture, "task_long_delay_in_milliseconds", 5000); } int TaskShortDelayInMilliseconds() {
diff --git a/components/content_capture/common/content_capture_features.h b/components/content_capture/common/content_capture_features.h index 17b65e9..eff8b18 100644 --- a/components/content_capture/common/content_capture_features.h +++ b/components/content_capture/common/content_capture_features.h
@@ -16,7 +16,7 @@ bool IsContentCaptureEnabled(); bool ShouldUseNodeID(); -int TaskLongDelayInSeconds(); +int TaskLongDelayInMilliseconds(); int TaskShortDelayInMilliseconds(); } // namespace features
diff --git a/components/content_capture/renderer/content_capture_sender.cc b/components/content_capture/renderer/content_capture_sender.cc index a912a97..1772c811 100644 --- a/components/content_capture/renderer/content_capture_sender.cc +++ b/components/content_capture/renderer/content_capture_sender.cc
@@ -28,6 +28,15 @@ return cc::NodeHolder::Type::kTextHolder; } +void ContentCaptureSender::GetTaskTimingParameters( + base::TimeDelta& short_delay, + base::TimeDelta& long_delay) const { + short_delay = base::TimeDelta::FromMilliseconds( + features::TaskShortDelayInMilliseconds()); + long_delay = base::TimeDelta::FromMilliseconds( + features::TaskLongDelayInMilliseconds()); +} + void ContentCaptureSender::DidCaptureContent( const std::vector<scoped_refptr<blink::WebContentHolder>>& data, bool first_data) {
diff --git a/components/content_capture/renderer/content_capture_sender.h b/components/content_capture/renderer/content_capture_sender.h index 08657bc2..120cde1 100644 --- a/components/content_capture/renderer/content_capture_sender.h +++ b/components/content_capture/renderer/content_capture_sender.h
@@ -28,6 +28,8 @@ // blink::WebContentCaptureClient: cc::NodeHolder::Type GetNodeHolderType() const override; + void GetTaskTimingParameters(base::TimeDelta& short_delay, + base::TimeDelta& long_delay) const override; void DidCaptureContent( const std::vector<scoped_refptr<blink::WebContentHolder>>& data, bool first_data) override;
diff --git a/components/feed/core/feed_networking_host.cc b/components/feed/core/feed_networking_host.cc index bf723ff6..27a9481 100644 --- a/components/feed/core/feed_networking_host.cc +++ b/components/feed/core/feed_networking_host.cc
@@ -66,7 +66,8 @@ private: void StartAccessTokenFetch(); - void AccessTokenFetchFinished(GoogleServiceAuthError error, + void AccessTokenFetchFinished(base::TimeTicks token_start_ticks, + GoogleServiceAuthError error, identity::AccessTokenInfo access_token_info); void StartLoader(); std::unique_ptr<network::SimpleURLLoader> MakeLoader(); @@ -130,15 +131,21 @@ token_fetcher_ = std::make_unique<identity::PrimaryAccountAccessTokenFetcher>( "feed", identity_manager_, scopes, base::BindOnce(&NetworkFetch::AccessTokenFetchFinished, - base::Unretained(this)), + base::Unretained(this), tick_clock_->NowTicks()), identity::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable); } void NetworkFetch::AccessTokenFetchFinished( + base::TimeTicks token_start_ticks, GoogleServiceAuthError error, identity::AccessTokenInfo access_token_info) { UMA_HISTOGRAM_ENUMERATION("ContentSuggestions.Feed.Network.TokenFetchStatus", error.state(), GoogleServiceAuthError::NUM_STATES); + + base::TimeDelta token_duration = tick_clock_->NowTicks() - token_start_ticks; + UMA_HISTOGRAM_MEDIUM_TIMES("ContentSuggestions.Feed.Network.TokenDuration", + token_duration); + access_token_ = access_token_info.token; StartLoader(); }
diff --git a/components/feed/core/feed_scheduler_host.cc b/components/feed/core/feed_scheduler_host.cc index 39435f4..52e4d45d 100644 --- a/components/feed/core/feed_scheduler_host.cc +++ b/components/feed/core/feed_scheduler_host.cc
@@ -224,6 +224,8 @@ } } +const int kHttpStatusOk = 200; + } // namespace FeedSchedulerHost::FeedSchedulerHost(PrefService* profile_prefs, @@ -386,6 +388,7 @@ base::Time content_creation_date_time) { profile_prefs_->SetTime(prefs::kLastFetchAttemptTime, content_creation_date_time); + last_fetch_status_ = kHttpStatusOk; TryRun(std::move(fixed_timer_completion_)); ScheduleFixedTimerWakeUp(GetTriggerThreshold(TriggerType::kFixedTimer)); outstanding_request_until_ = base::Time(); @@ -397,6 +400,7 @@ void FeedSchedulerHost::OnRequestError(int network_response_code) { profile_prefs_->SetTime(prefs::kLastFetchAttemptTime, clock_->Now()); + last_fetch_status_ = network_response_code; TryRun(std::move(fixed_timer_completion_)); outstanding_request_until_ = base::Time(); time_until_first_shown_trigger_reported_ = false; @@ -491,6 +495,18 @@ return false; } +UserClassifier* FeedSchedulerHost::GetUserClassifierForDebugging() { + return &user_classifier_; +} + +base::Time FeedSchedulerHost::GetSuppressRefreshesUntilForDebugging() const { + return suppress_refreshes_until_; +} + +int FeedSchedulerHost::GetLastFetchStatusForDebugging() const { + return last_fetch_status_; +} + void FeedSchedulerHost::OnEulaAccepted() { OnForegrounded(); } @@ -621,8 +637,4 @@ } } -UserClassifier* FeedSchedulerHost::user_classifier() { - return &user_classifier_; -} - } // namespace feed
diff --git a/components/feed/core/feed_scheduler_host.h b/components/feed/core/feed_scheduler_host.h index ff1b7d7..eb43b69 100644 --- a/components/feed/core/feed_scheduler_host.h +++ b/components/feed/core/feed_scheduler_host.h
@@ -134,8 +134,14 @@ // the return value, and if true, the caller should start a refresh. bool OnArticlesCleared(bool suppress_refreshes); - // Surface user classifier data for internals debugging page. - UserClassifier* user_classifier(); + // Surface user_classifier_ for internals debugging page. + UserClassifier* GetUserClassifierForDebugging(); + + // Surface suppress_refreshes_until_ for internals debugging page. + base::Time GetSuppressRefreshesUntilForDebugging() const; + + // Surface last_fetch_status_ for internals debugging page. + int GetLastFetchStatusForDebugging() const; private: FRIEND_TEST_ALL_PREFIXES(FeedSchedulerHostTest, GetTriggerThreshold); @@ -219,6 +225,9 @@ base::flat_map<UserClassifier::UserClass, std::unique_ptr<RefreshThrottler>> throttlers_; + // Status of the last fetch for debugging. + int last_fetch_status_; + DISALLOW_COPY_AND_ASSIGN(FeedSchedulerHost); };
diff --git a/components/guest_view/browser/guest_view_base.cc b/components/guest_view/browser/guest_view_base.cc index cf9aac5..1bf9418 100644 --- a/components/guest_view/browser/guest_view_base.cc +++ b/components/guest_view/browser/guest_view_base.cc
@@ -549,7 +549,7 @@ WillAttachToEmbedder(); if (content::GuestMode::IsCrossProcessFrameGuest(web_contents())) { - web_contents()->AttachToOuterWebContentsFrame( + owner_web_contents_->AttachInnerWebContents( base::WrapUnique<WebContents>(web_contents()), outer_contents_frame); // TODO(ekaramad): MimeHandlerViewGuest might not need this ACK // (https://crbug.com/659750).
diff --git a/components/gwp_asan/client/gwp_asan.cc b/components/gwp_asan/client/gwp_asan.cc index 51c4069..e5eb6e2 100644 --- a/components/gwp_asan/client/gwp_asan.cc +++ b/components/gwp_asan/client/gwp_asan.cc
@@ -37,11 +37,14 @@ const base::FeatureParam<double> kProcessSamplingParam{ &kGwpAsan, "ProcessSamplingProbability", 1.0}; -// The multiplier to increase MaxAllocations/TotalPages on canary/dev builds. -const base::FeatureParam<int> kCanaryDevMultiplierParam{ - &kGwpAsan, "CanaryDevMultiplier", 5}; +// The multiplier to increase MaxAllocations/TotalPages in scenarios where we +// want to perform additional testing (e.g. on canary/dev builds or in the +// browser process.) The multiplier increase is cumulative when multiple +// conditions apply. +const base::FeatureParam<int> kIncreasedMemoryMultiplierParam{ + &kGwpAsan, "IncreasedMemoryMultiplier", 4}; -bool EnableForMalloc(bool is_canary_dev) { +bool EnableForMalloc(bool is_canary_dev, bool is_browser_process) { if (!base::FeatureList::IsEnabled(kGwpAsan)) return false; @@ -77,24 +80,26 @@ return false; } - int multiplier = 1; + base::CheckedNumeric<int> multiplier = 1; if (is_canary_dev) - multiplier = kCanaryDevMultiplierParam.Get(); + multiplier += kIncreasedMemoryMultiplierParam.Get(); + if (is_browser_process) + multiplier += kIncreasedMemoryMultiplierParam.Get(); - if (multiplier < 1 || multiplier > kMaxPages) { - DLOG(ERROR) << "GWP-ASan CanaryDevMultiplier is out-of-range: " - << multiplier; + if (!multiplier.IsValid() || multiplier.ValueOrDie() < 1 || + multiplier.ValueOrDie() > kMaxPages) { + DLOG(ERROR) << "GWP-ASan IncreaseMemoryMultiplier is out-of-range"; return false; } base::CheckedNumeric<int> total_pages_mult = total_pages; - total_pages_mult *= multiplier; + total_pages_mult *= multiplier.ValueOrDie(); base::CheckedNumeric<int> max_allocations_mult = max_allocations; - max_allocations_mult *= multiplier; + max_allocations_mult *= multiplier.ValueOrDie(); if (!total_pages_mult.IsValid() || !max_allocations_mult.IsValid()) { DLOG(ERROR) << "GWP-ASan multiplier caused out-of-range multiply: " - << multiplier; + << multiplier.ValueOrDie(); return false; } @@ -112,8 +117,9 @@ } // namespace } // namespace internal -void EnableForMalloc(bool is_canary_dev) { - static bool init_once = internal::EnableForMalloc(is_canary_dev); +void EnableForMalloc(bool is_canary_dev, bool is_browser_process) { + static bool init_once = + internal::EnableForMalloc(is_canary_dev, is_browser_process); ignore_result(init_once); }
diff --git a/components/gwp_asan/client/gwp_asan.h b/components/gwp_asan/client/gwp_asan.h index 49c5a1a..5f3698c 100644 --- a/components/gwp_asan/client/gwp_asan.h +++ b/components/gwp_asan/client/gwp_asan.h
@@ -11,8 +11,10 @@ // Enable GWP-ASan for the current process. This should only be called once per // process. This can not be disabled once it has been enabled. The caller should -// indicate whether this build is a canary or dev build. -GWP_ASAN_EXPORT void EnableForMalloc(bool is_canary_dev); +// indicate whether this build is a canary or dev build or if the current +// process is the browser process. In both cases, GWP-ASan will use more memory. +GWP_ASAN_EXPORT void EnableForMalloc(bool is_canary_dev, + bool is_browser_process); } // namespace gwp_asan
diff --git a/components/mirroring/browser/single_client_video_capture_host.cc b/components/mirroring/browser/single_client_video_capture_host.cc index f1407a6..b67c71e 100644 --- a/components/mirroring/browser/single_client_video_capture_host.cc +++ b/components/mirroring/browser/single_client_video_capture_host.cc
@@ -266,6 +266,10 @@ NOTIMPLEMENTED(); } +void SingleClientVideoCaptureHost::OnStopped() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + void SingleClientVideoCaptureHost::OnDeviceLaunched( std::unique_ptr<content::LaunchedVideoCaptureDevice> device) { DVLOG(1) << __func__;
diff --git a/components/mirroring/browser/single_client_video_capture_host.h b/components/mirroring/browser/single_client_video_capture_host.h index dc1ece79..08b4e313 100644 --- a/components/mirroring/browser/single_client_video_capture_host.h +++ b/components/mirroring/browser/single_client_video_capture_host.h
@@ -86,6 +86,7 @@ void OnLog(const std::string& message) override; void OnStarted() override; void OnStartedUsingGpuDecode() override; + void OnStopped() override; void OnDeviceLaunched( std::unique_ptr<content::LaunchedVideoCaptureDevice> device);
diff --git a/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.cc b/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.cc index bdcbc95..256ce45 100644 --- a/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.cc +++ b/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.cc
@@ -194,6 +194,10 @@ void RemoteSuggestionsFetcherImpl::FetchSnippets( const RequestParams& params, SnippetsAvailableCallback callback) { + SnippetsAvailableCallback wrapped_callback = base::BindOnce( + &RemoteSuggestionsFetcherImpl::EmitDurationAndInvokeCallback, + base::Unretained(this), base::Time::Now(), std::move(callback)); + if (!params.interactive_request) { base::UmaHistogramSparse( "NewTabPage.Snippets.FetchTimeLocal", @@ -215,11 +219,12 @@ if (identity_manager_->HasPrimaryAccount()) { // Signed-in: get OAuth token --> fetch suggestions. - pending_requests_.emplace(std::move(builder), std::move(callback)); + pending_requests_.emplace(std::move(builder), std::move(wrapped_callback)); StartTokenRequest(); } else { // Not signed in: fetch suggestions (without authentication). - FetchSnippetsNonAuthenticated(std::move(builder), std::move(callback)); + FetchSnippetsNonAuthenticated(std::move(builder), + std::move(wrapped_callback)); } } @@ -276,20 +281,29 @@ return; } + base::Time token_start_time = clock_->Now(); identity::ScopeSet scopes{kContentSuggestionsApiScope}; token_fetcher_ = std::make_unique<identity::PrimaryAccountAccessTokenFetcher>( "ntp_snippets", identity_manager_, scopes, base::BindOnce(&RemoteSuggestionsFetcherImpl::AccessTokenFetchFinished, - base::Unretained(this)), + base::Unretained(this), token_start_time), identity::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable); } void RemoteSuggestionsFetcherImpl::AccessTokenFetchFinished( + base::Time token_start_time, GoogleServiceAuthError error, identity::AccessTokenInfo access_token_info) { DCHECK(token_fetcher_); token_fetcher_.reset(); + UMA_HISTOGRAM_ENUMERATION("ContentSuggestions.Feed.Network.TokenFetchStatus", + error.state(), GoogleServiceAuthError::NUM_STATES); + + base::TimeDelta token_duration = clock_->Now() - token_start_time; + UMA_HISTOGRAM_MEDIUM_TIMES("ContentSuggestions.Feed.Network.TokenDuration", + token_duration); + if (error.state() != GoogleServiceAuthError::NONE) { AccessTokenError(error); return; @@ -310,7 +324,6 @@ void RemoteSuggestionsFetcherImpl::AccessTokenError( const GoogleServiceAuthError& error) { DCHECK_NE(error.state(), GoogleServiceAuthError::NONE); - DLOG(ERROR) << "Unable to get token: " << error.ToString(); while (!pending_requests_.empty()) { @@ -343,7 +356,6 @@ UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime", request->GetFetchDuration()); - if (!result) { FetchFinished(OptionalFetchedCategories(), std::move(callback), status_code, error_details, is_authenticated, access_token); @@ -397,6 +409,17 @@ std::move(categories)); } +void RemoteSuggestionsFetcherImpl::EmitDurationAndInvokeCallback( + base::Time start_time, + SnippetsAvailableCallback callback, + Status status, + OptionalFetchedCategories fetched_categories) { + base::TimeDelta duration = clock_->Now() - start_time; + UMA_HISTOGRAM_MEDIUM_TIMES("ContentSuggestions.Feed.Network.Duration", + duration); + std::move(callback).Run(status, std::move(fetched_categories)); +} + // static void RemoteSuggestionsFetcherImpl::set_skip_api_key_check_for_testing() { skip_api_key_check_for_testing_ = true;
diff --git a/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h b/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h index e29a7c4..a7553fd 100644 --- a/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h +++ b/components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h
@@ -83,7 +83,8 @@ void StartTokenRequest(); - void AccessTokenFetchFinished(GoogleServiceAuthError error, + void AccessTokenFetchFinished(base::Time token_start_time, + GoogleServiceAuthError error, identity::AccessTokenInfo access_token_info); void AccessTokenError(const GoogleServiceAuthError& error); @@ -100,6 +101,11 @@ const std::string& error_details, bool is_authenticated, std::string access_token); + void EmitDurationAndInvokeCallback( + base::Time start_time, + SnippetsAvailableCallback callback, + Status status, + OptionalFetchedCategories fetched_categories); // Authentication for signed-in users. identity::IdentityManager* identity_manager_;
diff --git a/components/ntp_tiles/most_visited_sites.cc b/components/ntp_tiles/most_visited_sites.cc index 4612f00..8a03616 100644 --- a/components/ntp_tiles/most_visited_sites.cc +++ b/components/ntp_tiles/most_visited_sites.cc
@@ -718,6 +718,7 @@ tile.title = link.title; tile.url = link.url; tile.source = TileSource::CUSTOM_LINKS; + tile.from_most_visited = link.is_most_visited; tiles.push_back(std::move(tile)); }
diff --git a/components/ntp_tiles/ntp_tile.cc b/components/ntp_tiles/ntp_tile.cc index 73a4fac..0df7832f 100644 --- a/components/ntp_tiles/ntp_tile.cc +++ b/components/ntp_tiles/ntp_tile.cc
@@ -17,7 +17,8 @@ return (a.title == b.title) && (a.url == b.url) && (a.source == b.source) && (a.title_source == b.title_source) && (a.whitelist_icon_path == b.whitelist_icon_path) && - (a.favicon_url == b.favicon_url); + (a.favicon_url == b.favicon_url) && + (a.from_most_visited == b.from_most_visited); } bool operator!=(const NTPTile& a, const NTPTile& b) {
diff --git a/components/ntp_tiles/ntp_tile.h b/components/ntp_tiles/ntp_tile.h index 1b5970f5..21caea35 100644 --- a/components/ntp_tiles/ntp_tile.h +++ b/components/ntp_tiles/ntp_tile.h
@@ -36,6 +36,10 @@ // a ranking algorithm). base::Time data_generation_time; + // True if this tile is a custom link and was initialized from a Most Visited + // item. Used for debugging. + bool from_most_visited = false; + NTPTile(); NTPTile(const NTPTile&); ~NTPTile();
diff --git a/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc b/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc index 2cf7806..fdbadcbc 100644 --- a/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc +++ b/components/ntp_tiles/webui/ntp_tiles_internals_message_handler.cc
@@ -260,6 +260,9 @@ entry->SetInteger("source", static_cast<int>(tile.source)); entry->SetString("whitelistIconPath", tile.whitelist_icon_path.LossyDisplayName()); + if (tile.source == TileSource::CUSTOM_LINKS) { + entry->SetBoolean("fromMostVisited", tile.from_most_visited); + } auto icon_list = std::make_unique<base::ListValue>(); for (const auto& entry : kIconTypesAndNames) {
diff --git a/components/ntp_tiles/webui/resources/ntp_tiles_internals.html b/components/ntp_tiles/webui/resources/ntp_tiles_internals.html index 72ea941..ea76d561 100644 --- a/components/ntp_tiles/webui/resources/ntp_tiles_internals.html +++ b/components/ntp_tiles/webui/resources/ntp_tiles_internals.html
@@ -143,6 +143,11 @@ <span class="value" jsdisplay="onDemand">, on-demand</span>) </td> </tr> + <tr jsdisplay="fromMostVisited != null"> + <td class="detail">From Most Visited</td> + <td class="value" jsdisplay="fromMostVisited">yes</td> + <td class="value" jsdisplay="!fromMostVisited">no</td> + </tr> </tbody> </table> </div>
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc index 7649d926..2703046 100644 --- a/components/omnibox/browser/omnibox_field_trial.cc +++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -637,12 +637,6 @@ kHQPAllowMatchInSchemeRule) == "true"; } -bool OmniboxFieldTrial::DisableResultsCaching() { - return variations::GetVariationParamValue( - kBundledExperimentFieldTrialName, - kDisableResultsCachingRule) == "true"; -} - void OmniboxFieldTrial::GetSuggestPollingStrategy(bool* from_last_keystroke, int* polling_delay_ms) { *from_last_keystroke = variations::GetVariationParamValue( @@ -901,8 +895,6 @@ const char OmniboxFieldTrial::kHQPAllowMatchInSchemeRule[] = "HQPAllowMatchInScheme"; const char OmniboxFieldTrial::kZeroSuggestVariantRule[] = "ZeroSuggestVariant"; -const char OmniboxFieldTrial::kDisableResultsCachingRule[] = - "DisableResultsCaching"; const char OmniboxFieldTrial::kMeasureSuggestPollingDelayFromLastKeystrokeRule[] = "MeasureSuggestPollingDelayFromLastKeystroke";
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h index 96fc150..a8d31ff 100644 --- a/components/omnibox/browser/omnibox_field_trial.h +++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -312,9 +312,6 @@ // --------------------------------------------------------- // For SearchProvider related experiments. -// Returns true if the search provider should not be caching results. -bool DisableResultsCaching(); - // Returns how the search provider should poll Suggest. Currently, we support // measuring polling delay from the last keystroke or last suggest request. void GetSuggestPollingStrategy(bool* from_last_keystroke, @@ -483,7 +480,6 @@ extern const char kHQPAllowMatchInTLDRule[]; extern const char kHQPAllowMatchInSchemeRule[]; extern const char kZeroSuggestVariantRule[]; -extern const char kDisableResultsCachingRule[]; extern const char kMeasureSuggestPollingDelayFromLastKeystrokeRule[]; extern const char kSuggestPollingDelayMsRule[]; extern const char kHQPMaxVisitsToScoreRule[];
diff --git a/components/omnibox/browser/omnibox_field_trial_unittest.cc b/components/omnibox/browser/omnibox_field_trial_unittest.cc index e3d0f135..1a43850 100644 --- a/components/omnibox/browser/omnibox_field_trial_unittest.cc +++ b/components/omnibox/browser/omnibox_field_trial_unittest.cc
@@ -427,21 +427,6 @@ EXPECT_EQ(1.0, buckets.HalfLifeTimeDecay(base::TimeDelta::FromDays(-1))); } -TEST_F(OmniboxFieldTrialTest, DisableResultsCaching) { - EXPECT_FALSE(OmniboxFieldTrial::DisableResultsCaching()); - - { - std::map<std::string, std::string> params; - params[std::string(OmniboxFieldTrial::kDisableResultsCachingRule)] = "true"; - ASSERT_TRUE(variations::AssociateVariationParams( - OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params)); - base::FieldTrialList::CreateFieldTrial( - OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A"); - - EXPECT_TRUE(OmniboxFieldTrial::DisableResultsCaching()); - } -} - TEST_F(OmniboxFieldTrialTest, GetSuggestPollingStrategy) { // Invalid params. VerifySuggestPollingStrategy(
diff --git a/components/omnibox/browser/search_provider.cc b/components/omnibox/browser/search_provider.cc index 80f3be9e..739130e 100644 --- a/components/omnibox/browser/search_provider.cc +++ b/components/omnibox/browser/search_provider.cc
@@ -693,9 +693,6 @@ return; } - if (OmniboxFieldTrial::DisableResultsCaching()) - ClearAllResults(); - // For the minimal_changes case, if we finished the previous query and still // have its results, or are allowed to keep running it, just do that, rather // than starting a new query.
diff --git a/components/previews/content/previews_decider_impl.cc b/components/previews/content/previews_decider_impl.cc index db9dbd09..d5de54e 100644 --- a/components/previews/content/previews_decider_impl.cc +++ b/components/previews/content/previews_decider_impl.cc
@@ -45,31 +45,6 @@ ->Add(static_cast<int>(status)); } -bool AllowedOnReload(PreviewsType type) { - if (base::FeatureList::IsEnabled(features::kPreviewsDisallowedOnReloads)) - return false; - - switch (type) { - // These types return new content on refresh. - case PreviewsType::LITE_PAGE: - case PreviewsType::LITE_PAGE_REDIRECT: - case PreviewsType::LOFI: - case PreviewsType::NOSCRIPT: - case PreviewsType::RESOURCE_LOADING_HINTS: - return true; - // Loading these types will always be stale when refreshed. - case PreviewsType::OFFLINE: - return false; - case PreviewsType::NONE: - case PreviewsType::UNSPECIFIED: - case PreviewsType::DEPRECATED_AMP_REDIRECTION: - case PreviewsType::LAST: - break; - } - NOTREACHED(); - return false; -} - bool ShouldCheckOptimizationHints(PreviewsType type) { switch (type) { // These types may have server optimization hints. @@ -346,11 +321,10 @@ passed_reasons->push_back(PreviewsEligibilityReason::NETWORK_NOT_SLOW); } - // LOAD_VALIDATE_CACHE or LOAD_BYPASS_CACHE mean the user reloaded the page. - // If this is a query for offline previews, reloads should be disallowed. - if (!AllowedOnReload(type) && is_reload) { + if (is_reload) { return PreviewsEligibilityReason::RELOAD_DISALLOWED; } + passed_reasons->push_back(PreviewsEligibilityReason::RELOAD_DISALLOWED); // Check server whitelist/blacklist, if provided.
diff --git a/components/previews/content/previews_decider_impl_unittest.cc b/components/previews/content/previews_decider_impl_unittest.cc index 42ec7bc..8e811df 100644 --- a/components/previews/content/previews_decider_impl_unittest.cc +++ b/components/previews/content/previews_decider_impl_unittest.cc
@@ -625,9 +625,7 @@ TEST_F(PreviewsDeciderImplTest, TestDisallowLoFiOnReloadWithExperiment) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitWithFeatures( - {features::kPreviews, features::kClientLoFi, - features::kPreviewsDisallowedOnReloads}, - {}); + {features::kPreviews, features::kClientLoFi}, {}); InitializeUIService(); ReportEffectiveConnectionType(net::EFFECTIVE_CONNECTION_TYPE_2G); @@ -645,28 +643,6 @@ static_cast<int>(PreviewsEligibilityReason::RELOAD_DISALLOWED), 1); } -TEST_F(PreviewsDeciderImplTest, TestAllowLoFiOnReloadWithoutExperiment) { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitWithFeatures( - {features::kPreviews, features::kClientLoFi}, - {features::kPreviewsDisallowedOnReloads}); - InitializeUIService(); - - ReportEffectiveConnectionType(net::EFFECTIVE_CONNECTION_TYPE_2G); - - PreviewsUserData user_data(kDefaultPageId); - - base::HistogramTester histogram_tester; - previews_decider_impl()->ShouldAllowPreviewAtNavigationStart( - &user_data, GURL("https://www.google.com"), true, PreviewsType::LOFI); - histogram_tester.ExpectBucketCount( - "Previews.EligibilityReason", - static_cast<int>(PreviewsEligibilityReason::RELOAD_DISALLOWED), 0); - histogram_tester.ExpectBucketCount( - "Previews.EligibilityReason.LoFi", - static_cast<int>(PreviewsEligibilityReason::RELOAD_DISALLOWED), 0); -} - TEST_F(PreviewsDeciderImplTest, TestAllowOffline) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature(features::kPreviews);
diff --git a/components/previews/core/previews_features.cc b/components/previews/core/previews_features.cc index 56a744c..fb9f57c9 100644 --- a/components/previews/core/previews_features.cc +++ b/components/previews/core/previews_features.cc
@@ -96,10 +96,6 @@ const base::Feature kSlowPageTriggering{"PreviewsSlowPageTriggering", base::FEATURE_DISABLED_BY_DEFAULT}; -// A feature to prevent previews on all reloads. -const base::Feature kPreviewsDisallowedOnReloads{ - "PreviewsDisallowedOnReloads", base::FEATURE_ENABLED_BY_DEFAULT}; - // Allows HTTPS previews to be served via a URLLoader when network service is // enabled. const base::Feature kHTTPSServerPreviewsUsingURLLoader{
diff --git a/components/previews/core/previews_features.h b/components/previews/core/previews_features.h index 99a43ac5..9a7a266 100644 --- a/components/previews/core/previews_features.h +++ b/components/previews/core/previews_features.h
@@ -22,7 +22,6 @@ extern const base::Feature kLitePageServerPreviews; extern const base::Feature kAndroidOmniboxPreviewsBadge; extern const base::Feature kSlowPageTriggering; -extern const base::Feature kPreviewsDisallowedOnReloads; extern const base::Feature kHTTPSServerPreviewsUsingURLLoader; extern const base::Feature kDataSaverLiteModeRebranding; extern const base::Feature kPreviewsReloadsAreSoftOptOuts;
diff --git a/components/safe_browsing/db/BUILD.gn b/components/safe_browsing/db/BUILD.gn index a710fe5..0e5740fe 100644 --- a/components/safe_browsing/db/BUILD.gn +++ b/components/safe_browsing/db/BUILD.gn
@@ -488,6 +488,7 @@ "v4_store_fuzzer.cc", ] deps = [ + ":v4_protocol_manager_util", ":v4_store", ":v4_test_util", "//base/test:test_support",
diff --git a/components/safe_browsing/db/v4_store_fuzzer.cc b/components/safe_browsing/db/v4_store_fuzzer.cc index 4ddea7a..0f0c84b6 100644 --- a/components/safe_browsing/db/v4_store_fuzzer.cc +++ b/components/safe_browsing/db/v4_store_fuzzer.cc
@@ -8,40 +8,121 @@ #include "base/files/file_path.h" #include "base/test/test_simple_task_runner.h" +#include "components/safe_browsing/db/v4_protocol_manager_util.h" #include "components/safe_browsing/db/v4_store.h" #include "components/safe_browsing/db/v4_test_util.h" namespace safe_browsing { +const PrefixSize kMinHashPrefixLengthForFuzzing = kMinHashPrefixLength; +const PrefixSize kMaxHashPrefixLengthForFuzzing = 8; + class V4StoreFuzzer { public: static int FuzzMergeUpdate(const uint8_t* data, size_t size) { - // Empty update, not interesting. - if (size == 0) - return 0; - - size_t num_prefixes_first_half = size / (2 * kMinHashPrefixLength); - size_t first_half_size = num_prefixes_first_half * kMinHashPrefixLength; - - std::string first_half(data, data + first_half_size); + // |prefix_map_old| represents the existing state of the |V4Store|. HashPrefixMap prefix_map_old; - V4Store::AddUnlumpedHashes(kMinHashPrefixLength, first_half, - &prefix_map_old); - - std::string second_half(data + first_half_size, data + size); + // |prefix_map_additions| represents the update being applied. HashPrefixMap prefix_map_additions; - V4Store::AddUnlumpedHashes(kMinHashPrefixLength, second_half, - &prefix_map_additions); + + // Pass 1: + // Add a prefix_size->[prefixes] pair in |prefix_map_old|. + PopulateHashPrefixMap(&data, &size, &prefix_map_old); + // Add a prefix_size->[prefixes] pair in |prefix_map_additions|. + PopulateHashPrefixMap(&data, &size, &prefix_map_additions); + + // Pass 2: + // Add a prefix_size->[prefixes] pair in |prefix_map_old|. + // If the prefix_size is the same as that added in |prefix_map_old| during + // Pass 1, the older list of prefixes is lost. + PopulateHashPrefixMap(&data, &size, &prefix_map_old); + // Add a prefix_size->[prefixes] pair in |prefix_map_additions|. + // If the prefix_size is the same as that added in |prefix_map_additions| + // during Pass 1, the older list of prefixes is lost. + PopulateHashPrefixMap(&data, &size, &prefix_map_additions); auto store = std::make_unique<TestV4Store>( base::MakeRefCounted<base::TestSimpleTaskRunner>(), base::FilePath()); + // Assume no removals. google::protobuf::RepeatedField<google::protobuf::int32> raw_removals; + // Empty checksum indicates that the checksum calculation should be skipped. std::string empty_checksum; store->MergeUpdate(prefix_map_old, prefix_map_additions, &raw_removals, empty_checksum); +#ifndef NDEBUG + DisplayHashPrefixMapDetails(store->hash_prefix_map_); +#endif return 0; } + + private: + // Add a prefix_size->[prefixes] pair in |hash_prefix_map|. + // Ensures that length of [prefixes] is a multiple of prefix_size. + // If the map already contains a pair with key prefix_size, the existing value + // is discarded. + // Here's a summary of how the input is parsed: + // * First uint8_t is the |prefix_size| to be added. + // * Next uint8_t is the length of the list of prefixes. + // * It is adjusted to be no greater than the remaining size of |data|. + // * It is adjusted to be a multiple of |prefix_size|. + // * It is called as |prefixes_list_size|. + // * Next |prefixes_list_size| bytes are added to |hash_prefix_map| + // as a list of prefixes of size |prefix_size|. + static void PopulateHashPrefixMap(const uint8_t** data, + size_t* size, + HashPrefixMap* hash_prefix_map) { + uint8_t datum; + if (!GetDatum(data, size, &datum)) + return; + + // Prefix size is defined to be between |kMinHashPrefixLength| and + // |kMaxHashPrefixLength| but we are going to limit them to smaller sizes so + // that we have a higher chance of actually populating the + // |hash_prefix_map| for smaller inputs. + PrefixSize prefix_size = kMinHashPrefixLengthForFuzzing + + (datum % (kMaxHashPrefixLengthForFuzzing - + kMinHashPrefixLengthForFuzzing + 1)); + + if (!GetDatum(data, size, &datum)) + return; + // This |datum| tells us how long should the list of prefixes be. + // It can't be larger than the remaining buffer. + if (*size < datum) { + datum = *size; + } + // For the list of prefixes to be inserted into |hash_prefix_map|, its size + // needs to be a multiple of |prefix_size|. Otherwise + // |V4Store::AddUnlumpedHashes| would simply discard the input and it'll + // never reach |MergeUpdate|. + size_t prefixes_list_size = (datum / prefix_size) * prefix_size; + std::string prefixes(*data, *data + prefixes_list_size); + *size -= prefixes_list_size; + *data += prefixes_list_size; + V4Store::AddUnlumpedHashes(prefix_size, prefixes, hash_prefix_map); +#ifndef NDEBUG + DisplayHashPrefixMapDetails(*hash_prefix_map); +#endif + } + + static bool GetDatum(const uint8_t** data, size_t* size, uint8_t* datum) { + if (*size == 0) + return false; + *datum = *data[0]; + (*data)++; + (*size)--; + return true; + } + + static void DisplayHashPrefixMapDetails( + const HashPrefixMap& hash_prefix_map) { + for (const auto& pair : hash_prefix_map) { + PrefixSize prefix_size = pair.first; + size_t prefixes_length = pair.second.length(); + DVLOG(5) << __FUNCTION__ << " : " << prefix_size << " : " + << prefixes_length; + } + } }; } // namespace safe_browsing
diff --git a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_indicator_hover_reposition_browser_ui.Pixel_XL-25.png.sha1 b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_indicator_hover_reposition_browser_ui.Pixel_XL-25.png.sha1 new file mode 100644 index 0000000..2d75fedf --- /dev/null +++ b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_indicator_hover_reposition_browser_ui.Pixel_XL-25.png.sha1
@@ -0,0 +1 @@ +34a182606c337a192e2759931b5e30c7deff69b9 \ No newline at end of file
diff --git a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_indicator_hover_reposition_browser_ui.Pixel_XL-26.png.sha1 b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_indicator_hover_reposition_browser_ui.Pixel_XL-26.png.sha1 new file mode 100644 index 0000000..cbb79976 --- /dev/null +++ b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_indicator_hover_reposition_browser_ui.Pixel_XL-26.png.sha1
@@ -0,0 +1 @@ +071a33584ff15e29d0fbf02531393bae05c0bfd8 \ No newline at end of file
diff --git a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_reposition_granted_browser_ui.Pixel_XL-25.png.sha1 b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_reposition_granted_browser_ui.Pixel_XL-25.png.sha1 new file mode 100644 index 0000000..a6a76bd --- /dev/null +++ b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_reposition_granted_browser_ui.Pixel_XL-25.png.sha1
@@ -0,0 +1 @@ +7feca6b68dbf4f95f6b4bcb6d8b8534b5ed90cd1 \ No newline at end of file
diff --git a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_reposition_granted_browser_ui.Pixel_XL-26.png.sha1 b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_reposition_granted_browser_ui.Pixel_XL-26.png.sha1 new file mode 100644 index 0000000..3f335396 --- /dev/null +++ b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_reposition_granted_browser_ui.Pixel_XL-26.png.sha1
@@ -0,0 +1 @@ +47796386fdb68aa7714d29b905b120e32a380971 \ No newline at end of file
diff --git a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_reposition_visible_browser_ui.Pixel_XL-25.png.sha1 b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_reposition_visible_browser_ui.Pixel_XL-25.png.sha1 new file mode 100644 index 0000000..f081e8f --- /dev/null +++ b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_reposition_visible_browser_ui.Pixel_XL-25.png.sha1
@@ -0,0 +1 @@ +a1bb4630aeb33528b31d9329847718efe4ec90bc \ No newline at end of file
diff --git a/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_reposition_visible_browser_ui.Pixel_XL-26.png.sha1 b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_reposition_visible_browser_ui.Pixel_XL-26.png.sha1 new file mode 100644 index 0000000..c59ce33 --- /dev/null +++ b/components/test/data/permission_dialogs/render_tests/VrBrowserDialogTest.microphone_permission_prompt_reposition_visible_browser_ui.Pixel_XL-26.png.sha1
@@ -0,0 +1 @@ +97e6022aff289eae9b3435ff19aa8a311b94df6d \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.reposition_bar_permission_prompt_open.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.reposition_bar_permission_prompt_open.Pixel_XL-25.png.sha1 new file mode 100644 index 0000000..8c837bc --- /dev/null +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.reposition_bar_permission_prompt_open.Pixel_XL-25.png.sha1
@@ -0,0 +1 @@ +578b0246815fdd37d5851dabae6aa84a4e4b4a23 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.reposition_bar_permission_prompt_open.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.reposition_bar_permission_prompt_open.Pixel_XL-26.png.sha1 new file mode 100644 index 0000000..76888eb --- /dev/null +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.reposition_bar_permission_prompt_open.Pixel_XL-26.png.sha1
@@ -0,0 +1 @@ +561610e9945cd3ff1b052f714189b474a4fa1e97 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.repositioned_keyboard.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.repositioned_keyboard.Pixel_XL-25.png.sha1 new file mode 100644 index 0000000..0f77e0f --- /dev/null +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.repositioned_keyboard.Pixel_XL-25.png.sha1
@@ -0,0 +1 @@ +f3e870f5e8220ebddcada3f85c1618cbbd2db249 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.repositioned_keyboard.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.repositioned_keyboard.Pixel_XL-26.png.sha1 new file mode 100644 index 0000000..546c3c2 --- /dev/null +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.repositioned_keyboard.Pixel_XL-26.png.sha1
@@ -0,0 +1 @@ +59c9b14ba54519dd17786cb8c5631e2352c28550 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.repositioned_overflow_menu.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.repositioned_overflow_menu.Pixel_XL-25.png.sha1 new file mode 100644 index 0000000..1b5a654 --- /dev/null +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.repositioned_overflow_menu.Pixel_XL-25.png.sha1
@@ -0,0 +1 @@ +18bad9ffcce1a4e3e13c14f320462a94bcac1899 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.repositioned_overflow_menu.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.repositioned_overflow_menu.Pixel_XL-26.png.sha1 new file mode 100644 index 0000000..f6f9ac5c --- /dev/null +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.repositioned_overflow_menu.Pixel_XL-26.png.sha1
@@ -0,0 +1 @@ +be049c7b078fe370248cdb9bb068d9fc06fd3424 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.scroll_resizing.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.scroll_resizing.Pixel_XL-25.png.sha1 new file mode 100644 index 0000000..ea1e849 --- /dev/null +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.scroll_resizing.Pixel_XL-25.png.sha1
@@ -0,0 +1 @@ +aa113baa3920f9aa940ae339d9dbc15f24a56bd6 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.scroll_resizing.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.scroll_resizing.Pixel_XL-26.png.sha1 new file mode 100644 index 0000000..b95e1b2 --- /dev/null +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.scroll_resizing.Pixel_XL-26.png.sha1
@@ -0,0 +1 @@ +f83e0b6762a234126a98b236fefd74b66e116042 \ No newline at end of file
diff --git a/components/viz/common/display/renderer_settings.h b/components/viz/common/display/renderer_settings.h index e34c338..ce4fb4c 100644 --- a/components/viz/common/display/renderer_settings.h +++ b/components/viz/common/display/renderer_settings.h
@@ -30,6 +30,7 @@ bool tint_gl_composited_content = false; bool show_overdraw_feedback = false; bool use_skia_renderer = false; + bool use_skia_renderer_non_ddl = false; bool allow_overlays = true; bool dont_round_texture_sizes_for_pixel_tests = false; int highp_threshold_min = 0;
diff --git a/components/viz/common/frame_sinks/copy_output_util.cc b/components/viz/common/frame_sinks/copy_output_util.cc index 73335d4..fa917e2 100644 --- a/components/viz/common/frame_sinks/copy_output_util.cc +++ b/components/viz/common/frame_sinks/copy_output_util.cc
@@ -40,8 +40,8 @@ const gfx::Vector2d& scale_to) { DCHECK_GT(scale_from.x(), 0); DCHECK_GT(scale_from.y(), 0); - DCHECK_GT(scale_to.x(), 0); - DCHECK_GT(scale_to.y(), 0); + DCHECK_GE(scale_to.x(), 0); + DCHECK_GE(scale_to.y(), 0); const int64_t x = FloorScale(area.x(), scale_to.x(), scale_from.x()); const int64_t y = FloorScale(area.y(), scale_to.y(), scale_from.y());
diff --git a/components/viz/common/quads/draw_quad.h b/components/viz/common/quads/draw_quad.h index 7104746..01de49e 100644 --- a/components/viz/common/quads/draw_quad.h +++ b/components/viz/common/quads/draw_quad.h
@@ -74,7 +74,8 @@ bool IsDebugQuad() const { return material == DEBUG_BORDER; } bool ShouldDrawWithBlending() const { - return needs_blending || shared_quad_state->opacity < 1.0f; + return needs_blending || shared_quad_state->opacity < 1.0f || + shared_quad_state->blend_mode != SkBlendMode::kSrcOver; } // Is the left edge of this tile aligned with the originating layer's
diff --git a/components/viz/host/client_frame_sink_video_capturer.cc b/components/viz/host/client_frame_sink_video_capturer.cc index a09ce3e..d8b32c9 100644 --- a/components/viz/host/client_frame_sink_video_capturer.cc +++ b/components/viz/host/client_frame_sink_video_capturer.cc
@@ -150,13 +150,12 @@ void ClientFrameSinkVideoCapturer::OnFrameCaptured( base::ReadOnlySharedMemoryRegion data, media::mojom::VideoFrameInfoPtr info, - const gfx::Rect& update_rect, const gfx::Rect& content_rect, mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - consumer_->OnFrameCaptured(std::move(data), std::move(info), update_rect, - content_rect, std::move(callbacks)); + consumer_->OnFrameCaptured(std::move(data), std::move(info), content_rect, + std::move(callbacks)); } void ClientFrameSinkVideoCapturer::OnStopped() {
diff --git a/components/viz/host/client_frame_sink_video_capturer.h b/components/viz/host/client_frame_sink_video_capturer.h index c5717d7..1b790eb 100644 --- a/components/viz/host/client_frame_sink_video_capturer.h +++ b/components/viz/host/client_frame_sink_video_capturer.h
@@ -117,7 +117,6 @@ void OnFrameCaptured( base::ReadOnlySharedMemoryRegion data, media::mojom::VideoFrameInfoPtr info, - const gfx::Rect& update_rect, const gfx::Rect& content_rect, mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) final; void OnStopped() final;
diff --git a/components/viz/host/renderer_settings_creation.cc b/components/viz/host/renderer_settings_creation.cc index a235580..ee1f83f 100644 --- a/components/viz/host/renderer_settings_creation.cc +++ b/components/viz/host/renderer_settings_creation.cc
@@ -60,6 +60,8 @@ renderer_settings.allow_antialiasing = !command_line->HasSwitch(switches::kDisableCompositedAntialiasing); renderer_settings.use_skia_renderer = features::IsUsingSkiaRenderer(); + renderer_settings.use_skia_renderer_non_ddl = + features::IsUsingSkiaRendererNonDDL(); #if defined(OS_MACOSX) renderer_settings.allow_overlays = ui::RemoteLayerAPISupported() &&
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc index f5cf0101..d32567b 100644 --- a/components/viz/service/display/direct_renderer.cc +++ b/components/viz/service/display/direct_renderer.cc
@@ -200,6 +200,9 @@ if (quad->shared_quad_state->opacity != 1.0f) return nullptr; + if (quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver) + return nullptr; + const TileDrawQuad* tile_quad = TileDrawQuad::MaterialCast(quad); // Hack: this could be supported by passing in a subrectangle to draw // render pass, although in practice if there is only one quad there
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc index e124bce7..cd9f1dd 100644 --- a/components/viz/service/display/display.cc +++ b/components/viz/service/display/display.cc
@@ -281,8 +281,9 @@ resource_provider_ = std::make_unique<DisplayResourceProvider>( mode, output_surface_->context_provider(), bitmap_manager_, enable_shared_images); - - if (settings_.use_skia_renderer && mode == DisplayResourceProvider::kGpu) { + const bool use_skia_renderer = + settings_.use_skia_renderer || settings_.use_skia_renderer_non_ddl; + if (use_skia_renderer && mode == DisplayResourceProvider::kGpu) { // Default to use DDL if skia_output_surface is not null. if (skia_output_surface_) { renderer_ = std::make_unique<SkiaRenderer>(
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc index 62357eb..758b764 100644 --- a/components/viz/service/display/gl_renderer.cc +++ b/components/viz/service/display/gl_renderer.cc
@@ -1966,9 +1966,12 @@ // Blending is required for antialiasing. SetBlendEnabled(true); SetShaderOpacity(quad->shared_quad_state->opacity); + DCHECK(CanApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode)); + ApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode); // Draw the quad with antialiasing. DrawQuadGeometryWithAA(quad, &local_quad, tile_rect); + RestoreBlendFuncToDefault(quad->shared_quad_state->blend_mode); } void GLRenderer::DrawContentQuadNoAA(const ContentDrawQuadBase* quad, @@ -2051,7 +2054,9 @@ tex_coord_rect.x(), tex_coord_rect.y(), tex_coord_rect.width(), tex_coord_rect.height()); + DCHECK(CanApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode)); SetBlendEnabled(quad->ShouldDrawWithBlending()); + ApplyBlendModeUsingBlendFunc(quad->shared_quad_state->blend_mode); SetShaderOpacity(quad->shared_quad_state->opacity); @@ -2093,6 +2098,7 @@ gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr); num_triangles_drawn_ += 2; + RestoreBlendFuncToDefault(quad->shared_quad_state->blend_mode); } void GLRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad,
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc index 7ec41e79..227d52a 100644 --- a/components/viz/service/display/skia_renderer.cc +++ b/components/viz/service/display/skia_renderer.cc
@@ -69,8 +69,16 @@ return !resource_provider->IsResourceSoftwareBacked(resource_id); } -bool ApplyTransformAndScissorToTileRect(const gfx::Transform& transform) { - return transform.IsPositiveScaleOrTranslation(); +bool ApplyTransformAndScissorToTileRect( + const gfx::Transform& contents_device_transform) { + // This is slightly different than + // gfx::Transform::IsPositiveScaleAndTranslation in that it also allows zero + // scales. This is because in the common orthographic case the z scale is 0. + if (!contents_device_transform.IsScaleOrTranslation()) + return false; + return contents_device_transform.matrix().get(0, 0) >= 0.0 && + contents_device_transform.matrix().get(1, 1) >= 0.0 && + contents_device_transform.matrix().get(2, 2) >= 0.0; } } // namespace @@ -489,18 +497,23 @@ if (!current_canvas_) return; TRACE_EVENT0("viz", "SkiaRenderer::DoDrawQuad"); - if (MustDrawBatchedTileQuadsBeforeQuad(quad, draw_region)) - DrawBatchedTileQuads(); if (quad->material == DrawQuad::TILED_CONTENT) { AddTileQuadToBatch(TileDrawQuad::MaterialCast(quad), draw_region); return; } + // If the current quad is not tiled content then we must flush any + // bufferred tiled content quads. + if (!batched_tiles_.empty()) + DrawBatchedTileQuads(); base::Optional<SkAutoCanvasRestore> auto_canvas_restore; const gfx::Rect* scissor_rect = is_scissor_enabled_ ? &scissor_rect_ : nullptr; - PrepareCanvasForDrawQuads(quad->shared_quad_state->quad_to_target_transform, - draw_region, scissor_rect, &auto_canvas_restore); + gfx::Transform contents_device_transform = + current_frame()->window_matrix * current_frame()->projection_matrix * + quad->shared_quad_state->quad_to_target_transform; + PrepareCanvasForDrawQuads(contents_device_transform, draw_region, + scissor_rect, &auto_canvas_restore); SkPaint paint; if (settings_->force_antialiasing || @@ -566,25 +579,27 @@ current_canvas_->resetMatrix(); } -bool SkiaRenderer::MustDrawBatchedTileQuadsBeforeQuad( +bool SkiaRenderer::MustDrawBatchedTileQuads( const DrawQuad* new_quad, + const gfx::Transform& contents_device_transform, + bool apply_transform_and_scissor, const gfx::QuadF* draw_region) { + DCHECK(new_quad->material == DrawQuad::TILED_CONTENT); + DCHECK(apply_transform_and_scissor == + ApplyTransformAndScissorToTileRect(contents_device_transform)); + if (batched_tiles_.empty()) return false; bool has_draw_region = draw_region != nullptr; - if (new_quad->material != DrawQuad::TILED_CONTENT) - return true; - - if (ApplyTransformAndScissorToTileRect( - new_quad->shared_quad_state->quad_to_target_transform)) { - if (!batched_tile_state_.transform.IsIdentity()) + if (apply_transform_and_scissor) { + if (!batched_tile_state_.contents_device_transform.IsIdentity()) return true; DCHECK(!batched_tile_state_.has_scissor_rect); } else { - if (batched_tile_state_.transform != - new_quad->shared_quad_state->quad_to_target_transform || + if (batched_tile_state_.contents_device_transform != + contents_device_transform || batched_tile_state_.has_scissor_rect != is_scissor_enabled_ || (is_scissor_enabled_ && batched_tile_state_.scissor_rect != scissor_rect_)) @@ -606,7 +621,7 @@ } void SkiaRenderer::PrepareCanvasForDrawQuads( - const gfx::Transform& quad_to_target_transform, + gfx::Transform contents_device_transform, const gfx::QuadF* draw_region, const gfx::Rect* scissor_rect, base::Optional<SkAutoCanvasRestore>* auto_canvas_restore) { @@ -616,9 +631,6 @@ current_canvas_->clipRect(gfx::RectToSkRect(*scissor_rect)); } - gfx::Transform contents_device_transform = - current_frame()->window_matrix * current_frame()->projection_matrix * - quad_to_target_transform; contents_device_transform.FlattenTo2d(); SkMatrix sk_device_matrix; gfx::TransformToFlattenedSkMatrix(contents_device_transform, @@ -745,9 +757,15 @@ void SkiaRenderer::AddTileQuadToBatch(const TileDrawQuad* quad, const gfx::QuadF* draw_region) { - DCHECK(!MustDrawBatchedTileQuadsBeforeQuad(quad, draw_region)); - bool applyTransformAndScissor = ApplyTransformAndScissorToTileRect( - quad->shared_quad_state->quad_to_target_transform); + gfx::Transform contents_device_transform = + current_frame()->window_matrix * current_frame()->projection_matrix * + quad->shared_quad_state->quad_to_target_transform; + bool apply_transform_and_scissor = + ApplyTransformAndScissorToTileRect(contents_device_transform); + if (MustDrawBatchedTileQuads(quad, contents_device_transform, + apply_transform_and_scissor, draw_region)) + DrawBatchedTileQuads(); + if (batched_tiles_.empty()) { if (draw_region) { batched_tile_state_.draw_region = *draw_region; @@ -755,12 +773,11 @@ batched_tile_state_.blend_mode = quad->shared_quad_state->blend_mode; batched_tile_state_.is_nearest_neighbor = quad->nearest_neighbor; batched_tile_state_.has_draw_region = (draw_region != nullptr); - if (applyTransformAndScissor) { - batched_tile_state_.transform = gfx::Transform(); + if (apply_transform_and_scissor) { + batched_tile_state_.contents_device_transform = gfx::Transform(); batched_tile_state_.has_scissor_rect = false; } else { - batched_tile_state_.transform = - quad->shared_quad_state->quad_to_target_transform; + batched_tile_state_.contents_device_transform = contents_device_transform; batched_tile_state_.has_scissor_rect = is_scissor_enabled_; batched_tile_state_.scissor_rect = scissor_rect_; } @@ -791,8 +808,8 @@ aa_flags |= SkCanvas::kBottom_QuadAAFlag; } gfx::RectF quad_rect = gfx::RectF(quad->visible_rect); - if (applyTransformAndScissor) { - quad->shared_quad_state->quad_to_target_transform.TransformRect(&quad_rect); + if (apply_transform_and_scissor) { + contents_device_transform.TransformRect(&quad_rect); if (is_scissor_enabled_) { float left_inset = scissor_rect_.x() - quad_rect.x(); float top_inset = scissor_rect_.y() - quad_rect.y(); @@ -838,8 +855,8 @@ ? &batched_tile_state_.scissor_rect : nullptr; base::Optional<SkAutoCanvasRestore> auto_canvas_restore; - PrepareCanvasForDrawQuads(batched_tile_state_.transform, draw_region, - scissor_rect, &auto_canvas_restore); + PrepareCanvasForDrawQuads(batched_tile_state_.contents_device_transform, + draw_region, scissor_rect, &auto_canvas_restore); SkFilterQuality filter_quality = batched_tile_state_.is_nearest_neighbor ? kNone_SkFilterQuality
diff --git a/components/viz/service/display/skia_renderer.h b/components/viz/service/display/skia_renderer.h index 5ee1e12..b89fcc5 100644 --- a/components/viz/service/display/skia_renderer.h +++ b/components/viz/service/display/skia_renderer.h
@@ -93,7 +93,7 @@ void ClearFramebuffer(); void PrepareCanvasForDrawQuads( - const gfx::Transform& transform, + gfx::Transform contents_device_transform, const gfx::QuadF* draw_region, const gfx::Rect* scissor_rect, base::Optional<SkAutoCanvasRestore>* auto_canvas_restore); @@ -106,8 +106,10 @@ void DrawSolidColorQuad(const SolidColorDrawQuad* quad, SkPaint* paint); void DrawTextureQuad(const TextureDrawQuad* quad, SkPaint* paint); - bool MustDrawBatchedTileQuadsBeforeQuad(const DrawQuad* new_quad, - const gfx::QuadF* draw_region); + bool MustDrawBatchedTileQuads(const DrawQuad* new_quad, + const gfx::Transform& content_device_transform, + bool apply_transform_and_scissor, + const gfx::QuadF* draw_region); void AddTileQuadToBatch(const TileDrawQuad* quad, const gfx::QuadF* draw_region); void DrawBatchedTileQuads(); @@ -179,7 +181,7 @@ // State common to all tile quads in a batch struct BatchedTileState { - gfx::Transform transform; + gfx::Transform contents_device_transform; gfx::Rect scissor_rect; gfx::QuadF draw_region; SkBlendMode blend_mode;
diff --git a/components/viz/service/display_embedder/gpu_display_provider.cc b/components/viz/service/display_embedder/gpu_display_provider.cc index 9bc21a5a..72e6374 100644 --- a/components/viz/service/display_embedder/gpu_display_provider.cc +++ b/components/viz/service/display_embedder/gpu_display_provider.cc
@@ -18,17 +18,22 @@ #include "components/viz/service/display_embedder/gl_output_surface_offscreen.h" #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" #include "components/viz/service/display_embedder/skia_output_surface_impl.h" +#include "components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h" #include "components/viz/service/display_embedder/software_output_surface.h" #include "components/viz/service/display_embedder/viz_process_context_provider.h" #include "components/viz/service/gl/gpu_service_impl.h" #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h" #include "gpu/command_buffer/client/shared_memory_limits.h" #include "gpu/command_buffer/service/image_factory.h" +#include "gpu/command_buffer/service/mailbox_manager_factory.h" #include "gpu/config/gpu_finch_features.h" #include "gpu/ipc/command_buffer_task_executor.h" #include "gpu/ipc/common/surface_handle.h" #include "gpu/ipc/service/gpu_channel_manager_delegate.h" +#include "gpu/ipc/service/image_transport_surface.h" #include "ui/base/ui_base_switches.h" +#include "ui/gl/gl_context.h" +#include "ui/gl/init/gl_factory.h" #if defined(OS_WIN) #include "components/viz/service/display_embedder/gl_output_surface_win.h" @@ -124,15 +129,43 @@ output_surface = std::make_unique<SoftwareOutputSurface>( CreateSoftwareOutputDeviceForPlatform(surface_handle, display_client), synthetic_begin_frame_source); - } else if (renderer_settings.use_skia_renderer) { + } else if (renderer_settings.use_skia_renderer || + renderer_settings.use_skia_renderer_non_ddl) { #if defined(OS_MACOSX) || defined(OS_WIN) - // TODO(penghuang): Support DDL for all platforms. + // TODO(penghuang): Support SkiaRenderer for all platforms. NOTIMPLEMENTED(); return nullptr; #else - output_surface = std::make_unique<SkiaOutputSurfaceImpl>( - gpu_service_impl_, surface_handle, synthetic_begin_frame_source, - renderer_settings.show_overdraw_feedback); + if (renderer_settings.use_skia_renderer_non_ddl) { + DCHECK_EQ(gl::GetGLImplementation(), gl::kGLImplementationEGLGLES2) + << "SkiaRendererNonDDL is only supported with GLES2."; + auto gl_surface = gpu::ImageTransportSurface::CreateNativeSurface( + nullptr, surface_handle, gl::GLSurfaceFormat()); + if (!shared_context_state_) { + auto gl_share_group = base::MakeRefCounted<gl::GLShareGroup>(); + auto gl_context = gl::init::CreateGLContext( + gl_share_group.get(), gl_surface.get(), gl::GLContextAttribs()); + gl_context->MakeCurrent(gl_surface.get()); + shared_context_state_ = base::MakeRefCounted<gpu::SharedContextState>( + std::move(gl_share_group), gl_surface, std::move(gl_context), + false /* use_virtualized_gl_contexts */, base::DoNothing::Once(), + nullptr /* vulkan_context_provider */); + shared_context_state_->InitializeGrContext( + gpu::GpuDriverBugWorkarounds(), nullptr /* gr_shader_cache */); + mailbox_manager_ = gpu::gles2::CreateMailboxManager( + gpu_service_impl_->gpu_preferences()); + DCHECK(mailbox_manager_->UsesSync()); + } + output_surface = std::make_unique<SkiaOutputSurfaceImplNonDDL>( + std::move(gl_surface), shared_context_state_, mailbox_manager_.get(), + gpu_service_impl_->sync_point_manager(), + true /* need_swapbuffers_ack */); + + } else { + output_surface = std::make_unique<SkiaOutputSurfaceImpl>( + gpu_service_impl_, surface_handle, synthetic_begin_frame_source, + renderer_settings.show_overdraw_feedback); + } skia_output_surface = static_cast<SkiaOutputSurface*>(output_surface.get()); #endif } else {
diff --git a/components/viz/service/display_embedder/gpu_display_provider.h b/components/viz/service/display_embedder/gpu_display_provider.h index e28bca5..50935a0a 100644 --- a/components/viz/service/display_embedder/gpu_display_provider.h +++ b/components/viz/service/display_embedder/gpu_display_provider.h
@@ -27,6 +27,7 @@ class GpuChannelManagerDelegate; class GpuMemoryBufferManager; class ImageFactory; +class SharedContextState; } // namespace gpu namespace viz { @@ -88,6 +89,11 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + // A shared context which will be used on display compositor thread. + scoped_refptr<gpu::SharedContextState> shared_context_state_; + std::unique_ptr<gpu::MailboxManager> mailbox_manager_; + std::unique_ptr<gpu::SyncPointManager> sync_point_manager_; + const bool headless_; const bool wait_for_all_pipeline_stages_before_draw_;
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc index e4c5bc6..ea8deb7 100644 --- a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc +++ b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.cc
@@ -52,14 +52,17 @@ scoped_refptr<gl::GLSurface> gl_surface, scoped_refptr<gpu::SharedContextState> shared_context_state, gpu::MailboxManager* mailbox_manager, - gpu::SyncPointManager* sync_point_manager) + gpu::SyncPointManager* sync_point_manager, + bool need_swapbuffers_ack) : gl_surface_(std::move(gl_surface)), shared_context_state_(std::move(shared_context_state)), mailbox_manager_(mailbox_manager), sync_point_order_data_(sync_point_manager->CreateSyncPointOrderData()), sync_point_client_state_( CreateSyncPointClientState(sync_point_manager, - sync_point_order_data_->sequence_id())) {} + sync_point_order_data_->sequence_id())), + need_swapbuffers_ack_(need_swapbuffers_ack), + weak_ptr_factory_(this) {} SkiaOutputSurfaceImplNonDDL::~SkiaOutputSurfaceImplNonDDL() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); @@ -102,6 +105,15 @@ reshape_color_space_ = color_space; reshape_has_alpha_ = has_alpha; reshape_use_stencil_ = use_stencil; + + // Conversion to GLSurface's color space follows the same logic as in + // gl::GetGLColorSpace(). + gl::GLSurface::ColorSpace surface_color_space = + color_space.IsHDR() ? gl::GLSurface::ColorSpace::SCRGB_LINEAR + : gl::GLSurface::ColorSpace::UNSPECIFIED; + gl_surface_->Resize(size, device_scale_factor, surface_color_space, + has_alpha); + backing_framebuffer_object_ = gl_surface_->GetBackingFramebufferObject(); SkSurfaceProps surface_props = @@ -290,7 +302,10 @@ void SkiaOutputSurfaceImplNonDDL::SkiaSwapBuffers(OutputSurfaceFrame frame) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); gl_surface_->SwapBuffers( - base::DoNothing::Once<const gfx::PresentationFeedback&>()); + base::BindOnce(&SkiaOutputSurfaceImplNonDDL::BufferPresented, + weak_ptr_factory_.GetWeakPtr())); + if (need_swapbuffers_ack_) + client_->DidReceiveSwapBuffersAck(); } SkCanvas* SkiaOutputSurfaceImplNonDDL::BeginPaintRenderPass( @@ -419,6 +434,12 @@ metadata.resource_format, backend_texture); } +void SkiaOutputSurfaceImplNonDDL::BufferPresented( + const gfx::PresentationFeedback& feedback) { + if (need_swapbuffers_ack_) + client_->DidReceivePresentationFeedback(feedback); +} + void SkiaOutputSurfaceImplNonDDL::ContextLost() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); for (auto& observer : observers_)
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h index 11182acc..ca43df60 100644 --- a/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h +++ b/components/viz/service/display_embedder/skia_output_surface_impl_non_ddl.h
@@ -10,6 +10,7 @@ #include "base/containers/flat_map.h" #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/threading/thread_checker.h" #include "components/viz/service/display/skia_output_surface.h" @@ -17,6 +18,10 @@ #include "gpu/command_buffer/common/sync_token.h" #include "gpu/command_buffer/service/shared_context_state.h" +namespace gfx { +struct PresentationFeedback; +} + namespace gl { class GLSurface; } @@ -41,7 +46,8 @@ scoped_refptr<gl::GLSurface> gl_surface, scoped_refptr<gpu::SharedContextState> shared_context_state, gpu::MailboxManager* mailbox_manager, - gpu::SyncPointManager* sync_point_manager); + gpu::SyncPointManager* sync_point_manager, + bool need_swapbuffers_ack); ~SkiaOutputSurfaceImplNonDDL() override; // OutputSurface implementation: @@ -104,6 +110,7 @@ bool GetGrBackendTexture(const ResourceMetadata& metadata, GrBackendTexture* backend_texture); + void BufferPresented(const gfx::PresentationFeedback& feedback); void ContextLost(); uint64_t sync_fence_release_ = 0; @@ -114,6 +121,7 @@ gpu::MailboxManager* mailbox_manager_; scoped_refptr<gpu::SyncPointOrderData> sync_point_order_data_; scoped_refptr<gpu::SyncPointClientState> sync_point_client_state_; + const bool need_swapbuffers_ack_; uint32_t order_num_ = 0u; OutputSurfaceClient* client_ = nullptr; @@ -139,6 +147,8 @@ THREAD_CHECKER(thread_checker_); + base::WeakPtrFactory<SkiaOutputSurfaceImplNonDDL> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(SkiaOutputSurfaceImplNonDDL); };
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc index 5ad32d8e..96e240aa 100644 --- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc +++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -257,8 +257,10 @@ void FrameSinkManagerImpl::CreateVideoCapturer( mojom::FrameSinkVideoCapturerRequest request) { - video_capturers_.emplace( - std::make_unique<FrameSinkVideoCapturerImpl>(this, std::move(request))); + video_capturers_.emplace(std::make_unique<FrameSinkVideoCapturerImpl>( + this, std::move(request), + std::make_unique<media::VideoCaptureOracle>( + true /* enable_auto_throttling */))); } void FrameSinkManagerImpl::EvictSurfaces(
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc index 67542b76..48f14281 100644 --- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc +++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.cc
@@ -17,6 +17,7 @@ #include "build/build_config.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_result.h" +#include "components/viz/common/frame_sinks/copy_output_util.h" #include "components/viz/common/surfaces/local_surface_id.h" #include "components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_manager.h" #include "media/base/limits.h" @@ -51,16 +52,18 @@ FrameSinkVideoCapturerImpl::FrameSinkVideoCapturerImpl( FrameSinkVideoCapturerManager* frame_sink_manager, - mojom::FrameSinkVideoCapturerRequest request) + mojom::FrameSinkVideoCapturerRequest request, + std::unique_ptr<media::VideoCaptureOracle> oracle) : frame_sink_manager_(frame_sink_manager), binding_(this), copy_request_source_(base::UnguessableToken::Create()), clock_(base::DefaultTickClock::GetInstance()), - oracle_(true /* enable_auto_throttling */), + oracle_(std::move(oracle)), frame_pool_(kDesignLimitMaxFrames), - feedback_weak_factory_(&oracle_), + feedback_weak_factory_(oracle_.get()), capture_weak_factory_(this) { DCHECK(frame_sink_manager_); + DCHECK(oracle_); // Instantiate a default base::OneShotTimer instance. refresh_frame_retry_timer_.emplace(); @@ -165,7 +168,7 @@ } } - oracle_.SetMinCapturePeriod(min_capture_period); + oracle_->SetMinCapturePeriod(min_capture_period); if (refresh_frame_retry_timer_->IsRunning()) { // With the change in the minimum capture period, a pending refresh might // be ready to execute now (or sooner than it would have been). @@ -177,7 +180,7 @@ base::TimeDelta min_period) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - oracle_.SetMinSizeChangePeriod(min_period); + oracle_->SetMinSizeChangePeriod(min_period); } void FrameSinkVideoCapturerImpl::SetResolutionConstraints( @@ -197,12 +200,13 @@ return; } - oracle_.SetCaptureSizeConstraints(min_size, max_size, use_fixed_aspect_ratio); + oracle_->SetCaptureSizeConstraints(min_size, max_size, + use_fixed_aspect_ratio); RefreshEntireSourceSoon(); } void FrameSinkVideoCapturerImpl::SetAutoThrottlingEnabled(bool enabled) { - oracle_.SetAutoThrottlingEnabled(enabled); + oracle_->SetAutoThrottlingEnabled(enabled); } void FrameSinkVideoCapturerImpl::ChangeTarget( @@ -240,7 +244,7 @@ // Cancel any captures in-flight and any captured frames pending delivery. capture_weak_factory_.InvalidateWeakPtrs(); - oracle_.CancelAllCaptures(); + oracle_->CancelAllCaptures(); while (!delivery_queue_.empty()) { delivery_queue_.pop(); } @@ -286,8 +290,8 @@ // of frame captures that are expected to take place due to compositor update // events. However, the delay should not be excessively long either. Two frame // periods should be "just right." - return 2 * std::max(oracle_.estimated_frame_duration(), - oracle_.min_capture_period()); + return 2 * std::max(oracle_->estimated_frame_duration(), + oracle_->min_capture_period()); } void FrameSinkVideoCapturerImpl::RefreshEntireSourceSoon() { @@ -321,13 +325,13 @@ ScheduleRefreshFrame(); return; } - if (source_size != oracle_.source_size()) { - oracle_.SetSourceSize(source_size); + if (source_size != oracle_->source_size()) { + oracle_->SetSourceSize(source_size); dirty_rect_ = kMaxRect; } MaybeCaptureFrame(VideoCaptureOracle::kRefreshRequest, - gfx::Rect(oracle_.source_size()), clock_->NowTicks(), + gfx::Rect(oracle_->source_size()), clock_->NowTicks(), *resolved_target_->GetLastActivatedFrameMetadata()); } @@ -342,10 +346,10 @@ DCHECK(!expected_display_time.is_null()); DCHECK(resolved_target_); - if (frame_size == oracle_.source_size()) { + if (frame_size == oracle_->source_size()) { InvalidateRect(damage_rect); } else { - oracle_.SetSourceSize(frame_size); + oracle_->SetSourceSize(frame_size); dirty_rect_ = kMaxRect; } @@ -356,7 +360,7 @@ gfx::Size FrameSinkVideoCapturerImpl::GetSourceSize() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return oracle_.source_size(); + return oracle_->source_size(); } void FrameSinkVideoCapturerImpl::InvalidateRect(const gfx::Rect& rect) { @@ -401,7 +405,7 @@ DCHECK(resolved_target_); // Consult the oracle to determine whether this frame should be captured. - if (oracle_.ObserveEventAndDecideCapture(event, damage_rect, event_time)) { + if (oracle_->ObserveEventAndDecideCapture(event, damage_rect, event_time)) { // Regardless of the type of |event|, there is no longer a need for the // refresh frame retry timer to fire. The following is a no-op, if the timer // was not running. @@ -431,9 +435,10 @@ // are no changes in the source that need to be captured AND there are no // captures currently in-flight, attempt to resurrect the last frame from the // pool (and there is no need to capture anything new into the frame). - const OracleFrameNumber oracle_frame_number = oracle_.next_frame_number(); + const OracleFrameNumber oracle_frame_number = oracle_->next_frame_number(); const gfx::Size capture_size = - AdjustSizeForPixelFormat(oracle_.capture_size()); + AdjustSizeForPixelFormat(oracle_->capture_size()); + scoped_refptr<VideoFrame> frame; bool using_resurrected_frame = dirty_rect_.IsEmpty() && @@ -464,7 +469,7 @@ "gpu.capture", "PipelineLimited", TRACE_EVENT_SCOPE_THREAD, "trigger", VideoCaptureOracle::EventAsString(event), "atten_util_percent", base::saturated_cast<int>(utilization * 100.0f + 0.5f)); - oracle_.RecordWillNotCapture(utilization); + oracle_->RecordWillNotCapture(utilization); if (next_capture_frame_number_ == 0) { // The pool was unable to provide a buffer for the very first capture, and // so there is no expectation of recovery. Thus, treat this as a fatal @@ -489,13 +494,16 @@ // At this point, the capture is going to proceed. Populate the VideoFrame's // metadata, and notify the oracle. + const int64_t capture_frame_number = next_capture_frame_number_++; VideoFrameMetadata* const metadata = frame->metadata(); metadata->SetTimeTicks(VideoFrameMetadata::CAPTURE_BEGIN_TIME, clock_->NowTicks()); + metadata->SetInteger(VideoFrameMetadata::CAPTURE_COUNTER, + capture_frame_number); metadata->SetTimeDelta(VideoFrameMetadata::FRAME_DURATION, - oracle_.estimated_frame_duration()); + oracle_->estimated_frame_duration()); metadata->SetDouble(VideoFrameMetadata::FRAME_RATE, - 1.0 / oracle_.min_capture_period().InSecondsF()); + 1.0 / oracle_->min_capture_period().InSecondsF()); metadata->SetTimeTicks(VideoFrameMetadata::REFERENCE_TIME, event_time); metadata->SetDouble(VideoFrameMetadata::DEVICE_SCALE_FACTOR, frame_metadata.device_scale_factor); @@ -510,13 +518,12 @@ metadata->SetDouble(VideoFrameMetadata::TOP_CONTROLS_SHOWN_RATIO, frame_metadata.top_controls_shown_ratio); - oracle_.RecordCapture(utilization); - const int64_t frame_number = next_capture_frame_number_++; + oracle_->RecordCapture(utilization); TRACE_EVENT_ASYNC_BEGIN2("gpu.capture", "Capture", oracle_frame_number, - "frame_number", frame_number, "trigger", + "frame_number", capture_frame_number, "trigger", VideoCaptureOracle::EventAsString(event)); - const gfx::Size& source_size = oracle_.source_size(); + const gfx::Size& source_size = oracle_->source_size(); DCHECK(!source_size.IsEmpty()); gfx::Rect content_rect; if (pixel_format_ == media::PIXEL_FORMAT_I420) { @@ -527,6 +534,23 @@ content_rect = media::ComputeLetterboxRegion(frame->visible_rect(), source_size); } + + // Determine what rectangluar region has changed since the last captured + // frame. + gfx::Rect update_rect; + if (dirty_rect_ == kMaxRect || + frame->visible_rect() != last_frame_visible_rect_) { + // Source or VideoFrame size change: Assume entire frame (including + // letterboxed regions) have changed. + update_rect = last_frame_visible_rect_ = frame->visible_rect(); + } else { + update_rect = copy_output::ComputeResultRect( + dirty_rect_, gfx::Vector2d(source_size.width(), source_size.height()), + gfx::Vector2d(content_rect.width(), content_rect.height())); + update_rect.Offset(content_rect.OffsetFromOrigin()); + } + metadata->SetRect(media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, + update_rect); // Extreme edge-case: If somehow the source size is so tiny that the content // region becomes empty, just deliver a frame filled with black. if (content_rect.IsEmpty()) { @@ -538,7 +562,7 @@ frame->set_color_space(gfx::ColorSpace::CreateSRGB()); } dirty_rect_ = gfx::Rect(); - DidCaptureFrame(frame_number, oracle_frame_number, gfx::Rect(), + DidCaptureFrame(capture_frame_number, oracle_frame_number, gfx::Rect(), std::move(frame)); return; } @@ -546,7 +570,7 @@ // If the frame is a resurrected one, just deliver it since it already // contains the most up-to-date capture of the source content. if (using_resurrected_frame) { - DidCaptureFrame(frame_number, oracle_frame_number, content_rect, + DidCaptureFrame(capture_frame_number, oracle_frame_number, content_rect, std::move(frame)); return; } @@ -557,7 +581,7 @@ ? CopyOutputRequest::ResultFormat::I420_PLANES : CopyOutputRequest::ResultFormat::RGBA_BITMAP, base::BindOnce(&FrameSinkVideoCapturerImpl::DidCopyFrame, - capture_weak_factory_.GetWeakPtr(), frame_number, + capture_weak_factory_.GetWeakPtr(), capture_frame_number, oracle_frame_number, content_rect, VideoCaptureOverlay::MakeCombinedRenderer( GetOverlaysInOrder(), content_rect, frame->format()), @@ -589,14 +613,14 @@ } void FrameSinkVideoCapturerImpl::DidCopyFrame( - int64_t frame_number, + int64_t capture_frame_number, OracleFrameNumber oracle_frame_number, const gfx::Rect& content_rect, VideoCaptureOverlay::OnceRenderer overlay_renderer, scoped_refptr<VideoFrame> frame, std::unique_ptr<CopyOutputResult> result) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK_GE(frame_number, next_delivery_frame_number_); + DCHECK_GE(capture_frame_number, next_delivery_frame_number_); DCHECK(frame); DCHECK(result); @@ -656,17 +680,17 @@ AdjustSizeForPixelFormat(result->size()))); } - DidCaptureFrame(frame_number, oracle_frame_number, content_rect, + DidCaptureFrame(capture_frame_number, oracle_frame_number, content_rect, std::move(frame)); } void FrameSinkVideoCapturerImpl::DidCaptureFrame( - int64_t frame_number, + int64_t capture_frame_number, OracleFrameNumber oracle_frame_number, const gfx::Rect& content_rect, scoped_refptr<VideoFrame> frame) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK_GE(frame_number, next_delivery_frame_number_); + DCHECK_GE(capture_frame_number, next_delivery_frame_number_); if (frame) { frame->metadata()->SetTimeTicks(VideoFrameMetadata::CAPTURE_END_TIME, @@ -675,9 +699,10 @@ // Ensure frames are delivered in-order by using a min-heap, and only // deliver the next frame(s) in-sequence when they are found at the top. - delivery_queue_.emplace(frame_number, oracle_frame_number, content_rect, - std::move(frame)); - while (delivery_queue_.top().frame_number == next_delivery_frame_number_) { + delivery_queue_.emplace(capture_frame_number, oracle_frame_number, + content_rect, std::move(frame)); + while (delivery_queue_.top().capture_frame_number == + next_delivery_frame_number_) { auto& next = delivery_queue_.top(); MaybeDeliverFrame(next.oracle_frame_number, next.content_rect, std::move(next.frame)); @@ -699,7 +724,7 @@ // also rewrites the media timestamp in terms of the smooth flow of the // original source content. base::TimeTicks media_ticks; - if (!oracle_.CompleteCapture(oracle_frame_number, !!frame, &media_ticks)) { + if (!oracle_->CompleteCapture(oracle_frame_number, !!frame, &media_ticks)) { // The following is used by // chrome/browser/extension/api/cast_streaming/performance_test.cc, in // addition to the usual runtime tracing. @@ -747,7 +772,6 @@ info->visible_rect = frame->visible_rect(); DCHECK(frame->ColorSpace().IsValid()); // Ensure it was set by this point. info->color_space = frame->ColorSpace(); - const gfx::Rect update_rect = frame->visible_rect(); // Create an InFlightFrameDelivery for this frame, owned by its mojo binding. // It responds to the consumer's Done() notification by returning the video @@ -767,8 +791,8 @@ mojo::MakeRequest(&callbacks)); // Send the frame to the consumer. - consumer_->OnFrameCaptured(std::move(handle), std::move(info), update_rect, - content_rect, std::move(callbacks)); + consumer_->OnFrameCaptured(std::move(handle), std::move(info), content_rect, + std::move(callbacks)); } gfx::Size FrameSinkVideoCapturerImpl::AdjustSizeForPixelFormat( @@ -791,11 +815,11 @@ } FrameSinkVideoCapturerImpl::CapturedFrame::CapturedFrame( - int64_t frame_number, + int64_t capture_frame_number, OracleFrameNumber oracle_frame_number, const gfx::Rect& content_rect, scoped_refptr<media::VideoFrame> frame) - : frame_number(frame_number), + : capture_frame_number(capture_frame_number), oracle_frame_number(oracle_frame_number), content_rect(content_rect), frame(std::move(frame)) {} @@ -809,7 +833,7 @@ const FrameSinkVideoCapturerImpl::CapturedFrame& other) const { // Reverse the sort order; so std::priority_queue<CapturedFrame> becomes a // min-heap instead of a max-heap. - return other.frame_number < frame_number; + return other.capture_frame_number < capture_frame_number; } } // namespace viz
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h index 69f25da..15f79e5 100644 --- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h +++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h
@@ -74,7 +74,8 @@ // Mojo message pipe endpoint in |request|, but |request| may be empty for // unit testing. FrameSinkVideoCapturerImpl(FrameSinkVideoCapturerManager* frame_sink_manager, - mojom::FrameSinkVideoCapturerRequest request); + mojom::FrameSinkVideoCapturerRequest request, + std::unique_ptr<media::VideoCaptureOracle> oracle); ~FrameSinkVideoCapturerImpl() final; @@ -186,7 +187,7 @@ // Extracts the image data from the copy output |result|, populating the // |content_rect| region of a [possibly letterboxed] video |frame|. - void DidCopyFrame(int64_t frame_number, + void DidCopyFrame(int64_t capture_frame_number, OracleFrameNumber oracle_frame_number, const gfx::Rect& content_rect, VideoCaptureOverlay::OnceRenderer overlay_renderer, @@ -196,7 +197,7 @@ // Places the frame in the |delivery_queue_| and calls MaybeDeliverFrame(), // one frame at a time, in-order. |frame| may be null to indicate a // completed, but unsuccessful capture. - void DidCaptureFrame(int64_t frame_number, + void DidCaptureFrame(int64_t capture_frame_number, OracleFrameNumber oracle_frame_number, const gfx::Rect& content_rect, scoped_refptr<media::VideoFrame> frame); @@ -235,7 +236,7 @@ // Models current content change/draw behavior and proposes when to capture // frames, and at what size and frame rate. - media::VideoCaptureOracle oracle_; + const std::unique_ptr<media::VideoCaptureOracle> oracle_; // The target requested by the client, as provided in the last call to // ChangeTarget(). @@ -253,6 +254,10 @@ // captured. gfx::Rect dirty_rect_; + // Allows determining whether or not the frame size has changed since the last + // captured frame. + gfx::Rect last_frame_visible_rect_; + // These are sequence counters used to ensure that the frames are being // delivered in the same order they are captured. int64_t next_capture_frame_number_ = 0; @@ -275,11 +280,11 @@ // A queue of captured frames pending delivery. This queue is used to re-order // frames, if they should happen to be captured out-of-order. struct CapturedFrame { - int64_t frame_number; + int64_t capture_frame_number; OracleFrameNumber oracle_frame_number; gfx::Rect content_rect; scoped_refptr<media::VideoFrame> frame; - CapturedFrame(int64_t frame_number, + CapturedFrame(int64_t capture_frame_number, OracleFrameNumber oracle_frame_number, const gfx::Rect& content_rect, scoped_refptr<media::VideoFrame> frame);
diff --git a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc index e718f6d..073681e 100644 --- a/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc +++ b/components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl_unittest.cc
@@ -17,6 +17,7 @@ #include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_result.h" +#include "components/viz/common/frame_sinks/copy_output_util.h" #include "components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_manager.h" #include "media/base/limits.h" #include "media/base/video_util.h" @@ -72,20 +73,23 @@ constexpr base::TimeDelta kVsyncInterval = base::TimeDelta::FromSecondsD(1.0 / 60.0); -// The size of the compositor frame sink's Surface. -constexpr gfx::Size kSourceSize = gfx::Size(100, 100); - -// The size of the VideoFrames produced by the capturer. -constexpr gfx::Size kCaptureSize = gfx::Size(32, 18); +const struct SizeSet { + // The size of the compositor frame sink's Surface. + gfx::Size source_size; + // The size of the VideoFrames produced by the capturer. + gfx::Size capture_size; + // The location of the letterboxed content within each VideoFrame. All pixels + // outside of this region should be black. + gfx::Rect expected_content_rect; +} kSizeSets[3] = { + {gfx::Size(100, 100), gfx::Size(32, 18), gfx::Rect(6, 0, 18, 18)}, + {gfx::Size(64, 18), gfx::Size(32, 18), gfx::Rect(0, 4, 32, 8)}, + {gfx::Size(64, 18), gfx::Size(64, 18), gfx::Rect(0, 0, 64, 18)}}; constexpr float kDefaultDeviceScaleFactor = 1.f; constexpr float kDefaultPageScaleFactor = 1.f; constexpr gfx::Vector2dF kDefaultRootScrollOffset = gfx::Vector2dF(0, 0); -// The location of the letterboxed content within each VideoFrame. All pixels -// outside of this region should be black. -constexpr gfx::Rect kContentRect = gfx::Rect(6, 0, 18, 18); - struct YUVColor { uint8_t y; uint8_t u; @@ -110,10 +114,7 @@ public: MockConsumer() : binding_(this) {} - MOCK_METHOD3(OnFrameCapturedMock, - void(scoped_refptr<VideoFrame> frame, - const gfx::Rect& update_rect, - mojom::FrameSinkVideoConsumerFrameCallbacks* callbacks)); + MOCK_METHOD0(OnFrameCapturedMock, void()); MOCK_METHOD0(OnStopped, void()); int num_frames_received() const { return frames_.size(); } @@ -135,19 +136,17 @@ void OnFrameCaptured( base::ReadOnlySharedMemoryRegion data, media::mojom::VideoFrameInfoPtr info, - const gfx::Rect& update_rect, - const gfx::Rect& content_rect, + const gfx::Rect& expected_content_rect, mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) final { ASSERT_TRUE(data.IsValid()); const auto required_bytes_to_hold_planes = static_cast<uint32_t>(info->coded_size.GetArea() * 3 / 2); ASSERT_LE(required_bytes_to_hold_planes, data.GetSize()); ASSERT_TRUE(info); - EXPECT_EQ(gfx::Rect(kCaptureSize), update_rect); ASSERT_TRUE(callbacks.get()); // Map the shared memory buffer and re-constitute a VideoFrame instance - // around it for analysis by OnFrameCapturedMock(). + // around it for analysis via TakeFrame(). base::ReadOnlySharedMemoryMapping mapping = data.Map(); ASSERT_TRUE(mapping.IsValid()); ASSERT_LE( @@ -163,9 +162,10 @@ frame->metadata()->MergeInternalValuesFrom(info->metadata); if (info->color_space.has_value()) frame->set_color_space(info->color_space.value()); + frame->AddDestructionObserver(base::BindOnce( [](base::ReadOnlySharedMemoryMapping mapping) {}, std::move(mapping))); - OnFrameCapturedMock(frame, update_rect, callbacks.get()); + OnFrameCapturedMock(); frames_.push_back(std::move(frame)); done_callbacks_.push_back( @@ -216,7 +216,7 @@ class FakeCapturableFrameSink : public CapturableFrameSink { public: - FakeCapturableFrameSink() { + FakeCapturableFrameSink() : size_set_(kSizeSets[0]) { metadata_.root_scroll_offset = kDefaultRootScrollOffset; metadata_.page_scale_factor = kDefaultPageScaleFactor; metadata_.device_scale_factor = kDefaultDeviceScaleFactor; @@ -236,15 +236,16 @@ client_ = nullptr; } - gfx::Size GetActiveFrameSize() override { return kSourceSize; } + gfx::Size GetActiveFrameSize() override { return source_size(); } void RequestCopyOfOutput( const LocalSurfaceId& local_surface_id, std::unique_ptr<CopyOutputRequest> request) override { EXPECT_EQ(CopyOutputResult::Format::I420_PLANES, request->result_format()); EXPECT_NE(base::UnguessableToken(), request->source()); - EXPECT_EQ(gfx::Rect(kSourceSize), request->area()); - EXPECT_EQ(gfx::Rect(kContentRect.size()), request->result_selection()); + EXPECT_EQ(gfx::Rect(size_set_.source_size), request->area()); + EXPECT_EQ(gfx::Rect(size_set_.expected_content_rect.size()), + request->result_selection()); auto result = std::make_unique<SolidColorI420Result>( request->result_selection(), color_); @@ -260,6 +261,10 @@ return &metadata_; } + const gfx::Size& source_size() const { return size_set_.source_size; } + + void set_size_set(const SizeSet& size_set) { size_set_ = size_set; } + void set_metadata(const CompositorFrameMetadata& metadata) { metadata_ = metadata.Clone(); } @@ -277,30 +282,66 @@ private: CapturableFrameSink::Client* client_ = nullptr; YUVColor color_ = {0xde, 0xad, 0xbf}; + SizeSet size_set_; CompositorFrameMetadata metadata_; std::vector<base::OnceClosure> results_; }; +class InstrumentedVideoCaptureOracle : public media::VideoCaptureOracle { + public: + explicit InstrumentedVideoCaptureOracle(bool enable_auto_throttling) + : media::VideoCaptureOracle(enable_auto_throttling), + return_false_on_complete_capture_(false) {} + + bool CompleteCapture(int frame_number, + bool capture_was_successful, + base::TimeTicks* frame_timestamp) override { + capture_was_successful &= !return_false_on_complete_capture_; + return media::VideoCaptureOracle::CompleteCapture( + frame_number, capture_was_successful, frame_timestamp); + } + + void set_return_false_on_complete_capture(bool should_return_false) { + return_false_on_complete_capture_ = should_return_false; + } + + gfx::Size capture_size() const override { + if (forced_capture_size_.has_value()) + return forced_capture_size_.value(); + return media::VideoCaptureOracle::capture_size(); + } + + void set_forced_capture_size(base::Optional<gfx::Size> size) { + forced_capture_size_ = size; + } + + private: + bool return_false_on_complete_capture_; + base::Optional<gfx::Size> forced_capture_size_; +}; + // Matcher that returns true if the content region of a letterboxed VideoFrame // is filled with the given color, and black everywhere else. -MATCHER_P(IsLetterboxedFrame, color, "") { +MATCHER_P2(IsLetterboxedFrame, color, content_rect, "") { if (!arg) { return false; } const VideoFrame& frame = *arg; - const auto IsLetterboxedPlane = [&frame](int plane, uint8_t color) { - gfx::Rect content_rect = kContentRect; + const gfx::Rect kContentRect = content_rect; + const auto IsLetterboxedPlane = [&frame, kContentRect](int plane, + uint8_t color) { + gfx::Rect content_rect_copy = kContentRect; if (plane != VideoFrame::kYPlane) { - content_rect = - gfx::Rect(content_rect.x() / 2, content_rect.y() / 2, - content_rect.width() / 2, content_rect.height() / 2); + content_rect_copy = gfx::Rect( + content_rect_copy.x() / 2, content_rect_copy.y() / 2, + content_rect_copy.width() / 2, content_rect_copy.height() / 2); } for (int row = 0; row < frame.rows(plane); ++row) { const uint8_t* p = frame.visible_data(plane) + row * frame.stride(plane); for (int col = 0; col < frame.row_bytes(plane); ++col) { - if (content_rect.Contains(gfx::Point(col, row))) { + if (content_rect_copy.Contains(gfx::Point(col, row))) { if (p[col] != color) { return false; } @@ -323,9 +364,14 @@ class FrameSinkVideoCapturerTest : public testing::Test { public: - FrameSinkVideoCapturerTest() - : capturer_(&frame_sink_manager_, - mojom::FrameSinkVideoCapturerRequest()) {} + FrameSinkVideoCapturerTest() : size_set_(kSizeSets[0]) { + auto oracle = std::make_unique<InstrumentedVideoCaptureOracle>( + true /* enable_auto_throttling */); + oracle_ = oracle.get(); + capturer_ = std::make_unique<FrameSinkVideoCapturerImpl>( + &frame_sink_manager_, mojom::FrameSinkVideoCapturerRequest(), + std::move(oracle)); + } void SetUp() override { // Override the capturer's TickClock with a virtual clock managed by a @@ -334,44 +380,45 @@ base::Time::Now(), base::TimeTicks() + base::TimeDelta::FromSeconds(1), base::TestMockTimeTaskRunner::Type::kStandalone); start_time_ = task_runner_->NowTicks(); - capturer_.clock_ = task_runner_->GetMockTickClock(); + capturer_->clock_ = task_runner_->GetMockTickClock(); // Replace the retry timer with one that uses this test's fake clock and // task runner. - capturer_.refresh_frame_retry_timer_.emplace( + capturer_->refresh_frame_retry_timer_.emplace( task_runner_->GetMockTickClock()); - capturer_.refresh_frame_retry_timer_->SetTaskRunner(task_runner_); + capturer_->refresh_frame_retry_timer_->SetTaskRunner(task_runner_); // Before setting the format, ensure the defaults are in-place. Then, for // these tests, set a specific format and color space. ASSERT_EQ(FrameSinkVideoCapturerImpl::kDefaultPixelFormat, - capturer_.pixel_format_); + capturer_->pixel_format_); ASSERT_EQ(FrameSinkVideoCapturerImpl::kDefaultColorSpace, - capturer_.color_space_); - capturer_.SetFormat(media::PIXEL_FORMAT_I420, - gfx::ColorSpace::CreateREC709()); - ASSERT_EQ(media::PIXEL_FORMAT_I420, capturer_.pixel_format_); - ASSERT_EQ(gfx::ColorSpace::CreateREC709(), capturer_.color_space_); + capturer_->color_space_); + capturer_->SetFormat(media::PIXEL_FORMAT_I420, + gfx::ColorSpace::CreateREC709()); + ASSERT_EQ(media::PIXEL_FORMAT_I420, capturer_->pixel_format_); + ASSERT_EQ(gfx::ColorSpace::CreateREC709(), capturer_->color_space_); // Set min capture period as small as possible so that the // media::VideoCapturerOracle used by the capturer will want to capture // every composited frame. The capturer will override the too-small value of // zero with a value based on media::limits::kMaxFramesPerSecond. - capturer_.SetMinCapturePeriod(base::TimeDelta()); - ASSERT_LT(base::TimeDelta(), capturer_.oracle_.min_capture_period()); + capturer_->SetMinCapturePeriod(base::TimeDelta()); + ASSERT_LT(base::TimeDelta(), oracle_->min_capture_period()); - capturer_.SetResolutionConstraints(kCaptureSize, kCaptureSize, false); + capturer_->SetResolutionConstraints(size_set_.capture_size, + size_set_.capture_size, false); } void TearDown() override { task_runner_->ClearPendingTasks(); } void StartCapture(MockConsumer* consumer) { - capturer_.Start(consumer->BindVideoConsumer()); + capturer_->Start(consumer->BindVideoConsumer()); PropagateMojoTasks(); } void StopCapture() { - capturer_.Stop(); + capturer_->Stop(); PropagateMojoTasks(); } @@ -385,7 +432,18 @@ task_runner_->FastForwardBy(GetNextVsync() - task_runner_->NowTicks()); } + void SwitchToSizeSet(const SizeSet& size_set) { + size_set_ = size_set; + oracle_->set_forced_capture_size(size_set.capture_size); + frame_sink_.set_size_set(size_set); + capturer_->SetResolutionConstraints(size_set_.capture_size, + size_set_.capture_size, false); + } + + const SizeSet& size_set() { return size_set_; } + void NotifyFrameDamaged( + gfx::Rect damage_rect, float device_scale_factor = kDefaultDeviceScaleFactor, float page_scale_factor = kDefaultPageScaleFactor, gfx::Vector2dF root_scroll_offset = kDefaultRootScrollOffset) { @@ -397,30 +455,32 @@ frame_sink_.set_metadata(metadata); - capturer_.OnFrameDamaged(kSourceSize, gfx::Rect(kSourceSize), - GetNextVsync(), metadata); + capturer_->OnFrameDamaged(frame_sink_.source_size(), damage_rect, + GetNextVsync(), metadata); } void NotifyTargetWentAway() { - capturer_.OnTargetWillGoAway(); + capturer_->OnTargetWillGoAway(); PropagateMojoTasks(); } bool IsRefreshRetryTimerRunning() { - return capturer_.refresh_frame_retry_timer_->IsRunning(); + return capturer_->refresh_frame_retry_timer_->IsRunning(); } void AdvanceClockForRefreshTimer() { - task_runner_->FastForwardBy(capturer_.GetDelayBeforeNextRefreshAttempt()); + task_runner_->FastForwardBy(capturer_->GetDelayBeforeNextRefreshAttempt()); PropagateMojoTasks(); } protected: + SizeSet size_set_; scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; base::TimeTicks start_time_; MockFrameSinkManager frame_sink_manager_; FakeCapturableFrameSink frame_sink_; - FrameSinkVideoCapturerImpl capturer_; + std::unique_ptr<FrameSinkVideoCapturerImpl> capturer_; + InstrumentedVideoCaptureOracle* oracle_; }; // Tests that the capturer attaches to a frame sink immediately, in the case @@ -429,10 +489,10 @@ EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId)) .WillRepeatedly(Return(&frame_sink_)); - EXPECT_EQ(FrameSinkId(), capturer_.requested_target()); - capturer_.ChangeTarget(kFrameSinkId); - EXPECT_EQ(kFrameSinkId, capturer_.requested_target()); - EXPECT_EQ(&capturer_, frame_sink_.attached_client()); + EXPECT_EQ(FrameSinkId(), capturer_->requested_target()); + capturer_->ChangeTarget(kFrameSinkId); + EXPECT_EQ(kFrameSinkId, capturer_->requested_target()); + EXPECT_EQ(capturer_.get(), frame_sink_.attached_client()); } // Tests that the capturer attaches to a frame sink later, in the case where the @@ -441,13 +501,13 @@ EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId)) .WillRepeatedly(Return(nullptr)); - EXPECT_EQ(FrameSinkId(), capturer_.requested_target()); - capturer_.ChangeTarget(kFrameSinkId); - EXPECT_EQ(kFrameSinkId, capturer_.requested_target()); + EXPECT_EQ(FrameSinkId(), capturer_->requested_target()); + capturer_->ChangeTarget(kFrameSinkId); + EXPECT_EQ(kFrameSinkId, capturer_->requested_target()); EXPECT_EQ(nullptr, frame_sink_.attached_client()); - capturer_.SetResolvedTarget(&frame_sink_); - EXPECT_EQ(&capturer_, frame_sink_.attached_client()); + capturer_->SetResolvedTarget(&frame_sink_); + EXPECT_EQ(capturer_.get(), frame_sink_.attached_client()); } // Tests that no initial frame is sent after Start() is called until after the @@ -457,7 +517,7 @@ .WillRepeatedly(Return(&frame_sink_)); MockConsumer consumer; - EXPECT_CALL(consumer, OnFrameCapturedMock(_, _, _)).Times(0); + EXPECT_CALL(consumer, OnFrameCapturedMock()).Times(0); EXPECT_CALL(consumer, OnStopped()).Times(1); StartCapture(&consumer); @@ -477,7 +537,7 @@ // Now, set the target. As it resolves, the capturer will immediately attempt // a refresh capture, which will cancel the timer and trigger a copy request. - capturer_.ChangeTarget(kFrameSinkId); + capturer_->ChangeTarget(kFrameSinkId); EXPECT_EQ(1, frame_sink_.num_copy_results()); EXPECT_FALSE(IsRefreshRetryTimerRunning()); @@ -493,14 +553,14 @@ EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId)) .WillRepeatedly(Return(&frame_sink_)); - capturer_.ChangeTarget(kFrameSinkId); + capturer_->ChangeTarget(kFrameSinkId); EXPECT_FALSE(IsRefreshRetryTimerRunning()); MockConsumer consumer; const int num_refresh_frames = 1; const int num_update_frames = 3 * FrameSinkVideoCapturerImpl::kDesignLimitMaxFrames; - EXPECT_CALL(consumer, OnFrameCapturedMock(_, _, _)) + EXPECT_CALL(consumer, OnFrameCapturedMock()) .Times(num_refresh_frames + num_update_frames); EXPECT_CALL(consumer, OnStopped()).Times(1); StartCapture(&consumer); @@ -515,7 +575,8 @@ frame_sink_.SendCopyOutputResult(0); ASSERT_EQ(num_refresh_frames, consumer.num_frames_received()); EXPECT_THAT(consumer.TakeFrame(0), - IsLetterboxedFrame(YUVColor{0x80, 0x80, 0x80})); + IsLetterboxedFrame(YUVColor{0x80, 0x80, 0x80}, + size_set().expected_content_rect)); consumer.SendDoneNotification(0); // Drive the capturer pipeline for a series of frame composites. @@ -536,7 +597,7 @@ task_runner_->FastForwardBy(kVsyncInterval / 4); const base::TimeTicks expected_capture_begin_time = task_runner_->NowTicks(); - NotifyFrameDamaged(); + NotifyFrameDamaged(gfx::Rect(size_set().source_size)); // The frame sink should have received a CopyOutputRequest. Simulate a short // pause before the result is sent back to the capturer, and the capturer @@ -550,9 +611,10 @@ // Verify the frame is the right size, has the right content, and has // required metadata set. const scoped_refptr<VideoFrame> frame = consumer.TakeFrame(i); - EXPECT_THAT(frame, IsLetterboxedFrame(color)); - EXPECT_EQ(kCaptureSize, frame->coded_size()); - EXPECT_EQ(gfx::Rect(kCaptureSize), frame->visible_rect()); + EXPECT_THAT(frame, + IsLetterboxedFrame(color, size_set().expected_content_rect)); + EXPECT_EQ(size_set().capture_size, frame->coded_size()); + EXPECT_EQ(gfx::Rect(size_set().capture_size), frame->visible_rect()); EXPECT_LT(last_timestamp, frame->timestamp()); last_timestamp = frame->timestamp(); const VideoFrameMetadata* metadata = frame->metadata(); @@ -595,7 +657,7 @@ EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId)) .WillRepeatedly(Return(&frame_sink_)); - capturer_.ChangeTarget(kFrameSinkId); + capturer_->ChangeTarget(kFrameSinkId); NiceMock<MockConsumer> consumer; StartCapture(&consumer); @@ -608,7 +670,7 @@ int num_frames = FrameSinkVideoCapturerImpl::kDesignLimitMaxFrames; for (int i = num_refresh_frames; i < num_frames; ++i) { AdvanceClockToNextVsync(); - NotifyFrameDamaged(); + NotifyFrameDamaged(gfx::Rect(size_set().source_size)); // The oracle should not be rejecting captures caused by compositor updates. ASSERT_FALSE(IsRefreshRetryTimerRunning()); } @@ -619,7 +681,7 @@ // scheduled to account for the capture of changed content that could not take // place. AdvanceClockToNextVsync(); - NotifyFrameDamaged(); + NotifyFrameDamaged(gfx::Rect(size_set().source_size)); ASSERT_EQ(num_frames, frame_sink_.num_copy_results()); EXPECT_TRUE(IsRefreshRetryTimerRunning()); @@ -629,7 +691,7 @@ frame_sink_.SendCopyOutputResult(0); ASSERT_EQ(1, consumer.num_frames_received()); AdvanceClockToNextVsync(); - NotifyFrameDamaged(); + NotifyFrameDamaged(gfx::Rect(size_set().source_size)); ASSERT_EQ(num_frames, frame_sink_.num_copy_results()); EXPECT_TRUE(IsRefreshRetryTimerRunning()); @@ -640,7 +702,7 @@ EXPECT_TRUE(consumer.TakeFrame(0)); consumer.SendDoneNotification(0); AdvanceClockToNextVsync(); - NotifyFrameDamaged(); + NotifyFrameDamaged(gfx::Rect(size_set().source_size)); ++num_frames; ASSERT_EQ(num_frames, frame_sink_.num_copy_results()); EXPECT_FALSE(IsRefreshRetryTimerRunning()); @@ -649,7 +711,7 @@ // because the pipeline became saturated again. Once again, the refresh timer // should be started to account for the need to capture at some future point. AdvanceClockToNextVsync(); - NotifyFrameDamaged(); + NotifyFrameDamaged(gfx::Rect(size_set().source_size)); ASSERT_EQ(num_frames, frame_sink_.num_copy_results()); EXPECT_TRUE(IsRefreshRetryTimerRunning()); @@ -662,7 +724,7 @@ } ASSERT_EQ(frame_sink_.num_copy_results(), consumer.num_frames_received()); AdvanceClockToNextVsync(); - NotifyFrameDamaged(); + NotifyFrameDamaged(gfx::Rect(size_set().source_size)); ASSERT_EQ(num_frames, frame_sink_.num_copy_results()); EXPECT_TRUE(IsRefreshRetryTimerRunning()); @@ -674,7 +736,7 @@ consumer.SendDoneNotification(i); } AdvanceClockToNextVsync(); - NotifyFrameDamaged(); + NotifyFrameDamaged(gfx::Rect(size_set().source_size)); ++num_frames; ASSERT_EQ(num_frames, frame_sink_.num_copy_results()); frame_sink_.SendCopyOutputResult(frame_sink_.num_copy_results() - 1); @@ -693,7 +755,7 @@ EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId)) .WillRepeatedly(Return(&frame_sink_)); - capturer_.ChangeTarget(kFrameSinkId); + capturer_->ChangeTarget(kFrameSinkId); NiceMock<MockConsumer> consumer; StartCapture(&consumer); @@ -710,7 +772,7 @@ static_cast<uint8_t>((i << 4) + 0x20)}); frame_sink_.SetCopyOutputColor(colors.back()); AdvanceClockToNextVsync(); - NotifyFrameDamaged(); + NotifyFrameDamaged(gfx::Rect(size_set().source_size)); } ASSERT_EQ(num_frames, frame_sink_.num_copy_results()); @@ -719,19 +781,24 @@ // each video frame is correct. frame_sink_.SendCopyOutputResult(0); ASSERT_EQ(1, consumer.num_frames_received()); - EXPECT_THAT(consumer.TakeFrame(0), IsLetterboxedFrame(colors[0])); + EXPECT_THAT(consumer.TakeFrame(0), + IsLetterboxedFrame(colors[0], size_set().expected_content_rect)); frame_sink_.SendCopyOutputResult(2); ASSERT_EQ(1, consumer.num_frames_received()); // Waiting for frame 1. frame_sink_.SendCopyOutputResult(3); ASSERT_EQ(1, consumer.num_frames_received()); // Still waiting for frame 1. frame_sink_.SendCopyOutputResult(1); ASSERT_EQ(4, consumer.num_frames_received()); // Sent frames 1, 2, and 3. - EXPECT_THAT(consumer.TakeFrame(1), IsLetterboxedFrame(colors[1])); - EXPECT_THAT(consumer.TakeFrame(2), IsLetterboxedFrame(colors[2])); - EXPECT_THAT(consumer.TakeFrame(3), IsLetterboxedFrame(colors[3])); + EXPECT_THAT(consumer.TakeFrame(1), + IsLetterboxedFrame(colors[1], size_set().expected_content_rect)); + EXPECT_THAT(consumer.TakeFrame(2), + IsLetterboxedFrame(colors[2], size_set().expected_content_rect)); + EXPECT_THAT(consumer.TakeFrame(3), + IsLetterboxedFrame(colors[3], size_set().expected_content_rect)); frame_sink_.SendCopyOutputResult(4); ASSERT_EQ(5, consumer.num_frames_received()); - EXPECT_THAT(consumer.TakeFrame(4), IsLetterboxedFrame(colors[4])); + EXPECT_THAT(consumer.TakeFrame(4), + IsLetterboxedFrame(colors[4], size_set().expected_content_rect)); StopCapture(); } @@ -745,11 +812,11 @@ EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId)) .WillRepeatedly(Return(&frame_sink_)); - capturer_.ChangeTarget(kFrameSinkId); + capturer_->ChangeTarget(kFrameSinkId); // Start capturing to the first consumer. MockConsumer consumer; - EXPECT_CALL(consumer, OnFrameCapturedMock(_, _, _)).Times(2); + EXPECT_CALL(consumer, OnFrameCapturedMock()).Times(2); EXPECT_CALL(consumer, OnStopped()).Times(1); StartCapture(&consumer); // With the start, an immediate refresh should have occurred. @@ -762,7 +829,7 @@ for (int i = num_refresh_frames; i < num_copy_requests; ++i) { SCOPED_TRACE(testing::Message() << "frame #" << i); AdvanceClockToNextVsync(); - NotifyFrameDamaged(); + NotifyFrameDamaged(gfx::Rect(size_set().source_size)); } ASSERT_EQ(num_copy_requests, frame_sink_.num_copy_results()); @@ -772,7 +839,8 @@ SCOPED_TRACE(testing::Message() << "frame #" << i); frame_sink_.SendCopyOutputResult(i); ASSERT_EQ(i + 1, consumer.num_frames_received()); - EXPECT_THAT(consumer.TakeFrame(i), IsLetterboxedFrame(color1)); + EXPECT_THAT(consumer.TakeFrame(i), + IsLetterboxedFrame(color1, size_set().expected_content_rect)); } // Stopping capture should cancel the remaning copy requests. @@ -783,7 +851,7 @@ frame_sink_.SetCopyOutputColor(color2); MockConsumer consumer2; const int num_captures_for_second_consumer = 3; - EXPECT_CALL(consumer2, OnFrameCapturedMock(_, _, _)) + EXPECT_CALL(consumer2, OnFrameCapturedMock()) .Times(num_captures_for_second_consumer); EXPECT_CALL(consumer2, OnStopped()).Times(1); StartCapture(&consumer2); @@ -809,7 +877,7 @@ if (i == 0) { // Expect that advancing the clock caused the refresh timer to fire. } else { - NotifyFrameDamaged(); + NotifyFrameDamaged(gfx::Rect(size_set().source_size)); } ++num_copy_requests; ASSERT_EQ(num_copy_requests, frame_sink_.num_copy_results()); @@ -818,7 +886,7 @@ ++num_completed_captures; ASSERT_EQ(num_completed_captures, consumer2.num_frames_received()); EXPECT_THAT(consumer2.TakeFrame(consumer2.num_frames_received() - 1), - IsLetterboxedFrame(color2)); + IsLetterboxedFrame(color2, size_set().expected_content_rect)); } StopCapture(); @@ -831,12 +899,12 @@ EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId)) .WillRepeatedly(Return(&frame_sink_)); - capturer_.ChangeTarget(kFrameSinkId); + capturer_->ChangeTarget(kFrameSinkId); MockConsumer consumer; const int num_refresh_frames = 2; // Initial, plus later refresh. const int num_update_frames = 3; - EXPECT_CALL(consumer, OnFrameCapturedMock(_, _, _)) + EXPECT_CALL(consumer, OnFrameCapturedMock()) .Times(num_refresh_frames + num_update_frames); EXPECT_CALL(consumer, OnStopped()).Times(1); StartCapture(&consumer); @@ -853,7 +921,7 @@ int num_frames = 1 + num_update_frames; for (int i = 1; i < num_frames; ++i) { AdvanceClockToNextVsync(); - NotifyFrameDamaged(); + NotifyFrameDamaged(gfx::Rect(size_set().source_size)); ASSERT_EQ(i + 1, frame_sink_.num_copy_results()); ASSERT_FALSE(IsRefreshRetryTimerRunning()); frame_sink_.SendCopyOutputResult(i); @@ -863,7 +931,7 @@ // Request a refresh frame. Because the refresh request was made just after // the last frame capture, the refresh retry timer should be started. - capturer_.RequestRefreshFrame(); + capturer_->RequestRefreshFrame(); ASSERT_EQ(num_frames, frame_sink_.num_copy_results()); EXPECT_TRUE(IsRefreshRetryTimerRunning()); @@ -886,13 +954,13 @@ TEST_F(FrameSinkVideoCapturerTest, CompositorFrameMetadataReachesConsumer) { EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId)) .WillRepeatedly(Return(&frame_sink_)); - capturer_.ChangeTarget(kFrameSinkId); + capturer_->ChangeTarget(kFrameSinkId); MockConsumer consumer; // Initial refresh frame for starting capture, plus later refresh. const int num_refresh_frames = 2; const int num_update_frames = 1; - EXPECT_CALL(consumer, OnFrameCapturedMock(_, _, _)) + EXPECT_CALL(consumer, OnFrameCapturedMock()) .Times(num_refresh_frames + num_update_frames); EXPECT_CALL(consumer, OnStopped()).Times(1); StartCapture(&consumer); @@ -917,10 +985,11 @@ // Notify frame damage with new metadata, and expect that the refresh frame // is delivered to the consumer with this new metadata. AdvanceClockToNextVsync(); - NotifyFrameDamaged(kNewDeviceScaleFactor, kNewPageScaleFactor, - kNewRootScrollOffset); + NotifyFrameDamaged(gfx::Rect(size_set().source_size), kNewDeviceScaleFactor, + kNewPageScaleFactor, kNewRootScrollOffset); frame_sink_.SendCopyOutputResult(++cur_frame_index); - EXPECT_EQ(++expected_frames_count, consumer.num_frames_received()); + ++expected_frames_count; + EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); EXPECT_TRUE(CompareVarsInCompositorFrameMetadata( *(consumer.TakeFrame(cur_frame_index)), kNewDeviceScaleFactor, kNewPageScaleFactor, kNewRootScrollOffset)); @@ -930,13 +999,205 @@ // the last frame capture, the refresh retry timer should be started. // Expect that the refresh frame is delivered to the consumer with the same // metadata from the previous frame. - capturer_.RequestRefreshFrame(); + capturer_->RequestRefreshFrame(); AdvanceClockForRefreshTimer(); - EXPECT_EQ(++expected_frames_count, consumer.num_frames_received()); + ++expected_frames_count; + EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); EXPECT_TRUE(CompareVarsInCompositorFrameMetadata( *(consumer.TakeFrame(++cur_frame_index)), kNewDeviceScaleFactor, kNewPageScaleFactor, kNewRootScrollOffset)); StopCapture(); } +// Tests that frame metadata CAPTURE_COUNTER and CAPTURE_UPDATE_RECT are sent to +// the consumer as part of each frame delivery. +TEST_F(FrameSinkVideoCapturerTest, DeliversUpdateRectAndCaptureCounter) { + EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId)) + .WillRepeatedly(Return(&frame_sink_)); + capturer_->ChangeTarget(kFrameSinkId); + + MockConsumer consumer; + StartCapture(&consumer); + + // With the start, an immediate refresh occurred. Simulate a copy result. + // Expect to see the refresh frame delivered to the consumer, along with + // default metadata values. + int cur_capture_index = 0, cur_frame_index = 0, expected_frames_count = 1, + previous_capture_counter_received = 0; + frame_sink_.SendCopyOutputResult(cur_capture_index); + EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); + { + auto received_frame = consumer.TakeFrame(cur_frame_index); + gfx::Rect received_update_rect; + int received_capture_counter = 0; + ASSERT_TRUE(received_frame->metadata()->GetInteger( + media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); + ASSERT_TRUE(received_frame->metadata()->GetRect( + media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, &received_update_rect)); + EXPECT_EQ(gfx::Rect(size_set().capture_size), received_update_rect); + previous_capture_counter_received = received_capture_counter; + } + consumer.SendDoneNotification(cur_frame_index); + + const gfx::Rect kSourceDamageRect = gfx::Rect(3, 7, 60, 45); + gfx::Rect expected_frame_update_rect = copy_output::ComputeResultRect( + kSourceDamageRect, + gfx::Vector2d(size_set().source_size.width(), + size_set().source_size.height()), + gfx::Vector2d(size_set().expected_content_rect.width(), + size_set().expected_content_rect.height())); + expected_frame_update_rect.Offset( + size_set().expected_content_rect.OffsetFromOrigin()); + + // Notify frame damage with custom damage rect, and expect that the refresh + // frame is delivered to the consumer with a corresponding |update_rect|. + AdvanceClockToNextVsync(); + NotifyFrameDamaged(kSourceDamageRect); + frame_sink_.SendCopyOutputResult(++cur_capture_index); + ++expected_frames_count; + EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); + { + auto received_frame = consumer.TakeFrame(++cur_frame_index); + int received_capture_counter = 0; + gfx::Rect received_update_rect; + ASSERT_TRUE(received_frame->metadata()->GetInteger( + media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); + ASSERT_TRUE(received_frame->metadata()->GetRect( + media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, &received_update_rect)); + EXPECT_EQ(expected_frame_update_rect, received_update_rect); + EXPECT_EQ(previous_capture_counter_received + 1, received_capture_counter); + previous_capture_counter_received = received_capture_counter; + } + consumer.SendDoneNotification(cur_frame_index); + + // Request a refresh frame. Because the refresh request was made just after + // the last frame capture, the refresh retry timer should be started. + // Since there was no damage to the source, expect that the |update_region| + // delivered to the consumer is empty. + capturer_->RequestRefreshFrame(); + AdvanceClockForRefreshTimer(); + ++expected_frames_count; + EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); + { + auto received_frame = consumer.TakeFrame(++cur_frame_index); + int received_capture_counter = 0; + gfx::Rect received_update_rect; + ASSERT_TRUE(received_frame->metadata()->GetInteger( + media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); + ASSERT_TRUE(received_frame->metadata()->GetRect( + media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, &received_update_rect)); + EXPECT_TRUE(received_update_rect.IsEmpty()); + EXPECT_EQ(previous_capture_counter_received + 1, received_capture_counter); + previous_capture_counter_received = received_capture_counter; + } + consumer.SendDoneNotification(cur_frame_index); + + // If we do not advance the clock, the oracle will reject capture due to + // frame rate limits. + AdvanceClockToNextVsync(); + // Simulate a change in the source size. + // This is expected to trigger a refresh capture. + SwitchToSizeSet(kSizeSets[1]); + frame_sink_.SendCopyOutputResult(++cur_capture_index); + ++expected_frames_count; + EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); + { + auto received_frame = consumer.TakeFrame(++cur_frame_index); + int received_capture_counter = 0; + gfx::Rect received_update_rect; + ASSERT_TRUE(received_frame->metadata()->GetInteger( + media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); + ASSERT_TRUE(received_frame->metadata()->GetRect( + media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, &received_update_rect)); + EXPECT_EQ(gfx::Rect(size_set().capture_size), received_update_rect); + EXPECT_EQ(previous_capture_counter_received + 1, received_capture_counter); + previous_capture_counter_received = received_capture_counter; + } + consumer.SendDoneNotification(cur_frame_index); + + // If we do not advance the clock, the oracle will reject capture due to + // frame rate. + AdvanceClockToNextVsync(); + // Simulate a change in the capture size. + // This is expected to trigger a refresh capture. + SwitchToSizeSet(kSizeSets[2]); + frame_sink_.SendCopyOutputResult(++cur_capture_index); + ++expected_frames_count; + EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); + { + auto received_frame = consumer.TakeFrame(++cur_frame_index); + int received_capture_counter = 0; + gfx::Rect received_update_rect; + ASSERT_TRUE(received_frame->metadata()->GetInteger( + media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); + ASSERT_TRUE(received_frame->metadata()->GetRect( + media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, &received_update_rect)); + EXPECT_EQ(gfx::Rect(size_set().capture_size), received_update_rect); + EXPECT_EQ(previous_capture_counter_received + 1, received_capture_counter); + previous_capture_counter_received = received_capture_counter; + } + + StopCapture(); +} + +// Tests that when captured frames being dropped before delivery, the +// CAPTURE_COUNTER metadata value sent to the consumer reflects the frame drops +// indicating that CAPTURE_UPDATE_RECT must be ignored. +TEST_F(FrameSinkVideoCapturerTest, CaptureCounterSkipsWhenFramesAreDropped) { + EXPECT_CALL(frame_sink_manager_, FindCapturableFrameSink(kFrameSinkId)) + .WillRepeatedly(Return(&frame_sink_)); + capturer_->ChangeTarget(kFrameSinkId); + + MockConsumer consumer; + StartCapture(&consumer); + + // With the start, an immediate refresh occurred. Simulate a copy result. + // Expect to see the refresh frame delivered to the consumer, along with + // default metadata values. + int cur_capture_frame_index = 0, cur_receive_frame_index = 0, + expected_frames_count = 1, previous_capture_counter_received = 0; + frame_sink_.SendCopyOutputResult(cur_capture_frame_index); + EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); + { + auto received_frame = consumer.TakeFrame(cur_receive_frame_index); + int received_capture_counter = 0; + gfx::Rect received_update_rect; + ASSERT_TRUE(received_frame->metadata()->GetInteger( + media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); + ASSERT_TRUE(received_frame->metadata()->GetRect( + media::VideoFrameMetadata::CAPTURE_UPDATE_RECT, &received_update_rect)); + EXPECT_EQ(gfx::Rect(size_set().capture_size), received_update_rect); + previous_capture_counter_received = received_capture_counter; + } + consumer.SendDoneNotification(cur_receive_frame_index); + + const gfx::Rect kArbitraryDamageRect1 = gfx::Rect(1, 2, 6, 6); + const gfx::Rect kArbitraryDamageRect2 = gfx::Rect(3, 7, 5, 5); + + // Notify frame damage with custom damage rect, but have oracle reject frame + // delivery. Expect that no frame is sent to the consumer. + oracle_->set_return_false_on_complete_capture(true); + AdvanceClockToNextVsync(); + NotifyFrameDamaged(kArbitraryDamageRect1); + frame_sink_.SendCopyOutputResult(++cur_capture_frame_index); + EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); + + // Another frame damage with a different rect is reported. This time the + // oracle accepts frame delivery. + oracle_->set_return_false_on_complete_capture(false); + AdvanceClockToNextVsync(); + NotifyFrameDamaged(kArbitraryDamageRect2); + frame_sink_.SendCopyOutputResult(++cur_capture_frame_index); + ++expected_frames_count; + EXPECT_EQ(expected_frames_count, consumer.num_frames_received()); + { + auto received_frame = consumer.TakeFrame(++cur_receive_frame_index); + int received_capture_counter = 0; + ASSERT_TRUE(received_frame->metadata()->GetInteger( + media::VideoFrameMetadata::CAPTURE_COUNTER, &received_capture_counter)); + EXPECT_NE(previous_capture_counter_received + 1, received_capture_counter); + } + StopCapture(); +} + } // namespace viz
diff --git a/components/viz/test/data/image_mask_with_effect.png b/components/viz/test/data/image_mask_with_effect.png new file mode 100644 index 0000000..8414527 --- /dev/null +++ b/components/viz/test/data/image_mask_with_effect.png Binary files differ
diff --git a/components/viz/test/data/mask_with_effect.png b/components/viz/test/data/mask_with_effect.png new file mode 100644 index 0000000..8414527 --- /dev/null +++ b/components/viz/test/data/mask_with_effect.png Binary files differ
diff --git a/components/viz/test/data/mask_with_effect_different_size.png b/components/viz/test/data/mask_with_effect_different_size.png new file mode 100644 index 0000000..82917eb --- /dev/null +++ b/components/viz/test/data/mask_with_effect_different_size.png Binary files differ
diff --git a/components/viz/test/data/mask_with_effect_no_content.png b/components/viz/test/data/mask_with_effect_no_content.png new file mode 100644 index 0000000..c2e93257 --- /dev/null +++ b/components/viz/test/data/mask_with_effect_no_content.png Binary files differ
diff --git a/components/viz/test/data/scaled_mask_with_effect.png b/components/viz/test/data/scaled_mask_with_effect.png new file mode 100644 index 0000000..f02eace8 --- /dev/null +++ b/components/viz/test/data/scaled_mask_with_effect.png Binary files differ
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 376dd3c..578bf828 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -1117,6 +1117,8 @@ "loader_delegate_impl.h", "locks/lock_manager.cc", "locks/lock_manager.h", + "log_console_message.cc", + "log_console_message.h", "mach_broker_mac.h", "mach_broker_mac.mm", "manifest/manifest_icon_downloader.cc",
diff --git a/content/browser/devtools/devtools_video_consumer.cc b/content/browser/devtools/devtools_video_consumer.cc index 75acd6e0..8859f8f 100644 --- a/content/browser/devtools/devtools_video_consumer.cc +++ b/content/browser/devtools/devtools_video_consumer.cc
@@ -134,7 +134,6 @@ void DevToolsVideoConsumer::OnFrameCaptured( base::ReadOnlySharedMemoryRegion data, ::media::mojom::VideoFrameInfoPtr info, - const gfx::Rect& update_rect, const gfx::Rect& content_rect, viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) { if (!data.IsValid())
diff --git a/content/browser/devtools/devtools_video_consumer.h b/content/browser/devtools/devtools_video_consumer.h index 2634849..a34fb53 100644 --- a/content/browser/devtools/devtools_video_consumer.h +++ b/content/browser/devtools/devtools_video_consumer.h
@@ -65,7 +65,6 @@ void OnFrameCaptured( base::ReadOnlySharedMemoryRegion data, ::media::mojom::VideoFrameInfoPtr info, - const gfx::Rect& update_rect, const gfx::Rect& content_rect, viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) override; void OnStopped() override;
diff --git a/content/browser/devtools/devtools_video_consumer_unittest.cc b/content/browser/devtools/devtools_video_consumer_unittest.cc index c95590f..91403b1 100644 --- a/content/browser/devtools/devtools_video_consumer_unittest.cc +++ b/content/browser/devtools/devtools_video_consumer_unittest.cc
@@ -187,7 +187,7 @@ nullptr); consumer_->OnFrameCaptured(std::move(data), std::move(info), - gfx::Rect(kResolution), gfx::Rect(kResolution), + gfx::Rect(kResolution), std::move(callbacks_ptr)); }
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc index b34fad1..5ed0f15 100644 --- a/content/browser/download/download_manager_impl.cc +++ b/content/browser/download/download_manager_impl.cc
@@ -70,6 +70,8 @@ #include "content/public/common/origin_util.h" #include "content/public/common/previews_state.h" #include "content/public/common/referrer.h" +#include "content/public/common/url_utils.h" +#include "mojo/public/cpp/bindings/strong_binding.h" #include "net/base/elements_upload_data_stream.h" #include "net/base/load_flags.h" #include "net/base/request_priority.h" @@ -77,6 +79,7 @@ #include "net/url_request/url_request_context.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "services/network/public/cpp/features.h" +#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h" #include "storage/browser/blob/blob_url_loader_factory.h" #include "storage/browser/blob/blob_url_request_job_factory.h" #include "url/origin.h" @@ -1317,6 +1320,35 @@ base::MakeRefCounted<FileSystemDownloadURLLoaderFactoryGetter>( params->url(), rfh, /*is_navigation=*/false, storage_partition->GetFileSystemContext(), storage_domain); + } else if (!IsURLHandledByNetworkService(params->url())) { + ContentBrowserClient::NonNetworkURLLoaderFactoryMap + non_network_url_loader_factories; + GetContentClient() + ->browser() + ->RegisterNonNetworkSubresourceURLLoaderFactories( + params->render_process_host_id(), + params->render_frame_host_routing_id(), + &non_network_url_loader_factories); + auto it = non_network_url_loader_factories.find(params->url().scheme()); + if (it == non_network_url_loader_factories.end()) { + DLOG(ERROR) << "No URLLoaderFactory found to download " << params->url(); + return; + } else { + std::unique_ptr<network::mojom::URLLoaderFactory> factory = + std::move(it->second); + network::mojom::URLLoaderFactoryPtr factory_ptr; + mojo::MakeStrongBinding(std::move(factory), + mojo::MakeRequest(&factory_ptr)); + network::mojom::URLLoaderFactoryPtrInfo factory_ptr_info = + factory_ptr.PassInterface(); + + auto wrapper_factory = + std::make_unique<network::WrapperSharedURLLoaderFactoryInfo>( + std::move(factory_ptr_info)); + url_loader_factory_getter = + base::MakeRefCounted<download::DownloadURLLoaderFactoryGetterImpl>( + std::move(wrapper_factory)); + } } else { StoragePartitionImpl* storage_partition = static_cast<StoragePartitionImpl*>(
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 722d69d..ca7881b 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -65,6 +65,7 @@ #include "content/browser/keyboard_lock/keyboard_lock_service_impl.h" #include "content/browser/loader/prefetch_url_loader_service.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" +#include "content/browser/log_console_message.h" #include "content/browser/media/capture/audio_mirroring_manager.h" #include "content/browser/media/media_interface_proxy.h" #include "content/browser/media/session/media_session_service_impl.h" @@ -1847,21 +1848,11 @@ HasWebUIScheme(delegate_->GetMainFrameLastCommittedURL()) || GetContentClient()->browser()->IsBuiltinComponent( GetProcess()->GetBrowserContext(), GetLastCommittedOrigin()); - const int32_t resolved_level = - is_builtin_component ? level : ::logging::LOG_INFO; + const bool is_off_the_record = + GetSiteInstance()->GetBrowserContext()->IsOffTheRecord(); - // LogMessages can be persisted so this shouldn't be logged in incognito mode. - // This rule is not applied to WebUI pages or other builtin components, - // because WebUI and builtin components source code is a part of Chrome source - // code, and we want to treat messages from WebUI and other builtin components - // the same way as we treat log messages from native code. - if (::logging::GetMinLogLevel() <= resolved_level && - (is_builtin_component || - !GetSiteInstance()->GetBrowserContext()->IsOffTheRecord())) { - logging::LogMessage("CONSOLE", line_no, resolved_level).stream() - << "\"" << message << "\", source: " << source_id << " (" << line_no - << ")"; - } + LogConsoleMessage(level, message, line_no, is_builtin_component, + is_off_the_record, source_id); } void RenderFrameHostImpl::OnCreateChildFrame(
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc index 9796030d..a4a5e2c 100644 --- a/content/browser/gpu/gpu_data_manager_impl_private.cc +++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -585,12 +585,14 @@ #if !defined(OS_MACOSX) // MacOSX bots use real GPU in tests. - if (browser_command_line->HasSwitch( - switches::kOverrideUseSoftwareGLForTests) || - browser_command_line->HasSwitch(switches::kHeadless)) { - // TODO(zmo): We should also pass in kUseGL here. - // See https://crbug.com/805204. - command_line->AppendSwitch(switches::kOverrideUseSoftwareGLForTests); + if (browser_command_line->HasSwitch(switches::kHeadless)) { + if (command_line->HasSwitch(switches::kUseGL)) { + use_gl = command_line->GetSwitchValueASCII(switches::kUseGL); + // Don't append kOverrideUseSoftwareGLForTests when we need to enable GPU + // hardware for headless chromium. + if (use_gl != gl::kGLImplementationEGLName) + command_line->AppendSwitch(switches::kOverrideUseSoftwareGLForTests); + } } #endif // !OS_MACOSX }
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc index 30e8b6d6..db40189 100644 --- a/content/browser/loader/navigation_url_loader_impl.cc +++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -316,17 +316,6 @@ handled_externally ? net::ERR_ABORTED : net::ERR_UNKNOWN_URL_SCHEME)); } -// Returns whether this URL can be handled by the default network service -// URLLoader. -bool IsURLHandledByDefaultLoader(const GURL& url) { - // Data URLs are only handled by the network service if - // |enable_data_url_support| is set in NetworkContextParams. This is set to - // true for the context used by NavigationURLLoaderImpl, so in addition to - // checking whether the URL is handled by the network service, we also need to - // check for the data scheme. - return IsURLHandledByNetworkService(url) || url.SchemeIs(url::kDataScheme); -} - // Determines whether it is safe to redirect from |from_url| to |to_url|. bool IsRedirectSafe(const GURL& from_url, const GURL& to_url, @@ -787,8 +776,8 @@ if (!default_loader_used_ || (base::FeatureList::IsEnabled(network::features::kNetworkService) && url_chain_.size() > 1 && - IsURLHandledByDefaultLoader(url_chain_[url_chain_.size() - 1]) != - IsURLHandledByDefaultLoader(url_chain_[url_chain_.size() - 2]))) { + IsURLHandledByNetworkService(url_chain_[url_chain_.size() - 1]) != + IsURLHandledByNetworkService(url_chain_[url_chain_.size() - 2]))) { url_loader_.reset(); } interceptor_index_ = 0; @@ -972,7 +961,7 @@ // further refactor the factory getters to avoid this. scoped_refptr<network::SharedURLLoaderFactory> factory; - if (!IsURLHandledByDefaultLoader(resource_request_->url)) { + if (!IsURLHandledByNetworkService(resource_request_->url)) { if (known_schemes_.find(resource_request_->url.scheme()) == known_schemes_.end()) { bool handled = GetContentClient()->browser()->HandleExternalProtocol(
diff --git a/content/browser/log_console_message.cc b/content/browser/log_console_message.cc new file mode 100644 index 0000000..328f3a4 --- /dev/null +++ b/content/browser/log_console_message.cc
@@ -0,0 +1,35 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/log_console_message.h" + +#include "base/logging.h" + +namespace content { + +void LogConsoleMessage(int32_t level, + const base::string16& message, + int32_t line_number, + bool is_builtin_component, + bool is_off_the_record, + const base::string16& source_id) { + const int32_t resolved_level = + is_builtin_component ? level : ::logging::LOG_INFO; + if (::logging::GetMinLogLevel() > resolved_level) + return; + + // LogMessages can be persisted so this shouldn't be logged in incognito mode. + // This rule is not applied to WebUI pages or other builtin components, + // because WebUI and builtin components source code is a part of Chrome source + // code, and we want to treat messages from WebUI and other builtin components + // the same way as we treat log messages from native code. + if (is_off_the_record && !is_builtin_component) + return; + + logging::LogMessage("CONSOLE", line_number, resolved_level).stream() + << "\"" << message << "\", source: " << source_id << " (" << line_number + << ")"; +} + +} // namespace content
diff --git a/content/browser/log_console_message.h b/content/browser/log_console_message.h new file mode 100644 index 0000000..55e7e67 --- /dev/null +++ b/content/browser/log_console_message.h
@@ -0,0 +1,23 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_LOG_CONSOLE_MESSAGE_H_ +#define CONTENT_BROWSER_LOG_CONSOLE_MESSAGE_H_ + +#include "base/strings/string16.h" + +namespace content { + +// Optionally logs a message from the console, depending on the set logging +// levels and incognito state. +void LogConsoleMessage(int32_t level, + const base::string16& message, + int32_t line_number, + bool is_builtin_component, + bool is_off_the_record, + const base::string16& source_id); + +} // namespace content + +#endif // CONTENT_BROWSER_LOG_CONSOLE_MESSAGE_H_
diff --git a/content/browser/media/capture/fake_video_capture_stack.cc b/content/browser/media/capture/fake_video_capture_stack.cc index 86c6022..161351d 100644 --- a/content/browser/media/capture/fake_video_capture_stack.cc +++ b/content/browser/media/capture/fake_video_capture_stack.cc
@@ -103,6 +103,8 @@ void OnStartedUsingGpuDecode() final { NOTREACHED(); } + void OnStopped() final {} + FakeVideoCaptureStack* const capture_stack_; base::flat_map<int, media::mojom::VideoBufferHandlePtr> buffers_;
diff --git a/content/browser/media/capture/frame_sink_video_capture_device.cc b/content/browser/media/capture/frame_sink_video_capture_device.cc index dcb8b00f..c6a10d3f 100644 --- a/content/browser/media/capture/frame_sink_video_capture_device.cc +++ b/content/browser/media/capture/frame_sink_video_capture_device.cc
@@ -209,7 +209,6 @@ void FrameSinkVideoCaptureDevice::OnFrameCaptured( base::ReadOnlySharedMemoryRegion data, media::mojom::VideoFrameInfoPtr info, - const gfx::Rect& update_rect, const gfx::Rect& content_rect, viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/content/browser/media/capture/frame_sink_video_capture_device.h b/content/browser/media/capture/frame_sink_video_capture_device.h index be8de012c..c130686 100644 --- a/content/browser/media/capture/frame_sink_video_capture_device.h +++ b/content/browser/media/capture/frame_sink_video_capture_device.h
@@ -77,7 +77,6 @@ void OnFrameCaptured( base::ReadOnlySharedMemoryRegion data, media::mojom::VideoFrameInfoPtr info, - const gfx::Rect& update_rect, const gfx::Rect& content_rect, viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) final; void OnStopped() final;
diff --git a/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc b/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc index 29965e5..9e4fd30 100644 --- a/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc +++ b/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc
@@ -195,6 +195,7 @@ MOCK_METHOD1(OnFrameDropped, void(media::VideoCaptureFrameDropReason reason)); MOCK_METHOD1(OnLog, void(const std::string& message)); MOCK_METHOD0(OnStarted, void()); + MOCK_METHOD0(OnStopped, void()); void OnStartedUsingGpuDecode() final { NOTREACHED(); } base::ReadOnlySharedMemoryRegion TakeBufferHandle(int buffer_id) { @@ -377,7 +378,7 @@ base::Value(base::Value::Type::DICTIONARY), kFormat, kResolution, gfx::Rect(kResolution), gfx::ColorSpace::CreateREC709(), nullptr), - gfx::Rect(kResolution), gfx::Rect(kResolution), + gfx::Rect(kResolution), viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr( std::move(callbacks_info))); },
diff --git a/content/browser/media/capture/lame_window_capturer_chromeos.cc b/content/browser/media/capture/lame_window_capturer_chromeos.cc index d3daea21..b11eca3 100644 --- a/content/browser/media/capture/lame_window_capturer_chromeos.cc +++ b/content/browser/media/capture/lame_window_capturer_chromeos.cc
@@ -363,7 +363,6 @@ info->visible_rect = frame->visible_rect(); DCHECK(frame->ColorSpace().IsValid()); // Ensure it was set by this point. info->color_space = frame->ColorSpace(); - const gfx::Rect update_rect = frame->visible_rect(); const gfx::Rect content_rect = in_flight_frame->content_rect(); // Create a mojo message pipe and bind to the InFlightFrame to wait for the @@ -374,8 +373,8 @@ mojo::MakeRequest(&callbacks)); // Send the frame to the consumer. - consumer_->OnFrameCaptured(std::move(handle), std::move(info), update_rect, - content_rect, std::move(callbacks)); + consumer_->OnFrameCaptured(std::move(handle), std::move(info), content_rect, + std::move(callbacks)); } void LameWindowCapturerChromeOS::OnWindowDestroying(aura::Window* window) {
diff --git a/content/browser/portal/portal.cc b/content/browser/portal/portal.cc index 093cfc2..c2f18623 100644 --- a/content/browser/portal/portal.cc +++ b/content/browser/portal/portal.cc
@@ -82,8 +82,8 @@ portal_contents_impl_ = static_cast<WebContentsImpl*>(portal_contents.get()); portal_contents_impl_->set_portal(this); - portal_contents_impl_->AttachToOuterWebContentsFrame( - std::move(portal_contents), outer_node->current_frame_host()); + outer_contents_impl->AttachInnerWebContents(std::move(portal_contents), + outer_node->current_frame_host()); FrameTreeNode* frame_tree_node = portal_contents_impl_->GetMainFrame()->frame_tree_node();
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc index 1171031a..2a99a81 100644 --- a/content/browser/renderer_host/media/video_capture_controller.cc +++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -628,6 +628,16 @@ base::BindRepeating(&CallOnStartedUsingGpuDecode)); } +void VideoCaptureController::OnStopped() { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + EmitLogMessage(__func__, 3); + // Clients of VideoCaptureController are currently not interested in + // OnStopped events, so we simply swallow the event here. Note that, if we + // wanted to forward it to clients in the future, care would have to be taken + // for the case of there being outstanding OnBufferRetired() events that have + // been deferred because a client was still consuming a retired buffer. +} + void VideoCaptureController::OnDeviceLaunched( std::unique_ptr<LaunchedVideoCaptureDevice> device) { DCHECK_CURRENTLY_ON(BrowserThread::IO);
diff --git a/content/browser/renderer_host/media/video_capture_controller.h b/content/browser/renderer_host/media/video_capture_controller.h index 134bba8..dcd98b25 100644 --- a/content/browser/renderer_host/media/video_capture_controller.h +++ b/content/browser/renderer_host/media/video_capture_controller.h
@@ -119,6 +119,7 @@ void OnLog(const std::string& message) override; void OnStarted() override; void OnStartedUsingGpuDecode() override; + void OnStopped() override; // Implementation of VideoCaptureDeviceLauncher::Callbacks interface: void OnDeviceLaunched(
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index cb14971..7d0c4e3 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1552,6 +1552,19 @@ return GetTextInputManager() && GetTextInputManager()->should_do_learning(); } +#if defined(OS_WIN) +void RenderWidgetHostViewAura::SetCompositionFromExistingText( + const gfx::Range& range, + const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) { + RenderFrameHostImpl* frame = GetFocusedFrame(); + if (frame) { + frame->GetFrameInputHandler()->SetCompositionFromExistingText( + range.start(), range.end(), ui_ime_text_spans); + has_composition_text_ = true; + } +} +#endif + //////////////////////////////////////////////////////////////////////////////// // RenderWidgetHostViewAura, display::DisplayObserver implementation:
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h index e5755ad..3c3bd1c 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -242,6 +242,13 @@ ukm::SourceId GetClientSourceForMetrics() const override; bool ShouldDoLearning() override; +#if defined(OS_WIN) + // Overridden from ui::TextInputClient(Windows only): + void SetCompositionFromExistingText( + const gfx::Range& range, + const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) override; +#endif + // Overridden from display::DisplayObserver: void OnDisplayMetricsChanged(const display::Display& display, uint32_t metrics) override;
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc index 64a0774..43a4682c7b 100644 --- a/content/browser/service_worker/service_worker_context_core.cc +++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -20,6 +20,7 @@ #include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/browser/log_console_message.h" #include "content/browser/service_worker/embedded_worker_registry.h" #include "content/browser/service_worker/embedded_worker_status.h" #include "content/browser/service_worker/service_worker_consts.h" @@ -38,6 +39,7 @@ #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/child_process_host.h" +#include "content/public/common/url_utils.h" #include "ipc/ipc_message.h" #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" @@ -837,6 +839,18 @@ const base::string16& message, int line_number, const GURL& source_url) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + // NOTE: This differs slightly from + // RenderFrameHostImpl::OnDidAddMessageToConsole, which also asks the + // content embedder whether to classify the message as a builtin component. + // This is called on the IO thread, though, so we can't easily get a + // BrowserContext and call ContentBrowserClient::IsBuiltinComponent(). + const bool is_builtin_component = HasWebUIScheme(source_url); + + LogConsoleMessage(message_level, message, line_number, is_builtin_component, + wrapper_->is_incognito(), + base::UTF8ToUTF16(source_url.spec())); + observer_list_->Notify( FROM_HERE, &ServiceWorkerContextCoreObserver::OnReportConsoleMessage, version->version_id(),
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 3f5c4fc..09c35a2b 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -451,23 +451,6 @@ WebContentsImpl::WebContentsTreeNode::~WebContentsTreeNode() {} -void WebContentsImpl::WebContentsTreeNode::ConnectToOuterWebContents( - std::unique_ptr<WebContents> current_web_contents, - RenderFrameHostImpl* outer_contents_frame) { - DCHECK_EQ(current_web_contents.get(), current_web_contents_); - auto* outer_web_contents = - static_cast<WebContentsImpl*>(FromRenderFrameHost(outer_contents_frame)); - - focused_web_contents_ = nullptr; - outer_web_contents_ = outer_web_contents; - outer_contents_frame_tree_node_id_ = - outer_contents_frame->frame_tree_node()->frame_tree_node_id(); - - outer_web_contents_->node_.AttachInnerWebContents( - std::move(current_web_contents)); - outer_contents_frame->frame_tree_node()->AddObserver(this); -} - std::unique_ptr<WebContents> WebContentsImpl::WebContentsTreeNode::DisconnectFromOuterWebContents() { std::unique_ptr<WebContents> inner_contents = @@ -479,8 +462,20 @@ } void WebContentsImpl::WebContentsTreeNode::AttachInnerWebContents( - std::unique_ptr<WebContents> inner_web_contents) { + std::unique_ptr<WebContents> inner_web_contents, + RenderFrameHostImpl* render_frame_host) { + WebContentsImpl* inner_web_contents_impl = + static_cast<WebContentsImpl*>(inner_web_contents.get()); + WebContentsTreeNode& inner_web_contents_node = inner_web_contents_impl->node_; + + inner_web_contents_node.focused_web_contents_ = nullptr; + inner_web_contents_node.outer_web_contents_ = current_web_contents_; + inner_web_contents_node.outer_contents_frame_tree_node_id_ = + render_frame_host->frame_tree_node()->frame_tree_node_id(); + inner_web_contents_.push_back(std::move(inner_web_contents)); + + render_frame_host->frame_tree_node()->AddObserver(&inner_web_contents_node); } std::unique_ptr<WebContents> @@ -1808,23 +1803,33 @@ GetMainFrame()->DispatchBeforeUnload(before_unload_type, false); } -void WebContentsImpl::AttachToOuterWebContentsFrame( - std::unique_ptr<WebContents> current_web_contents, - RenderFrameHost* outer_contents_frame) { - DCHECK(!node_.outer_web_contents()); - DCHECK_EQ(current_web_contents.get(), this); - auto* outer_contents_frame_impl = - static_cast<RenderFrameHostImpl*>(outer_contents_frame); +void WebContentsImpl::AttachInnerWebContents( + std::unique_ptr<WebContents> inner_web_contents, + RenderFrameHost* render_frame_host) { + WebContentsImpl* inner_web_contents_impl = + static_cast<WebContentsImpl*>(inner_web_contents.get()); + DCHECK(!inner_web_contents_impl->node_.outer_web_contents()); + auto* render_frame_host_impl = + static_cast<RenderFrameHostImpl*>(render_frame_host); + DCHECK_EQ(&frame_tree_, + render_frame_host_impl->frame_tree_node()->frame_tree()); - RenderFrameHostManager* render_manager = GetRenderManager(); - auto* outer_contnets_render_manager = - outer_contents_frame_impl->frame_tree_node()->render_manager(); + RenderFrameHostManager* inner_render_manager = + inner_web_contents_impl->GetRenderManager(); + RenderFrameHostImpl* inner_main_frame = + inner_render_manager->current_frame_host(); + RenderViewHostImpl* inner_render_view_host = + inner_render_manager->current_host(); + auto* outer_render_manager = + render_frame_host_impl->frame_tree_node()->render_manager(); // When attaching a WebContents as an inner WebContents, we need to replace // the Webcontents' view with a WebContentsViewChildFrame. - view_.reset(new WebContentsViewChildFrame( - this, GetContentClient()->browser()->GetWebContentsViewDelegate(this), - &render_view_host_delegate_view_)); + inner_web_contents_impl->view_.reset(new WebContentsViewChildFrame( + inner_web_contents_impl, + GetContentClient()->browser()->GetWebContentsViewDelegate( + inner_web_contents_impl), + &inner_web_contents_impl->render_view_host_delegate_view_)); // When the WebContents being initialized has an opener, the browser side // Render{View,Frame}Host must be initialized and the RenderWidgetHostView @@ -1832,36 +1837,39 @@ // the first navigation, but when attaching a new window we don't navigate // before attaching. If the browser side is already initialized, the calls // below will just early return. - render_manager->InitRenderView(GetRenderViewHost(), nullptr); - GetMainFrame()->Init(); - if (!render_manager->GetRenderWidgetHostView()) - CreateRenderWidgetHostViewForRenderManager(GetRenderViewHost()); + inner_render_manager->InitRenderView(inner_render_view_host, nullptr); + inner_main_frame->Init(); + if (!inner_render_manager->GetRenderWidgetHostView()) { + inner_web_contents_impl->CreateRenderWidgetHostViewForRenderManager( + inner_render_view_host); + } // Create a link to our outer WebContents. - node_.ConnectToOuterWebContents(std::move(current_web_contents), - outer_contents_frame_impl); + node_.AttachInnerWebContents(std::move(inner_web_contents), + render_frame_host_impl); // Create a proxy in top-level RenderFrameHostManager, pointing to the // SiteInstance of the outer WebContents. The proxy will be used to send // postMessage to the inner WebContents. - auto* proxy = render_manager->CreateOuterDelegateProxy( - outer_contents_frame->GetSiteInstance()); + auto* proxy = inner_render_manager->CreateOuterDelegateProxy( + render_frame_host_impl->GetSiteInstance()); // When attaching a GuestView as an inner WebContents, there should already be // a live RenderFrame, which has to be swapped. When attaching a portal, there // will not be a live RenderFrame before creating the proxy. - if (outer_contents_frame->IsRenderFrameLive()) { - render_manager->SwapOuterDelegateFrame(outer_contents_frame_impl, proxy); + if (render_frame_host_impl->IsRenderFrameLive()) { + inner_render_manager->SwapOuterDelegateFrame(render_frame_host_impl, proxy); - ReattachToOuterWebContentsFrame(); + inner_web_contents_impl->ReattachToOuterWebContentsFrame(); } - if (node_.outer_web_contents()->frame_tree_.GetFocusedFrame() == - outer_contents_frame_impl->frame_tree_node()) { - SetFocusedFrame(frame_tree_.root(), - outer_contents_frame->GetSiteInstance()); + if (frame_tree_.GetFocusedFrame() == + render_frame_host_impl->frame_tree_node()) { + inner_web_contents_impl->SetFocusedFrame( + inner_web_contents_impl->frame_tree_.root(), + render_frame_host_impl->GetSiteInstance()); } - outer_contnets_render_manager->set_attach_complete(); + outer_render_manager->set_attach_complete(); } std::unique_ptr<WebContents> WebContentsImpl::DetachFromOuterWebContents() {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index 8d85f56..568aa2e 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h
@@ -359,9 +359,8 @@ Visibility GetVisibility() override; bool NeedToFireBeforeUnload() override; void DispatchBeforeUnload(bool auto_cancel) override; - void AttachToOuterWebContentsFrame( - std::unique_ptr<WebContents> current_web_contents, - RenderFrameHost* outer_contents_frame) override; + void AttachInnerWebContents(std::unique_ptr<WebContents> inner_web_contents, + RenderFrameHost* render_frame_host) override; RenderFrameHostImpl* GetOuterWebContentsFrame() override; WebContentsImpl* GetOuterWebContents() override; WebContentsImpl* GetOutermostWebContents() override; @@ -1110,11 +1109,10 @@ explicit WebContentsTreeNode(WebContentsImpl* current_web_contents); ~WebContentsTreeNode() final; - // Connects |current_web_contents| to the outer WebContents that owns - // |outer_contents_frame|. - void ConnectToOuterWebContents( - std::unique_ptr<WebContents> current_web_contents, - RenderFrameHostImpl* outer_contents_frame); + // Attaches |inner_web_contents| to the |render_frame_host| within this + // WebContents. + void AttachInnerWebContents(std::unique_ptr<WebContents> inner_web_contents, + RenderFrameHostImpl* render_frame_host); // Disconnects the current WebContents from its outer WebContents, and // returns ownership to the caller. This is used when activating a portal, @@ -1141,8 +1139,6 @@ std::vector<WebContentsImpl*> GetInnerWebContents() const; private: - void AttachInnerWebContents( - std::unique_ptr<WebContents> inner_web_contents); std::unique_ptr<WebContents> DetachInnerWebContents( WebContentsImpl* inner_web_contents);
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index 1a180aa..feaaf54 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc
@@ -481,6 +481,9 @@ base::FeatureList::IsEnabled( features::kMimeHandlerViewInCrossProcessFrame)); + WebRuntimeFeatures::EnableFallbackCursorMode( + base::FeatureList::IsEnabled(features::kFallbackCursorMode)); + if (base::FeatureList::IsEnabled(features::kUserAgentClientHint)) WebRuntimeFeatures::EnableFeatureFromString("UserAgentClientHint", true); }
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index 2a71fea..7fc0a70 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h
@@ -1172,9 +1172,10 @@ // Allows the embedder to register per-scheme URLLoaderFactory implementations // to handle subresource URL requests for schemes not handled by the Network // Service. This function can also be used to make a factory for other - // non-subresource requests, such as for the service worker script when - // starting a service worker. In that case, the frame id will be - // MSG_ROUTING_NONE. + // non-subresource requests, such as: + // -downloads + // -service worker script when starting a service worker. In that case, the + // frame id will be MSG_ROUTING_NONE virtual void RegisterNonNetworkSubresourceURLLoaderFactories( int render_process_id, int render_frame_id, @@ -1270,6 +1271,10 @@ // // If |relative_partition_path| is the empty string, it means this needs to // create the default NetworkContext for the BrowserContext. + // + // For NetworkContexts returned from the Network Service, some requirements: + // -enable data URL support (or else data URLs will fail) + // -disable file URL support (for security) virtual network::mojom::NetworkContextPtr CreateNetworkContext( BrowserContext* context, bool in_memory,
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h index 8bf04ec..2fe0a25a 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h
@@ -529,20 +529,17 @@ // potential discard without causing the dialog to appear. virtual void DispatchBeforeUnload(bool auto_cancel) = 0; - // Attaches |current_web_contents|, which should be the same as |this| - // WebContents, to its container frame |outer_contents_frame|, which should be - // in the outer WebContents. The |current_web_contents| is pointer is needed - // for passing ownership of the inner WebContents to the outer WebContents. - // Note: |outer_contents_frame| will be swapped out and destroyed during the + // Attaches |inner_web_contents| to the container frame |render_frame_host|, + // which should be in this WebContents' FrameTree. This outer WebContents + // takes ownership of |inner_web_contents|. + // Note: |render_frame_host| will be swapped out and destroyed during the // process. Generally a frame same-process with its parent is the right choice // but ideally it should be "about:blank" to avoid problems with beforeunload. // To ensure sane usage of this API users first should call the async API // RenderFrameHost::PrepareForInnerWebContentsAttach first. - // TODO(lfg): This API should be moved so that it is called on the outer - // WebContents. - virtual void AttachToOuterWebContentsFrame( - std::unique_ptr<WebContents> current_web_contents, - RenderFrameHost* outer_contents_frame) = 0; + virtual void AttachInnerWebContents( + std::unique_ptr<WebContents> inner_web_contents, + RenderFrameHost* render_frame_host) = 0; // Returns the outer WebContents frame, the same frame that this WebContents // was attached in AttachToOuterWebContentsFrame().
diff --git a/content/public/common/url_utils.cc b/content/public/common/url_utils.cc index 5fcc9b7..43da363b 100644 --- a/content/public/common/url_utils.cc +++ b/content/public/common/url_utils.cc
@@ -64,7 +64,8 @@ bool IsURLHandledByNetworkService(const GURL& url) { return url.SchemeIsHTTPOrHTTPS() || url.SchemeIsWSOrWSS() || - url.SchemeIs(url::kFtpScheme) || url.SchemeIs(url::kGopherScheme); + url.SchemeIs(url::kFtpScheme) || url.SchemeIs(url::kGopherScheme) || + url.SchemeIs(url::kDataScheme); } bool IsRendererDebugURL(const GURL& url) {
diff --git a/content/public/test/test_utils.cc b/content/public/test/test_utils.cc index 01ac477a..151f062 100644 --- a/content/public/test/test_utils.cc +++ b/content/public/test/test_utils.cc
@@ -285,8 +285,7 @@ // Attach. |inner_contents| becomes owned by |outer_contents|. WebContents* inner_contents = inner_contents_ptr.get(); - inner_contents->AttachToOuterWebContentsFrame(std::move(inner_contents_ptr), - rfh); + outer_contents->AttachInnerWebContents(std::move(inner_contents_ptr), rfh); // |guest_delegate| becomes owned by |inner_contents|. guest_delegate.release()->SetInnerWebContents(inner_contents);
diff --git a/extensions/browser/extension_service_worker_message_filter.cc b/extensions/browser/extension_service_worker_message_filter.cc index 59c4915..58d237e 100644 --- a/extensions/browser/extension_service_worker_message_filter.cc +++ b/extensions/browser/extension_service_worker_message_filter.cc
@@ -34,8 +34,6 @@ content::BrowserThread::ID* thread) { if (message.type() == ExtensionHostMsg_RequestWorker::ID || message.type() == ExtensionHostMsg_EventAckWorker::ID || - message.type() == - ExtensionHostMsg_DidInitializeServiceWorkerContext::ID || message.type() == ExtensionHostMsg_DidStartServiceWorkerContext::ID || message.type() == ExtensionHostMsg_DidStopServiceWorkerContext::ID) { *thread = content::BrowserThread::UI; @@ -52,8 +50,6 @@ IPC_MESSAGE_HANDLER(ExtensionHostMsg_DecrementServiceWorkerActivity, OnDecrementServiceWorkerActivity) IPC_MESSAGE_HANDLER(ExtensionHostMsg_EventAckWorker, OnEventAckWorker) - IPC_MESSAGE_HANDLER(ExtensionHostMsg_DidInitializeServiceWorkerContext, - OnDidInitializeServiceWorkerContext) IPC_MESSAGE_HANDLER(ExtensionHostMsg_DidStartServiceWorkerContext, OnDidStartServiceWorkerContext) IPC_MESSAGE_HANDLER(ExtensionHostMsg_DidStopServiceWorkerContext, @@ -106,22 +102,8 @@ this)); } -void ExtensionServiceWorkerMessageFilter::OnDidInitializeServiceWorkerContext( - const ExtensionId& extension_id, - int64_t service_worker_version_id, - int thread_id) { - if (!ProcessMap::Get(browser_context_) - ->Contains(extension_id, render_process_id_)) { - // We can legitimately get here if the extension was already unloaded. - return; - } - ServiceWorkerTaskQueue::Get(browser_context_) - ->DidInitializeServiceWorkerContext(render_process_id_, extension_id, - service_worker_version_id, thread_id); -} - void ExtensionServiceWorkerMessageFilter::OnDidStartServiceWorkerContext( - const ExtensionId& extension_id, + const std::string& extension_id, const GURL& service_worker_scope, int64_t service_worker_version_id, int thread_id) { @@ -142,7 +124,7 @@ } void ExtensionServiceWorkerMessageFilter::OnDidStopServiceWorkerContext( - const ExtensionId& extension_id, + const std::string& extension_id, const GURL& service_worker_scope, int64_t service_worker_version_id, int thread_id) {
diff --git a/extensions/browser/extension_service_worker_message_filter.h b/extensions/browser/extension_service_worker_message_filter.h index 6976e492..3790b69 100644 --- a/extensions/browser/extension_service_worker_message_filter.h +++ b/extensions/browser/extension_service_worker_message_filter.h
@@ -46,9 +46,6 @@ void OnDecrementServiceWorkerActivity(int64_t service_worker_version_id, const std::string& request_uuid); void OnEventAckWorker(int64_t service_worker_version_id, int event_id); - void OnDidInitializeServiceWorkerContext(const ExtensionId& extension_id, - int64_t service_worker_version_id, - int thread_id); void OnDidStartServiceWorkerContext(const ExtensionId& extension_id, const GURL& service_worker_scope, int64_t service_worker_version_id,
diff --git a/extensions/browser/process_manager.cc b/extensions/browser/process_manager.cc index 14fa00f..874c6cf 100644 --- a/extensions/browser/process_manager.cc +++ b/extensions/browser/process_manager.cc
@@ -259,7 +259,6 @@ {content::BrowserThread::IO})), startup_background_hosts_created_(false), last_background_close_sequence_id_(0), - process_observer_(this), weak_ptr_factory_(this) { // ExtensionRegistry is shared between incognito and regular contexts. DCHECK_EQ(original_context, extension_registry_->browser_context()); @@ -969,49 +968,9 @@ void ProcessManager::RegisterServiceWorker(const WorkerId& worker_id) { all_extension_workers_.Add(worker_id); - - // Observe the RenderProcessHost for cleaning up on process shutdown. - int render_process_id = worker_id.render_process_id; - bool inserted = worker_process_to_extension_ids_[render_process_id] - .insert(worker_id.extension_id) - .second; - if (inserted) { - content::RenderProcessHost* render_process_host = - content::RenderProcessHost::FromID(render_process_id); - DCHECK(render_process_host); - if (!process_observer_.IsObserving(render_process_host)) { - // These will be cleaned up in RenderProcessExited(). - process_observer_.Add(render_process_host); - } - } -} - -void ProcessManager::RenderProcessExited( - content::RenderProcessHost* host, - const content::ChildProcessTerminationInfo& info) { - DCHECK(process_observer_.IsObserving(host)); - process_observer_.Remove(host); - const int render_process_id = host->GetID(); - // Look up and then clean up the entries that are affected by - // |render_process_id| destruction. - // - // TODO(lazyboy): Revisit this once incognito is tested for extension SWs, as - // the cleanup below only works because regular and OTR ProcessManagers are - // separate. The conclusive approach would be to have a - // all_extension_workers_.RemoveAllForProcess(render_process_id) method: - // Pros: We won't need worker_process_to_extension_ids_ anymore. - // Cons: We would require traversing all workers within - // |all_extension_workers_| (slow) as things stand right now. - auto iter = worker_process_to_extension_ids_.find(render_process_id); - if (iter == worker_process_to_extension_ids_.end()) - return; - for (const ExtensionId& extension_id : iter->second) - all_extension_workers_.RemoveAllForExtension(extension_id); - worker_process_to_extension_ids_.erase(iter); } void ProcessManager::UnregisterServiceWorker(const WorkerId& worker_id) { - // TODO(lazyboy): DCHECK that |worker_id| exists in |all_extension_workers_|. all_extension_workers_.Remove(worker_id); } @@ -1026,10 +985,6 @@ render_process_id); } -std::vector<WorkerId> ProcessManager::GetAllWorkersIdsForTesting() { - return all_extension_workers_.GetAllForTesting(); -} - void ProcessManager::ClearBackgroundPageData(const std::string& extension_id) { background_page_data_.erase(extension_id);
diff --git a/extensions/browser/process_manager.h b/extensions/browser/process_manager.h index 463d557..a1a3841 100644 --- a/extensions/browser/process_manager.h +++ b/extensions/browser/process_manager.h
@@ -19,12 +19,10 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" -#include "base/scoped_observer.h" #include "components/keyed_service/core/keyed_service.h" #include "content/public/browser/devtools_agent_host_observer.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" -#include "content/public/browser/render_process_host_observer.h" #include "extensions/browser/activity.h" #include "extensions/browser/event_page_tracker.h" #include "extensions/browser/extension_registry_observer.h" @@ -57,8 +55,7 @@ public content::NotificationObserver, public ExtensionRegistryObserver, public EventPageTracker, - public content::DevToolsAgentHostObserver, - public content::RenderProcessHostObserver { + public content::DevToolsAgentHostObserver { public: using ExtensionHostSet = std::set<extensions::ExtensionHost*>; @@ -75,6 +72,7 @@ // Registers or unregisters a running worker state to this process manager. // Note: This does not create any Service Workers. + // TODO(lazyboy): Hook this up to ServiceWorkerTaskQueue class. void RegisterServiceWorker(const WorkerId& worker_id); void UnregisterServiceWorker(const WorkerId& worker_id); @@ -237,8 +235,6 @@ return startup_background_hosts_created_; } - std::vector<WorkerId> GetAllWorkersIdsForTesting(); - protected: static ProcessManager* Create(content::BrowserContext* context); @@ -268,11 +264,6 @@ const Extension* extension, UnloadedExtensionReason reason) override; - // content::RenderProcessHostObserver: - void RenderProcessExited( - content::RenderProcessHost* host, - const content::ChildProcessTerminationInfo& info) override; - // Extra information we keep for each extension's background page. struct BackgroundPageData; struct ExtensionRenderFrameData; @@ -385,13 +376,6 @@ // start notification. In that case we want to avoid decrementing keepalive. std::map<int, ExtensionHost*> pending_network_requests_; - // Observers of Service Worker RPH this ProcessManager manages. - ScopedObserver<content::RenderProcessHost, content::RenderProcessHostObserver> - process_observer_; - // Maps render render_process_id -> extension_id for all Service Workers this - // ProcessManager manages. - std::map<int, std::set<ExtensionId>> worker_process_to_extension_ids_; - // Must be last member, see doc on WeakPtrFactory. base::WeakPtrFactory<ProcessManager> weak_ptr_factory_;
diff --git a/extensions/browser/service_worker/worker_id_set.cc b/extensions/browser/service_worker/worker_id_set.cc index 311d138..d417a3a 100644 --- a/extensions/browser/service_worker/worker_id_set.cc +++ b/extensions/browser/service_worker/worker_id_set.cc
@@ -91,8 +91,4 @@ return std::vector<WorkerId>(begin_range, end_range); } -std::vector<WorkerId> WorkerIdSet::GetAllForTesting() const { - return std::vector<WorkerId>(workers_.begin(), workers_.end()); -} - } // namespace extensions
diff --git a/extensions/browser/service_worker/worker_id_set.h b/extensions/browser/service_worker/worker_id_set.h index 4be2fd0..7a050ff 100644 --- a/extensions/browser/service_worker/worker_id_set.h +++ b/extensions/browser/service_worker/worker_id_set.h
@@ -28,7 +28,6 @@ std::vector<WorkerId> GetAllForExtension(const ExtensionId& extension_id, int render_process_id) const; - std::vector<WorkerId> GetAllForTesting() const; size_t count_for_testing() const { return workers_.size(); } private:
diff --git a/extensions/browser/service_worker_task_queue.cc b/extensions/browser/service_worker_task_queue.cc index 051d4c2..f7cc7fb 100644 --- a/extensions/browser/service_worker_task_queue.cc +++ b/extensions/browser/service_worker_task_queue.cc
@@ -20,7 +20,6 @@ #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/lazy_context_id.h" -#include "extensions/browser/process_manager.h" #include "extensions/browser/service_worker_task_queue_factory.h" #include "extensions/common/constants.h" #include "extensions/common/manifest_handlers/background_info.h" @@ -96,17 +95,6 @@ RunPendingTasksIfWorkerReady(context_id, version_id, process_id, thread_id); } -void ServiceWorkerTaskQueue::DidInitializeServiceWorkerContext( - int render_process_id, - const ExtensionId& extension_id, - int64_t service_worker_version_id, - int thread_id) { - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - ProcessManager::Get(browser_context_) - ->RegisterServiceWorker({extension_id, render_process_id, - service_worker_version_id, thread_id}); -} - void ServiceWorkerTaskQueue::DidStartServiceWorkerContext( int render_process_id, const ExtensionId& extension_id, @@ -128,9 +116,6 @@ int64_t service_worker_version_id, int thread_id) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - ProcessManager::Get(browser_context_) - ->UnregisterServiceWorker({extension_id, render_process_id, - service_worker_version_id, thread_id}); LazyContextId context_id(browser_context_, extension_id, service_worker_scope); // TODO(lazyboy): Run orphaned tasks with nullptr ContextInfo.
diff --git a/extensions/browser/service_worker_task_queue.h b/extensions/browser/service_worker_task_queue.h index 4b423beb..dafa067e 100644 --- a/extensions/browser/service_worker_task_queue.h +++ b/extensions/browser/service_worker_task_queue.h
@@ -52,12 +52,6 @@ // e.g. unregistering |extension|'s worker. void DeactivateExtension(const Extension* extension); - // Called once an extension Service Worker context was initialized but not - // necessarily started executing its JavaScript. - void DidInitializeServiceWorkerContext(int render_process_id, - const ExtensionId& extension_id, - int64_t service_worker_version_id, - int thread_id); // Called once an extension Service Worker started running. // This can be thought as "loadstop", i.e. the global JS script of the worker // has completed executing.
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h index dd37b8c..a372dde 100644 --- a/extensions/common/extension_messages.h +++ b/extensions/common/extension_messages.h
@@ -1039,13 +1039,6 @@ int64_t /* service_worker_version_id */, int /* event_id */) -// Tells the browser that an extension service worker context was initialized, -// but possibly didn't start executing its top-level JavaScript. -IPC_MESSAGE_CONTROL3(ExtensionHostMsg_DidInitializeServiceWorkerContext, - std::string /* extension_id */, - int64_t /* service_worker_version_id */, - int /* worker_thread_id */) - // Tells the browser that an extension service worker context has started and // finished executing its top-level JavaScript. // Start corresponds to EmbeddedWorkerInstance::OnStarted notification.
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc index 254813df..f5b02e2 100644 --- a/extensions/renderer/dispatcher.cc +++ b/extensions/renderer/dispatcher.cc
@@ -423,8 +423,6 @@ // TODO(lazyboy): Get rid of RequireGuestViewModules() as this doesn't seem // necessary for Extension SW. RequireGuestViewModules(context); - - worker_dispatcher->DidInitializeContext(service_worker_version_id); } g_worker_script_context_set.Get().Insert(base::WrapUnique(context));
diff --git a/extensions/renderer/worker_thread_dispatcher.cc b/extensions/renderer/worker_thread_dispatcher.cc index 9d830fe..de817f0 100644 --- a/extensions/renderer/worker_thread_dispatcher.cc +++ b/extensions/renderer/worker_thread_dispatcher.cc
@@ -168,16 +168,6 @@ } } -void WorkerThreadDispatcher::DidInitializeContext( - int64_t service_worker_version_id) { - ServiceWorkerData* data = g_data_tls.Pointer()->Get(); - DCHECK_EQ(service_worker_version_id, data->service_worker_version_id()); - const int thread_id = content::WorkerThread::GetCurrentId(); - DCHECK_NE(thread_id, kMainThreadId); - Send(new ExtensionHostMsg_DidInitializeServiceWorkerContext( - data->context()->GetExtensionID(), service_worker_version_id, thread_id)); -} - void WorkerThreadDispatcher::DidStartContext( const GURL& service_worker_scope, int64_t service_worker_version_id) {
diff --git a/extensions/renderer/worker_thread_dispatcher.h b/extensions/renderer/worker_thread_dispatcher.h index de3ae75..f092e4d 100644 --- a/extensions/renderer/worker_thread_dispatcher.h +++ b/extensions/renderer/worker_thread_dispatcher.h
@@ -56,9 +56,6 @@ EventBookkeeper* event_bookkeeper() { return &event_bookkeeper_; } - // Called when a service worker context was initialized. - void DidInitializeContext(int64_t service_worker_version_id); - // Called when a service worker context started running. void DidStartContext(const GURL& service_worker_scope, int64_t service_worker_version_id);
diff --git a/fuchsia/BUILD.gn b/fuchsia/BUILD.gn index c063c4b..dc55fcd 100644 --- a/fuchsia/BUILD.gn +++ b/fuchsia/BUILD.gn
@@ -7,12 +7,6 @@ import("//build/config/fuchsia/fidl_library.gni") import("//build/util/process_version.gni") -# Location where Fuchsia release archives and supporting files are placed. -_release_artifact_root = "$root_out_dir/release_artifacts" - -# Location where debug symbol tarballs are placed. -_symbol_artifact_root = "$root_out_dir/symbol_artifacts" - fidl_library("web_fidl") { library_name = "web" namespace = "chromium" @@ -54,8 +48,11 @@ _gn_path = "//buildtools/linux64/gn" } +# Build location where all Fuchsia archive source files are placed. +_artifact_root = "$root_out_dir/fuchsia_artifacts" + # Produces a LICENSE file for webrunner and its transitive dependencies. -_license_path = "$_release_artifact_root/LICENSE" +_license_path = "$_artifact_root/LICENSE" action("license") { script = "//tools/licenses.py" inputs = [ @@ -85,12 +82,12 @@ sources = [ "//chrome/VERSION", ] - output = "$_release_artifact_root/build_id.txt" + output = "$_artifact_root/build_id.txt" process_only = true } # Puts copies of files at the top level of the CIPD archive's structure. -copy("release_archives") { +copy("restaged_packages") { sources = [ "$root_gen_dir/fuchsia/engine/chromium/chromium.far", "$root_gen_dir/fuchsia/http/http/http.far", @@ -98,7 +95,7 @@ "$root_gen_dir/fuchsia/runners/web_runner/web_runner.far", ] outputs = [ - "$_release_artifact_root/{{source_file_part}}", + "$_artifact_root/{{source_file_part}}", ] deps = [ "//fuchsia/engine:web_engine", @@ -108,36 +105,13 @@ ] } -copy("symbol_tarballs") { - sources = [ - "$_release_artifact_root/build_id.txt", - "$root_gen_dir/fuchsia/engine/chromium/chromium.symbols.tar.bz2", - "$root_gen_dir/fuchsia/http/http/http.symbols.tar.bz2", - "$root_gen_dir/fuchsia/runners/cast_runner/cast_runner.symbols.tar.bz2", - "$root_gen_dir/fuchsia/runners/web_runner/web_runner.symbols.tar.bz2", - _license_path, - ] - outputs = [ - "$_symbol_artifact_root/{{source_file_part}}", - ] - deps = [ - ":build_id", - ":license", - "//fuchsia/engine:symbol_archive", - "//fuchsia/http:symbol_archive", - "//fuchsia/runners:cast_runner_symbol_archive", - "//fuchsia/runners:web_runner_symbol_archive", - ] -} - # Specifies the build steps that must be performed before the creation of # a CIPD archive. group("archive_sources") { deps = [ ":build_id", ":license", - ":release_archives", - ":symbol_tarballs", + ":restaged_packages", ] }
diff --git a/fuchsia/cipd/debug_symbols.yaml b/fuchsia/cipd/debug_symbols.yaml deleted file mode 100644 index 04c6f89..0000000 --- a/fuchsia/cipd/debug_symbols.yaml +++ /dev/null
@@ -1,29 +0,0 @@ -# Copyright 2018 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Creates a package of archived debugging symbols for Chromium engine, -# Web Runner, Cast Runner, and the HTTP FIDL service. -# -# pkg-var arguments: -# outdir: A fully qualified path to the build output directory. -# targetarch: The target architecture, either "amd64" or "arm64". -# -# To create a CIPD package, run the following command from the build output -# directory. -# -# $ cipd create --pkg-def ../../fuchsia/cipd/debug_symbols.yaml \ -# -pkg-var targetarch:$TARGET_ARCH \ -# -pkg-var outdir:`pwd` \ -# -ref latest \ -# -tag version:$(cat symbol_artifacts/build_id.txt) - -package: chromium/fuchsia/debug-symbols-${targetarch} -description: Debugging symbols for prebuilt binaries from Chromium. -root: ${outdir}/symbol_artifacts -data: - - file: cast_runner.symbols.tar.bz2 - - file: chromium.symbols.tar.bz2 - - file: http.symbols.tar.bz2 - - file: web_runner.symbols.tar.bz2 - - file: LICENSE
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn index dcb2539a..1ebec46 100644 --- a/fuchsia/engine/BUILD.gn +++ b/fuchsia/engine/BUILD.gn
@@ -5,7 +5,6 @@ assert(is_fuchsia) import("//build/config/fuchsia/rules.gni") -import("//build/config/fuchsia/symbol_archive.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//testing/test.gni") import("//tools/grit/repack.gni") @@ -243,12 +242,3 @@ "//testing/gtest", ] } - -symbol_archive("symbol_archive") { - deps = [ - ":web_engine", - ] - ids_txt = "$root_gen_dir/fuchsia/engine/chromium/ids.txt" - archive_name = - "$root_gen_dir/fuchsia/engine/chromium/chromium.symbols.tar.bz2" -}
diff --git a/fuchsia/http/BUILD.gn b/fuchsia/http/BUILD.gn index 439f2af..e2e83ec 100644 --- a/fuchsia/http/BUILD.gn +++ b/fuchsia/http/BUILD.gn
@@ -6,7 +6,6 @@ import("//build/config/fuchsia/fidl_library.gni") import("//build/config/fuchsia/rules.gni") -import("//build/config/fuchsia/symbol_archive.gni") import("//build/util/process_version.gni") import("//testing/test.gni") import("//tools/grit/repack.gni") @@ -62,11 +61,3 @@ "testdata/", ] } - -symbol_archive("symbol_archive") { - deps = [ - ":http_pkg", - ] - ids_txt = "$root_gen_dir/fuchsia/http/http/ids.txt" - archive_name = "$root_gen_dir/fuchsia/http/http/http.symbols.tar.bz2" -}
diff --git a/fuchsia/runners/BUILD.gn b/fuchsia/runners/BUILD.gn index d4eb8d2..2d5bb37 100644 --- a/fuchsia/runners/BUILD.gn +++ b/fuchsia/runners/BUILD.gn
@@ -5,7 +5,6 @@ assert(is_fuchsia) import("//build/config/fuchsia/rules.gni") -import("//build/config/fuchsia/symbol_archive.gni") import("//testing/test.gni") # Files common to both cast_runner and web_runner targets. @@ -210,21 +209,3 @@ ], ] } - -symbol_archive("cast_runner_symbol_archive") { - deps = [ - ":cast_runner_pkg", - ] - ids_txt = "$root_gen_dir/fuchsia/runners/cast_runner/ids.txt" - archive_name = - "$root_gen_dir/fuchsia/runners/cast_runner/cast_runner.symbols.tar.bz2" -} - -symbol_archive("web_runner_symbol_archive") { - deps = [ - ":web_runner_pkg", - ] - ids_txt = "$root_gen_dir/fuchsia/runners/web_runner/ids.txt" - archive_name = - "$root_gen_dir/fuchsia/runners/web_runner/web_runner.symbols.tar.bz2" -}
diff --git a/gpu/ipc/service/direct_composition_child_surface_win.cc b/gpu/ipc/service/direct_composition_child_surface_win.cc index e59a2b83..16726c5b 100644 --- a/gpu/ipc/service/direct_composition_child_surface_win.cc +++ b/gpu/ipc/service/direct_composition_child_surface_win.cc
@@ -154,7 +154,9 @@ params.DirtyRectsCount = 1; params.pDirtyRects = &dirty_rect; HRESULT hr = swap_chain_->Present1(interval, flags, ¶ms); - if (FAILED(hr)) { + // Ignore DXGI_STATUS_OCCLUDED since that's not an error but only + // indicates that the window is occluded and we can stop rendering. + if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) { DLOG(ERROR) << "Present1 failed with error " << std::hex << hr; return false; }
diff --git a/gpu/ipc/service/direct_composition_surface_win.cc b/gpu/ipc/service/direct_composition_surface_win.cc index 5c2d205..057b929 100644 --- a/gpu/ipc/service/direct_composition_surface_win.cc +++ b/gpu/ipc/service/direct_composition_surface_win.cc
@@ -1058,7 +1058,9 @@ HRESULT hr = decode_swap_chain_->PresentBuffer(image_dxgi->level(), 1, 0); base::UmaHistogramSparse("GPU.DirectComposition.DecodeSwapChainPresentResult", hr); - if (FAILED(hr)) { + // Ignore DXGI_STATUS_OCCLUDED since that's not an error but only indicates + // that the window is occluded and we can stop rendering. + if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) { DLOG(ERROR) << "PresentBuffer failed with error 0x" << std::hex << hr; return false; } @@ -1224,7 +1226,9 @@ first_present_ = false; HRESULT hr = swap_chain_->Present(0, 0); - if (FAILED(hr)) { + // Ignore DXGI_STATUS_OCCLUDED since that's not an error but only indicates + // that the window is occluded and we can stop rendering. + if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) { DLOG(ERROR) << "Present failed with error 0x" << std::hex << hr; return false; } @@ -1259,8 +1263,10 @@ event.Wait(); } + // Ignore DXGI_STATUS_OCCLUDED since that's not an error but only indicates + // that the window is occluded and we can stop rendering. HRESULT hr = swap_chain_->Present(1, 0); - if (FAILED(hr)) { + if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) { DLOG(ERROR) << "Present failed with error 0x" << std::hex << hr; return false; }
diff --git a/headless/lib/headless_content_main_delegate.cc b/headless/lib/headless_content_main_delegate.cc index 7809413..2288d967 100644 --- a/headless/lib/headless_content_main_delegate.cc +++ b/headless/lib/headless_content_main_delegate.cc
@@ -124,7 +124,16 @@ command_line->AppendSwitchASCII(::switches::kOzonePlatform, "headless"); #endif - if (!command_line->HasSwitch(::switches::kUseGL)) { + if (command_line->HasSwitch(::switches::kUseGL)) { + std::string use_gl = command_line->GetSwitchValueASCII(switches::kUseGL); + if (use_gl != gl::kGLImplementationEGLName) { + // Headless uses a software output device which will cause us to fall back + // to software compositing anyway, but only after attempting and failing + // to initialize GPU compositing. We disable GPU compositing here + // explicitly to preempt this attempt. + command_line->AppendSwitch(::switches::kDisableGpuCompositing); + } + } else { if (!browser_->options()->gl_implementation.empty()) { command_line->AppendSwitchASCII(::switches::kUseGL, browser_->options()->gl_implementation); @@ -133,12 +142,6 @@ } } - // Headless uses a software output device which will cause us to fall back to - // software compositing anyway, but only after attempting and failing to - // initialize GPU compositing. We disable GPU compositing here explicitly to - // preempt this attempt. - command_line->AppendSwitch(::switches::kDisableGpuCompositing); - content::Profiling::ProcessStarted(); SetContentClient(&content_client_);
diff --git a/ios/chrome/browser/sessions/session_service_ios.mm b/ios/chrome/browser/sessions/session_service_ios.mm index 3cd03f2..675ff92 100644 --- a/ios/chrome/browser/sessions/session_service_ios.mm +++ b/ios/chrome/browser/sessions/session_service_ios.mm
@@ -170,7 +170,7 @@ _taskRunner->PostTaskAndReply( FROM_HERE, base::BindOnce(^{ base::ScopedBlockingCall scoped_blocking_call( - base::BlockingType::MAY_BLOCK); + FROM_HERE, base::BlockingType::MAY_BLOCK); NSFileManager* fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:sessionPath]) return; @@ -222,7 +222,7 @@ - (void)performSaveSessionData:(NSData*)sessionData sessionPath:(NSString*)sessionPath { base::ScopedBlockingCall scoped_blocking_call( - base::BlockingType::MAY_BLOCK); + FROM_HERE, base::BlockingType::MAY_BLOCK); NSFileManager* fileManager = [NSFileManager defaultManager]; NSString* directory = [sessionPath stringByDeletingLastPathComponent];
diff --git a/ios/chrome/browser/share_extension/share_extension_item_receiver.mm b/ios/chrome/browser/share_extension/share_extension_item_receiver.mm index 24927831..02cadfc 100644 --- a/ios/chrome/browser/share_extension/share_extension_item_receiver.mm +++ b/ios/chrome/browser/share_extension/share_extension_item_receiver.mm
@@ -169,7 +169,7 @@ - (void)createReadingListFolder { { base::ScopedBlockingCall scoped_blocking_call( - base::BlockingType::WILL_BLOCK); + FROM_HERE, base::BlockingType::WILL_BLOCK); NSFileManager* manager = [NSFileManager defaultManager]; if (![manager fileExistsAtPath:[[self presentedItemURL] path]]) { [manager createDirectoryAtPath:[[self presentedItemURL] path] @@ -296,7 +296,7 @@ }; void (^readingAccessor)(NSURL*) = ^(NSURL* newURL) { base::ScopedBlockingCall scoped_blocking_call( - base::BlockingType::WILL_BLOCK); + FROM_HERE, base::BlockingType::WILL_BLOCK); NSFileManager* manager = [NSFileManager defaultManager]; NSData* data = [manager contentsAtPath:[newURL path]]; if (![weakSelf receivedData:data withCompletion:successCompletion]) { @@ -318,7 +318,7 @@ base::BlockingType::WILL_BLOCK); void (^deletingAccessor)(NSURL*) = ^(NSURL* newURL) { base::ScopedBlockingCall scoped_blocking_call( - base::BlockingType::MAY_BLOCK); + FROM_HERE, base::BlockingType::MAY_BLOCK); NSFileManager* manager = [NSFileManager defaultManager]; [manager removeItemAtURL:newURL error:nil]; };
diff --git a/ios/chrome/browser/ui/image_util/image_saver.mm b/ios/chrome/browser/ui/image_util/image_saver.mm index 2a17996..55161de 100644 --- a/ios/chrome/browser/ui/image_util/image_saver.mm +++ b/ios/chrome/browser/ui/image_util/image_saver.mm
@@ -130,7 +130,7 @@ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}, base::BindOnce(^{ base::ScopedBlockingCall scoped_blocking_call( - base::BlockingType::MAY_BLOCK); + FROM_HERE, base::BlockingType::MAY_BLOCK); NSString* fileName = [[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingString:fileExtension]; @@ -156,7 +156,7 @@ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}, base::BindOnce(^{ base::ScopedBlockingCall scoped_blocking_call( - base::BlockingType::MAY_BLOCK); + FROM_HERE, base::BlockingType::MAY_BLOCK); if (completion) completion(success, error);
diff --git a/ios/chrome/browser/ui/ntp/ntp_tile_saver.mm b/ios/chrome/browser/ui/ntp/ntp_tile_saver.mm index d466056..dfa3a8a 100644 --- a/ios/chrome/browser/ui/ntp/ntp_tile_saver.mm +++ b/ios/chrome/browser/ui/ntp/ntp_tile_saver.mm
@@ -185,7 +185,7 @@ base::OnceCallback<void()> writeImage = base::BindOnce(^{ base::ScopedBlockingCall scoped_blocking_call( - base::BlockingType::WILL_BLOCK); + FROM_HERE, base::BlockingType::WILL_BLOCK); [imageData writeToURL:fileURL atomically:YES]; }); @@ -210,7 +210,7 @@ [favicons_directory URLByAppendingPathComponent:faviconFileName]; base::OnceCallback<void()> removeImage = base::BindOnce(^{ base::ScopedBlockingCall scoped_blocking_call( - base::BlockingType::WILL_BLOCK); + FROM_HERE, base::BlockingType::WILL_BLOCK); [[NSFileManager defaultManager] removeItemAtURL:fileURL error:nil]; });
diff --git a/ios/chrome/browser/ui/settings/password/password_exporter.mm b/ios/chrome/browser/ui/settings/password/password_exporter.mm index 4befaa6..85022e9 100644 --- a/ios/chrome/browser/ui/settings/password/password_exporter.mm +++ b/ios/chrome/browser/ui/settings/password/password_exporter.mm
@@ -67,7 +67,7 @@ NSFileManager* fileManager = [NSFileManager defaultManager]; base::ScopedBlockingCall scoped_blocking_call( - base::BlockingType::WILL_BLOCK); + FROM_HERE, base::BlockingType::WILL_BLOCK); if (![fileManager createDirectoryAtURL:directoryURL withIntermediateDirectories:YES attributes:nil @@ -352,7 +352,7 @@ base::BindOnce(^{ NSFileManager* fileManager = [NSFileManager defaultManager]; base::ScopedBlockingCall scoped_blocking_call( - base::BlockingType::WILL_BLOCK); + FROM_HERE, base::BlockingType::WILL_BLOCK); [fileManager removeItemAtURL:uniqueDirectoryURL error:nil]; })); }
diff --git a/ios/web/features.mm b/ios/web/features.mm index 87ea3f29..9db3fa9b 100644 --- a/ios/web/features.mm +++ b/ios/web/features.mm
@@ -7,6 +7,15 @@ namespace web { namespace features { +bool StorePendingItemInContext() { + if (base::FeatureList::IsEnabled(web::features::kSlimNavigationManager)) { + // TODO(crbug.com/899827): Store Pending Item in NavigationContext with + // slim-navigation-manager. + return false; + } + return base::FeatureList::IsEnabled(kStorePendingItemInContext); +} + const base::Feature kIgnoresViewportScaleLimits{ "IgnoresViewportScaleLimits", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -16,6 +25,9 @@ const base::Feature kSlimNavigationManager{"SlimNavigationManager", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kStorePendingItemInContext{ + "StorePendingItemInContext", base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kWKHTTPSystemCookieStore{"WKHTTPSystemCookieStore", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/ios/web/navigation/crw_session_controller.h b/ios/web/navigation/crw_session_controller.h index c3c5ade5..594b3e16 100644 --- a/ios/web/navigation/crw_session_controller.h +++ b/ios/web/navigation/crw_session_controller.h
@@ -6,6 +6,8 @@ #define IOS_WEB_NAVIGATION_CRW_SESSION_CONTROLLER_H_ #import <Foundation/Foundation.h> + +#include <memory> #include <vector> #import "ios/web/navigation/navigation_item_impl_list.h" @@ -21,6 +23,14 @@ struct Referrer; } +@class CRWSessionController; + +@protocol CRWSessionControllerDelegate +// Used to access pending item stored in NavigationContext. +- (web::NavigationItemImpl*)pendingItemForSessionController: + (CRWSessionController*)sessionController; +@end + // A CRWSessionController is similar to a NavigationController object in desktop // Chrome. It maintains information needed to save/restore a tab with its // complete session history. There is one of these for each tab. @@ -29,6 +39,8 @@ // TODO(crbug.com/454984): Remove this class. @interface CRWSessionController : NSObject +@property(nonatomic, weak) id<CRWSessionControllerDelegate> delegate; + @property(nonatomic, readonly, assign) NSInteger lastCommittedItemIndex; @property(nonatomic, readwrite, assign) NSInteger previousItemIndex; // The index of the pending item if it is in |items|, or -1 if |pendingItem| @@ -78,6 +90,17 @@ // Sets the corresponding BrowserState. - (void)setBrowserState:(web::BrowserState*)browserState; +// Removes pending item, so it can be stored in NavigationContext. +// Pending item is stored in this object when NavigationContext object does not +// yet exist (e.g. when navigation was just requested, or when navigation has +// aborted). +- (std::unique_ptr<web::NavigationItemImpl>)releasePendingItem; + +// Allows transferring pending item from NavigationContext to this object. +// Pending item can be moved from NavigationContext to this object when +// navigation is aborted, but pending item should be retained. +- (void)setPendingItem:(std::unique_ptr<web::NavigationItemImpl>)item; + // Add a new item with the given url, referrer, navigation type and user agent // override option, making it the current item. If pending item is the same as // current item, this does nothing. |referrer| may be nil if there isn't one. @@ -93,8 +116,14 @@ // Commits the current pending item. No changes are made to the item during // this process, it is just moved from pending to committed. // TODO(pinkerton): Desktop Chrome broadcasts a notification here, should we? +// TODO(crbug.com/936933): Remove this method. - (void)commitPendingItem; +// Commits given pending |item| stored outside of session controller +// (normally in NavigationContext). It is possible to have additional pending +// items owned by session controller and/or outside of session controller. +- (void)commitPendingItem:(std::unique_ptr<web::NavigationItemImpl>)item; + // Adds a transient item with the given URL. A transient item will be // discarded on any navigation. // TODO(stuartmorgan): Make this work more like upstream, where the item can
diff --git a/ios/web/navigation/crw_session_controller.mm b/ios/web/navigation/crw_session_controller.mm index 0840b2a..5d1e43d 100644 --- a/ios/web/navigation/crw_session_controller.mm +++ b/ios/web/navigation/crw_session_controller.mm
@@ -10,6 +10,7 @@ #include <memory> #include <utility> +#include "base/feature_list.h" #include "base/format_macros.h" #include "base/logging.h" #import "base/mac/foundation_util.h" @@ -21,6 +22,7 @@ #include "ios/web/navigation/time_smoother.h" #include "ios/web/public/browser_state.h" #include "ios/web/public/browser_url_rewriter.h" +#include "ios/web/public/features.h" #include "ios/web/public/referrer.h" #include "ios/web/public/ssl_status.h" #import "ios/web/public/web_client.h" @@ -171,8 +173,12 @@ } - (web::NavigationItemImpl*)pendingItem { - if (self.pendingItemIndex == -1) + if (self.pendingItemIndex == -1) { + if (web::features::StorePendingItemInContext() && !_pendingItem) { + return [self.delegate pendingItemForSessionController:self]; + } return _pendingItem.get(); + } return self.items[self.pendingItemIndex].get(); } @@ -284,6 +290,14 @@ _navigationManager->GetBrowserState() == _browserState); } +- (std::unique_ptr<web::NavigationItemImpl>)releasePendingItem { + return std::move(_pendingItem); +} + +- (void)setPendingItem:(std::unique_ptr<web::NavigationItemImpl>)item { + _pendingItem = std::move(item); +} + - (void)addPendingItem:(const GURL&)url referrer:(const web::Referrer&)ref transition:(ui::PageTransition)trans @@ -405,7 +419,9 @@ DCHECK(!_pendingItem); } - web::NavigationItem* item = self.currentItem; + web::NavigationItem* item = web::features::StorePendingItemInContext() + ? self.lastCommittedItem + : self.currentItem; // Update the navigation timestamp now that it's actually happened. if (item) item->SetTimestamp(_timeSmoother.GetSmoothedTime(base::Time::Now())); @@ -415,6 +431,23 @@ DCHECK_EQ(self.pendingItemIndex, -1); } +- (void)commitPendingItem:(std::unique_ptr<web::NavigationItemImpl>)item { + DCHECK(web::features::StorePendingItemInContext()); + if (!item) + return; + + // Once an item is committed it's not renderer-initiated any more. (Matches + // the implementation in NavigationController.) + item->ResetForCommit(); + item->SetTimestamp(_timeSmoother.GetSmoothedTime(base::Time::Now())); + + [self clearForwardItems]; + _items.push_back(std::move(item)); + _previousItemIndex = _lastCommittedItemIndex; + self.lastCommittedItemIndex = self.items.size() - 1; + self.pendingItemIndex = -1; +} + - (void)addTransientItemWithURL:(const GURL&)URL { _transientItem = [self itemWithURL:URL
diff --git a/ios/web/navigation/crw_session_controller_unittest.mm b/ios/web/navigation/crw_session_controller_unittest.mm index d0fbcc8..c89d3855 100644 --- a/ios/web/navigation/crw_session_controller_unittest.mm +++ b/ios/web/navigation/crw_session_controller_unittest.mm
@@ -11,13 +11,16 @@ #include "base/logging.h" #include "base/strings/sys_string_conversions.h" +#include "base/test/scoped_feature_list.h" #import "ios/web/navigation/crw_session_controller+private_constructors.h" #import "ios/web/navigation/legacy_navigation_manager_impl.h" #import "ios/web/navigation/navigation_item_impl.h" #import "ios/web/navigation/navigation_manager_impl.h" +#include "ios/web/public/features.h" #include "ios/web/public/referrer.h" #include "ios/web/public/test/fakes/test_browser_state.h" #include "ios/web/public/test/test_web_thread_bundle.h" +#import "ios/web/test/fakes/crw_fake_session_controller_delegate.h" #include "ios/web/test/fakes/fake_navigation_manager_delegate.h" #import "net/base/mac/url_conversions.h" #include "testing/gtest/include/gtest/gtest.h" @@ -54,6 +57,9 @@ void SetUp() override { session_controller_ = [[CRWSessionController alloc] initWithBrowserState:&browser_state_]; + session_controller_delegate_ = + [[CRWFakeSessionControllerDelegate alloc] init]; + session_controller_.delegate = session_controller_delegate_; CreateNavigationManagerForSessionController(session_controller_); } @@ -67,7 +73,7 @@ auto navigation_manager = std::make_unique<web::LegacyNavigationManagerImpl>(); navigation_manager->SetBrowserState(&browser_state_); - navigation_manager->SetDelegate(&delegate_); + navigation_manager->SetDelegate(&navigation_manager_delegate_); navigation_manager->SetSessionController(session_controller); navigation_managers_.push_back(std::move(navigation_manager)); } @@ -76,10 +82,12 @@ return web::Referrer(GURL(url), web::ReferrerPolicyDefault); } + base::test::ScopedFeatureList feature_list_; web::TestWebThreadBundle thread_bundle_; web::TestBrowserState browser_state_; - web::FakeNavigationManagerDelegate delegate_; + web::FakeNavigationManagerDelegate navigation_manager_delegate_; CRWSessionController* session_controller_; + CRWFakeSessionControllerDelegate* session_controller_delegate_ = nil; // Implements RAII pattern for navigation manager objects created by // CreateNavigationManagerForSessionController. std::vector<std::unique_ptr<web::LegacyNavigationManagerImpl>> @@ -91,8 +99,44 @@ EXPECT_FALSE([session_controller_ currentItem]); } +// Tests that [session_controller_ pendingItem] returns item provided by the +// delegate. +TEST_F(CRWSessionControllerTest, GetPendingItemFromDelegate) { + feature_list_.InitWithFeatures( + /*enabled_features=*/{web::features::kStorePendingItemInContext}, + /*disabled_features=*/{web::features::kSlimNavigationManager}); + + ASSERT_FALSE([session_controller_ pendingItem]); + auto item = std::make_unique<web::NavigationItemImpl>(); + session_controller_delegate_.pendingItem = item.get(); + EXPECT_EQ(item.get(), [session_controller_ pendingItem]); +} + +// Tests that [session_controller_ pendingItem] ignores item provided by +// the delegate if session controller has own pending item. +TEST_F(CRWSessionControllerTest, GetPendingItemIgnoringDelegate) { + ASSERT_FALSE([session_controller_ pendingItem]); + auto item = std::make_unique<web::NavigationItemImpl>(); + session_controller_delegate_.pendingItem = item.get(); + + GURL url("http://www.url.test"); + [session_controller_ + addPendingItem:url + referrer:web::Referrer() + transition:ui::PAGE_TRANSITION_TYPED + initiationType:web::NavigationInitiationType::BROWSER_INITIATED + userAgentOverrideOption:UserAgentOverrideOption::INHERIT]; + + EXPECT_NE(item.get(), [session_controller_ pendingItem]); + ASSERT_TRUE([session_controller_ pendingItem]); + EXPECT_EQ(url, [session_controller_ pendingItem] -> GetURL()); +} + // Tests session controller state after setting a pending index. TEST_F(CRWSessionControllerTest, SetPendingIndex) { + auto delegate_item = std::make_unique<web::NavigationItemImpl>(); + session_controller_delegate_.pendingItem = delegate_item.get(); + [session_controller_ addPendingItem:GURL("http://www.example.com") referrer:web::Referrer() @@ -461,6 +505,61 @@ EXPECT_EQ(3U, [session_controller_ items].size()); } +// Tests that -[CRWSessionController commitPendingItem:] is no-op when called +// with null. +TEST_F(CRWSessionControllerTest, CommitNilPendingItem) { + if (!web::features::StorePendingItemInContext()) { + return; + } + ASSERT_TRUE([session_controller_ items].empty()); + [session_controller_ commitPendingItem:nil]; + EXPECT_TRUE([session_controller_ items].empty()); +} + +// Tests -[CRWSessionController commitPendingItem:] with a valid pending item. +TEST_F(CRWSessionControllerTest, CommitNonNilPendingItem) { + if (!web::features::StorePendingItemInContext()) { + return; + } + + // Create session controller with a single forward item and no back items. + [session_controller_ + addPendingItem:GURL("http://www.example.test/0") + referrer:web::Referrer() + transition:ui::PAGE_TRANSITION_TYPED + initiationType:web::NavigationInitiationType::BROWSER_INITIATED + userAgentOverrideOption:UserAgentOverrideOption::INHERIT]; + [session_controller_ commitPendingItem]; + [session_controller_ + addPendingItem:GURL("http://www.example.test/1") + referrer:web::Referrer() + transition:ui::PAGE_TRANSITION_TYPED + initiationType:web::NavigationInitiationType::BROWSER_INITIATED + userAgentOverrideOption:UserAgentOverrideOption::INHERIT]; + [session_controller_ commitPendingItem]; + [session_controller_ goToItemAtIndex:0 discardNonCommittedItems:NO]; + ASSERT_EQ(2U, session_controller_.items.size()); + ASSERT_EQ(0, session_controller_.lastCommittedItemIndex); + + // Call commitPendingItem: with a valid pending item. + auto item = std::make_unique<web::NavigationItemImpl>(); + item->SetNavigationInitiationType( + web::NavigationInitiationType::BROWSER_INITIATED); + [session_controller_ commitPendingItem:std::move(item)]; + + // Verify session controller and navigation item states. + EXPECT_EQ(0, session_controller_.previousItemIndex); + EXPECT_EQ(1, session_controller_.lastCommittedItemIndex); + EXPECT_EQ(-1, session_controller_.pendingItemIndex); + ASSERT_TRUE(session_controller_.lastCommittedItem); + EXPECT_FALSE(session_controller_.lastCommittedItem->GetTimestamp().is_null()); + EXPECT_EQ(web::NavigationInitiationType::NONE, + session_controller_.lastCommittedItem->NavigationInitiationType()); + ASSERT_EQ(2U, session_controller_.items.size()); + EXPECT_EQ(session_controller_.lastCommittedItem, + [session_controller_ itemAtIndex:1]); +} + TEST_F(CRWSessionControllerTest, DiscardPendingItemWithoutPendingOrCommittedItem) { [session_controller_ discardNonCommittedItems]; @@ -1270,4 +1369,17 @@ EXPECT_TRUE(back_items.empty()); } +// Tests setPendingItem: and releasePendingItem methods. +TEST_F(CRWSessionControllerTest, TransferPendingItem) { + auto item = std::make_unique<web::NavigationItemImpl>(); + web::NavigationItemImpl* item_ptr = item.get(); + + [session_controller_ setPendingItem:std::move(item)]; + EXPECT_EQ(item_ptr, [session_controller_ pendingItem]); + + auto extracted_item = [session_controller_ releasePendingItem]; + EXPECT_FALSE([session_controller_ pendingItem]); + EXPECT_EQ(item_ptr, extracted_item.get()); +} + } // anonymous namespace
diff --git a/ios/web/navigation/legacy_navigation_manager_impl.h b/ios/web/navigation/legacy_navigation_manager_impl.h index 66c4ba2..76ddf79 100644 --- a/ios/web/navigation/legacy_navigation_manager_impl.h +++ b/ios/web/navigation/legacy_navigation_manager_impl.h
@@ -47,6 +47,7 @@ NavigationInitiationType initiation_type, UserAgentOverrideOption user_agent_override_option) override; void CommitPendingItem() override; + void CommitPendingItem(std::unique_ptr<NavigationItemImpl> item) override; int GetIndexForOffset(int offset) const override; int GetPreviousItemIndex() const override; void SetPreviousItemIndex(int previous_item_index) override;
diff --git a/ios/web/navigation/legacy_navigation_manager_impl.mm b/ios/web/navigation/legacy_navigation_manager_impl.mm index 72b6289..0b6fccdbe 100644 --- a/ios/web/navigation/legacy_navigation_manager_impl.mm +++ b/ios/web/navigation/legacy_navigation_manager_impl.mm
@@ -14,6 +14,7 @@ #import "ios/web/navigation/navigation_item_impl.h" #import "ios/web/navigation/navigation_item_impl_list.h" #import "ios/web/navigation/navigation_manager_delegate.h" +#include "ios/web/public/features.h" #import "ios/web/public/navigation_item.h" #include "ios/web/public/reload_type.h" #import "ios/web/public/web_client.h" @@ -109,6 +110,18 @@ [session_controller_ commitPendingItem]; } +void LegacyNavigationManagerImpl::CommitPendingItem( + std::unique_ptr<NavigationItemImpl> item) { + // TODO(crbug.com/665189): NavigationManager::GetPendingItemIndex returns + // incorrect value, so CRWSessionController.pendingItemIndex is used instead. + if (web::features::StorePendingItemInContext() && + session_controller_.pendingItemIndex == -1) { + [session_controller_ commitPendingItem:std::move(item)]; + } else { + CommitPendingItem(); + } +} + BrowserState* LegacyNavigationManagerImpl::GetBrowserState() const { return browser_state_; }
diff --git a/ios/web/navigation/navigation_manager_impl.h b/ios/web/navigation/navigation_manager_impl.h index 68c803a..8a278ef7 100644 --- a/ios/web/navigation/navigation_manager_impl.h +++ b/ios/web/navigation/navigation_manager_impl.h
@@ -122,8 +122,14 @@ UserAgentOverrideOption user_agent_override_option) = 0; // Commits the pending item, if any. + // TODO(crbug.com/936933): Remove this method. virtual void CommitPendingItem() = 0; + // Commits given pending |item| stored outside of navigation manager + // (normally in NavigationContext). It is possible to have additional pending + // items owned by navigation manager and/or outside of navigation manager. + virtual void CommitPendingItem(std::unique_ptr<NavigationItemImpl> item) = 0; + // Returns the navigation index that differs from the current item (or pending // item if it exists) by the specified |offset|, skipping redirect navigation // items. The index returned is not guaranteed to be valid.
diff --git a/ios/web/navigation/navigation_manager_impl_unittest.mm b/ios/web/navigation/navigation_manager_impl_unittest.mm index 834746f..ebc6f75 100644 --- a/ios/web/navigation/navigation_manager_impl_unittest.mm +++ b/ios/web/navigation/navigation_manager_impl_unittest.mm
@@ -24,6 +24,7 @@ #import "ios/web/public/test/fakes/test_navigation_manager.h" #import "ios/web/public/web_client.h" #import "ios/web/test/fakes/crw_fake_back_forward_list.h" +#import "ios/web/test/fakes/crw_fake_session_controller_delegate.h" #include "ios/web/test/test_url_constants.h" #import "ios/web/web_state/ui/crw_web_view_navigation_proxy.h" #include "testing/gmock/include/gmock/gmock.h" @@ -129,8 +130,11 @@ feature_list_.InitAndDisableFeature( web::features::kSlimNavigationManager); manager_.reset(new LegacyNavigationManagerImpl); + session_controller_delegate_ = + [[CRWFakeSessionControllerDelegate alloc] init]; controller_ = [[CRWSessionController alloc] initWithBrowserState:&browser_state_]; + controller_.delegate = session_controller_delegate_; delegate_.SetSessionController(session_controller()); } else { feature_list_.InitAndEnableFeature(web::features::kSlimNavigationManager); @@ -169,10 +173,23 @@ } } + // Makes delegate to return navigation item, which is stored in navigation + // context in the real app. + void SimulateReturningPendingItemFromDelegate(web::NavigationItemImpl* item) { + if (GetParam() == TEST_LEGACY_NAVIGATION_MANAGER) { + session_controller_delegate_.pendingItem = item; + } else { + // TODO(crbug.com/899827): Allow the delegate to provide pending item when + // slim-navigation-manager is enabled. + NOTREACHED(); + } + } + CRWFakeBackForwardList* mock_wk_list_; WKWebView* mock_web_view_; base::test::ScopedFeatureList feature_list_; base::HistogramTester histogram_tester_; + CRWFakeSessionControllerDelegate* session_controller_delegate_ = nil; private: TestBrowserState browser_state_; @@ -265,6 +282,67 @@ } } +// Tests that NavigationManagerImpl::GetPendingItem() returns item provided by +// the delegate. +TEST_P(NavigationManagerTest, GetPendingItemFromDelegate) { + if (!web::features::StorePendingItemInContext()) { + return; + } + + ASSERT_FALSE(navigation_manager()->GetPendingItem()); + auto item = std::make_unique<web::NavigationItemImpl>(); + SimulateReturningPendingItemFromDelegate(item.get()); + EXPECT_EQ(item.get(), navigation_manager()->GetPendingItem()); +} + +// Tests that NavigationManagerImpl::GetPendingItem() ignores item provided by +// the delegate if navigation manager has own pending item. +TEST_P(NavigationManagerTest, GetPendingItemIgnoringDelegate) { + if (!web::features::StorePendingItemInContext()) { + return; + } + + ASSERT_FALSE(navigation_manager()->GetPendingItem()); + auto item = std::make_unique<web::NavigationItemImpl>(); + SimulateReturningPendingItemFromDelegate(item.get()); + + GURL url("http://www.url.test"); + navigation_manager()->AddPendingItem( + url, Referrer(), ui::PAGE_TRANSITION_TYPED, + web::NavigationInitiationType::BROWSER_INITIATED, + web::NavigationManager::UserAgentOverrideOption::INHERIT); + + EXPECT_NE(item.get(), navigation_manager()->GetPendingItem()); + ASSERT_TRUE(navigation_manager()->GetPendingItem()); + EXPECT_EQ(url, navigation_manager()->GetPendingItem()->GetURL()); +} + +// Tests that GetPendingItem() returns indexed pending item. +TEST_P(NavigationManagerTest, GetPendingItemWithIndexedPendingEntry) { + if (!web::features::StorePendingItemInContext()) { + return; + } + + GURL url("http://www.url.test"); + navigation_manager()->AddPendingItem( + url, Referrer(), ui::PAGE_TRANSITION_TYPED, + web::NavigationInitiationType::BROWSER_INITIATED, + web::NavigationManager::UserAgentOverrideOption::INHERIT); + navigation_manager()->CommitPendingItem(); + if (GetParam() == TEST_LEGACY_NAVIGATION_MANAGER) { + [session_controller() setPendingItemIndex:0]; + } else { + navigation_manager()->SetPendingItemIndex(0); + } + + auto item = std::make_unique<web::NavigationItemImpl>(); + SimulateReturningPendingItemFromDelegate(item.get()); + + ASSERT_TRUE(navigation_manager()->GetPendingItem()); + EXPECT_NE(item.get(), navigation_manager()->GetPendingItem()); + EXPECT_EQ(url, navigation_manager()->GetPendingItem()->GetURL()); +} + // Tests that going back or negative offset is not possible without a committed // item. TEST_P(NavigationManagerTest, CanGoBackWithoutCommitedItem) { @@ -2518,6 +2596,60 @@ ui::PAGE_TRANSITION_FORWARD_BACK); } +// Tests that NavigationManagerImpl::CommitPendingItem() is no-op when called +// with null. +TEST_P(NavigationManagerTest, CommitNilPendingItem) { + if (!web::features::StorePendingItemInContext()) { + return; + } + ASSERT_EQ(0, navigation_manager()->GetItemCount()); + navigation_manager()->CommitPendingItem(nullptr); + ASSERT_EQ(0, navigation_manager()->GetItemCount()); +} + +// Tests NavigationManagerImpl::CommitPendingItem() with a valid pending item. +TEST_P(NavigationManagerTest, CommitNonNilPendingItem) { + if (!web::features::StorePendingItemInContext()) { + return; + } + + // Create navigation manager with a single forward item and no back items. + navigation_manager()->AddPendingItem( + GURL("http://www.url.test/0"), Referrer(), ui::PAGE_TRANSITION_TYPED, + web::NavigationInitiationType::BROWSER_INITIATED, + web::NavigationManager::UserAgentOverrideOption::INHERIT); + navigation_manager()->CommitPendingItem(); + navigation_manager()->AddPendingItem( + GURL("http://www.url.test/1"), Referrer(), ui::PAGE_TRANSITION_TYPED, + web::NavigationInitiationType::BROWSER_INITIATED, + web::NavigationManager::UserAgentOverrideOption::INHERIT); + navigation_manager()->CommitPendingItem(); + SimulateGoToIndex(0); + ASSERT_EQ(2, navigation_manager()->GetItemCount()); + ASSERT_EQ(0, navigation_manager()->GetLastCommittedItemIndex()); + + // Call CommitPendingItem() with a valid pending item. + auto item = std::make_unique<web::NavigationItemImpl>(); + item->SetNavigationInitiationType( + web::NavigationInitiationType::BROWSER_INITIATED); + navigation_manager()->CommitPendingItem(std::move(item)); + + // Verify navigation manager and navigation item states. + EXPECT_EQ(0, navigation_manager()->GetPreviousItemIndex()); + EXPECT_EQ(1, navigation_manager()->GetLastCommittedItemIndex()); + EXPECT_EQ(-1, navigation_manager()->GetPendingItemIndex()); + ASSERT_TRUE(navigation_manager()->GetLastCommittedItem()); + EXPECT_FALSE( + navigation_manager()->GetLastCommittedItem()->GetTimestamp().is_null()); + EXPECT_EQ(web::NavigationInitiationType::NONE, + navigation_manager() + ->GetLastCommittedItemImpl() + ->NavigationInitiationType()); + ASSERT_EQ(2, navigation_manager()->GetItemCount()); + EXPECT_EQ(navigation_manager()->GetLastCommittedItem(), + navigation_manager()->GetItemAtIndex(1)); +} + TEST_P(NavigationManagerTest, LoadIfNecessary) { EXPECT_CALL(navigation_manager_delegate(), LoadIfNecessary()).Times(1); navigation_manager()->LoadIfNecessary();
diff --git a/ios/web/navigation/navigation_manager_util.h b/ios/web/navigation/navigation_manager_util.h index 78cc515..9545efe 100644 --- a/ios/web/navigation/navigation_manager_util.h +++ b/ios/web/navigation/navigation_manager_util.h
@@ -10,16 +10,17 @@ namespace web { +class NavigationContextImpl; class NavigationItemImpl; class NavigationManager; class NavigationManagerImpl; -// Returns transient, committed or pending navigation item with given -// |unique_id| or null if item is not found. Item's unique id is retrieved via -// GetUniqueID method. +// Returns transient, committed or pending navigation item for the given +// navigation context or null if item is not found. Item's unique id is +// retrieved via GetUniqueID method if |context| is null. NavigationItemImpl* GetItemWithUniqueID( NavigationManagerImpl* navigation_manager, - int unique_id); + NavigationContextImpl* context); // Returns committed navigation item with given |unique_id| or null if item // is not found or it is pending or transient. Item's unique id is retrieved
diff --git a/ios/web/navigation/navigation_manager_util.mm b/ios/web/navigation/navigation_manager_util.mm index b6cafd6..d89ac5f7 100644 --- a/ios/web/navigation/navigation_manager_util.mm +++ b/ios/web/navigation/navigation_manager_util.mm
@@ -6,6 +6,8 @@ #import "ios/web/navigation/navigation_item_impl.h" #import "ios/web/navigation/navigation_manager_impl.h" +#include "ios/web/public/features.h" +#import "ios/web/web_state/navigation_context_impl.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." @@ -15,7 +17,11 @@ NavigationItemImpl* GetItemWithUniqueID( NavigationManagerImpl* navigation_manager, - int unique_id) { + NavigationContextImpl* context) { + if (context->GetItem()) + return context->GetItem(); + + int unique_id = context->GetNavigationItemUniqueID(); NavigationItemImpl* transient_item = navigation_manager->GetTransientItemImpl(); if (transient_item && transient_item->GetUniqueID() == unique_id)
diff --git a/ios/web/navigation/navigation_manager_util_unittest.mm b/ios/web/navigation/navigation_manager_util_unittest.mm index b8df4db..61f121c 100644 --- a/ios/web/navigation/navigation_manager_util_unittest.mm +++ b/ios/web/navigation/navigation_manager_util_unittest.mm
@@ -11,6 +11,7 @@ #import "ios/web/public/navigation_item.h" #include "ios/web/public/test/fakes/test_browser_state.h" #import "ios/web/test/fakes/fake_navigation_manager_delegate.h" +#import "ios/web/web_state/navigation_context_impl.h" #include "testing/platform_test.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -52,20 +53,26 @@ // GetItemWithUniqueID functions. TEST_P(NavigationManagerUtilTest, GetCommittedItemWithUniqueID) { // Start with NavigationManager that only has a pending item. + std::unique_ptr<NavigationContextImpl> context = + NavigationContextImpl::CreateNavigationContext( + /*web_state=*/nullptr, GURL::EmptyGURL(), + /*has_user_gesture=*/false, ui::PAGE_TRANSITION_TYPED, + /*is_renderer_initiated=*/false); manager_->AddPendingItem( GURL("http://chromium.org"), Referrer(), ui::PAGE_TRANSITION_TYPED, web::NavigationInitiationType::BROWSER_INITIATED, web::NavigationManager::UserAgentOverrideOption::INHERIT); NavigationItem* item = manager_->GetPendingItem(); int unique_id = item->GetUniqueID(); + context->SetNavigationItemUniqueID(item->GetUniqueID()); EXPECT_FALSE(GetCommittedItemWithUniqueID(manager_.get(), unique_id)); - EXPECT_EQ(item, GetItemWithUniqueID(manager_.get(), unique_id)); + EXPECT_EQ(item, GetItemWithUniqueID(manager_.get(), context.get())); EXPECT_EQ(-1, GetCommittedItemIndexWithUniqueID(manager_.get(), unique_id)); // Commit that pending item. manager_->CommitPendingItem(); EXPECT_EQ(item, GetCommittedItemWithUniqueID(manager_.get(), unique_id)); - EXPECT_EQ(item, GetItemWithUniqueID(manager_.get(), unique_id)); + EXPECT_EQ(item, GetItemWithUniqueID(manager_.get(), context.get())); EXPECT_EQ(0, GetCommittedItemIndexWithUniqueID(manager_.get(), unique_id)); // Commit another navigation so that the current item is updated. This allows @@ -77,16 +84,24 @@ manager_->CommitPendingItem(); manager_->RemoveItemAtIndex(0); EXPECT_FALSE(GetCommittedItemWithUniqueID(manager_.get(), unique_id)); - EXPECT_FALSE(GetItemWithUniqueID(manager_.get(), unique_id)); + EXPECT_FALSE(GetItemWithUniqueID(manager_.get(), context.get())); EXPECT_EQ(-1, GetCommittedItemIndexWithUniqueID(manager_.get(), unique_id)); // Add transient item. [controller_ addTransientItemWithURL:GURL("http://chromium.org")]; item = manager_->GetTransientItem(); unique_id = item->GetUniqueID(); + context->SetNavigationItemUniqueID(unique_id); EXPECT_FALSE(GetCommittedItemWithUniqueID(manager_.get(), unique_id)); - EXPECT_EQ(item, GetItemWithUniqueID(manager_.get(), unique_id)); + EXPECT_EQ(item, GetItemWithUniqueID(manager_.get(), context.get())); EXPECT_EQ(-1, GetCommittedItemIndexWithUniqueID(manager_.get(), unique_id)); + + // Add item to NavigationContextImpl. + auto context_item = std::make_unique<NavigationItemImpl>(); + context->SetNavigationItemUniqueID(context_item->GetUniqueID()); + context->SetItem(std::move(context_item)); + EXPECT_EQ(context->GetItem(), + GetItemWithUniqueID(manager_.get(), context.get())); } INSTANTIATE_TEST_SUITE_P(
diff --git a/ios/web/navigation/wk_based_navigation_manager_impl.h b/ios/web/navigation/wk_based_navigation_manager_impl.h index a1a21449..0166f5c 100644 --- a/ios/web/navigation/wk_based_navigation_manager_impl.h +++ b/ios/web/navigation/wk_based_navigation_manager_impl.h
@@ -105,6 +105,7 @@ NavigationInitiationType initiation_type, UserAgentOverrideOption user_agent_override_option) override; void CommitPendingItem() override; + void CommitPendingItem(std::unique_ptr<NavigationItemImpl> item) override; int GetIndexForOffset(int offset) const override; // Returns the previous navigation item in the main frame. int GetPreviousItemIndex() const override;
diff --git a/ios/web/navigation/wk_based_navigation_manager_impl.mm b/ios/web/navigation/wk_based_navigation_manager_impl.mm index cf38f77a..3ab5ec5 100644 --- a/ios/web/navigation/wk_based_navigation_manager_impl.mm +++ b/ios/web/navigation/wk_based_navigation_manager_impl.mm
@@ -258,6 +258,13 @@ OnNavigationItemCommitted(); } +void WKBasedNavigationManagerImpl::CommitPendingItem( + std::unique_ptr<NavigationItemImpl> item) { + // TODO(crbug.com/899827): Store Pending Item in NavigationContext with + // slim-navigation-manager. + CommitPendingItem(); +} + int WKBasedNavigationManagerImpl::GetIndexForOffset(int offset) const { int current_item_index = pending_item_index_; if (pending_item_index_ == -1) {
diff --git a/ios/web/navigation/wk_navigation_util.mm b/ios/web/navigation/wk_navigation_util.mm index 55fc4d3..5fba8c4b 100644 --- a/ios/web/navigation/wk_navigation_util.mm +++ b/ios/web/navigation/wk_navigation_util.mm
@@ -33,11 +33,12 @@ const char kOriginalUrlKey[] = "for"; namespace { -// Returns begin and end iterators for the given navigation items. The length of -// these iterators range will not exceed kMaxSessionSize. If |items.size()| is -// greater than kMaxSessionSize, then this function will trim navigation items, -// which are the furthest to |last_committed_item_index|. -void GetSafeItemIterators( +// Returns begin and end iterators and an updated last committed index for the +// given navigation items. The length of these iterators range will not exceed +// kMaxSessionSize. If |items.size()| is greater than kMaxSessionSize, then this +// function will trim navigation items, which are the furthest to +// |last_committed_item_index|. +int GetSafeItemIterators( int last_committed_item_index, const std::vector<std::unique_ptr<NavigationItem>>& items, std::vector<std::unique_ptr<NavigationItem>>::const_iterator* begin, @@ -46,7 +47,7 @@ // No need to trim anything. *begin = items.begin(); *end = items.end(); - return; + return last_committed_item_index; } if (last_committed_item_index < kMaxSessionSize / 2) { @@ -54,7 +55,7 @@ // on the right side of the vector. Trim those. *begin = items.begin(); *end = items.begin() + kMaxSessionSize; - return; + return last_committed_item_index; } if (items.size() - last_committed_item_index < kMaxSessionSize / 2) { @@ -62,13 +63,14 @@ // on the left side of the vector. Trim those. *begin = items.end() - kMaxSessionSize; *end = items.end(); - return; + return last_committed_item_index - kMaxSessionSize; } // Trim items from both sides of the vector. Keep the same number of items // on the left and right side of |last_committed_item_index|. *begin = items.begin() + last_committed_item_index - kMaxSessionSize / 2; *end = items.begin() + last_committed_item_index + kMaxSessionSize / 2 + 1; + return last_committed_item_index - (*begin - items.begin()); } } @@ -115,7 +117,8 @@ std::vector<std::unique_ptr<NavigationItem>>::const_iterator begin; std::vector<std::unique_ptr<NavigationItem>>::const_iterator end; - GetSafeItemIterators(last_committed_item_index, items, &begin, &end); + int new_last_committed_item_index = + GetSafeItemIterators(last_committed_item_index, items, &begin, &end); size_t new_size = end - begin; // The URLs and titles of the restored entries are stored in two separate @@ -136,7 +139,7 @@ restored_titles.GetList().push_back(base::Value(item->GetTitle())); } base::Value session(base::Value::Type::DICTIONARY); - int offset = last_committed_item_index + 1 - new_size; + int offset = new_last_committed_item_index + 1 - new_size; session.SetKey("offset", base::Value(offset)); session.SetKey("urls", std::move(restored_urls)); session.SetKey("titles", std::move(restored_titles));
diff --git a/ios/web/navigation/wk_navigation_util_unittest.mm b/ios/web/navigation/wk_navigation_util_unittest.mm index 71651ef..237f23b 100644 --- a/ios/web/navigation/wk_navigation_util_unittest.mm +++ b/ios/web/navigation/wk_navigation_util_unittest.mm
@@ -157,6 +157,9 @@ ASSERT_EQ("http:%2F%2Fwww.0.com%2F", urls_value->GetList()[0].GetString()); ASSERT_EQ("http:%2F%2Fwww.74.com%2F", urls_value->GetList()[kMaxSessionSize - 1].GetString()); + + // Verify the offset is correct. + ASSERT_EQ(1 - kMaxSessionSize, session_value->FindKey("offset")->GetInt()); } // Verifies that large session can be stored in NSURL and that extra items @@ -193,6 +196,9 @@ ASSERT_EQ("http:%2F%2Fwww.75.com%2F", urls_value->GetList()[0].GetString()); ASSERT_EQ("http:%2F%2Fwww.149.com%2F", urls_value->GetList()[kMaxSessionSize - 1].GetString()); + + // Verify the offset is correct. + ASSERT_EQ(0, session_value->FindKey("offset")->GetInt()); } // Verifies that large session can be stored in NSURL and that extra items @@ -230,6 +236,10 @@ ASSERT_EQ("http:%2F%2Fwww.38.com%2F", urls_value->GetList()[0].GetString()); ASSERT_EQ("http:%2F%2Fwww.112.com%2F", urls_value->GetList()[kMaxSessionSize - 1].GetString()); + + // Verify the offset is correct. + ASSERT_EQ((1 - kMaxSessionSize) / 2, + session_value->FindKey("offset")->GetInt()); } TEST_F(WKNavigationUtilTest, IsNotRestoreSessionUrl) {
diff --git a/ios/web/public/features.h b/ios/web/public/features.h index 555a3c8..feb2c3b 100644 --- a/ios/web/public/features.h +++ b/ios/web/public/features.h
@@ -10,6 +10,12 @@ namespace web { namespace features { +// Returns true if pending item should be stored in NavigationContext after +// context is created. The item is still stored in NavigationManager if the +// navigated was requested, but context does not yet exist or when navigation +// was aborted. +bool StorePendingItemInContext(); + // Used to always allow scaling of the web page, regardless of author intent. extern const base::Feature kIgnoresViewportScaleLimits; @@ -19,6 +25,12 @@ // Used to enable the WKBackForwardList based navigation manager. extern const base::Feature kSlimNavigationManager; +// Enable to store pending navigation item in NavigationContext after context +// is created. Requires kSlimNavigationManager enabled. The item is still stored +// in NavigationManager if the navigated was requested, but context does not yet +// exist or when navigation was aborted. +extern const base::Feature kStorePendingItemInContext; + // Used to enable using WKHTTPSystemCookieStore in main context URL requests. extern const base::Feature kWKHTTPSystemCookieStore;
diff --git a/ios/web/public/web_state/navigation_context.h b/ios/web/public/web_state/navigation_context.h index 39abc400..29c3455 100644 --- a/ios/web/public/web_state/navigation_context.h +++ b/ios/web/public/web_state/navigation_context.h
@@ -29,7 +29,8 @@ // The WebState the navigation is taking place in. virtual WebState* GetWebState() = 0; - // Returns a unique ID for this navigation. + // Returns a unique ID for this navigation. This number is a counter, so + // new context will have higher ID number than older context. virtual int64_t GetNavigationId() const = 0; // The URL the WebState is navigating to. This may change during the
diff --git a/ios/web/test/fakes/BUILD.gn b/ios/web/test/fakes/BUILD.gn index 388f3b6..a978897 100644 --- a/ios/web/test/fakes/BUILD.gn +++ b/ios/web/test/fakes/BUILD.gn
@@ -8,6 +8,7 @@ deps = [ "//base", + "//ios/web/navigation", "//ios/web/navigation:core", "//ios/web/public:public", "//ios/web/web_state/ui:crw_web_view_navigation_proxy", @@ -20,6 +21,8 @@ "crw_fake_back_forward_list.mm", "crw_fake_nsurl_session_task.h", "crw_fake_nsurl_session_task.mm", + "crw_fake_session_controller_delegate.h", + "crw_fake_session_controller_delegate.mm", "crw_fake_wk_navigation_action.h", "crw_fake_wk_navigation_action.mm", "crw_fake_wk_navigation_response.h",
diff --git a/ios/web/test/fakes/crw_fake_session_controller_delegate.h b/ios/web/test/fakes/crw_fake_session_controller_delegate.h new file mode 100644 index 0000000..cfb56b41 --- /dev/null +++ b/ios/web/test/fakes/crw_fake_session_controller_delegate.h
@@ -0,0 +1,18 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_WEB_TEST_FAKES_CRW_FAKE_SESSION_CONTROLLER_DELEGATE_H_ +#define IOS_WEB_TEST_FAKES_CRW_FAKE_SESSION_CONTROLLER_DELEGATE_H_ + +#import "ios/web/navigation/crw_session_controller.h" + +// Fake implementation of CRWSessionControllerDelegate, which allows to stub +// delegate method call results in tests. +@interface CRWFakeSessionControllerDelegate + : NSObject <CRWSessionControllerDelegate> +// Pending item to be returned from pendingItemForSessionController: method. +@property(nonatomic) web::NavigationItemImpl* pendingItem; +@end + +#endif // IOS_WEB_TEST_FAKES_CRW_FAKE_SESSION_CONTROLLER_DELEGATE_H_
diff --git a/ios/web/test/fakes/crw_fake_session_controller_delegate.mm b/ios/web/test/fakes/crw_fake_session_controller_delegate.mm new file mode 100644 index 0000000..b9729ff --- /dev/null +++ b/ios/web/test/fakes/crw_fake_session_controller_delegate.mm
@@ -0,0 +1,18 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/web/test/fakes/crw_fake_session_controller_delegate.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@implementation CRWFakeSessionControllerDelegate + +- (web::NavigationItemImpl*)pendingItemForSessionController: + (CRWSessionController*)sessionController { + return self.pendingItem; +} + +@end
diff --git a/ios/web/web_state/navigation_context_impl.h b/ios/web/web_state/navigation_context_impl.h index ca98a33..2361495 100644 --- a/ios/web/web_state/navigation_context_impl.h +++ b/ios/web/web_state/navigation_context_impl.h
@@ -16,6 +16,8 @@ namespace web { +class NavigationItemImpl; + // Tracks information related to a single navigation. class NavigationContextImpl : public NavigationContext { public: @@ -92,6 +94,19 @@ void SetMimeType(NSString* mime_type); NSString* GetMimeType() const; + // Returns pending navigation item. + NavigationItemImpl* GetItem(); + + // Similar to GetItem(), but this method transfers the ownership to the + // caller. Clients may use this method to commit navigation item or transfer + // the item to a different navigation context or back to the navigation + // manager. + std::unique_ptr<NavigationItemImpl> ReleaseItem(); + + // Stores pending navigation item. Clients may use this method to store + // pending navigation item after navigation context was created. + void SetItem(std::unique_ptr<NavigationItemImpl> item); + private: NavigationContextImpl(WebState* web_state, const GURL& url, @@ -119,6 +134,12 @@ bool is_placeholder_navigation_ = false; NSString* mime_type_ = nil; + // Holds pending navigation item in this object. Pending item is stored in + // NavigationContext after context is created. The item is still stored in + // NavigationManager if the navigated was requested, but context does not yet + // exist or when navigation was aborted. + std::unique_ptr<NavigationItemImpl> item_; + DISALLOW_COPY_AND_ASSIGN(NavigationContextImpl); };
diff --git a/ios/web/web_state/navigation_context_impl.mm b/ios/web/web_state/navigation_context_impl.mm index 9981c6726..0f847c0 100644 --- a/ios/web/web_state/navigation_context_impl.mm +++ b/ios/web/web_state/navigation_context_impl.mm
@@ -7,6 +7,7 @@ #import <Foundation/Foundation.h> #include "base/memory/ptr_util.h" +#import "ios/web/navigation/navigation_item_impl.h" #include "net/http/http_response_headers.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -187,6 +188,25 @@ return mime_type_; } +NavigationItemImpl* NavigationContextImpl::GetItem() { + return item_.get(); +} + +std::unique_ptr<NavigationItemImpl> NavigationContextImpl::ReleaseItem() { + return std::move(item_); +} + +void NavigationContextImpl::SetItem(std::unique_ptr<NavigationItemImpl> item) { + DCHECK(!item_); + if (item) { + // |item| can be null for same-docuemnt navigations and reloads, where + // navigation item is committed and should not be stored in + // NavigationContext. + DCHECK_EQ(GetNavigationItemUniqueID(), item->GetUniqueID()); + } + item_ = std::move(item); +} + NavigationContextImpl::NavigationContextImpl(WebState* web_state, const GURL& url, bool has_user_gesture,
diff --git a/ios/web/web_state/navigation_context_impl_unittest.mm b/ios/web/web_state/navigation_context_impl_unittest.mm index 6f177658..eb9b9cb 100644 --- a/ios/web/web_state/navigation_context_impl_unittest.mm +++ b/ios/web/web_state/navigation_context_impl_unittest.mm
@@ -4,6 +4,7 @@ #import "ios/web/web_state/navigation_context_impl.h" +#import "ios/web/navigation/navigation_item_impl.h" #import "ios/web/public/test/fakes/test_web_state.h" #include "net/http/http_response_headers.h" #include "testing/gtest/include/gtest/gtest.h" @@ -95,6 +96,7 @@ ASSERT_NE(response_headers_.get(), context->GetResponseHeaders()); EXPECT_EQ(WKNavigationTypeOther, context->GetWKNavigationType()); EXPECT_FALSE(context->GetMimeType()); + EXPECT_FALSE(context->GetItem()); // SetUrl GURL new_url("https://new.test"); @@ -110,6 +112,7 @@ EXPECT_EQ(WKNavigationTypeOther, context->GetWKNavigationType()); EXPECT_EQ(WKNavigationTypeOther, context->GetWKNavigationType()); EXPECT_FALSE(context->GetMimeType()); + EXPECT_FALSE(context->GetItem()); // SetSameDocument context->SetIsSameDocument(true); @@ -124,6 +127,7 @@ EXPECT_EQ(WKNavigationTypeOther, context->GetWKNavigationType()); EXPECT_EQ(WKNavigationTypeOther, context->GetWKNavigationType()); EXPECT_FALSE(context->GetMimeType()); + EXPECT_FALSE(context->GetItem()); // SetHasCommitted context->SetHasCommitted(true); @@ -138,6 +142,7 @@ EXPECT_EQ(WKNavigationTypeOther, context->GetWKNavigationType()); EXPECT_EQ(WKNavigationTypeOther, context->GetWKNavigationType()); EXPECT_FALSE(context->GetMimeType()); + EXPECT_FALSE(context->GetItem()); // SetIsDownload context->SetIsDownload(true); @@ -152,6 +157,7 @@ EXPECT_EQ(WKNavigationTypeOther, context->GetWKNavigationType()); EXPECT_EQ(WKNavigationTypeOther, context->GetWKNavigationType()); EXPECT_FALSE(context->GetMimeType()); + EXPECT_FALSE(context->GetItem()); // SetPost context->SetIsPost(true); @@ -165,6 +171,7 @@ EXPECT_NE(response_headers_.get(), context->GetResponseHeaders()); EXPECT_EQ(WKNavigationTypeOther, context->GetWKNavigationType()); EXPECT_FALSE(context->GetMimeType()); + EXPECT_FALSE(context->GetItem()); // SetErrorPage NSError* error = [[NSError alloc] initWithDomain:@"" code:0 userInfo:nil]; @@ -179,6 +186,7 @@ EXPECT_NE(response_headers_.get(), context->GetResponseHeaders()); EXPECT_EQ(WKNavigationTypeOther, context->GetWKNavigationType()); EXPECT_FALSE(context->GetMimeType()); + EXPECT_FALSE(context->GetItem()); // SetResponseHeaders context->SetResponseHeaders(response_headers_); @@ -192,6 +200,7 @@ EXPECT_EQ(response_headers_.get(), context->GetResponseHeaders()); EXPECT_EQ(WKNavigationTypeOther, context->GetWKNavigationType()); EXPECT_FALSE(context->GetMimeType()); + EXPECT_FALSE(context->GetItem()); // SetWKNavigationType context->SetWKNavigationType(WKNavigationTypeBackForward); @@ -205,7 +214,9 @@ EXPECT_EQ(response_headers_.get(), context->GetResponseHeaders()); EXPECT_EQ(WKNavigationTypeBackForward, context->GetWKNavigationType()); EXPECT_FALSE(context->GetMimeType()); + EXPECT_FALSE(context->GetItem()); + // SetMimeType context->SetMimeType(@"test/mime"); EXPECT_EQ(new_url, context->GetUrl()); EXPECT_TRUE(context->IsSameDocument()); @@ -217,6 +228,26 @@ EXPECT_EQ(response_headers_.get(), context->GetResponseHeaders()); EXPECT_EQ(WKNavigationTypeBackForward, context->GetWKNavigationType()); EXPECT_NSEQ(@"test/mime", context->GetMimeType()); + EXPECT_FALSE(context->GetItem()); + + // SetItem and ReleaseItem + auto item = std::make_unique<NavigationItemImpl>(); + NavigationItemImpl* item_ptr = item.get(); + context->SetNavigationItemUniqueID(item->GetUniqueID()); + context->SetItem(std::move(item)); + EXPECT_EQ(new_url, context->GetUrl()); + EXPECT_TRUE(context->IsSameDocument()); + EXPECT_TRUE(context->HasCommitted()); + EXPECT_TRUE(context->IsDownload()); + ASSERT_TRUE(context->IsPost()); + EXPECT_EQ(error, context->GetError()); + EXPECT_FALSE(context->IsRendererInitiated()); + EXPECT_EQ(response_headers_.get(), context->GetResponseHeaders()); + EXPECT_EQ(WKNavigationTypeBackForward, context->GetWKNavigationType()); + EXPECT_NSEQ(@"test/mime", context->GetMimeType()); + EXPECT_EQ(item_ptr, context->GetItem()); + item = context->ReleaseItem(); + EXPECT_EQ(item_ptr, item.get()); } } // namespace web
diff --git a/ios/web/web_state/ui/crw_web_controller.h b/ios/web/web_state/ui/crw_web_controller.h index 7e88168..cff7966 100644 --- a/ios/web/web_state/ui/crw_web_controller.h +++ b/ios/web/web_state/ui/crw_web_controller.h
@@ -7,6 +7,7 @@ #import <UIKit/UIKit.h> +#import "ios/web/navigation/crw_session_controller.h" #import "ios/web/public/web_state/js/crw_js_injection_evaluator.h" #include "ios/web/public/web_state/url_verification_constants.h" #import "ios/web/public/web_state/web_state.h" @@ -53,9 +54,10 @@ // web view. // This is an abstract class which must not be instantiated directly. // TODO(stuartmorgan): Move all of the navigation APIs out of this class. -@interface CRWWebController : NSObject<CRWJSInjectionEvaluator, - CRWTouchTrackingDelegate, - UIGestureRecognizerDelegate> +@interface CRWWebController : NSObject <CRWJSInjectionEvaluator, + CRWSessionControllerDelegate, + CRWTouchTrackingDelegate, + UIGestureRecognizerDelegate> // Whether or not a UIWebView is allowed to exist in this CRWWebController. // Defaults to NO; this should be enabled before attempting to access the view.
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm index 080008e..9c936f0 100644 --- a/ios/web/web_state/ui/crw_web_controller.mm +++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -45,7 +45,6 @@ #include "ios/web/history_state_util.h" #import "ios/web/interstitials/web_interstitial_impl.h" #import "ios/web/navigation/crw_navigation_item_holder.h" -#import "ios/web/navigation/crw_session_controller.h" #include "ios/web/navigation/error_retry_state_machine.h" #import "ios/web/navigation/navigation_item_impl.h" #import "ios/web/navigation/navigation_manager_impl.h" @@ -448,7 +447,7 @@ // information about the navigation that triggered the document/URL change. // TODO(stuartmorgan): The code conflates URL changes and document object // changes; the two need to be separated and handled differently. -- (void)webPageChangedWithContext:(const web::NavigationContextImpl*)context; +- (void)webPageChangedWithContext:(web::NavigationContextImpl*)context; // Resets any state that is associated with a specific document object (e.g., // page interaction tracking). - (void)resetDocumentSpecificState; @@ -1500,6 +1499,27 @@ if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) [_webUIManager loadWebUIForURL:requestURL]; } + + // WKWebView may have multiple pending items. Move pending item ownership from + // NavigationManager to NavigationContext. NavigationManager owns pending item + // after navigation was requested and until NavigationContext is created. + if (web::features::StorePendingItemInContext()) { + // No need to transfer the ownership for NativeContent URLs, because the + // load of NativeContent is synchronous. No need to transfer the ownership + // for WebUI navigations, because those navigation do not have access to + // NavigationContext. + if (![_nativeProvider hasControllerForURL:context->GetUrl()] && + !(_webUIManager && + web::GetWebClient()->IsAppSpecificURL(context->GetUrl()))) { + // TODO(crbug.com/665189): NavigationManager::GetPendingItemIndex returns + // incorrect value, so CRWSessionController.pendingItemIndex is used + // instead. + if ([self.sessionController pendingItemIndex] == -1) { + context->SetItem([self.sessionController releasePendingItem]); + } + } + } + return context; } @@ -1832,7 +1852,7 @@ // they should have already been triggered during navigation commit for // failures that happen after commit. [self didStartLoading]; - self.navigationManagerImpl->CommitPendingItem(); + self.navigationManagerImpl->CommitPendingItem(context->ReleaseItem()); web::NavigationItem* lastCommittedItem = self.navigationManagerImpl->GetLastCommittedItem(); [self setDocumentURL:lastCommittedItem->GetURL() context:context]; @@ -2130,7 +2150,8 @@ placeholderNavigation:NO]; _webStateImpl->OnNavigationStarted(navigationContext.get()); [self didStartLoading]; - self.navigationManagerImpl->CommitPendingItem(); + self.navigationManagerImpl->CommitPendingItem( + navigationContext->ReleaseItem()); [self.nativeController reload]; navigationContext->SetHasCommitted(true); _webStateImpl->OnNavigationFinished(navigationContext.get()); @@ -2205,8 +2226,8 @@ - (void)didReceiveRedirectForNavigation:(web::NavigationContextImpl*)context withURL:(const GURL&)URL { context->SetUrl(URL); - web::NavigationItemImpl* item = web::GetItemWithUniqueID( - self.navigationManagerImpl, context->GetNavigationItemUniqueID()); + web::NavigationItemImpl* item = + web::GetItemWithUniqueID(self.navigationManagerImpl, context); // Associated item can be a pending item, previously discarded by another // navigation. WKWebView allows multiple provisional navigations, while @@ -2991,7 +3012,7 @@ // TODO(stuartmorgan): This method conflates document changes and URL changes; // we should be distinguishing better, and be clear about the expected // WebDelegate and WCO callbacks in each case. -- (void)webPageChangedWithContext:(const web::NavigationContextImpl*)context { +- (void)webPageChangedWithContext:(web::NavigationContextImpl*)context { DCHECK_EQ(_loadPhase, web::LOAD_REQUESTED); web::Referrer referrer = [self currentReferrer]; @@ -3014,7 +3035,7 @@ // Do not commit pending item in the middle of loading a placeholder URL. The // item will be committed when the native content or webUI is displayed. if (!context->IsPlaceholderNavigation()) { - self.navigationManagerImpl->CommitPendingItem(); + self.navigationManagerImpl->CommitPendingItem(context->ReleaseItem()); // If a SafeBrowsing warning is currently displayed, the user has tapped // the button on the warning page to proceed to the site, the site has // started loading, and the warning is about to be removed. In this case, @@ -3220,8 +3241,8 @@ } web::NavigationItemImpl* item = - web::GetItemWithUniqueID(self.navigationManagerImpl, - navigationContext->GetNavigationItemUniqueID()); + web::GetItemWithUniqueID(self.navigationManagerImpl, navigationContext); + if (item) { GURL errorURL = net::GURLWithNSURL(error.userInfo[NSURLErrorFailingURLErrorKey]); @@ -3957,8 +3978,14 @@ web::NavigationContextImpl* context = [_navigationStates contextForNavigation:navigation]; if (context) { - web::NavigationItemImpl* item = web::GetItemWithUniqueID( - self.navigationManagerImpl, context->GetNavigationItemUniqueID()); + if (web::features::StorePendingItemInContext()) { + // This NavigationContext will be destroyed, so return pending item + // ownership to NavigationManager. NavigationContext can only own pending + // item until the navigation has committed or aborted. + [self.sessionController setPendingItem:context->ReleaseItem()]; + } + web::NavigationItemImpl* item = + web::GetItemWithUniqueID(self.navigationManagerImpl, context); if (item && item->error_retry_state_machine().state() == web::ErrorRetryState::kRetryFailedNavigationItem) { item->error_retry_state_machine().SetDisplayingWebError(); @@ -4186,6 +4213,14 @@ [_webView removeFromSuperview]; [_containerView resetContent]; [self setWebView:nil]; + + if (web::features::StorePendingItemInContext()) { + // webView:didFailProvisionalNavigation:withError: may never be called after + // resetting WKWebView, so it is important to clear pending navigations now. + for (__strong id navigation in [_navigationStates pendingNavigations]) { + [_navigationStates removeNavigation:navigation]; + } + } } - (void)webViewWebProcessDidCrash { @@ -4254,6 +4289,12 @@ _webStateImpl, URL, /*has_user_gesture=*/true, loadHTMLTransition, /*is_renderer_initiated=*/false); context->SetNavigationItemUniqueID(self.currentNavItem->GetUniqueID()); + if (web::features::StorePendingItemInContext()) { + // Transfer pending item ownership to NavigationContext. + // NavigationManager owns pending item after navigation is requested and + // until navigation context is created. + context->SetItem([self.sessionController releasePendingItem]); + } } else { context = [self registerLoadRequestForURL:URL referrer:web::Referrer() @@ -4752,7 +4793,12 @@ // Discard the pending item to ensure that the current URL is not different // from what is displayed on the view. [self discardNonCommittedItemsIfLastCommittedWasNotNativeView]; - _webStateImpl->SetIsLoading(false); + if (!web::features::StorePendingItemInContext()) { + // Loading will be stopped in webView:didFinishNavigation: callback. This + // call is here to preserve the original behavior when pending item is not + // stored in NavigationContext. + _webStateImpl->SetIsLoading(false); + } } else if (!shouldRenderResponse && WKResponse.forMainFrame) { [_pendingNavigationInfo setCancelled:YES]; } @@ -4799,8 +4845,8 @@ context->GetUrl() != webViewURL) { // Update last seen URL because it may be changed by WKWebView (f.e. by // performing characters escaping). - web::NavigationItem* item = web::GetItemWithUniqueID( - self.navigationManagerImpl, context->GetNavigationItemUniqueID()); + web::NavigationItem* item = + web::GetItemWithUniqueID(self.navigationManagerImpl, context); if (!web::wk_navigation_util::IsWKInternalUrl(webViewURL)) { if (item) { item->SetURL(webViewURL); @@ -4860,9 +4906,12 @@ hasUserGesture:[_pendingNavigationInfo hasUserGesture] rendererInitiated:YES placeholderNavigation:IsPlaceholderUrl(webViewURL)]; - _webStateImpl->OnNavigationStarted(navigationContext.get()); + web::NavigationContextImpl* navigationContextPtr = navigationContext.get(); + // GetPendingItem which may be called inside OnNavigationStarted relies on + // association between NavigationContextImpl and WKNavigation. [_navigationStates setContext:std::move(navigationContext) forNavigation:navigation]; + _webStateImpl->OnNavigationStarted(navigationContextPtr); DCHECK(self.loadPhase == web::LOAD_REQUESTED); } @@ -4940,7 +4989,15 @@ // the pending load. _pendingNavigationInfo = nil; _certVerificationErrors->Clear(); - [self forgetNullWKNavigation:navigation]; + if (web::features::StorePendingItemInContext()) { + // Remove the navigation to immediately get rid of pending item. + if (web::WKNavigationState::NONE != + [_navigationStates stateForNavigation:navigation]) { + [_navigationStates removeNavigation:navigation]; + } + } else { + [self forgetNullWKNavigation:navigation]; + } } - (void)webView:(WKWebView*)webView @@ -4957,8 +5014,11 @@ BOOL committedNavigation = [_navigationStates isCommittedNavigation:navigation]; - [_navigationStates setState:web::WKNavigationState::COMMITTED - forNavigation:navigation]; + if (!web::features::StorePendingItemInContext()) { + // Code in this method relies on existance of pending item. + [_navigationStates setState:web::WKNavigationState::COMMITTED + forNavigation:navigation]; + } DCHECK_EQ(_webView, webView); _certVerificationErrors->Clear(); @@ -5108,6 +5168,13 @@ } } + if (web::features::StorePendingItemInContext()) { + // No further code relies an existance of pending item, so this navigation + // can be marked as "committed". + [_navigationStates setState:web::WKNavigationState::COMMITTED + forNavigation:navigation]; + } + // This is the point where the document's URL has actually changed. [self setDocumentURL:webViewURL context:context]; @@ -5242,8 +5309,7 @@ web::NavigationContextImpl* context = [_navigationStates contextForNavigation:navigation]; web::NavigationItemImpl* item = - context ? web::GetItemWithUniqueID(self.navigationManagerImpl, - context->GetNavigationItemUniqueID()) + context ? web::GetItemWithUniqueID(self.navigationManagerImpl, context) : nullptr; // Invariant: every |navigation| should have a |context| and a |item|. @@ -5454,6 +5520,18 @@ } #pragma mark - +#pragma mark CRWSessionControllerDelegate methods + +- (web::NavigationItemImpl*)pendingItemForSessionController: + (CRWSessionController*)sessionController { + WKNavigation* navigation = + [_navigationStates lastNavigationWithPendingItemInNavigationContext]; + if (!navigation) + return nullptr; + return [_navigationStates contextForNavigation:navigation] -> GetItem(); +} + +#pragma mark - #pragma mark KVO Observation - (void)observeValueForKeyPath:(NSString*)keyPath @@ -5840,7 +5918,9 @@ // Otherwise, sync the current title for items created by same document // navigations. if (!web::GetWebClient()->IsSlimNavigationManagerEnabled()) { - auto* pendingItem = self.navigationManagerImpl->GetPendingItem(); + auto* pendingItem = web::features::StorePendingItemInContext() + ? newNavigationContext->GetItem() + : self.navigationManagerImpl->GetPendingItem(); if (pendingItem) pendingItem->SetTitle(_webStateImpl->GetTitle()); } @@ -5860,7 +5940,8 @@ navigationContext->SetIsSameDocument(true); _webStateImpl->OnNavigationStarted(navigationContext); [self didStartLoading]; - self.navigationManagerImpl->CommitPendingItem(); + self.navigationManagerImpl->CommitPendingItem( + navigationContext->ReleaseItem()); navigationContext->SetHasCommitted(true); _webStateImpl->OnNavigationFinished(navigationContext);
diff --git a/ios/web/web_state/ui/crw_wk_navigation_states.h b/ios/web/web_state/ui/crw_wk_navigation_states.h index 40ceb45..714111a 100644 --- a/ios/web/web_state/ui/crw_wk_navigation_states.h +++ b/ios/web/web_state/ui/crw_wk_navigation_states.h
@@ -85,6 +85,10 @@ // last navigation was null. - (WKNavigation*)lastAddedNavigation; +// WKNavigation which was added the most recently via |setState:forNavigation:| +// and has associated navigation context with pending item. +- (WKNavigation*)lastNavigationWithPendingItemInNavigationContext; + // State of WKNavigation which was added the most recently via // |setState:forNavigation:|. WKNavigationState::NONE if CRWWKNavigationStates // is empty.
diff --git a/ios/web/web_state/ui/crw_wk_navigation_states.mm b/ios/web/web_state/ui/crw_wk_navigation_states.mm index 1de369dd..7f911ef 100644 --- a/ios/web/web_state/ui/crw_wk_navigation_states.mm +++ b/ios/web/web_state/ui/crw_wk_navigation_states.mm
@@ -191,6 +191,20 @@ return result; } +- (WKNavigation*)lastNavigationWithPendingItemInNavigationContext { + NSUInteger lastAddedIndex = 0; // record indices start with 1. + WKNavigation* result = nullptr; + for (id navigation in _records) { + CRWWKNavigationsStateRecord* record = [_records objectForKey:navigation]; + web::NavigationContextImpl* context = [record context]; + if (context && context->GetItem() && lastAddedIndex < record.index) { + result = navigation; + lastAddedIndex = record.index; + } + } + return result; +} + - (web::WKNavigationState)lastAddedNavigationState { CRWWKNavigationsStateRecord* result = nil; WKNavigation* unused = nil;
diff --git a/ios/web/web_state/ui/crw_wk_navigation_states_unittest.mm b/ios/web/web_state/ui/crw_wk_navigation_states_unittest.mm index 5249b9b3..d77375f 100644 --- a/ios/web/web_state/ui/crw_wk_navigation_states_unittest.mm +++ b/ios/web/web_state/ui/crw_wk_navigation_states_unittest.mm
@@ -6,6 +6,7 @@ #import <WebKit/WebKit.h> +#import "ios/web/navigation/navigation_item_impl.h" #import "ios/web/web_state/navigation_context_impl.h" #include "net/http/http_response_headers.h" #include "testing/gtest/include/gtest/gtest.h" @@ -92,6 +93,53 @@ EXPECT_EQ(WKNavigationState::NONE, [states_ lastAddedNavigationState]); } +// Tests |lastNavigationWithPendingItemInNavigationContext| method. +TEST_F(CRWWKNavigationStatesTest, + LastNavigationWithPendingItemInNavigationContext) { + // Empty state. + EXPECT_FALSE([states_ lastNavigationWithPendingItemInNavigationContext]); + + // Navigation without context. + [states_ setState:WKNavigationState::REQUESTED forNavigation:navigation1_]; + EXPECT_FALSE([states_ lastNavigationWithPendingItemInNavigationContext]); + + // Navigation with context that does not have pending item. + std::unique_ptr<web::NavigationContextImpl> context = + NavigationContextImpl::CreateNavigationContext( + nullptr /*web_state*/, GURL(kTestUrl1), /*has_user_gesture=*/false, + ui::PageTransition::PAGE_TRANSITION_SERVER_REDIRECT, + /*is_renderer_initiated=*/true); + web::NavigationContextImpl* context_ptr = context.get(); + [states_ setContext:std::move(context) forNavigation:navigation1_]; + EXPECT_FALSE([states_ lastNavigationWithPendingItemInNavigationContext]); + + // Navigation with context that has pending item. + auto item = std::make_unique<NavigationItemImpl>(); + context_ptr->SetNavigationItemUniqueID(item->GetUniqueID()); + context_ptr->SetItem(std::move(item)); + EXPECT_EQ(navigation1_, + [states_ lastNavigationWithPendingItemInNavigationContext]); + + // Newest context does not have pending item. + std::unique_ptr<web::NavigationContextImpl> context2 = + NavigationContextImpl::CreateNavigationContext( + nullptr /*web_state*/, GURL(kTestUrl1), /*has_user_gesture=*/false, + ui::PageTransition::PAGE_TRANSITION_SERVER_REDIRECT, + /*is_renderer_initiated=*/true); + web::NavigationContextImpl* context_ptr2 = context2.get(); + [states_ setState:WKNavigationState::REQUESTED forNavigation:navigation2_]; + [states_ setContext:std::move(context2) forNavigation:navigation2_]; + EXPECT_EQ(navigation1_, + [states_ lastNavigationWithPendingItemInNavigationContext]); + + // Navigation with newest context that has pending item. + auto item2 = std::make_unique<NavigationItemImpl>(); + context_ptr2->SetNavigationItemUniqueID(item2->GetUniqueID()); + context_ptr2->SetItem(std::move(item2)); + EXPECT_EQ(navigation2_, + [states_ lastNavigationWithPendingItemInNavigationContext]); +} + // Tests |setContext:forNavigation:| and |contextForNavigation:| methods. TEST_F(CRWWKNavigationStatesTest, Context) { EXPECT_FALSE([states_ contextForNavigation:navigation1_]);
diff --git a/ios/web/web_state/web_state_impl.mm b/ios/web/web_state/web_state_impl.mm index fcff8ff..6266f24 100644 --- a/ios/web/web_state/web_state_impl.mm +++ b/ios/web/web_state/web_state_impl.mm
@@ -61,6 +61,8 @@ // Initialize the new session. web_state->GetNavigationManagerImpl().InitializeSession(); + web_state->GetNavigationManagerImpl().GetSessionController().delegate = + web_state->GetWebController(); return web_state; } @@ -907,6 +909,8 @@ restored_session_storage_ = session_storage; SessionStorageBuilder session_storage_builder; session_storage_builder.ExtractSessionState(this, session_storage); + GetNavigationManagerImpl().GetSessionController().delegate = + GetWebController(); } } // namespace web
diff --git a/ios/web/web_state/web_state_observer_inttest.mm b/ios/web/web_state/web_state_observer_inttest.mm index 26e9656e..349d5b40 100644 --- a/ios/web/web_state/web_state_observer_inttest.mm +++ b/ios/web/web_state/web_state_observer_inttest.mm
@@ -179,9 +179,16 @@ EXPECT_FALSE((*context)->IsRendererInitiated()); ASSERT_FALSE((*context)->GetResponseHeaders()); ASSERT_FALSE(web_state->IsLoading()); - // TODO(crbug.com/899827): Pending URL should exist and be owned by - // NavigationContext. - ASSERT_FALSE(web_state->GetNavigationManager()->GetPendingItem()); + NavigationItem* pendig_item = + web_state->GetNavigationManager()->GetPendingItem(); + if (web::features::StorePendingItemInContext()) { + ASSERT_TRUE(pendig_item); + EXPECT_EQ(url, pendig_item->GetURL()); + } else { + // TODO(crbug.com/899827): Pending URL should exist and be owned by + // NavigationContext. + ASSERT_FALSE(pendig_item); + } } // Verifies correctness of |NavigationContext| (|arg1|) for new page navigation
diff --git a/media/base/video_frame.h b/media/base/video_frame.h index bb7011a..813c804 100644 --- a/media/base/video_frame.h +++ b/media/base/video_frame.h
@@ -375,8 +375,15 @@ VideoPixelFormat format() const { return layout_.format(); } StorageType storage_type() const { return storage_type_; } + // The full dimensions of the video frame data. const gfx::Size& coded_size() const { return layout_.coded_size(); } + // A subsection of [0, 0, coded_size().width(), coded_size.height()]. This + // can be set to "soft-apply" a cropping. It determines the pointers into + // the data returned by visible_data(). const gfx::Rect& visible_rect() const { return visible_rect_; } + // Specifies that the |visible_rect| section of the frame is supposed to be + // scaled to this size when being presented. This can be used to represent + // anamorphic frames, or to "soft-apply" any custom scaling. const gfx::Size& natural_size() const { return natural_size_; } int stride(size_t plane) const {
diff --git a/media/base/video_frame_metadata.cc b/media/base/video_frame_metadata.cc index 2bdb0ed..0257844 100644 --- a/media/base/video_frame_metadata.cc +++ b/media/base/video_frame_metadata.cc
@@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/value_conversions.h" +#include "ui/gfx/geometry/rect.h" namespace media { @@ -85,6 +86,15 @@ base::CreateUnguessableTokenValue(value)); } +void VideoFrameMetadata::SetRect(Key key, const gfx::Rect& value) { + base::Value init[] = {base::Value(value.x()), base::Value(value.y()), + base::Value(value.width()), + base::Value(value.height())}; + SetValue(key, std::make_unique<base::ListValue>(base::Value::ListStorage{ + std::make_move_iterator(std::begin(init)), + std::make_move_iterator(std::end(init))})); +} + void VideoFrameMetadata::SetValue(Key key, std::unique_ptr<base::Value> value) { dictionary_.SetWithoutPathExpansion(ToInternalKey(key), std::move(value)); } @@ -157,6 +167,22 @@ return base::GetValueAsUnguessableToken(*internal_value, value); } +bool VideoFrameMetadata::GetRect(Key key, gfx::Rect* value) const { + const base::ListValue* internal_value = GetList(key); + if (!internal_value || internal_value->GetList().size() != 4) + return false; + *value = gfx::Rect(internal_value->GetList()[0].GetInt(), + internal_value->GetList()[1].GetInt(), + internal_value->GetList()[2].GetInt(), + internal_value->GetList()[3].GetInt()); + return true; +} + +const base::ListValue* VideoFrameMetadata::GetList(Key key) const { + return static_cast<const base::ListValue*>( + dictionary_.FindKeyOfType(ToInternalKey(key), base::Value::Type::LIST)); +} + const base::Value* VideoFrameMetadata::GetValue(Key key) const { return dictionary_.FindKey(ToInternalKey(key)); }
diff --git a/media/base/video_frame_metadata.h b/media/base/video_frame_metadata.h index 0c0b4213..5fa55b4 100644 --- a/media/base/video_frame_metadata.h +++ b/media/base/video_frame_metadata.h
@@ -17,6 +17,10 @@ #include "media/base/media_export.h" #include "media/base/video_rotation.h" +namespace gfx { +class Rect; +} + namespace media { class MEDIA_EXPORT VideoFrameMetadata { @@ -34,6 +38,24 @@ CAPTURE_BEGIN_TIME, CAPTURE_END_TIME, + // A counter that is increased by the producer of video frames each time + // it pushes out a new frame. By looking for gaps in this counter, clients + // can determine whether or not any frames have been dropped on the way from + // the producer between two consecutively received frames. Note that the + // counter may start at arbitrary values, so the absolute value of it has no + // meaning. + CAPTURE_COUNTER, + + // A base::ListValue containing 4 integers representing x, y, width, height + // of the rectangular region of the frame that has changed since the frame + // with the directly preceding CAPTURE_COUNTER. If that frame was not + // received, typically because it was dropped during transport from the + // producer, clients must assume that the entire frame has changed. + // The rectangle is relative to the full frame data, i.e. [0, 0, + // coded_size().width(), coded_size().height()]. It does not have to be + // fully contained within visible_rect(). + CAPTURE_UPDATE_RECT, + // Indicates that this frame must be copied to a new texture before use, // rather than being used directly. Specifically this is required for // WebView because of limitations about sharing surface textures between GL @@ -158,6 +180,7 @@ void SetTimeDelta(Key key, const base::TimeDelta& value); void SetTimeTicks(Key key, const base::TimeTicks& value); void SetUnguessableToken(Key key, const base::UnguessableToken& value); + void SetRect(Key key, const gfx::Rect& value); void SetValue(Key key, std::unique_ptr<base::Value> value); // Getters. Returns true if |key| is present, and its value has been set. @@ -170,7 +193,9 @@ bool GetTimeTicks(Key key, base::TimeTicks* value) const WARN_UNUSED_RESULT; bool GetUnguessableToken(Key key, base::UnguessableToken* value) const WARN_UNUSED_RESULT; - + bool GetRect(Key key, gfx::Rect* value) const WARN_UNUSED_RESULT; + // Returns null if |key| was not present or value was not a ListValue. + const base::ListValue* GetList(Key key) const WARN_UNUSED_RESULT; // Returns null if |key| was not present. const base::Value* GetValue(Key key) const WARN_UNUSED_RESULT;
diff --git a/media/capture/content/video_capture_oracle.cc b/media/capture/content/video_capture_oracle.cc index 9927616d..5e8d29df 100644 --- a/media/capture/content/video_capture_oracle.cc +++ b/media/capture/content/video_capture_oracle.cc
@@ -355,6 +355,10 @@ min_size_change_period_ = period; } +gfx::Size VideoCaptureOracle::capture_size() const { + return capture_size_; +} + // static const char* VideoCaptureOracle::EventAsString(Event event) { switch (event) {
diff --git a/media/capture/content/video_capture_oracle.h b/media/capture/content/video_capture_oracle.h index 01d961b..c1eb299 100644 --- a/media/capture/content/video_capture_oracle.h +++ b/media/capture/content/video_capture_oracle.h
@@ -84,9 +84,9 @@ // Returns true iff the captured frame should be delivered. |frame_timestamp| // is set to the timestamp that should be provided to the consumer of the // frame. - bool CompleteCapture(int frame_number, - bool capture_was_successful, - base::TimeTicks* frame_timestamp); + virtual bool CompleteCapture(int frame_number, + bool capture_was_successful, + base::TimeTicks* frame_timestamp); // Notify that all in-flight captures have been canceled. This has the same // effect as calling CompleteCapture() with a non-success status for all @@ -118,7 +118,7 @@ // Returns the capture frame size the client should use. This is updated by // calls to ObserveEventAndDecideCapture(). The oracle prevents too-frequent // changes to the capture size, to avoid stressing the end-to-end pipeline. - gfx::Size capture_size() const { return capture_size_; } + virtual gfx::Size capture_size() const; // Returns the oracle's estimate of the last time animation was detected. base::TimeTicks last_time_animation_was_detected() const {
diff --git a/media/capture/video/mock_video_frame_receiver.h b/media/capture/video/mock_video_frame_receiver.h index f690ff9..cee00ad 100644 --- a/media/capture/video/mock_video_frame_receiver.h +++ b/media/capture/video/mock_video_frame_receiver.h
@@ -29,6 +29,7 @@ MOCK_METHOD1(OnBufferRetired, void(int buffer_id)); MOCK_METHOD0(OnStarted, void()); MOCK_METHOD0(OnStartedUsingGpuDecode, void()); + MOCK_METHOD0(OnStopped, void()); void OnNewBuffer(int buffer_id, media::mojom::VideoBufferHandlePtr buffer_handle) override {
diff --git a/media/capture/video/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc index 6a401ff..ef84b21 100644 --- a/media/capture/video/video_capture_device_client.cc +++ b/media/capture/video/video_capture_device_client.cc
@@ -115,6 +115,7 @@ VideoCaptureDeviceClient::~VideoCaptureDeviceClient() { for (int buffer_id : buffer_ids_known_by_receiver_) receiver_->OnBufferRetired(buffer_id); + receiver_->OnStopped(); } // static
diff --git a/media/capture/video/video_frame_receiver.h b/media/capture/video/video_frame_receiver.h index 7f8454eb..baffd35 100644 --- a/media/capture/video/video_frame_receiver.h +++ b/media/capture/video/video_frame_receiver.h
@@ -56,6 +56,7 @@ virtual void OnLog(const std::string& message) = 0; virtual void OnStarted() = 0; virtual void OnStartedUsingGpuDecode() = 0; + virtual void OnStopped() = 0; }; } // namespace media
diff --git a/media/capture/video/video_frame_receiver_on_task_runner.cc b/media/capture/video/video_frame_receiver_on_task_runner.cc index 444260f..7f6bb4b 100644 --- a/media/capture/video/video_frame_receiver_on_task_runner.cc +++ b/media/capture/video/video_frame_receiver_on_task_runner.cc
@@ -72,4 +72,9 @@ base::BindOnce(&VideoFrameReceiver::OnStartedUsingGpuDecode, receiver_)); } +void VideoFrameReceiverOnTaskRunner::OnStopped() { + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&VideoFrameReceiver::OnStopped, receiver_)); +} + } // namespace media
diff --git a/media/capture/video/video_frame_receiver_on_task_runner.h b/media/capture/video/video_frame_receiver_on_task_runner.h index 48124aa..cb47221 100644 --- a/media/capture/video/video_frame_receiver_on_task_runner.h +++ b/media/capture/video/video_frame_receiver_on_task_runner.h
@@ -38,6 +38,7 @@ void OnLog(const std::string& message) override; void OnStarted() override; void OnStartedUsingGpuDecode() override; + void OnStopped() override; private: const base::WeakPtr<VideoFrameReceiver> receiver_;
diff --git a/media/gpu/windows/d3d11_video_decoder.cc b/media/gpu/windows/d3d11_video_decoder.cc index 09a4654e..1ebcc01 100644 --- a/media/gpu/windows/d3d11_video_decoder.cc +++ b/media/gpu/windows/d3d11_video_decoder.cc
@@ -613,13 +613,14 @@ texture_desc.SampleDesc.Count = 1; texture_desc.Usage = D3D11_USAGE_DEFAULT; texture_desc.BindFlags = D3D11_BIND_DECODER | D3D11_BIND_SHADER_RESOURCE; - texture_desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; if (base::FeatureList::IsEnabled( features::kDirectCompositionUseNV12DecodeSwapChain)) { // Decode swap chains do not support shared resources. // TODO(sunnyps): Find a workaround for when the decoder moves to its own // thread and D3D device. See https://crbug.com/911847 texture_desc.MiscFlags = 0; + } else { + texture_desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; } if (config_.is_encrypted()) texture_desc.MiscFlags |= D3D11_RESOURCE_MISC_HW_PROTECTED;
diff --git a/media/gpu/windows/dxva_video_decode_accelerator_win.cc b/media/gpu/windows/dxva_video_decode_accelerator_win.cc index 9c7c5eba5..49de7510 100644 --- a/media/gpu/windows/dxva_video_decode_accelerator_win.cc +++ b/media/gpu/windows/dxva_video_decode_accelerator_win.cc
@@ -44,6 +44,7 @@ #include "base/win/windows_version.h" #include "build/build_config.h" #include "gpu/config/gpu_driver_bug_workarounds.h" +#include "gpu/config/gpu_finch_features.h" #include "gpu/config/gpu_preferences.h" #include "media/base/media_log.h" #include "media/base/media_switches.h" @@ -1810,12 +1811,19 @@ RETURN_ON_HR_FAILURE(hr, "Failed to get stream attributes", false); out_attributes->SetUINT32(MF_SA_D3D11_BINDFLAGS, D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_DECODER); - // For some reason newer Intel drivers need D3D11_BIND_DECODER textures to - // be created with a share handle or they'll crash in - // CreateShaderResourceView. Technically MF_SA_D3D11_SHARED_WITHOUT_MUTEX - // is only honored by the sample allocator, not by the media foundation - // transform, but Microsoft's h.264 transform happens to pass it through. - out_attributes->SetUINT32(MF_SA_D3D11_SHARED_WITHOUT_MUTEX, TRUE); + // TODO(sunnyps): Find if we can always set resource sharing to disabled. + if (base::FeatureList::IsEnabled( + features::kDirectCompositionUseNV12DecodeSwapChain)) { + // Decode swap chains do not support shared resources. + out_attributes->SetUINT32(MF_SA_D3D11_SHARED, FALSE); + } else { + // For some reason newer Intel drivers need D3D11_BIND_DECODER textures to + // be created with a share handle or they'll crash in + // CreateShaderResourceView. Technically MF_SA_D3D11_SHARED_WITHOUT_MUTEX + // is only honored by the sample allocator, not by the media foundation + // transform, but Microsoft's h.264 transform happens to pass it through. + out_attributes->SetUINT32(MF_SA_D3D11_SHARED_WITHOUT_MUTEX, TRUE); + } } hr = decoder_->SetInputType(0, media_type.Get(), 0); // No flags
diff --git a/media/test/data/README.md b/media/test/data/README.md index 27e9790..fc2f94e 100644 --- a/media/test/data/README.md +++ b/media/test/data/README.md
@@ -828,6 +828,11 @@ ``` jpegtran -grayscale pixel-1280x720.jpg > pixel-1280x720-grayscale.jpg ``` +#### pixel-1280x720-yuv420.jpg +A version of pixel-1280x720.jpg converted to 4:2:0 subsampling using: +``` +convert pixel-1280x720.jpg -sampling-factor 4:2:0 pixel-1280x720-yuv420.jpg +``` #### peach_pi-1280x720.jpg Single MJPEG encoded frame of 1280x720, captured on Samsung Chromebook 2(13").
diff --git a/media/test/data/pixel-1280x720-yuv420.jpg b/media/test/data/pixel-1280x720-yuv420.jpg new file mode 100644 index 0000000..3074792 --- /dev/null +++ b/media/test/data/pixel-1280x720-yuv420.jpg Binary files differ
diff --git a/remoting/signaling/ftl.proto b/remoting/signaling/ftl.proto index 967d995..748579b 100644 --- a/remoting/signaling/ftl.proto +++ b/remoting/signaling/ftl.proto
@@ -8,6 +8,8 @@ package remoting.ftl; +// Enums + // This is exactly google.protobuf.Duration. The Chromium proto_library // template has trouble importing protobuf outside the current directory. message Duration { @@ -41,6 +43,33 @@ } } +message DeviceIdType { + enum Type { + UNKNOWN = 0; + ANDROID_IID_TOKEN = 1; + IOS_VENDOR_ID = 2; + WEB_UUID = 3; + } +} + +message SignInGaiaMode { + enum Value { + DEFAULT_CREATE_ACCOUNT = 0; + LOOKUP = 1; + LOOKUP_AND_SIGN_IN = 2; + } +} + +message FtlCapability { + enum Feature { + UNKNOWN = 0; + RECEIVE_CALLS_FROM_GAIA = 86; + GAIA_REACHABLE = 87; + } +} + +// Messages + message ClientInfo { // Renamed from major, minor, point because they are defined in glibc macros. int32 version_major = 3; @@ -83,6 +112,24 @@ string ice_transport_policy = 6; } +message DeviceId { + DeviceIdType.Type type = 1; + string id = 2; +} + +message RegisterData { + DeviceId device_id = 1; + string locale = 8; + repeated int32 caps = 9; +} + +message AuthToken { + bytes payload = 1; + int64 expires_in = 2; +} + +// Requests and responses + message GetICEServerRequest { RequestHeader header = 1; bool unblock_me = 2; @@ -93,3 +140,16 @@ ResponseHeader header = 1; ICEConfiguration ice_config = 4; } + +message SignInGaiaRequest { + RequestHeader header = 1; + RegisterData register_data = 2; + SignInGaiaMode.Value mode = 3; + string app = 4; +} + +message SignInGaiaResponse { + ResponseHeader header = 1; + bytes registration_id = 2; + AuthToken auth_token = 4; +}
diff --git a/remoting/signaling/ftl_client.cc b/remoting/signaling/ftl_client.cc index 92eca00..9fe2039 100644 --- a/remoting/signaling/ftl_client.cc +++ b/remoting/signaling/ftl_client.cc
@@ -12,6 +12,8 @@ #include "google_apis/google_api_keys.h" #include "third_party/grpc/src/include/grpcpp/grpcpp.h" +namespace remoting { + namespace { constexpr char kChromotingAppIdentifier[] = "CRD"; @@ -19,9 +21,11 @@ // TODO(yuweih): We should target different service environments. constexpr char kFtlServerEndpoint[] = "instantmessaging-pa.googleapis.com"; -} // namespace +constexpr ftl::FtlCapability::Feature kFtlCapabilities[] = { + ftl::FtlCapability_Feature_RECEIVE_CALLS_FROM_GAIA, + ftl::FtlCapability_Feature_GAIA_REACHABLE}; -namespace remoting { +} // namespace FtlClient::FtlClient(OAuthTokenGetter* token_getter) : weak_factory_(this) { DCHECK(token_getter); @@ -29,6 +33,7 @@ auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions()); auto channel = grpc::CreateChannel(kFtlServerEndpoint, channel_creds); peer_to_peer_stub_ = PeerToPeer::NewStub(channel); + registration_stub_ = Registration::NewStub(channel); } FtlClient::~FtlClient() = default; @@ -43,6 +48,32 @@ request, std::move(callback)); } +void FtlClient::SignInGaia(const std::string& device_id, + ftl::SignInGaiaMode::Value sign_in_gaia_mode, + RpcCallback<ftl::SignInGaiaResponse> callback) { + ftl::SignInGaiaRequest request; + request.set_allocated_header(BuildRequestHeader().release()); + request.set_app(kChromotingAppIdentifier); + request.set_mode(sign_in_gaia_mode); + + request.mutable_register_data()->mutable_device_id()->set_id(device_id); + + // TODO(yuweih): Consider using different device ID type. + request.mutable_register_data()->mutable_device_id()->set_type( + ftl::DeviceIdType_Type_WEB_UUID); + + size_t ftl_capability_count = + sizeof(kFtlCapabilities) / sizeof(ftl::FtlCapability::Feature); + for (size_t i = 0; i < ftl_capability_count; i++) { + request.mutable_register_data()->add_caps(kFtlCapabilities[i]); + } + + GetOAuthTokenAndExecuteRpc( + base::BindOnce(&Registration::Stub::AsyncSignInGaia, + base::Unretained(registration_stub_.get())), + request, std::move(callback)); +} + template <typename RequestType, typename ResponseType> void FtlClient::GetOAuthTokenAndExecuteRpc( GrpcAsyncDispatcher::AsyncRpcFunction<RequestType, ResponseType> rpc,
diff --git a/remoting/signaling/ftl_client.h b/remoting/signaling/ftl_client.h index a5e8004..bd52e09 100644 --- a/remoting/signaling/ftl_client.h +++ b/remoting/signaling/ftl_client.h
@@ -30,9 +30,16 @@ // Retrieves the ice server configs. void GetIceServer(RpcCallback<ftl::GetICEServerResponse> callback); + // Performs a SignInGaia call for this device. + void SignInGaia(const std::string& device_id, + ftl::SignInGaiaMode::Value sign_in_gaia_mode, + RpcCallback<ftl::SignInGaiaResponse> callback); + private: using PeerToPeer = google::internal::communications::instantmessaging::v1::PeerToPeer; + using Registration = + google::internal::communications::instantmessaging::v1::Registration; template <typename RequestType, typename ResponseType> void GetOAuthTokenAndExecuteRpc( @@ -54,6 +61,7 @@ OAuthTokenGetter* token_getter_; std::unique_ptr<PeerToPeer::Stub> peer_to_peer_stub_; + std::unique_ptr<Registration::Stub> registration_stub_; GrpcAsyncDispatcher dispatcher_; base::WeakPtrFactory<FtlClient> weak_factory_;
diff --git a/remoting/signaling/ftl_services.proto b/remoting/signaling/ftl_services.proto index d9d31bd2..f6de15f 100644 --- a/remoting/signaling/ftl_services.proto +++ b/remoting/signaling/ftl_services.proto
@@ -16,3 +16,8 @@ rpc GetICEServer(remoting.ftl.GetICEServerRequest) returns (remoting.ftl.GetICEServerResponse) {} } + +service Registration { + rpc SignInGaia(remoting.ftl.SignInGaiaRequest) + returns (remoting.ftl.SignInGaiaResponse) {} +}
diff --git a/remoting/test/ftl_signaling_playground.cc b/remoting/test/ftl_signaling_playground.cc index 3ca052a..d17cc16b 100644 --- a/remoting/test/ftl_signaling_playground.cc +++ b/remoting/test/ftl_signaling_playground.cc
@@ -5,14 +5,18 @@ #include "remoting/test/ftl_signaling_playground.h" #include <inttypes.h> +#include <string> #include <utility> +#include "base/base64.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/command_line.h" #include "base/files/file_path.h" +#include "base/guid.h" #include "base/logging.h" #include "base/path_service.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "remoting/base/oauth_token_getter_impl.h" #include "remoting/signaling/ftl_client.h" @@ -49,6 +53,18 @@ return ReadString(); } +void PrintGrpcStatusError(const grpc::Status& status) { + DCHECK(!status.ok()); + LOG(ERROR) << "RPC failed. Code=" << status.error_code() << ", " + << "Message=" << status.error_message(); + if (status.error_code() == grpc::StatusCode::UNAVAILABLE) { + VLOG(0) + << "Set the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable " + << "to third_party/grpc/src/etc/roots.pem if gRPC cannot locate the " + << "root certificates."; + } +} + } // namespace namespace remoting { @@ -92,6 +108,36 @@ auth_code, base::DoNothing::Repeatedly<const std::string&, const std::string&>()); client_ = std::make_unique<FtlClient>(token_getter_.get()); + + StartLoop(); +} + +void FtlSignalingPlayground::StartLoop() { + while (true) { + printf( + "\nOptions:\n" + " 1. GetIceServer\n" + " 2. SignInGaia\n" + " 3. Quit\n\n" + "Your choice [number]: "); + int choice = 0; + base::StringToInt(ReadString(), &choice); + base::RunLoop run_loop; + switch (choice) { + case 1: + GetIceServer(run_loop.QuitClosure()); + break; + case 2: + SignInGaia(run_loop.QuitClosure()); + break; + case 3: + return; + default: + fprintf(stderr, "Unknown option\n"); + continue; + } + run_loop.Run(); + } } void FtlSignalingPlayground::GetIceServer(base::OnceClosure on_done) { @@ -124,14 +170,43 @@ } } } else { - LOG(ERROR) << "RPC failed. Code=" << status.error_code() << ", " - << "Message=" << status.error_message(); - if (status.error_code() == grpc::StatusCode::UNAVAILABLE) { - VLOG(0) - << "Set the GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable " - << "to third_party/grpc/src/etc/roots.pem if gRPC cannot locate the " - << "root certificates."; - } + PrintGrpcStatusError(status); + } + std::move(on_done).Run(); +} + +void FtlSignalingPlayground::SignInGaia(base::OnceClosure on_done) { + DCHECK(client_); + VLOG(0) << "Running SignInGaia..."; + // TODO(yuweih): Store generated GUID and reuse them when possible. + std::string device_id = "crd-web-" + base::GenerateGUID(); + VLOG(0) << "Using device_id: " << device_id; + VLOG(0) << "Using sign_in_gaia_mode: DEFAULT_CREATE_ACCOUNT"; + client_->SignInGaia( + device_id, ftl::SignInGaiaMode_Value_DEFAULT_CREATE_ACCOUNT, + base::BindOnce(&FtlSignalingPlayground::OnSignInGaiaResponse, + std::move(on_done))); +} + +// static +void FtlSignalingPlayground::OnSignInGaiaResponse( + base::OnceClosure on_done, + grpc::Status status, + const ftl::SignInGaiaResponse& response) { + if (status.ok()) { + // TODO(yuweih): Allow loading auth token directly from command line. + std::string registration_id_base64; + std::string auth_token_base64; + base::Base64Encode(response.registration_id(), ®istration_id_base64); + base::Base64Encode(response.auth_token().payload(), &auth_token_base64); + printf( + "registration_id(base64)=%s\n" + "auth_token.payload(base64)=%s\n" + "auth_token.expires_in=%" PRId64 "\n", + registration_id_base64.c_str(), auth_token_base64.c_str(), + response.auth_token().expires_in()); + } else { + PrintGrpcStatusError(status); } std::move(on_done).Run(); }
diff --git a/remoting/test/ftl_signaling_playground.h b/remoting/test/ftl_signaling_playground.h index e261a32..d9b75bc 100644 --- a/remoting/test/ftl_signaling_playground.h +++ b/remoting/test/ftl_signaling_playground.h
@@ -24,13 +24,20 @@ bool ShouldPrintHelp(); void PrintHelp(); void StartAndAuthenticate(); - void GetIceServer(base::OnceClosure on_done); private: + void StartLoop(); + + void GetIceServer(base::OnceClosure on_done); static void OnGetIceServerResponse(base::OnceClosure on_done, grpc::Status status, const ftl::GetICEServerResponse& response); + void SignInGaia(base::OnceClosure on_done); + static void OnSignInGaiaResponse(base::OnceClosure on_done, + grpc::Status status, + const ftl::SignInGaiaResponse& response); + std::unique_ptr<TestOAuthTokenGetterFactory> token_getter_factory_; std::unique_ptr<OAuthTokenGetter> token_getter_; std::unique_ptr<FtlClient> client_;
diff --git a/remoting/test/ftl_signaling_playground_main.cc b/remoting/test/ftl_signaling_playground_main.cc index 239f378..eadfb0aa 100644 --- a/remoting/test/ftl_signaling_playground_main.cc +++ b/remoting/test/ftl_signaling_playground_main.cc
@@ -27,9 +27,6 @@ mojo::core::Init(); playground.StartAndAuthenticate(); - base::RunLoop run_loop; - playground.GetIceServer(run_loop.QuitClosure()); - run_loop.Run(); return 0; }
diff --git a/services/video_capture/broadcasting_receiver.cc b/services/video_capture/broadcasting_receiver.cc index 9babeca..64860cab 100644 --- a/services/video_capture/broadcasting_receiver.cc +++ b/services/video_capture/broadcasting_receiver.cc
@@ -126,6 +126,19 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } +void BroadcastingReceiver::HideSourceRestartFromClients( + base::OnceClosure on_stopped_handler) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + on_stopped_handler_ = std::move(on_stopped_handler); + status_ = Status::kDeviceIsRestarting; +} + +void BroadcastingReceiver::SetOnStoppedHandler( + base::OnceClosure on_stopped_handler) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + on_stopped_handler_ = std::move(on_stopped_handler); +} + int32_t BroadcastingReceiver::AddClient(mojom::ReceiverPtr client) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); auto client_id = next_client_id_++; @@ -263,6 +276,21 @@ status_ = Status::kOnStartedUsingGpuDecodeHasBeenCalled; } +void BroadcastingReceiver::OnStopped() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (status_ == Status::kDeviceIsRestarting) { + status_ = Status::kOnStartedHasNotYetBeenCalled; + std::move(on_stopped_handler_).Run(); + } else { + for (auto& client : clients_) { + client.second.client()->OnStopped(); + } + status_ = Status::kOnStoppedHasBeenCalled; + if (on_stopped_handler_) + std::move(on_stopped_handler_).Run(); + } +} + void BroadcastingReceiver::OnClientFinishedConsumingFrame(int32_t buffer_id) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); auto& buffer_context = LookupBufferContextFromBufferId(buffer_id);
diff --git a/services/video_capture/broadcasting_receiver.h b/services/video_capture/broadcasting_receiver.h index 47636dee..4aa648b 100644 --- a/services/video_capture/broadcasting_receiver.h +++ b/services/video_capture/broadcasting_receiver.h
@@ -22,6 +22,17 @@ BroadcastingReceiver(); ~BroadcastingReceiver() override; + // Indicates to the BroadcastingReceiver that we want to restart the source + // without letting connected clients know about the restart. The + // BroadcastingReceiver will hide the OnStopped() event sent by the source + // from the connected clients and instead invoke the given + // |on_stopped_handler|. It will also not forward the subsequent + // OnStarted() and possibly OnStartedUsingGpuDecode() events to clients who + // have already received these events. + void HideSourceRestartFromClients(base::OnceClosure on_stopped_handler); + + void SetOnStoppedHandler(base::OnceClosure on_stopped_handler); + // Returns a client_id that can be used for a call to Suspend/Resume/Remove. int32_t AddClient(mojom::ReceiverPtr client); void SuspendClient(int32_t client_id); @@ -43,17 +54,21 @@ void OnLog(const std::string& message) override; void OnStarted() override; void OnStartedUsingGpuDecode() override; + void OnStopped() override; private: enum class Status { kOnStartedHasNotYetBeenCalled, kOnStartedHasBeenCalled, kOnStartedUsingGpuDecodeHasBeenCalled, + kDeviceIsRestarting, kOnErrorHasBeenCalled, + kOnStoppedHasBeenCalled }; // Wrapper that suppresses calls to OnStarted() and OnStartedUsingGpuDecode() - // after they have already been called once. + // after they have already been called once. Keeps track of whether or not + // a client is suspended. class ClientContext { public: explicit ClientContext(mojom::ReceiverPtr client); @@ -108,6 +123,7 @@ std::map<int32_t /*client_id*/, ClientContext> clients_; std::vector<BufferContext> buffer_contexts_; Status status_; + base::OnceClosure on_stopped_handler_; // Keeps track of the last VideoCaptureError that arrived via OnError(). // This is used for relaying the error event to clients that connect after the
diff --git a/services/video_capture/device_factory_media_to_mojo_adapter.cc b/services/video_capture/device_factory_media_to_mojo_adapter.cc index d8a6fdc..bc9ea7a 100644 --- a/services/video_capture/device_factory_media_to_mojo_adapter.cc +++ b/services/video_capture/device_factory_media_to_mojo_adapter.cc
@@ -113,7 +113,7 @@ // Revoke the access and close the device, then bind to the new request. ActiveDeviceEntry& device_entry = active_device_iter->second; device_entry.binding->Unbind(); - device_entry.device->Stop(base::DoNothing()); + device_entry.device->Stop(); device_entry.binding->Bind(std::move(device_request)); device_entry.binding->set_connection_error_handler(base::Bind( &DeviceFactoryMediaToMojoAdapter::OnClientConnectionErrorOrClose, @@ -191,7 +191,7 @@ video_capture::uma::LogVideoCaptureServiceEvent( video_capture::uma::SERVICE_LOST_CONNECTION_TO_BROWSER); - active_devices_by_id_[device_id].device->Stop(base::DoNothing()); + active_devices_by_id_[device_id].device->Stop(); active_devices_by_id_.erase(device_id); }
diff --git a/services/video_capture/device_media_to_mojo_adapter.cc b/services/video_capture/device_media_to_mojo_adapter.cc index e4069c8..4e2c36b5 100644 --- a/services/video_capture/device_media_to_mojo_adapter.cc +++ b/services/video_capture/device_media_to_mojo_adapter.cc
@@ -29,13 +29,6 @@ decoder_task_runner); } -void FinishUpCallToStop( - std::unique_ptr<video_capture::ReceiverMojoToMediaAdapter> receiver, - video_capture::mojom::Device::StopCallback callback) { - receiver.reset(); - std::move(callback).Run(); -} - } // anonymous namespace namespace video_capture { @@ -138,27 +131,24 @@ device_->TakePhoto(std::move(scoped_callback)); } -void DeviceMediaToMojoAdapter::Stop(StopCallback callback) { +void DeviceMediaToMojoAdapter::Stop() { DCHECK(thread_checker_.CalledOnValidThread()); - if (!device_started_) { - std::move(callback).Run(); + if (!device_started_) return; - } device_started_ = false; weak_factory_.InvalidateWeakPtrs(); device_->StopAndDeAllocate(); - // We need to post a continuation of the stop routine to the end of the - // message queue, because |device_->StopAndDeAllocate()| may post messages - // (e.g. OnBufferRetired()) to a WeakPtr to |receiver_| to this queue, and we - // need those messages to be sent out before we continue. - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&FinishUpCallToStop, std::move(receiver_), - std::move(callback))); + // We need to post the deletion of receiver to the end of the message queue, + // because |device_->StopAndDeAllocate()| may post messages (e.g. + // OnBufferRetired()) to a WeakPtr to |receiver_| to this queue, and we need + // those messages to be sent before we invalidate the WeakPtr. + base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, + std::move(receiver_)); } void DeviceMediaToMojoAdapter::OnClientConnectionErrorOrClose() { DCHECK(thread_checker_.CalledOnValidThread()); - Stop(base::DoNothing()); + Stop(); } // static
diff --git a/services/video_capture/device_media_to_mojo_adapter.h b/services/video_capture/device_media_to_mojo_adapter.h index 09a69def..78bf74481 100644 --- a/services/video_capture/device_media_to_mojo_adapter.h +++ b/services/video_capture/device_media_to_mojo_adapter.h
@@ -38,8 +38,8 @@ void SetPhotoOptions(media::mojom::PhotoSettingsPtr settings, SetPhotoOptionsCallback callback) override; void TakePhoto(TakePhotoCallback callback) override; - void Stop(StopCallback callback) override; + void Stop(); void OnClientConnectionErrorOrClose(); // Returns the fixed maximum number of buffers passed to the constructor
diff --git a/services/video_capture/public/cpp/mock_receiver.h b/services/video_capture/public/cpp/mock_receiver.h index aa8fb08..6493b21 100644 --- a/services/video_capture/public/cpp/mock_receiver.h +++ b/services/video_capture/public/cpp/mock_receiver.h
@@ -45,6 +45,7 @@ MOCK_METHOD1(OnLog, void(const std::string&)); MOCK_METHOD0(OnStarted, void()); MOCK_METHOD0(OnStartedUsingGpuDecode, void()); + MOCK_METHOD0(OnStopped, void()); private: const mojo::Binding<mojom::Receiver> binding_;
diff --git a/services/video_capture/public/cpp/receiver_media_to_mojo_adapter.cc b/services/video_capture/public/cpp/receiver_media_to_mojo_adapter.cc index f1d7dd3b..80ce1b7 100644 --- a/services/video_capture/public/cpp/receiver_media_to_mojo_adapter.cc +++ b/services/video_capture/public/cpp/receiver_media_to_mojo_adapter.cc
@@ -74,4 +74,8 @@ receiver_->OnStartedUsingGpuDecode(); } +void ReceiverMediaToMojoAdapter::OnStopped() { + receiver_->OnStopped(); +} + } // namespace video_capture
diff --git a/services/video_capture/public/cpp/receiver_media_to_mojo_adapter.h b/services/video_capture/public/cpp/receiver_media_to_mojo_adapter.h index 3080aa8..be88a5d5 100644 --- a/services/video_capture/public/cpp/receiver_media_to_mojo_adapter.h +++ b/services/video_capture/public/cpp/receiver_media_to_mojo_adapter.h
@@ -32,6 +32,7 @@ void OnLog(const std::string& message) override; void OnStarted() override; void OnStartedUsingGpuDecode() override; + void OnStopped() override; private: std::unique_ptr<media::VideoFrameReceiver> receiver_;
diff --git a/services/video_capture/public/mojom/device.mojom b/services/video_capture/public/mojom/device.mojom index 82f3d7c..86df8a3b 100644 --- a/services/video_capture/public/mojom/device.mojom +++ b/services/video_capture/public/mojom/device.mojom
@@ -10,11 +10,11 @@ // Represents access to a video capture device available on the machine. // The device is stopped automatically when the message pipe corresponding to -// either the Device or the given |receiver| is closed. Note, however, that -// as a response to closing the device, the service may still need to send out -// events such as Receiver.OnBufferRetired(). Clients who need to wait for this -// to complete must explicitly call Stop() and wait for its callback to indicate -// that all events related to stopping device have been sent out. +// either the Device or the given |receiver| is closed. Note that as a response +// to stopping the device, the service may still need to send out events such as +// Receiver.OnBufferRetired() to |receiver|. The service will send a final event +// Receiver.OnStopped() to indicate that stopping has completed and no further +// events are going to be sent to |receiver|. interface Device { Start(media.mojom.VideoCaptureParams requested_settings, Receiver receiver); MaybeSuspend(); @@ -25,6 +25,4 @@ => (bool success); TakePhoto() => (media.mojom.Blob? blob); - - Stop() => (); };
diff --git a/services/video_capture/public/mojom/receiver.mojom b/services/video_capture/public/mojom/receiver.mojom index 22a8846e..676a80a 100644 --- a/services/video_capture/public/mojom/receiver.mojom +++ b/services/video_capture/public/mojom/receiver.mojom
@@ -20,4 +20,5 @@ OnLog(string message); OnStarted(); OnStartedUsingGpuDecode(); + OnStopped(); };
diff --git a/services/video_capture/receiver_mojo_to_media_adapter.cc b/services/video_capture/receiver_mojo_to_media_adapter.cc index 69718b1d..0b6ac891 100644 --- a/services/video_capture/receiver_mojo_to_media_adapter.cc +++ b/services/video_capture/receiver_mojo_to_media_adapter.cc
@@ -68,4 +68,8 @@ receiver_->OnStartedUsingGpuDecode(); } +void ReceiverMojoToMediaAdapter::OnStopped() { + receiver_->OnStopped(); +} + } // namespace video_capture
diff --git a/services/video_capture/receiver_mojo_to_media_adapter.h b/services/video_capture/receiver_mojo_to_media_adapter.h index f1bd5dc..5aaa4a89 100644 --- a/services/video_capture/receiver_mojo_to_media_adapter.h +++ b/services/video_capture/receiver_mojo_to_media_adapter.h
@@ -36,6 +36,7 @@ void OnLog(const std::string& message) override; void OnStarted() override; void OnStartedUsingGpuDecode() override; + void OnStopped() override; private: mojom::ReceiverPtr receiver_;
diff --git a/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc b/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc index 91d5e2a..615c039 100644 --- a/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc +++ b/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc
@@ -194,21 +194,22 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } -void SharedMemoryVirtualDeviceMojoAdapter::Stop(StopCallback callback) { +void SharedMemoryVirtualDeviceMojoAdapter::Stop() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!receiver_.is_bound()) { - std::move(callback).Run(); + if (!receiver_.is_bound()) return; - } // Unsubscribe from connection error callbacks. receiver_.set_connection_error_handler(base::OnceClosure()); + // Send out OnBufferRetired events and OnStopped. + for (auto buffer_id : known_buffer_ids_) + receiver_->OnBufferRetired(buffer_id); + receiver_->OnStopped(); receiver_.reset(); - std::move(callback).Run(); } void SharedMemoryVirtualDeviceMojoAdapter::OnReceiverConnectionErrorOrClose() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - Stop(base::DoNothing()); + Stop(); } } // namespace video_capture
diff --git a/services/video_capture/shared_memory_virtual_device_mojo_adapter.h b/services/video_capture/shared_memory_virtual_device_mojo_adapter.h index b1a2483..45c4a2d4 100644 --- a/services/video_capture/shared_memory_virtual_device_mojo_adapter.h +++ b/services/video_capture/shared_memory_virtual_device_mojo_adapter.h
@@ -44,7 +44,8 @@ void SetPhotoOptions(media::mojom::PhotoSettingsPtr settings, SetPhotoOptionsCallback callback) override; void TakePhoto(TakePhotoCallback callback) override; - void Stop(StopCallback callback) override; + + void Stop(); // Returns the fixed maximum number of buffers passed to the constructor // of VideoCaptureBufferPoolImpl.
diff --git a/services/video_capture/test/fake_device_unittest.cc b/services/video_capture/test/fake_device_unittest.cc index 3f9cde1..4818d99 100644 --- a/services/video_capture/test/fake_device_unittest.cc +++ b/services/video_capture/test/fake_device_unittest.cc
@@ -106,20 +106,20 @@ ASSERT_LE(num_buffers_created, kMaxBufferPoolBuffers); } -// Tests that OnBufferRetired() events get sent out to the receiver when the -// device is stopped. +// Tests that when the device is stopped OnBufferRetired() events get sent out +// to the receiver followed by OnStopped(). TEST_F(FakeVideoCaptureDeviceTest, BuffersGetRetiredWhenDeviceIsStopped) { base::RunLoop wait_for_frames_loop; static const int kNumFramesToWaitFor = 2; - std::vector<int32_t> received_buffer_ids; + std::vector<int32_t> known_buffer_ids; int num_frames_arrived = 0; mojom::ReceiverPtr receiver_proxy; MockReceiver receiver(mojo::MakeRequest(&receiver_proxy)); EXPECT_CALL(receiver, DoOnNewBuffer(_, _)) .WillRepeatedly( - Invoke([&received_buffer_ids](int32_t buffer_id, - media::mojom::VideoBufferHandlePtr*) { - received_buffer_ids.push_back(buffer_id); + Invoke([&known_buffer_ids](int32_t buffer_id, + media::mojom::VideoBufferHandlePtr*) { + known_buffer_ids.push_back(buffer_id); })); EXPECT_CALL(receiver, DoOnFrameReadyInBuffer(_, _, _, _)) .WillRepeatedly( @@ -133,23 +133,22 @@ std::move(receiver_proxy)); wait_for_frames_loop.Run(); - base::RunLoop wait_for_buffers_retired_loop; + base::RunLoop wait_for_on_stopped_loop; EXPECT_CALL(receiver, DoOnBufferRetired(_)) - .WillRepeatedly( - Invoke([&received_buffer_ids, - &wait_for_buffers_retired_loop](int32_t buffer_id) { - auto iter = std::find(received_buffer_ids.begin(), - received_buffer_ids.end(), buffer_id); - ASSERT_TRUE(iter != received_buffer_ids.end()); - received_buffer_ids.erase(iter); - if (received_buffer_ids.empty()) { - wait_for_buffers_retired_loop.Quit(); - } - })); + .WillRepeatedly(Invoke([&known_buffer_ids](int32_t buffer_id) { + auto iter = std::find(known_buffer_ids.begin(), known_buffer_ids.end(), + buffer_id); + ASSERT_TRUE(iter != known_buffer_ids.end()); + known_buffer_ids.erase(iter); + })); + EXPECT_CALL(receiver, OnStopped()) + .WillOnce(Invoke( + [&wait_for_on_stopped_loop]() { wait_for_on_stopped_loop.Quit(); })); // Stop the device i420_fake_device_proxy_.reset(); - wait_for_buffers_retired_loop.Run(); + wait_for_on_stopped_loop.Run(); + ASSERT_TRUE(known_buffer_ids.empty()); } // This requires platforms where base::SharedMemoryHandle is backed by a
diff --git a/services/video_capture/test/mock_device_shared_access_unittest.cc b/services/video_capture/test/mock_device_shared_access_unittest.cc index da8e46f..70b7138 100644 --- a/services/video_capture/test/mock_device_shared_access_unittest.cc +++ b/services/video_capture/test/mock_device_shared_access_unittest.cc
@@ -279,7 +279,7 @@ } TEST_F(MockVideoCaptureDeviceSharedAccessTest, - SecondClientsForcesReopenWithDifferentSettings) { + SecondClientForcesReopenWithDifferentSettings) { LetClient1ConnectWithRequestableSettingsAndExpectToGetThem(); subscription_1_->Activate(); @@ -306,9 +306,11 @@ SendFrameAndExpectToArriveAtBothSubscribers(); } -TEST_F( - MockVideoCaptureDeviceSharedAccessTest, - ExistingBuffersAreRetiredAndOnStartedIsNotSentAgainWhenDeviceIsReopened) { +// Tests that existing buffers are retired but no OnStopped() and OnStarted() +// event is sent to existing client when the device internally restarts because +// a new client connects with |force_reopen_with_new_settings| set to true. +TEST_F(MockVideoCaptureDeviceSharedAccessTest, + InternalDeviceRestartIsTransparentToExistingSubscribers) { LetClient1ConnectWithRequestableSettingsAndExpectToGetThem(); EXPECT_CALL(mock_receiver_1_, DoOnNewBuffer(_, _)).Times(1); EXPECT_CALL(mock_receiver_1_, OnStarted()).Times(1); @@ -327,6 +329,7 @@ EXPECT_CALL(mock_receiver_1_, DoOnBufferRetired(_)).Times(1); EXPECT_CALL(mock_receiver_1_, DoOnNewBuffer(_, _)).Times(1); } + EXPECT_CALL(mock_receiver_1_, OnStopped()).Times(0); EXPECT_CALL(mock_receiver_1_, OnStarted()).Times(0); LetClient2ConnectWithRequestableSettings( @@ -336,6 +339,7 @@ mock_device_.SendOnStarted(); SendFrameAndExpectToArriveAtBothSubscribers(); + Mock::VerifyAndClearExpectations(&mock_receiver_1_); } TEST_F(MockVideoCaptureDeviceSharedAccessTest,
diff --git a/services/video_capture/test/virtual_device_unittest.cc b/services/video_capture/test/virtual_device_unittest.cc index aad1088..58908c7 100644 --- a/services/video_capture/test/virtual_device_unittest.cc +++ b/services/video_capture/test/virtual_device_unittest.cc
@@ -165,6 +165,21 @@ device_adapter_->RequestFrameBuffer(kTestFrameSize, kTestPixelFormat, nullptr, request_frame_buffer_callback.Get()); wait_loop2.RunUntilIdle(); + + // Verify that when stopping the device, the receiver receives calls to + // OnBufferRetired() followed by a single call to OnStopped(). + base::RunLoop wait_for_stopped_loop; + { + testing::InSequence s; + EXPECT_CALL(receiver, DoOnBufferRetired(_)) + .Times(SharedMemoryVirtualDeviceMojoAdapter:: + max_buffer_pool_buffer_count()); + EXPECT_CALL(receiver, OnStopped()) + .WillOnce(Invoke( + [&wait_for_stopped_loop]() { wait_for_stopped_loop.Quit(); })); + } + device_adapter_->Stop(); + wait_for_stopped_loop.Run(); } } // namespace video_capture
diff --git a/services/video_capture/texture_virtual_device_mojo_adapter.cc b/services/video_capture/texture_virtual_device_mojo_adapter.cc index 8bfdb327..42c604de 100644 --- a/services/video_capture/texture_virtual_device_mojo_adapter.cc +++ b/services/video_capture/texture_virtual_device_mojo_adapter.cc
@@ -109,21 +109,22 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } -void TextureVirtualDeviceMojoAdapter::Stop(StopCallback callback) { +void TextureVirtualDeviceMojoAdapter::Stop() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!receiver_.is_bound()) { - std::move(callback).Run(); + if (!receiver_.is_bound()) return; - } // Unsubscribe from connection error callbacks. receiver_.set_connection_error_handler(base::OnceClosure()); + // Send out OnBufferRetired events and OnStopped. + for (const auto& entry : known_buffer_handles_) + receiver_->OnBufferRetired(entry.first); + receiver_->OnStopped(); receiver_.reset(); - std::move(callback).Run(); } void TextureVirtualDeviceMojoAdapter::OnReceiverConnectionErrorOrClose() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - Stop(base::DoNothing()); + Stop(); if (optional_receiver_disconnected_callback_) std::move(optional_receiver_disconnected_callback_).Run(); }
diff --git a/services/video_capture/texture_virtual_device_mojo_adapter.h b/services/video_capture/texture_virtual_device_mojo_adapter.h index 6d7488a..8f1749e 100644 --- a/services/video_capture/texture_virtual_device_mojo_adapter.h +++ b/services/video_capture/texture_virtual_device_mojo_adapter.h
@@ -44,7 +44,8 @@ void SetPhotoOptions(media::mojom::PhotoSettingsPtr settings, SetPhotoOptionsCallback callback) override; void TakePhoto(TakePhotoCallback callback) override; - void Stop(StopCallback callback) override; + + void Stop(); private: void OnReceiverConnectionErrorOrClose();
diff --git a/services/video_capture/video_source_impl.cc b/services/video_capture/video_source_impl.cc index 742b1e25..91b1e48 100644 --- a/services/video_capture/video_source_impl.cc +++ b/services/video_capture/video_source_impl.cc
@@ -20,15 +20,18 @@ device_status_(DeviceStatus::kNotStarted), restart_device_once_when_stop_complete_(false), weak_factory_(this) { + // Unretained(this) is safe because |this| owns |bindings_|. bindings_.set_connection_error_handler(base::BindRepeating( &VideoSourceImpl::OnClientDisconnected, base::Unretained(this))); } VideoSourceImpl::~VideoSourceImpl() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); bindings_.set_connection_error_handler(base::DoNothing()); } void VideoSourceImpl::AddToBindingSet(mojom::VideoSourceRequest request) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); bindings_.AddBinding(this, std::move(request)); } @@ -38,6 +41,7 @@ bool force_reopen_with_new_settings, mojom::PushVideoStreamSubscriptionRequest subscription_request, CreatePushSubscriptionCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); auto subscription = std::make_unique<PushVideoStreamSubscriptionImpl>( std::move(subscription_request), std::move(subscriber), requested_settings, std::move(callback), &broadcaster_, &device_); @@ -76,6 +80,7 @@ } void VideoSourceImpl::OnClientDisconnected() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (bindings_.empty()) { // Note: Invoking this callback may synchronously trigger the destruction of // |this|, so no more member access should be done after it. @@ -85,6 +90,7 @@ void VideoSourceImpl::StartDeviceWithSettings( const media::VideoCaptureParams& requested_settings) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); device_start_settings_ = requested_settings; device_status_ = DeviceStatus::kStartingAsynchronously; device_factory_->CreateDevice( @@ -95,6 +101,7 @@ void VideoSourceImpl::OnCreateDeviceResponse( mojom::DeviceAccessResultCode result_code) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); switch (result_code) { case mojom::DeviceAccessResultCode::SUCCESS: { mojom::ReceiverPtr broadcaster_as_receiver; @@ -129,6 +136,7 @@ void VideoSourceImpl::OnPushSubscriptionClosedOrDisconnectedOrDiscarded( PushVideoStreamSubscriptionImpl* subscription, base::OnceClosure done_cb) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // We keep the subscription instance alive until after having called |done_cb| // in order to allow it to send out a callback before being destroyed. auto subscription_ownership = std::move(push_subscriptions_[subscription]); @@ -154,13 +162,29 @@ } void VideoSourceImpl::StopDeviceAsynchronously() { - device_->Stop(base::BindOnce(&VideoSourceImpl::OnStopDeviceComplete, - weak_factory_.GetWeakPtr())); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (restart_device_once_when_stop_complete_) { + // We do not want to send out OnStopped() or OnStarted() to already + // connected clients, to make this internal restart transparent to them. + // The broadcaster already drops additional OnStarted() events for clients + // who already received one. But for OnStopped() we need to explicitly tell + // it to. + // Unretained(this) is safe because |this| owns |broadcaster_|. + broadcaster_.HideSourceRestartFromClients(base::BindOnce( + &VideoSourceImpl::OnStopDeviceComplete, base::Unretained(this))); + } else { + broadcaster_.SetOnStoppedHandler(base::BindOnce( + &VideoSourceImpl::OnStopDeviceComplete, base::Unretained(this))); + } + + // Stop the device by closing the connection to it. Stopping is complete when + // OnStopDeviceComplete() gets invoked. + device_.reset(); device_status_ = DeviceStatus::kStoppingAsynchronously; } void VideoSourceImpl::OnStopDeviceComplete() { - device_.reset(); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); device_status_ = DeviceStatus::kNotStarted; if (!restart_device_once_when_stop_complete_) return;
diff --git a/services/video_capture/video_source_impl.h b/services/video_capture/video_source_impl.h index 950c281..8cf68af 100644 --- a/services/video_capture/video_source_impl.h +++ b/services/video_capture/video_source_impl.h
@@ -71,6 +71,8 @@ media::VideoCaptureParams device_start_settings_; bool restart_device_once_when_stop_complete_; + SEQUENCE_CHECKER(sequence_checker_); + base::WeakPtrFactory<VideoSourceImpl> weak_factory_; DISALLOW_COPY_AND_ASSIGN(VideoSourceImpl);
diff --git a/services/video_capture/virtual_device_enabled_device_factory.cc b/services/video_capture/virtual_device_enabled_device_factory.cc index 131d981..6db39c7 100644 --- a/services/video_capture/virtual_device_enabled_device_factory.cc +++ b/services/video_capture/virtual_device_enabled_device_factory.cc
@@ -59,9 +59,9 @@ void StopDevice() { if (shared_memory_device_) - shared_memory_device_->Stop(base::DoNothing()); + shared_memory_device_->Stop(); else - texture_device_->Stop(base::DoNothing()); + texture_device_->Stop(); } media::VideoCaptureDeviceInfo device_info() const { return device_info_; }
diff --git a/services/viz/privileged/interfaces/compositing/frame_sink_video_capture.mojom b/services/viz/privileged/interfaces/compositing/frame_sink_video_capture.mojom index f65d3023..847a4884 100644 --- a/services/viz/privileged/interfaces/compositing/frame_sink_video_capture.mojom +++ b/services/viz/privileged/interfaces/compositing/frame_sink_video_capture.mojom
@@ -38,14 +38,13 @@ // the service that it is Done(). |info| is used to interpret the // format/layout of the data, and also contains the frame timestamps and other // metadata (the following media::VideoFrameMetadata keys are set: - // CAPTURE_BEGIN_TIME, CAPTURE_END_TIME, COLOR_SPACE, FRAME_DURATION, - // INTERACTIVE_CONTENT, REFERENCE_TIME). |update_rect| is the region of the - // frame that has changed since the last frame. |content_rect| is the region - // of the frame that contains the captured content, with the rest of the frame - // having been letterboxed to adhere to resolution constraints. + // CAPTURE_BEGIN_TIME, CAPTURE_END_TIME, CAPTURE_COUNTER, CAPTURE_UPDATE_RECT, + // COLOR_SPACE, FRAME_DURATION, INTERACTIVE_CONTENT, REFERENCE_TIME). + // |content_rect| is the region of the frame that contains the captured + // content, with the rest of the frame having been letterboxed to adhere to + // resolution constraints. OnFrameCaptured(mojo_base.mojom.ReadOnlySharedMemoryRegion data, media.mojom.VideoFrameInfo info, - gfx.mojom.Rect update_rect, gfx.mojom.Rect content_rect, FrameSinkVideoConsumerFrameCallbacks callbacks);
diff --git a/services/viz/privileged/interfaces/compositing/renderer_settings.mojom b/services/viz/privileged/interfaces/compositing/renderer_settings.mojom index 1d3aeeb..60118bf 100644 --- a/services/viz/privileged/interfaces/compositing/renderer_settings.mojom +++ b/services/viz/privileged/interfaces/compositing/renderer_settings.mojom
@@ -20,6 +20,7 @@ bool show_overdraw_feedback; int32 slow_down_compositing_scale_factor; bool use_skia_renderer; + bool use_skia_renderer_non_ddl; bool record_sk_picture; bool allow_overlays; bool requires_alpha_channel;
diff --git a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc index 10726010..23c730b 100644 --- a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc +++ b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc
@@ -27,6 +27,7 @@ out->slow_down_compositing_scale_factor = data.slow_down_compositing_scale_factor(); out->use_skia_renderer = data.use_skia_renderer(); + out->use_skia_renderer_non_ddl = data.use_skia_renderer_non_ddl(); out->record_sk_picture = data.record_sk_picture(); out->allow_overlays = data.allow_overlays(); out->requires_alpha_channel = data.requires_alpha_channel();
diff --git a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h index 4e9c42a..f379f96 100644 --- a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h +++ b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h
@@ -66,6 +66,10 @@ return input.use_skia_renderer; } + static bool use_skia_renderer_non_ddl(const viz::RendererSettings& input) { + return input.use_skia_renderer_non_ddl; + } + static bool record_sk_picture(const viz::RendererSettings& input) { return input.record_sk_picture; }
diff --git a/services/viz/privileged/interfaces/struct_traits_unittest.cc b/services/viz/privileged/interfaces/struct_traits_unittest.cc index e80892f..2ed869d4 100644 --- a/services/viz/privileged/interfaces/struct_traits_unittest.cc +++ b/services/viz/privileged/interfaces/struct_traits_unittest.cc
@@ -29,6 +29,7 @@ input.show_overdraw_feedback = true; input.highp_threshold_min = -1; input.use_skia_renderer = true; + input.use_skia_renderer_non_ddl = true; RendererSettings output; mojom::RendererSettings::Deserialize( @@ -49,6 +50,7 @@ EXPECT_EQ(input.show_overdraw_feedback, output.show_overdraw_feedback); EXPECT_EQ(input.highp_threshold_min, output.highp_threshold_min); EXPECT_EQ(input.use_skia_renderer, output.use_skia_renderer); + EXPECT_EQ(input.use_skia_renderer_non_ddl, output.use_skia_renderer_non_ddl); } } // namespace
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h index 63fa037..a8aa7980 100644 --- a/skia/config/SkUserConfig.h +++ b/skia/config/SkUserConfig.h
@@ -176,6 +176,10 @@ // See chromium:913223, skia:6886. #define SK_DISABLE_DAA +// Staging for lowp::bilerp_clamp_8888, and for planned misc. others. +#define SK_DISABLE_LOWP_BILERP_CLAMP_CLAMP_STAGE +#define SK_DISABLE_NEXT_BATCH_OF_LOWP_STAGES + ///////////////////////// Imported from BUILD.gn and skia_common.gypi /* In some places Skia can use static initializers for global initialization,
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 1cfd70db..5a7406d 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -1291,7 +1291,7 @@ { "name": "ContentCaptureUseTextHolder", "params": { - "task_long_delay_in_seconds": "5", + "task_long_delay_in_milliseconds": "5000", "task_short_delay_in_milliseconds": "500", "use_node_id": "false" },
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h index 91c4fd9a..6a78e96 100644 --- a/third_party/blink/public/platform/web_runtime_features.h +++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -99,6 +99,7 @@ BLINK_PLATFORM_EXPORT static void EnableDatabase(bool); BLINK_PLATFORM_EXPORT static void EnableDecodeToYUV(bool); BLINK_PLATFORM_EXPORT static void EnableDisplayCutoutAPI(bool); + BLINK_PLATFORM_EXPORT static void EnableFallbackCursorMode(bool); BLINK_PLATFORM_EXPORT static void EnableFastMobileScrolling(bool); BLINK_PLATFORM_EXPORT static void EnableFeaturePolicyForSandbox(bool); BLINK_PLATFORM_EXPORT static void EnableFileSystem(bool);
diff --git a/third_party/blink/public/web/web_content_capture_client.h b/third_party/blink/public/web/web_content_capture_client.h index e09dd44f..332dbba 100644 --- a/third_party/blink/public/web/web_content_capture_client.h +++ b/third_party/blink/public/web/web_content_capture_client.h
@@ -8,6 +8,7 @@ #include <vector> #include "base/memory/scoped_refptr.h" +#include "base/time/time.h" #include "cc/paint/node_holder.h" namespace blink { @@ -19,6 +20,10 @@ class WebContentCaptureClient { public: virtual cc::NodeHolder::Type GetNodeHolderType() const = 0; + // Adjusts the ContentCaptureTask delay time, has no effect for the existing + // tasks. + virtual void GetTaskTimingParameters(base::TimeDelta& short_delay, + base::TimeDelta& long_delay) const = 0; // Invoked when a list of |content| is captured, |first_content| indicates if // this is first captured content in the current document.
diff --git a/third_party/blink/renderer/bindings/core/v8/script_controller.cc b/third_party/blink/renderer/bindings/core/v8/script_controller.cc index d390e8d..a9d2c237 100644 --- a/third_party/blink/renderer/bindings/core/v8/script_controller.cc +++ b/third_party/blink/renderer/bindings/core/v8/script_controller.cc
@@ -231,12 +231,10 @@ ContentSecurityPolicy::ShouldBypassMainWorld(GetFrame()->GetDocument()); if (!GetFrame()->GetPage() || (!should_bypass_main_world_content_security_policy && - !GetFrame() - ->GetDocument() - ->GetContentSecurityPolicy() - ->AllowJavaScriptURLs(element, script_source, - GetFrame()->GetDocument()->Url(), - EventHandlerPosition().line_))) { + !GetFrame()->GetDocument()->GetContentSecurityPolicy()->AllowInline( + ContentSecurityPolicy::InlineType::kJavaScriptURL, element, + script_source, String() /* nonce */, + GetFrame()->GetDocument()->Url(), EventHandlerPosition().line_))) { return true; }
diff --git a/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl b/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl index 7bea2c9..6c018981 100644 --- a/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl +++ b/third_party/blink/renderer/build/scripts/core/css/templates/cssom_types.cc.tmpl
@@ -99,8 +99,9 @@ DCHECK_EQ(id == CSSPropertyVariable, !custom_property_name.IsNull()); if (id == CSSPropertyVariable && registration) { - if (value.GetType() == CSSStyleValue::kUnknownType) { - return ToCSSUnsupportedStyleValue(value).IsValidFor( + if (auto* unsupported_style_value = + DynamicTo<CSSUnsupportedStyleValue>(value)) { + return unsupported_style_value->IsValidFor( CSSPropertyName(custom_property_name)); } match = registration->Syntax().Match(value); @@ -111,8 +112,9 @@ return CSSOMKeywords::ValidKeywordForProperty( id, ToCSSKeywordValue(value)); } - if (value.GetType() == CSSStyleValue::kUnknownType) { - return ToCSSUnsupportedStyleValue(value).IsValidFor(CSSPropertyName(id)); + if (auto* unsupported_style_value = + DynamicTo<CSSUnsupportedStyleValue>(value)) { + return unsupported_style_value->IsValidFor(CSSPropertyName(id)); } if (value.GetType() == CSSStyleValue::kUnparsedType) { return true;
diff --git a/third_party/blink/renderer/core/content_capture/content_capture_task.cc b/third_party/blink/renderer/core/content_capture/content_capture_task.cc index e82ca0d..d0fca45 100644 --- a/third_party/blink/renderer/core/content_capture/content_capture_task.cc +++ b/third_party/blink/renderer/core/content_capture/content_capture_task.cc
@@ -21,7 +21,11 @@ ContentCaptureTask::ContentCaptureTask(LocalFrame& local_frame_root, TaskSession& task_session) - : local_frame_root_(&local_frame_root), task_session_(&task_session) {} + : local_frame_root_(&local_frame_root), task_session_(&task_session) { + local_frame_root.Client() + ->GetWebContentCaptureClient() + ->GetTaskTimingParameters(task_short_delay_, task_long_delay_); +} ContentCaptureTask::~ContentCaptureTask() {} @@ -170,15 +174,15 @@ if (is_scheduled_) return; - int delay_ms = 0; + TimeDelta delay; switch (reason) { case ScheduleReason::kFirstContentChange: case ScheduleReason::kScrolling: case ScheduleReason::kRetryTask: - delay_ms = kTaskShortDelayInMS; + delay = task_short_delay_; break; case ScheduleReason::kContentChange: - delay_ms = kTaskLongDelayInMS; + delay = task_long_delay_; break; } @@ -189,8 +193,7 @@ task_runner, this, &ContentCaptureTask::Run); } - delay_task_->StartOneShot(base::TimeDelta::FromMilliseconds(delay_ms), - FROM_HERE); + delay_task_->StartOneShot(delay, FROM_HERE); is_scheduled_ = true; }
diff --git a/third_party/blink/renderer/core/content_capture/content_capture_task.h b/third_party/blink/renderer/core/content_capture/content_capture_task.h index 443a3066..bb30774 100644 --- a/third_party/blink/renderer/core/content_capture/content_capture_task.h +++ b/third_party/blink/renderer/core/content_capture/content_capture_task.h
@@ -50,11 +50,6 @@ // Make those const public for testing purpose. static constexpr size_t kBatchSize = 5; - // Schedules the task with short delay for kFirstContentChange, kScrolling and - // kRetryTask, with long delay for kContentChange. - static constexpr int kTaskShortDelayInMS = 500; - static constexpr int kTaskLongDelayInMS = 5000; - TaskState GetTaskStateForTesting() const { return task_state_; } void RunTaskForTestingUntil(TaskState stop_state) { @@ -107,6 +102,11 @@ UntracedMember<TaskSession> task_session_; std::unique_ptr<TaskRunnerTimer<ContentCaptureTask>> delay_task_; TaskState task_state_ = TaskState::kStop; + + // Schedules the task with short delay for kFirstContentChange, kScrolling and + // kRetryTask, with long delay for kContentChange. + base::TimeDelta task_short_delay_; + base::TimeDelta task_long_delay_; base::Optional<TaskState> task_stop_for_testing_; base::Optional<std::vector<cc::NodeHolder>> captured_content_for_testing_; };
diff --git a/third_party/blink/renderer/core/content_capture/content_capture_test.cc b/third_party/blink/renderer/core/content_capture/content_capture_test.cc index 3b66332..e5ce8b18 100644 --- a/third_party/blink/renderer/core/content_capture/content_capture_test.cc +++ b/third_party/blink/renderer/core/content_capture/content_capture_test.cc
@@ -13,6 +13,7 @@ #include "third_party/blink/renderer/core/html_element_type_helpers.h" #include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/layout/layout_text.h" +#include "third_party/blink/renderer/core/loader/empty_clients.h" #include "third_party/blink/renderer/core/testing/page_test_base.h" #include "third_party/blink/renderer/core/testing/sim/sim_request.h" #include "third_party/blink/renderer/core/testing/sim/sim_test.h" @@ -29,6 +30,20 @@ return node_holder_type_; } + void GetTaskTimingParameters(base::TimeDelta& short_delay, + base::TimeDelta& long_delay) const override { + short_delay = GetTaskShortDelay(); + long_delay = GetTaskLongDelay(); + } + + base::TimeDelta GetTaskLongDelay() const { + return base::TimeDelta::FromMilliseconds(5000); + } + + base::TimeDelta GetTaskShortDelay() const { + return base::TimeDelta::FromMilliseconds(500); + } + void DidCaptureContent( const std::vector<scoped_refptr<WebContentHolder>>& data, bool first_data) override { @@ -114,6 +129,19 @@ scoped_refptr<ContentCaptureTaskTestHelper> content_capture_task_; }; +class ContentCaptureLocalFrameClientHelper : public EmptyLocalFrameClient { + public: + ContentCaptureLocalFrameClientHelper(WebContentCaptureClient& client) + : client_(client) {} + + WebContentCaptureClient* GetWebContentCaptureClient() const override { + return &client_; + } + + private: + WebContentCaptureClient& client_; +}; + class ContentCaptureTest : public PageTestBase, public ::testing::WithParamInterface<NodeHolder::Type> { @@ -121,7 +149,12 @@ ContentCaptureTest() { EnablePlatform(); } void SetUp() override { - PageTestBase::SetUp(); + content_capture_client_ = + std::make_unique<WebContentCaptureClientTestHelper>(GetParam()); + local_frame_client_ = + MakeGarbageCollected<ContentCaptureLocalFrameClientHelper>( + *content_capture_client_); + SetupPageWithClients(nullptr, local_frame_client_); SetHtmlInnerHTML( "<!DOCTYPE HTML>" "<p id='p1'>1</p>" @@ -134,8 +167,6 @@ "<p id='p8'>8</p>"); platform()->SetAutoAdvanceNowToPendingTasks(false); // TODO(michaelbai): ContentCaptureManager should be get from LocalFrame. - content_capture_client_ = - std::make_unique<WebContentCaptureClientTestHelper>(GetParam()); content_capture_manager_ = MakeGarbageCollected<ContentCaptureManagerTestHelper>( GetFrame(), *content_capture_client_); @@ -161,14 +192,12 @@ void RunContentCaptureTask() { ResetResult(); - platform()->RunForPeriod(base::TimeDelta::FromMilliseconds( - ContentCaptureTask::kTaskShortDelayInMS)); + platform()->RunForPeriod(GetWebContentCaptureClient()->GetTaskShortDelay()); } void RunLongDelayContentCaptureTask() { ResetResult(); - platform()->RunForPeriod(base::TimeDelta::FromMilliseconds( - ContentCaptureTask::kTaskLongDelayInMS)); + platform()->RunForPeriod(GetWebContentCaptureClient()->GetTaskLongDelay()); } void RemoveNode(NodeHolder node_holder, Node* node) { @@ -210,6 +239,7 @@ std::vector<NodeHolder> node_holders_; std::unique_ptr<WebContentCaptureClientTestHelper> content_capture_client_; Persistent<ContentCaptureManagerTestHelper> content_capture_manager_; + Persistent<ContentCaptureLocalFrameClientHelper> local_frame_client_; }; INSTANTIATE_TEST_SUITE_P(, @@ -364,6 +394,8 @@ EXPECT_EQ(1u, GetWebContentCaptureClient()->RemovedData().size()); } +// TODO(michaelbai): use RenderingTest instead of PageTestBase for multiple +// frame test. class ContentCaptureSimTest : public SimTest, public ::testing::WithParamInterface<NodeHolder::Type> {
diff --git a/third_party/blink/renderer/core/css/active_style_sheets_test.cc b/third_party/blink/renderer/core/css/active_style_sheets_test.cc index a319ce8..2faff4b 100644 --- a/third_party/blink/renderer/core/css/active_style_sheets_test.cc +++ b/third_party/blink/renderer/core/css/active_style_sheets_test.cc
@@ -514,7 +514,7 @@ ASSERT_TRUE(sheet); ASSERT_TRUE(sheet->IsCSSStyleSheet()); - CSSStyleSheet* css_sheet = ToCSSStyleSheet(sheet); + auto* css_sheet = To<CSSStyleSheet>(sheet); ActiveStyleSheetVector old_style_sheets; old_style_sheets.push_back( std::make_pair(css_sheet, &css_sheet->Contents()->GetRuleSet()));
diff --git a/third_party/blink/renderer/core/css/css_style_sheet.h b/third_party/blink/renderer/core/css/css_style_sheet.h index 0261fae4..e76ff05 100644 --- a/third_party/blink/renderer/core/css/css_style_sheet.h +++ b/third_party/blink/renderer/core/css/css_style_sheet.h
@@ -30,6 +30,7 @@ #include "third_party/blink/renderer/core/dom/tree_scope.h" #include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h" #include "third_party/blink/renderer/platform/heap/handle.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h" #include "third_party/blink/renderer/platform/wtf/text/text_position.h" @@ -282,11 +283,12 @@ style_sheet_->DidMutateRules(); } -DEFINE_TYPE_CASTS(CSSStyleSheet, - StyleSheet, - sheet, - sheet->IsCSSStyleSheet(), - sheet.IsCSSStyleSheet()); +template <> +struct DowncastTraits<CSSStyleSheet> { + static bool AllowFrom(const StyleSheet& sheet) { + return sheet.IsCSSStyleSheet(); + } +}; } // namespace blink
diff --git a/third_party/blink/renderer/core/css/cssom/css_unsupported_style_value.h b/third_party/blink/renderer/core/css/cssom/css_unsupported_style_value.h index 3eb73ef..4056e9c 100644 --- a/third_party/blink/renderer/core/css/cssom/css_unsupported_style_value.h +++ b/third_party/blink/renderer/core/css/cssom/css_unsupported_style_value.h
@@ -9,6 +9,7 @@ #include "base/optional.h" #include "third_party/blink/renderer/core/css/css_property_name.h" #include "third_party/blink/renderer/core/css/cssom/css_style_value.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -74,6 +75,13 @@ value.GetType() == CSSStyleValue::StyleValueType::kUnknownType); +template <> +struct DowncastTraits<CSSUnsupportedStyleValue> { + static bool AllowFrom(const CSSStyleValue& value) { + return value.GetType() == CSSStyleValue::StyleValueType::kUnknownType; + } +}; + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSSOM_CSS_UNSUPPORTED_STYLE_VALUE_H_
diff --git a/third_party/blink/renderer/core/css/document_style_sheet_collection.cc b/third_party/blink/renderer/core/css/document_style_sheet_collection.cc index 2bb47e9..5ee69b2 100644 --- a/third_party/blink/renderer/core/css/document_style_sheet_collection.cc +++ b/third_party/blink/renderer/core/css/document_style_sheet_collection.cc
@@ -84,7 +84,7 @@ GetDocument().GetStyleEngine().PreferredStylesheetSetName())) continue; - CSSStyleSheet* css_sheet = ToCSSStyleSheet(sheet); + CSSStyleSheet* css_sheet = To<CSSStyleSheet>(sheet); collector.AppendActiveStyleSheet( std::make_pair(css_sheet, master_engine.RuleSetForSheet(*css_sheet))); } @@ -144,7 +144,7 @@ GetDocument().GetStyleEngine().PreferredStylesheetSetName())) continue; viewport_resolver.CollectViewportRulesFromAuthorSheet( - *ToCSSStyleSheet(sheet)); + To<CSSStyleSheet>(*sheet)); } }
diff --git a/third_party/blink/renderer/core/css/shadow_tree_style_sheet_collection.cc b/third_party/blink/renderer/core/css/shadow_tree_style_sheet_collection.cc index f8b4cb0a..ccd7964 100644 --- a/third_party/blink/renderer/core/css/shadow_tree_style_sheet_collection.cc +++ b/third_party/blink/renderer/core/css/shadow_tree_style_sheet_collection.cc
@@ -57,7 +57,7 @@ collection.AppendSheetForList(sheet); if (candidate.CanBeActivated(g_null_atom)) { - CSSStyleSheet* css_sheet = ToCSSStyleSheet(sheet); + CSSStyleSheet* css_sheet = To<CSSStyleSheet>(sheet); collection.AppendActiveStyleSheet( std::make_pair(css_sheet, master_engine.RuleSetForSheet(*css_sheet))); }
diff --git a/third_party/blink/renderer/core/css/shadow_tree_style_sheet_collection.h b/third_party/blink/renderer/core/css/shadow_tree_style_sheet_collection.h index 79601ee..138bf72 100644 --- a/third_party/blink/renderer/core/css/shadow_tree_style_sheet_collection.h +++ b/third_party/blink/renderer/core/css/shadow_tree_style_sheet_collection.h
@@ -32,6 +32,7 @@ #include "base/macros.h" #include "third_party/blink/renderer/core/css/tree_scope_style_sheet_collection.h" +#include "third_party/blink/renderer/platform/wtf/casting.h" namespace blink { @@ -55,11 +56,12 @@ DISALLOW_COPY_AND_ASSIGN(ShadowTreeStyleSheetCollection); }; -DEFINE_TYPE_CASTS(ShadowTreeStyleSheetCollection, - TreeScopeStyleSheetCollection, - value, - value->IsShadowTreeStyleSheetCollection(), - value.IsShadowTreeStyleSheetCollection()); +template <> +struct DowncastTraits<ShadowTreeStyleSheetCollection> { + static bool AllowFrom(const TreeScopeStyleSheetCollection& value) { + return value.IsShadowTreeStyleSheetCollection(); + } +}; } // namespace blink
diff --git a/third_party/blink/renderer/core/css/style_element.cc b/third_party/blink/renderer/core/css/style_element.cc index 375c42f..db5812e 100644 --- a/third_party/blink/renderer/core/css/style_element.cc +++ b/third_party/blink/renderer/core/css/style_element.cc
@@ -142,9 +142,9 @@ const ContentSecurityPolicy* csp = document.GetContentSecurityPolicy(); bool passes_content_security_policy_checks = ShouldBypassMainWorldCSP(element) || - csp->AllowInlineStyle(&element, document.Url(), element.nonce(), - start_position_.line_, text, - ContentSecurityPolicy::InlineType::kBlock); + csp->AllowInline(ContentSecurityPolicy::InlineType::kInlineStyleElement, + &element, text, element.nonce(), document.Url(), + start_position_.line_); // Clearing the current sheet may remove the cache entry so create the new // sheet first
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc index f773610..5fb0822 100644 --- a/third_party/blink/renderer/core/css/style_engine.cc +++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -365,8 +365,8 @@ UnorderedTreeScopeSet& tree_scopes) { for (TreeScope* tree_scope : tree_scopes) { DCHECK(tree_scope != document_); - ShadowTreeStyleSheetCollection* collection = - ToShadowTreeStyleSheetCollection(StyleSheetCollectionFor(*tree_scope)); + auto* collection = To<ShadowTreeStyleSheetCollection>( + StyleSheetCollectionFor(*tree_scope)); DCHECK(collection); if (collection->MediaQueryAffectingValueChanged()) SetNeedsActiveStyleUpdate(*tree_scope); @@ -408,8 +408,8 @@ TreeScope* tree_scope, UnorderedTreeScopeSet& tree_scopes_removed) { DCHECK_NE(tree_scope, document_); - ShadowTreeStyleSheetCollection* collection = - ToShadowTreeStyleSheetCollection(StyleSheetCollectionFor(*tree_scope)); + auto* collection = + To<ShadowTreeStyleSheetCollection>(StyleSheetCollectionFor(*tree_scope)); DCHECK(collection); collection->UpdateActiveStyleSheets(*this); if (!collection->HasStyleSheetCandidateNodes() &&
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc index 84dd40c..f5c1c91 100644 --- a/third_party/blink/renderer/core/css/style_engine_test.cc +++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -975,7 +975,7 @@ EXPECT_EQ(MakeRGB(0, 0, 255), t1->GetComputedStyle()->VisitedDependentColor( GetCSSPropertyColor())); - CSSStyleSheet* sheet = ToCSSStyleSheet(GetDocument().StyleSheets().item(0)); + auto* sheet = To<CSSStyleSheet>(GetDocument().StyleSheets().item(0)); ASSERT_TRUE(sheet); DummyExceptionStateForTesting exception_state; ASSERT_TRUE(sheet->cssRules(exception_state));
diff --git a/third_party/blink/renderer/core/css/style_sheet_candidate.cc b/third_party/blink/renderer/core/css/style_sheet_candidate.cc index 321f00c..d558990 100644 --- a/third_party/blink/renderer/core/css/style_sheet_candidate.cc +++ b/third_party/blink/renderer/core/css/style_sheet_candidate.cc
@@ -74,9 +74,10 @@ bool StyleSheetCandidate::CanBeActivated( const String& current_preferrable_name) const { StyleSheet* sheet = this->Sheet(); - if (!sheet || sheet->disabled() || !sheet->IsCSSStyleSheet()) + auto* css_style_sheet = DynamicTo<CSSStyleSheet>(sheet); + if (!css_style_sheet || sheet->disabled()) return false; - return ToCSSStyleSheet(Sheet())->CanBeActivated(current_preferrable_name); + return css_style_sheet->CanBeActivated(current_preferrable_name); } StyleSheetCandidate::Type StyleSheetCandidate::TypeOf(Node& node) {
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc index daafc1d0f..1d04d16 100644 --- a/third_party/blink/renderer/core/dom/document.cc +++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2440,13 +2440,42 @@ DCHECK(node); if (!NeedsLayoutTreeUpdateForNode(*node)) return; + + // Force unlock any element from the given node up the ancestor chain. + Vector<DisplayLockContext::ScopedForcedUpdate> scoped_update_forced_list; + if (RuntimeEnabledFeatures::DisplayLockingEnabled() && + LockedDisplayLockCount() > 0) { + const_cast<Node*>(node)->UpdateDistributionForFlatTreeTraversal(); + for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(*node)) { + if (!ancestor.IsElementNode()) + continue; + if (auto* context = ToElement(ancestor).GetDisplayLockContext()) + scoped_update_forced_list.push_back(context->GetScopedForcedUpdate()); + } + } + UpdateStyleAndLayoutTree(); } -void Document::UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(Node* node) { +void Document::UpdateStyleAndLayoutIgnorePendingStylesheetsForNode( + const Node* node) { DCHECK(node); if (!node->InActiveDocument()) return; + + // Force unlock any element from the given node up the ancestor chain. + Vector<DisplayLockContext::ScopedForcedUpdate> scoped_update_forced_list; + if (RuntimeEnabledFeatures::DisplayLockingEnabled() && + LockedDisplayLockCount() > 0) { + const_cast<Node*>(node)->UpdateDistributionForFlatTreeTraversal(); + for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(*node)) { + if (!ancestor.IsElementNode()) + continue; + if (auto* context = ToElement(ancestor).GetDisplayLockContext()) + scoped_update_forced_list.push_back(context->GetScopedForcedUpdate()); + } + } + UpdateStyleAndLayoutIgnorePendingStylesheets(); } @@ -6729,8 +6758,10 @@ const WTF::OrdinalNumber& context_line) { Element* element = node && node->IsElementNode() ? ToElement(node) : nullptr; if (!ContentSecurityPolicy::ShouldBypassMainWorld(this) && - !GetContentSecurityPolicy()->AllowInlineEventHandler( - element, listener->ScriptBody(), context_url, context_line)) + !GetContentSecurityPolicy()->AllowInline( + ContentSecurityPolicy::InlineType::kInlineEventHandler, element, + listener->ScriptBody(), String() /* nonce */, context_url, + context_line)) return false; // HTML says that inline script needs browsing context to create its execution
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h index da6b59a..348004be7 100644 --- a/third_party/blink/renderer/core/dom/document.h +++ b/third_party/blink/renderer/core/dom/document.h
@@ -527,7 +527,7 @@ // but allows style & layout tree calculation for invisible nodes. void UpdateStyleAndLayoutIgnorePendingStylesheetsConsideringInvisibleNodes( RunPostLayoutTasks = kRunPostLayoutTasksAsynchronously); - void UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(Node*); + void UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(const Node*); scoped_refptr<ComputedStyle> StyleForPage(int page_index); // Ensures that location-based data will be valid for a given node.
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index d8747d65..b293858 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -4931,10 +4931,10 @@ ContentSecurityPolicy::ShouldBypassMainWorld(&GetDocument()) || (ContainingShadowRoot() && ContainingShadowRoot()->IsUserAgent()) || - GetDocument().GetContentSecurityPolicy()->AllowInlineStyle( - this, GetDocument().Url(), String(), start_line_number, - new_style_string, - ContentSecurityPolicy::InlineType::kAttribute)) { + GetDocument().GetContentSecurityPolicy()->AllowInline( + ContentSecurityPolicy::InlineType::kInlineStyleAttribute, this, + new_style_string, String() /* nonce */, GetDocument().Url(), + start_line_number)) { SetInlineStyleFromString(new_style_string); }
diff --git a/third_party/blink/renderer/core/dom/processing_instruction.cc b/third_party/blink/renderer/core/dom/processing_instruction.cc index 876b8a5..dcb70413 100644 --- a/third_party/blink/renderer/core/dom/processing_instruction.cc +++ b/third_party/blink/renderer/core/dom/processing_instruction.cc
@@ -233,7 +233,7 @@ loading_ = false; if (is_css_) - ToCSSStyleSheet(sheet_.Get())->Contents()->CheckLoaded(); + To<CSSStyleSheet>(sheet_.Get())->Contents()->CheckLoaded(); else if (is_xsl_) ToXSLStyleSheet(sheet_.Get())->CheckLoaded(); }
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc index 6577f143..2360367 100644 --- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc +++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
@@ -549,8 +549,10 @@ String script = DecodeURLEscapeSequences(kurl.GetString(), DecodeURLMode::kUTF8OrIsomorphic); - if (!element_->GetDocument().GetContentSecurityPolicy()->AllowJavaScriptURLs( - element_, script, element_->GetDocument().Url(), OrdinalNumber())) { + if (!element_->GetDocument().GetContentSecurityPolicy()->AllowInline( + ContentSecurityPolicy::InlineType::kJavaScriptURL, element_, script, + String() /* nonce */, element_->GetDocument().Url(), + OrdinalNumber())) { return WebString(); } script = script.Substring(strlen("javascript:"));
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc index 847c9aa..0399b9ea 100644 --- a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc +++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
@@ -463,130 +463,66 @@ } // static -bool ContentSecurityPolicy::CheckScriptHashAgainstPolicy( +bool ContentSecurityPolicy::CheckHashAgainstPolicy( Vector<CSPHashValue>& csp_hash_values, const Member<CSPDirectiveList>& policy, InlineType inline_type) { for (const auto& csp_hash_value : csp_hash_values) { - if (policy->AllowScriptHash(csp_hash_value, inline_type)) { + if (policy->AllowHash(csp_hash_value, inline_type)) return true; - } } return false; } -// static -bool ContentSecurityPolicy::CheckStyleHashAgainstPolicy( - Vector<CSPHashValue>& csp_hash_values, - const Member<CSPDirectiveList>& policy, - InlineType inline_type) { - for (const auto& csp_hash_value : csp_hash_values) { - if (policy->AllowStyleHash(csp_hash_value, inline_type)) { - return true; - } - } - return false; -} - -bool ContentSecurityPolicy::AllowJavaScriptURLs( - Element* element, - const String& source, - const String& context_url, - const WTF::OrdinalNumber& context_line, - SecurityViolationReportingPolicy reporting_policy) const { - // Javascript URLs may be whitelisted by hash, if - // 'unsafe-hashes' is present in a policy. Check against the digest - // of the |source| and also check whether inline script is allowed. - Vector<CSPHashValue> csp_hash_values; - FillInCSPHashValues(source, script_hash_algorithms_used_, &csp_hash_values); - - bool is_allowed = true; - for (const auto& policy : policies_) { - is_allowed &= CheckScriptHashAgainstPolicy(csp_hash_values, policy, - InlineType::kAttribute) || - policy->AllowJavaScriptURLs(element, source, context_url, - context_line, reporting_policy); - } - return is_allowed; -} - -bool ContentSecurityPolicy::AllowInlineEventHandler( - Element* element, - const String& source, - const String& context_url, - const WTF::OrdinalNumber& context_line, - SecurityViolationReportingPolicy reporting_policy) const { - // Inline event handlers may be whitelisted by hash, if - // 'unsafe-hashes' is present in a policy. Check against the digest - // of the |source| and also check whether inline script is allowed. - Vector<CSPHashValue> csp_hash_values; - FillInCSPHashValues(source, script_hash_algorithms_used_, &csp_hash_values); - - bool is_allowed = true; - for (const auto& policy : policies_) { - is_allowed &= - CheckScriptHashAgainstPolicy(csp_hash_values, policy, - InlineType::kAttribute) || - policy->AllowInlineEventHandlers(element, source, context_url, - context_line, reporting_policy); - } - - return is_allowed; -} - -bool ContentSecurityPolicy::AllowInlineScript( - Element* element, - const String& context_url, - const String& nonce, - const WTF::OrdinalNumber& context_line, - const String& script_content, - SecurityViolationReportingPolicy reporting_policy) const { - DCHECK(element); - - Vector<CSPHashValue> csp_hash_values; - FillInCSPHashValues(script_content, script_hash_algorithms_used_, - &csp_hash_values); - - bool is_allowed = true; - for (const auto& policy : policies_) { - is_allowed &= - CheckScriptHashAgainstPolicy(csp_hash_values, policy, - InlineType::kBlock) || - policy->AllowInlineScript(element, context_url, nonce, context_line, - reporting_policy, script_content); - } - - return is_allowed; -} - -bool ContentSecurityPolicy::AllowInlineStyle( - Element* element, - const String& context_url, - const String& nonce, - const WTF::OrdinalNumber& context_line, - const String& style_content, +bool ContentSecurityPolicy::AllowInline( InlineType inline_type, + Element* element, + const String& content, + const String& nonce, + const String& context_url, + const WTF::OrdinalNumber& context_line, SecurityViolationReportingPolicy reporting_policy) const { - DCHECK(element); + DCHECK(element || inline_type == InlineType::kInlineEventHandler || + inline_type == InlineType::kJavaScriptURL); - if (override_inline_style_allowed_) + const bool is_script = IsScriptInlineType(inline_type); + if (!is_script && override_inline_style_allowed_) { return true; + } Vector<CSPHashValue> csp_hash_values; - FillInCSPHashValues(style_content, style_hash_algorithms_used_, - &csp_hash_values); + FillInCSPHashValues( + content, + is_script ? script_hash_algorithms_used_ : style_hash_algorithms_used_, + &csp_hash_values); bool is_allowed = true; for (const auto& policy : policies_) { + // May be whitelisted by hash, if 'unsafe-hashes' is present in a policy. + // Check against the digest of the |content| and also check whether inline + // script is allowed. is_allowed &= - CheckStyleHashAgainstPolicy(csp_hash_values, policy, inline_type) || - policy->AllowInlineStyle(element, context_url, nonce, context_line, - reporting_policy, style_content, inline_type); + CheckHashAgainstPolicy(csp_hash_values, policy, inline_type) || + policy->AllowInline(inline_type, element, content, nonce, context_url, + context_line, reporting_policy); } return is_allowed; } +bool ContentSecurityPolicy::IsScriptInlineType(InlineType inline_type) { + switch (inline_type) { + case ContentSecurityPolicy::InlineType::kJavaScriptURL: + case ContentSecurityPolicy::InlineType::kInlineEventHandler: + case ContentSecurityPolicy::InlineType::kInlineScriptElement: + return true; + + case ContentSecurityPolicy::InlineType::kInlineStyleAttribute: + case ContentSecurityPolicy::InlineType::kInlineStyleElement: + return false; + } +} + bool ContentSecurityPolicy::AllowEval( ScriptState* script_state, SecurityViolationReportingPolicy reporting_policy,
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.h b/third_party/blink/renderer/core/frame/csp/content_security_policy.h index 5a37d45..0a1f91d 100644 --- a/third_party/blink/renderer/core/frame/csp/content_security_policy.h +++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.h
@@ -145,7 +145,13 @@ kTrustedTypesViolation }; - enum class InlineType { kBlock, kAttribute }; + enum class InlineType { + kJavaScriptURL, + kInlineEventHandler, + kInlineScriptElement, + kInlineStyleAttribute, + kInlineStyleElement + }; enum class DirectiveType { kBaseURI, @@ -222,27 +228,6 @@ Vector<CSPHeaderAndType> Headers() const; - // |element| will not be present for navigations to javascript URLs, - // as those checks happen in the middle of the navigation algorithm, - // and we generally don't have access to the responsible element. - bool AllowJavaScriptURLs(Element*, - const String& source, - const String& context_url, - const WTF::OrdinalNumber& context_line, - SecurityViolationReportingPolicy = - SecurityViolationReportingPolicy::kReport) const; - - // |element| will be present almost all of the time, but because of - // strangeness around targeting handlers for '<body>', '<svg>', and - // '<frameset>', it will be 'nullptr' for handlers on those - // elements. - bool AllowInlineEventHandler( - Element*, - const String& source, - const String& context_url, - const WTF::OrdinalNumber& context_line, - SecurityViolationReportingPolicy = - SecurityViolationReportingPolicy::kReport) const; // When the reporting status is |SendReport|, the |ExceptionStatus| // should indicate whether the caller will throw a JavaScript // exception in the event of a violation. When the caller will throw @@ -302,21 +287,25 @@ // Passing 'String()' into the |nonce| arguments in the following methods // represents an unnonced resource load. - bool AllowInlineScript(Element*, - const String& context_url, - const String& nonce, - const WTF::OrdinalNumber& context_line, - const String& script_content, - SecurityViolationReportingPolicy = - SecurityViolationReportingPolicy::kReport) const; - bool AllowInlineStyle(Element*, - const String& context_url, - const String& nonce, - const WTF::OrdinalNumber& context_line, - const String& style_content, - InlineType, - SecurityViolationReportingPolicy = - SecurityViolationReportingPolicy::kReport) const; + // + // For kJavaScriptURL case, |element| will not be present for navigations to + // javascript URLs, as those checks happen in the middle of the navigation + // algorithm, and we generally don't have access to the responsible element. + // + // For kInlineEventHandler case, |element| will be present almost all of the + // time, but because of strangeness around targeting handlers for '<body>', + // '<svg>', and '<frameset>', it will be 'nullptr' for handlers on those + // elements. + bool AllowInline(InlineType, + Element*, + const String& content, + const String& nonce, + const String& context_url, + const WTF::OrdinalNumber& context_line, + SecurityViolationReportingPolicy = + SecurityViolationReportingPolicy::kReport) const; + + static bool IsScriptInlineType(InlineType); // |allowAncestors| does not need to know whether the resource was a // result of a redirect. After a redirect, source paths are usually @@ -547,12 +536,9 @@ // checks a vector of csp hashes against policy, probably a good idea // to use in tandem with FillInCSPHashValues. - static bool CheckScriptHashAgainstPolicy(Vector<CSPHashValue>&, - const Member<CSPDirectiveList>&, - InlineType); - static bool CheckStyleHashAgainstPolicy(Vector<CSPHashValue>&, - const Member<CSPDirectiveList>&, - InlineType); + static bool CheckHashAgainstPolicy(Vector<CSPHashValue>&, + const Member<CSPDirectiveList>&, + InlineType); bool ShouldBypassContentSecurityPolicy( const KURL&,
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc index 6db1e5cb..00685e1 100644 --- a/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc +++ b/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc
@@ -732,9 +732,11 @@ policy->DidReceiveHeader(String("script-src ") + test.policy, kContentSecurityPolicyHeaderTypeEnforce, kContentSecurityPolicyHeaderSourceHTTP); - EXPECT_EQ(test.allowed, policy->AllowInlineScript(element, context_url, - String(test.nonce), - context_line, content)); + EXPECT_EQ( + test.allowed, + policy->AllowInline( + ContentSecurityPolicy::InlineType::kInlineScriptElement, element, + content, String(test.nonce), context_url, context_line)); EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size()); // Enforce 'style-src' @@ -743,10 +745,11 @@ policy->DidReceiveHeader(String("style-src ") + test.policy, kContentSecurityPolicyHeaderTypeEnforce, kContentSecurityPolicyHeaderSourceHTTP); - EXPECT_EQ(test.allowed, - policy->AllowInlineStyle( - element, context_url, String(test.nonce), context_line, - content, ContentSecurityPolicy::InlineType::kBlock)); + EXPECT_EQ( + test.allowed, + policy->AllowInline( + ContentSecurityPolicy::InlineType::kInlineStyleElement, element, + content, String(test.nonce), context_url, context_line)); EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size()); // Report 'script-src' @@ -755,8 +758,9 @@ policy->DidReceiveHeader(String("script-src ") + test.policy, kContentSecurityPolicyHeaderTypeReport, kContentSecurityPolicyHeaderSourceHTTP); - EXPECT_TRUE(policy->AllowInlineScript( - element, context_url, String(test.nonce), context_line, content)); + EXPECT_TRUE(policy->AllowInline( + ContentSecurityPolicy::InlineType::kInlineScriptElement, element, + content, String(test.nonce), context_url, context_line)); EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size()); // Report 'style-src' @@ -765,9 +769,9 @@ policy->DidReceiveHeader(String("style-src ") + test.policy, kContentSecurityPolicyHeaderTypeReport, kContentSecurityPolicyHeaderSourceHTTP); - EXPECT_TRUE(policy->AllowInlineStyle( - element, context_url, String(test.nonce), context_line, content, - ContentSecurityPolicy::InlineType::kBlock)); + EXPECT_TRUE(policy->AllowInline( + ContentSecurityPolicy::InlineType::kInlineStyleElement, element, + content, String(test.nonce), context_url, context_line)); EXPECT_EQ(expected_reports, policy->violation_reports_sent_.size()); } } @@ -1518,10 +1522,12 @@ HTMLScriptElement::Create(*document, CreateElementFlags::ByParser()); EXPECT_TRUE(csp->Headers().IsEmpty()); - EXPECT_TRUE( - csp->AllowJavaScriptURLs(element, source, context_url, ordinal_number)); - EXPECT_TRUE(csp->AllowInlineEventHandler(element, source, context_url, - ordinal_number)); + EXPECT_TRUE(csp->AllowInline( + ContentSecurityPolicy::InlineType::kJavaScriptURL, element, source, + String() /* nonce */, context_url, ordinal_number)); + EXPECT_TRUE(csp->AllowInline( + ContentSecurityPolicy::InlineType::kInlineEventHandler, element, source, + String() /* nonce */, context_url, ordinal_number)); EXPECT_TRUE(csp->AllowEval(nullptr, SecurityViolationReportingPolicy::kReport, ContentSecurityPolicy::kWillNotThrowException, g_empty_string)); @@ -1563,11 +1569,12 @@ example_url, nonce, IntegrityMetadataSet(), kParserInserted)); EXPECT_TRUE(csp->AllowTrustedTypePolicy("somepolicy")); - EXPECT_TRUE(csp->AllowInlineScript(element, context_url, nonce, - ordinal_number, source)); - EXPECT_TRUE(csp->AllowInlineStyle(element, context_url, nonce, ordinal_number, - source, - ContentSecurityPolicy::InlineType::kBlock)); + EXPECT_TRUE( + csp->AllowInline(ContentSecurityPolicy::InlineType::kInlineScriptElement, + element, source, nonce, context_url, ordinal_number)); + EXPECT_TRUE( + csp->AllowInline(ContentSecurityPolicy::InlineType::kInlineStyleElement, + element, source, nonce, context_url, ordinal_number)); EXPECT_TRUE(csp->AllowAncestors(document->GetFrame(), example_url)); EXPECT_FALSE(csp->IsFrameAncestorsEnforced()); EXPECT_TRUE(csp->AllowRequestWithoutIntegrity(
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc index b45ff1b7..22c42b5 100644 --- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc +++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
@@ -74,6 +74,45 @@ return true; } +// TODO(hiroshige): The following two methods are slightly different. +// Investigate the correct behavior and merge them. +ContentSecurityPolicy::DirectiveType +GetDirectiveTypeForAllowInlineFromInlineType( + ContentSecurityPolicy::InlineType inline_type) { + switch (inline_type) { + case ContentSecurityPolicy::InlineType::kJavaScriptURL: + case ContentSecurityPolicy::InlineType::kInlineScriptElement: + return ContentSecurityPolicy::DirectiveType::kScriptSrcElem; + + case ContentSecurityPolicy::InlineType::kInlineEventHandler: + return ContentSecurityPolicy::DirectiveType::kScriptSrcAttr; + + case ContentSecurityPolicy::InlineType::kInlineStyleAttribute: + return ContentSecurityPolicy::DirectiveType::kStyleSrcAttr; + + case ContentSecurityPolicy::InlineType::kInlineStyleElement: + return ContentSecurityPolicy::DirectiveType::kStyleSrcElem; + } +} + +ContentSecurityPolicy::DirectiveType GetDirectiveTypeForAllowHashFromInlineType( + ContentSecurityPolicy::InlineType inline_type) { + switch (inline_type) { + case ContentSecurityPolicy::InlineType::kInlineScriptElement: + return ContentSecurityPolicy::DirectiveType::kScriptSrcElem; + + case ContentSecurityPolicy::InlineType::kJavaScriptURL: + case ContentSecurityPolicy::InlineType::kInlineEventHandler: + return ContentSecurityPolicy::DirectiveType::kScriptSrcAttr; + + case ContentSecurityPolicy::InlineType::kInlineStyleAttribute: + return ContentSecurityPolicy::DirectiveType::kStyleSrcAttr; + + case ContentSecurityPolicy::InlineType::kInlineStyleElement: + return ContentSecurityPolicy::DirectiveType::kStyleSrcElem; + } +} + } // namespace CSPDirectiveList::CSPDirectiveList(ContentSecurityPolicy* policy, @@ -615,99 +654,70 @@ return DenyIfEnforcingPolicy(); } -bool CSPDirectiveList::AllowJavaScriptURLs( +bool CSPDirectiveList::AllowInline( + ContentSecurityPolicy::InlineType inline_type, Element* element, - const String& source, - const String& context_url, - const WTF::OrdinalNumber& context_line, - SecurityViolationReportingPolicy reporting_policy) const { - SourceListDirective* directive = - OperativeDirective(ContentSecurityPolicy::DirectiveType::kScriptSrcElem); - if (reporting_policy == SecurityViolationReportingPolicy::kReport) { - return CheckInlineAndReportViolation( - directive, - "Refused to run the JavaScript URL because it violates the following " - "Content Security Policy directive: ", - element, source, context_url, context_line, true, "sha256-...", - ContentSecurityPolicy::DirectiveType::kScriptSrcElem); - } - - return !directive || directive->AllowAllInline(); -} - -bool CSPDirectiveList::AllowInlineEventHandlers( - Element* element, - const String& source, - const String& context_url, - const WTF::OrdinalNumber& context_line, - SecurityViolationReportingPolicy reporting_policy) const { - SourceListDirective* directive = - OperativeDirective(ContentSecurityPolicy::DirectiveType::kScriptSrcAttr); - if (reporting_policy == SecurityViolationReportingPolicy::kReport) { - return CheckInlineAndReportViolation( - directive, - "Refused to execute inline event handler because it violates the " - "following Content Security Policy directive: ", - element, source, context_url, context_line, true, "sha256-...", - ContentSecurityPolicy::DirectiveType::kScriptSrcAttr); - } - - return !directive || directive->AllowAllInline(); -} - -bool CSPDirectiveList::AllowInlineScript( - Element* element, - const String& context_url, - const String& nonce, - const WTF::OrdinalNumber& context_line, - SecurityViolationReportingPolicy reporting_policy, - const String& content) const { - SourceListDirective* directive = - OperativeDirective(ContentSecurityPolicy::DirectiveType::kScriptSrcElem); - if (IsMatchingNoncePresent(directive, nonce)) - return true; - if (element && IsHTMLScriptElement(element) && - !ToHTMLScriptElement(element)->Loader()->IsParserInserted() && - AllowDynamic(ContentSecurityPolicy::DirectiveType::kScriptSrcElem)) { - return true; - } - if (reporting_policy == SecurityViolationReportingPolicy::kReport) { - return CheckInlineAndReportViolation( - directive, - "Refused to execute inline script because it violates the following " - "Content Security Policy directive: ", - element, content, context_url, context_line, true, - GetSha256String(content), - ContentSecurityPolicy::DirectiveType::kScriptSrcElem); - } - - return !directive || directive->AllowAllInline(); -} - -bool CSPDirectiveList::AllowInlineStyle( - Element* element, - const String& context_url, - const String& nonce, - const WTF::OrdinalNumber& context_line, - SecurityViolationReportingPolicy reporting_policy, const String& content, - ContentSecurityPolicy::InlineType inline_type) const { - ContentSecurityPolicy::DirectiveType effective_type = - inline_type == ContentSecurityPolicy::InlineType::kAttribute - ? ContentSecurityPolicy::DirectiveType::kStyleSrcAttr - : ContentSecurityPolicy::DirectiveType::kStyleSrcElem; - SourceListDirective* directive = OperativeDirective(effective_type); + const String& nonce, + const String& context_url, + const WTF::OrdinalNumber& context_line, + SecurityViolationReportingPolicy reporting_policy) const { + ContentSecurityPolicy::DirectiveType type = + GetDirectiveTypeForAllowInlineFromInlineType(inline_type); + SourceListDirective* directive = OperativeDirective(type); if (IsMatchingNoncePresent(directive, nonce)) return true; + if (inline_type == ContentSecurityPolicy::InlineType::kInlineScriptElement && + element && IsHTMLScriptElement(element) && + !ToHTMLScriptElement(element)->Loader()->IsParserInserted() && + AllowDynamic(type)) { + return true; + } if (reporting_policy == SecurityViolationReportingPolicy::kReport) { + String hash_value; + switch (inline_type) { + case ContentSecurityPolicy::InlineType::kJavaScriptURL: + case ContentSecurityPolicy::InlineType::kInlineEventHandler: + hash_value = "sha256-..."; + break; + + case ContentSecurityPolicy::InlineType::kInlineScriptElement: + case ContentSecurityPolicy::InlineType::kInlineStyleAttribute: + case ContentSecurityPolicy::InlineType::kInlineStyleElement: + hash_value = GetSha256String(content); + break; + } + + String message; + switch (inline_type) { + case ContentSecurityPolicy::InlineType::kJavaScriptURL: + message = "run the JavaScript URL"; + break; + + case ContentSecurityPolicy::InlineType::kInlineEventHandler: + message = "execute inline event handler"; + break; + + case ContentSecurityPolicy::InlineType::kInlineScriptElement: + message = "execute inline script"; + break; + + case ContentSecurityPolicy::InlineType::kInlineStyleAttribute: + case ContentSecurityPolicy::InlineType::kInlineStyleElement: + message = "apply inline style"; + break; + } + return CheckInlineAndReportViolation( directive, - "Refused to apply inline style because it violates the following " - "Content Security Policy directive: ", - element, content, context_url, context_line, false, - GetSha256String(content), effective_type); + "Refused to " + message + + " because it violates the following Content Security Policy " + "directive: ", + element, content, context_url, context_line, + ContentSecurityPolicy::IsScriptInlineType(inline_type), hash_value, + type); } return !directive || directive->AllowAllInline(); @@ -860,35 +870,29 @@ bool CSPDirectiveList::AllowHash( const CSPHashValue& hash_value, - const ContentSecurityPolicy::InlineType type, - const ContentSecurityPolicy::DirectiveType directive_type) const { - if (type == ContentSecurityPolicy::InlineType::kAttribute) { - if (!policy_->ExperimentalFeaturesEnabled()) - return false; - if (!CheckUnsafeHashesAllowed(OperativeDirective(directive_type))) - return false; + const ContentSecurityPolicy::InlineType inline_type) const { + ContentSecurityPolicy::DirectiveType directive_type = + GetDirectiveTypeForAllowHashFromInlineType(inline_type); + switch (directive_type) { + case ContentSecurityPolicy::DirectiveType::kScriptSrcAttr: + case ContentSecurityPolicy::DirectiveType::kStyleSrcAttr: + if (!policy_->ExperimentalFeaturesEnabled()) + return false; + if (!CheckUnsafeHashesAllowed(OperativeDirective(directive_type))) + return false; + break; + + case ContentSecurityPolicy::DirectiveType::kScriptSrcElem: + case ContentSecurityPolicy::DirectiveType::kStyleSrcElem: + break; + + default: + NOTREACHED(); + break; } return CheckHash(OperativeDirective(directive_type), hash_value); } -bool CSPDirectiveList::AllowScriptHash( - const CSPHashValue& hash_value, - ContentSecurityPolicy::InlineType type) const { - return AllowHash(hash_value, type, - type == ContentSecurityPolicy::InlineType::kAttribute - ? ContentSecurityPolicy::DirectiveType::kScriptSrcAttr - : ContentSecurityPolicy::DirectiveType::kScriptSrcElem); -} - -bool CSPDirectiveList::AllowStyleHash( - const CSPHashValue& hash_value, - ContentSecurityPolicy::InlineType type) const { - return AllowHash(hash_value, type, - type == ContentSecurityPolicy::InlineType::kAttribute - ? ContentSecurityPolicy::DirectiveType::kStyleSrcAttr - : ContentSecurityPolicy::DirectiveType::kStyleSrcElem); -} - bool CSPDirectiveList::AllowDynamic( ContentSecurityPolicy::DirectiveType directive_type) const { return CheckDynamic(OperativeDirective(directive_type));
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.h b/third_party/blink/renderer/core/frame/csp/csp_directive_list.h index 9826d8ae..27af666a 100644 --- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.h +++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.h
@@ -52,29 +52,14 @@ return header_source_; } - bool AllowJavaScriptURLs(Element*, - const String& source, - const String& context_url, - const WTF::OrdinalNumber& context_line, - SecurityViolationReportingPolicy) const; - bool AllowInlineEventHandlers(Element*, - const String& source, - const String& context_url, - const WTF::OrdinalNumber& context_line, - SecurityViolationReportingPolicy) const; - bool AllowInlineScript(Element*, - const String& context_url, - const String& nonce, - const WTF::OrdinalNumber& context_line, - SecurityViolationReportingPolicy, - const String& script_content) const; - bool AllowInlineStyle(Element*, - const String& context_url, - const String& nonce, - const WTF::OrdinalNumber& context_line, - SecurityViolationReportingPolicy, - const String& style_content, - ContentSecurityPolicy::InlineType inline_type) const; + bool AllowInline(ContentSecurityPolicy::InlineType, + Element*, + const String& content, + const String& nonce, + const String& context_url, + const WTF::OrdinalNumber& context_line, + SecurityViolationReportingPolicy) const; + bool AllowEval(ScriptState*, SecurityViolationReportingPolicy, ContentSecurityPolicy::ExceptionStatus, @@ -107,10 +92,6 @@ bool AllowAncestors(LocalFrame*, const KURL&, SecurityViolationReportingPolicy) const; - bool AllowScriptHash(const CSPHashValue&, - ContentSecurityPolicy::InlineType) const; - bool AllowStyleHash(const CSPHashValue&, - ContentSecurityPolicy::InlineType) const; bool AllowDynamic(ContentSecurityPolicy::DirectiveType) const; bool AllowDynamicWorker() const; @@ -151,6 +132,9 @@ bool ShouldSendCSPHeader(ResourceType) const; + bool AllowHash(const CSPHashValue& hash_value, + const ContentSecurityPolicy::InlineType inline_type) const; + // The algorithm is described here: // https://w3c.github.io/webappsec-csp/embedded/#subsume-policy bool Subsumes(const CSPDirectiveListVector&); @@ -305,11 +289,6 @@ const ContentSecurityPolicy::DirectiveType, const CSPDirectiveListVector& policies); - bool AllowHash( - const CSPHashValue& hash_value, - const ContentSecurityPolicy::InlineType type, - const ContentSecurityPolicy::DirectiveType directive_type) const; - Member<ContentSecurityPolicy> policy_; String header_;
diff --git a/third_party/blink/renderer/core/frame/location.cc b/third_party/blink/renderer/core/frame/location.cc index eedf81247..01384aaf 100644 --- a/third_party/blink/renderer/core/frame/location.cc +++ b/third_party/blink/renderer/core/frame/location.cc
@@ -300,8 +300,10 @@ !ContentSecurityPolicy::ShouldBypassMainWorld(current_document)) { String script_source = DecodeURLEscapeSequences( completed_url.GetString(), DecodeURLMode::kUTF8OrIsomorphic); - if (!current_document->GetContentSecurityPolicy()->AllowJavaScriptURLs( - nullptr, script_source, current_document->Url(), OrdinalNumber())) { + if (!current_document->GetContentSecurityPolicy()->AllowInline( + ContentSecurityPolicy::InlineType::kJavaScriptURL, + nullptr /* element */, script_source, String() /* nonce */, + current_document->Url(), OrdinalNumber())) { return; } }
diff --git a/third_party/blink/renderer/core/frame/settings.json5 b/third_party/blink/renderer/core/frame/settings.json5 index b47ca3eb..46a5a88c 100644 --- a/third_party/blink/renderer/core/frame/settings.json5 +++ b/third_party/blink/renderer/core/frame/settings.json5
@@ -932,32 +932,32 @@ // { name: "lazyFrameLoadingDistanceThresholdPxUnknown", - initial: 800, + initial: 6000, type: "int", }, { name: "lazyFrameLoadingDistanceThresholdPxOffline", - initial: 800, + initial: 8000, type: "int", }, { name: "lazyFrameLoadingDistanceThresholdPxSlow2G", - initial: 800, + initial: 8000, type: "int", }, { name: "lazyFrameLoadingDistanceThresholdPx2G", - initial: 800, + initial: 6000, type: "int", }, { name: "lazyFrameLoadingDistanceThresholdPx3G", - initial: 800, + initial: 5000, type: "int", }, { name: "lazyFrameLoadingDistanceThresholdPx4G", - initial: 800, + initial: 4000, type: "int", }, @@ -966,32 +966,32 @@ // { name: "lazyImageLoadingDistanceThresholdPxUnknown", - initial: 800, + initial: 5000, type: "int", }, { name: "lazyImageLoadingDistanceThresholdPxOffline", - initial: 800, + initial: 8000, type: "int", }, { name: "lazyImageLoadingDistanceThresholdPxSlow2G", - initial: 800, + initial: 8000, type: "int", }, { name: "lazyImageLoadingDistanceThresholdPx2G", - initial: 800, + initial: 6000, type: "int", }, { name: "lazyImageLoadingDistanceThresholdPx3G", - initial: 800, + initial: 4000, type: "int", }, { name: "lazyImageLoadingDistanceThresholdPx4G", - initial: 800, + initial: 3000, type: "int", },
diff --git a/third_party/blink/renderer/core/html/html_script_element.cc b/third_party/blink/renderer/core/html/html_script_element.cc index 8116751..bab8769d 100644 --- a/third_party/blink/renderer/core/html/html_script_element.cc +++ b/third_party/blink/renderer/core/html/html_script_element.cc
@@ -244,8 +244,9 @@ const AtomicString& nonce, const WTF::OrdinalNumber& context_line, const String& script_content) { - return GetDocument().GetContentSecurityPolicy()->AllowInlineScript( - this, GetDocument().Url(), nonce, context_line, script_content); + return GetDocument().GetContentSecurityPolicy()->AllowInline( + ContentSecurityPolicy::InlineType::kInlineScriptElement, this, + script_content, nonce, GetDocument().Url(), context_line); } Document& HTMLScriptElement::GetDocument() const {
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc index 62d7558..5622122 100644 --- a/third_party/blink/renderer/core/loader/frame_loader.cc +++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -695,9 +695,11 @@ bool javascript_url_is_allowed = request.ShouldCheckMainWorldContentSecurityPolicy() == kDoNotCheckContentSecurityPolicy || - origin_document->GetContentSecurityPolicy()->AllowJavaScriptURLs( + origin_document->GetContentSecurityPolicy()->AllowInline( + ContentSecurityPolicy::InlineType::kJavaScriptURL, frame_->DeprecatedLocalOwner(), url.GetString(), - origin_document->Url(), OrdinalNumber::First()); + String() /* nonce */, origin_document->Url(), + OrdinalNumber::First()); if (!javascript_url_is_allowed) return false;
diff --git a/third_party/blink/renderer/core/loader/http_equiv.cc b/third_party/blink/renderer/core/loader/http_equiv.cc index 85ce2f43..3af0d588 100644 --- a/third_party/blink/renderer/core/loader/http_equiv.cc +++ b/third_party/blink/renderer/core/loader/http_equiv.cc
@@ -182,8 +182,9 @@ const AtomicString& content, Element* element) { UseCounter::Count(document, WebFeature::kMetaRefresh); - if (!document.GetContentSecurityPolicy()->AllowInlineScript( - element, NullURL(), "", OrdinalNumber(), "", + if (!document.GetContentSecurityPolicy()->AllowInline( + ContentSecurityPolicy::InlineType::kInlineScriptElement, element, + "" /* content */, "" /* nonce */, NullURL(), OrdinalNumber(), SecurityViolationReportingPolicy::kSuppressReporting)) { UseCounter::Count(document, WebFeature::kMetaRefreshWhenCSPBlocksInlineScript); @@ -197,8 +198,9 @@ Element* element) { Deprecation::CountDeprecation(document, WebFeature::kMetaSetCookie); - if (!document.GetContentSecurityPolicy()->AllowInlineScript( - element, NullURL(), "", OrdinalNumber(), "", + if (!document.GetContentSecurityPolicy()->AllowInline( + ContentSecurityPolicy::InlineType::kInlineScriptElement, element, + "" /* content */, "" /* nonce */, NullURL(), OrdinalNumber(), SecurityViolationReportingPolicy::kSuppressReporting)) { UseCounter::Count(document, WebFeature::kMetaSetCookieWhenCSPBlocksInlineScript);
diff --git a/third_party/blink/renderer/core/page/create_window.cc b/third_party/blink/renderer/core/page/create_window.cc index 32aa618b..85519fd 100644 --- a/third_party/blink/renderer/core/page/create_window.cc +++ b/third_party/blink/renderer/core/page/create_window.cc
@@ -388,11 +388,10 @@ String script_source = DecodeURLEscapeSequences( completed_url.GetString(), DecodeURLMode::kUTF8OrIsomorphic); - if (!opener_frame.GetDocument() - ->GetContentSecurityPolicy() - ->AllowJavaScriptURLs(nullptr, script_source, - opener_frame.GetDocument()->Url(), - OrdinalNumber())) { + if (!opener_frame.GetDocument()->GetContentSecurityPolicy()->AllowInline( + ContentSecurityPolicy::InlineType::kJavaScriptURL, + nullptr /* element */, script_source, String() /* nonce */, + opener_frame.GetDocument()->Url(), OrdinalNumber())) { return nullptr; } }
diff --git a/third_party/blink/renderer/core/svg/svg_geometry_element.cc b/third_party/blink/renderer/core/svg/svg_geometry_element.cc index a4fb066e..8fc1efe 100644 --- a/third_party/blink/renderer/core/svg/svg_geometry_element.cc +++ b/third_party/blink/renderer/core/svg/svg_geometry_element.cc
@@ -84,7 +84,7 @@ } bool SVGGeometryElement::isPointInFill(SVGPointTearOff* point) const { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); // FIXME: Eventually we should support isPointInFill for display:none // elements. @@ -98,7 +98,7 @@ } bool SVGGeometryElement::isPointInStroke(SVGPointTearOff* point) const { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); // FIXME: Eventually we should support isPointInStroke for display:none // elements. @@ -135,7 +135,7 @@ } float SVGGeometryElement::getTotalLength() { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); if (!GetLayoutObject()) return 0; @@ -143,7 +143,7 @@ } SVGPointTearOff* SVGGeometryElement::getPointAtLength(float length) { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); FloatPoint point; if (GetLayoutObject()) {
diff --git a/third_party/blink/renderer/core/svg/svg_path_element.cc b/third_party/blink/renderer/core/svg/svg_path_element.cc index 6b8cffd..0b03e77 100644 --- a/third_party/blink/renderer/core/svg/svg_path_element.cc +++ b/third_party/blink/renderer/core/svg/svg_path_element.cc
@@ -64,12 +64,12 @@ } float SVGPathElement::getTotalLength() { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); return SVGPathQuery(PathByteStream()).GetTotalLength(); } SVGPointTearOff* SVGPathElement::getPointAtLength(float length) { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); SVGPathQuery path_query(PathByteStream()); if (length < 0) { length = 0;
diff --git a/third_party/blink/renderer/core/svg/svg_script_element.cc b/third_party/blink/renderer/core/svg/svg_script_element.cc index fd81c48..fd929bb 100644 --- a/third_party/blink/renderer/core/svg/svg_script_element.cc +++ b/third_party/blink/renderer/core/svg/svg_script_element.cc
@@ -137,8 +137,9 @@ const AtomicString& nonce, const WTF::OrdinalNumber& context_line, const String& script_content) { - return GetDocument().GetContentSecurityPolicy()->AllowInlineScript( - this, GetDocument().Url(), nonce, context_line, script_content); + return GetDocument().GetContentSecurityPolicy()->AllowInline( + ContentSecurityPolicy::InlineType::kInlineScriptElement, this, + script_content, nonce, GetDocument().Url(), context_line); } Document& SVGScriptElement::GetDocument() const {
diff --git a/third_party/blink/renderer/core/svg/svg_svg_element.cc b/third_party/blink/renderer/core/svg/svg_svg_element.cc index 1c84ccb..f9e9d71 100644 --- a/third_party/blink/renderer/core/svg/svg_svg_element.cc +++ b/third_party/blink/renderer/core/svg/svg_svg_element.cc
@@ -373,7 +373,7 @@ StaticNodeList* SVGSVGElement::getIntersectionList( SVGRectTearOff* rect, SVGElement* reference_element) const { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); return CollectIntersectionOrEnclosureList( rect->Target()->Value(), reference_element, kCheckIntersection); @@ -382,7 +382,7 @@ StaticNodeList* SVGSVGElement::getEnclosureList( SVGRectTearOff* rect, SVGElement* reference_element) const { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); return CollectIntersectionOrEnclosureList(rect->Target()->Value(), reference_element, kCheckEnclosure); @@ -391,7 +391,7 @@ bool SVGSVGElement::checkIntersection(SVGElement* element, SVGRectTearOff* rect) const { DCHECK(element); - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); return CheckIntersectionOrEnclosure(*element, rect->Target()->Value(), kCheckIntersection); @@ -400,7 +400,7 @@ bool SVGSVGElement::checkEnclosure(SVGElement* element, SVGRectTearOff* rect) const { DCHECK(element); - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); return CheckIntersectionOrEnclosure(*element, rect->Target()->Value(), kCheckEnclosure);
diff --git a/third_party/blink/renderer/core/svg/svg_text_content_element.cc b/third_party/blink/renderer/core/svg/svg_text_content_element.cc index 307267c..a08ba52 100644 --- a/third_party/blink/renderer/core/svg/svg_text_content_element.cc +++ b/third_party/blink/renderer/core/svg/svg_text_content_element.cc
@@ -94,12 +94,12 @@ } unsigned SVGTextContentElement::getNumberOfChars() { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); return SVGTextQuery(GetLayoutObject()).NumberOfCharacters(); } float SVGTextContentElement::getComputedTextLength() { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); return SVGTextQuery(GetLayoutObject()).TextLength(); } @@ -107,7 +107,7 @@ unsigned charnum, unsigned nchars, ExceptionState& exception_state) { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); unsigned number_of_chars = getNumberOfChars(); if (charnum >= number_of_chars) { @@ -127,7 +127,7 @@ SVGPointTearOff* SVGTextContentElement::getStartPositionOfChar( unsigned charnum, ExceptionState& exception_state) { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); if (charnum >= getNumberOfChars()) { exception_state.ThrowDOMException( @@ -145,7 +145,7 @@ SVGPointTearOff* SVGTextContentElement::getEndPositionOfChar( unsigned charnum, ExceptionState& exception_state) { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); if (charnum >= getNumberOfChars()) { exception_state.ThrowDOMException( @@ -163,7 +163,7 @@ SVGRectTearOff* SVGTextContentElement::getExtentOfChar( unsigned charnum, ExceptionState& exception_state) { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); if (charnum >= getNumberOfChars()) { exception_state.ThrowDOMException( @@ -180,7 +180,7 @@ float SVGTextContentElement::getRotationOfChar( unsigned charnum, ExceptionState& exception_state) { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); if (charnum >= getNumberOfChars()) { exception_state.ThrowDOMException( @@ -196,7 +196,7 @@ int SVGTextContentElement::getCharNumAtPosition( SVGPointTearOff* point, ExceptionState& exception_state) { - GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); + GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this); return SVGTextQuery(GetLayoutObject()) .CharacterNumberAtPosition(point->Target()->Value()); }
diff --git a/third_party/blink/renderer/devtools/front_end/emulation/SensorsView.js b/third_party/blink/renderer/devtools/front_end/emulation/SensorsView.js index 514b5657e..90d603a 100644 --- a/third_party/blink/renderer/devtools/front_end/emulation/SensorsView.js +++ b/third_party/blink/renderer/devtools/front_end/emulation/SensorsView.js
@@ -41,7 +41,8 @@ */ _createGeolocationSection(geolocation) { const geogroup = this.contentElement.createChild('section', 'sensors-group'); - geogroup.createChild('div', 'sensors-group-title').textContent = Common.UIString('Geolocation'); + const geogroupTitle = UI.createLabel(ls`Geolocation`, 'sensors-group-title'); + geogroup.appendChild(geogroupTitle); const fields = geogroup.createChild('div', 'geo-fields'); const noOverrideOption = { @@ -50,6 +51,7 @@ }; this._locationSelectElement = fields.createChild('select', 'chrome-select'); + UI.bindLabelToControl(geogroupTitle, this._locationSelectElement); // No override this._locationSelectElement.appendChild(new Option(noOverrideOption.title, noOverrideOption.location)); @@ -113,8 +115,8 @@ this._latitudeInput.title = modifierKeyMessage; this._longitudeInput.title = modifierKeyMessage; - latitudeGroup.createChild('div', 'latlong-title').textContent = Common.UIString('Latitude'); - longitudeGroup.createChild('div', 'latlong-title').textContent = Common.UIString('Longitude'); + latitudeGroup.appendChild(UI.createLabel(ls`Latitude`, 'latlong-title', this._latitudeInput)); + longitudeGroup.appendChild(UI.createLabel(ls`Longitude`, 'latlong-title', this._longitudeInput)); } _geolocationSelectChanged() { @@ -166,7 +168,8 @@ _createDeviceOrientationSection() { const orientationGroup = this.contentElement.createChild('section', 'sensors-group'); - orientationGroup.createChild('div', 'sensors-group-title').textContent = Common.UIString('Orientation'); + const orientationTitle = UI.createLabel(ls`Orientation`, 'sensors-group-title'); + orientationGroup.appendChild(orientationTitle); const orientationContent = orientationGroup.createChild('div', 'orientation-content'); const fields = orientationContent.createChild('div', 'orientation-fields'); @@ -179,6 +182,7 @@ orientation: Emulation.SensorsView.NonPresetOptions.Custom }; this._orientationSelectElement = this.contentElement.createChild('select', 'chrome-select'); + UI.bindLabelToControl(orientationTitle, this._orientationSelectElement); this._orientationSelectElement.appendChild( new Option(orientationOffOption.title, orientationOffOption.orientation)); this._orientationSelectElement.appendChild( @@ -321,7 +325,7 @@ _createAxisInput(parentElement, input, label) { const div = parentElement.createChild('div', 'orientation-axis-input-container'); div.appendChild(input); - div.createTextChild(label); + div.appendChild(UI.createLabel(label, /* className */ '', input)); input.type = 'number'; return UI.bindInput( input, this._applyDeviceOrientationUserInput.bind(this), SDK.EmulationModel.DeviceOrientation.validator, true); @@ -351,8 +355,10 @@ this._gammaSetter = this._createAxisInput(cellElement, this._gammaElement, Common.UIString('\u03B3 (gamma)')); this._gammaSetter(String(deviceOrientation.gamma)); - cellElement.appendChild(UI.createTextButton( - Common.UIString('Reset'), this._resetDeviceOrientation.bind(this), 'orientation-reset-button')); + const resetButton = UI.createTextButton( + Common.UIString('Reset'), this._resetDeviceOrientation.bind(this), 'orientation-reset-button'); + resetButton.setAttribute('type', 'reset'); + cellElement.appendChild(resetButton); return fieldsetElement; } @@ -446,11 +452,12 @@ _appendTouchControl() { const groupElement = this.contentElement.createChild('div', 'sensors-group'); - const title = groupElement.createChild('div', 'sensors-group-title'); + const title = UI.createLabel(ls`Touch`, 'sensors-group-title'); + groupElement.appendChild(title); const fieldsElement = groupElement.createChild('div', 'sensors-group-fields'); - title.textContent = Common.UIString('Touch'); const select = fieldsElement.createChild('select', 'chrome-select'); + UI.bindLabelToControl(title, select); select.appendChild(new Option(Common.UIString('Device-based'), 'auto')); select.appendChild(new Option(Common.UIString('Force enabled'), 'enabled')); select.addEventListener('change', applyTouch, false);
diff --git a/third_party/blink/renderer/devtools/front_end/resources/AppManifestView.js b/third_party/blink/renderer/devtools/front_end/resources/AppManifestView.js index b0af86b0..d61a88a2 100644 --- a/third_party/blink/renderer/devtools/front_end/resources/AppManifestView.js +++ b/third_party/blink/renderer/devtools/front_end/resources/AppManifestView.js
@@ -115,7 +115,7 @@ this._errorsSection.element.classList.toggle('hidden', !errors.length); for (const error of errors) { this._errorsSection.appendRow().appendChild( - UI.createLabel(error.message, error.critical ? 'smallicon-error' : 'smallicon-warning')); + UI.createIconLabel(error.message, error.critical ? 'smallicon-error' : 'smallicon-warning')); } if (!data) @@ -188,7 +188,7 @@ this._installabilitySection.clearContent(); this._installabilitySection.element.classList.toggle('hidden', !installabilityErrors.length); for (const error of installabilityErrors) - this._installabilitySection.appendRow().appendChild(UI.createLabel(error, 'smallicon-warning')); + this._installabilitySection.appendRow().appendChild(UI.createIconLabel(error, 'smallicon-warning')); /** * @param {string} name
diff --git a/third_party/blink/renderer/devtools/front_end/resources/IndexedDBViews.js b/third_party/blink/renderer/devtools/front_end/resources/IndexedDBViews.js index 37ddf40..50d854a5 100644 --- a/third_party/blink/renderer/devtools/front_end/resources/IndexedDBViews.js +++ b/third_party/blink/renderer/devtools/front_end/resources/IndexedDBViews.js
@@ -125,7 +125,8 @@ this._clearButton = new UI.ToolbarButton(Common.UIString('Clear object store'), 'largeicon-clear'); this._clearButton.addEventListener(UI.ToolbarButton.Events.Click, this._clearButtonClicked, this); - this._needsRefresh = new UI.ToolbarItem(UI.createLabel(Common.UIString('Data may be stale'), 'smallicon-warning')); + this._needsRefresh = + new UI.ToolbarItem(UI.createIconLabel(Common.UIString('Data may be stale'), 'smallicon-warning')); this._needsRefresh.setVisible(false); this._needsRefresh.setTitle(Common.UIString('Some entries may have been modified'));
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js index 6297481..be3aad96 100644 --- a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js +++ b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js
@@ -438,7 +438,7 @@ const name = this._sourceField.createChild('div', 'report-field-value-filename'); name.appendChild(Components.Linkifier.linkifyURL(version.scriptURL, {text: fileName})); if (this._registration.errors.length) { - const errorsLabel = UI.createLabel(String(this._registration.errors.length), 'smallicon-error'); + const errorsLabel = UI.createIconLabel(String(this._registration.errors.length), 'smallicon-error'); errorsLabel.classList.add('link'); errorsLabel.addEventListener('click', () => Common.console.show()); name.appendChild(errorsLabel);
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js b/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js index 1f37294..12f2070 100644 --- a/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js +++ b/third_party/blink/renderer/devtools/front_end/ui/ARIAUtils.js
@@ -3,6 +3,7 @@ // found in the LICENSE file. UI.ARIAUtils = {}; +UI.ARIAUtils._id = 0; /** * @param {!Element} element @@ -100,6 +101,14 @@ }; /** + * @param {string} prefix + * @return {string} + */ +UI.ARIAUtils.nextId = function(prefix) { + return (prefix || '') + ++UI.ARIAUtils._id; +}; + +/** * @param {!Element} element * @param {?Element} controlledElement */
diff --git a/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js b/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js index 9820f1e..1e64860 100644 --- a/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js +++ b/third_party/blink/renderer/devtools/front_end/ui/UIUtils.js
@@ -1171,6 +1171,16 @@ }; /** + * @param {!Element} label + * @param {!Element} control + */ +UI.bindLabelToControl = function(label, control) { + const controlId = UI.ARIAUtils.nextId('labelledControl'); + control.id = controlId; + label.setAttribute('for', controlId); +}; + +/** * @param {string} localName * @param {string} typeExtension * @param {function(new:HTMLElement, *)} definition @@ -1222,6 +1232,21 @@ }; /** + * @param {string} title + * @param {string=} className + * @param {!Element=} associatedControl + * @return {!Element} + */ +UI.createLabel = function(title, className, associatedControl) { + const element = createElementWithClass('label', className || ''); + element.textContent = title; + if (associatedControl) + UI.bindLabelToControl(element, associatedControl); + + return element; +}; + +/** * @param {string} name * @param {string} title * @param {boolean=} checked @@ -1240,7 +1265,7 @@ * @param {string} iconClass * @return {!Element} */ -UI.createLabel = function(title, iconClass) { +UI.createIconLabel = function(title, iconClass) { const element = createElement('span', 'dt-icon-label'); element.createChild('span').textContent = title; element.type = iconClass;
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc index 69fa1fa..19966a9 100644 --- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc +++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -169,6 +169,10 @@ RuntimeEnabledFeatures::SetDecodeToYUVEnabled(enable); } +void WebRuntimeFeatures::EnableFallbackCursorMode(bool enable) { + RuntimeEnabledFeatures::SetFallbackCursorModeEnabled(enable); +} + void WebRuntimeFeatures::EnableFastMobileScrolling(bool enable) { RuntimeEnabledFeatures::SetFastMobileScrollingEnabled(enable); }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc index f9598402..a6542d5 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -3374,6 +3374,25 @@ kHasRenderSurface); } +TEST_P(PaintArtifactCompositorTest, + DirectCompositingReasonsCausesRenderSurface) { + // When an effect has an animated transform, we should get a render surface. + auto t1 = CreateTransform(t0(), TransformationMatrix(), FloatPoint3D(), + CompositingReason::kActiveTransformAnimation); + auto e1 = CreateOpacityEffect(e0(), *t1, nullptr, 1.f); + auto c1 = CreateClip(c0(), t0(), FloatRoundedRect(50, 50, 50, 50)); + TestPaintArtifact artifact; + FloatRect r(150, 150, 100, 100); + artifact.Chunk(t0(), c0(), e0()).RectDrawing(r, Color::kWhite); + artifact.Chunk(t0(), *c1, *e1).RectDrawing(r, Color::kWhite); + Update(artifact.Build()); + ASSERT_EQ(2u, ContentLayerCount()); + + const auto* effect = GetPropertyTrees().effect_tree.Node( + ContentLayerAt(1)->effect_tree_index()); + EXPECT_TRUE(effect->has_render_surface); +} + TEST_P(PaintArtifactCompositorTest, OpacityIndirectlyAffectingTwoLayers) { auto opacity = CreateOpacityEffect(e0(), 0.5f); auto child_composited_effect = CreateOpacityEffect(
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc index 333488c..9af76e0 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -191,7 +191,9 @@ } else if (previous_transform && !current_.may_be_2d_axis_misaligned_to_render_surface) { current_.may_be_2d_axis_misaligned_to_render_surface = - !TransformsAre2dAxisAligned(current_.Transform(), *previous_transform); + !TransformsAre2dAxisAligned(current_.Transform(), + *previous_transform) || + current_.Transform().Unalias().HasActiveTransformAnimation(); } } @@ -440,7 +442,6 @@ GetEffectTree().Insert(cc::EffectNode(), current_.effect_id)); mask_effect.stable_id = mask_effect_id.GetInternalValue(); mask_effect.clip_id = clip_id; - mask_effect.has_render_surface = true; mask_effect.blend_mode = SkBlendMode::kDstIn; const auto& clip_space = current_.clip->LocalTransformSpace();
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index 8bfb19ae..9f4fa8b 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -494,6 +494,9 @@ name: "ExtraWebGLVideoTextureMetadata", }, { + name: "FallbackCursorMode", + }, + { name: "FastFlatTreeTraversal", }, {
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 6255634d..d543860 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -5951,3 +5951,5 @@ crbug.com/864994 [ Linux Win7 ] external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html [ Timeout Failure Pass ] crbug.com/937416 [ Linux Mac ] http/tests/devtools/resource-tree/resource-tree-htmlimports.js [ Pass Failure ] crbug.com/937416 http/tests/devtools/resource-tree/resource-tree-frame-navigate.js [ Pass Failure ] +crbug.com/937546 [ Win7 ] http/tests/security/xss-DENIED-cross-origin-stack-overflow.html [ Pass Timeout ] +crbug.com/937544 [ Win7 Mac ] virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/registration-mime-types.https.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/compositing/overflow/tiled-mask-expected.png b/third_party/blink/web_tests/compositing/overflow/tiled-mask-expected.png index 0eac266..2fb86778 100644 --- a/third_party/blink/web_tests/compositing/overflow/tiled-mask-expected.png +++ b/third_party/blink/web_tests/compositing/overflow/tiled-mask-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/measure-forced-svg-text.html b/third_party/blink/web_tests/display-lock/lock-before-append/measure-forced-svg-text.html new file mode 100644 index 0000000..00f2143 --- /dev/null +++ b/third_party/blink/web_tests/display-lock/lock-before-append/measure-forced-svg-text.html
@@ -0,0 +1,68 @@ +<!doctype HTML> + +<!-- +Runs an acquire, which constructs and measures a subtree. +This should do a forced layout. Commit also measures it to get +post-final-layout values. +--> + +<style> +#container { + contain: style layout; + width: 100px; + height: 100px; + background: lightgreen; +} +</style> + +<div id="parent"></div> + +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> + +<script> +async_test((t) => { + function measureForced() { + t.step(() => { + assert_not_equals(document.getElementById("text").getComputedTextLength(), 0, "forced"); + }); + } + + function measureInCommit() { + t.step(() => { + assert_not_equals(document.getElementById("text").getComputedTextLength(), 0, "in commit"); + }); + } + + function construct(container) { + container.innerHTML = ` + <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> + <style> + .t { font: 10px sans-serif; } + </style> + <text id="text" x="10" y="10" class="t">This is text</text> + </svg> + `; + } + + async function runTest() { + let container = document.createElement("div"); + container.id = "container"; + await container.displayLock.acquire({ timeout: Infinity }); + + document.getElementById("parent").appendChild(container); + construct(container); + measureForced(); + + container.displayLock.commit().then(() => { + measureInCommit(); + t.done(); + }); + } + + window.onload = function() { + requestAnimationFrame(() => requestAnimationFrame(runTest)); + }; +}, "Measure Forced SVG Text"); +</script> +
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-disabled-image-tentative.sub.html b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-disabled-image-tentative.sub.html index 0d46d2f..5bd9fca 100644 --- a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-disabled-image-tentative.sub.html +++ b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-disabled-image-tentative.sub.html
@@ -21,7 +21,7 @@ #image-container { position: absolute; - top: 400%; + top: 10000px; } </style> <body> @@ -58,4 +58,4 @@ await img_auto.load_complete; }, "Sanity-check: Verify that all images load after they are scrolled into view."); </script> -</body> \ No newline at end of file +</body>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-disabled-tentative.sub.html b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-disabled-tentative.sub.html index 772998a..b6a5d93 100644 --- a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-disabled-tentative.sub.html +++ b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-disabled-tentative.sub.html
@@ -16,7 +16,7 @@ .spacer { width: 100%; - height: 300%; + height: 10000px; } </style> <div class="spacer"></div>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-enabled-image-tentative.sub.html b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-enabled-image-tentative.sub.html index 2dc6a6cc..7a564be 100644 --- a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-enabled-image-tentative.sub.html +++ b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-enabled-image-tentative.sub.html
@@ -20,7 +20,7 @@ #image-container { position: absolute; - top: 400%; + top: 10000px; } </style> <body> @@ -42,4 +42,4 @@ assert_true(img.did_load, "Image should have loaded."); }, "When feature is enabled, lazyload=OFF works as expected."); </script> -</body> \ No newline at end of file +</body>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-enabled-tentative.sub.html b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-enabled-tentative.sub.html index 058b025..ca040b0 100644 --- a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-enabled-tentative.sub.html +++ b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-enabled-tentative.sub.html
@@ -16,7 +16,7 @@ .spacer { width: 100%; - height: 300%; + height: 10000px; } </style> <div class="spacer"></div>
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-image-attribute-on-sanity-check-tentative.sub.html b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-image-attribute-on-sanity-check-tentative.sub.html index 6111e69..a913158 100644 --- a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-image-attribute-on-sanity-check-tentative.sub.html +++ b/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/lazyload/lazyload-image-attribute-on-sanity-check-tentative.sub.html
@@ -19,7 +19,7 @@ #image-container { position: absolute; - top: 400%; + top: 10000px; } </style> <body> @@ -45,4 +45,4 @@ }, "Verify 'lazyload' attribute state 'on' works as expected: image loads only when in " + "viewport."); </script> -</body> \ No newline at end of file +</body>
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/request-headers.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/update-no-cache-request-headers.https.html similarity index 100% rename from third_party/blink/web_tests/external/wpt/service-workers/service-worker/request-headers.https.html rename to third_party/blink/web_tests/external/wpt/service-workers/service-worker/update-no-cache-request-headers.https.html
diff --git a/third_party/crc32c/README.chromium b/third_party/crc32c/README.chromium index 2da55b3..4c1704e 100644 --- a/third_party/crc32c/README.chromium +++ b/third_party/crc32c/README.chromium
@@ -1,7 +1,7 @@ Name: CRC32C Short Name: crc32c URL: https://github.com/google/crc32c -Version: 1.0.4 +Version: 1.0.7.git-5998f8451548244de8cde7fab387a550e7c4497d License: New BSD License File: src/LICENSE Security Critical: yes
diff --git a/third_party/dav1d/dav1d_generated.gni b/third_party/dav1d/dav1d_generated.gni index 3c5b4f7..2d8342f 100644 --- a/third_party/dav1d/dav1d_generated.gni +++ b/third_party/dav1d/dav1d_generated.gni
@@ -6,6 +6,7 @@ x86_asm_sources = [ "libdav1d/src/x86/cdef.asm", + "libdav1d/src/x86/cdef_ssse3.asm", "libdav1d/src/x86/cpuid.asm", "libdav1d/src/x86/ipred.asm", "libdav1d/src/x86/ipred_ssse3.asm",
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium index fb242411e..a40413b 100644 --- a/third_party/harfbuzz-ng/README.chromium +++ b/third_party/harfbuzz-ng/README.chromium
@@ -1,9 +1,9 @@ Name: harfbuzz-ng Short Name: harfbuzz-ng URL: http://harfbuzz.org -Version: 2.3.0-181 -Date: 20190129 -Revision: fe532923101586e316b300d419a337d357cd93da +Version: 2.3.1-79 +Date: 20190301 +Revision: 4f37ab63de9705d7bf74ee75364747e41b7c06a1 Security Critical: yes License: MIT License File: src/COPYING
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids index 27903df..e823c9b9e 100644 --- a/tools/gritsettings/resource_ids +++ b/tools/gritsettings/resource_ids
@@ -94,7 +94,7 @@ "chrome/browser/resources/invalidations_resources.grd": { "includes": [12400], }, - "chrome/browser/resources/local_ntp_resources.grd": { + "chrome/browser/resources/local_ntp/local_ntp_resources.grd": { "includes": [12440], }, "chrome/browser/resources/md_extensions/extensions_resources_vulcanized.grd": { @@ -103,7 +103,7 @@ "chrome/browser/resources/md_extensions/extensions_resources.grd": { "structures": [12510], }, - "chrome/browser/resources/net_internals_resources.grd": { + "chrome/browser/resources/net_internals/net_internals_resources.grd": { "includes": [12600], }, "chrome/browser/resources/print_preview/print_preview_resources_vulcanized.grd": { @@ -112,7 +112,7 @@ "chrome/browser/resources/print_preview/print_preview_resources.grd": { "structures": [12620], }, - "chrome/browser/resources/quota_internals_resources.grd": { + "chrome/browser/resources/quota_internals/quota_internals_resources.grd": { "includes": [12810], }, "chrome/browser/resources/settings/settings_resources_vulcanized.grd": { @@ -121,13 +121,13 @@ "chrome/browser/resources/settings/settings_resources.grd": { "structures": [12840], }, - "chrome/browser/resources/sync_file_system_internals_resources.grd": { + "chrome/browser/resources/sync_file_system_internals/sync_file_system_internals_resources.grd": { "includes": [13340], }, - "chrome/browser/resources/translate_internals_resources.grd": { + "chrome/browser/resources/translate_internals/translate_internals_resources.grd": { "includes": [13370], }, - "chrome/browser/resources/webapks_ui_resources.grd": { + "chrome/browser/resources/webapks/webapks_ui_resources.grd": { "includes": [13380], }, "chrome/browser/resources/welcome/onboarding_welcome/onboarding_welcome_resources.grd": {
diff --git a/tools/imagediff/BUILD.gn b/tools/imagediff/BUILD.gn index 107f6a63..ae60516 100644 --- a/tools/imagediff/BUILD.gn +++ b/tools/imagediff/BUILD.gn
@@ -20,7 +20,7 @@ # not provide it. Instead, determine it here. if (target_os == "win" && host_os != "win") { # When targeting Windows from not Windows, use the test target toolchain. - image_diff_toolchain = default_toolchain + imagediff_toolchain = default_toolchain } else if (target_os != host_os) { # In common cross-compilation scenarios, the build host matches the test host. # TODO: In chrome/android-on-mac cross builds, image_diff would still have to
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 6c45592..d830ded 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -31341,6 +31341,7 @@ <int value="-1396974542" label="UserMediaScreenCapturing:enabled"/> <int value="-1392689905" label="ServiceWorkerLongRunningMessage:enabled"/> <int value="-1392562498" label="disable-origin-chip"/> + <int value="-1391728260" label="ContextualSearchDefinitions:enabled"/> <int value="-1391693054" label="ContentSuggestionsFaviconsFromNewServer:disabled"/> <int value="-1390331361" @@ -32319,6 +32320,7 @@ <int value="270267831" label="enable-scripts-require-action"/> <int value="272631627" label="BookmarkAppsMac:enabled"/> <int value="274103741" label="enable-ntp-popular-sites"/> + <int value="277565405" label="ContextualSearchDefinitions:disabled"/> <int value="278756320" label="disable-app-list-app-info"/> <int value="280644887" label="mash"/> <int value="282636212" label="VideoPlayerNativeControls:disabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 2c80e68..2e9982b 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -17670,7 +17670,8 @@ <owner>fgorski@chromium.org</owner> <owner>skym@chromium.org</owner> <summary> - The amount of time fetching articles takes, regardless of success. + The amount of time fetching articles takes, regardless of success. This + includes the time it takes to get an auth token for signed in users. </summary> </histogram> @@ -17702,6 +17703,15 @@ </summary> </histogram> +<histogram name="ContentSuggestions.Feed.Network.TokenDuration" units="ms" + expires_after="2020-03-01"> + <owner>skym@chromium.org</owner> + <owner>pnoland@chromium.org</owner> + <summary> + The amount of time it takes to get an access token for signed in users. + </summary> +</histogram> + <histogram name="ContentSuggestions.Feed.Network.TokenFetchStatus" enum="GoogleServiceAuthError" expires_after="2019-10-01"> <owner>pnoland@chromium.org</owner>
diff --git a/tools/perf/benchmarks/blink_perf.py b/tools/perf/benchmarks/blink_perf.py index 12f9fedc..c889eb6b 100644 --- a/tools/perf/benchmarks/blink_perf.py +++ b/tools/perf/benchmarks/blink_perf.py
@@ -322,6 +322,7 @@ kwargs['upload_bucket'] = results.telemetry_info.upload_bucket kwargs['cloud_url'] = results.telemetry_info.trace_remote_url trace_value = trace.TraceValue(page, trace_data, **kwargs) + trace_value.SerializeTraceData() results.AddValue(trace_value) trace_events_to_measure = tab.EvaluateJavaScript(
diff --git a/tools/perf/metrics/__init__.py b/tools/perf/metrics/__init__.py index 3c50f3b..b2e7a3ed 100644 --- a/tools/perf/metrics/__init__.py +++ b/tools/perf/metrics/__init__.py
@@ -12,18 +12,6 @@ about one thing. """ - @classmethod - def CustomizeBrowserOptions(cls, options): - """Add browser options that are required by this metric. - - Some metrics do not have any special browser options that need - to be added, and they do not need to override this method; by - default, no browser options are added. - - To add options here, call options.AppendExtraBrowserArgs(arg). - """ - pass - def Start(self, page, tab): """Start collecting data for this metric.""" pass
diff --git a/ui/base/ime/dummy_text_input_client.cc b/ui/base/ime/dummy_text_input_client.cc index 34dfc79..a13d767 100644 --- a/ui/base/ime/dummy_text_input_client.cc +++ b/ui/base/ime/dummy_text_input_client.cc
@@ -4,7 +4,12 @@ #include "ui/base/ime/dummy_text_input_client.h" +#if defined(OS_WIN) +#include <vector> +#endif + #include "base/strings/string_util.h" +#include "build/build_config.h" #include "ui/events/event.h" #include "ui/gfx/geometry/rect.h" @@ -140,4 +145,10 @@ return false; } +#if defined(OS_WIN) +void DummyTextInputClient::SetCompositionFromExistingText( + const gfx::Range& range, + const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) {} +#endif + } // namespace ui
diff --git a/ui/base/ime/dummy_text_input_client.h b/ui/base/ime/dummy_text_input_client.h index e313b035..f6e751d 100644 --- a/ui/base/ime/dummy_text_input_client.h +++ b/ui/base/ime/dummy_text_input_client.h
@@ -9,6 +9,7 @@ #include <stdint.h> #include "base/macros.h" +#include "build/build_config.h" #include "ui/base/ime/text_input_client.h" namespace ui { @@ -55,6 +56,13 @@ ukm::SourceId GetClientSourceForMetrics() const override; bool ShouldDoLearning() override; +#if defined(OS_WIN) + // Overridden from ui::TextInputClient(Windows only): + void SetCompositionFromExistingText( + const gfx::Range& range, + const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) override; +#endif + int insert_char_count() const { return insert_char_count_; } base::char16 last_insert_char() const { return last_insert_char_; } const std::vector<base::string16>& insert_text_history() const {
diff --git a/ui/base/ime/input_method_win_tsf.cc b/ui/base/ime/input_method_win_tsf.cc index 0bc1842..99394a0 100644 --- a/ui/base/ime/input_method_win_tsf.cc +++ b/ui/base/ime/input_method_win_tsf.cc
@@ -41,10 +41,13 @@ void InputMethodWinTSF::OnFocus() { tsf_event_router_->SetManager( ui::TSFBridge::GetInstance()->GetThreadManager().Get()); + ui::TSFBridge::GetInstance()->SetInputMethodDelegate( + InputMethodBase::delegate()); } void InputMethodWinTSF::OnBlur() { tsf_event_router_->SetManager(nullptr); + ui::TSFBridge::GetInstance()->RemoveInputMethodDelegate(); } bool InputMethodWinTSF::OnUntranslatedIMEMessage(
diff --git a/ui/base/ime/text_input_client.h b/ui/base/ime/text_input_client.h index 72090841..1bbffb7 100644 --- a/ui/base/ime/text_input_client.h +++ b/ui/base/ime/text_input_client.h
@@ -8,8 +8,13 @@ #include <stddef.h> #include <stdint.h> +#if defined(OS_WIN) +#include <vector> +#endif + #include "base/i18n/rtl.h" #include "base/strings/string16.h" +#include "build/build_config.h" #include "services/metrics/public/cpp/ukm_source_id.h" #include "ui/base/ime/composition_text.h" #include "ui/base/ime/text_input_mode.h" @@ -198,6 +203,15 @@ // improve typing suggestions for the user. This should return false for text // fields that are considered 'private' (e.g. in incognito tabs). virtual bool ShouldDoLearning() = 0; + +#if defined(OS_WIN) + // Start composition over a given UTF-16 code range from existing text. This + // should only be used for composition scenario when IME wants to start + // composition on existing text. + virtual void SetCompositionFromExistingText( + const gfx::Range& range, + const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) = 0; +#endif }; } // namespace ui
diff --git a/ui/base/ime/win/mock_tsf_bridge.cc b/ui/base/ime/win/mock_tsf_bridge.cc index a69f3760..d634989 100644 --- a/ui/base/ime/win/mock_tsf_bridge.cc +++ b/ui/base/ime/win/mock_tsf_bridge.cc
@@ -45,6 +45,15 @@ focused_window_ = nullptr; } +void MockTSFBridge::SetInputMethodDelegate( + internal::InputMethodDelegate* delegate) { + input_method_delegate_ = delegate; +} + +void MockTSFBridge::RemoveInputMethodDelegate() { + input_method_delegate_ = nullptr; +} + Microsoft::WRL::ComPtr<ITfThreadMgr> MockTSFBridge::GetThreadManager() { return thread_manager_; }
diff --git a/ui/base/ime/win/mock_tsf_bridge.h b/ui/base/ime/win/mock_tsf_bridge.h index 5baca1a..4c71fa1 100644 --- a/ui/base/ime/win/mock_tsf_bridge.h +++ b/ui/base/ime/win/mock_tsf_bridge.h
@@ -9,6 +9,7 @@ #include <wrl/client.h> #include "base/compiler_specific.h" +#include "ui/base/ime/input_method_delegate.h" #include "ui/base/ime/text_input_type.h" #include "ui/base/ime/win/tsf_bridge.h" @@ -26,6 +27,8 @@ void OnTextLayoutChanged() override; void SetFocusedClient(HWND focused_window, TextInputClient* client) override; void RemoveFocusedClient(TextInputClient* client) override; + void SetInputMethodDelegate(internal::InputMethodDelegate* delegate) override; + void RemoveInputMethodDelegate() override; Microsoft::WRL::ComPtr<ITfThreadMgr> GetThreadManager() override; TextInputClient* GetFocusedTextInputClient() const override; @@ -87,6 +90,7 @@ unsigned set_focused_client_call_count_ = 0; unsigned remove_focused_client_call_count_ = 0; TextInputClient* text_input_client_ = nullptr; + internal::InputMethodDelegate* input_method_delegate_ = nullptr; HWND focused_window_ = nullptr; TextInputType latest_text_input_type_ = TEXT_INPUT_TYPE_NONE; Microsoft::WRL::ComPtr<ITfThreadMgr> thread_manager_;
diff --git a/ui/base/ime/win/tsf_bridge.cc b/ui/base/ime/win/tsf_bridge.cc index f3f094c..fa78d5c9 100644 --- a/ui/base/ime/win/tsf_bridge.cc +++ b/ui/base/ime/win/tsf_bridge.cc
@@ -13,6 +13,7 @@ #include "base/stl_util.h" #include "base/threading/thread_local_storage.h" #include "base/win/scoped_variant.h" +#include "ui/base/ime/input_method_delegate.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/ime/win/tsf_bridge.h" #include "ui/base/ime/win/tsf_text_store.h" @@ -39,6 +40,8 @@ bool ConfirmComposition() override; void SetFocusedClient(HWND focused_window, TextInputClient* client) override; void RemoveFocusedClient(TextInputClient* client) override; + void SetInputMethodDelegate(internal::InputMethodDelegate* delegate) override; + void RemoveInputMethodDelegate() override; Microsoft::WRL::ComPtr<ITfThreadMgr> GetThreadManager() override; TextInputClient* GetFocusedTextInputClient() const override; @@ -126,6 +129,9 @@ // Represents the window that is currently owns text input focus. HWND attached_window_handle_ = nullptr; + // Handle to ITfKeyTraceEventSink. + DWORD key_trace_sink_cookie_ = 0; + DISALLOW_COPY_AND_ASSIGN(TSFBridgeImpl); }; @@ -135,6 +141,14 @@ DCHECK(base::MessageLoopCurrentForUI::IsSet()); if (!IsInitialized()) return; + + if (thread_manager_ != nullptr) { + Microsoft::WRL::ComPtr<ITfSource> source; + if (SUCCEEDED(thread_manager_->QueryInterface(IID_PPV_ARGS(&source)))) { + source->UnadviseSink(key_trace_sink_cookie_); + } + } + for (TSFDocumentMap::iterator it = tsf_document_map_.begin(); it != tsf_document_map_.end(); ++it) { Microsoft::WRL::ComPtr<ITfContext> context; @@ -292,6 +306,32 @@ } } +void TSFBridgeImpl::SetInputMethodDelegate( + internal::InputMethodDelegate* delegate) { + DCHECK(base::MessageLoopCurrentForUI::IsSet()); + DCHECK(delegate); + DCHECK(IsInitialized()); + + for (TSFDocumentMap::iterator it = tsf_document_map_.begin(); + it != tsf_document_map_.end(); ++it) { + if (it->second.text_store.get() == nullptr) + continue; + it->second.text_store->SetInputMethodDelegate(delegate); + } +} + +void TSFBridgeImpl::RemoveInputMethodDelegate() { + DCHECK(base::MessageLoopCurrentForUI::IsSet()); + DCHECK(IsInitialized()); + + for (TSFDocumentMap::iterator it = tsf_document_map_.begin(); + it != tsf_document_map_.end(); ++it) { + if (it->second.text_store.get() == nullptr) + continue; + it->second.text_store->RemoveInputMethodDelegate(); + } +} + TextInputClient* TSFBridgeImpl::GetFocusedTextInputClient() const { return client_; } @@ -311,6 +351,9 @@ return false; } + if (!text_store || !source_cookie) + return true; + DWORD edit_cookie = TF_INVALID_EDIT_COOKIE; if (FAILED((*document_manager) ->CreateContext(client_id_, 0, @@ -325,9 +368,6 @@ return false; } - if (!text_store || !source_cookie) - return true; - Microsoft::WRL::ComPtr<ITfSource> source; if (FAILED((*context)->QueryInterface(IID_PPV_ARGS(&source)))) { DVLOG(1) << "Failed to get source."; @@ -341,6 +381,21 @@ return false; } + Microsoft::WRL::ComPtr<ITfSource> source_ITfThreadMgr; + if (FAILED(thread_manager_->QueryInterface( + IID_PPV_ARGS(&source_ITfThreadMgr)))) { + DVLOG(1) << "Failed to get source_ITfThreadMgr."; + return false; + } + + if (FAILED(source_ITfThreadMgr->AdviseSink( + IID_ITfKeyTraceEventSink, + static_cast<ITfKeyTraceEventSink*>(text_store), + &key_trace_sink_cookie_))) { + DVLOG(1) << "AdviseSink for ITfKeyTraceEventSink failed."; + return false; + } + if (*source_cookie == TF_INVALID_COOKIE) { DVLOG(1) << "The result of cookie is invalid."; return false; @@ -368,9 +423,8 @@ document_manager.GetAddressOf(), context.GetAddressOf(), cookie_ptr)) return false; - const bool use_disabled_context = (input_type == TEXT_INPUT_TYPE_PASSWORD || - input_type == TEXT_INPUT_TYPE_NONE); - if (use_disabled_context && !InitializeDisabledContext(context.Get())) + if ((input_type == TEXT_INPUT_TYPE_PASSWORD) && + !InitializeDisabledContext(context.Get())) return false; tsf_document_map_[input_type].text_store = text_store; tsf_document_map_[input_type].document_manager = document_manager; @@ -419,6 +473,10 @@ } bool TSFBridgeImpl::IsFocused(ITfDocumentMgr* document_manager) { + if (!IsInitialized()) { + // Hasn't been initialized yet. Return false. + return false; + } Microsoft::WRL::ComPtr<ITfDocumentMgr> focused_document_manager; if (FAILED( thread_manager_->GetFocus(focused_document_manager.GetAddressOf()))) @@ -431,6 +489,10 @@ } void TSFBridgeImpl::UpdateAssociateFocus() { + if (!IsInitialized()) { + // Hasn't been initialized yet. Do nothing. + return; + } if (attached_window_handle_ == nullptr) return; TSFDocument* document = GetAssociatedDocument(); @@ -450,6 +512,10 @@ } void TSFBridgeImpl::ClearAssociateFocus() { + if (!IsInitialized()) { + // Hasn't been initialized yet. Do nothing. + return; + } if (attached_window_handle_ == nullptr) return; Microsoft::WRL::ComPtr<ITfDocumentMgr> previous_focus;
diff --git a/ui/base/ime/win/tsf_bridge.h b/ui/base/ime/win/tsf_bridge.h index 0d24edd..541264d0 100644 --- a/ui/base/ime/win/tsf_bridge.h +++ b/ui/base/ime/win/tsf_bridge.h
@@ -75,6 +75,13 @@ // Caller must free |client|. virtual void RemoveFocusedClient(TextInputClient* client) = 0; + // Lets TSFTextstore see InputMethodDelegate instance when in focus. + virtual void SetInputMethodDelegate( + internal::InputMethodDelegate* delegate) = 0; + + // Remove InputMethodDelegate instance from TSFTextStore when not in focus. + virtual void RemoveInputMethodDelegate() = 0; + // Obtains current thread manager. virtual Microsoft::WRL::ComPtr<ITfThreadMgr> GetThreadManager() = 0;
diff --git a/ui/base/ime/win/tsf_text_store.cc b/ui/base/ime/win/tsf_text_store.cc index 6fb766de..bc855ee 100644 --- a/ui/base/ime/win/tsf_text_store.cc +++ b/ui/base/ime/win/tsf_text_store.cc
@@ -11,9 +11,12 @@ #include <algorithm> +#include "base/bind_helpers.h" #include "base/win/scoped_variant.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/ime/win/tsf_input_scope.h" +#include "ui/display/win/screen_win.h" +#include "ui/events/event_dispatcher.h" #include "ui/gfx/geometry/rect.h" namespace ui { @@ -60,6 +63,8 @@ *result = static_cast<ITfContextOwnerCompositionSink*>(this); } else if (iid == IID_ITfTextEditSink) { *result = static_cast<ITfTextEditSink*>(this); + } else if (iid == IID_ITfKeyTraceEventSink) { + *result = static_cast<ITfKeyTraceEventSink*>(this); } else { *result = nullptr; return E_NOINTERFACE; @@ -142,7 +147,7 @@ return E_INVALIDARG; if (!HasReadLock()) return TS_E_NOLOCK; - *acp = string_buffer_.size(); + *acp = string_buffer_document_.size(); return S_OK; } @@ -213,10 +218,8 @@ return E_INVALIDARG; status->dwDynamicFlags = 0; - // We use transitory contexts and we don't support hidden text. - // TODO(dtapuska): Remove TS_SS_TRANSITORY it was added to fix - // https://crbug.com/148355 - status->dwStaticFlags = TS_SS_TRANSITORY | TS_SS_NOHIDDENTEXT; + // We don't support hidden text. + status->dwStaticFlags = TS_SS_NOHIDDENTEXT; return S_OK; } @@ -240,7 +243,7 @@ return E_INVALIDARG; if (!HasReadLock()) return TF_E_NOLOCK; - const LONG string_buffer_size = string_buffer_.size(); + const LONG string_buffer_size = string_buffer_document_.size(); if (acp_end == -1) acp_end = string_buffer_size; if (!((0 <= acp_start) && (acp_start <= acp_end) && @@ -251,7 +254,7 @@ *text_buffer_copied = acp_end - acp_start; const base::string16& result = - string_buffer_.substr(acp_start, *text_buffer_copied); + string_buffer_document_.substr(acp_start, *text_buffer_copied); for (size_t i = 0; i < result.size(); ++i) { text_buffer[i] = result[i]; } @@ -279,21 +282,21 @@ return E_INVALIDARG; if (!HasReadLock()) return TS_E_NOLOCK; - if (!((static_cast<LONG>(committed_size_) <= acp_start) && + if (!((static_cast<LONG>(composition_start_) <= acp_start) && (acp_start <= acp_end) && - (acp_end <= static_cast<LONG>(string_buffer_.size())))) { + (acp_end <= static_cast<LONG>(string_buffer_document_.size())))) { return TS_E_INVALIDPOS; } // According to a behavior of notepad.exe and wordpad.exe, top left corner of // rect indicates a first character's one, and bottom right corner of rect // indicates a last character's one. - // We use RECT instead of gfx::Rect since left position may be bigger than - // right position when composition has multiple lines. - RECT result; + // TODO(IME): add tests for scenario that left position is bigger than right + // position. + gfx::Rect result_rect; gfx::Rect tmp_rect; - const uint32_t start_pos = acp_start - committed_size_; - const uint32_t end_pos = acp_end - committed_size_; + const uint32_t start_pos = acp_start - composition_start_; + const uint32_t end_pos = acp_end - composition_start_; if (start_pos == end_pos) { // According to MSDN document, if |acp_start| and |acp_end| are equal it is @@ -304,32 +307,31 @@ if (start_pos == 0) { if (text_input_client_->GetCompositionCharacterBounds(0, &tmp_rect)) { tmp_rect.set_width(0); - result = tmp_rect.ToRECT(); - } else if (string_buffer_.size() == committed_size_) { - result = text_input_client_->GetCaretBounds().ToRECT(); + result_rect = gfx::Rect(tmp_rect); + } else if (string_buffer_document_.size() == composition_start_) { + result_rect = gfx::Rect(text_input_client_->GetCaretBounds()); } else { return TS_E_NOLAYOUT; } } else if (text_input_client_->GetCompositionCharacterBounds(start_pos - 1, &tmp_rect)) { - result.left = tmp_rect.right(); - result.right = tmp_rect.right(); - result.top = tmp_rect.y(); - result.bottom = tmp_rect.bottom(); + tmp_rect.set_x(tmp_rect.right()); + tmp_rect.set_width(0); + result_rect = gfx::Rect(tmp_rect); + } else { return TS_E_NOLAYOUT; } } else { if (text_input_client_->GetCompositionCharacterBounds(start_pos, &tmp_rect)) { - result.left = tmp_rect.x(); - result.top = tmp_rect.y(); - result.right = tmp_rect.right(); - result.bottom = tmp_rect.bottom(); + result_rect = gfx::Rect(tmp_rect); if (text_input_client_->GetCompositionCharacterBounds(end_pos - 1, &tmp_rect)) { - result.right = tmp_rect.right(); - result.bottom = tmp_rect.bottom(); + result_rect.set_width(tmp_rect.x() - result_rect.x() + + tmp_rect.width()); + result_rect.set_height(tmp_rect.y() - result_rect.y() + + tmp_rect.height()); } else { // We may not be able to get the last character bounds, so we use the // first character bounds instead of returning TS_E_NOLAYOUT. @@ -339,14 +341,14 @@ // it's better to return previous caret rectangle instead. // TODO(nona, kinaba): Remove this hack. if (start_pos == 0) { - result = text_input_client_->GetCaretBounds().ToRECT(); + result_rect = gfx::Rect(text_input_client_->GetCaretBounds()); } else { return TS_E_NOLAYOUT; } } } - - *rect = result; + *rect = display::win::ScreenWin::DIPToScreenRect(window_handle_, result_rect) + .ToRECT(); *clipped = FALSE; return S_OK; } @@ -408,9 +410,15 @@ return E_INVALIDARG; DCHECK_LE(start_pos, end_pos); - string_buffer_ = string_buffer_.substr(0, start_pos) + - base::string16(text_buffer, text_buffer + text_buffer_size) + - string_buffer_.substr(end_pos); + string_buffer_document_ = + string_buffer_document_.substr(0, start_pos) + + base::string16(text_buffer, text_buffer + text_buffer_size) + + string_buffer_document_.substr(end_pos); + + // reconstruct string that needs to be inserted. + string_pending_insertion_ = + string_buffer_document_.substr(start_pos, text_buffer_size); + if (acp_start) *acp_start = start_pos; if (acp_end) @@ -432,12 +440,12 @@ LONG* acp_result_end) { if (!acp_result_start || !acp_result_end || acp_test_start > acp_test_end) return E_INVALIDARG; - const LONG committed_size = static_cast<LONG>(committed_size_); - const LONG buffer_size = static_cast<LONG>(string_buffer_.size()); + const LONG composition_start = static_cast<LONG>(composition_start_); + const LONG buffer_size = static_cast<LONG>(string_buffer_document_.size()); *acp_result_start = - std::min(std::max(committed_size, acp_test_start), buffer_size); + std::min(std::max(composition_start, acp_test_start), buffer_size); *acp_result_end = - std::min(std::max(committed_size, acp_test_end), buffer_size); + std::min(std::max(composition_start, acp_test_end), buffer_size); return S_OK; } @@ -475,6 +483,9 @@ } STDMETHODIMP TSFTextStore::RequestLock(DWORD lock_flags, HRESULT* result) { + if (!text_input_client_) + return E_UNEXPECTED; + if (!text_store_acp_sink_.Get()) return E_FAIL; if (!result) @@ -496,7 +507,13 @@ current_lock_type_ = (lock_flags & TS_LF_READWRITE); edit_flag_ = false; - const size_t last_committed_size = committed_size_; + // if there is not already some composition text, they we are about to start + // composition. we need to set last_composition_start to the selection start. + // Otherwise we are updating an existing composition, we should use the cached + // composition_start_ for reference. + const size_t last_composition_start = text_input_client_->HasCompositionText() + ? composition_start_ + : selection_.start(); // Grant the lock. *result = text_store_acp_sink_->OnLockGranted(current_lock_type_); @@ -512,66 +529,85 @@ current_lock_type_ = 0; } + // if nothing has changed from input service, then only need to + // compare our cache with latest textinputstate. if (!edit_flag_) { + CalculateTextandSelectionDiffAndNotifyIfNeeded(); return S_OK; } + if (!text_input_client_) + return E_UNEXPECTED; + + // If string_pending_insertion_ is empty, then there are three cases: + // 1. there is no composition We only need to do comparison between our + // cache and latest textinputstate and send notifications accordingly. + // 2. A new composition is about to start on existing text. We need to start + // composition on range from composition_range_. + // 3. There is composition. User cancels the composition by deleting all of + // the composing text, we need to reset the composition_start_ and call + // into blink to complete the existing composition(later in this method). + if (string_pending_insertion_.empty()) { + if (!text_input_client_->HasCompositionText()) { + if (has_composition_range_) { + StartCompositionOnExistingText(); + } else { + composition_start_ = selection_.start(); + CalculateTextandSelectionDiffAndNotifyIfNeeded(); + } + return S_OK; + } else { + composition_start_ = last_composition_start; + } + } + + // If we saved a keydown event before this, now is the right time to fire it + // We should only fire JS key event during composition. + if (has_composition_range_ && wparam_keydown_cached_ != 0 && + lparam_keydown_cached_ != 0) { + DispatchKeyEvent(ui::ET_KEY_PRESSED, wparam_keydown_cached_, + lparam_keydown_cached_); + } + // If the text store is edited in OnLockGranted(), we may need to call // TextInputClient::InsertText() or TextInputClient::SetCompositionText(). - const size_t new_committed_size = committed_size_; - const base::string16& new_committed_string = string_buffer_.substr( - last_committed_size, new_committed_size - last_committed_size); - const base::string16& composition_string = - string_buffer_.substr(new_committed_size); + const size_t new_composition_start = composition_start_; - // If there is new committed string, calls TextInputClient::InsertText(). - if ((!new_committed_string.empty()) && text_input_client_) { - text_input_client_->InsertText(new_committed_string); + // If new_composition_start is not equal to last_composition_start, + // then we know that there are some committed text. we need to call + // TextInputClient::InsertText to complete the current composition. When there + // are some committed text, it is not necessarily true that composition_string + // is empty. We need to complete current composition with committed text and + // start new composition with composition_string. + if ((new_composition_start != last_composition_start) && text_input_client_) { + CommitTextAndEndCompositionIfAny(last_composition_start, + new_composition_start); } - // Calls TextInputClient::SetCompositionText(). - CompositionText composition_text; - composition_text.text = composition_string; - composition_text.ime_text_spans = text_spans_; - // Adjusts the offset. - for (size_t i = 0; i < composition_text.ime_text_spans.size(); ++i) { - composition_text.ime_text_spans[i].start_offset -= new_committed_size; - composition_text.ime_text_spans[i].end_offset -= new_committed_size; - } - if (selection_.start() < new_committed_size) { - composition_text.selection.set_start(0); - } else { - composition_text.selection.set_start(selection_.start() - - new_committed_size); - } - if (selection_.end() < new_committed_size) { - composition_text.selection.set_end(0); - } else { - composition_text.selection.set_end(selection_.end() - new_committed_size); - } - if (text_input_client_) - text_input_client_->SetCompositionText(composition_text); + const base::string16& composition_string = string_buffer_document_.substr( + composition_range_.start(), + composition_range_.end() - composition_range_.start()); - // If there is no composition string, clear the text store status. - // And call OnSelectionChange(), OnLayoutChange(), and OnTextChange(). - if ((composition_string.empty()) && (new_committed_size != 0)) { - string_buffer_.clear(); - committed_size_ = 0; - selection_.set_start(0); - selection_.set_end(0); - if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) - text_store_acp_sink_->OnSelectionChange(); - if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) - text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); - if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { - TS_TEXTCHANGE textChange; - textChange.acpStart = 0; - textChange.acpOldEnd = new_committed_size; - textChange.acpNewEnd = 0; - text_store_acp_sink_->OnTextChange(0, &textChange); - } + // Only need to set composition if the current composition string + // (composition_string) is not the same as previous composition string + // (prev_composition_string_) during same composition. + // If composition_string is empty and there is an existing composition going + // on, we still need to call into blink to complete the composition. + if (!previous_composition_string_._Equal(composition_string) || + (text_input_client_->HasCompositionText() && + composition_string.empty())) { + previous_composition_string_ = composition_string; + + StartCompositionOnNewText(new_composition_start, composition_string); } + // reset string_buffer_ if composition is no longer active. + if (!text_input_client_->HasCompositionText()) { + string_pending_insertion_.clear(); + } + + CalculateTextandSelectionDiffAndNotifyIfNeeded(); + return S_OK; } @@ -625,9 +661,8 @@ if (selection_buffer_size > 0) { const LONG start_pos = selection_buffer[0].acpStart; const LONG end_pos = selection_buffer[0].acpEnd; - if (!((static_cast<LONG>(committed_size_) <= start_pos) && - (start_pos <= end_pos) && - (end_pos <= static_cast<LONG>(string_buffer_.size())))) { + if (!((start_pos <= end_pos) && + (end_pos <= static_cast<LONG>(string_buffer_document_.size())))) { return TF_E_INVALIDPOS; } selection_.set_start(start_pos); @@ -644,11 +679,6 @@ TS_TEXTCHANGE* text_change) { if (!HasReadWriteLock()) return TS_E_NOLOCK; - if (!((static_cast<LONG>(committed_size_) <= acp_start) && - (acp_start <= acp_end) && - (acp_end <= static_cast<LONG>(string_buffer_.size())))) { - return TS_E_INVALIDPOS; - } TS_SELECTION_ACP selection; selection.acpStart = acp_start; @@ -662,6 +692,13 @@ return ret; TS_TEXTCHANGE change; + if (text_buffer_size > 0) { + new_text_inserted_ = true; + replace_text_range_.set_start(acp_start); + replace_text_range_.set_end(acp_end); + replace_text_size_ = text_buffer_size; + } + ret = InsertTextAtSelection(0, text_buffer, text_buffer_size, &acp_start, &acp_end, &change); if (ret != S_OK) @@ -700,6 +737,56 @@ return S_OK; } +STDMETHODIMP TSFTextStore::OnKeyTraceDown(WPARAM wParam, LPARAM lParam) { + // fire the event right away if we're in composition + if (has_composition_range_) { + DispatchKeyEvent(ui::ET_KEY_PRESSED, wParam, lParam); + } else { + // we're not in composition but we might be starting it - remember these key + // events to fire when composition starts + wparam_keydown_cached_ = wParam; + lparam_keydown_cached_ = lParam; + } + return S_OK; +} + +STDMETHODIMP TSFTextStore::OnKeyTraceUp(WPARAM wParam, LPARAM lParam) { + if (has_composition_range_ || wparam_keydown_fired_ == wParam) { + DispatchKeyEvent(ui::ET_KEY_RELEASED, wParam, lParam); + } + return S_OK; +} + +void TSFTextStore::DispatchKeyEvent(ui::EventType type, + WPARAM wparam, + LPARAM lparam) { + if (!text_input_client_) + return; + + if (type == ui::ET_KEY_PRESSED) { + // clear the saved values since we just fired a keydown + wparam_keydown_cached_ = 0; + lparam_keydown_cached_ = 0; + wparam_keydown_fired_ = wparam; + } else if (type == ui::ET_KEY_RELEASED) { + // clear the saved values since we just fired a keyup + wparam_keydown_fired_ = 0; + } else { + // shouldn't expect event other than et_key_pressed and et_key_released; + return; + } + + // prepare ui::KeyEvent. + UINT message = type == ui::ET_KEY_PRESSED ? WM_KEYDOWN : WM_KEYUP; + const MSG key_event_MSG = {window_handle_, message, VK_PROCESSKEY, lparam}; + ui::KeyEvent key_event = KeyEventFromMSG(key_event_MSG); + + if (input_method_delegate_) { + input_method_delegate_->DispatchKeyEventPostIME(&key_event, + base::NullCallback()); + } +} + STDMETHODIMP TSFTextStore::OnEndEdit(ITfContext* context, TfEditCookie read_only_edit_cookie, ITfEditRecord* edit_record) { @@ -713,8 +800,50 @@ return S_OK; } text_spans_ = spans; - committed_size_ = committed_size; edit_flag_ = true; + + // This function is guaranteed to be called after each keystroke during + // composition Therefore we can use this function to update composition status + // after each keystroke. If there is existing composition range, we can cache + // the composition range and set composition start position as the start of + // composition range. If there is no existing composition range, then we know + // that there is no active composition, we then need to reset the cached + // composition range and set the new composition start as the current + // selection start. + DCHECK(context); + Microsoft::WRL::ComPtr<ITfContextComposition> context_composition; + if (SUCCEEDED(context->QueryInterface(IID_PPV_ARGS(&context_composition)))) { + Microsoft::WRL::ComPtr<IEnumITfCompositionView> enum_composition_view; + if (SUCCEEDED( + context_composition->EnumCompositions(&enum_composition_view))) { + Microsoft::WRL::ComPtr<ITfCompositionView> composition_view; + if (enum_composition_view->Next(1, &composition_view, nullptr) == S_OK) { + Microsoft::WRL::ComPtr<ITfRange> range; + if (SUCCEEDED(composition_view->GetRange(&range))) { + Microsoft::WRL::ComPtr<ITfRangeACP> range_acp; + if (SUCCEEDED(range->QueryInterface(IID_PPV_ARGS(&range_acp)))) { + LONG start = 0; + LONG length = 0; + if (SUCCEEDED(range_acp->GetExtent(&start, &length))) { + composition_start_ = start; + has_composition_range_ = true; + composition_range_.set_start(start); + composition_range_.set_end(start + length); + } + } + } + } else { + composition_start_ = selection_.start(); + if (has_composition_range_) { + has_composition_range_ = false; + composition_range_.set_start(0); + composition_range_.set_end(0); + previous_composition_string_.clear(); + } + } + } + } + return S_OK; } @@ -823,6 +952,98 @@ return true; } +void TSFTextStore::CalculateTextandSelectionDiffAndNotifyIfNeeded() { + if (!text_input_client_) + return; + + gfx::Range latest_buffer_range_from_client; + base::string16 latest_buffer_from_client; + gfx::Range latest_selection_from_client; + + if (text_input_client_->GetTextRange(&latest_buffer_range_from_client) && + text_input_client_->GetTextFromRange(latest_buffer_range_from_client, + &latest_buffer_from_client) && + text_input_client_->GetEditableSelectionRange( + &latest_selection_from_client) && + latest_buffer_range_from_client.Contains(latest_selection_from_client)) { + // if the text and selection from text input client is the same as the text + // and buffer we got last time, either the state hasn't changed since last + // time we synced or the change hasn't completed yet. Either case we don't + // want to update our buffer and selection cache. We also don't notify + // input service about the change. + if (!buffer_from_client_.compare(latest_buffer_from_client) && + selection_from_client_.EqualsIgnoringDirection( + latest_selection_from_client)) { + return; + } + + // update cache value for next comparison. + buffer_from_client_ = latest_buffer_from_client; + selection_from_client_.set_start(latest_selection_from_client.start()); + selection_from_client_.set_end(latest_selection_from_client.end()); + + if (has_composition_range_) { + return; + } + + if (latest_buffer_from_client.compare(string_buffer_document_)) { + TS_TEXTCHANGE text_change = {}; + bool notify_text_change = + (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) != 0; + + // Execute diffing algorithm only if we need to send notification. + if (notify_text_change) { + size_t acp_start = 0; + size_t acp_old_end = string_buffer_document_.size(); + size_t acp_new_end = latest_buffer_from_client.size(); + + // Compare two strings to find first difference. + for (; acp_start < std::min(latest_buffer_from_client.size(), + string_buffer_document_.size()); + acp_start++) { + if (latest_buffer_from_client.at(acp_start) != + string_buffer_document_.at(acp_start)) { + break; + } + } + + // if two strings have same length, find last difference. + if (latest_buffer_from_client.size() == + string_buffer_document_.size()) { + for (acp_new_end = latest_buffer_from_client.size() - 1; + acp_new_end > acp_start; acp_new_end--) { + if (latest_buffer_from_client.at(acp_new_end) != + string_buffer_document_.at(acp_new_end)) { + break; + } + } + acp_new_end = acp_new_end + 1; + acp_old_end = acp_new_end; + } + + text_change.acpStart = acp_start; + text_change.acpOldEnd = acp_old_end; + text_change.acpNewEnd = acp_new_end; + } + + string_buffer_document_ = latest_buffer_from_client; + + if (notify_text_change) { + text_store_acp_sink_->OnTextChange(0, &text_change); + } + } + + if (!selection_.EqualsIgnoringDirection(latest_selection_from_client)) { + selection_.set_start(latest_selection_from_client.GetMin()); + selection_.set_end(latest_selection_from_client.GetMax()); + + if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) { + text_store_acp_sink_->OnSelectionChange(); + } + } + } +} + void TSFTextStore::SetFocusedTextInputClient( HWND focused_window, TextInputClient* text_input_client) { @@ -838,12 +1059,21 @@ } } +void TSFTextStore::SetInputMethodDelegate( + internal::InputMethodDelegate* delegate) { + input_method_delegate_ = delegate; +} + +void TSFTextStore::RemoveInputMethodDelegate() { + input_method_delegate_ = nullptr; +} + bool TSFTextStore::CancelComposition() { // If there is an on-going document lock, we must not edit the text. if (edit_flag_) return false; - if (string_buffer_.empty()) + if (string_pending_insertion_.empty()) return true; // Unlike ImmNotifyIME(NI_COMPOSITIONSTR, CPS_CANCEL, 0) in IMM32, TSF does @@ -854,11 +1084,10 @@ // we use the same operation to cancel composition here to minimize the risk // of potential compatibility issues. - const size_t previous_buffer_size = string_buffer_.size(); - string_buffer_.clear(); - committed_size_ = 0; - selection_.set_start(0); - selection_.set_end(0); + previous_composition_string_.clear(); + const size_t previous_buffer_size = string_pending_insertion_.size(); + string_pending_insertion_.clear(); + composition_start_ = selection_.start(); if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) text_store_acp_sink_->OnSelectionChange(); if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) @@ -878,23 +1107,25 @@ if (edit_flag_) return false; - if (string_buffer_.empty()) + if (string_pending_insertion_.empty()) return true; + if (!text_input_client_) + return false; + // See the comment in TSFTextStore::CancelComposition. // This logic is based on the observation about how to emulate // ImmNotifyIME(NI_COMPOSITIONSTR, CPS_COMPLETE, 0) by CUAS. const base::string16& composition_text = - string_buffer_.substr(committed_size_); + string_buffer_document_.substr(composition_start_); if (!composition_text.empty()) text_input_client_->InsertText(composition_text); - const size_t previous_buffer_size = string_buffer_.size(); - string_buffer_.clear(); - committed_size_ = 0; - selection_.set_start(0); - selection_.set_end(0); + previous_composition_string_.clear(); + const size_t previous_buffer_size = string_pending_insertion_.size(); + string_pending_insertion_.clear(); + composition_start_ = selection_.start(); if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) text_store_acp_sink_->OnSelectionChange(); if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) @@ -910,6 +1141,7 @@ } void TSFTextStore::SendOnLayoutChange() { + CalculateTextandSelectionDiffAndNotifyIfNeeded(); if (text_store_acp_sink_ && (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)) text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); } @@ -922,4 +1154,85 @@ return (current_lock_type_ & TS_LF_READWRITE) == TS_LF_READWRITE; } +void TSFTextStore::StartCompositionOnExistingText() const { + ui::ImeTextSpans text_spans = text_spans_; + // Adjusts the offset. + for (size_t i = 0; i < text_spans.size(); ++i) { + text_spans[i].start_offset -= composition_start_; + text_spans[i].end_offset -= composition_start_; + } + + text_input_client_->SetCompositionFromExistingText(composition_range_, + text_spans); +} + +void TSFTextStore::CommitTextAndEndCompositionIfAny(size_t old_size, + size_t new_size) const { + if (new_text_inserted_ && + (replace_text_range_.start() != replace_text_range_.end()) && + !text_input_client_->HasCompositionText()) { + // This is a special case to handle text replacement scenarios during + // English typing when we are trying to replace an existing text with some + // new text. + size_t new_text_size; + if (new_size == replace_text_range_.start()) { + // This usually happens when TSF is trying to replace a part of a string + // from the selection end + new_text_size = new_size; + } else { + new_text_size = new_size - replace_text_range_.start(); + } + const base::string16& new_committed_string = string_buffer_document_.substr( + replace_text_range_.start(), new_text_size); + text_input_client_->ExtendSelectionAndDelete( + replace_text_range_.end() - replace_text_range_.start(), 0); + text_input_client_->InsertText(new_committed_string); + } else { + // Construct string to be committed. + size_t new_committed_string_offset = old_size; + size_t new_committed_string_size = new_size - old_size; + // This is a special case. if we are replacing existing text, then + // commit the new text. + if (new_text_inserted_ && + (replace_text_range_.start() != replace_text_range_.end())) { + new_committed_string_offset = replace_text_range_.start(); + new_committed_string_size = replace_text_size_; + } + const base::string16& new_committed_string = string_buffer_document_.substr( + new_committed_string_offset, new_committed_string_size); + text_input_client_->InsertText(new_committed_string); + text_input_client_->SetEditableSelectionRange(selection_); + } +} + +void TSFTextStore::StartCompositionOnNewText( + size_t start_offset, + const base::string16& composition_string) { + CompositionText composition_text; + composition_text.text = composition_string; + composition_text.ime_text_spans = text_spans_; + + for (size_t i = 0; i < composition_text.ime_text_spans.size(); ++i) { + composition_text.ime_text_spans[i].start_offset -= start_offset; + composition_text.ime_text_spans[i].end_offset -= start_offset; + } + + if (selection_.start() < start_offset) { + composition_text.selection.set_start(0); + } else { + composition_text.selection.set_start(selection_.start() - start_offset); + } + + if (selection_.end() < start_offset) { + composition_text.selection.set_end(0); + } else { + composition_text.selection.set_end(selection_.end() - start_offset); + } + + if (text_input_client_) { + new_text_inserted_ = false; + text_input_client_->SetCompositionText(composition_text); + } +} + } // namespace ui
diff --git a/ui/base/ime/win/tsf_text_store.h b/ui/base/ime/win/tsf_text_store.h index 687ca8f6..eb86030 100644 --- a/ui/base/ime/win/tsf_text_store.h +++ b/ui/base/ime/win/tsf_text_store.h
@@ -13,7 +13,9 @@ #include "base/macros.h" #include "base/strings/string16.h" #include "ui/base/ime/ime_text_span.h" +#include "ui/base/ime/input_method_delegate.h" #include "ui/base/ime/ui_base_ime_export.h" +#include "ui/events/event_utils.h" #include "ui/gfx/range/range.h" namespace ui { @@ -24,10 +26,10 @@ // ITextStoreACP interface methods such as SetText(). // When the input method updates the composition, TSFTextStore calls // TextInputClient::SetCompositionText(). And when the input method finishes the -// composition, TSFTextStore calls TextInputClient::InsertText() and clears the -// buffer. +// composition, TSFTextStore calls TextInputClient::InsertText(). // // How TSFTextStore works: +// - Assume the document is empty and in focus. // - The user enters "a". // - The input method set composition as "a". // - TSF manager calls TSFTextStore::RequestLock(). @@ -35,37 +37,53 @@ // - In OnLockGranted(), TSF manager calls // - TSFTextStore::OnStartComposition() // - TSFTextStore::SetText() -// The string buffer is set as "a". +// The pending string buffer is set as "a". +// The document whole buffer is set as "a". // - TSFTextStore::OnUpdateComposition() // - TSFTextStore::OnEndEdit() // TSFTextStore can get the composition information such as underlines. // - TSFTextStore calls TextInputClient::SetCompositionText(). // "a" is shown with an underline as composition string. -// - The user enters <space>. -// - The input method set composition as "A". +// - The user enters 'b'. +// - The input method set composition as "ab". // - TSF manager calls TSFTextStore::RequestLock(). // - TSFTextStore callbacks ITextStoreACPSink::OnLockGranted(). // - In OnLockGranted(), TSF manager calls // - TSFTextStore::SetText() -// The string buffer is set as "A". +// The pending string buffer is set as "b". +// The document whole buffer is changed to "ab". // - TSFTextStore::OnUpdateComposition() // - TSFTextStore::OnEndEdit() // - TSFTextStore calls TextInputClient::SetCompositionText(). -// "A" is shown with an underline as composition string. +// "ab" is shown with an underline as composition string. +// - The user enters <space>. +// - The input method set composition as "aB". +// - TSF manager calls TSFTextStore::RequestLock(). +// - TSFTextStore callbacks ITextStoreACPSink::OnLockGranted(). +// - In OnLockGranted(), TSF manager calls +// - TSFTextStore::SetText() +// The pending string buffer is set as "B". +// The document whole buffer is changed to "aB". +// - TSFTextStore::OnUpdateComposition() +// - TSFTextStore::OnEndEdit() +// - TSFTextStore calls TextInputClient::SetCompositionText(). +// "aB" is shown with an underline as composition string. // - The user enters <enter>. -// - The input method commits "A". +// - The input method commits "aB". // - TSF manager calls TSFTextStore::RequestLock(). // - TSFTextStore callbacks ITextStoreACPSink::OnLockGranted(). // - In OnLockGranted(), TSF manager calls // - TSFTextStore::OnEndComposition() // - TSFTextStore::OnEndEdit() -// TSFTextStore knows "A" is committed. +// TSFTextStore knows "aB" is committed. // - TSFTextStore calls TextInputClient::InsertText(). -// "A" is shown as committed string. -// - TSFTextStore clears the string buffer. -// - TSFTextStore calls OnSelectionChange(), OnLayoutChange() and +// "aB" is shown as committed string. +// - TSFTextStore clears the pending string buffer. +// - TSFTextStore verified if the document whole buffer is the same as the +// buffer returned from TextInputClient. If the buffer is different, then +// call OnSelectionChange(), OnLayoutChange() and // OnTextChange() of ITextStoreACPSink to let TSF manager know that the -// string buffer has been changed. +// string buffer has been changed other than IME. // // About the locking scheme: // When TSF manager manipulates the string buffer it calls RequestLock() to get @@ -83,6 +101,7 @@ // http://msdn.microsoft.com/en-us/library/ms629032 class UI_BASE_IME_EXPORT TSFTextStore : public ITextStoreACP, public ITfContextOwnerCompositionSink, + public ITfKeyTraceEventSink, public ITfTextEditSink { public: TSFTextStore(); @@ -208,12 +227,24 @@ TfEditCookie read_only_edit_cookie, ITfEditRecord* edit_record) override; + // ITfKeyTraceEventSink + STDMETHOD(OnKeyTraceDown) + (WPARAM wParam, LPARAM lParam) override; + STDMETHOD(OnKeyTraceUp) + (WPARAM wParam, LPARAM lParam) override; + // Sets currently focused TextInputClient. void SetFocusedTextInputClient(HWND focused_window, TextInputClient* text_input_client); // Removes currently focused TextInputClient. void RemoveFocusedTextInputClient(TextInputClient* text_input_client); + // Sets InputMethodDelegate pointer. + void SetInputMethodDelegate(internal::InputMethodDelegate* delegate); + + // Removes InputMethodDelegate pointer. + void RemoveInputMethodDelegate(); + // Cancels the ongoing composition if exists. bool CancelComposition(); @@ -227,6 +258,26 @@ friend class TSFTextStoreTest; friend class TSFTextStoreTestCallback; + // Compare our cached text buffer and selection with the up-to-date + // text buffer and selection from TextInputClient. We also update + // cached text buffer and selection with the new version. Then notify + // input service about the change. + void CalculateTextandSelectionDiffAndNotifyIfNeeded(); + + // Synthesize keyevent and send to text input client to fire corresponding + // javascript keyevent during composition. + void DispatchKeyEvent(ui::EventType type, WPARAM wparam, LPARAM lparam); + + // Start new composition on existing text. + void StartCompositionOnExistingText() const; + + // Start new composition with new text. + void StartCompositionOnNewText(size_t start_offset, + const base::string16& composition_string); + + // Commit and insert text into TextInputClient. End any ongoing composition. + void CommitTextAndEndCompositionIfAny(size_t old_size, size_t new_size) const; + // Checks if the document has a read-only lock. bool HasReadLock() const; @@ -258,27 +309,75 @@ // Current TextInputClient which is set in SetFocusedTextInputClient. TextInputClient* text_input_client_ = nullptr; - // TODO(dtapuska): determine if we can expose more the entire document - // more than the committed string and composition string to the TIP. - // |string_buffer_| contains committed string and composition string. + // InputMethodDelegate instance which is used dispatch key events. + internal::InputMethodDelegate* input_method_delegate_ = nullptr; + + // |string_buffer_document_| contains all string in current active view. + // |string_pending_insertion_| contains only string in current edit session. + // |composition_start_| indicates the location for a composition to start at. + // |has_composition_range_| indicates the state of composition. + // |composition_range_| indicates the range of composition if any. // Example: "aoi" is committed, and "umi" is under composition. - // |string_buffer_|: "aoiumi" - // |committed_size_|: 3 - base::string16 string_buffer_; - size_t committed_size_ = 0; + // In current edit session, user press "i" on keyboard. + // |string_buffer_document_|: "aoiumi" + // |string_pending_insertion_| : "i" + // |composition_start_|: 3 + // |has_composition_range_| = true; + // |composition_range_start_| = 3; + // |composition_range_end_| = 6; + base::string16 string_buffer_document_; + base::string16 string_pending_insertion_; + size_t composition_start_ = 0; + bool has_composition_range_ = false; + gfx::Range composition_range_; + + // |previous_composition_string_| indicicates composition string in last + // edit session during same composition. If RequestLock() is called during two + // edit sessions, we don't want to set same composition string twice. + base::string16 previous_composition_string_; + + // |new_text_inserted_| indicates there is text to be inserted + // into blink during ITextStoreACP::SetText(). + // |replace_text_range_| indicates the start and end offsets of the text to be + // replaced by the new text to be inserted. + // |replace_text_size_| indicates the size of the text to be inserted. + // Example: "k" is going to replace "i" + // |string_buffer_document_|: "aeiou" + // |new_text_inserted_|: true + // |replace_text_range_start_|: 2 + // |replace_text_range_end_|: 3 + // |replace_text_size_|: 1 + bool new_text_inserted_ = false; + gfx::Range replace_text_range_; + size_t replace_text_size_; + + // |buffer_from_client_| contains all string returned from + // TextInputClient::GetTextFromRange(); + base::string16 buffer_from_client_; + + // |selection_from_client_| indicates the selection range returned from + // TextInputClient::GetEditableSelectionRange(); + gfx::Range selection_from_client_; + + // |wparam_keydown_cached_| and |lparam_keydown_cached_| contains key event + // info that is used to synthesize key event during composition. + // |wparam_keydown_fired_| indicates if a keydown event has been fired. + WPARAM wparam_keydown_cached_ = 0; + LPARAM lparam_keydown_cached_ = 0; + WPARAM wparam_keydown_fired_ = 0; // |selection_start_| and |selection_end_| indicates the selection range. // Example: "iue" is selected - // |string_buffer_|: "aiueo" + // |string_buffer_document_|: "aiueo" // |selection_.start()|: 1 // |selection_.end()|: 4 gfx::Range selection_; // |start_offset| and |end_offset| of |text_spans_| indicates - // the offsets in |string_buffer_|. + // the offsets in |string_buffer_document_|. // Example: "aoi" is committed. There are two underlines in "umi" and "no". - // |string_buffer_|: "aoiumino" - // |committed_size_|: 3 + // |string_buffer_document_|: "aoiumino" + // |composition_start_|: 3 // text_spans_[0].start_offset: 3 // text_spans_[0].end_offset: 6 // text_spans_[1].start_offset: 6
diff --git a/ui/base/ime/win/tsf_text_store_unittest.cc b/ui/base/ime/win/tsf_text_store_unittest.cc index 4238043..52bc6b8 100644 --- a/ui/base/ime/win/tsf_text_store_unittest.cc +++ b/ui/base/ime/win/tsf_text_store_unittest.cc
@@ -10,14 +10,20 @@ #include <OleCtl.h> #include <wrl/client.h> +#if defined(OS_WIN) +#include <vector> +#endif + #include "base/memory/ref_counted.h" #include "base/stl_util.h" #include "base/win/scoped_com_initializer.h" #include "base/win/scoped_variant.h" +#include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/ime/text_input_client.h" #include "ui/events/event.h" +#include "ui/events/event_dispatcher.h" #include "ui/gfx/geometry/rect.h" using testing::_; @@ -60,6 +66,16 @@ MOCK_CONST_METHOD1(IsTextEditCommandEnabled, bool(TextEditCommand)); MOCK_METHOD1(SetTextEditCommandForNextKeyEvent, void(TextEditCommand)); MOCK_CONST_METHOD0(GetClientSourceForMetrics, ukm::SourceId()); + MOCK_METHOD2(SetCompositionFromExistingText, + void(const gfx::Range&, const std::vector<ui::ImeTextSpan>&)); +}; + +class MockInputMethodDelegate : public internal::InputMethodDelegate { + public: + ~MockInputMethodDelegate() {} + MOCK_METHOD2(DispatchKeyEventPostIME, + EventDispatchDetails(KeyEvent*, + base::OnceCallback<void(bool, bool)>)); }; class MockStoreACPSink : public ITextStoreACPSink { @@ -127,6 +143,7 @@ EXPECT_EQ(S_OK, text_store_->AdviseSink(IID_ITextStoreACPSink, sink_.get(), TS_AS_ALL_SINKS)); text_store_->SetFocusedTextInputClient(kWindowHandle, &text_input_client_); + text_store_->SetInputMethodDelegate(&input_method_delegate_); } void TearDown() override { @@ -136,11 +153,14 @@ } // Accessors to the internal state of TSFTextStore. - base::string16* string_buffer() { return &text_store_->string_buffer_; } - size_t* committed_size() { return &text_store_->committed_size_; } + base::string16* string_buffer() { + return &text_store_->string_buffer_document_; + } + size_t* composition_start() { return &text_store_->composition_start_; } base::win::ScopedCOMInitializer com_initializer_; MockTextInputClient text_input_client_; + MockInputMethodDelegate input_method_delegate_; scoped_refptr<TSFTextStore> text_store_; scoped_refptr<MockStoreACPSink> sink_; }; @@ -153,24 +173,48 @@ } virtual ~TSFTextStoreTestCallback() {} + bool HasCompositionText() { return has_composition_text_; } + bool GetTextRange(gfx::Range* range) { + range->set_start(text_range_.start()); + range->set_end(text_range_.end()); + return true; + } + bool GetTextFromRange(const gfx::Range& range, base::string16* text) { + *text = text_buffer_.substr(range.GetMin(), range.length()); + return true; + } + bool GetEditableSelectionRange(gfx::Range* range) { + range->set_start(selection_range_.start()); + range->set_end(selection_range_.end()); + return true; + } + protected: // Accessors to the internal state of TSFTextStore. bool* edit_flag() { return &text_store_->edit_flag_; } - base::string16* string_buffer() { return &text_store_->string_buffer_; } - size_t* committed_size() { return &text_store_->committed_size_; } + base::string16* string_buffer() { + return &text_store_->string_buffer_document_; + } + base::string16* string_pending_insertion() { + return &text_store_->string_pending_insertion_; + } + size_t* composition_start() { return &text_store_->composition_start_; } gfx::Range* selection() { return &text_store_->selection_; } ImeTextSpans* text_spans() { return &text_store_->text_spans_; } + gfx::Range* composition_range() { return &text_store_->composition_range_; } + bool* has_composition_range() { return &text_store_->has_composition_range_; } void SetInternalState(const base::string16& new_string_buffer, - LONG new_committed_size, + LONG new_composition_start, LONG new_selection_start, LONG new_selection_end) { - ASSERT_LE(0, new_committed_size); - ASSERT_LE(new_committed_size, new_selection_start); + ASSERT_LE(0, new_composition_start); + ASSERT_LE(new_composition_start, new_selection_start); ASSERT_LE(new_selection_start, new_selection_end); ASSERT_LE(new_selection_end, static_cast<LONG>(new_string_buffer.size())); *string_buffer() = new_string_buffer; - *committed_size() = new_committed_size; + *string_pending_insertion() = new_string_buffer; + *composition_start() = new_composition_start; selection()->set_start(new_selection_start); selection()->set_end(new_selection_end); } @@ -306,6 +350,29 @@ acp_end, &rect, &clipped)); } + void SetHasCompositionText(bool compText) { + has_composition_text_ = compText; + } + + void SetTextRange(uint32_t start, uint32_t end) { + text_range_.set_start(start); + text_range_.set_end(end); + } + + void SetSelectionRange(uint32_t start, uint32_t end) { + selection_range_.set_start(start); + selection_range_.set_end(end); + } + + void SetTextBuffer(const wchar_t* buffer) { + text_buffer_.clear(); + text_buffer_.assign(buffer); + } + + bool has_composition_text_ = false; + gfx::Range text_range_; + gfx::Range selection_range_; + base::string16 text_buffer_ = L""; scoped_refptr<TSFTextStore> text_store_; private: @@ -320,15 +387,14 @@ TS_STATUS status = {}; EXPECT_EQ(S_OK, text_store_->GetStatus(&status)); EXPECT_EQ(0u, status.dwDynamicFlags); - EXPECT_EQ((ULONG)(TS_SS_TRANSITORY | TS_SS_NOHIDDENTEXT), - status.dwStaticFlags); + EXPECT_EQ((ULONG)(TS_SS_NOHIDDENTEXT), status.dwStaticFlags); } TEST_F(TSFTextStoreTest, QueryInsertTest) { LONG result_start = 0; LONG result_end = 0; *string_buffer() = L""; - *committed_size() = 0; + *composition_start() = 0; EXPECT_EQ(E_INVALIDARG, text_store_->QueryInsert(0, 0, 0, nullptr, &result_end)); EXPECT_EQ(E_INVALIDARG, @@ -338,7 +404,7 @@ EXPECT_EQ(0, result_start); EXPECT_EQ(0, result_end); *string_buffer() = L"1234"; - *committed_size() = 1; + *composition_start() = 1; EXPECT_EQ(S_OK, text_store_->QueryInsert(0, 1, 0, &result_start, &result_end)); EXPECT_EQ(1, result_start); @@ -565,31 +631,40 @@ state_ = 3; } - void SetCompositionText(const ui::CompositionText& composition) { - EXPECT_EQ(3, state_); - EXPECT_EQ(L"", composition.text); - EXPECT_EQ(0u, composition.selection.start()); - EXPECT_EQ(0u, composition.selection.end()); - EXPECT_EQ(0u, composition.ime_text_spans.size()); - state_ = 4; + bool GetTextRange(gfx::Range* range) const { + range->set_start(0); + range->set_end(6); + return true; } - HRESULT OnTextChange(DWORD flags, const TS_TEXTCHANGE* change) { - EXPECT_EQ(4, state_); + bool GetTextFromRange(const gfx::Range& range, base::string16* text) const { + base::string16 string_buffer = L"012345"; + *text = string_buffer.substr(range.GetMin(), range.length()); + return true; + } + + bool GetEditableSelectionRange(gfx::Range* range) const { + range->set_start(0); + range->set_end(0); + return true; + } + + HRESULT OnSelectionChange() { + EXPECT_EQ(3, state_); HRESULT result = kInvalidResult; - state_ = 5; + state_ = 4; EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); EXPECT_EQ(S_OK, result); - EXPECT_EQ(6, state_); - state_ = 7; + EXPECT_EQ(5, state_); + state_ = 6; return S_OK; } HRESULT LockGranted2(DWORD flags) { - EXPECT_EQ(5, state_); + EXPECT_EQ(4, state_); EXPECT_TRUE(HasReadLock()); EXPECT_TRUE(HasReadWriteLock()); - state_ = 6; + state_ = 5; return S_OK; } @@ -607,17 +682,29 @@ .WillOnce( Invoke(&callback, &RequestLockTextChangeTestCallback::LockGranted2)); - EXPECT_CALL(*sink_, OnSelectionChange()).WillOnce(Return(S_OK)); - EXPECT_CALL(*sink_, OnLayoutChange(_, _)).WillOnce(Return(S_OK)); - EXPECT_CALL(*sink_, OnTextChange(_, _)) - .WillOnce( - Invoke(&callback, &RequestLockTextChangeTestCallback::OnTextChange)); + EXPECT_CALL(*sink_, OnSelectionChange()) + .WillOnce(Invoke(&callback, + &RequestLockTextChangeTestCallback::OnSelectionChange)); EXPECT_CALL(text_input_client_, InsertText(_)) .WillOnce( Invoke(&callback, &RequestLockTextChangeTestCallback::InsertText)); - EXPECT_CALL(text_input_client_, SetCompositionText(_)) + EXPECT_CALL(text_input_client_, GetEditableSelectionRange(_)) + .WillOnce( + Invoke(&callback, + &RequestLockTextChangeTestCallback::GetEditableSelectionRange)) + .WillOnce(Invoke( + &callback, + &RequestLockTextChangeTestCallback::GetEditableSelectionRange)); + EXPECT_CALL(text_input_client_, GetTextFromRange(_, _)) .WillOnce(Invoke(&callback, - &RequestLockTextChangeTestCallback::SetCompositionText)); + &RequestLockTextChangeTestCallback::GetTextFromRange)) + .WillOnce(Invoke(&callback, + &RequestLockTextChangeTestCallback::GetTextFromRange)); + EXPECT_CALL(text_input_client_, GetTextRange(_)) + .WillOnce( + Invoke(&callback, &RequestLockTextChangeTestCallback::GetTextRange)) + .WillOnce( + Invoke(&callback, &RequestLockTextChangeTestCallback::GetTextRange)); HRESULT result = kInvalidResult; EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); @@ -654,18 +741,18 @@ SetInternalState(L"0123456", 3, 3, 3); - SetSelectionTest(0, 0, TF_E_INVALIDPOS); - SetSelectionTest(0, 1, TF_E_INVALIDPOS); - SetSelectionTest(0, 3, TF_E_INVALIDPOS); - SetSelectionTest(0, 6, TF_E_INVALIDPOS); - SetSelectionTest(0, 7, TF_E_INVALIDPOS); + SetSelectionTest(0, 0, S_OK); + SetSelectionTest(0, 1, S_OK); + SetSelectionTest(0, 3, S_OK); + SetSelectionTest(0, 6, S_OK); + SetSelectionTest(0, 7, S_OK); SetSelectionTest(0, 8, TF_E_INVALIDPOS); SetSelectionTest(1, 0, TF_E_INVALIDPOS); - SetSelectionTest(1, 1, TF_E_INVALIDPOS); - SetSelectionTest(1, 3, TF_E_INVALIDPOS); - SetSelectionTest(1, 6, TF_E_INVALIDPOS); - SetSelectionTest(1, 7, TF_E_INVALIDPOS); + SetSelectionTest(1, 1, S_OK); + SetSelectionTest(1, 3, S_OK); + SetSelectionTest(1, 6, S_OK); + SetSelectionTest(1, 7, S_OK); SetSelectionTest(1, 8, TF_E_INVALIDPOS); SetSelectionTest(3, 0, TF_E_INVALIDPOS); @@ -799,16 +886,16 @@ SetInternalState(L"0123456", 3, 3, 3); - SetTextTest(0, 0, L"", TS_E_INVALIDPOS); - SetTextTest(0, 1, L"", TS_E_INVALIDPOS); - SetTextTest(0, 3, L"", TS_E_INVALIDPOS); + SetTextTest(0, 0, L"", S_OK); + SetTextTest(0, 1, L"", S_OK); + SetTextTest(0, 3, L"", S_OK); SetTextTest(0, 6, L"", TS_E_INVALIDPOS); SetTextTest(0, 7, L"", TS_E_INVALIDPOS); SetTextTest(0, 8, L"", TS_E_INVALIDPOS); SetTextTest(1, 0, L"", TS_E_INVALIDPOS); - SetTextTest(1, 1, L"", TS_E_INVALIDPOS); - SetTextTest(1, 3, L"", TS_E_INVALIDPOS); + SetTextTest(1, 1, L"", S_OK); + SetTextTest(1, 3, L"", S_OK); SetTextTest(1, 6, L"", TS_E_INVALIDPOS); SetTextTest(1, 7, L"", TS_E_INVALIDPOS); SetTextTest(1, 8, L"", TS_E_INVALIDPOS); @@ -816,9 +903,9 @@ SetTextTest(3, 0, L"", TS_E_INVALIDPOS); SetTextTest(3, 1, L"", TS_E_INVALIDPOS); - SetTextTest(3, 3, L"", S_OK); - GetTextTest(0, -1, L"0123456", 7); - GetSelectionTest(3, 3); + SetTextTest(3, 3, L"", TS_E_INVALIDPOS); + GetTextTest(0, -1, L"4", 1); + GetSelectionTest(1, 1); SetInternalState(L"0123456", 3, 3, 3); SetTextTest(3, 6, L"", S_OK); @@ -1009,13 +1096,13 @@ : TSFTextStoreTestCallback(text_store) {} HRESULT LockGranted1(DWORD flags) { - SetSelectionTest(0, 0, S_OK); - SetTextTest(0, 0, L"abc", S_OK); SetTextTest(1, 2, L"xyz", S_OK); GetTextTest(0, -1, L"axyzc", 5); + SetSelectionTest(0, 5, S_OK); + text_spans()->clear(); ImeTextSpan text_span; text_span.start_offset = 0; @@ -1025,14 +1112,17 @@ text_span.background_color = SK_ColorTRANSPARENT; text_spans()->push_back(text_span); *edit_flag() = true; - *committed_size() = 0; + *composition_start() = 0; + composition_range()->set_start(0); + composition_range()->set_end(5); + return S_OK; } void SetCompositionText1(const ui::CompositionText& composition) { EXPECT_EQ(L"axyzc", composition.text); - EXPECT_EQ(1u, composition.selection.start()); - EXPECT_EQ(4u, composition.selection.end()); + EXPECT_EQ(0u, composition.selection.start()); + EXPECT_EQ(5u, composition.selection.end()); ASSERT_EQ(1u, composition.ime_text_spans.size()); EXPECT_EQ(SK_ColorBLACK, composition.ime_text_spans[0].underline_color); EXPECT_EQ(SK_ColorTRANSPARENT, @@ -1044,45 +1134,33 @@ } HRESULT LockGranted2(DWORD flags) { - SetTextTest(3, 4, L"ZCP", S_OK); + SetTextTest(0, 5, L"axyZCPc", S_OK); GetTextTest(0, -1, L"axyZCPc", 7); text_spans()->clear(); ImeTextSpan text_span; - text_span.start_offset = 3; + text_span.start_offset = 0; text_span.end_offset = 5; text_span.underline_color = SK_ColorBLACK; text_span.thickness = ImeTextSpan::Thickness::kThick; text_spans()->push_back(text_span); - text_span.start_offset = 5; - text_span.end_offset = 7; - text_span.underline_color = SK_ColorBLACK; - text_span.thickness = ImeTextSpan::Thickness::kThin; - text_spans()->push_back(text_span); *edit_flag() = true; - *committed_size() = 3; + *composition_start() = 7; + composition_range()->set_start(0); + composition_range()->set_end(7); return S_OK; } - void InsertText2(const base::string16& text) { EXPECT_EQ(L"axy", text); } + void InsertText2(const base::string16& text) { EXPECT_EQ(L"axyZCPc", text); } void SetCompositionText2(const ui::CompositionText& composition) { - EXPECT_EQ(L"ZCPc", composition.text); + EXPECT_EQ(L"axyZCPc", composition.text); EXPECT_EQ(0u, composition.selection.start()); - EXPECT_EQ(3u, composition.selection.end()); - ASSERT_EQ(2u, composition.ime_text_spans.size()); - EXPECT_EQ(SK_ColorBLACK, composition.ime_text_spans[0].underline_color); - EXPECT_EQ(0u, composition.ime_text_spans[0].start_offset); - EXPECT_EQ(2u, composition.ime_text_spans[0].end_offset); - EXPECT_EQ(ImeTextSpan::Thickness::kThick, - composition.ime_text_spans[0].thickness); - EXPECT_EQ(SK_ColorBLACK, composition.ime_text_spans[1].underline_color); - EXPECT_EQ(2u, composition.ime_text_spans[1].start_offset); - EXPECT_EQ(4u, composition.ime_text_spans[1].end_offset); - EXPECT_EQ(ImeTextSpan::Thickness::kThin, - composition.ime_text_spans[1].thickness); + EXPECT_EQ(0u, composition.selection.end()); + ASSERT_EQ(1u, composition.ime_text_spans.size()); + // There is no styling applied from TSF in English typing } HRESULT LockGranted3(DWORD flags) { @@ -1090,20 +1168,13 @@ text_spans()->clear(); *edit_flag() = true; - *committed_size() = 7; + *composition_start() = 7; + composition_range()->set_start(0); + composition_range()->set_end(0); return S_OK; } - void InsertText3(const base::string16& text) { EXPECT_EQ(L"ZCPc", text); } - - void SetCompositionText3(const ui::CompositionText& composition) { - EXPECT_EQ(L"", composition.text); - EXPECT_EQ(0u, composition.selection.start()); - EXPECT_EQ(0u, composition.selection.end()); - EXPECT_EQ(0u, composition.ime_text_spans.size()); - } - private: DISALLOW_COPY_AND_ASSIGN(ScenarioTestCallback); }; @@ -1112,26 +1183,14 @@ ScenarioTestCallback callback(text_store_.get()); EXPECT_CALL(text_input_client_, SetCompositionText(_)) .WillOnce(Invoke(&callback, &ScenarioTestCallback::SetCompositionText1)) - .WillOnce(Invoke(&callback, &ScenarioTestCallback::SetCompositionText2)) - .WillOnce(Invoke(&callback, &ScenarioTestCallback::SetCompositionText3)); + .WillOnce(Invoke(&callback, &ScenarioTestCallback::SetCompositionText2)); EXPECT_CALL(text_input_client_, InsertText(_)) - .WillOnce(Invoke(&callback, &ScenarioTestCallback::InsertText2)) - .WillOnce(Invoke(&callback, &ScenarioTestCallback::InsertText3)); + .WillOnce(Invoke(&callback, &ScenarioTestCallback::InsertText2)); EXPECT_CALL(*sink_, OnLockGranted(_)) .WillOnce(Invoke(&callback, &ScenarioTestCallback::LockGranted1)) - .WillOnce(Invoke(&callback, &ScenarioTestCallback::LockGranted2)) - .WillOnce(Invoke(&callback, &ScenarioTestCallback::LockGranted3)); - - // OnSelectionChange will be called once after LockGranted3(). - EXPECT_CALL(*sink_, OnSelectionChange()).WillOnce(Return(S_OK)); - - // OnLayoutChange will be called once after LockGranted3(). - EXPECT_CALL(*sink_, OnLayoutChange(_, _)).WillOnce(Return(S_OK)); - - // OnTextChange will be called once after LockGranted3(). - EXPECT_CALL(*sink_, OnTextChange(_, _)).WillOnce(Return(S_OK)); + .WillOnce(Invoke(&callback, &ScenarioTestCallback::LockGranted2)); HRESULT result = kInvalidResult; EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); @@ -1140,8 +1199,6 @@ EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); EXPECT_EQ(S_OK, result); result = kInvalidResult; - EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); - EXPECT_EQ(S_OK, result); } class GetTextExtTestCallback : public TSFTextStoreTestCallback { @@ -1164,8 +1221,8 @@ GetTextExtTest(view_cookie, 10, 10, 110, 12, 110, 20); GetTextExtTest(view_cookie, 11, 11, 20, 112, 20, 120); GetTextExtTest(view_cookie, 11, 12, 21, 112, 30, 120); - GetTextExtTest(view_cookie, 9, 12, 101, 12, 30, 120); - GetTextExtTest(view_cookie, 9, 13, 101, 12, 40, 120); + GetTextExtTest(view_cookie, 9, 12, 101, 12, 101, 120); + GetTextExtTest(view_cookie, 9, 13, 101, 12, 101, 120); GetTextExtTest(view_cookie, 0, 13, 11, 12, 40, 120); GetTextExtTest(view_cookie, 13, 13, 40, 112, 40, 120); @@ -1300,5 +1357,683 @@ } } +class KeyEventTestCallback : public TSFTextStoreTestCallback { + public: + explicit KeyEventTestCallback(TSFTextStore* text_store) + : TSFTextStoreTestCallback(text_store) {} + + HRESULT LockGranted1(DWORD flags) { + SetTextTest(0, 0, L"a", S_OK); + + GetTextTest(0, -1, L"a", 1); + + SetSelectionTest(0, 1, S_OK); + + text_spans()->clear(); + ImeTextSpan text_span; + text_span.start_offset = 0; + text_span.end_offset = 1; + text_span.underline_color = SK_ColorBLACK; + text_span.thickness = ImeTextSpan::Thickness::kThin; + text_span.background_color = SK_ColorTRANSPARENT; + text_spans()->push_back(text_span); + *edit_flag() = true; + *composition_start() = 0; + composition_range()->set_start(0); + composition_range()->set_end(1); + text_store_->OnKeyTraceDown(65u, 1966081u); + *has_composition_range() = true; + + return S_OK; + } + + void SetCompositionText1(const ui::CompositionText& composition) { + EXPECT_EQ(L"a", composition.text); + EXPECT_EQ(0u, composition.selection.start()); + EXPECT_EQ(1u, composition.selection.end()); + ASSERT_EQ(1u, composition.ime_text_spans.size()); + EXPECT_EQ(SK_ColorBLACK, composition.ime_text_spans[0].underline_color); + EXPECT_EQ(SK_ColorTRANSPARENT, + composition.ime_text_spans[0].background_color); + EXPECT_EQ(0u, composition.ime_text_spans[0].start_offset); + EXPECT_EQ(1u, composition.ime_text_spans[0].end_offset); + EXPECT_EQ(ImeTextSpan::Thickness::kThin, + composition.ime_text_spans[0].thickness); + SetHasCompositionText(true); + } + + ui::EventDispatchDetails DispatchKeyEventPostIME1( + KeyEvent* key, + base::OnceCallback<void(bool, bool)> ack_callback) { + EXPECT_EQ(ui::ET_KEY_PRESSED, key->type()); + EXPECT_EQ(VKEY_PROCESSKEY, key->key_code()); + return ui::EventDispatchDetails(); + } + + HRESULT LockGranted2(DWORD flags) { + SetSelectionTest(1, 1, S_OK); + InsertTextAtSelectionTest(L"B", 1, 1, 2, 1, 1, 2); + GetTextTest(0, -1, L"aB", 2); + + text_spans()->clear(); + ImeTextSpan text_span; + text_span.start_offset = 1; + text_span.end_offset = 2; + text_span.underline_color = SK_ColorBLACK; + text_span.thickness = ImeTextSpan::Thickness::kThick; + text_spans()->push_back(text_span); + + *edit_flag() = true; + *composition_start() = 1; + composition_range()->set_start(1); + composition_range()->set_end(2); + + text_store_->OnKeyTraceUp(65u, 1966081u); + text_store_->OnKeyTraceDown(66u, 3145729u); + return S_OK; + } + + void InsertText2(const base::string16& text) { + EXPECT_EQ(L"a", text); + SetHasCompositionText(false); + } + + void SetCompositionText2(const ui::CompositionText& composition) { + EXPECT_EQ(L"B", composition.text); + EXPECT_EQ(0u, composition.selection.start()); + EXPECT_EQ(1u, composition.selection.end()); + ASSERT_EQ(1u, composition.ime_text_spans.size()); + EXPECT_EQ(SK_ColorBLACK, composition.ime_text_spans[0].underline_color); + EXPECT_EQ(0u, composition.ime_text_spans[0].start_offset); + EXPECT_EQ(1u, composition.ime_text_spans[0].end_offset); + EXPECT_EQ(ImeTextSpan::Thickness::kThick, + composition.ime_text_spans[0].thickness); + SetHasCompositionText(true); + } + + ui::EventDispatchDetails DispatchKeyEventPostIME2( + KeyEvent* key, + base::OnceCallback<void(bool, bool)> ack_callback) { + EXPECT_EQ(ui::ET_KEY_RELEASED, key->type()); + EXPECT_EQ(VKEY_PROCESSKEY, key->key_code()); + return ui::EventDispatchDetails(); + } + + ui::EventDispatchDetails DispatchKeyEventPostIME3a( + KeyEvent* key, + base::OnceCallback<void(bool, bool)> ack_callback) { + EXPECT_EQ(ui::ET_KEY_PRESSED, key->type()); + EXPECT_EQ(VKEY_PROCESSKEY, key->key_code()); + return ui::EventDispatchDetails(); + } + + HRESULT LockGranted3(DWORD flags) { + GetTextTest(0, -1, L"aB", 2); + + text_spans()->clear(); + *edit_flag() = true; + *composition_start() = 2; + composition_range()->set_start(0); + composition_range()->set_end(0); + + *has_composition_range() = false; + text_store_->OnKeyTraceUp(66u, 3145729u); + return S_OK; + } + + void InsertText3(const base::string16& text) { + EXPECT_EQ(L"B", text); + SetHasCompositionText(false); + } + + void SetCompositionText3(const ui::CompositionText& composition) { + EXPECT_EQ(L"", composition.text); + EXPECT_EQ(0u, composition.selection.start()); + EXPECT_EQ(0u, composition.selection.end()); + EXPECT_EQ(0u, composition.ime_text_spans.size()); + } + + ui::EventDispatchDetails DispatchKeyEventPostIME3b( + KeyEvent* key, + base::OnceCallback<void(bool, bool)> ack_callback) { + EXPECT_EQ(ui::ET_KEY_RELEASED, key->type()); + EXPECT_EQ(VKEY_PROCESSKEY, key->key_code()); + return ui::EventDispatchDetails(); + } + + HRESULT LockGranted4(DWORD flags) { + text_store_->OnKeyTraceDown(8u, 917505u); + text_store_->OnKeyTraceUp(8u, 917505u); + return S_OK; + } + + private: + DISALLOW_COPY_AND_ASSIGN(KeyEventTestCallback); +}; + +TEST_F(TSFTextStoreTest, KeyEventTest) { + KeyEventTestCallback callback(text_store_.get()); + EXPECT_CALL(text_input_client_, SetCompositionText(_)) + .WillOnce(Invoke(&callback, &KeyEventTestCallback::SetCompositionText1)) + .WillOnce(Invoke(&callback, &KeyEventTestCallback::SetCompositionText2)) + .WillOnce(Invoke(&callback, &KeyEventTestCallback::SetCompositionText3)); + + EXPECT_CALL(text_input_client_, InsertText(_)) + .WillOnce(Invoke(&callback, &KeyEventTestCallback::InsertText2)) + .WillOnce(Invoke(&callback, &KeyEventTestCallback::InsertText3)); + + EXPECT_CALL(input_method_delegate_, DispatchKeyEventPostIME(_, _)) + .WillOnce( + Invoke(&callback, &KeyEventTestCallback::DispatchKeyEventPostIME1)) + .WillOnce( + Invoke(&callback, &KeyEventTestCallback::DispatchKeyEventPostIME2)) + .WillOnce( + Invoke(&callback, &KeyEventTestCallback::DispatchKeyEventPostIME3a)) + .WillOnce( + Invoke(&callback, &KeyEventTestCallback::DispatchKeyEventPostIME3b)); + + EXPECT_CALL(*sink_, OnLockGranted(_)) + .WillOnce(Invoke(&callback, &KeyEventTestCallback::LockGranted1)) + .WillOnce(Invoke(&callback, &KeyEventTestCallback::LockGranted2)) + .WillOnce(Invoke(&callback, &KeyEventTestCallback::LockGranted3)) + .WillOnce(Invoke(&callback, &KeyEventTestCallback::LockGranted4)); + + ON_CALL(text_input_client_, HasCompositionText()) + .WillByDefault( + Invoke(&callback, &TSFTextStoreTestCallback::HasCompositionText)); + + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); +} + +// Summary of test scenarios: +// 1. renderer proc changes buffer from "" to "a". +// 2. input service changes buffer from "a" to "abcde". +// 3. renderer proc changes buffer from "abcde" to "about". +// 4. renderer proc changes buffer from "about" to "abFGt". +// 5. renderer proc changes buffer from "abFGt" to "aHIGt". +// 6. renderer proc changes buffer from "aHIGt" to "JKLMN". +// 7. renderer proc changes buffer from "JKLMN" to "". +// 8. renderer proc changes buffer from "" to "OPQ". +// 9. renderer proc changes buffer from "OPQ" to "OPR". +// 10. renderer proc changes buffer from "OPR" to "SPR". +class DiffingAlgorithmTestCallback : public TSFTextStoreTestCallback { + public: + explicit DiffingAlgorithmTestCallback(TSFTextStore* text_store) + : TSFTextStoreTestCallback(text_store) {} + + HRESULT LockGranted1(DWORD flags) { + SetTextTest(0, 0, L"", S_OK); + GetTextTest(0, -1, L"", 0); + + SetTextRange(0, 1); + SetTextBuffer(L"a"); + SetSelectionRange(1, 1); + *composition_start() = 1; + return S_OK; + } + + HRESULT OnTextChange1(DWORD flag, const TS_TEXTCHANGE* pChange) { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(0, pChange->acpStart); + EXPECT_EQ(0, pChange->acpOldEnd); + EXPECT_EQ(1, pChange->acpNewEnd); + + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted1a(DWORD flags) { + GetTextTest(0, -1, L"a", 1); + + return S_OK; + } + + HRESULT OnSelectionChange1() { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted1b(DWORD flags) { + GetSelectionTest(1, 1); + return S_OK; + } + + HRESULT LockGranted2(DWORD flags) { + SetTextTest(1, 1, L"bcde", S_OK); + GetTextTest(0, -1, L"abcde", 5); + SetSelectionTest(5, 5, S_OK); + + *edit_flag() = true; + *composition_start() = 5; + return S_OK; + } + + void InsertText2(const base::string16& text) { + EXPECT_EQ(L"bcde", text); + SetTextRange(0, 5); + SetSelectionRange(5, 5); + SetTextBuffer(L"abcde"); + } + + HRESULT LockGranted3(DWORD flags) { + SetTextRange(0, 5); + SetTextBuffer(L"about"); + SetSelectionRange(0, 5); + return S_OK; + } + + HRESULT OnTextChange3(DWORD flag, const TS_TEXTCHANGE* pChange) { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(2, pChange->acpStart); + EXPECT_EQ(5, pChange->acpOldEnd); + EXPECT_EQ(5, pChange->acpNewEnd); + + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted3a(DWORD flags) { + GetTextTest(1, 5, L"bout", 5); + + return S_OK; + } + + HRESULT OnSelectionChange3() { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted3b(DWORD flags) { + GetSelectionTest(0, 5); + return S_OK; + } + + HRESULT LockGranted4(DWORD flags) { + SetTextRange(0, 5); + SetTextBuffer(L"abFGt"); + SetSelectionRange(3, 4); + return S_OK; + } + + HRESULT OnTextChange4(DWORD flag, const TS_TEXTCHANGE* pChange) { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(2, pChange->acpStart); + EXPECT_EQ(4, pChange->acpOldEnd); + EXPECT_EQ(4, pChange->acpNewEnd); + + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted4a(DWORD flags) { + GetTextTest(2, 4, L"FG", 4); + + return S_OK; + } + + HRESULT OnSelectionChange4() { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted4b(DWORD flags) { + GetSelectionTest(3, 4); + return S_OK; + } + + HRESULT LockGranted5(DWORD flags) { + SetTextRange(0, 3); + SetTextBuffer(L"aHI"); + SetSelectionRange(3, 3); + return S_OK; + } + + HRESULT OnTextChange5(DWORD flag, const TS_TEXTCHANGE* pChange) { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(1, pChange->acpStart); + EXPECT_EQ(5, pChange->acpOldEnd); + EXPECT_EQ(3, pChange->acpNewEnd); + + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted5a(DWORD flags) { + GetTextTest(1, 3, L"HI", 3); + + return S_OK; + } + + HRESULT OnSelectionChange5() { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted5b(DWORD flags) { + GetSelectionTest(3, 3); + return S_OK; + } + + HRESULT LockGranted6(DWORD flags) { + SetTextRange(0, 5); + SetTextBuffer(L"JKLMN"); + SetSelectionRange(2, 5); + return S_OK; + } + + HRESULT OnTextChange6(DWORD flag, const TS_TEXTCHANGE* pChange) { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(0, pChange->acpStart); + EXPECT_EQ(3, pChange->acpOldEnd); + EXPECT_EQ(5, pChange->acpNewEnd); + + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted6a(DWORD flags) { + GetTextTest(3, 5, L"MN", 5); + + return S_OK; + } + + HRESULT OnSelectionChange6() { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted6b(DWORD flags) { + GetSelectionTest(2, 5); + return S_OK; + } + + HRESULT LockGranted7(DWORD flags) { + SetTextRange(0, 0); + SetTextBuffer(L""); + SetSelectionRange(0, 0); + return S_OK; + } + + HRESULT OnTextChange7(DWORD flag, const TS_TEXTCHANGE* pChange) { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(0, pChange->acpStart); + EXPECT_EQ(5, pChange->acpOldEnd); + EXPECT_EQ(0, pChange->acpNewEnd); + + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted7a(DWORD flags) { + GetTextTest(0, -1, L"", 0); + + return S_OK; + } + + HRESULT OnSelectionChange7() { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted7b(DWORD flags) { + GetSelectionTest(0, 0); + return S_OK; + } + + HRESULT LockGranted8(DWORD flags) { + SetTextRange(0, 3); + SetTextBuffer(L"OPQ"); + SetSelectionRange(0, 2); + return S_OK; + } + + HRESULT OnTextChange8(DWORD flag, const TS_TEXTCHANGE* pChange) { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(0, pChange->acpStart); + EXPECT_EQ(0, pChange->acpOldEnd); + EXPECT_EQ(3, pChange->acpNewEnd); + + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted8a(DWORD flags) { + GetTextTest(0, -1, L"OPQ", 3); + + return S_OK; + } + + HRESULT OnSelectionChange8() { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted8b(DWORD flags) { + GetSelectionTest(0, 2); + return S_OK; + } + + HRESULT LockGranted9(DWORD flags) { + SetTextRange(0, 3); + SetTextBuffer(L"OPR"); + SetSelectionRange(2, 3); + return S_OK; + } + + HRESULT OnTextChange9(DWORD flag, const TS_TEXTCHANGE* pChange) { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(2, pChange->acpStart); + EXPECT_EQ(3, pChange->acpOldEnd); + EXPECT_EQ(3, pChange->acpNewEnd); + + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted9a(DWORD flags) { + GetTextTest(2, 3, L"R", 3); + + return S_OK; + } + + HRESULT OnSelectionChange9() { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted9b(DWORD flags) { + GetSelectionTest(2, 3); + return S_OK; + } + + HRESULT LockGranted10(DWORD flags) { + SetTextRange(0, 3); + SetTextBuffer(L"SPR"); + SetSelectionRange(0, 1); + return S_OK; + } + + HRESULT OnTextChange10(DWORD flag, const TS_TEXTCHANGE* pChange) { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(0, pChange->acpStart); + EXPECT_EQ(1, pChange->acpOldEnd); + EXPECT_EQ(1, pChange->acpNewEnd); + + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted10a(DWORD flags) { + GetTextTest(0, 1, L"S", 1); + + return S_OK; + } + + HRESULT OnSelectionChange10() { + HRESULT result = S_OK; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + return S_OK; + } + + HRESULT LockGranted10b(DWORD flags) { + GetSelectionTest(0, 1); + return S_OK; + } + + private: + DISALLOW_COPY_AND_ASSIGN(DiffingAlgorithmTestCallback); +}; + +TEST_F(TSFTextStoreTest, DiffingAlgorithmTest) { + DiffingAlgorithmTestCallback callback(text_store_.get()); + + EXPECT_CALL(*sink_, OnTextChange(_, _)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::OnTextChange1)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::OnTextChange3)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::OnTextChange4)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::OnTextChange5)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::OnTextChange6)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::OnTextChange7)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::OnTextChange8)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::OnTextChange9)) + .WillOnce( + Invoke(&callback, &DiffingAlgorithmTestCallback::OnTextChange10)); + + EXPECT_CALL(*sink_, OnSelectionChange()) + .WillOnce( + Invoke(&callback, &DiffingAlgorithmTestCallback::OnSelectionChange1)) + .WillOnce( + Invoke(&callback, &DiffingAlgorithmTestCallback::OnSelectionChange3)) + .WillOnce( + Invoke(&callback, &DiffingAlgorithmTestCallback::OnSelectionChange4)) + .WillOnce( + Invoke(&callback, &DiffingAlgorithmTestCallback::OnSelectionChange5)) + .WillOnce( + Invoke(&callback, &DiffingAlgorithmTestCallback::OnSelectionChange6)) + .WillOnce( + Invoke(&callback, &DiffingAlgorithmTestCallback::OnSelectionChange7)) + .WillOnce( + Invoke(&callback, &DiffingAlgorithmTestCallback::OnSelectionChange8)) + .WillOnce( + Invoke(&callback, &DiffingAlgorithmTestCallback::OnSelectionChange9)) + .WillOnce(Invoke(&callback, + &DiffingAlgorithmTestCallback::OnSelectionChange10)); + + EXPECT_CALL(text_input_client_, InsertText(_)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::InsertText2)); + + EXPECT_CALL(*sink_, OnLockGranted(_)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted1)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted1a)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted1b)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted2)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted3)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted3a)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted3b)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted4)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted4a)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted4b)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted5)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted5a)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted5b)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted6)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted6a)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted6b)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted7)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted7a)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted7b)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted8)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted8a)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted8b)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted9)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted9a)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted9b)) + .WillOnce(Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted10)) + .WillOnce( + Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted10a)) + .WillOnce( + Invoke(&callback, &DiffingAlgorithmTestCallback::LockGranted10b)); + + ON_CALL(text_input_client_, GetTextRange(_)) + .WillByDefault( + Invoke(&callback, &TSFTextStoreTestCallback::GetTextRange)); + + ON_CALL(text_input_client_, GetTextFromRange(_, _)) + .WillByDefault( + Invoke(&callback, &TSFTextStoreTestCallback::GetTextFromRange)); + + ON_CALL(text_input_client_, GetEditableSelectionRange(_)) + .WillByDefault(Invoke( + &callback, &TSFTextStoreTestCallback::GetEditableSelectionRange)); + + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; +} + } // namespace } // namespace ui
diff --git a/ui/display/manager/display_change_observer_unittest.cc b/ui/display/manager/display_change_observer_unittest.cc index 7cb44526..fb0959f 100644 --- a/ui/display/manager/display_change_observer_unittest.cc +++ b/ui/display/manager/display_change_observer_unittest.cc
@@ -172,7 +172,8 @@ TEST_P(DisplayChangeObserverTest, GetEmptyExternalManagedDisplayModeList) { FakeDisplaySnapshot display_snapshot( 123, gfx::Point(), gfx::Size(), DISPLAY_CONNECTION_TYPE_UNKNOWN, false, - false, false, false, std::string(), {}, nullptr, nullptr, 0, gfx::Size()); + false, false, false, std::string(), {}, nullptr, nullptr, 0, gfx::Size(), + /*has_associated_crtc=*/true); ManagedDisplayInfo::ManagedDisplayModeList display_modes = DisplayChangeObserver::GetExternalManagedDisplayModeList(
diff --git a/ui/display/manager/display_configurator.cc b/ui/display/manager/display_configurator.cc index c81b1e8..653057c 100644 --- a/ui/display/manager/display_configurator.cc +++ b/ui/display/manager/display_configurator.cc
@@ -1167,6 +1167,16 @@ cached_displays_ = displays; has_unassociated_display_ = unassociated_displays.size(); + if (has_unassociated_display_) { + std::string names; + for (const auto* unassociated_display : unassociated_displays) { + if (!names.empty()) + names.push_back(','); + names += unassociated_display->display_name(); + } + LOG(WARNING) << "Following displays have no associated crtc: " << names; + } + if (success) { current_display_state_ = new_display_state; UpdatePowerState(new_power_state);
diff --git a/ui/display/manager/display_configurator.h b/ui/display/manager/display_configurator.h index 1ceb678..e81bd8d 100644 --- a/ui/display/manager/display_configurator.h +++ b/ui/display/manager/display_configurator.h
@@ -484,7 +484,7 @@ // Indicates whether there is any connected display having no associated crtc. // This can be caused by crtc shortage. When it is true, the corresponding - // notification will be created to inform user. + // notification will be created to inform the user. bool has_unassociated_display_; // This must be the last variable.
diff --git a/ui/display/manager/fake_display_snapshot.cc b/ui/display/manager/fake_display_snapshot.cc index 27df936..cc68b77 100644 --- a/ui/display/manager/fake_display_snapshot.cc +++ b/ui/display/manager/fake_display_snapshot.cc
@@ -163,7 +163,8 @@ id_, origin_, physical_size, type_, is_aspect_preserving_scaling_, has_overscan_, has_color_correction_matrix_, color_correction_in_linear_space_, name_, std::move(modes_), - current_mode_, native_mode_, product_code_, maximum_cursor_size_); + current_mode_, native_mode_, product_code_, maximum_cursor_size_, + /*has_associated_crtc=*/true); } Builder& Builder::SetId(int64_t id) { @@ -298,7 +299,8 @@ const DisplayMode* current_mode, const DisplayMode* native_mode, int64_t product_code, - const gfx::Size& maximum_cursor_size) + const gfx::Size& maximum_cursor_size, + bool has_associated_crtc) : DisplaySnapshot(display_id, origin, physical_size, @@ -316,7 +318,8 @@ native_mode, product_code, 2018 /*year_of_manufacture */, - maximum_cursor_size) {} + maximum_cursor_size, + has_associated_crtc) {} FakeDisplaySnapshot::~FakeDisplaySnapshot() {}
diff --git a/ui/display/manager/fake_display_snapshot.h b/ui/display/manager/fake_display_snapshot.h index ec90b71..674279d 100644 --- a/ui/display/manager/fake_display_snapshot.h +++ b/ui/display/manager/fake_display_snapshot.h
@@ -109,7 +109,8 @@ const DisplayMode* current_mode, const DisplayMode* native_mode, int64_t product_code, - const gfx::Size& maximum_cursor_size); + const gfx::Size& maximum_cursor_size, + bool has_associated_crtc); ~FakeDisplaySnapshot() override; // Creates a display snapshot from the provided |spec| string. Returns null if
diff --git a/ui/display/manager/update_display_configuration_task.cc b/ui/display/manager/update_display_configuration_task.cc index 92ae15d..5f8f4be 100644 --- a/ui/display/manager/update_display_configuration_task.cc +++ b/ui/display/manager/update_display_configuration_task.cc
@@ -58,7 +58,16 @@ void UpdateDisplayConfigurationTask::OnDisplaysUpdated( const std::vector<DisplaySnapshot*>& displays) { - cached_displays_ = displays; + cached_displays_.clear(); + cached_unassociated_displays_.clear(); + + for (auto* const display : displays) { + if (display->has_associated_crtc()) + cached_displays_.push_back(display); + else + cached_unassociated_displays_.push_back(display); + } + requesting_displays_ = false; // If the user hasn't requested a display state, update it using the requested
diff --git a/ui/display/manager/update_display_configuration_task_unittest.cc b/ui/display/manager/update_display_configuration_task_unittest.cc index 1db2dcd..0dd36ac1f 100644 --- a/ui/display/manager/update_display_configuration_task_unittest.cc +++ b/ui/display/manager/update_display_configuration_task_unittest.cc
@@ -86,6 +86,7 @@ std::vector<DisplayConfigureRequest>* requests) const override { gfx::Point origin; for (DisplaySnapshot* display : displays) { + DCHECK(display->has_associated_crtc()); const DisplayMode* mode = display->native_mode(); if (new_display_state == MULTIPLE_DISPLAY_STATE_MULTI_MIRROR) mode = should_mirror_ ? FindMirrorMode(displays) : nullptr;
diff --git a/ui/display/mojo/display_snapshot.mojom b/ui/display/mojo/display_snapshot.mojom index 9c358b5..2ade3512 100644 --- a/ui/display/mojo/display_snapshot.mojom +++ b/ui/display/mojo/display_snapshot.mojom
@@ -33,4 +33,5 @@ int64 product_code; int32 year_of_manufacture; gfx.mojom.Size maximum_cursor_size; + bool has_associated_crtc; };
diff --git a/ui/display/mojo/display_snapshot_struct_traits.cc b/ui/display/mojo/display_snapshot_struct_traits.cc index 9d97cfe..d4b43e71 100644 --- a/ui/display/mojo/display_snapshot_struct_traits.cc +++ b/ui/display/mojo/display_snapshot_struct_traits.cc
@@ -134,7 +134,8 @@ data.has_color_correction_matrix(), data.color_correction_in_linear_space(), color_space, display_name, file_path, std::move(modes), std::move(edid), current_mode, native_mode, - data.product_code(), data.year_of_manufacture(), maximum_cursor_size); + data.product_code(), data.year_of_manufacture(), maximum_cursor_size, + data.has_associated_crtc()); return true; }
diff --git a/ui/display/mojo/display_snapshot_struct_traits.h b/ui/display/mojo/display_snapshot_struct_traits.h index 0267d56c..9ca22f0 100644 --- a/ui/display/mojo/display_snapshot_struct_traits.h +++ b/ui/display/mojo/display_snapshot_struct_traits.h
@@ -113,6 +113,11 @@ return snapshot->maximum_cursor_size(); } + static bool has_associated_crtc( + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { + return snapshot->has_associated_crtc(); + } + static bool Read(display::mojom::DisplaySnapshotDataView data, std::unique_ptr<display::DisplaySnapshot>* out); };
diff --git a/ui/display/mojo/display_struct_traits_unittest.cc b/ui/display/mojo/display_struct_traits_unittest.cc index 32a460e..2631bba 100644 --- a/ui/display/mojo/display_struct_traits_unittest.cc +++ b/ui/display/mojo/display_struct_traits_unittest.cc
@@ -267,13 +267,14 @@ const DisplayMode* current_mode = nullptr; const DisplayMode* native_mode = nullptr; const std::vector<uint8_t> edid = {1}; + const bool has_associated_crtc = true; std::unique_ptr<DisplaySnapshot> input = std::make_unique<DisplaySnapshot>( display_id, origin, physical_size, type, is_aspect_preserving_scaling, has_overscan, has_color_correction_matrix, color_correction_in_linear_space, display_color_space, display_name, sys_path, std::move(modes), edid, current_mode, native_mode, product_code, - year_of_manufacture, maximum_cursor_size); + year_of_manufacture, maximum_cursor_size, has_associated_crtc); std::unique_ptr<DisplaySnapshot> output; SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); @@ -307,13 +308,14 @@ const DisplayMode* current_mode = nullptr; const DisplayMode* native_mode = modes[0].get(); const std::vector<uint8_t> edid = {1}; + const bool has_associated_crtc = true; std::unique_ptr<DisplaySnapshot> input = std::make_unique<DisplaySnapshot>( display_id, origin, physical_size, type, is_aspect_preserving_scaling, has_overscan, has_color_correction_matrix, color_correction_in_linear_space, display_color_space, display_name, sys_path, std::move(modes), edid, current_mode, native_mode, product_code, - year_of_manufacture, maximum_cursor_size); + year_of_manufacture, maximum_cursor_size, has_associated_crtc); std::unique_ptr<DisplaySnapshot> output; SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); @@ -351,13 +353,14 @@ const DisplayMode* current_mode = modes[1].get(); const DisplayMode* native_mode = modes[2].get(); const std::vector<uint8_t> edid = {2, 3, 4, 5}; + const bool has_associated_crtc = true; std::unique_ptr<DisplaySnapshot> input = std::make_unique<DisplaySnapshot>( display_id, origin, physical_size, type, is_aspect_preserving_scaling, has_overscan, has_color_correction_matrix, color_correction_in_linear_space, display_color_space, display_name, sys_path, std::move(modes), edid, current_mode, native_mode, product_code, - year_of_manufacture, maximum_cursor_size); + year_of_manufacture, maximum_cursor_size, has_associated_crtc); std::unique_ptr<DisplaySnapshot> output; SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); @@ -391,13 +394,14 @@ const DisplayMode* current_mode = modes[0].get(); const DisplayMode* native_mode = modes[0].get(); const std::vector<uint8_t> edid = {2, 3}; + const bool has_associated_crtc = true; std::unique_ptr<DisplaySnapshot> input = std::make_unique<DisplaySnapshot>( display_id, origin, physical_size, type, is_aspect_preserving_scaling, has_overscan, has_color_correction_matrix, color_correction_in_linear_space, display_color_space, display_name, sys_path, std::move(modes), edid, current_mode, native_mode, product_code, - year_of_manufacture, maximum_cursor_size); + year_of_manufacture, maximum_cursor_size, has_associated_crtc); std::unique_ptr<DisplaySnapshot> output; SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output);
diff --git a/ui/display/types/display_snapshot.cc b/ui/display/types/display_snapshot.cc index 2adf25c..0d0435b 100644 --- a/ui/display/types/display_snapshot.cc +++ b/ui/display/types/display_snapshot.cc
@@ -77,7 +77,8 @@ const DisplayMode* native_mode, int64_t product_code, int32_t year_of_manufacture, - const gfx::Size& maximum_cursor_size) + const gfx::Size& maximum_cursor_size, + bool has_associated_crtc) : display_id_(display_id), origin_(origin), physical_size_(physical_size), @@ -95,7 +96,8 @@ native_mode_(native_mode), product_code_(product_code), year_of_manufacture_(year_of_manufacture), - maximum_cursor_size_(maximum_cursor_size) { + maximum_cursor_size_(maximum_cursor_size), + has_associated_crtc_(has_associated_crtc) { // We must explicitly clear out the bytes that represent the serial number. const size_t end = std::min(kSerialNumberBeginingByte + kSerialNumberLengthInBytes, @@ -127,7 +129,7 @@ has_color_correction_matrix_, color_correction_in_linear_space_, color_space_, display_name_, sys_path_, std::move(clone_modes), edid_, cloned_current_mode, cloned_native_mode, product_code_, - year_of_manufacture_, maximum_cursor_size_); + year_of_manufacture_, maximum_cursor_size_, has_associated_crtc_); } std::string DisplaySnapshot::ToString() const {
diff --git a/ui/display/types/display_snapshot.h b/ui/display/types/display_snapshot.h index d1dfd11..ee5112b 100644 --- a/ui/display/types/display_snapshot.h +++ b/ui/display/types/display_snapshot.h
@@ -46,7 +46,8 @@ const DisplayMode* native_mode, int64_t product_code, int32_t year_of_manufacture, - const gfx::Size& maximum_cursor_size); + const gfx::Size& maximum_cursor_size, + bool has_associated_crtc); virtual ~DisplaySnapshot(); int64_t display_id() const { return display_id_; } @@ -76,6 +77,7 @@ int64_t product_code() const { return product_code_; } int32_t year_of_manufacture() const { return year_of_manufacture_; } const gfx::Size& maximum_cursor_size() const { return maximum_cursor_size_; } + bool has_associated_crtc() const { return has_associated_crtc_; } void add_mode(const DisplayMode* mode) { modes_.push_back(mode->Clone()); } @@ -138,6 +140,9 @@ // Maximum supported cursor size on this display. const gfx::Size maximum_cursor_size_; + // Indicates whether the display has an associated crtc. + const bool has_associated_crtc_; + private: DISALLOW_COPY_AND_ASSIGN(DisplaySnapshot); };
diff --git a/ui/events/blink/blink_features.cc b/ui/events/blink/blink_features.cc index a5ded831..7a76b7d 100644 --- a/ui/events/blink/blink_features.cc +++ b/ui/events/blink/blink_features.cc
@@ -23,4 +23,7 @@ const base::Feature kCompositorTouchAction{"CompositorTouchAction", base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kFallbackCursorMode{"FallbackCursorMode", + base::FEATURE_DISABLED_BY_DEFAULT}; }
diff --git a/ui/events/blink/blink_features.h b/ui/events/blink/blink_features.h index 08b123e..7a26c79 100644 --- a/ui/events/blink/blink_features.h +++ b/ui/events/blink/blink_features.h
@@ -37,6 +37,9 @@ // Enables handling touch events in compositor using impl side touch action // knowledge. extern const base::Feature kCompositorTouchAction; + +// Enables fallback cursor mode for dpad devices. +extern const base::Feature kFallbackCursorMode; } #endif // UI_EVENTS_BLINK_BLINK_FEATURES_H_
diff --git a/ui/gfx/font_render_params_linux_unittest.cc b/ui/gfx/font_render_params_linux_unittest.cc index 5859a3e4..f6ed64c 100644 --- a/ui/gfx/font_render_params_linux_unittest.cc +++ b/ui/gfx/font_render_params_linux_unittest.cc
@@ -394,6 +394,8 @@ EXPECT_EQ(system_params.hinting, params.hinting); EXPECT_EQ(system_params.subpixel_rendering, params.subpixel_rendering); EXPECT_EQ(query.families[0], suggested_family); + + FcConfigDestroy(FcConfigGetCurrent()); } TEST_F(FontRenderParamsTest, MissingFamily) {
diff --git a/ui/gl/android/surface_texture.cc b/ui/gl/android/surface_texture.cc index 5c6f425..6420765 100644 --- a/ui/gl/android/surface_texture.cc +++ b/ui/gl/android/surface_texture.cc
@@ -8,6 +8,7 @@ #include <utility> #include "base/android/jni_android.h" +#include "base/debug/crash_logging.h" #include "base/logging.h" #include "jni/SurfaceTexturePlatformWrapper_jni.h" #include "ui/gl/android/scoped_java_surface.h" @@ -51,6 +52,10 @@ } void SurfaceTexture::UpdateTexImage() { + static auto* kCrashKey = base::debug::AllocateCrashKeyString( + "inside_surface_texture_update_tex_image", + base::debug::CrashKeySize::Size256); + base::debug::ScopedCrashKeyString scoped_crash_key(kCrashKey, "1"); JNIEnv* env = base::android::AttachCurrentThread(); Java_SurfaceTexturePlatformWrapper_updateTexImage(env, j_surface_texture_); }
diff --git a/ui/ozone/common/gpu/ozone_gpu_message_params.h b/ui/ozone/common/gpu/ozone_gpu_message_params.h index cce7a669..246a20c 100644 --- a/ui/ozone/common/gpu/ozone_gpu_message_params.h +++ b/ui/ozone/common/gpu/ozone_gpu_message_params.h
@@ -57,6 +57,7 @@ int64_t product_code = 0; int32_t year_of_manufacture = display::kInvalidYearOfManufacture; gfx::Size maximum_cursor_size; + bool has_associated_crtc = true; }; struct OverlayCheck_Params {
diff --git a/ui/ozone/common/gpu/ozone_gpu_messages.h b/ui/ozone/common/gpu/ozone_gpu_messages.h index 6a93dc2..30cd521 100644 --- a/ui/ozone/common/gpu/ozone_gpu_messages.h +++ b/ui/ozone/common/gpu/ozone_gpu_messages.h
@@ -67,6 +67,7 @@ IPC_STRUCT_TRAITS_MEMBER(product_code) IPC_STRUCT_TRAITS_MEMBER(year_of_manufacture) IPC_STRUCT_TRAITS_MEMBER(maximum_cursor_size) + IPC_STRUCT_TRAITS_MEMBER(has_associated_crtc) IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(display::GammaRampRGBEntry)
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn index d68ecdf..9b655da 100644 --- a/ui/ozone/platform/drm/BUILD.gn +++ b/ui/ozone/platform/drm/BUILD.gn
@@ -175,6 +175,7 @@ sources = [ "common/drm_overlay_manager_unittest.cc", "common/drm_util_unittest.cc", + "gpu/drm_gpu_display_manager_unittest.cc", "gpu/drm_overlay_validator_unittest.cc", "gpu/drm_window_unittest.cc", "gpu/hardware_display_controller_unittest.cc",
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc index 76a6caf..732a931 100644 --- a/ui/ozone/platform/drm/common/drm_util.cc +++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -33,12 +33,11 @@ outcome); } -bool IsCrtcInUse( - uint32_t crtc, - const std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>& - displays) { +bool IsCrtcInUse(uint32_t crtc, + const HardwareDisplayControllerInfos& displays) { for (size_t i = 0; i < displays.size(); ++i) { - if (crtc == displays[i]->crtc()->crtc_id) + if (displays[i]->has_associated_crtc() && + crtc == displays[i]->crtc()->crtc_id) return true; } @@ -48,12 +47,10 @@ // Return a CRTC compatible with |connector| and not already used in |displays|. // If there are multiple compatible CRTCs, the one that supports the majority of // planes will be returned. -uint32_t GetCrtc( - int fd, - drmModeConnector* connector, - drmModeRes* resources, - const std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>& - displays) { +uint32_t GetCrtc(int fd, + drmModeConnector* connector, + drmModeRes* resources, + const HardwareDisplayControllerInfos& displays) { ScopedDrmPlaneResPtr plane_resources(drmModeGetPlaneResources(fd)); std::vector<ScopedDrmPlanePtr> planes; for (uint32_t i = 0; i < plane_resources->count_planes; i++) @@ -290,11 +287,12 @@ HardwareDisplayControllerInfo::~HardwareDisplayControllerInfo() { } -std::vector<std::unique_ptr<HardwareDisplayControllerInfo>> -GetAvailableDisplayControllerInfos(int fd) { +HardwareDisplayControllerInfos GetAvailableDisplayControllerInfos( + int fd, + bool* support_all_connectors) { ScopedDrmResourcesPtr resources(drmModeGetResources(fd)); DCHECK(resources) << "Failed to get DRM resources"; - std::vector<std::unique_ptr<HardwareDisplayControllerInfo>> displays; + HardwareDisplayControllerInfos displays; std::vector<ScopedDrmConnectorPtr> connectors; std::vector<drmModeConnector*> available_connectors; @@ -336,12 +334,20 @@ c1_crtcs != c2_crtcs; }); + if (support_all_connectors) + *support_all_connectors = true; for (auto* c : available_connectors) { uint32_t crtc_id = GetCrtc(fd, c, resources.get(), displays); - if (!crtc_id) - continue; + ScopedDrmCrtcPtr crtc; - ScopedDrmCrtcPtr crtc(drmModeGetCrtc(fd, crtc_id)); + if (crtc_id) { + crtc = ScopedDrmCrtcPtr(drmModeGetCrtc(fd, crtc_id)); + } else { + // No available crtc for this connector. + if (support_all_connectors) + *support_all_connectors = false; + } + auto iter = std::find_if(connectors.begin(), connectors.end(), [c](const ScopedDrmConnectorPtr& connector) { return connector.get() == c; @@ -388,7 +394,8 @@ const drmModeModeInfo& mode = info->connector()->modes[i]; modes.push_back(CreateDisplayMode(mode)); - if (info->crtc()->mode_valid && SameMode(info->crtc()->mode, mode)) + if (info->has_associated_crtc() && info->crtc()->mode_valid && + SameMode(info->crtc()->mode, mode)) *out_current_mode = modes.back().get(); if (mode.type & DRM_MODE_TYPE_PREFERRED) @@ -427,8 +434,9 @@ const bool is_aspect_preserving_scaling = IsAspectPreserving(fd, info->connector()); const bool has_color_correction_matrix = - HasColorCorrectionMatrix(fd, info->crtc()) || - HasPerPlaneColorCorrectionMatrix(fd, info->crtc()); + info->has_associated_crtc() && + (HasColorCorrectionMatrix(fd, info->crtc()) || + HasPerPlaneColorCorrectionMatrix(fd, info->crtc())); // On rk3399 we can set a color correction matrix that will be applied in // linear space. https://crbug.com/839020 to track if it will be possible to // disable the per-plane degamma/gamma. @@ -478,7 +486,7 @@ has_overscan, has_color_correction_matrix, color_correction_in_linear_space, display_color_space, display_name, sys_path, std::move(modes), edid, current_mode, native_mode, product_code, - year_of_manufacture, maximum_cursor_size); + year_of_manufacture, maximum_cursor_size, info->has_associated_crtc()); } // TODO(rjkroege): Remove in a subsequent CL once Mojo IPC is used everywhere. @@ -518,6 +526,7 @@ p.product_code = d->product_code(); p.year_of_manufacture = d->year_of_manufacture(); p.maximum_cursor_size = d->maximum_cursor_size(); + p.has_associated_crtc = d->has_associated_crtc(); params.push_back(p); } @@ -544,7 +553,8 @@ params.color_correction_in_linear_space, params.color_space, params.display_name, params.sys_path, std::move(modes), params.edid, current_mode, native_mode, params.product_code, - params.year_of_manufacture, params.maximum_cursor_size); + params.year_of_manufacture, params.maximum_cursor_size, + params.has_associated_crtc); } int GetFourCCFormatForOpaqueFramebuffer(gfx::BufferFormat format) {
diff --git a/ui/ozone/platform/drm/common/drm_util.h b/ui/ozone/platform/drm/common/drm_util.h index e70c23a8..7166f588 100644 --- a/ui/ozone/platform/drm/common/drm_util.h +++ b/ui/ozone/platform/drm/common/drm_util.h
@@ -30,6 +30,10 @@ namespace ui { +class HardwareDisplayControllerInfo; +using HardwareDisplayControllerInfos = + std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>; + // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. enum class EdidColorSpaceChecksOutcome { @@ -54,8 +58,12 @@ drmModeConnector* connector() const { return connector_.get(); } drmModeCrtc* crtc() const { return crtc_.get(); } + ScopedDrmCrtcPtr release_crtc() { return std::move(crtc_); } + void set_crtc(ScopedDrmCrtcPtr crtc) { crtc_ = std::move(crtc); } size_t index() const { return index_; } + bool has_associated_crtc() const { return crtc_.get(); } + private: ScopedDrmConnectorPtr connector_; ScopedDrmCrtcPtr crtc_; @@ -65,9 +73,11 @@ }; // Looks-up and parses the native display configurations returning all available -// displays. -std::vector<std::unique_ptr<HardwareDisplayControllerInfo>> -GetAvailableDisplayControllerInfos(int fd); +// displays. The boolean value indicates whether device has enough hardware +// resource to support all of displays. +HardwareDisplayControllerInfos GetAvailableDisplayControllerInfos( + int fd, + bool* support_all_connectors); bool SameMode(const drmModeModeInfo& lhs, const drmModeModeInfo& rhs);
diff --git a/ui/ozone/platform/drm/gpu/drm_device_manager.h b/ui/ozone/platform/drm/gpu/drm_device_manager.h index f34170a3..94680e1 100644 --- a/ui/ozone/platform/drm/gpu/drm_device_manager.h +++ b/ui/ozone/platform/drm/gpu/drm_device_manager.h
@@ -49,11 +49,12 @@ const DrmDeviceVector& GetDrmDevices() const; + protected: + DrmDeviceVector devices_; + private: const std::unique_ptr<DrmDeviceGenerator> drm_device_generator_; - DrmDeviceVector devices_; - std::map<gfx::AcceleratedWidget, scoped_refptr<DrmDevice>> drm_device_map_; // This device represents the primary graphics device and is used when:
diff --git a/ui/ozone/platform/drm/gpu/drm_display.cc b/ui/ozone/platform/drm/gpu/drm_display.cc index f800442..d7b771c6 100644 --- a/ui/ozone/platform/drm/gpu/drm_display.cc +++ b/ui/ozone/platform/drm/gpu/drm_display.cc
@@ -92,7 +92,7 @@ size_t device_index) { std::unique_ptr<display::DisplaySnapshot> params = CreateDisplaySnapshot( info, drm_->get_fd(), drm_->device_path(), device_index, origin_); - crtc_ = info->crtc()->crtc_id; + crtc_ = info->has_associated_crtc() ? info->crtc()->crtc_id : 0; connector_ = info->connector()->connector_id; display_id_ = params->display_id(); modes_ = GetDrmModeVector(info->connector());
diff --git a/ui/ozone/platform/drm/gpu/drm_display.h b/ui/ozone/platform/drm/gpu/drm_display.h index 16f2dae..44ec1821 100644 --- a/ui/ozone/platform/drm/gpu/drm_display.h +++ b/ui/ozone/platform/drm/gpu/drm_display.h
@@ -39,6 +39,10 @@ scoped_refptr<DrmDevice> drm() const { return drm_; } uint32_t crtc() const { return crtc_; } uint32_t connector() const { return connector_; } + void UpdateForTesting(uint32_t connector_id, uint32_t crtc_id) { + connector_ = connector_id; + crtc_ = crtc_id; + } const std::vector<drmModeModeInfo>& modes() const { return modes_; } std::unique_ptr<display::DisplaySnapshot> Update(
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc index 232ae76..1de60873 100644 --- a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc +++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
@@ -5,12 +5,12 @@ #include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h" #include <stddef.h> +#include <utility> #include "ui/display/types/display_mode.h" #include "ui/display/types/display_snapshot.h" #include "ui/display/types/gamma_ramp_rgb_entry.h" #include "ui/ozone/common/linux/drm_util_linux.h" -#include "ui/ozone/platform/drm/common/drm_util.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_device_manager.h" #include "ui/ozone/platform/drm/gpu/drm_display.h" @@ -73,28 +73,75 @@ } MovableDisplaySnapshots DrmGpuDisplayManager::GetDisplays() { + MovableDisplaySnapshots params_list; std::vector<std::unique_ptr<DrmDisplay>> old_displays; old_displays.swap(displays_); - MovableDisplaySnapshots params_list; + HardwareDisplayControllerInfos old_hardware_infos; + old_hardware_infos.swap(hardware_infos_); const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices(); size_t device_index = 0; for (const auto& drm : devices) { - auto display_infos = GetAvailableDisplayControllerInfos(drm->get_fd()); - for (const auto& display_info : display_infos) { - auto it = std::find_if( - old_displays.begin(), old_displays.end(), - DisplayComparator(drm, display_info->crtc()->crtc_id, - display_info->connector()->connector_id)); - if (it != old_displays.end()) { - displays_.push_back(std::move(*it)); - old_displays.erase(it); - } else { - displays_.push_back(std::make_unique<DrmDisplay>(screen_manager_, drm)); + bool support_all_displays; + auto display_infos = QueryAvailableDisplayControllerInfos( + drm->get_fd(), &support_all_displays); + + // Reallocate crtc with following strategy: + // (1) if there exists any display without associated crtc, the most + // recently connected display is denied the allocated crtc. Then the + // remaining displays are configured with previously assigned crtc which is + // recorded in |old_hardware_infos|. (2) if there are sufficient crtcs for + // each display, do the normal configuration. + if (!support_all_displays) { + for (auto& display_info : display_infos) { + // Connector id is unique. For each connected display, + // check whether it is recently connected with device or not. + auto hardware_info_it = std::find_if( + old_hardware_infos.begin(), old_hardware_infos.end(), + [&display_info]( + std::unique_ptr<HardwareDisplayControllerInfo>& hardware_info) { + return display_info->connector()->connector_id == + hardware_info->connector()->connector_id; + }); + + if (hardware_info_it == old_hardware_infos.end()) { + // |display_info| corresponds to the most recently connected display. + display_info->set_crtc(nullptr); + } else { + // |display_info| corresponds to the display which has been connected + // with device before. + auto display_it = + std::find_if(old_displays.begin(), old_displays.end(), + DisplayComparator( + drm, (*hardware_info_it)->crtc()->crtc_id, + (*hardware_info_it)->connector()->connector_id)); + DCHECK(display_it != old_displays.end()); + displays_.push_back(std::move(*display_it)); + old_displays.erase(display_it); + display_info->set_crtc((*hardware_info_it)->release_crtc()); + old_hardware_infos.erase(hardware_info_it); + } } - params_list.push_back( - displays_.back()->Update(display_info.get(), device_index)); + } else { + for (auto& display_info : display_infos) { + auto it = std::find_if( + old_displays.begin(), old_displays.end(), + DisplayComparator(drm, display_info->crtc()->crtc_id, + display_info->connector()->connector_id)); + if (it != old_displays.end()) { + displays_.push_back(std::move(*it)); + old_displays.erase(it); + } else { + displays_.push_back( + std::make_unique<DrmDisplay>(screen_manager_, drm)); + } + } } + + MovableDisplaySnapshots sub_params_list = + GenerateParamsList(display_infos, device_index); + std::move(sub_params_list.begin(), sub_params_list.end(), + std::back_inserter(params_list)); device_index++; } @@ -226,13 +273,35 @@ display->SetGammaCorrection(degamma_lut, gamma_lut); } -DrmDisplay* DrmGpuDisplayManager::FindDisplay(int64_t display_id) { - for (const auto& display : displays_) { - if (display->display_id() == display_id) - return display.get(); +HardwareDisplayControllerInfos +DrmGpuDisplayManager::QueryAvailableDisplayControllerInfos( + int fd, + bool* support_all_displays) const { + return GetAvailableDisplayControllerInfos(fd, support_all_displays); +} + +MovableDisplaySnapshots DrmGpuDisplayManager::GenerateParamsList( + HardwareDisplayControllerInfos& display_infos, + size_t device_index) { + MovableDisplaySnapshots params_list; + auto drm = drm_device_manager_->GetDrmDevices()[device_index]; + + // Generate |param_list| with updated |displays_|. Notice that the display + // connection without crtc allocated is not included in |displays_| but + // contained in |params_list|. Because Chrome side should be notified of it. + size_t display_index = 0; + for (auto& display_info : display_infos) { + if (display_info->has_associated_crtc()) { + params_list.push_back( + displays_[display_index++]->Update(display_info.get(), device_index)); + hardware_infos_.push_back(std::move(display_info)); + } else { + params_list.push_back(std::make_unique<DrmDisplay>(screen_manager_, drm) + ->Update(display_info.get(), device_index)); + } } - return nullptr; + return params_list; } void DrmGpuDisplayManager::NotifyScreenManager( @@ -259,4 +328,13 @@ } } +DrmDisplay* DrmGpuDisplayManager::FindDisplay(int64_t display_id) { + for (const auto& display : displays_) { + if (display->display_id() == display_id) + return display.get(); + } + + return nullptr; +} + } // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h index 4a7669fa..d4f477d 100644 --- a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h +++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
@@ -14,6 +14,7 @@ #include "ui/gfx/native_widget_types.h" #include "ui/ozone/common/gpu/ozone_gpu_message_params.h" #include "ui/ozone/platform/drm/common/display_types.h" +#include "ui/ozone/platform/drm/common/drm_util.h" namespace display { class DisplayMode; @@ -30,7 +31,11 @@ public: DrmGpuDisplayManager(ScreenManager* screen_manager, DrmDeviceManager* drm_device_manager); - ~DrmGpuDisplayManager(); + virtual ~DrmGpuDisplayManager(); + + const HardwareDisplayControllerInfos& hardware_infos_for_test() const { + return hardware_infos_; + } // Returns a list of the connected displays. When this is called the list of // displays is refreshed. @@ -55,20 +60,35 @@ const std::vector<display::GammaRampRGBEntry>& degamma_lut, const std::vector<display::GammaRampRGBEntry>& gamma_lut); - private: - DrmDisplay* FindDisplay(int64_t display_id); + protected: + // Virtual for testing. + virtual HardwareDisplayControllerInfos QueryAvailableDisplayControllerInfos( + int fd, + bool* support_all_displays) const; + + // Virtual for testing. + virtual MovableDisplaySnapshots GenerateParamsList( + HardwareDisplayControllerInfos& display_infos, + size_t device_index); // Notify ScreenManager of all the displays that were present before the // update but are gone after the update. - void NotifyScreenManager( + // Virtual for testing. + virtual void NotifyScreenManager( const std::vector<std::unique_ptr<DrmDisplay>>& new_displays, const std::vector<std::unique_ptr<DrmDisplay>>& old_displays) const; + // List of available displays and their controller information. Only displays + // having associated crtc are included. + std::vector<std::unique_ptr<DrmDisplay>> displays_; + HardwareDisplayControllerInfos hardware_infos_; + + private: + DrmDisplay* FindDisplay(int64_t display_id); + ScreenManager* const screen_manager_; // Not owned. DrmDeviceManager* const drm_device_manager_; // Not owned. - std::vector<std::unique_ptr<DrmDisplay>> displays_; - DISALLOW_COPY_AND_ASSIGN(DrmGpuDisplayManager); };
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager_unittest.cc b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager_unittest.cc new file mode 100644 index 0000000..f6597e5 --- /dev/null +++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager_unittest.cc
@@ -0,0 +1,263 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h" + +#include <string> +#include <unordered_map> +#include <utility> + +#include "base/rand_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/ozone/platform/drm/gpu/drm_device_generator.h" +#include "ui/ozone/platform/drm/gpu/drm_device_manager.h" +#include "ui/ozone/platform/drm/gpu/drm_display.h" +#include "ui/ozone/platform/drm/gpu/mock_drm_device.h" +#include "ui/ozone/platform/drm/gpu/mock_gbm_device.h" +#include "ui/ozone/platform/drm/gpu/screen_manager.h" + +namespace ui { + +namespace { + +constexpr int kMaxSupportDisplayNum = 2; + +class MockDrmDeviceManager : public DrmDeviceManager { + public: + MockDrmDeviceManager(std::unique_ptr<DrmDeviceGenerator> drm_device_generator) + : DrmDeviceManager(std::move(drm_device_generator)) {} + ~MockDrmDeviceManager() = default; + + void AddDrmDeviceForTesting(scoped_refptr<DrmDevice> drm_device) { + devices_.push_back(drm_device); + } +}; + +class DrmGpuDisplayManagerTestHelper { + public: + DrmGpuDisplayManagerTestHelper(size_t max_support_display_num) { + for (size_t i = 1; i <= max_support_display_num; i++) + crtc_resources_.push_back(i); + } + + HardwareDisplayControllerInfos GetAvailableDisplayControllerInfos( + int fd, + bool* support_all_displays); + + const std::unordered_map<uint32_t, uint32_t>& intermediate_mappings() const { + return intermediate_mappings_; + } + + void AddDisplay(uint32_t connector_id) { + connector_resources_.push_back(connector_id); + } + + private: + // Record the mappings between connector and crtc before crtc reallocation. + std::unordered_map<uint32_t, uint32_t> intermediate_mappings_; + + std::vector<uint32_t> connector_resources_; + std::vector<uint32_t> crtc_resources_; +}; + +HardwareDisplayControllerInfos +DrmGpuDisplayManagerTestHelper::GetAvailableDisplayControllerInfos( + int fd, + bool* support_all_displays) { + intermediate_mappings_.clear(); + *support_all_displays = true; + HardwareDisplayControllerInfos ret; + auto crtc_iter = crtc_resources_.rbegin(); + + // Pair connector with crtc in a dummy way. If there is no available crtc, the + // assigned crtc should be a null pointer. Use reverse iterator to ensure that + // the most recent connected display has associated crtc before crtc + // reallocation. It can help to test the code block of crtc reallocation. + for (auto connector_iter = connector_resources_.rbegin(); + connector_iter != connector_resources_.rend(); connector_iter++) { + drmModeConnectorPtr connector = new drmModeConnector; + connector->connector_id = *connector_iter; + connector->encoders = nullptr; + connector->prop_values = nullptr; + connector->props = nullptr; + connector->modes = nullptr; + + drmModeCrtcPtr crtc = nullptr; + if (crtc_iter != crtc_resources_.rend()) { + crtc = new drmModeCrtc; + crtc->crtc_id = *crtc_iter; + intermediate_mappings_[connector->connector_id] = crtc->crtc_id; + crtc_iter++; + } else { + *support_all_displays = false; + } + + ret.push_back(std::make_unique<HardwareDisplayControllerInfo>( + ScopedDrmConnectorPtr(connector), ScopedDrmCrtcPtr(crtc), + connector_iter - connector_resources_.rbegin())); + } + + return ret; +} + +class MockDrmGpuDisplayManager : public DrmGpuDisplayManager { + public: + MockDrmGpuDisplayManager( + std::unique_ptr<DrmGpuDisplayManagerTestHelper> test_helper, + ScreenManager* screen_manager, + MockDrmDeviceManager* device_manager) + : DrmGpuDisplayManager(screen_manager, device_manager), + test_helper_(std::move(test_helper)) {} + + // Get the mappings between connector and crtc before crtc reallocation. + const std::unordered_map<uint32_t, uint32_t>& intermediate_mappings() const { + return test_helper_->intermediate_mappings(); + } + + void AddDisplay(uint32_t connector_id) { + test_helper_->AddDisplay(connector_id); + } + + private: + HardwareDisplayControllerInfos QueryAvailableDisplayControllerInfos( + int fd, + bool* support_all_displays) const override { + return test_helper_->GetAvailableDisplayControllerInfos( + fd, support_all_displays); + } + + MovableDisplaySnapshots GenerateParamsList( + HardwareDisplayControllerInfos& display_infos, + size_t device_index) override; + + // In test do nothing. + void NotifyScreenManager( + const std::vector<std::unique_ptr<DrmDisplay>>& new_displays, + const std::vector<std::unique_ptr<DrmDisplay>>& old_displays) + const override {} + + std::unique_ptr<DrmGpuDisplayManagerTestHelper> test_helper_; +}; + +MovableDisplaySnapshots MockDrmGpuDisplayManager::GenerateParamsList( + HardwareDisplayControllerInfos& display_infos, + size_t device_index) { + MovableDisplaySnapshots params_list; + + size_t display_index = 0; + for (auto& display_info : display_infos) { + if (display_info->has_associated_crtc()) { + displays_[display_index++]->UpdateForTesting( + display_info->connector()->connector_id, + display_info->crtc()->crtc_id); + hardware_infos_.push_back(std::move(display_info)); + } + + // Generate dummy snapshots. + params_list.push_back(std::make_unique<display::DisplaySnapshot>( + /*display_id=*/display::kInvalidDisplayId, /*origin=*/gfx::Point(), + /*physical_size=*/gfx::Size(), + /*type=*/display::DisplayConnectionType(), + /*is_aspect_preserving_scaling=*/false, /*has_overscan=*/false, + /*has_color_correction_matrix=*/false, + /*color_correction_in_linear_space=*/false, + /*color_space=*/gfx::ColorSpace(), /*display_name=*/std::string(), + /*sys_path=*/base::FilePath(), + /*modes=*/display::DisplaySnapshot::DisplayModeList(), + /*edid=*/std::vector<uint8_t>(), /*current_mode=*/nullptr, + /*native_mode=*/nullptr, /*product_code=*/0, + /*year_of_manufacture=*/display::kInvalidYearOfManufacture, + /*maximum_cursor_size=*/gfx::Size(), /*has_associated_crtc=*/true)); + } + + return params_list; +} + +} // namespace + +class DrmGpuDisplayManagerTest : public testing::Test { + public: + DrmGpuDisplayManagerTest() {} + ~DrmGpuDisplayManagerTest() override {} + + // From testing::Test. + void SetUp() override; + void TearDown() override { + screen_manager_.reset(); + device_manager_.reset(); + drm_gpu_display_manager_.reset(); + } + + protected: + std::unique_ptr<ScreenManager> screen_manager_; + std::unique_ptr<MockDrmDeviceManager> device_manager_; + std::unique_ptr<MockDrmGpuDisplayManager> drm_gpu_display_manager_; + + private: + DISALLOW_COPY_AND_ASSIGN(DrmGpuDisplayManagerTest); +}; + +void DrmGpuDisplayManagerTest::SetUp() { + auto gbm = std::make_unique<ui::MockGbmDevice>(); + MockDrmDevice* device = new MockDrmDevice(std::move(gbm)); + scoped_refptr<DrmDevice> drm_device(device); + + device_manager_.reset(new MockDrmDeviceManager(nullptr)); + device_manager_->AddDrmDeviceForTesting(drm_device); + screen_manager_.reset(new ScreenManager()); + + size_t max_support_display_num = kMaxSupportDisplayNum; + drm_gpu_display_manager_.reset(new MockDrmGpuDisplayManager( + std::make_unique<DrmGpuDisplayManagerTestHelper>(max_support_display_num), + screen_manager_.get(), device_manager_.get())); +} + +TEST_F(DrmGpuDisplayManagerTest, GetDisplays) { + const uint32_t display_id1 = 1, display_id2 = 2, display_id3 = 3; + + // Connect two displays with device then update |hardware_infos_|. + drm_gpu_display_manager_->AddDisplay(display_id1); + drm_gpu_display_manager_->AddDisplay(display_id2); + MovableDisplaySnapshots display_snapshots1 = + drm_gpu_display_manager_->GetDisplays(); + EXPECT_EQ(display_snapshots1.size(), 2u); + + std::unordered_map<uint32_t, uint32_t> connector_crtc_map; + + // Record the mappings between connector and crtc. + const auto& hardware_infos = + drm_gpu_display_manager_->hardware_infos_for_test(); + size_t connected_display_size = hardware_infos.size(); + for (const auto& hardware_info : hardware_infos) { + uint32_t connector_id = hardware_info->connector()->connector_id, + crtc_id = hardware_info->crtc()->crtc_id; + connector_crtc_map[connector_id] = crtc_id; + } + + drm_gpu_display_manager_->AddDisplay(display_id3); + MovableDisplaySnapshots display_snapshots2 = + drm_gpu_display_manager_->GetDisplays(); + EXPECT_EQ(display_snapshots2.size(), 3u); + + // The third display has associated crtc before crtc reallocation. + const auto& iter = + drm_gpu_display_manager_->intermediate_mappings().find(display_id3); + EXPECT_EQ(iter->second, 2u); + + // Note that device only supports at most two displays. So the third one + // should not have associated crtc after reallocation (see comments in + // DrmGpuDisplayManager::GetDisplays for more details). |hardware_infos|, + // recording information of connected displays, should not change. + const auto& updated_hardware_infos = + drm_gpu_display_manager_->hardware_infos_for_test(); + EXPECT_EQ(connected_display_size, updated_hardware_infos.size()); + for (const auto& hardware_info : updated_hardware_infos) { + uint32_t connector_id = hardware_info->connector()->connector_id, + crtc_id = hardware_info->crtc()->crtc_id; + + uint32_t expected_crtc_id = connector_crtc_map[connector_id]; + EXPECT_EQ(crtc_id, expected_crtc_id); + } +} +} // namespace ui
diff --git a/ui/ozone/platform/drm/host/drm_display_host_manager.cc b/ui/ozone/platform/drm/host/drm_display_host_manager.cc index 6aaee53..cbed94f 100644 --- a/ui/ozone/platform/drm/host/drm_display_host_manager.cc +++ b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
@@ -146,8 +146,8 @@ proxy_->RegisterHandlerForDrmDisplayHostManager(this); proxy_->AddGpuThreadObserver(this); - auto display_infos = - GetAvailableDisplayControllerInfos(primary_drm_device_handle_->fd()); + const auto& display_infos = GetAvailableDisplayControllerInfos( + primary_drm_device_handle_->fd(), nullptr); has_dummy_display_ = !display_infos.empty(); for (const auto& display_info : display_infos) { displays_.push_back(std::make_unique<DrmDisplayHost>(
diff --git a/ui/ozone/platform/headless/headless_native_display_delegate.cc b/ui/ozone/platform/headless/headless_native_display_delegate.cc index a4ca1d32..0fa6a8d 100644 --- a/ui/ozone/platform/headless/headless_native_display_delegate.cc +++ b/ui/ozone/platform/headless/headless_native_display_delegate.cc
@@ -38,7 +38,8 @@ next_display_id(), gfx::Point(0, 0), kDefaultWindowSize, display::DisplayConnectionType::DISPLAY_CONNECTION_TYPE_NONE, false, false, false, false, gfx::ColorSpace(), "", base::FilePath(), - std::move(modes), std::vector<uint8_t>(), mode, mode, 0, 0, gfx::Size()); + std::move(modes), std::vector<uint8_t>(), mode, mode, 0, 0, gfx::Size(), + /*has_associated_crtc=*/true); for (display::NativeDisplayObserver& observer : observers_) observer.OnConfigurationChanged();
diff --git a/ui/views/controls/prefix_selector.cc b/ui/views/controls/prefix_selector.cc index fe8decc9..033778ef 100644 --- a/ui/views/controls/prefix_selector.cc +++ b/ui/views/controls/prefix_selector.cc
@@ -4,8 +4,13 @@ #include "ui/views/controls/prefix_selector.h" +#if defined(OS_WIN) +#include <vector> +#endif + #include "base/i18n/case_conversion.h" #include "base/time/default_tick_clock.h" +#include "build/build_config.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/text_input_type.h" #include "ui/gfx/range/range.h" @@ -164,6 +169,12 @@ return false; } +#if defined(OS_WIN) +void PrefixSelector::SetCompositionFromExistingText( + const gfx::Range& range, + const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) {} +#endif + void PrefixSelector::OnTextInput(const base::string16& text) { // Small hack to filter out 'tab' and 'enter' input, as the expectation is // that they are control characters and will not affect the currently-active
diff --git a/ui/views/controls/prefix_selector.h b/ui/views/controls/prefix_selector.h index de9caa3..7637fcd9 100644 --- a/ui/views/controls/prefix_selector.h +++ b/ui/views/controls/prefix_selector.h
@@ -8,9 +8,14 @@ #include <stddef.h> #include <stdint.h> +#if defined(OS_WIN) +#include <vector> +#endif + #include "base/macros.h" #include "base/strings/string16.h" #include "base/time/time.h" +#include "build/build_config.h" #include "ui/base/ime/text_input_client.h" #include "ui/views/views_export.h" @@ -71,6 +76,13 @@ ukm::SourceId GetClientSourceForMetrics() const override; bool ShouldDoLearning() override; +#if defined(OS_WIN) + // Overridden from ui::TextInputClient(Windows only): + void SetCompositionFromExistingText( + const gfx::Range& range, + const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) override; +#endif + void set_tick_clock_for_testing(const base::TickClock* clock) { tick_clock_ = clock; }
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc index 850143f..0ef9830a 100644 --- a/ui/views/controls/textfield/textfield.cc +++ b/ui/views/controls/textfield/textfield.cc
@@ -7,6 +7,10 @@ #include <string> #include <utility> +#if defined(OS_WIN) +#include <vector> +#endif + #include "base/bind.h" #include "base/command_line.h" #include "base/strings/utf_string_conversions.h" @@ -1770,6 +1774,14 @@ return false; } +#if defined(OS_WIN) +// TODO(IME): Implement this method to support Korean IME reconversion feature +// on native text fields (e.g. find bar). +void Textfield::SetCompositionFromExistingText( + const gfx::Range& range, + const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) {} +#endif + //////////////////////////////////////////////////////////////////////////////// // Textfield, protected:
diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h index 0707ac7e..161c0a1e 100644 --- a/ui/views/controls/textfield/textfield.h +++ b/ui/views/controls/textfield/textfield.h
@@ -11,6 +11,10 @@ #include <memory> #include <string> +#if defined(OS_WIN) +#include <vector> +#endif + #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" @@ -364,6 +368,13 @@ ukm::SourceId GetClientSourceForMetrics() const override; bool ShouldDoLearning() override; +#if defined(OS_WIN) + // Overridden from ui::TextInputClient(Windows only): + void SetCompositionFromExistingText( + const gfx::Range& range, + const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) override; +#endif + protected: // Inserts or appends a character in response to an IME operation. virtual void DoInsertChar(base::char16 ch);