diff --git a/chrome/browser/devtools/device/adb/adb_client_socket.cc b/chrome/browser/devtools/device/adb/adb_client_socket.cc index 98da348d..bd9f447 100644 --- a/chrome/browser/devtools/device/adb/adb_client_socket.cc +++ b/chrome/browser/devtools/device/adb/adb_client_socket.cc
@@ -212,7 +212,7 @@ destination: LOCAL } policy { - cookies_allowed: false + cookies_allowed: NO setting: "To use adb with a device connected over USB, you must enable USB " "debugging in the device system settings, under Developer options."
diff --git a/chrome/browser/search_engines/ui_thread_search_terms_data.cc b/chrome/browser/search_engines/ui_thread_search_terms_data.cc index a25f3fed..d80a711 100644 --- a/chrome/browser/search_engines/ui_thread_search_terms_data.cc +++ b/chrome/browser/search_engines/ui_thread_search_terms_data.cc
@@ -68,15 +68,11 @@ BrowserThread::CurrentlyOn(BrowserThread::UI)); base::string16 rlz_string; #if BUILDFLAG(ENABLE_RLZ) - static std::string* brand = []() { - auto* extracted = new std::string(); - if (!google_brand::GetBrand(extracted)) - extracted->clear(); - return extracted; - }(); // For organic brandcodes do not use rlz at all. Empty brandcode usually // means a chromium install. This is ok. - if (!brand->empty() && !google_brand::IsOrganic(*brand)) { + std::string brand; + if (google_brand::GetBrand(&brand) && !brand.empty() && + !google_brand::IsOrganic(brand)) { // This call will return false the first time(s) it is called until the // value has been cached. This normally would mean that at most one omnibox // search might not send the RLZ data but this is not really a problem.
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 07e4c001..d64d6e9 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -899,8 +899,6 @@ OnJavaScriptExecuteResponse) IPC_MESSAGE_HANDLER(FrameHostMsg_VisualStateResponse, OnVisualStateResponse) - IPC_MESSAGE_HANDLER(FrameHostMsg_SmartClipDataExtracted, - OnSmartClipDataExtracted) IPC_MESSAGE_HANDLER_DELAY_REPLY(FrameHostMsg_RunJavaScriptDialog, OnRunJavaScriptDialog) IPC_MESSAGE_HANDLER_DELAY_REPLY(FrameHostMsg_RunBeforeUnloadConfirm, @@ -1935,11 +1933,17 @@ for (const auto& iter : ax_tree_snapshot_callbacks_) iter.second.Run(ui::AXTreeUpdate()); - for (const auto& iter : smart_clip_callbacks_) - iter.second.Run(base::string16(), base::string16()); +#if defined(OS_ANDROID) + // Execute any pending Samsung smart clip callbacks. + for (base::IDMap<std::unique_ptr<ExtractSmartClipDataCallback>>::iterator + iter(&smart_clip_callbacks_); + !iter.IsAtEnd(); iter.Advance()) { + std::move(*iter.GetCurrentValue()).Run(base::string16(), base::string16()); + } + smart_clip_callbacks_.Clear(); +#endif // defined(OS_ANDROID) ax_tree_snapshot_callbacks_.clear(); - smart_clip_callbacks_.clear(); javascript_callbacks_.clear(); visual_state_callbacks_.clear(); @@ -2046,25 +2050,24 @@ } } -void RenderFrameHostImpl::RequestSmartClipExtract(SmartClipCallback callback, - gfx::Rect rect) { - static uint32_t next_id = 1; - uint32_t key = next_id++; - Send(new FrameMsg_ExtractSmartClipData(routing_id_, key, rect)); - smart_clip_callbacks_.insert(std::make_pair(key, callback)); +#if defined(OS_ANDROID) +void RenderFrameHostImpl::RequestSmartClipExtract( + ExtractSmartClipDataCallback callback, + gfx::Rect rect) { + int32_t callback_id = smart_clip_callbacks_.Add( + std::make_unique<ExtractSmartClipDataCallback>(std::move(callback))); + frame_->ExtractSmartClipData( + rect, base::BindOnce(&RenderFrameHostImpl::OnSmartClipDataExtracted, + base::Unretained(this), callback_id)); } -void RenderFrameHostImpl::OnSmartClipDataExtracted(uint32_t id, - base::string16 text, - base::string16 html) { - auto it = smart_clip_callbacks_.find(id); - if (it != smart_clip_callbacks_.end()) { - it->second.Run(text, html); - smart_clip_callbacks_.erase(it); - } else { - NOTREACHED() << "Received smartclip data response for unknown request"; - } +void RenderFrameHostImpl::OnSmartClipDataExtracted(int32_t callback_id, + const base::string16& text, + const base::string16& html) { + std::move(*smart_clip_callbacks_.Lookup(callback_id)).Run(text, html); + smart_clip_callbacks_.Remove(callback_id); } +#endif // defined(OS_ANDROID) void RenderFrameHostImpl::OnVisualStateResponse(uint64_t id) { auto it = visual_state_callbacks_.find(id);
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h index f00ec001..6ab79b6fe 100644 --- a/content/browser/frame_host/render_frame_host_impl.h +++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -18,6 +18,7 @@ #include "base/callback.h" #include "base/compiler_specific.h" +#include "base/containers/id_map.h" #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" @@ -146,8 +147,6 @@ using AXTreeSnapshotCallback = base::Callback<void( const ui::AXTreeUpdate&)>; - using SmartClipCallback = base::Callback<void(const base::string16& text, - const base::string16& html)>; // An accessibility reset is only allowed to prevent very rare corner cases // or race conditions where the browser and renderer get out of sync. If @@ -502,8 +501,18 @@ // renderer process to change the accessibility mode. void UpdateAccessibilityMode(); +#if defined(OS_ANDROID) // Samsung Galaxy Note-specific "smart clip" stylus text getter. - void RequestSmartClipExtract(SmartClipCallback callback, gfx::Rect rect); + using ExtractSmartClipDataCallback = + base::OnceCallback<void(const base::string16&, const base::string16&)>; + + void RequestSmartClipExtract(ExtractSmartClipDataCallback callback, + gfx::Rect rect); + + void OnSmartClipDataExtracted(int32_t callback_id, + const base::string16& text, + const base::string16& html); +#endif // defined(OS_ANDROID) // Request a one-time snapshot of the accessibility tree without changing // the accessibility mode. @@ -832,9 +841,6 @@ void OnAccessibilitySnapshotResponse( int callback_id, const AXContentTreeUpdate& snapshot); - void OnSmartClipDataExtracted(uint32_t id, - base::string16 text, - base::string16 html); void OnToggleFullscreen(bool enter_fullscreen); void OnSuddenTerminationDisablerChanged( bool present, @@ -1250,7 +1256,10 @@ std::map<int, AXTreeSnapshotCallback> ax_tree_snapshot_callbacks_; // Samsung Galaxy Note-specific "smart clip" stylus text getter. - std::map<uint32_t, SmartClipCallback> smart_clip_callbacks_; +#if defined(OS_ANDROID) + base::IDMap<std::unique_ptr<ExtractSmartClipDataCallback>> + smart_clip_callbacks_; +#endif // defined(OS_ANDROID) // Callback when an event is received, for testing. base::Callback<void(RenderFrameHostImpl*, ui::AXEvent, int)>
diff --git a/content/browser/web_contents/web_contents_android.cc b/content/browser/web_contents/web_contents_android.cc index 074ad70..0b1f5c2 100644 --- a/content/browser/web_contents/web_contents_android.cc +++ b/content/browser/web_contents/web_contents_android.cc
@@ -5,6 +5,8 @@ #include "content/browser/web_contents/web_contents_android.h" #include <stdint.h> +#include <string> +#include <vector> #include "base/android/jni_android.h" #include "base/android/jni_array.h" @@ -27,6 +29,7 @@ #include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/web_contents/web_contents_view_android.h" #include "content/common/devtools_messages.h" +#include "content/common/frame.mojom.h" #include "content/common/frame_messages.h" #include "content/common/input_messages.h" #include "content/common/view_messages.h" @@ -558,11 +561,9 @@ ScopedJavaGlobalRef<jobject> j_callback; j_callback.Reset(env, callback); - RenderFrameHostImpl::SmartClipCallback smart_clip_callback = - base::Bind(&SmartClipCallback, j_callback); - web_contents_->GetMainFrame()->RequestSmartClipExtract( - smart_clip_callback, gfx::Rect(x, y, width, height)); + base::BindOnce(&SmartClipCallback, j_callback), + gfx::Rect(x, y, width, height)); } void WebContentsAndroid::RequestAccessibilitySnapshot(
diff --git a/content/common/frame.mojom b/content/common/frame.mojom index b5589cf..08f9728c 100644 --- a/content/common/frame.mojom +++ b/content/common/frame.mojom
@@ -10,12 +10,14 @@ import "content/public/common/url_loader.mojom"; import "content/public/common/window_container_type.mojom"; import "mojo/common/unguessable_token.mojom"; +import "mojo/common/string16.mojom"; import "services/service_manager/public/interfaces/interface_provider.mojom"; import "third_party/WebKit/common/feature_policy/feature_policy.mojom"; import "third_party/WebKit/public/platform/referrer.mojom"; import "third_party/WebKit/public/web/window_features.mojom"; import "ui/base/mojo/window_open_disposition.mojom"; import "url/mojo/url.mojom"; +import "ui/gfx/geometry/mojo/geometry.mojom"; // The name of the InterfaceProviderSpec in service manifests used by the // frame tree to expose frame-specific interfaces between renderer and browser. @@ -25,6 +27,12 @@ interface Frame { GetInterfaceProvider(service_manager.mojom.InterfaceProvider& interfaces); GetCanonicalUrlForSharing() => (url.mojom.Url? canonical_url); + + // Samsung Galaxy Note-specific "smart clip" stylus text getter. + // Extracts the data at the given rect. + // TODO(crbug.com/676224): Only enable for OS_ANDROID. + ExtractSmartClipData(gfx.mojom.Rect rect) + => (mojo.common.mojom.String16 text, mojo.common.mojom.String16 html); }; // See src/content/common/navigation_params.h
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h index 4628f0a..8ba6261 100644 --- a/content/common/frame_messages.h +++ b/content/common/frame_messages.h
@@ -834,12 +834,6 @@ IPC_MESSAGE_ROUTED1(FrameMsg_TextSurroundingSelectionRequest, uint32_t /* max_length */) -// Extracts the data at the given rect, returning it through the -// SmartClipDataExtracted IPC. -IPC_MESSAGE_ROUTED2(FrameMsg_ExtractSmartClipData, - uint32_t /* id */, - gfx::Rect /* rect */) - // Change the accessibility mode in the renderer process. IPC_MESSAGE_ROUTED1(FrameMsg_SetAccessibilityMode, ui::AXMode) @@ -1554,12 +1548,6 @@ // The message is delivered using RenderWidget::QueueMessage. IPC_MESSAGE_ROUTED1(FrameHostMsg_VisualStateResponse, uint64_t /* id */) -// Reply to the ExtractSmartClipData message. -IPC_MESSAGE_ROUTED3(FrameHostMsg_SmartClipDataExtracted, - uint32_t /* id */, - base::string16 /* text */, - base::string16 /* html */) - // Puts the browser into "tab fullscreen" mode for the sending renderer. // See the comment in chrome/browser/ui/browser.h for more details. IPC_MESSAGE_ROUTED1(FrameHostMsg_ToggleFullscreen, bool /* enter_fullscreen */)
diff --git a/content/renderer/loader/url_loader_client_impl.cc b/content/renderer/loader/url_loader_client_impl.cc index a38faf48..e2d628e 100644 --- a/content/renderer/loader/url_loader_client_impl.cc +++ b/content/renderer/loader/url_loader_client_impl.cc
@@ -4,6 +4,8 @@ #include "content/renderer/loader/url_loader_client_impl.h" +#include <iterator> + #include "base/callback.h" #include "base/single_thread_task_runner.h" #include "content/common/resource_messages.h" @@ -13,6 +15,112 @@ namespace content { +class URLLoaderClientImpl::DeferredMessage { + public: + DeferredMessage() = default; + virtual void HandleMessage(ResourceDispatcher* dispatcher, + int request_id) = 0; + virtual bool IsCompletionMessage() const = 0; + virtual ~DeferredMessage() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(DeferredMessage); +}; + +class URLLoaderClientImpl::DeferredOnReceiveResponse final + : public DeferredMessage { + public: + explicit DeferredOnReceiveResponse(const ResourceResponseHead& response_head) + : response_head_(response_head) {} + + void HandleMessage(ResourceDispatcher* dispatcher, int request_id) override { + dispatcher->OnReceivedResponse(request_id, response_head_); + } + bool IsCompletionMessage() const override { return false; } + + private: + const ResourceResponseHead response_head_; +}; + +class URLLoaderClientImpl::DeferredOnReceiveRedirect final + : public DeferredMessage { + public: + DeferredOnReceiveRedirect(const net::RedirectInfo& redirect_info, + const ResourceResponseHead& response_head) + : redirect_info_(redirect_info), response_head_(response_head) {} + + void HandleMessage(ResourceDispatcher* dispatcher, int request_id) override { + dispatcher->OnReceivedRedirect(request_id, redirect_info_, response_head_); + } + bool IsCompletionMessage() const override { return false; } + + private: + const net::RedirectInfo redirect_info_; + const ResourceResponseHead response_head_; +}; + +class URLLoaderClientImpl::DeferredOnDataDownloaded final + : public DeferredMessage { + public: + DeferredOnDataDownloaded(int64_t data_length, int64_t encoded_data_length) + : data_length_(data_length), encoded_data_length_(encoded_data_length) {} + + void HandleMessage(ResourceDispatcher* dispatcher, int request_id) override { + dispatcher->OnDownloadedData(request_id, data_length_, + encoded_data_length_); + } + bool IsCompletionMessage() const override { return false; } + + private: + const int64_t data_length_; + const int64_t encoded_data_length_; +}; + +class URLLoaderClientImpl::DeferredOnUploadProgress final + : public DeferredMessage { + public: + DeferredOnUploadProgress(int64_t current, int64_t total) + : current_(current), total_(total) {} + + void HandleMessage(ResourceDispatcher* dispatcher, int request_id) override { + dispatcher->OnDownloadedData(request_id, current_, total_); + } + bool IsCompletionMessage() const override { return false; } + + private: + const int64_t current_; + const int64_t total_; +}; + +class URLLoaderClientImpl::DeferredOnReceiveCachedMetadata final + : public DeferredMessage { + public: + explicit DeferredOnReceiveCachedMetadata(const std::vector<uint8_t>& data) + : data_(data) {} + + void HandleMessage(ResourceDispatcher* dispatcher, int request_id) override { + dispatcher->OnReceivedCachedMetadata(request_id, data_); + } + bool IsCompletionMessage() const override { return false; } + + private: + const std::vector<uint8_t> data_; +}; + +class URLLoaderClientImpl::DeferredOnComplete final : public DeferredMessage { + public: + explicit DeferredOnComplete(const network::URLLoaderCompletionStatus& status) + : status_(status) {} + + void HandleMessage(ResourceDispatcher* dispatcher, int request_id) override { + dispatcher->OnRequestComplete(request_id, status_); + } + bool IsCompletionMessage() const override { return true; } + + private: + const network::URLLoaderCompletionStatus status_; +}; + URLLoaderClientImpl::URLLoaderClientImpl( int request_id, ResourceDispatcher* resource_dispatcher, @@ -40,7 +148,7 @@ void URLLoaderClientImpl::FlushDeferredMessages() { DCHECK(!is_deferred_); - std::vector<IPC::Message> messages; + std::vector<std::unique_ptr<DeferredMessage>> messages; messages.swap(deferred_messages_); bool has_completion_message = false; base::WeakPtr<URLLoaderClientImpl> weak_this = weak_factory_.GetWeakPtr(); @@ -49,19 +157,22 @@ // - transfer size change (dispatched later) // - completion (dispatched by |body_consumer_| or dispatched later) for (size_t index = 0; index < messages.size(); ++index) { - if (messages[index].type() == ResourceMsg_RequestComplete::ID) { + if (messages[index]->IsCompletionMessage()) { // The completion message arrives at the end of the message queue. DCHECK(!has_completion_message); DCHECK_EQ(index, messages.size() - 1); has_completion_message = true; break; } - resource_dispatcher_->DispatchMessage(messages[index]); + + messages[index]->HandleMessage(resource_dispatcher_, request_id_); if (!weak_this) return; if (is_deferred_) { - deferred_messages_.insert(deferred_messages_.begin(), - messages.begin() + index + 1, messages.end()); + deferred_messages_.insert( + deferred_messages_.begin(), + std::make_move_iterator(messages.begin()) + index + 1, + std::make_move_iterator(messages.end())); return; } } @@ -77,8 +188,7 @@ if (is_deferred_) { if (has_completion_message) { DCHECK_GT(messages.size(), 0u); - DCHECK_EQ(messages.back().type(), - static_cast<uint32_t>(ResourceMsg_RequestComplete::ID)); + DCHECK(messages.back()->IsCompletionMessage()); deferred_messages_.emplace_back(std::move(messages.back())); } return; @@ -97,10 +207,8 @@ // Dispatch the completion message. if (has_completion_message) { DCHECK_GT(messages.size(), 0u); - DCHECK_EQ(messages.back().type(), - static_cast<uint32_t>(ResourceMsg_RequestComplete::ID)); - - resource_dispatcher_->DispatchMessage(messages.back()); + DCHECK(messages.back()->IsCompletionMessage()); + messages.back()->HandleMessage(resource_dispatcher_, request_id_); } } @@ -117,10 +225,12 @@ mojom::DownloadedTempFilePtr downloaded_file) { has_received_response_ = true; downloaded_file_ = std::move(downloaded_file); - if (NeedsStoringMessage()) - StoreAndDispatch(ResourceMsg_ReceivedResponse(request_id_, response_head)); - else + if (NeedsStoringMessage()) { + StoreAndDispatch( + std::make_unique<DeferredOnReceiveResponse>(response_head)); + } else { resource_dispatcher_->OnReceivedResponse(request_id_, response_head); + } } void URLLoaderClientImpl::OnReceiveRedirect( @@ -129,8 +239,8 @@ DCHECK(!has_received_response_); DCHECK(!body_consumer_); if (NeedsStoringMessage()) { - StoreAndDispatch(ResourceMsg_ReceivedRedirect(request_id_, redirect_info, - response_head)); + StoreAndDispatch(std::make_unique<DeferredOnReceiveRedirect>( + redirect_info, response_head)); } else { resource_dispatcher_->OnReceivedRedirect(request_id_, redirect_info, response_head); @@ -141,17 +251,31 @@ int64_t encoded_data_len) { if (NeedsStoringMessage()) { StoreAndDispatch( - ResourceMsg_DataDownloaded(request_id_, data_len, encoded_data_len)); + std::make_unique<DeferredOnDataDownloaded>(data_len, encoded_data_len)); } else { resource_dispatcher_->OnDownloadedData(request_id_, data_len, encoded_data_len); } } +void URLLoaderClientImpl::OnUploadProgress( + int64_t current_position, + int64_t total_size, + OnUploadProgressCallback ack_callback) { + if (NeedsStoringMessage()) { + StoreAndDispatch(std::make_unique<DeferredOnUploadProgress>( + current_position, total_size)); + } else { + resource_dispatcher_->OnUploadProgress(request_id_, current_position, + total_size); + } + std::move(ack_callback).Run(); +} + void URLLoaderClientImpl::OnReceiveCachedMetadata( const std::vector<uint8_t>& data) { if (NeedsStoringMessage()) { - StoreAndDispatch(ResourceMsg_ReceivedCachedMetadata(request_id_, data)); + StoreAndDispatch(std::make_unique<DeferredOnReceiveCachedMetadata>(data)); } else { resource_dispatcher_->OnReceivedCachedMetadata(request_id_, data); } @@ -186,7 +310,7 @@ has_received_complete_ = true; if (!body_consumer_) { if (NeedsStoringMessage()) { - StoreAndDispatch(ResourceMsg_RequestComplete(request_id_, status)); + StoreAndDispatch(std::make_unique<DeferredOnComplete>(status)); } else { resource_dispatcher_->OnRequestComplete(request_id_, status); } @@ -199,32 +323,19 @@ return is_deferred_ || deferred_messages_.size() > 0; } -void URLLoaderClientImpl::StoreAndDispatch(const IPC::Message& message) { +void URLLoaderClientImpl::StoreAndDispatch( + std::unique_ptr<DeferredMessage> message) { DCHECK(NeedsStoringMessage()); if (is_deferred_) { - deferred_messages_.push_back(message); + deferred_messages_.push_back(std::move(message)); } else if (deferred_messages_.size() > 0) { - deferred_messages_.push_back(message); + deferred_messages_.push_back(std::move(message)); FlushDeferredMessages(); } else { NOTREACHED(); } } -void URLLoaderClientImpl::OnUploadProgress( - int64_t current_position, - int64_t total_size, - OnUploadProgressCallback ack_callback) { - if (NeedsStoringMessage()) { - StoreAndDispatch( - ResourceMsg_UploadProgress(request_id_, current_position, total_size)); - } else { - resource_dispatcher_->OnUploadProgress(request_id_, current_position, - total_size); - } - std::move(ack_callback).Run(); -} - void URLLoaderClientImpl::OnConnectionClosed() { // If the connection aborts before the load completes, mark it as aborted. if (!has_received_complete_) {
diff --git a/content/renderer/loader/url_loader_client_impl.h b/content/renderer/loader/url_loader_client_impl.h index 7497bbe..26ce1d2 100644 --- a/content/renderer/loader/url_loader_client_impl.h +++ b/content/renderer/loader/url_loader_client_impl.h
@@ -12,7 +12,6 @@ #include "base/memory/weak_ptr.h" #include "content/common/content_export.h" #include "content/public/common/url_loader.mojom.h" -#include "ipc/ipc_message.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/system/data_pipe.h" @@ -75,13 +74,21 @@ void OnComplete(const network::URLLoaderCompletionStatus& status) override; private: + class DeferredMessage; + class DeferredOnReceiveResponse; + class DeferredOnReceiveRedirect; + class DeferredOnDataDownloaded; + class DeferredOnUploadProgress; + class DeferredOnReceiveCachedMetadata; + class DeferredOnComplete; + bool NeedsStoringMessage() const; - void StoreAndDispatch(const IPC::Message& message); + void StoreAndDispatch(std::unique_ptr<DeferredMessage> message); void OnConnectionClosed(); scoped_refptr<URLResponseBodyConsumer> body_consumer_; mojom::DownloadedTempFilePtr downloaded_file_; - std::vector<IPC::Message> deferred_messages_; + std::vector<std::unique_ptr<DeferredMessage>> deferred_messages_; const int request_id_; bool has_received_response_ = false; bool has_received_complete_ = false;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 5c03598c8..7e5d0a19 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -1777,7 +1777,6 @@ OnSetAccessibilityMode) IPC_MESSAGE_HANDLER(AccessibilityMsg_SnapshotTree, OnSnapshotAccessibilityTree) - IPC_MESSAGE_HANDLER(FrameMsg_ExtractSmartClipData, OnExtractSmartClipData) IPC_MESSAGE_HANDLER(FrameMsg_UpdateOpener, OnUpdateOpener) IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateFramePolicy, OnDidUpdateFramePolicy) IPC_MESSAGE_HANDLER(FrameMsg_SetFrameOwnerProperties, @@ -2407,13 +2406,15 @@ routing_id_, callback_id, response)); } -void RenderFrameImpl::OnExtractSmartClipData(uint32_t id, - const gfx::Rect& rect) { +void RenderFrameImpl::ExtractSmartClipData( + const gfx::Rect& rect, + ExtractSmartClipDataCallback callback) { +#if defined(OS_ANDROID) blink::WebString clip_text; blink::WebString clip_html; GetWebFrame()->ExtractSmartClipData(rect, clip_text, clip_html); - Send(new FrameHostMsg_SmartClipDataExtracted( - routing_id_, id, clip_text.Utf16(), clip_html.Utf16())); + std::move(callback).Run(clip_text.Utf16(), clip_html.Utf16()); +#endif // defined(OS_ANDROID) } void RenderFrameImpl::OnUpdateOpener(int opener_routing_id) {
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h index a96be169..85d16b2 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h
@@ -515,6 +515,9 @@ service_manager::mojom::InterfaceProviderRequest request) override; void GetCanonicalUrlForSharing( GetCanonicalUrlForSharingCallback callback) override; + void ExtractSmartClipData( + const gfx::Rect& rect, + const ExtractSmartClipDataCallback callback) override; // mojom::FrameBindingsControl implementation: void AllowBindings(int32_t enabled_bindings_flags) override; @@ -1028,7 +1031,6 @@ void OnTextSurroundingSelectionRequest(uint32_t max_length); void OnSetAccessibilityMode(ui::AXMode new_mode); void OnSnapshotAccessibilityTree(int callback_id); - void OnExtractSmartClipData(uint32_t callback_id, const gfx::Rect& rect); void OnUpdateOpener(int opener_routing_id); void OnDidUpdateFramePolicy(const blink::FramePolicy& frame_policy); void OnSetFrameOwnerProperties(
diff --git a/media/mojo/clients/mojo_audio_decoder.cc b/media/mojo/clients/mojo_audio_decoder.cc index 94a86b3..3bfc626 100644 --- a/media/mojo/clients/mojo_audio_decoder.cc +++ b/media/mojo/clients/mojo_audio_decoder.cc
@@ -24,6 +24,8 @@ mojom::AudioDecoderPtr remote_decoder) : task_runner_(task_runner), remote_decoder_info_(remote_decoder.PassInterface()), + writer_capacity_( + GetDefaultDecoderBufferConverterCapacity(DemuxerStream::AUDIO)), client_binding_(this) { DVLOG(1) << __func__; } @@ -179,7 +181,8 @@ if (success && !mojo_decoder_buffer_writer_) { mojo::ScopedDataPipeConsumerHandle remote_consumer_handle; mojo_decoder_buffer_writer_ = MojoDecoderBufferWriter::Create( - DemuxerStream::AUDIO, &remote_consumer_handle); + writer_capacity_, &remote_consumer_handle); + // Pass consumer end to |remote_decoder_|. remote_decoder_->SetDataSource(std::move(remote_consumer_handle)); }
diff --git a/media/mojo/clients/mojo_audio_decoder.h b/media/mojo/clients/mojo_audio_decoder.h index 11dc76e..8b23e33 100644 --- a/media/mojo/clients/mojo_audio_decoder.h +++ b/media/mojo/clients/mojo_audio_decoder.h
@@ -44,6 +44,10 @@ // AudioDecoderClient implementation. void OnBufferDecoded(mojom::AudioBufferPtr buffer) final; + void set_writer_capacity_for_testing(uint32_t capacity) { + writer_capacity_ = capacity; + } + private: void BindRemoteDecoder(); @@ -71,6 +75,8 @@ std::unique_ptr<MojoDecoderBufferWriter> mojo_decoder_buffer_writer_; + uint32_t writer_capacity_ = 0; + // Binding for AudioDecoderClient, bound to the |task_runner_|. mojo::AssociatedBinding<AudioDecoderClient> client_binding_;
diff --git a/media/mojo/clients/mojo_audio_decoder_unittest.cc b/media/mojo/clients/mojo_audio_decoder_unittest.cc index 8f8ba11..0a49100 100644 --- a/media/mojo/clients/mojo_audio_decoder_unittest.cc +++ b/media/mojo/clients/mojo_audio_decoder_unittest.cc
@@ -123,6 +123,10 @@ std::move(request)); } + void SetWriterCapacity(uint32_t capacity) { + mojo_audio_decoder_->set_writer_capacity_for_testing(capacity); + } + void InitializeAndExpect(bool success) { DVLOG(1) << __func__ << ": success=" << success; EXPECT_CALL(*this, OnInitialized(success)) @@ -259,7 +263,13 @@ TEST_F(MojoAudioDecoderTest, Reset_DuringDecode) { Initialize(); + DecodeAndReset(); +} +TEST_F(MojoAudioDecoderTest, Reset_DuringDecode_ChunkedWrite) { + // Use a small writer capacity to force chunked write. + SetWriterCapacity(10); + Initialize(); DecodeAndReset(); }
diff --git a/media/mojo/clients/mojo_decryptor.cc b/media/mojo/clients/mojo_decryptor.cc index b9da44ae..9a56cb9 100644 --- a/media/mojo/clients/mojo_decryptor.cc +++ b/media/mojo/clients/mojo_decryptor.cc
@@ -33,26 +33,48 @@ } // namespace -MojoDecryptor::MojoDecryptor(mojom::DecryptorPtr remote_decryptor) +// TODO(xhwang): Consider adding an Initialize() to reduce the amount of work +// done in the constructor. +MojoDecryptor::MojoDecryptor(mojom::DecryptorPtr remote_decryptor, + uint32_t writer_capacity) : remote_decryptor_(std::move(remote_decryptor)), weak_factory_(this) { DVLOG(1) << __func__; - // Allocate DataPipe size based on video content. + uint32_t audio_writer_capacity = + writer_capacity + ? writer_capacity + : GetDefaultDecoderBufferConverterCapacity(DemuxerStream::AUDIO); + uint32_t video_writer_capacity = + writer_capacity + ? writer_capacity + : GetDefaultDecoderBufferConverterCapacity(DemuxerStream::VIDEO); - mojo::ScopedDataPipeConsumerHandle remote_consumer_handle; - mojo_decoder_buffer_writer_ = MojoDecoderBufferWriter::Create( - DemuxerStream::VIDEO, &remote_consumer_handle); + mojo::ScopedDataPipeConsumerHandle audio_consumer_handle; + audio_buffer_writer_ = MojoDecoderBufferWriter::Create( + audio_writer_capacity, &audio_consumer_handle); - mojo::ScopedDataPipeProducerHandle remote_producer_handle; - mojo_decoder_buffer_reader_ = MojoDecoderBufferReader::Create( - DemuxerStream::VIDEO, &remote_producer_handle); + mojo::ScopedDataPipeConsumerHandle video_consumer_handle; + video_buffer_writer_ = MojoDecoderBufferWriter::Create( + video_writer_capacity, &video_consumer_handle); + + mojo::ScopedDataPipeConsumerHandle decrypt_consumer_handle; + // Allocate decrypt-only DataPipe size based on video content. + decrypt_buffer_writer_ = MojoDecoderBufferWriter::Create( + video_writer_capacity, &decrypt_consumer_handle); + + mojo::ScopedDataPipeProducerHandle decrypted_producer_handle; + // Allocate decrypt-only DataPipe size based on video content. + decrypted_buffer_reader_ = MojoDecoderBufferReader::Create( + GetDefaultDecoderBufferConverterCapacity(DemuxerStream::VIDEO), + &decrypted_producer_handle); remote_decryptor_.set_connection_error_with_reason_handler( base::Bind(&MojoDecryptor::OnConnectionError, base::Unretained(this))); // Pass the other end of each pipe to |remote_decryptor_|. - remote_decryptor_->Initialize(std::move(remote_consumer_handle), - std::move(remote_producer_handle)); + remote_decryptor_->Initialize( + std::move(audio_consumer_handle), std::move(video_consumer_handle), + std::move(decrypt_consumer_handle), std::move(decrypted_producer_handle)); } MojoDecryptor::~MojoDecryptor() { @@ -82,7 +104,7 @@ DCHECK(thread_checker_.CalledOnValidThread()); mojom::DecoderBufferPtr mojo_buffer = - mojo_decoder_buffer_writer_->WriteDecoderBuffer(encrypted); + decrypt_buffer_writer_->WriteDecoderBuffer(encrypted); if (!mojo_buffer) { decrypt_cb.Run(kError, nullptr); return; @@ -123,11 +145,11 @@ void MojoDecryptor::DecryptAndDecodeAudio( const scoped_refptr<DecoderBuffer>& encrypted, const AudioDecodeCB& audio_decode_cb) { - DVLOG(3) << __func__; + DVLOG(3) << __func__ << ": " << encrypted->AsHumanReadableString(); DCHECK(thread_checker_.CalledOnValidThread()); mojom::DecoderBufferPtr mojo_buffer = - mojo_decoder_buffer_writer_->WriteDecoderBuffer(encrypted); + audio_buffer_writer_->WriteDecoderBuffer(encrypted); if (!mojo_buffer) { audio_decode_cb.Run(kError, AudioFrames()); return; @@ -143,11 +165,11 @@ void MojoDecryptor::DecryptAndDecodeVideo( const scoped_refptr<DecoderBuffer>& encrypted, const VideoDecodeCB& video_decode_cb) { - DVLOG(3) << __func__; + DVLOG(3) << __func__ << ": " << encrypted->AsHumanReadableString(); DCHECK(thread_checker_.CalledOnValidThread()); mojom::DecoderBufferPtr mojo_buffer = - mojo_decoder_buffer_writer_->WriteDecoderBuffer(encrypted); + video_buffer_writer_->WriteDecoderBuffer(encrypted); if (!mojo_buffer) { video_decode_cb.Run(kError, nullptr); return; @@ -161,7 +183,7 @@ } void MojoDecryptor::ResetDecoder(StreamType stream_type) { - DVLOG(1) << __func__; + DVLOG(1) << __func__ << ": stream_type = " << stream_type; DCHECK(thread_checker_.CalledOnValidThread()); remote_decryptor_->ResetDecoder(stream_type); @@ -197,7 +219,7 @@ return; } - mojo_decoder_buffer_reader_->ReadDecoderBuffer( + decrypted_buffer_reader_->ReadDecoderBuffer( std::move(buffer), base::BindOnce(&MojoDecryptor::OnBufferRead, weak_factory_.GetWeakPtr(), std::move(decrypt_cb), status));
diff --git a/media/mojo/clients/mojo_decryptor.h b/media/mojo/clients/mojo_decryptor.h index d90d4b4..7ddd060 100644 --- a/media/mojo/clients/mojo_decryptor.h +++ b/media/mojo/clients/mojo_decryptor.h
@@ -26,7 +26,10 @@ // lives on the first time it is used in this class. class MojoDecryptor : public Decryptor { public: - explicit MojoDecryptor(mojom::DecryptorPtr remote_decryptor); + // |writer_capacity| can be used for testing. If 0, default writer capacity + // will be used. + MojoDecryptor(mojom::DecryptorPtr remote_decryptor, + uint32_t writer_capacity = 0); ~MojoDecryptor() final; // Decryptor implementation. @@ -80,17 +83,22 @@ void OnConnectionError(uint32_t custom_reason, const std::string& description); + // Helper class to get the correct MojoDecoderBufferWriter; + MojoDecoderBufferWriter* GetWriter(StreamType stream_type); + base::ThreadChecker thread_checker_; mojom::DecryptorPtr remote_decryptor_; - // Helper class to send DecoderBuffer to the |remote_decryptor_| for decrypt - // or decrypt-and-decode. - std::unique_ptr<MojoDecoderBufferWriter> mojo_decoder_buffer_writer_; + // Helper class to send DecoderBuffer to the |remote_decryptor_| for + // DecryptAndDecodeAudio(), DecryptAndDecodeVideo() and Decrypt(). + std::unique_ptr<MojoDecoderBufferWriter> audio_buffer_writer_; + std::unique_ptr<MojoDecoderBufferWriter> video_buffer_writer_; + std::unique_ptr<MojoDecoderBufferWriter> decrypt_buffer_writer_; // Helper class to receive decrypted DecoderBuffer from the - // |remote_decryptor_|. - std::unique_ptr<MojoDecoderBufferReader> mojo_decoder_buffer_reader_; + // |remote_decryptor_|, shared by audio and video. + std::unique_ptr<MojoDecoderBufferReader> decrypted_buffer_reader_; NewKeyCB new_audio_key_cb_; NewKeyCB new_video_key_cb_;
diff --git a/media/mojo/clients/mojo_decryptor_unittest.cc b/media/mojo/clients/mojo_decryptor_unittest.cc index 179741b3..d4e570f 100644 --- a/media/mojo/clients/mojo_decryptor_unittest.cc +++ b/media/mojo/clients/mojo_decryptor_unittest.cc
@@ -35,7 +35,12 @@ class MojoDecryptorTest : public ::testing::Test { public: - MojoDecryptorTest() { + MojoDecryptorTest() = default; + ~MojoDecryptorTest() override = default; + + void SetWriterCapacity(uint32_t capacity) { writer_capacity_ = capacity; } + + void Initialize() { decryptor_.reset(new StrictMock<MockDecryptor>()); mojom::DecryptorPtr remote_decryptor; @@ -44,11 +49,10 @@ base::Bind(&MojoDecryptorTest::OnConnectionClosed, base::Unretained(this)))); - mojo_decryptor_.reset(new MojoDecryptor(std::move(remote_decryptor))); + mojo_decryptor_.reset( + new MojoDecryptor(std::move(remote_decryptor), writer_capacity_)); } - ~MojoDecryptorTest() override = default; - void DestroyClient() { EXPECT_CALL(*this, OnConnectionClosed()); mojo_decryptor_.reset(); @@ -108,6 +112,8 @@ // Fixture members. base::TestMessageLoop message_loop_; + uint32_t writer_capacity_ = 0; + // The MojoDecryptor that we are testing. std::unique_ptr<MojoDecryptor> mojo_decryptor_; @@ -122,7 +128,32 @@ }; // DecryptAndDecodeAudio() and ResetDecoder(kAudio) immediately. -TEST_F(MojoDecryptorTest, ResetDuringDecryptAndDecodeAudio) { +TEST_F(MojoDecryptorTest, Reset_DuringDecryptAndDecode_Audio) { + Initialize(); + + { + // Make sure calls are made in order. + InSequence seq; + EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)) + .WillOnce(Invoke(this, &MojoDecryptorTest::ReturnAudioFrames)); + EXPECT_CALL(*decryptor_, ResetDecoder(Decryptor::kAudio)); + // The returned status could be success or aborted. + EXPECT_CALL(*this, AudioDecoded(_, _)); + } + + scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(100)); + mojo_decryptor_->DecryptAndDecodeAudio( + std::move(buffer), + base::Bind(&MojoDecryptorTest::AudioDecoded, base::Unretained(this))); + mojo_decryptor_->ResetDecoder(Decryptor::kAudio); + base::RunLoop().RunUntilIdle(); +} + +// DecryptAndDecodeAudio() and ResetDecoder(kAudio) immediately. +TEST_F(MojoDecryptorTest, Reset_DuringDecryptAndDecode_Audio_ChunkedWrite) { + SetWriterCapacity(20); + Initialize(); + { // Make sure calls are made in order. InSequence seq; @@ -142,7 +173,9 @@ } // DecryptAndDecodeVideo() and ResetDecoder(kVideo) immediately. -TEST_F(MojoDecryptorTest, ResetDuringDecryptAndDecodeVideo) { +TEST_F(MojoDecryptorTest, Reset_DuringDecryptAndDecode_Video) { + Initialize(); + { // Make sure calls are made in order. InSequence seq; @@ -163,7 +196,77 @@ base::RunLoop().RunUntilIdle(); } +// DecryptAndDecodeVideo() and ResetDecoder(kVideo) immediately. +TEST_F(MojoDecryptorTest, Reset_DuringDecryptAndDecode_Video_ChunkedWrite) { + SetWriterCapacity(20); + Initialize(); + + { + // Make sure calls are made in order. + InSequence seq; + EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) + .WillOnce( + Invoke(this, &MojoDecryptorTest::ReturnSharedBufferVideoFrame)); + EXPECT_CALL(*decryptor_, ResetDecoder(Decryptor::kVideo)); + // The returned status could be success or aborted. + EXPECT_CALL(*this, VideoDecoded(_, _)); + EXPECT_CALL(*this, OnFrameDestroyed()); + } + + scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(100)); + mojo_decryptor_->DecryptAndDecodeVideo( + std::move(buffer), + base::Bind(&MojoDecryptorTest::VideoDecoded, base::Unretained(this))); + mojo_decryptor_->ResetDecoder(Decryptor::kVideo); + base::RunLoop().RunUntilIdle(); +} + +// DecryptAndDecodeAudio(), DecryptAndDecodeVideo(), ResetDecoder(kAudio) and +// ResetDecoder(kVideo). +TEST_F(MojoDecryptorTest, Reset_DuringDecryptAndDecode_AudioAndVideo) { + // Only test chunked write as it's the most complex and error prone case. + SetWriterCapacity(20); + Initialize(); + + { + // Make sure calls are made in order. + InSequence seq; + EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _)) + .WillOnce(Invoke(this, &MojoDecryptorTest::ReturnAudioFrames)); + EXPECT_CALL(*decryptor_, ResetDecoder(Decryptor::kAudio)); + } + + { + // Make sure calls are made in order. + InSequence seq; + EXPECT_CALL(*decryptor_, DecryptAndDecodeVideo(_, _)) + .WillOnce( + Invoke(this, &MojoDecryptorTest::ReturnSharedBufferVideoFrame)); + EXPECT_CALL(*decryptor_, ResetDecoder(Decryptor::kVideo)); + } + + // The returned status could be success or aborted, and we don't care about + // the order. + EXPECT_CALL(*this, AudioDecoded(_, _)); + EXPECT_CALL(*this, VideoDecoded(_, _)); + EXPECT_CALL(*this, OnFrameDestroyed()); + + scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(100)); + + mojo_decryptor_->DecryptAndDecodeAudio( + std::move(buffer), + base::Bind(&MojoDecryptorTest::AudioDecoded, base::Unretained(this))); + mojo_decryptor_->DecryptAndDecodeVideo( + std::move(buffer), + base::Bind(&MojoDecryptorTest::VideoDecoded, base::Unretained(this))); + mojo_decryptor_->ResetDecoder(Decryptor::kAudio); + mojo_decryptor_->ResetDecoder(Decryptor::kVideo); + base::RunLoop().RunUntilIdle(); +} + TEST_F(MojoDecryptorTest, VideoDecodeFreesBuffer) { + Initialize(); + // Call DecryptAndDecodeVideo(). Once the callback VideoDecoded() completes, // the frame will be destroyed, and the buffer will be released. { @@ -182,6 +285,8 @@ } TEST_F(MojoDecryptorTest, VideoDecodeFreesMultipleBuffers) { + Initialize(); + // Call DecryptAndDecodeVideo() multiple times. const int TIMES = 5; EXPECT_CALL(*this, VideoDecoded(Decryptor::Status::kSuccess, NotNull())) @@ -201,6 +306,8 @@ } TEST_F(MojoDecryptorTest, VideoDecodeHoldThenFreeBuffers) { + Initialize(); + // Call DecryptAndDecodeVideo() twice. Hang on to the buffers returned, // and free them later. scoped_refptr<VideoFrame> saved_frame1; @@ -233,6 +340,8 @@ } TEST_F(MojoDecryptorTest, EOSBuffer) { + Initialize(); + // Call DecryptAndDecodeVideo(), but return an EOS frame (which has no frame // data, so no memory needs to be freed). EXPECT_CALL(*this, VideoDecoded(Decryptor::Status::kSuccess, NotNull())); @@ -247,11 +356,13 @@ } TEST_F(MojoDecryptorTest, DestroyClient) { + Initialize(); DestroyClient(); base::RunLoop().RunUntilIdle(); } TEST_F(MojoDecryptorTest, DestroyService) { + Initialize(); DestroyService(); base::RunLoop().RunUntilIdle();
diff --git a/media/mojo/clients/mojo_demuxer_stream_impl.cc b/media/mojo/clients/mojo_demuxer_stream_impl.cc index e2043fa..56c0e56 100644 --- a/media/mojo/clients/mojo_demuxer_stream_impl.cc +++ b/media/mojo/clients/mojo_demuxer_stream_impl.cc
@@ -45,8 +45,9 @@ } mojo::ScopedDataPipeConsumerHandle remote_consumer_handle; - mojo_decoder_buffer_writer_ = - MojoDecoderBufferWriter::Create(stream_->type(), &remote_consumer_handle); + mojo_decoder_buffer_writer_ = MojoDecoderBufferWriter::Create( + GetDefaultDecoderBufferConverterCapacity(stream_->type()), + &remote_consumer_handle); std::move(callback).Run(stream_->type(), std::move(remote_consumer_handle), audio_config, video_config);
diff --git a/media/mojo/clients/mojo_video_decoder.cc b/media/mojo/clients/mojo_video_decoder.cc index 64d45a04d..5e4d06e0 100644 --- a/media/mojo/clients/mojo_video_decoder.cc +++ b/media/mojo/clients/mojo_video_decoder.cc
@@ -80,6 +80,8 @@ : task_runner_(task_runner), remote_decoder_info_(remote_decoder.PassInterface()), gpu_factories_(gpu_factories), + writer_capacity_( + GetDefaultDecoderBufferConverterCapacity(DemuxerStream::VIDEO)), client_binding_(this), media_log_service_(media_log), media_log_binding_(&media_log_service_), @@ -275,10 +277,9 @@ base::MakeRefCounted<MojoVideoFrameHandleReleaser>( std::move(video_frame_handle_releaser_ptr_info), task_runner_); - // Create |decoder_buffer_pipe|, and bind |mojo_decoder_buffer_writer_| to it. mojo::ScopedDataPipeConsumerHandle remote_consumer_handle; mojo_decoder_buffer_writer_ = MojoDecoderBufferWriter::Create( - DemuxerStream::VIDEO, &remote_consumer_handle); + writer_capacity_, &remote_consumer_handle); // Generate |command_buffer_id|. media::mojom::CommandBufferIdPtr command_buffer_id;
diff --git a/media/mojo/clients/mojo_video_decoder.h b/media/mojo/clients/mojo_video_decoder.h index b3c96ca..9a957a9 100644 --- a/media/mojo/clients/mojo_video_decoder.h +++ b/media/mojo/clients/mojo_video_decoder.h
@@ -61,6 +61,10 @@ const base::Optional<base::UnguessableToken>& release_token) final; void RequestOverlayInfo(bool restart_for_transitions) final; + void set_writer_capacity_for_testing(uint32_t capacity) { + writer_capacity_ = capacity; + } + private: void OnInitializeDone(bool status, bool needs_bitstream_conversion, @@ -96,6 +100,9 @@ mojom::VideoDecoderPtr remote_decoder_; std::unique_ptr<MojoDecoderBufferWriter> mojo_decoder_buffer_writer_; + + uint32_t writer_capacity_ = 0; + bool remote_decoder_bound_ = false; bool has_connection_error_ = false; mojo::AssociatedBinding<mojom::VideoDecoderClient> client_binding_;
diff --git a/media/mojo/common/mojo_decoder_buffer_converter.cc b/media/mojo/common/mojo_decoder_buffer_converter.cc index f361159d..df8dbf0 100644 --- a/media/mojo/common/mojo_decoder_buffer_converter.cc +++ b/media/mojo/common/mojo_decoder_buffer_converter.cc
@@ -19,7 +19,13 @@ namespace { -std::unique_ptr<mojo::DataPipe> CreateDataPipe(DemuxerStream::Type type) { +bool IsPipeReadWriteError(MojoResult result) { + return result != MOJO_RESULT_OK && result != MOJO_RESULT_SHOULD_WAIT; +} + +} // namespace + +uint32_t GetDefaultDecoderBufferConverterCapacity(DemuxerStream::Type type) { uint32_t capacity = 0; if (type == DemuxerStream::AUDIO) { @@ -36,26 +42,22 @@ capacity = 512 * 1024; } - return base::MakeUnique<mojo::DataPipe>(capacity); + return capacity; } -bool IsPipeReadWriteError(MojoResult result) { - return result != MOJO_RESULT_OK && result != MOJO_RESULT_SHOULD_WAIT; -} - -} // namespace - // MojoDecoderBufferReader // static std::unique_ptr<MojoDecoderBufferReader> MojoDecoderBufferReader::Create( - DemuxerStream::Type type, + uint32_t capacity, mojo::ScopedDataPipeProducerHandle* producer_handle) { DVLOG(1) << __func__; - std::unique_ptr<mojo::DataPipe> data_pipe = CreateDataPipe(type); + DCHECK_GT(capacity, 0u); + + auto data_pipe = std::make_unique<mojo::DataPipe>(capacity); *producer_handle = std::move(data_pipe->producer_handle); - return base::WrapUnique( - new MojoDecoderBufferReader(std::move(data_pipe->consumer_handle))); + return std::make_unique<MojoDecoderBufferReader>( + std::move(data_pipe->consumer_handle)); } MojoDecoderBufferReader::MojoDecoderBufferReader( @@ -81,6 +83,8 @@ MojoDecoderBufferReader::~MojoDecoderBufferReader() { DVLOG(1) << __func__; CancelAllPendingReadCBs(); + if (flush_cb_) + std::move(flush_cb_).Run(); } void MojoDecoderBufferReader::CancelReadCB(ReadCB read_cb) { @@ -101,6 +105,8 @@ void MojoDecoderBufferReader::CompleteCurrentRead() { DVLOG(4) << __func__; + DCHECK(!pending_read_cbs_.empty()); + DCHECK_EQ(pending_read_cbs_.size(), pending_buffers_.size()); ReadCB read_cb = std::move(pending_read_cbs_.front()); pending_read_cbs_.pop_front(); @@ -112,6 +118,9 @@ bytes_read_ = 0; std::move(read_cb).Run(std::move(buffer)); + + if (pending_read_cbs_.empty() && flush_cb_) + std::move(flush_cb_).Run(); } void MojoDecoderBufferReader::ScheduleNextRead() { @@ -128,6 +137,7 @@ mojom::DecoderBufferPtr mojo_buffer, ReadCB read_cb) { DVLOG(3) << __func__; + DCHECK(!flush_cb_); if (!consumer_handle_.is_valid()) { DCHECK(pending_read_cbs_.empty()); @@ -152,6 +162,22 @@ ProcessPendingReads(); } +void MojoDecoderBufferReader::Flush(base::OnceClosure flush_cb) { + DVLOG(2) << __func__; + DCHECK(!flush_cb_); + + if (pending_read_cbs_.empty()) { + std::move(flush_cb).Run(); + return; + } + + flush_cb_ = std::move(flush_cb); +} + +bool MojoDecoderBufferReader::HasPendingReads() const { + return !pending_read_cbs_.empty(); +} + void MojoDecoderBufferReader::OnPipeReadable( MojoResult result, const mojo::HandleSignalsState& state) { @@ -213,6 +239,7 @@ } DCHECK_EQ(result, MOJO_RESULT_OK); + DVLOG(4) << __func__ << ": " << num_bytes << " bytes read."; DCHECK_GT(num_bytes, 0u); bytes_read_ += num_bytes; @@ -245,13 +272,15 @@ // static std::unique_ptr<MojoDecoderBufferWriter> MojoDecoderBufferWriter::Create( - DemuxerStream::Type type, + uint32_t capacity, mojo::ScopedDataPipeConsumerHandle* consumer_handle) { DVLOG(1) << __func__; - std::unique_ptr<mojo::DataPipe> data_pipe = CreateDataPipe(type); + DCHECK_GT(capacity, 0u); + + auto data_pipe = std::make_unique<mojo::DataPipe>(capacity); *consumer_handle = std::move(data_pipe->consumer_handle); - return base::WrapUnique( - new MojoDecoderBufferWriter(std::move(data_pipe->producer_handle))); + return std::make_unique<MojoDecoderBufferWriter>( + std::move(data_pipe->producer_handle)); } MojoDecoderBufferWriter::MojoDecoderBufferWriter( @@ -367,6 +396,7 @@ } DCHECK_EQ(MOJO_RESULT_OK, result); + DVLOG(4) << __func__ << ": " << num_bytes << " bytes written."; DCHECK_GT(num_bytes, 0u); bytes_written_ += num_bytes; if (bytes_written_ == buffer_size) {
diff --git a/media/mojo/common/mojo_decoder_buffer_converter.h b/media/mojo/common/mojo_decoder_buffer_converter.h index d3afd2b..538ff43 100644 --- a/media/mojo/common/mojo_decoder_buffer_converter.h +++ b/media/mojo/common/mojo_decoder_buffer_converter.h
@@ -19,15 +19,20 @@ class DecoderBuffer; +// Returns the default capacity to be used with MojoDecoderBufferReader and +// MojoDecoderBufferWriter for |type|. +uint32_t GetDefaultDecoderBufferConverterCapacity(DemuxerStream::Type type); + // Combines mojom::DecoderBuffers with data read from a DataPipe to produce // media::DecoderBuffers (counterpart of MojoDecoderBufferWriter). class MojoDecoderBufferReader { public: using ReadCB = base::OnceCallback<void(scoped_refptr<DecoderBuffer>)>; - // Creates a MojoDecoderBufferReader of |type| and set the |producer_handle|. + // Creates a MojoDecoderBufferReader of |capacity| bytes and set the + // |producer_handle|. static std::unique_ptr<MojoDecoderBufferReader> Create( - DemuxerStream::Type type, + uint32_t capacity, mojo::ScopedDataPipeProducerHandle* producer_handle); // Hold the consumer handle to read DecoderBuffer data. @@ -49,6 +54,16 @@ // be called with nullptr. void ReadDecoderBuffer(mojom::DecoderBufferPtr buffer, ReadCB read_cb); + // Reads all pending data from the pipe and fire all pending ReadCBs, after + // which fire the |flush_cb|. No further ReadDecoderBuffer() or Flush() calls + // should be made before |flush_cb| is fired. + // Note that |flush_cb| may be called on the same call stack as this Flush() + // call if there are no pending reads. + void Flush(base::OnceClosure flush_cb); + + // Whether there's any pending reads in |this|. + bool HasPendingReads() const; + private: void CancelReadCB(ReadCB read_cb); void CancelAllPendingReadCBs(); @@ -71,6 +86,9 @@ // Callbacks for pending buffers. base::circular_deque<ReadCB> pending_read_cbs_; + // Callback for Flush(). + base::OnceClosure flush_cb_; + // Number of bytes already read into the current buffer. uint32_t bytes_read_; @@ -89,9 +107,10 @@ // successful prior to the closure. class MojoDecoderBufferWriter { public: - // Creates a MojoDecoderBufferWriter of |type| and set the |consumer_handle|. + // Creates a MojoDecoderBufferWriter of |capacity| bytes and set the + // |consumer_handle|. static std::unique_ptr<MojoDecoderBufferWriter> Create( - DemuxerStream::Type type, + uint32_t capacity, mojo::ScopedDataPipeConsumerHandle* consumer_handle); // Hold the producer handle to write DecoderBuffer data.
diff --git a/media/mojo/common/mojo_decoder_buffer_converter_unittest.cc b/media/mojo/common/mojo_decoder_buffer_converter_unittest.cc index a0fd834..e3b62a2 100644 --- a/media/mojo/common/mojo_decoder_buffer_converter_unittest.cc +++ b/media/mojo/common/mojo_decoder_buffer_converter_unittest.cc
@@ -229,4 +229,169 @@ run_loop.Run(); } +TEST(MojoDecoderBufferConverterTest, FlushWithoutRead) { + base::MessageLoop message_loop; + base::RunLoop run_loop; + + base::MockCallback<base::OnceClosure> mock_flush_cb; + EXPECT_CALL(mock_flush_cb, Run()); + + MojoDecoderBufferConverter converter; + converter.reader->Flush(mock_flush_cb.Get()); + + run_loop.RunUntilIdle(); +} + +TEST(MojoDecoderBufferConverterTest, FlushAfterRead) { + base::MessageLoop message_loop; + base::RunLoop run_loop; + + const uint8_t kData[] = "Lorem ipsum dolor sit amet, consectetur cras amet"; + const size_t kDataSize = arraysize(kData); + scoped_refptr<DecoderBuffer> media_buffer = + DecoderBuffer::CopyFrom(kData, kDataSize); + + MojoDecoderBufferConverter converter(kDataSize / 3); + converter.ConvertAndVerify(media_buffer); + + base::MockCallback<base::OnceClosure> mock_flush_cb; + EXPECT_CALL(mock_flush_cb, Run()) + .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); + + converter.reader->Flush(mock_flush_cb.Get()); + + run_loop.Run(); +} + +TEST(MojoDecoderBufferConverterTest, FlushBeforeRead) { + base::MessageLoop message_loop; + base::RunLoop run_loop; + + const uint8_t kData[] = "Lorem ipsum dolor sit amet, consectetur cras amet"; + const size_t kDataSize = arraysize(kData); + scoped_refptr<DecoderBuffer> media_buffer = + DecoderBuffer::CopyFrom(kData, kDataSize); + + MojoDecoderBufferConverter converter; + + base::MockCallback<MojoDecoderBufferReader::ReadCB> mock_read_cb; + base::MockCallback<base::OnceClosure> mock_flush_cb; + + ::testing::InSequence sequence; + EXPECT_CALL(mock_flush_cb, Run()); + EXPECT_CALL(mock_read_cb, Run(MatchesDecoderBuffer(media_buffer))) + .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); + + // Write, Flush, then Read. + mojom::DecoderBufferPtr mojo_buffer = + converter.writer->WriteDecoderBuffer(media_buffer); + converter.reader->Flush(mock_flush_cb.Get()); + converter.reader->ReadDecoderBuffer(std::move(mojo_buffer), + mock_read_cb.Get()); + run_loop.Run(); +} + +TEST(MojoDecoderBufferConverterTest, FlushBeforeChunkedRead) { + base::MessageLoop message_loop; + base::RunLoop run_loop; + + const uint8_t kData[] = "Lorem ipsum dolor sit amet, consectetur cras amet"; + const size_t kDataSize = arraysize(kData); + scoped_refptr<DecoderBuffer> media_buffer = + DecoderBuffer::CopyFrom(kData, kDataSize); + + MojoDecoderBufferConverter converter(kDataSize / 3); + + base::MockCallback<MojoDecoderBufferReader::ReadCB> mock_read_cb; + base::MockCallback<base::OnceClosure> mock_flush_cb; + + // Read callback should be fired after reset callback. + ::testing::InSequence sequence; + EXPECT_CALL(mock_flush_cb, Run()); + EXPECT_CALL(mock_read_cb, Run(MatchesDecoderBuffer(media_buffer))) + .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); + + // Write, reset, then read. + mojom::DecoderBufferPtr mojo_buffer = + converter.writer->WriteDecoderBuffer(media_buffer); + converter.reader->Flush(mock_flush_cb.Get()); + converter.reader->ReadDecoderBuffer(std::move(mojo_buffer), + mock_read_cb.Get()); + run_loop.Run(); +} + +TEST(MojoDecoderBufferConverterTest, FlushDuringChunkedRead) { + base::MessageLoop message_loop; + base::RunLoop run_loop; + + const uint8_t kData[] = "Lorem ipsum dolor sit amet, consectetur cras amet"; + const size_t kDataSize = arraysize(kData); + scoped_refptr<DecoderBuffer> media_buffer = + DecoderBuffer::CopyFrom(kData, kDataSize); + + MojoDecoderBufferConverter converter(kDataSize / 3); + + base::MockCallback<MojoDecoderBufferReader::ReadCB> mock_read_cb; + base::MockCallback<base::OnceClosure> mock_flush_cb; + + // Flush callback should be fired after read callback. + ::testing::InSequence sequence; + EXPECT_CALL(mock_read_cb, Run(MatchesDecoderBuffer(media_buffer))); + EXPECT_CALL(mock_flush_cb, Run()) + .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); + + // Write, read, then reset. + mojom::DecoderBufferPtr mojo_buffer = + converter.writer->WriteDecoderBuffer(media_buffer); + converter.reader->ReadDecoderBuffer(std::move(mojo_buffer), + mock_read_cb.Get()); + converter.reader->Flush(mock_flush_cb.Get()); + run_loop.Run(); +} + +TEST(MojoDecoderBufferConverterTest, FlushDuringConcurrentReads) { + base::MessageLoop message_loop; + base::RunLoop run_loop; + + // Prevent all of the buffers from fitting at once to exercise the chunking + // logic. + MojoDecoderBufferConverter converter(4); + auto& writer = converter.writer; + auto& reader = converter.reader; + + // Three buffers: normal, EOS, normal. + const uint8_t kData[] = "Hello, world"; + const size_t kDataSize = arraysize(kData); + auto media_buffer1 = DecoderBuffer::CopyFrom(kData, kDataSize); + auto media_buffer2 = DecoderBuffer::CreateEOSBuffer(); + auto media_buffer3 = DecoderBuffer::CopyFrom(kData, kDataSize); + + // Expect the read callbacks to be issued in the same order. + base::MockCallback<MojoDecoderBufferReader::ReadCB> mock_read_cb1; + base::MockCallback<MojoDecoderBufferReader::ReadCB> mock_read_cb2; + base::MockCallback<MojoDecoderBufferReader::ReadCB> mock_read_cb3; + base::MockCallback<base::OnceClosure> mock_flush_cb; + + ::testing::InSequence scoper; + EXPECT_CALL(mock_read_cb1, Run(MatchesDecoderBuffer(media_buffer1))); + EXPECT_CALL(mock_read_cb2, Run(MatchesDecoderBuffer(media_buffer2))); + EXPECT_CALL(mock_read_cb3, Run(MatchesDecoderBuffer(media_buffer3))); + EXPECT_CALL(mock_flush_cb, Run()) + .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); + + // Write all of the buffers at once. + auto mojo_buffer1 = writer->WriteDecoderBuffer(media_buffer1); + auto mojo_buffer2 = writer->WriteDecoderBuffer(media_buffer2); + auto mojo_buffer3 = writer->WriteDecoderBuffer(media_buffer3); + + // Read all of the buffers at once. + reader->ReadDecoderBuffer(std::move(mojo_buffer1), mock_read_cb1.Get()); + reader->ReadDecoderBuffer(std::move(mojo_buffer2), mock_read_cb2.Get()); + reader->ReadDecoderBuffer(std::move(mojo_buffer3), mock_read_cb3.Get()); + reader->Flush(mock_flush_cb.Get()); + // No ReadDecoderBuffer() can be called during pending reset. + + run_loop.Run(); +} + } // namespace media
diff --git a/media/mojo/interfaces/decryptor.mojom b/media/mojo/interfaces/decryptor.mojom index 19ec61a9..041ca55d 100644 --- a/media/mojo/interfaces/decryptor.mojom +++ b/media/mojo/interfaces/decryptor.mojom
@@ -8,6 +8,8 @@ // Interface for decrypting (and decoding) encrypted streams. // See media/base/decryptor.h for details. +// TODO(crbug.com/794326): Deduplicate this interface with audio_decoder.mojom +// and audio_decoder.mojom. interface Decryptor { // Status of a decrypt or decrypt-and-decode operation. See decryptor.h for // descriptions. @@ -18,14 +20,15 @@ [Native] enum StreamType; - // Pass the two data pipes used to transfer DecoderBuffer contents to and - // from the Decryptor. |receive_pipe| will be used to receive DecoderBuffer - // data on Decrypt(), DecryptAndDecodeAudio(), and DecryptAndDecodeVideo() - // calls. |transmit_pipe| will be used to pass the DecoderBuffer data - // back with OnDecryptDone() calls. This method must be called before any - // methods listed are called. - Initialize(handle<data_pipe_consumer> receive_pipe, - handle<data_pipe_producer> transmit_pipe); + // Pass three data pipes used to transfer compressed DecoderBuffer data for + // DecryptAndDecodeAudio(), and DecryptAndDecodeVideo() and Decrypt(), + // respectively, and one data pipe to receive DecoderBuffer data for decrypt + // result passed back in OnDecryptDone() calls. + // This method must be called before any methods listed are called. + Initialize(handle<data_pipe_consumer> audio_pipe, + handle<data_pipe_consumer> video_pipe, + handle<data_pipe_consumer> decrypt_pipe, + handle<data_pipe_producer> decrypted_pipe); // Decrypts the |encrypted| buffer and returns the decrypt |status| and // decrypted |buffer|.
diff --git a/media/mojo/services/mojo_audio_decoder_service.cc b/media/mojo/services/mojo_audio_decoder_service.cc index b563711..486ff4e 100644 --- a/media/mojo/services/mojo_audio_decoder_service.cc +++ b/media/mojo/services/mojo_audio_decoder_service.cc
@@ -82,8 +82,11 @@ void MojoAudioDecoderService::Reset(ResetCallback callback) { DVLOG(1) << __func__; - decoder_->Reset(base::Bind(&MojoAudioDecoderService::OnResetDone, weak_this_, - base::Passed(&callback))); + + // Reset the reader so that pending decodes will be dispatches first. + mojo_decoder_buffer_reader_->Flush( + base::Bind(&MojoAudioDecoderService::OnReaderFlushDone, weak_this_, + base::Passed(&callback))); } void MojoAudioDecoderService::OnInitialized( @@ -118,6 +121,11 @@ weak_this_, base::Passed(&callback))); } +void MojoAudioDecoderService::OnReaderFlushDone(ResetCallback callback) { + decoder_->Reset(base::Bind(&MojoAudioDecoderService::OnResetDone, weak_this_, + base::Passed(&callback))); +} + void MojoAudioDecoderService::OnDecodeStatus(DecodeCallback callback, media::DecodeStatus status) { DVLOG(3) << __func__ << " status:" << status;
diff --git a/media/mojo/services/mojo_audio_decoder_service.h b/media/mojo/services/mojo_audio_decoder_service.h index 8b0ba5df..8530073 100644 --- a/media/mojo/services/mojo_audio_decoder_service.h +++ b/media/mojo/services/mojo_audio_decoder_service.h
@@ -46,8 +46,12 @@ scoped_refptr<ContentDecryptionModule> cdm, bool success); + // Called by |mojo_decoder_buffer_reader_| when read is finished. void OnReadDone(DecodeCallback callback, scoped_refptr<DecoderBuffer> buffer); + // Called by |mojo_decoder_buffer_reader_| when reset is finished. + void OnReaderFlushDone(ResetCallback callback); + // Called by |decoder_| when DecoderBuffer is accepted or rejected. void OnDecodeStatus(DecodeCallback callback, media::DecodeStatus status);
diff --git a/media/mojo/services/mojo_decryptor_service.cc b/media/mojo/services/mojo_decryptor_service.cc index 7d3c8d4..55a5704 100644 --- a/media/mojo/services/mojo_decryptor_service.cc +++ b/media/mojo/services/mojo_decryptor_service.cc
@@ -61,19 +61,25 @@ MojoDecryptorService::~MojoDecryptorService() = default; void MojoDecryptorService::Initialize( - mojo::ScopedDataPipeConsumerHandle receive_pipe, - mojo::ScopedDataPipeProducerHandle transmit_pipe) { - mojo_decoder_buffer_writer_.reset( - new MojoDecoderBufferWriter(std::move(transmit_pipe))); - mojo_decoder_buffer_reader_.reset( - new MojoDecoderBufferReader(std::move(receive_pipe))); + mojo::ScopedDataPipeConsumerHandle audio_pipe, + mojo::ScopedDataPipeConsumerHandle video_pipe, + mojo::ScopedDataPipeConsumerHandle decrypt_pipe, + mojo::ScopedDataPipeProducerHandle decrypted_pipe) { + audio_buffer_reader_.reset( + new MojoDecoderBufferReader(std::move(audio_pipe))); + video_buffer_reader_.reset( + new MojoDecoderBufferReader(std::move(video_pipe))); + decrypt_buffer_reader_.reset( + new MojoDecoderBufferReader(std::move(decrypt_pipe))); + decrypted_buffer_writer_.reset( + new MojoDecoderBufferWriter(std::move(decrypted_pipe))); } void MojoDecryptorService::Decrypt(StreamType stream_type, mojom::DecoderBufferPtr encrypted, DecryptCallback callback) { DVLOG(3) << __func__; - mojo_decoder_buffer_reader_->ReadDecoderBuffer( + decrypt_buffer_reader_->ReadDecoderBuffer( std::move(encrypted), base::BindOnce(&MojoDecryptorService::OnReadDone, weak_this_, stream_type, std::move(callback))); @@ -106,7 +112,7 @@ mojom::DecoderBufferPtr encrypted, DecryptAndDecodeAudioCallback callback) { DVLOG(3) << __func__; - mojo_decoder_buffer_reader_->ReadDecoderBuffer( + audio_buffer_reader_->ReadDecoderBuffer( std::move(encrypted), base::BindOnce(&MojoDecryptorService::OnAudioRead, weak_this_, std::move(callback))); } @@ -115,18 +121,28 @@ mojom::DecoderBufferPtr encrypted, DecryptAndDecodeVideoCallback callback) { DVLOG(3) << __func__; - mojo_decoder_buffer_reader_->ReadDecoderBuffer( + video_buffer_reader_->ReadDecoderBuffer( std::move(encrypted), base::BindOnce(&MojoDecryptorService::OnVideoRead, weak_this_, std::move(callback))); } void MojoDecryptorService::ResetDecoder(StreamType stream_type) { - DVLOG(1) << __func__; - decryptor_->ResetDecoder(stream_type); + DVLOG(1) << __func__ << ": stream_type = " << stream_type; + + // Reset the reader so that pending decodes will be dispatched first. + if (!GetBufferReader(stream_type)) + return; + + GetBufferReader(stream_type) + ->Flush(base::Bind(&MojoDecryptorService::OnReaderFlushDone, weak_this_, + stream_type)); } void MojoDecryptorService::DeinitializeDecoder(StreamType stream_type) { DVLOG(1) << __func__; + DCHECK(!GetBufferReader(stream_type)->HasPendingReads()) + << "The decoder should be fully flushed before deinitialized."; + decryptor_->DeinitializeDecoder(stream_type); } @@ -157,7 +173,7 @@ } mojom::DecoderBufferPtr mojo_buffer = - mojo_decoder_buffer_writer_->WriteDecoderBuffer(buffer); + decrypted_buffer_writer_->WriteDecoderBuffer(buffer); if (!mojo_buffer) { std::move(callback).Run(Status::kError, nullptr); return; @@ -205,6 +221,11 @@ weak_this_, base::Passed(&callback))); } +void MojoDecryptorService::OnReaderFlushDone(StreamType stream_type) { + DVLOG(1) << __func__ << ": stream_type = " << stream_type; + decryptor_->ResetDecoder(stream_type); +} + void MojoDecryptorService::OnAudioDecoded( DecryptAndDecodeAudioCallback callback, Status status, @@ -246,4 +267,17 @@ std::move(callback).Run(status, std::move(frame), std::move(releaser)); } +MojoDecoderBufferReader* MojoDecryptorService::GetBufferReader( + StreamType stream_type) const { + switch (stream_type) { + case StreamType::kAudio: + return audio_buffer_reader_.get(); + case StreamType::kVideo: + return video_buffer_reader_.get(); + } + + NOTREACHED() << "Unexpected stream_type: " << stream_type; + return nullptr; +} + } // namespace media
diff --git a/media/mojo/services/mojo_decryptor_service.h b/media/mojo/services/mojo_decryptor_service.h index 691f03e..232b211 100644 --- a/media/mojo/services/mojo_decryptor_service.h +++ b/media/mojo/services/mojo_decryptor_service.h
@@ -41,8 +41,10 @@ ~MojoDecryptorService() final; // mojom::Decryptor implementation. - void Initialize(mojo::ScopedDataPipeConsumerHandle receive_pipe, - mojo::ScopedDataPipeProducerHandle transmit_pipe) final; + void Initialize(mojo::ScopedDataPipeConsumerHandle audio_pipe, + mojo::ScopedDataPipeConsumerHandle video_pipe, + mojo::ScopedDataPipeConsumerHandle decrypt_pipe, + mojo::ScopedDataPipeProducerHandle decrypted_pipe) final; void Decrypt(StreamType stream_type, mojom::DecoderBufferPtr encrypted, DecryptCallback callback) final; @@ -78,6 +80,7 @@ scoped_refptr<DecoderBuffer> buffer); void OnVideoRead(DecryptAndDecodeVideoCallback callback, scoped_refptr<DecoderBuffer> buffer); + void OnReaderFlushDone(StreamType stream_type); // Callbacks executed when DecryptAndDecode are done. void OnAudioDecoded(DecryptAndDecodeAudioCallback callback, @@ -87,14 +90,19 @@ Status status, const scoped_refptr<VideoFrame>& frame); + // Returns audio/video buffer reader according to the |stream_type|. + MojoDecoderBufferReader* GetBufferReader(StreamType stream_type) const; + // A weak binding is used to connect to the MojoDecryptor. mojo::Binding<mojom::Decryptor> binding_; - // Helper class to send decrypted DecoderBuffer to the client. - std::unique_ptr<MojoDecoderBufferWriter> mojo_decoder_buffer_writer_; + // Helper classes to receive encrypted DecoderBuffer from the client. + std::unique_ptr<MojoDecoderBufferReader> audio_buffer_reader_; + std::unique_ptr<MojoDecoderBufferReader> video_buffer_reader_; + std::unique_ptr<MojoDecoderBufferReader> decrypt_buffer_reader_; - // Helper class to receive encrypted DecoderBuffer from the client. - std::unique_ptr<MojoDecoderBufferReader> mojo_decoder_buffer_reader_; + // Helper class to send decrypted DecoderBuffer to the client. + std::unique_ptr<MojoDecoderBufferWriter> decrypted_buffer_writer_; media::Decryptor* decryptor_;
diff --git a/media/mojo/services/mojo_video_decoder_service.cc b/media/mojo/services/mojo_video_decoder_service.cc index 5cf0b8f..1d1a2ff 100644 --- a/media/mojo/services/mojo_video_decoder_service.cc +++ b/media/mojo/services/mojo_video_decoder_service.cc
@@ -192,7 +192,7 @@ } mojo_decoder_buffer_reader_->ReadDecoderBuffer( - std::move(buffer), base::BindOnce(&MojoVideoDecoderService::OnDecoderRead, + std::move(buffer), base::BindOnce(&MojoVideoDecoderService::OnReaderRead, weak_this_, std::move(callback))); } @@ -204,8 +204,10 @@ return; } - decoder_->Reset(base::Bind(&MojoVideoDecoderService::OnDecoderReset, - weak_this_, base::Passed(&callback))); + // Flush the reader so that pending decodes will be dispatches first. + mojo_decoder_buffer_reader_->Flush( + base::Bind(&MojoVideoDecoderService::OnReaderFlushed, weak_this_, + base::Passed(&callback))); } void MojoVideoDecoderService::OnDecoderInitialized( @@ -222,7 +224,7 @@ decoder_->GetMaxDecodeRequests()); } -void MojoVideoDecoderService::OnDecoderRead( +void MojoVideoDecoderService::OnReaderRead( DecodeCallback callback, scoped_refptr<DecoderBuffer> buffer) { DVLOG(3) << __func__; @@ -237,6 +239,11 @@ base::Passed(&callback))); } +void MojoVideoDecoderService::OnReaderFlushed(ResetCallback callback) { + decoder_->Reset(base::Bind(&MojoVideoDecoderService::OnDecoderReset, + weak_this_, base::Passed(&callback))); +} + void MojoVideoDecoderService::OnDecoderDecoded(DecodeCallback callback, DecodeStatus status) { DVLOG(2) << __func__;
diff --git a/media/mojo/services/mojo_video_decoder_service.h b/media/mojo/services/mojo_video_decoder_service.h index e2692745..06215c4b 100644 --- a/media/mojo/services/mojo_video_decoder_service.h +++ b/media/mojo/services/mojo_video_decoder_service.h
@@ -63,9 +63,13 @@ void OnDecoderInitialized(InitializeCallback callback, scoped_refptr<ContentDecryptionModule> cdm, bool success); - void OnDecoderRead(DecodeCallback callback, - scoped_refptr<DecoderBuffer> buffer); + void OnReaderRead(DecodeCallback callback, + scoped_refptr<DecoderBuffer> buffer); void OnDecoderDecoded(DecodeCallback callback, DecodeStatus status); + + // Called by |mojo_decoder_buffer_reader_| when reset is finished. + void OnReaderFlushed(ResetCallback callback); + void OnDecoderReset(ResetCallback callback); void OnDecoderOutput(const scoped_refptr<VideoFrame>& frame);
diff --git a/media/mojo/test/mojo_video_decoder_integration_test.cc b/media/mojo/test/mojo_video_decoder_integration_test.cc index 51ccf533..2f0ae631 100644 --- a/media/mojo/test/mojo_video_decoder_integration_test.cc +++ b/media/mojo/test/mojo_video_decoder_integration_test.cc
@@ -184,6 +184,10 @@ protected: void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); } + void SetWriterCapacity(uint32_t capacity) { + client_->set_writer_capacity_for_testing(capacity); + } + bool Initialize() { bool result = false; @@ -220,12 +224,16 @@ } scoped_refptr<DecoderBuffer> CreateKeyframe(int64_t timestamp_ms) { - std::vector<uint8_t> data = {1, 2, 3, 4}; + // Use 32 bytes to simulated chunked write (with capacity 10; see below). + std::vector<uint8_t> data(32, 0); + scoped_refptr<DecoderBuffer> buffer = DecoderBuffer::CopyFrom(data.data(), data.size()); + buffer->set_timestamp(base::TimeDelta::FromMilliseconds(timestamp_ms)); buffer->set_duration(base::TimeDelta::FromMilliseconds(10)); buffer->set_is_key_frame(true); + return buffer; } @@ -349,4 +357,34 @@ RunUntilIdle(); } +TEST_F(MojoVideoDecoderIntegrationTest, ResetDuringDecode_ChunkedWrite) { + // Use a small writer capacity to force chunked write. + SetWriterCapacity(10); + + ASSERT_TRUE(Initialize()); + + VideoFrame::ReleaseMailboxCB release_cb = VideoFrame::ReleaseMailboxCB(); + StrictMock<base::MockCallback<VideoDecoder::DecodeCB>> decode_cb; + StrictMock<base::MockCallback<base::Closure>> reset_cb; + + EXPECT_CALL(*decoder_, GetReleaseMailboxCB()) + .WillRepeatedly(Return(release_cb)); + EXPECT_CALL(output_cb_, Run(_)).Times(kMaxDecodeRequests); + EXPECT_CALL(*decoder_, Decode(_, _)).Times(kMaxDecodeRequests); + EXPECT_CALL(*decoder_, Reset(_)).Times(1); + + InSequence s; // Make sure all callbacks are fired in order. + EXPECT_CALL(decode_cb, Run(_)).Times(kMaxDecodeRequests); + EXPECT_CALL(reset_cb, Run()); + + int64_t timestamp_ms = 0; + for (int j = 0; j < kMaxDecodeRequests; ++j) { + client_->Decode(CreateKeyframe(timestamp_ms++), decode_cb.Get()); + } + + client_->Reset(reset_cb.Get()); + + RunUntilIdle(); +} + } // namespace media
diff --git a/services/shape_detection/BUILD.gn b/services/shape_detection/BUILD.gn index 1f47289..36d2ff0 100644 --- a/services/shape_detection/BUILD.gn +++ b/services/shape_detection/BUILD.gn
@@ -37,7 +37,8 @@ "face_detection_impl_win.h", "face_detection_provider_win.cc", "face_detection_provider_win.h", - "text_detection_impl.cc", + "text_detection_impl_win.cc", + "text_detection_impl_win.h", ] } else { sources += [ @@ -109,21 +110,19 @@ source_set("tests") { testonly = true - sources = [] + sources = [ + "barcode_detection_impl_mac_unittest.mm", + "face_detection_impl_mac_unittest.mm", + "face_detection_impl_win_unittest.cc", + "text_detection_impl_mac_unittest.mm", + "text_detection_impl_win_unittest.cc", + ] if (is_mac) { - sources += [ - "barcode_detection_impl_mac_unittest.mm", - "face_detection_impl_mac_unittest.mm", - "text_detection_impl_mac_unittest.mm", - ] - libs = [ "CoreFoundation.framework", "CoreGraphics.framework", "QuartzCore.framework", ] - } else if (is_win) { - sources += [ "face_detection_impl_win_unittest.cc" ] } deps = [
diff --git a/services/shape_detection/text_detection_impl.h b/services/shape_detection/text_detection_impl.h index 617f6e8..30e8400a1 100644 --- a/services/shape_detection/text_detection_impl.h +++ b/services/shape_detection/text_detection_impl.h
@@ -11,6 +11,7 @@ class TextDetectionImpl { public: + // Binds TextDetection request to an implementation of mojom::TextDetection. static void Create(mojom::TextDetectionRequest request); private:
diff --git a/services/shape_detection/text_detection_impl_win.cc b/services/shape_detection/text_detection_impl_win.cc new file mode 100644 index 0000000..b1522e9 --- /dev/null +++ b/services/shape_detection/text_detection_impl_win.cc
@@ -0,0 +1,112 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/shape_detection/text_detection_impl_win.h" + +#include <windows.globalization.h> +#include <memory> +#include <utility> +#include <vector> + +#include "base/logging.h" +#include "base/win/core_winrt_util.h" +#include "base/win/scoped_hstring.h" +#include "base/win/windows_version.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "services/shape_detection/text_detection_impl.h" + +namespace shape_detection { + +using ABI::Windows::Globalization::ILanguageFactory; +using ABI::Windows::Media::Ocr::IOcrEngineStatics; +using base::win::GetActivationFactory; +using base::win::ScopedHString; + +// static +void TextDetectionImpl::Create(mojom::TextDetectionRequest request) { + // OcrEngine class is only available in Win 10 onwards (v10.0.10240.0) that + // documents in + // https://docs.microsoft.com/en-us/uwp/api/windows.media.ocr.ocrengine. + if (base::win::GetVersion() < base::win::VERSION_WIN10) { + DVLOG(1) << "Optical character recognition not supported before Windows 10"; + return; + } + DCHECK_GE(base::win::OSInfo::GetInstance()->version_number().build, 10240); + + // Loads functions dynamically at runtime to prevent library dependencies. + if (!(base::win::ResolveCoreWinRTDelayload() && + ScopedHString::ResolveCoreWinRTStringDelayload())) { + DLOG(ERROR) << "Failed loading functions from combase.dll"; + return; + } + + // Text Detection specification only supports Latin-1 text as documented in + // https://wicg.github.io/shape-detection-api/text.html#text-detection-api. + // TODO(junwei.fu): https://crbug.com/794097 consider supporting other Latin + // script language. + ScopedHString language_hstring = ScopedHString::Create("en"); + if (!language_hstring.is_valid()) + return; + + Microsoft::WRL::ComPtr<ILanguageFactory> language_factory; + HRESULT hr = + GetActivationFactory<ILanguageFactory, + RuntimeClass_Windows_Globalization_Language>( + &language_factory); + if (FAILED(hr)) { + DLOG(ERROR) << "ILanguage factory failed: " + << logging::SystemErrorCodeToString(hr); + return; + } + + Microsoft::WRL::ComPtr<ABI::Windows::Globalization::ILanguage> language; + hr = language_factory->CreateLanguage(language_hstring.get(), &language); + if (FAILED(hr)) { + DLOG(ERROR) << "Create language failed: " + << logging::SystemErrorCodeToString(hr); + return; + } + + Microsoft::WRL::ComPtr<IOcrEngineStatics> engine_factory; + hr = GetActivationFactory<IOcrEngineStatics, + RuntimeClass_Windows_Media_Ocr_OcrEngine>( + &engine_factory); + if (FAILED(hr)) { + DLOG(ERROR) << "IOcrEngineStatics factory failed: " + << logging::SystemErrorCodeToString(hr); + return; + } + + boolean is_supported = false; + hr = engine_factory->IsLanguageSupported(language.Get(), &is_supported); + if (FAILED(hr) || !is_supported) + return; + + Microsoft::WRL::ComPtr<IOcrEngine> ocr_engine; + hr = engine_factory->TryCreateFromLanguage(language.Get(), &ocr_engine); + if (FAILED(hr)) { + DLOG(ERROR) << "Create engine failed from language: " + << logging::SystemErrorCodeToString(hr); + return; + } + + mojo::MakeStrongBinding( + std::make_unique<TextDetectionImplWin>(std::move(ocr_engine)), + std::move(request)); +} + +TextDetectionImplWin::TextDetectionImplWin( + Microsoft::WRL::ComPtr<IOcrEngine> ocr_engine) + : ocr_engine_(std::move(ocr_engine)) { + DCHECK(ocr_engine_); +} + +TextDetectionImplWin::~TextDetectionImplWin() = default; + +void TextDetectionImplWin::Detect(const SkBitmap& bitmap, + DetectCallback callback) { + std::move(callback).Run(std::vector<mojom::TextDetectionResultPtr>()); +} + +} // namespace shape_detection
diff --git a/services/shape_detection/text_detection_impl_win.h b/services/shape_detection/text_detection_impl_win.h new file mode 100644 index 0000000..52e78d5a --- /dev/null +++ b/services/shape_detection/text_detection_impl_win.h
@@ -0,0 +1,37 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_SHAPE_DETECTION_TEXT_DETECTION_IMPL_WIN_H_ +#define SERVICES_SHAPE_DETECTION_TEXT_DETECTION_IMPL_WIN_H_ + +#include <windows.media.ocr.h> +#include <wrl/client.h> + +#include "base/macros.h" +#include "services/shape_detection/public/interfaces/textdetection.mojom.h" + +class SkBitmap; + +namespace shape_detection { + +using ABI::Windows::Media::Ocr::IOcrEngine; + +class TextDetectionImplWin : public mojom::TextDetection { + public: + explicit TextDetectionImplWin(Microsoft::WRL::ComPtr<IOcrEngine> ocr_engine); + ~TextDetectionImplWin() override; + + // mojom::TextDetection implementation. + void Detect(const SkBitmap& bitmap, + mojom::TextDetection::DetectCallback callback) override; + + private: + Microsoft::WRL::ComPtr<IOcrEngine> ocr_engine_; + + DISALLOW_COPY_AND_ASSIGN(TextDetectionImplWin); +}; + +} // namespace shape_detection + +#endif // SERVICES_SHAPE_DETECTION_TEXT_DETECTION_IMPL_WIN_H_
diff --git a/services/shape_detection/text_detection_impl_win_unittest.cc b/services/shape_detection/text_detection_impl_win_unittest.cc new file mode 100644 index 0000000..c8a286ef --- /dev/null +++ b/services/shape_detection/text_detection_impl_win_unittest.cc
@@ -0,0 +1,75 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" +#include "base/win/scoped_com_initializer.h" +#include "base/win/windows_version.h" +#include "mojo/public/cpp/bindings/strong_binding.h" +#include "services/shape_detection/public/interfaces/textdetection.mojom.h" +#include "services/shape_detection/text_detection_impl.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkImage.h" + +namespace shape_detection { + +namespace { + +void DetectTextCallback(base::Closure quit_closure, + size_t* num_text_chunks, + std::vector<mojom::TextDetectionResultPtr> results) { + *num_text_chunks = results.size(); + quit_closure.Run(); +} + +} // namespace + +class TextDetectionImplWinTest : public testing::Test { + protected: + TextDetectionImplWinTest() = default; + ~TextDetectionImplWinTest() override = default; + + void SetUp() override { + scoped_com_initializer_ = std::make_unique<base::win::ScopedCOMInitializer>( + base::win::ScopedCOMInitializer::kMTA); + ASSERT_TRUE(scoped_com_initializer_->Succeeded()); + } + + private: + std::unique_ptr<base::win::ScopedCOMInitializer> scoped_com_initializer_; + + base::test::ScopedTaskEnvironment scoped_task_environment_; + + DISALLOW_COPY_AND_ASSIGN(TextDetectionImplWinTest); +}; + +TEST_F(TextDetectionImplWinTest, ScanOnce) { + // OCR not supported before Windows 10 + if (base::win::GetVersion() < base::win::VERSION_WIN10) + return; + + mojom::TextDetectionPtr text_service; + auto request = mojo::MakeRequest(&text_service); + TextDetectionImpl::Create(std::move(request)); + + SkBitmap bitmap; + bitmap.allocN32Pixels(100, 100); + bitmap.eraseColor(SK_ColorBLUE); + + base::RunLoop run_loop; + size_t num_text_chunks = 1u; + text_service->Detect( + bitmap, base::BindOnce(&DetectTextCallback, run_loop.QuitClosure(), + &num_text_chunks)); + run_loop.Run(); + EXPECT_EQ(0u, num_text_chunks); +} + +} // namespace shape_detection
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/selectors/invalidation/any-link-pseudo.html b/third_party/WebKit/LayoutTests/external/wpt/css/selectors/invalidation/any-link-pseudo.html new file mode 100644 index 0000000..9792fd0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/css/selectors/invalidation/any-link-pseudo.html
@@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Selectors Invalidation: :any-link</title> + <link rel="author" title="Victoria Su" href="mailto:victoriaytsu@google.com"> + <link rel="help" href="https://drafts.csswg.org/selectors-4/#the-any-link-pseudo"> + <meta name="assert" content="This tests that the :any-link selector is effective"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <style> + #link { background-color: red } + #link:any-link { background-color: green } + #link + div { color: pink } + </style> + <a id="link">This link should have a green background.</a> + <div> + <div></div> + <div></div> + <div></div> + <div></div> + </div> + <script> + test(function() { + var red = "rgb(255, 0, 0)"; + var green = "rgb(0, 128, 0)"; + + assert_equals(getComputedStyle(link).backgroundColor, red); + + link.href = "not-visited.html"; + + assert_equals(getComputedStyle(link).backgroundColor, green); + }, "Style was recalculated for the :any-link pseudo class."); + + </script> + </head> +</html> \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/fast/css/invalidation/any-link-pseudo-expected.txt b/third_party/WebKit/LayoutTests/fast/css/invalidation/any-link-pseudo-expected.txt index 5ce6faf..79b403c 100644 --- a/third_party/WebKit/LayoutTests/fast/css/invalidation/any-link-pseudo-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/css/invalidation/any-link-pseudo-expected.txt
@@ -1,4 +1,4 @@ -Use descendant invalidation set for :-webkit-any-link pseudo class. +Use descendant invalidation set for :any-link pseudo class. On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/LayoutTests/fast/css/invalidation/any-link-pseudo.html b/third_party/WebKit/LayoutTests/fast/css/invalidation/any-link-pseudo.html index b9daa2e..5abc1668 100644 --- a/third_party/WebKit/LayoutTests/fast/css/invalidation/any-link-pseudo.html +++ b/third_party/WebKit/LayoutTests/fast/css/invalidation/any-link-pseudo.html
@@ -2,7 +2,7 @@ <script src="../../../resources/js-test.js"></script> <style> #link { background-color: red } -#link:-webkit-any-link { background-color: green } +#link:any-link { background-color: green } #link + div { color: pink } </style> <a id="link">This link should have a green background.</a> @@ -13,7 +13,7 @@ <div></div> </div> <script> -description("Use descendant invalidation set for :-webkit-any-link pseudo class.") +description("Use descendant invalidation set for :any-link pseudo class.") var red = "rgb(255, 0, 0)"; var green = "rgb(0, 128, 0)";
diff --git a/third_party/WebKit/LayoutTests/fast/css/invalidation/webkit-any-link-pseudo-expected.txt b/third_party/WebKit/LayoutTests/fast/css/invalidation/webkit-any-link-pseudo-expected.txt new file mode 100644 index 0000000..5ce6faf --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/css/invalidation/webkit-any-link-pseudo-expected.txt
@@ -0,0 +1,13 @@ +Use descendant invalidation set for :-webkit-any-link pseudo class. + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +PASS getComputedStyle(link).backgroundColor is red +PASS internals.updateStyleAndReturnAffectedElementCount() is 1 +PASS getComputedStyle(link).backgroundColor is green +PASS successfullyParsed is true + +TEST COMPLETE +This link should have a green background. +
diff --git a/third_party/WebKit/LayoutTests/fast/css/invalidation/webkit-any-link-pseudo.html b/third_party/WebKit/LayoutTests/fast/css/invalidation/webkit-any-link-pseudo.html new file mode 100644 index 0000000..b9daa2e --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/css/invalidation/webkit-any-link-pseudo.html
@@ -0,0 +1,30 @@ +<!DOCTYPE html> +<script src="../../../resources/js-test.js"></script> +<style> +#link { background-color: red } +#link:-webkit-any-link { background-color: green } +#link + div { color: pink } +</style> +<a id="link">This link should have a green background.</a> +<div> + <div></div> + <div></div> + <div></div> + <div></div> +</div> +<script> +description("Use descendant invalidation set for :-webkit-any-link pseudo class.") + +var red = "rgb(255, 0, 0)"; +var green = "rgb(0, 128, 0)"; + +shouldBe("getComputedStyle(link).backgroundColor", "red"); + +link.offsetTop; // Force recalc. +link.href = "not-visited.html"; + +if (window.internals) + shouldBe("internals.updateStyleAndReturnAffectedElementCount()", "1"); + +shouldBe("getComputedStyle(link).backgroundColor", "green"); +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/usecounter-pseudo-webkit-any-link.html b/third_party/WebKit/LayoutTests/fast/css/usecounter-pseudo-webkit-any-link.html deleted file mode 100644 index dbd9a8e..0000000 --- a/third_party/WebKit/LayoutTests/fast/css/usecounter-pseudo-webkit-any-link.html +++ /dev/null
@@ -1,24 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <title>Use counter test for :-webkit-any-link</title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <style id="style"></style> - <a href="http://google.com" id="link">Link</a> - <script> - 'use strict'; - test(() => { - let CSSSelectorPseudoWebkitAnyLink = 2260; // From UseCounter.h - let isCounted = () => internals.isUseCounted(document, CSSSelectorPseudoWebkitAnyLink); - var div = document.createElement('div'); - getComputedStyle(link); - assert_false(isCounted(), ':-webkit-any-link should not be counted'); - var style = document.getElementById('style'); - style.textContent = ':-webkit-any-link { color: blue; }'; - assert_equals(getComputedStyle(link).color, 'rgb(0, 0, 255)'); - assert_true(isCounted(), ':-webkit-any-link should be counted'); - }, ':-webkit-any-link counter is working'); - </script> - </head> -<html> \ No newline at end of file
diff --git a/third_party/WebKit/Source/core/css/CSSSelector.cpp b/third_party/WebKit/Source/core/css/CSSSelector.cpp index f170482..1d3af474 100644 --- a/third_party/WebKit/Source/core/css/CSSSelector.cpp +++ b/third_party/WebKit/Source/core/css/CSSSelector.cpp
@@ -215,6 +215,7 @@ case kPseudoVisited: case kPseudoAny: case kPseudoAnyLink: + case kPseudoWebkitAnyLink: case kPseudoAutofill: case kPseudoHover: case kPseudoDrag: @@ -303,7 +304,7 @@ {"-internal-video-persistent", CSSSelector::kPseudoVideoPersistent}, {"-internal-video-persistent-ancestor", CSSSelector::kPseudoVideoPersistentAncestor}, - {"-webkit-any-link", CSSSelector::kPseudoAnyLink}, + {"-webkit-any-link", CSSSelector::kPseudoWebkitAnyLink}, {"-webkit-autofill", CSSSelector::kPseudoAutofill}, {"-webkit-drag", CSSSelector::kPseudoDrag}, {"-webkit-full-page-media", CSSSelector::kPseudoFullPageMedia}, @@ -318,6 +319,7 @@ {"-webkit-scrollbar-track-piece", CSSSelector::kPseudoScrollbarTrackPiece}, {"active", CSSSelector::kPseudoActive}, {"after", CSSSelector::kPseudoAfter}, + {"any-link", CSSSelector::kPseudoAnyLink}, {"backdrop", CSSSelector::kPseudoBackdrop}, {"before", CSSSelector::kPseudoBefore}, {"checked", CSSSelector::kPseudoChecked}, @@ -619,6 +621,7 @@ case kPseudoValid: case kPseudoVertical: case kPseudoVisited: + case kPseudoWebkitAnyLink: case kPseudoWindowInactive: if (match_ != kPseudoClass) pseudo_type_ = kPseudoUnknown;
diff --git a/third_party/WebKit/Source/core/css/CSSSelector.h b/third_party/WebKit/Source/core/css/CSSSelector.h index 23f4d7e..9edd6cb7 100644 --- a/third_party/WebKit/Source/core/css/CSSSelector.h +++ b/third_party/WebKit/Source/core/css/CSSSelector.h
@@ -158,6 +158,7 @@ kPseudoVisited, kPseudoAny, kPseudoAnyLink, + kPseudoWebkitAnyLink, kPseudoAutofill, kPseudoHover, kPseudoDrag,
diff --git a/third_party/WebKit/Source/core/css/RuleFeatureSet.cpp b/third_party/WebKit/Source/core/css/RuleFeatureSet.cpp index 0f5607ac..12ccf5c 100644 --- a/third_party/WebKit/Source/core/css/RuleFeatureSet.cpp +++ b/third_party/WebKit/Source/core/css/RuleFeatureSet.cpp
@@ -90,6 +90,7 @@ case CSSSelector::kPseudoLink: case CSSSelector::kPseudoVisited: case CSSSelector::kPseudoAny: + case CSSSelector::kPseudoWebkitAnyLink: case CSSSelector::kPseudoAnyLink: case CSSSelector::kPseudoAutofill: case CSSSelector::kPseudoHover: @@ -436,6 +437,7 @@ case CSSSelector::kPseudoOnlyChild: case CSSSelector::kPseudoLink: case CSSSelector::kPseudoVisited: + case CSSSelector::kPseudoWebkitAnyLink: case CSSSelector::kPseudoAnyLink: case CSSSelector::kPseudoAutofill: case CSSSelector::kPseudoHover:
diff --git a/third_party/WebKit/Source/core/css/RuleSet.cpp b/third_party/WebKit/Source/core/css/RuleSet.cpp index 205bacf..425e44e 100644 --- a/third_party/WebKit/Source/core/css/RuleSet.cpp +++ b/third_party/WebKit/Source/core/css/RuleSet.cpp
@@ -119,6 +119,7 @@ case CSSSelector::kPseudoCue: case CSSSelector::kPseudoLink: case CSSSelector::kPseudoVisited: + case CSSSelector::kPseudoWebkitAnyLink: case CSSSelector::kPseudoAnyLink: case CSSSelector::kPseudoFocus: case CSSSelector::kPseudoPlaceholder: @@ -191,6 +192,7 @@ case CSSSelector::kPseudoLink: case CSSSelector::kPseudoVisited: case CSSSelector::kPseudoAnyLink: + case CSSSelector::kPseudoWebkitAnyLink: link_pseudo_class_rules_.push_back(rule_data); return true; case CSSSelector::kPseudoFocus:
diff --git a/third_party/WebKit/Source/core/css/SelectorChecker.cpp b/third_party/WebKit/Source/core/css/SelectorChecker.cpp index 77566c1..4a773c0 100644 --- a/third_party/WebKit/Source/core/css/SelectorChecker.cpp +++ b/third_party/WebKit/Source/core/css/SelectorChecker.cpp
@@ -900,7 +900,12 @@ ToHTMLFormControlElement(element).IsAutofilled(); case CSSSelector::kPseudoAnyLink: UseCounter::Count(context.element->GetDocument(), + WebFeature::kCSSSelectorPseudoAnyLink); + return element.IsLink(); + case CSSSelector::kPseudoWebkitAnyLink: + UseCounter::Count(context.element->GetDocument(), WebFeature::kCSSSelectorPseudoWebkitAnyLink); + // Fall through case CSSSelector::kPseudoLink: return element.IsLink(); case CSSSelector::kPseudoVisited:
diff --git a/third_party/WebKit/Source/core/dom/VisitedLinkState.cpp b/third_party/WebKit/Source/core/dom/VisitedLinkState.cpp index 8251512..5cff2b8e 100644 --- a/third_party/WebKit/Source/core/dom/VisitedLinkState.cpp +++ b/third_party/WebKit/Source/core/dom/VisitedLinkState.cpp
@@ -71,6 +71,7 @@ ToHTMLAnchorElement(node).InvalidateCachedVisitedLinkHash(); ToElement(node).PseudoStateChanged(CSSSelector::kPseudoLink); ToElement(node).PseudoStateChanged(CSSSelector::kPseudoVisited); + ToElement(node).PseudoStateChanged(CSSSelector::kPseudoWebkitAnyLink); ToElement(node).PseudoStateChanged(CSSSelector::kPseudoAnyLink); } if (IsShadowHost(&node)) { @@ -95,6 +96,7 @@ if (node.IsLink() && LinkHashForElement(ToElement(node)) == link_hash) { ToElement(node).PseudoStateChanged(CSSSelector::kPseudoLink); ToElement(node).PseudoStateChanged(CSSSelector::kPseudoVisited); + ToElement(node).PseudoStateChanged(CSSSelector::kPseudoWebkitAnyLink); ToElement(node).PseudoStateChanged(CSSSelector::kPseudoAnyLink); } if (IsShadowHost(&node))
diff --git a/third_party/WebKit/Source/core/frame/UseCounterTest.cpp b/third_party/WebKit/Source/core/frame/UseCounterTest.cpp index 9741df4..47f53431 100644 --- a/third_party/WebKit/Source/core/frame/UseCounterTest.cpp +++ b/third_party/WebKit/Source/core/frame/UseCounterTest.cpp
@@ -57,6 +57,8 @@ LocalFrame* GetFrame() { return &dummy_->GetFrame(); } void SetIsViewSource() { dummy_->GetDocument().SetIsViewSource(true); } void SetURL(const KURL& url) { dummy_->GetDocument().SetURL(url); } + Document& GetDocument() { return dummy_->GetDocument(); } + template <typename T> void HistogramBasicTest(const std::string& histogram, T item, @@ -245,6 +247,22 @@ [&](LocalFrame* frame) { use_counter.DidCommitLoad(frame); }, kSvgUrl); } +TEST_F(UseCounterTest, CSSSelectorPseudoAnyLink) { + UseCounter use_counter; + WebFeature feature = WebFeature::kCSSSelectorPseudoAnyLink; + EXPECT_FALSE(use_counter.IsCounted(GetDocument(), feature)); + use_counter.Count(GetDocument(), feature); + EXPECT_TRUE(use_counter.IsCounted(GetDocument(), feature)); +} + +TEST_F(UseCounterTest, CSSSelectorPseudoWebkitAnyLink) { + UseCounter use_counter; + WebFeature feature = WebFeature::kCSSSelectorPseudoWebkitAnyLink; + EXPECT_FALSE(use_counter.IsCounted(GetDocument(), feature)); + use_counter.Count(GetDocument(), feature); + EXPECT_TRUE(use_counter.IsCounted(GetDocument(), feature)); +} + TEST_F(UseCounterTest, InspectorDisablesMeasurement) { UseCounter use_counter;
diff --git a/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp b/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp index 5708793..85973ddd 100644 --- a/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLAnchorElement.cpp
@@ -203,6 +203,7 @@ if (was_link || IsLink()) { PseudoStateChanged(CSSSelector::kPseudoLink); PseudoStateChanged(CSSSelector::kPseudoVisited); + PseudoStateChanged(CSSSelector::kPseudoWebkitAnyLink); PseudoStateChanged(CSSSelector::kPseudoAnyLink); } if (IsLink()) {
diff --git a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp index 91547ca..cc699f5 100644 --- a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp +++ b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
@@ -257,6 +257,7 @@ DEFINE_STRING_MAPPING(PseudoLink) DEFINE_STRING_MAPPING(PseudoVisited) DEFINE_STRING_MAPPING(PseudoAny) + DEFINE_STRING_MAPPING(PseudoWebkitAnyLink) DEFINE_STRING_MAPPING(PseudoAnyLink) DEFINE_STRING_MAPPING(PseudoAutofill) DEFINE_STRING_MAPPING(PseudoHover)
diff --git a/third_party/WebKit/Source/core/svg/SVGAElement.cpp b/third_party/WebKit/Source/core/svg/SVGAElement.cpp index 1eb1be6..0c7769d6 100644 --- a/third_party/WebKit/Source/core/svg/SVGAElement.cpp +++ b/third_party/WebKit/Source/core/svg/SVGAElement.cpp
@@ -88,6 +88,7 @@ if (was_link || IsLink()) { PseudoStateChanged(CSSSelector::kPseudoLink); PseudoStateChanged(CSSSelector::kPseudoVisited); + PseudoStateChanged(CSSSelector::kPseudoWebkitAnyLink); PseudoStateChanged(CSSSelector::kPseudoAnyLink); } return;
diff --git a/third_party/WebKit/Source/modules/fetch/DEPS b/third_party/WebKit/Source/modules/fetch/DEPS index 3cf197d..a7cc136 100644 --- a/third_party/WebKit/Source/modules/fetch/DEPS +++ b/third_party/WebKit/Source/modules/fetch/DEPS
@@ -3,7 +3,6 @@ "-modules", "+modules/ModulesExport.h", "+modules/fetch", - "+modules/credentialmanager", "+mojo/public/cpp/system/data_pipe.h", "+mojo/public/cpp/system/simple_watcher.h", "+services/network/public/interfaces",
diff --git a/third_party/WebKit/public/platform/web_feature.mojom b/third_party/WebKit/public/platform/web_feature.mojom index ef5107e..e12a515 100644 --- a/third_party/WebKit/public/platform/web_feature.mojom +++ b/third_party/WebKit/public/platform/web_feature.mojom
@@ -1772,6 +1772,7 @@ kAudioWorkletNodeConstructor = 2263, kHTMLMediaElementEmptyLoadWithFutureData = 2264, kCSSValueDisplayContents = 2265, + kCSSSelectorPseudoAnyLink = 2266, // Add new features immediately above this line. Don't change assigned // numbers of any item, and don't reuse removed slots.
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index b6e0bd9..9aeb4dd 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -17239,6 +17239,7 @@ <int value="2263" label="AudioWorkletNodeConstructor"/> <int value="2264" label="HTMLMediaElementEmptyLoadWithFutureData"/> <int value="2265" label="CSSValueDisplayContents"/> + <int value="2266" label="CSSSelectorPseudoAnyLink"/> </enum> <enum name="FeedbackSource">
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc index aedc2838..fcc808f4 100644 --- a/ui/aura/mus/window_port_mus.cc +++ b/ui/aura/mus/window_port_mus.cc
@@ -91,6 +91,10 @@ touch_insets); } +void WindowPortMus::SetHitTestMask(const base::Optional<gfx::Rect>& rect) { + window_tree_client_->SetHitTestMask(this, rect); +} + void WindowPortMus::Embed( ui::mojom::WindowTreeClientPtr client, uint32_t flags,
diff --git a/ui/aura/mus/window_port_mus.h b/ui/aura/mus/window_port_mus.h index 732ca7b..318ea8fc 100644 --- a/ui/aura/mus/window_port_mus.h +++ b/ui/aura/mus/window_port_mus.h
@@ -13,6 +13,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "base/optional.h" #include "components/viz/client/client_layer_tree_frame_sink.h" #include "components/viz/common/surfaces/surface_info.h" #include "services/ui/public/interfaces/cursor/cursor.mojom.h" @@ -85,6 +86,9 @@ void SetExtendedHitRegionForChildren(const gfx::Insets& mouse_insets, const gfx::Insets& touch_insets); + // See description in mojom for details on this. + void SetHitTestMask(const base::Optional<gfx::Rect>& rect); + // Embeds a new client in this Window. See WindowTreeClient::Embed() for // details on arguments. void Embed(ui::mojom::WindowTreeClientPtr client,
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc index 3f26feb1..115ae85 100644 --- a/ui/aura/mus/window_tree_client.cc +++ b/ui/aura/mus/window_tree_client.cc
@@ -361,6 +361,18 @@ tree_->SetImeVisibility(window->server_id(), visible, std::move(state)); } +void WindowTreeClient::SetHitTestMask( + WindowMus* window, + const base::Optional<gfx::Rect>& mask_rect) { + base::Optional<gfx::Rect> out_rect = base::nullopt; + if (mask_rect) { + out_rect = gfx::ConvertRectToPixel(window->GetDeviceScaleFactor(), + mask_rect.value()); + } + + tree_->SetHitTestMask(window->server_id(), out_rect); +} + void WindowTreeClient::Embed( Window* window, ui::mojom::WindowTreeClientPtr client, @@ -2240,20 +2252,6 @@ additional_client_areas_in_pixel); } -void WindowTreeClient::OnWindowTreeHostHitTestMaskWillChange( - WindowTreeHostMus* window_tree_host, - const base::Optional<gfx::Rect>& mask_rect) { - WindowMus* window = WindowMus::Get(window_tree_host->window()); - - base::Optional<gfx::Rect> out_rect = base::nullopt; - if (mask_rect) { - out_rect = gfx::ConvertRectToPixel(window->GetDeviceScaleFactor(), - mask_rect.value()); - } - - tree_->SetHitTestMask(window->server_id(), out_rect); -} - void WindowTreeClient::OnWindowTreeHostSetOpacity( WindowTreeHostMus* window_tree_host, float opacity) {
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h index 1906821..c59b8a9 100644 --- a/ui/aura/mus/window_tree_client.h +++ b/ui/aura/mus/window_tree_client.h
@@ -18,6 +18,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "base/optional.h" #include "base/single_thread_task_runner.h" #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" #include "mojo/public/cpp/bindings/associated_binding.h" @@ -146,6 +147,7 @@ void SetImeVisibility(WindowMus* window, bool visible, ui::mojom::TextInputStatePtr state); + void SetHitTestMask(WindowMus* window, const base::Optional<gfx::Rect>& rect); // Embeds a new client in |window|. |flags| is a bitmask of the values defined // by kEmbedFlag*; 0 gives default behavior. |callback| is called to indicate @@ -548,9 +550,6 @@ WindowTreeHostMus* window_tree_host, const gfx::Insets& client_area, const std::vector<gfx::Rect>& additional_client_areas) override; - void OnWindowTreeHostHitTestMaskWillChange( - WindowTreeHostMus* window_tree_host, - const base::Optional<gfx::Rect>& mask_rect) override; void OnWindowTreeHostSetOpacity(WindowTreeHostMus* window_tree_host, float opacity) override; void OnWindowTreeHostDeactivateWindow(
diff --git a/ui/aura/mus/window_tree_host_mus.cc b/ui/aura/mus/window_tree_host_mus.cc index 1552c67..6d3a3f2 100644 --- a/ui/aura/mus/window_tree_host_mus.cc +++ b/ui/aura/mus/window_tree_host_mus.cc
@@ -130,10 +130,6 @@ additional_client_area); } -void WindowTreeHostMus::SetHitTestMask(const base::Optional<gfx::Rect>& rect) { - delegate_->OnWindowTreeHostHitTestMaskWillChange(this, rect); -} - void WindowTreeHostMus::SetOpacity(float value) { delegate_->OnWindowTreeHostSetOpacity(this, value); }
diff --git a/ui/aura/mus/window_tree_host_mus.h b/ui/aura/mus/window_tree_host_mus.h index 1457f35e..6896e19 100644 --- a/ui/aura/mus/window_tree_host_mus.h +++ b/ui/aura/mus/window_tree_host_mus.h
@@ -52,10 +52,6 @@ void SetClientArea(const gfx::Insets& insets, const std::vector<gfx::Rect>& additional_client_area); - // Sets the hit test mask on the underlying mus window. Pass base::nullopt to - // clear. - void SetHitTestMask(const base::Optional<gfx::Rect>& rect); - // Sets the opacity of the underlying mus window. void SetOpacity(float value);
diff --git a/ui/aura/mus/window_tree_host_mus_delegate.h b/ui/aura/mus/window_tree_host_mus_delegate.h index d2deb47..e9131d8 100644 --- a/ui/aura/mus/window_tree_host_mus_delegate.h +++ b/ui/aura/mus/window_tree_host_mus_delegate.h
@@ -37,11 +37,6 @@ const gfx::Insets& client_area, const std::vector<gfx::Rect>& additional_client_areas) = 0; - // Called when the hit test mask is about to be cleared. - virtual void OnWindowTreeHostHitTestMaskWillChange( - WindowTreeHostMus* window_tree_host, - const base::Optional<gfx::Rect>& mask_rect) = 0; - // Called when the opacity is changed client side. virtual void OnWindowTreeHostSetOpacity(WindowTreeHostMus* window_tree_host, float opacity) = 0;
diff --git a/ui/aura/mus/window_tree_host_mus_unittest.cc b/ui/aura/mus/window_tree_host_mus_unittest.cc index 056c3e8f..3d7714e 100644 --- a/ui/aura/mus/window_tree_host_mus_unittest.cc +++ b/ui/aura/mus/window_tree_host_mus_unittest.cc
@@ -5,6 +5,7 @@ #include "ui/aura/mus/window_tree_host_mus.h" #include "base/memory/ptr_util.h" +#include "ui/aura/mus/window_port_mus.h" #include "ui/aura/mus/window_tree_host_mus_init_params.h" #include "ui/aura/test/aura_mus_test_base.h" #include "ui/aura/test/mus/test_window_tree.h" @@ -30,11 +31,12 @@ EXPECT_FALSE(window_tree()->last_hit_test_mask().has_value()); gfx::Rect mask(10, 10, 10, 10); - window_tree_host_mus->SetHitTestMask(mask); + WindowPortMus::Get(window_tree_host_mus->window())->SetHitTestMask(mask); ASSERT_TRUE(window_tree()->last_hit_test_mask().has_value()); EXPECT_EQ(mask, window_tree()->last_hit_test_mask()); - window_tree_host_mus->SetHitTestMask(base::nullopt); + WindowPortMus::Get(window_tree_host_mus->window()) + ->SetHitTestMask(base::nullopt); ASSERT_FALSE(window_tree()->last_hit_test_mask().has_value()); }
diff --git a/ui/views/mus/desktop_window_tree_host_mus.cc b/ui/views/mus/desktop_window_tree_host_mus.cc index 8d5bf470..b24db3f 100644 --- a/ui/views/mus/desktop_window_tree_host_mus.cc +++ b/ui/views/mus/desktop_window_tree_host_mus.cc
@@ -253,7 +253,7 @@ void DesktopWindowTreeHostMus::SendHitTestMaskToServer() { if (!native_widget_delegate_->HasHitTestMask()) { - SetHitTestMask(base::nullopt); + aura::WindowPortMus::Get(window())->SetHitTestMask(base::nullopt); return; } @@ -262,7 +262,7 @@ // TODO(jamescook): Use the full path for the mask. gfx::Rect mask_rect = gfx::ToEnclosingRect(gfx::SkRectToRectF(mask_path.getBounds())); - SetHitTestMask(mask_rect); + aura::WindowPortMus::Get(window())->SetHitTestMask(mask_rect); } bool DesktopWindowTreeHostMus::IsFocusClientInstalledOnFocusSynchronizer()
diff --git a/ui/wm/BUILD.gn b/ui/wm/BUILD.gn index f16ed5a..4872ecd 100644 --- a/ui/wm/BUILD.gn +++ b/ui/wm/BUILD.gn
@@ -125,6 +125,7 @@ "core/compound_event_filter_unittest.cc", "core/coordinate_conversion_unittest.cc", "core/cursor_manager_unittest.cc", + "core/easy_resize_window_targeter_unittest.cc", "core/focus_controller_unittest.cc", "core/shadow_controller_unittest.cc", "core/shadow_unittest.cc",
diff --git a/ui/wm/core/easy_resize_window_targeter.cc b/ui/wm/core/easy_resize_window_targeter.cc index 6986a19..9ac6839a 100644 --- a/ui/wm/core/easy_resize_window_targeter.cc +++ b/ui/wm/core/easy_resize_window_targeter.cc
@@ -23,17 +23,65 @@ // Returns an insets whose values are all negative or 0. Any positive value is // forced to 0. gfx::Insets InsetsWithOnlyNegativeValues(const gfx::Insets& insets) { - if (insets.top() > 0 || insets.left() > 0 || insets.right() > 0 || - insets.bottom() > 0) { - // See TODO at call site. - NOTIMPLEMENTED_LOG_ONCE(); - } return gfx::Insets(std::min(0, insets.top()), std::min(0, insets.left()), std::min(0, insets.bottom()), std::min(0, insets.right())); } +gfx::Insets InsetsWithOnlyPositiveValues(const gfx::Insets& insets) { + return gfx::Insets(std::max(0, insets.top()), std::max(0, insets.left()), + std::max(0, insets.bottom()), std::max(0, insets.right())); +} + } // namespace +// HitMaskSetter is responsible for setting the hit-test mask on a Window. +class EasyResizeWindowTargeter::HitMaskSetter : public aura::WindowObserver { + public: + explicit HitMaskSetter(aura::Window* window) : window_(window) { + window_->AddObserver(this); + } + ~HitMaskSetter() override { + if (window_) { + aura::WindowPortMus::Get(window_)->SetHitTestMask(base::nullopt); + window_->RemoveObserver(this); + } + } + + void SetHitMaskInsets(const gfx::Insets& insets) { + if (insets == insets_) + return; + + insets_ = insets; + ApplyHitTestMask(); + } + + private: + void ApplyHitTestMask() { + base::Optional<gfx::Rect> hit_test_mask( + gfx::Rect(window_->bounds().size())); + hit_test_mask->Inset(insets_); + aura::WindowPortMus::Get(window_)->SetHitTestMask(hit_test_mask); + } + + // aura::WindowObserver: + void OnWindowDestroying(aura::Window* window) override { + window_->RemoveObserver(this); + window_ = nullptr; + } + void OnWindowBoundsChanged(aura::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds, + ui::PropertyChangeReason reason) override { + ApplyHitTestMask(); + } + + private: + aura::Window* window_; + gfx::Insets insets_; + + DISALLOW_COPY_AND_ASSIGN(HitMaskSetter); +}; + EasyResizeWindowTargeter::EasyResizeWindowTargeter( aura::Window* container, const gfx::Insets& mouse_extend, @@ -52,8 +100,6 @@ return; // Mus only accepts 0 or negative values, force all values to fit that. - // TODO: figure out how best to deal with positive values, see - // http://crbug.com/775223 const gfx::Insets effective_last_mouse_extend = InsetsWithOnlyNegativeValues(last_mouse_extend); const gfx::Insets effective_last_touch_extend = @@ -62,14 +108,23 @@ InsetsWithOnlyNegativeValues(mouse_extend()); const gfx::Insets effective_touch_extend = InsetsWithOnlyNegativeValues(touch_extend()); - if (effective_last_touch_extend == effective_touch_extend && - effective_last_mouse_extend == effective_mouse_extend) { - return; + if (effective_last_touch_extend != effective_touch_extend || + effective_last_mouse_extend != effective_mouse_extend) { + aura::WindowPortMus::Get(container_) + ->SetExtendedHitRegionForChildren(effective_mouse_extend, + effective_touch_extend); } - aura::WindowPortMus::Get(container_) - ->SetExtendedHitRegionForChildren(effective_mouse_extend, - effective_touch_extend); + // Positive values equate to a hit test mask. + const gfx::Insets positive_mouse_insets = + InsetsWithOnlyPositiveValues(mouse_extend()); + if (positive_mouse_insets.IsEmpty()) { + hit_mask_setter_.reset(); + } else { + if (!hit_mask_setter_) + hit_mask_setter_ = std::make_unique<HitMaskSetter>(container_); + hit_mask_setter_->SetHitMaskInsets(positive_mouse_insets); + } } bool EasyResizeWindowTargeter::EventLocationInsideBounds(
diff --git a/ui/wm/core/easy_resize_window_targeter.h b/ui/wm/core/easy_resize_window_targeter.h index 100be11..d8ffa70 100644 --- a/ui/wm/core/easy_resize_window_targeter.h +++ b/ui/wm/core/easy_resize_window_targeter.h
@@ -31,6 +31,8 @@ const gfx::Insets& last_touch_extend) override; private: + class HitMaskSetter; + // aura::WindowTargeter: // Delegates to WindowTargeter's impl and prevents overriding in subclasses. bool EventLocationInsideBounds(aura::Window* target, @@ -42,6 +44,8 @@ aura::Window* container_; + std::unique_ptr<HitMaskSetter> hit_mask_setter_; + DISALLOW_COPY_AND_ASSIGN(EasyResizeWindowTargeter); };
diff --git a/ui/wm/core/easy_resize_window_targeter_unittest.cc b/ui/wm/core/easy_resize_window_targeter_unittest.cc new file mode 100644 index 0000000..092a17b --- /dev/null +++ b/ui/wm/core/easy_resize_window_targeter_unittest.cc
@@ -0,0 +1,75 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/wm/core/easy_resize_window_targeter.h" + +#include "ui/aura/mus/window_port_mus.h" +#include "ui/aura/test/aura_mus_test_base.h" +#include "ui/aura/test/mus/test_window_tree.h" +#include "ui/aura/window.h" +#include "ui/compositor/layer_type.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/rect.h" + +namespace wm { + +namespace { + +class TestEasyResizeWindowTargeter : public EasyResizeWindowTargeter { + public: + explicit TestEasyResizeWindowTargeter(aura::Window* window) + : EasyResizeWindowTargeter(window, gfx::Insets(), gfx::Insets()) {} + ~TestEasyResizeWindowTargeter() override = default; + + void SetInsets(const gfx::Insets& mouse_extend, + const gfx::Insets& touch_extend) { + EasyResizeWindowTargeter::SetInsets(mouse_extend, touch_extend); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestEasyResizeWindowTargeter); +}; + +} // namespace + +using EasyResizeWindowTargeterTest = aura::test::AuraMusClientTestBase; + +TEST_F(EasyResizeWindowTargeterTest, SetHitTestMask) { + aura::Window window(nullptr); + window.Init(ui::LAYER_NOT_DRAWN); + TestEasyResizeWindowTargeter window_targeter(&window); + const gfx::Rect bounds1 = gfx::Rect(10, 20, 200, 300); + window.SetBounds(bounds1); + const gfx::Insets insets1(1, 2, 3, 4); + window_targeter.SetInsets(insets1, insets1); + ASSERT_TRUE(window_tree()->last_hit_test_mask().has_value()); + EXPECT_EQ(gfx::Rect(insets1.left(), insets1.top(), + bounds1.width() - insets1.width(), + bounds1.height() - insets1.height()), + *window_tree()->last_hit_test_mask()); + + // Adjusting the bounds should trigger resetting the mask. + const gfx::Rect bounds2 = gfx::Rect(10, 20, 300, 400); + window.SetBounds(bounds2); + ASSERT_TRUE(window_tree()->last_hit_test_mask().has_value()); + EXPECT_EQ(gfx::Rect(insets1.left(), insets1.top(), + bounds2.width() - insets1.width(), + bounds2.height() - insets1.height()), + *window_tree()->last_hit_test_mask()); + + // Empty insets should reset the mask. + window_targeter.SetInsets(gfx::Insets(), gfx::Insets()); + EXPECT_FALSE(window_tree()->last_hit_test_mask().has_value()); + + const gfx::Insets insets2(-1, 3, 4, 5); + const gfx::Insets effective_insets2(0, 3, 4, 5); + window_targeter.SetInsets(insets2, insets2); + ASSERT_TRUE(window_tree()->last_hit_test_mask().has_value()); + EXPECT_EQ(gfx::Rect(effective_insets2.left(), effective_insets2.top(), + bounds2.width() - effective_insets2.width(), + bounds2.height() - effective_insets2.height()), + *window_tree()->last_hit_test_mask()); +} + +} // namespace wm