diff options
author | yzshen <yzshen@chromium.org> | 2015-08-21 19:41:13 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-08-22 02:41:48 +0000 |
commit | 020dd26e26903bf63564601cbf31670f7cda6854 (patch) | |
tree | dc7509aa4b4dea7e1b428dc585091123edfdeb1d | |
parent | 10032d2ee6783d9848bb04bafa245ec63916d039 (diff) | |
download | chromium_src-020dd26e26903bf63564601cbf31670f7cda6854.zip chromium_src-020dd26e26903bf63564601cbf31670f7cda6854.tar.gz chromium_src-020dd26e26903bf63564601cbf31670f7cda6854.tar.bz2 |
Mandoline: Introduce a tab-level DevTools agent and persist state across navigations.
The tab-level agent (i.e., FrameDevToolsAgent) relays messages between the DevTools service and the
DevTools agent of the frame that it attaches to.
It persists state across frame navigations and also intercepts "Page.navigate" requests.
BUG=517266
Review URL: https://codereview.chromium.org/1288653004
Cr-Commit-Position: refs/heads/master@{#344956}
25 files changed, 514 insertions, 150 deletions
diff --git a/components/devtools_service/devtools_agent_host.cc b/components/devtools_service/devtools_agent_host.cc index d344a59..30b2287 100644 --- a/components/devtools_service/devtools_agent_host.cc +++ b/components/devtools_service/devtools_agent_host.cc @@ -4,17 +4,11 @@ #include "components/devtools_service/devtools_agent_host.h" -#include "base/guid.h" -#include "base/logging.h" - namespace devtools_service { -DevToolsAgentHost::DevToolsAgentHost(DevToolsAgentPtr agent) - : id_(base::GenerateGUID()), - agent_(agent.Pass()), - binding_(this), - delegate_(nullptr) { -} +DevToolsAgentHost::DevToolsAgentHost(const std::string& id, + DevToolsAgentPtr agent) + : id_(id), agent_(agent.Pass()), binding_(this), delegate_(nullptr) {} DevToolsAgentHost::~DevToolsAgentHost() { if (delegate_) @@ -29,7 +23,7 @@ void DevToolsAgentHost::SetDelegate(Delegate* delegate) { DevToolsAgentClientPtr client; binding_.Bind(&client); - agent_->SetClient(client.Pass(), id_); + agent_->SetClient(client.Pass()); } else { if (!binding_.is_bound()) return; @@ -42,7 +36,9 @@ void DevToolsAgentHost::SendProtocolMessageToAgent(const std::string& message) { agent_->DispatchProtocolMessage(message); } -void DevToolsAgentHost::DispatchProtocolMessage(const mojo::String& message) { +void DevToolsAgentHost::DispatchProtocolMessage(int32_t call_id, + const mojo::String& message, + const mojo::String& state) { delegate_->DispatchProtocolMessage(this, message); } diff --git a/components/devtools_service/devtools_agent_host.h b/components/devtools_service/devtools_agent_host.h index 84591c7..131307a 100644 --- a/components/devtools_service/devtools_agent_host.h +++ b/components/devtools_service/devtools_agent_host.h @@ -28,7 +28,7 @@ class DevToolsAgentHost : public DevToolsAgentClient { virtual void OnAgentHostClosed(DevToolsAgentHost* agent_host) = 0; }; - explicit DevToolsAgentHost(DevToolsAgentPtr agent); + DevToolsAgentHost(const std::string& id, DevToolsAgentPtr agent); ~DevToolsAgentHost() override; @@ -49,7 +49,9 @@ class DevToolsAgentHost : public DevToolsAgentClient { private: // DevToolsAgentClient implementation. - void DispatchProtocolMessage(const mojo::String& message) override; + void DispatchProtocolMessage(int32_t call_id, + const mojo::String& message, + const mojo::String& state) override; const std::string id_; diff --git a/components/devtools_service/devtools_registry_impl.cc b/components/devtools_service/devtools_registry_impl.cc index 3fc7e0d..5e402e8 100644 --- a/components/devtools_service/devtools_registry_impl.cc +++ b/components/devtools_service/devtools_registry_impl.cc @@ -36,13 +36,14 @@ DevToolsAgentHost* DevToolsRegistryImpl::GetAgentById(const std::string& id) { return iter->second.get(); } -void DevToolsRegistryImpl::RegisterAgent(DevToolsAgentPtr agent) { - linked_ptr<DevToolsAgentHost> agent_host(new DevToolsAgentHost(agent.Pass())); - std::string id = agent_host->id(); +void DevToolsRegistryImpl::RegisterAgent(const mojo::String& id, + DevToolsAgentPtr agent) { + linked_ptr<DevToolsAgentHost> agent_host( + new DevToolsAgentHost(id, agent.Pass())); agent_host->set_agent_connection_error_handler( [this, id]() { OnAgentConnectionError(id); }); - agents_[agent_host->id()] = agent_host; + agents_[id] = agent_host; } void DevToolsRegistryImpl::OnAgentConnectionError(const std::string& id) { diff --git a/components/devtools_service/devtools_registry_impl.h b/components/devtools_service/devtools_registry_impl.h index 431f39a..64974f5 100644 --- a/components/devtools_service/devtools_registry_impl.h +++ b/components/devtools_service/devtools_registry_impl.h @@ -46,7 +46,7 @@ class DevToolsRegistryImpl : public DevToolsRegistry { private: // DevToolsRegistry implementation. - void RegisterAgent(DevToolsAgentPtr agent) override; + void RegisterAgent(const mojo::String& id, DevToolsAgentPtr agent) override; void OnAgentConnectionError(const std::string& id); diff --git a/components/devtools_service/public/interfaces/devtools_service.mojom b/components/devtools_service/public/interfaces/devtools_service.mojom index 80a2ad8..83d4750 100644 --- a/components/devtools_service/public/interfaces/devtools_service.mojom +++ b/components/devtools_service/public/interfaces/devtools_service.mojom @@ -21,8 +21,10 @@ interface DevToolsCoordinator { }; interface DevToolsRegistry { - // Registers a DevTools agent. - RegisterAgent(DevToolsAgent agent); + // Registers a DevTools agent. |id| is the agent ID, which is used to identify + // the agent when the service and its clients communicate using the Chrome + // remote debugging protocol. + RegisterAgent(string id, DevToolsAgent agent); }; interface DevToolsAgent { @@ -30,14 +32,17 @@ interface DevToolsAgent { // DispatchProtocolMessage() calls. If a client doesn't want to receive // messages anymore, it could simply close the underlying message pipe of // |client|. - SetClient(DevToolsAgentClient client, string client_id); + SetClient(DevToolsAgentClient client); // Sends a command (in remote debugging protocol JSON format) to the agent. DispatchProtocolMessage(string message); }; interface DevToolsAgentClient { - // Sends a notification or response message (in remote debugging protocol JSON - // format) to the client. - DispatchProtocolMessage(string message); + // Sends a notification or response message to the client. |message| is in + // remote debugging protocol JSON format. |call_id| is the "id" field of the + // message or 0 if such a field doesn't exist. If not empty or null, |state| + // is the state of the DevTools agent at the point when generating this + // message. + DispatchProtocolMessage(int32 call_id, string message, string? state); }; diff --git a/components/html_viewer/BUILD.gn b/components/html_viewer/BUILD.gn index 28dadba..359b457 100644 --- a/components/html_viewer/BUILD.gn +++ b/components/html_viewer/BUILD.gn @@ -139,7 +139,6 @@ source_set("lib") { "//cc/blink", "//cc/surfaces", "//components/clipboard/public/interfaces", - "//components/devtools_service/public/cpp", "//components/devtools_service/public/interfaces", "//components/html_viewer/public/interfaces", "//components/message_port", diff --git a/components/html_viewer/devtools_agent_impl.cc b/components/html_viewer/devtools_agent_impl.cc index 26de27a..bc4c871 100644 --- a/components/html_viewer/devtools_agent_impl.cc +++ b/components/html_viewer/devtools_agent_impl.cc @@ -4,94 +4,59 @@ #include "components/html_viewer/devtools_agent_impl.h" -#include <string> - -#include "base/json/json_reader.h" +#include "base/bind.h" #include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/values.h" -#include "mojo/application/public/cpp/connect.h" -#include "mojo/application/public/interfaces/shell.mojom.h" #include "third_party/WebKit/public/platform/WebString.h" -#include "third_party/WebKit/public/platform/WebURLRequest.h" #include "third_party/WebKit/public/web/WebDevToolsAgent.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" namespace html_viewer { DevToolsAgentImpl::DevToolsAgentImpl(blink::WebLocalFrame* frame, - mojo::Shell* shell) - : frame_(frame), binding_(this), handling_page_navigate_request_(false) { + const std::string& id, + const std::string* state) + : frame_(frame), id_(id), binding_(this), cache_until_client_ready_(false) { DCHECK(frame); - DCHECK(shell); - - mojo::ServiceProviderPtr devtools_service_provider; - mojo::URLRequestPtr request(mojo::URLRequest::New()); - request->url = "mojo:devtools_service"; - shell->ConnectToApplication(request.Pass(), - mojo::GetProxy(&devtools_service_provider), - nullptr, - nullptr); - devtools_service::DevToolsRegistryPtr devtools_registry; - mojo::ConnectToService(devtools_service_provider.get(), &devtools_registry); - - devtools_service::DevToolsAgentPtr agent; - binding_.Bind(&agent); - devtools_registry->RegisterAgent(agent.Pass()); - frame_->setDevToolsAgentClient(this); + + if (state) { + cache_until_client_ready_ = true; + frame_->devToolsAgent()->reattach(blink::WebString::fromUTF8(id_), + blink::WebString::fromUTF8(*state)); + } } DevToolsAgentImpl::~DevToolsAgentImpl() { - if (client_) + if (cache_until_client_ready_ || client_) frame_->devToolsAgent()->detach(); } +void DevToolsAgentImpl::BindToRequest( + mojo::InterfaceRequest<DevToolsAgent> request) { + binding_.Bind(request.Pass()); +} + void DevToolsAgentImpl::SetClient( - devtools_service::DevToolsAgentClientPtr client, - const mojo::String& client_id) { + devtools_service::DevToolsAgentClientPtr client) { if (client_) frame_->devToolsAgent()->detach(); client_ = client.Pass(); - client_.set_connection_error_handler([this]() { OnConnectionError(); }); - - frame_->devToolsAgent()->attach(blink::WebString::fromUTF8(client_id)); + client_.set_connection_error_handler(base::Bind( + &DevToolsAgentImpl::OnConnectionError, base::Unretained(this))); + + if (cache_until_client_ready_) { + cache_until_client_ready_ = false; + for (const auto& message : cached_client_messages_) + client_->DispatchProtocolMessage(message.call_id, message.response, + message.state); + cached_client_messages_.clear(); + } else { + frame_->devToolsAgent()->attach(blink::WebString::fromUTF8(id_)); + } } void DevToolsAgentImpl::DispatchProtocolMessage(const mojo::String& message) { - // TODO(yzshen): (1) Eventually the handling code for Page.navigate (and some - // other commands) should live with the code managing tabs and navigation. - // We will need a DevToolsAgent implementation there as well, which handles - // some of the commands and relays messages between the DevTools service and - // the HTML viewer. - // (2) Consider refactoring and reusing the existing DevTools protocol parsing - // code. - do { - scoped_ptr<base::Value> value = base::JSONReader::Read(message.get()); - base::DictionaryValue* command = nullptr; - if (!value || !value->GetAsDictionary(&command)) - break; - - std::string method; - if (!command->GetString("method", &method) || method != "Page.navigate") - break; - - std::string url_string; - if (!command->GetString("params.url", &url_string)) - break; - - GURL url(url_string); - if (!url.is_valid()) - break; - - handling_page_navigate_request_ = true; - frame_->loadRequest(blink::WebURLRequest(url)); - handling_page_navigate_request_ = false; - - // The command should fall through to be handled by frame_->devToolsAgent(). - } while (false); - frame_->devToolsAgent()->dispatchOnInspectorBackend( blink::WebString::fromUTF8(message)); } @@ -99,8 +64,22 @@ void DevToolsAgentImpl::DispatchProtocolMessage(const mojo::String& message) { void DevToolsAgentImpl::sendProtocolMessage(int call_id, const blink::WebString& response, const blink::WebString& state) { - if (client_) - client_->DispatchProtocolMessage(response.utf8()); + DCHECK(!response.isNull()); + + mojo::String response_str = response.utf8(); + mojo::String state_str; + if (!state.isNull()) + state_str = state.utf8(); + + if (client_) { + client_->DispatchProtocolMessage(call_id, response_str, state_str); + } else if (cache_until_client_ready_) { + cached_client_messages_.resize(cached_client_messages_.size() + 1); + CachedClientMessage& message = cached_client_messages_.back(); + message.call_id = call_id; + message.response.Swap(&response_str); + message.state.Swap(&state_str); + } } void DevToolsAgentImpl::OnConnectionError() { diff --git a/components/html_viewer/devtools_agent_impl.h b/components/html_viewer/devtools_agent_impl.h index 1998928..f062b31d 100644 --- a/components/html_viewer/devtools_agent_impl.h +++ b/components/html_viewer/devtools_agent_impl.h @@ -5,6 +5,9 @@ #ifndef COMPONENTS_HTML_VIEWER_DEVTOOLS_AGENT_IMPL_H_ #define COMPONENTS_HTML_VIEWER_DEVTOOLS_AGENT_IMPL_H_ +#include <string> +#include <vector> + #include "base/macros.h" #include "components/devtools_service/public/interfaces/devtools_service.mojom.h" #include "third_party/WebKit/public/web/WebDevToolsAgentClient.h" @@ -14,30 +17,24 @@ namespace blink { class WebLocalFrame; } -namespace mojo { -class Shell; -} - namespace html_viewer { class DevToolsAgentImpl : public devtools_service::DevToolsAgent, public blink::WebDevToolsAgentClient { public: // |frame| must outlive this object. - DevToolsAgentImpl(blink::WebLocalFrame* frame, mojo::Shell* shell); + // This agent should restore its internal state using |state| if it is not + // null. + DevToolsAgentImpl(blink::WebLocalFrame* frame, + const std::string& id, + const std::string* state); ~DevToolsAgentImpl() override; - blink::WebLocalFrame* frame() const { return frame_; } - - // Returns whether a "Page.navigate" command is being handled. - bool handling_page_navigate_request() const { - return handling_page_navigate_request_; - } + void BindToRequest(mojo::InterfaceRequest<DevToolsAgent> request); private: // devtools_service::DevToolsAgent implementation. - void SetClient(devtools_service::DevToolsAgentClientPtr client, - const mojo::String& client_id) override; + void SetClient(devtools_service::DevToolsAgentClientPtr client) override; void DispatchProtocolMessage(const mojo::String& message) override; // blink::WebDevToolsAgentClient implementation. @@ -48,10 +45,22 @@ class DevToolsAgentImpl : public devtools_service::DevToolsAgent, void OnConnectionError(); blink::WebLocalFrame* const frame_; + const std::string id_; + mojo::Binding<DevToolsAgent> binding_; devtools_service::DevToolsAgentClientPtr client_; - bool handling_page_navigate_request_; + // If we restore the agent's internal state using serialized state data from a + // previous agent, the agent may generate messages before |client_| is set. + // In that case, we need to cache messages for the client. + bool cache_until_client_ready_; + + struct CachedClientMessage { + int call_id; + mojo::String response; + mojo::String state; + }; + std::vector<CachedClientMessage> cached_client_messages_; DISALLOW_COPY_AND_ASSIGN(DevToolsAgentImpl); }; diff --git a/components/html_viewer/html_document_oopif.cc b/components/html_viewer/html_document_oopif.cc index 7538add..d8f00ef 100644 --- a/components/html_viewer/html_document_oopif.cc +++ b/components/html_viewer/html_document_oopif.cc @@ -10,7 +10,6 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/thread_task_runner_handle.h" -#include "components/devtools_service/public/cpp/switches.h" #include "components/html_viewer/blink_url_request_type_converters.h" #include "components/html_viewer/devtools_agent_impl.h" #include "components/html_viewer/document_resource_waiter.h" @@ -44,11 +43,6 @@ bool IsTestInterfaceEnabled() { kEnableTestInterface); } -bool EnableRemoteDebugging() { - return base::CommandLine::ForCurrentProcess()->HasSwitch( - devtools_service::kRemoteDebuggingPort); -} - } // namespace HTMLDocumentOOPIF::BeforeLoadCache::BeforeLoadCache() { @@ -83,6 +77,9 @@ HTMLDocumentOOPIF::HTMLDocumentOOPIF( static_cast<mojo::InterfaceFactory<mandoline::FrameTreeClient>*>(this)); connection->AddService(static_cast<InterfaceFactory<AxProvider>*>(this)); connection->AddService(&view_manager_client_factory_); + connection->AddService( + static_cast<mojo::InterfaceFactory<devtools_service::DevToolsAgent>*>( + this)); if (IsTestInterfaceEnabled()) { connection->AddService( static_cast<mojo::InterfaceFactory<TestHTMLViewer>*>(this)); @@ -138,13 +135,13 @@ void HTMLDocumentOOPIF::Load() { frame_ = HTMLFrameTreeManager::CreateFrameAndAttachToTree( global_state_, view, resource_waiter_.Pass(), this); - // TODO(yzshen): http://crbug.com/498986 Creating DevToolsAgentImpl instances - // causes html_viewer_apptests flakiness currently. Before we fix that we - // cannot enable remote debugging (which is required by Telemetry tests) on - // the bots. - if (EnableRemoteDebugging() && !frame_->parent()) { - devtools_agent_.reset(new DevToolsAgentImpl( - frame_->web_frame()->toWebLocalFrame(), html_document_app_->shell())); + if (devtools_agent_request_.is_pending()) { + if (frame_->devtools_agent()) { + frame_->devtools_agent()->BindToRequest(devtools_agent_request_.Pass()); + } else { + devtools_agent_request_ = + mojo::InterfaceRequest<devtools_service::DevToolsAgent>(); + } } const GURL url(extra_data->synthetic_response->url); @@ -191,10 +188,6 @@ void HTMLDocumentOOPIF::OnViewDestroyed(View* view) { resource_waiter_->set_root(nullptr); } -bool HTMLDocumentOOPIF::ShouldNavigateLocallyInMainFrame() { - return devtools_agent_ && devtools_agent_->handling_page_navigate_request(); -} - void HTMLDocumentOOPIF::OnFrameDidFinishLoad() { did_finish_local_frame_load_ = true; scoped_ptr<BeforeLoadCache> before_load_cache = before_load_cache_.Pass(); @@ -263,4 +256,15 @@ void HTMLDocumentOOPIF::Create( resource_waiter_->Bind(request.Pass()); } +void HTMLDocumentOOPIF::Create( + mojo::ApplicationConnection* connection, + mojo::InterfaceRequest<devtools_service::DevToolsAgent> request) { + if (frame_) { + if (frame_->devtools_agent()) + frame_->devtools_agent()->BindToRequest(request.Pass()); + } else { + devtools_agent_request_ = request.Pass(); + } +} + } // namespace html_viewer diff --git a/components/html_viewer/html_document_oopif.h b/components/html_viewer/html_document_oopif.h index 7701ba7..593e8dc 100644 --- a/components/html_viewer/html_document_oopif.h +++ b/components/html_viewer/html_document_oopif.h @@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" +#include "components/devtools_service/public/interfaces/devtools_service.mojom.h" #include "components/html_viewer/ax_provider_impl.h" #include "components/html_viewer/html_frame_delegate.h" #include "components/html_viewer/public/interfaces/test_html_viewer.mojom.h" @@ -36,7 +37,6 @@ class View; namespace html_viewer { class AxProviderImpl; -class DevToolsAgentImpl; class DocumentResourceWaiter; class GlobalState; class HTMLFrameTreeManager; @@ -54,7 +54,8 @@ class HTMLDocumentOOPIF public HTMLFrameDelegate, public mojo::InterfaceFactory<mojo::AxProvider>, public mojo::InterfaceFactory<mandoline::FrameTreeClient>, - public mojo::InterfaceFactory<TestHTMLViewer> { + public mojo::InterfaceFactory<TestHTMLViewer>, + public mojo::InterfaceFactory<devtools_service::DevToolsAgent> { public: using DeleteCallback = base::Callback<void(HTMLDocumentOOPIF*)>; using HTMLFrameCreationCallback = @@ -106,7 +107,6 @@ class HTMLDocumentOOPIF void OnViewDestroyed(mojo::View* view) override; // HTMLFrameDelegate: - bool ShouldNavigateLocallyInMainFrame() override; void OnFrameDidFinishLoad() override; mojo::ApplicationImpl* GetApp() override; HTMLFrame* CreateHTMLFrame(HTMLFrame::CreateParams* params) override; @@ -125,6 +125,11 @@ class HTMLDocumentOOPIF void Create(mojo::ApplicationConnection* connection, mojo::InterfaceRequest<TestHTMLViewer> request) override; + // mojo::InterfaceFactory<devtools_service::DevToolsAgent>: + void Create( + mojo::ApplicationConnection* connection, + mojo::InterfaceRequest<devtools_service::DevToolsAgent> request) override; + scoped_ptr<mojo::AppRefCount> app_refcount_; mojo::ApplicationImpl* html_document_app_; mojo::ApplicationConnection* connection_; @@ -142,8 +147,6 @@ class HTMLDocumentOOPIF HTMLFrame* frame_; - scoped_ptr<DevToolsAgentImpl> devtools_agent_; - scoped_ptr<DocumentResourceWaiter> resource_waiter_; scoped_ptr<BeforeLoadCache> before_load_cache_; @@ -154,6 +157,11 @@ class HTMLDocumentOOPIF mojo::View* root_; + // Cache interface request of DevToolsAgent if |frame_| hasn't been + // initialized. + mojo::InterfaceRequest<devtools_service::DevToolsAgent> + devtools_agent_request_; + DISALLOW_COPY_AND_ASSIGN(HTMLDocumentOOPIF); }; diff --git a/components/html_viewer/html_frame.cc b/components/html_viewer/html_frame.cc index 0ce2a90..5cb3223 100644 --- a/components/html_viewer/html_frame.cc +++ b/components/html_viewer/html_frame.cc @@ -17,6 +17,7 @@ #include "components/html_viewer/blink_input_events_type_converters.h" #include "components/html_viewer/blink_text_input_type_converters.h" #include "components/html_viewer/blink_url_request_type_converters.h" +#include "components/html_viewer/devtools_agent_impl.h" #include "components/html_viewer/geolocation_client_impl.h" #include "components/html_viewer/global_state.h" #include "components/html_viewer/html_frame_delegate.h" @@ -37,6 +38,7 @@ #include "mojo/application/public/cpp/application_impl.h" #include "mojo/application/public/cpp/connect.h" #include "mojo/application/public/interfaces/shell.mojom.h" +#include "mojo/common/common_type_converters.h" #include "mojo/converters/geometry/geometry_type_converters.h" #include "skia/ext/refptr.h" #include "third_party/WebKit/public/platform/Platform.h" @@ -150,6 +152,19 @@ HTMLFrame::HTMLFrame(CreateParams* params) local_web_frame->swap(remote_web_frame); web_frame_ = remote_web_frame; } else { + // Setup a DevTools agent if this is the local main frame and the browser + // side has set relevant client properties. + mojo::Array<uint8_t> devtools_id = + GetValueFromClientProperties("devtools-id", params->properties); + if (!devtools_id.is_null()) { + mojo::Array<uint8_t> devtools_state = + GetValueFromClientProperties("devtools-state", params->properties); + std::string devtools_state_str = devtools_state.To<std::string>(); + devtools_agent_.reset(new DevToolsAgentImpl( + web_frame_->toWebLocalFrame(), devtools_id.To<std::string>(), + devtools_state.is_null() ? nullptr : &devtools_state_str)); + } + // Collect startup perf data for local main frames in test environments. // Child frames aren't tracked, and tracking remote frames is redundant. startup_performance_data_collector_ = @@ -707,12 +722,6 @@ blink::WebCookieJar* HTMLFrame::cookieJar(blink::WebLocalFrame* frame) { blink::WebNavigationPolicy HTMLFrame::decidePolicyForNavigation( const NavigationPolicyInfo& info) { - // Allow the delegate to force a navigation type for the root. - if (info.frame == web_frame() && this == frame_tree_manager_->root_ && - delegate_ && delegate_->ShouldNavigateLocallyInMainFrame()) { - return info.defaultPolicy; - } - // If we have extraData() it means we already have the url response // (presumably because we are being called via Navigate()). In that case we // can go ahead and navigate locally. diff --git a/components/html_viewer/html_frame.h b/components/html_viewer/html_frame.h index c5b7807..7b47bdc 100644 --- a/components/html_viewer/html_frame.h +++ b/components/html_viewer/html_frame.h @@ -34,6 +34,7 @@ class View; namespace html_viewer { +class DevToolsAgentImpl; class GeolocationClientImpl; class HTMLFrameDelegate; class HTMLFrameTreeManager; @@ -126,6 +127,10 @@ class HTMLFrame : public blink::WebFrameClient, HTMLFrameTreeManager* frame_tree_manager() { return frame_tree_manager_; } + // Returns null if the browser side didn't request to setup an agent in this + // frame. + DevToolsAgentImpl* devtools_agent() { return devtools_agent_.get(); } + // Returns true if this or one of the frames descendants is local. bool HasLocalDescendant() const; @@ -331,6 +336,8 @@ class HTMLFrame : public blink::WebFrameClient, tracing::StartupPerformanceDataCollectorPtr startup_performance_data_collector_; + scoped_ptr<DevToolsAgentImpl> devtools_agent_; + base::WeakPtrFactory<HTMLFrame> weak_factory_; DISALLOW_COPY_AND_ASSIGN(HTMLFrame); diff --git a/components/html_viewer/html_frame_apptest.cc b/components/html_viewer/html_frame_apptest.cc index 7a205e9..ef0e18f 100644 --- a/components/html_viewer/html_frame_apptest.cc +++ b/components/html_viewer/html_frame_apptest.cc @@ -202,7 +202,8 @@ class HTMLFrameTest : public ViewManagerTestBase { FrameTreeClient* frame_tree_client = frame_connection->frame_tree_client(); frame_tree_.reset(new FrameTree(view, frame_tree_delegate_.get(), frame_tree_client, - frame_connection.Pass())); + frame_connection.Pass(), + Frame::ClientPropertyMap())); frame_tree_delegate_->set_frame_tree(frame_tree_.get()); view->Embed(view_manager_client.Pass()); return result; diff --git a/components/html_viewer/html_frame_delegate.h b/components/html_viewer/html_frame_delegate.h index 1161138..3521339 100644 --- a/components/html_viewer/html_frame_delegate.h +++ b/components/html_viewer/html_frame_delegate.h @@ -15,10 +15,6 @@ namespace html_viewer { class HTMLFrameDelegate { public: - // TODO(yzshen): Remove this check once the browser is able to navigate an - // existing html_viewer instance and about:blank page support is ready. - virtual bool ShouldNavigateLocallyInMainFrame() = 0; - // Invoked when the Frame the delegate is attached to finishes loading. This // is not invoked for any child frames, only the frame returned from // HTMLFrameTreeManager::CreateFrameAndAttachToTree(). diff --git a/mandoline/tab/BUILD.gn b/mandoline/tab/BUILD.gn index 85ee536..f29cbf2 100644 --- a/mandoline/tab/BUILD.gn +++ b/mandoline/tab/BUILD.gn @@ -10,6 +10,9 @@ source_set("lib") { "frame.h", "frame_connection.cc", "frame_connection.h", + "frame_devtools_agent.cc", + "frame_devtools_agent.h", + "frame_devtools_agent_delegate.h", "frame_tree.cc", "frame_tree.h", "frame_tree_delegate.h", @@ -23,6 +26,7 @@ source_set("lib") { deps = [ "//base", "//components/clipboard/public/interfaces", + "//components/devtools_service/public/cpp", "//components/devtools_service/public/interfaces", "//components/resource_provider/public/interfaces", "//components/view_manager/public/cpp", @@ -34,6 +38,7 @@ source_set("lib") { "//mojo/services/network/public/interfaces", "//mojo/services/tracing/public/interfaces", "//third_party/mojo/src/mojo/public/cpp/bindings", + "//url", ] public_deps = [ diff --git a/mandoline/tab/DEPS b/mandoline/tab/DEPS index 924f677..65bd6eb 100644 --- a/mandoline/tab/DEPS +++ b/mandoline/tab/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+components/clipboard/public/interfaces", + "+components/devtools_service/public/cpp", "+components/devtools_service/public/interfaces", "+components/font_service/public/interfaces", "+components/resource_provider/public/interfaces", diff --git a/mandoline/tab/frame_apptest.cc b/mandoline/tab/frame_apptest.cc index 3e4cac4..2706d79 100644 --- a/mandoline/tab/frame_apptest.cc +++ b/mandoline/tab/frame_apptest.cc @@ -175,7 +175,7 @@ TEST_F(FrameTest, RootGetsConnect) { TestFrameTreeDelegate tree_delegate; TestFrameTreeClient root_client; FrameTree tree(window_manager()->GetRoot(), &tree_delegate, &root_client, - nullptr); + nullptr, Frame::ClientPropertyMap()); ASSERT_EQ(1, root_client.connect_count()); mojo::Array<FrameDataPtr> frames = root_client.connect_frames(); ASSERT_EQ(1u, frames.size()); @@ -188,7 +188,7 @@ TEST_F(FrameTest, SingleChild) { TestFrameTreeDelegate tree_delegate; TestFrameTreeClient root_client; FrameTree tree(window_manager()->GetRoot(), &tree_delegate, &root_client, - nullptr); + nullptr, Frame::ClientPropertyMap()); View* child = window_manager()->CreateView(); EXPECT_EQ(nullptr, Frame::FindFirstFrameAncestor(child)); diff --git a/mandoline/tab/frame_connection.cc b/mandoline/tab/frame_connection.cc index 5fa2ae4..5b14f8d 100644 --- a/mandoline/tab/frame_connection.cc +++ b/mandoline/tab/frame_connection.cc @@ -5,7 +5,6 @@ #include "mandoline/tab/frame_connection.h" #include "components/clipboard/public/interfaces/clipboard.mojom.h" -#include "components/devtools_service/public/interfaces/devtools_service.mojom.h" #include "components/resource_provider/public/interfaces/resource_provider.mojom.h" #include "components/view_manager/public/interfaces/gpu.mojom.h" #include "components/view_manager/public/interfaces/surfaces.mojom.h" @@ -62,10 +61,6 @@ void FrameConnection::Init(mojo::ApplicationImpl* app, view_manager_interfaces.push_back(mojo::Surface::Name_); filter->filter.insert("mojo:view_manager", view_manager_interfaces.Pass()); - mojo::Array<mojo::String> devtools_interfaces; - devtools_interfaces.push_back(devtools_service::DevToolsRegistry::Name_); - filter->filter.insert("mojo:devtools_service", devtools_interfaces.Pass()); - #if defined(OS_LINUX) && !defined(OS_ANDROID) mojo::Array<mojo::String> font_service_interfaces; font_service_interfaces.push_back(font_service::FontService::Name_); diff --git a/mandoline/tab/frame_devtools_agent.cc b/mandoline/tab/frame_devtools_agent.cc new file mode 100644 index 0000000..c17ce95 --- /dev/null +++ b/mandoline/tab/frame_devtools_agent.cc @@ -0,0 +1,199 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mandoline/tab/frame_devtools_agent.h" + +#include <string.h> + +#include <vector> + +#include "base/bind.h" +#include "base/guid.h" +#include "base/json/json_reader.h" +#include "base/logging.h" +#include "base/values.h" +#include "mandoline/tab/frame_devtools_agent_delegate.h" +#include "mojo/application/public/cpp/application_impl.h" +#include "mojo/services/network/public/interfaces/url_loader.mojom.h" +#include "url/gurl.h" + +namespace web_view { + +using devtools_service::DevToolsAgentClientPtr; +using devtools_service::DevToolsAgentPtr; +using devtools_service::DevToolsRegistryPtr; + +using mojo::String; + +namespace { + +void StringToVector(const std::string& in, std::vector<uint8_t>* out) { + out->resize(in.size()); + if (!in.empty()) + memcpy(&out->front(), in.c_str(), in.size()); +} + +} // namespace + +// This class is used by FrameDevToolsAgent to relay client messages from the +// frame to the DevTools service. +class FrameDevToolsAgent::FrameDevToolsAgentClient + : public devtools_service::DevToolsAgentClient { + public: + FrameDevToolsAgentClient(FrameDevToolsAgent* owner, + DevToolsAgentClientPtr forward_client) + : owner_(owner), binding_(this), forward_client_(forward_client.Pass()) { + forward_client_.set_connection_error_handler(base::Bind( + &FrameDevToolsAgent::OnForwardClientClosed, base::Unretained(owner_))); + if (owner_->forward_agent_) + OnAttachedFrame(); + } + + ~FrameDevToolsAgentClient() override {} + + void OnAttachedFrame() { + DCHECK(owner_->forward_agent_); + + if (binding_.is_bound()) + binding_.Close(); + + DevToolsAgentClientPtr client; + binding_.Bind(&client); + owner_->forward_agent_->SetClient(client.Pass()); + } + + private: + // DevToolsAgentClient implementation. + void DispatchProtocolMessage(int32_t call_id, + const String& message, + const String& state) override { + DCHECK(forward_client_); + owner_->OnReceivedClientMessage(call_id, message, state); + // The state is used to persist state across frame navigations. There is no + // need to forward it. + forward_client_->DispatchProtocolMessage(call_id, message, String()); + } + + FrameDevToolsAgent* const owner_; + mojo::Binding<DevToolsAgentClient> binding_; + // The DevToolsAgentClient interface provided by the DevTools service. This + // class will forward DevToolsAgentClient method calls it receives to + // |forward_client_|. + DevToolsAgentClientPtr forward_client_; + + DISALLOW_COPY_AND_ASSIGN(FrameDevToolsAgentClient); +}; + +FrameDevToolsAgent::FrameDevToolsAgent(mojo::ApplicationImpl* app, + FrameDevToolsAgentDelegate* delegate) + : app_(app), + delegate_(delegate), + id_(base::GenerateGUID()), + binding_(this) { + DCHECK(app_); + DCHECK(delegate_); +} + +FrameDevToolsAgent::~FrameDevToolsAgent() {} + +void FrameDevToolsAgent::AttachFrame( + DevToolsAgentPtr forward_agent, + mandoline::Frame::ClientPropertyMap* devtools_properties) { + RegisterAgentIfNecessary(); + forward_agent_ = forward_agent.Pass(); + + StringToVector(id_, &(*devtools_properties)["devtools-id"]); + if (client_impl_) { + StringToVector(state_, &(*devtools_properties)["devtools-state"]); + client_impl_->OnAttachedFrame(); + } + + // This follows Chrome's logic and relies on the fact that call IDs increase + // monotonously so iterating over |pending_messages_| preserves the order in + // which they were received. + for (const auto& item : pending_messages_) + forward_agent_->DispatchProtocolMessage(item.second); +} + +void FrameDevToolsAgent::RegisterAgentIfNecessary() { + if (binding_.is_bound()) + return; + + mojo::URLRequestPtr request(mojo::URLRequest::New()); + request->url = "mojo:devtools_service"; + DevToolsRegistryPtr devtools_registry; + app_->ConnectToService(request.Pass(), &devtools_registry); + + DevToolsAgentPtr agent; + binding_.Bind(&agent); + devtools_registry->RegisterAgent(id_, agent.Pass()); +} + +void FrameDevToolsAgent::HandlePageNavigateRequest( + const base::DictionaryValue* request) { + std::string method; + if (!request->GetString("method", &method) || method != "Page.navigate") + return; + + std::string url_string; + if (!request->GetString("params.url", &url_string)) + return; + + GURL url(url_string); + if (!url.is_valid()) + return; + + // Stop sending messages to the old frame which will be navigated away soon. + // However, we don't reset |client_impl_| because we want to receive responses + // and events from the old frame in the meantime. + forward_agent_.reset(); + + delegate_->HandlePageNavigateRequest(url); +} + +void FrameDevToolsAgent::SetClient(DevToolsAgentClientPtr client) { + client_impl_.reset(new FrameDevToolsAgentClient(this, client.Pass())); +} + +void FrameDevToolsAgent::DispatchProtocolMessage(const String& message) { + // TODO(yzshen): Consider refactoring and reusing the existing DevTools + // protocol parsing code. + + scoped_ptr<base::Value> value = base::JSONReader::Read(message.get()); + base::DictionaryValue* command = nullptr; + if (!value || !value->GetAsDictionary(&command)) { + VLOG(1) << "Unable to parse DevTools message: " << message; + return; + } + + int call_id = -1; + if (!command->GetInteger("id", &call_id)) { + VLOG(1) << "Call Id not found in a DevTools request message: " << message; + return; + } + + pending_messages_[call_id] = message; + + HandlePageNavigateRequest(command); + + if (forward_agent_) + forward_agent_->DispatchProtocolMessage(message); +} + +void FrameDevToolsAgent::OnReceivedClientMessage(int32_t call_id, + const String& message, + const String& state) { + if (!state.is_null() && state.size() > 0) + state_ = state; + + pending_messages_.erase(call_id); +} + +void FrameDevToolsAgent::OnForwardClientClosed() { + client_impl_.reset(); + state_.clear(); + pending_messages_.clear(); +} + +} // namespace web_view diff --git a/mandoline/tab/frame_devtools_agent.h b/mandoline/tab/frame_devtools_agent.h new file mode 100644 index 0000000..ed16416 --- /dev/null +++ b/mandoline/tab/frame_devtools_agent.h @@ -0,0 +1,83 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MANDOLINE_TAB_FRAME_DEVTOOLS_AGENT_H_ +#define MANDOLINE_TAB_FRAME_DEVTOOLS_AGENT_H_ + +#include <map> +#include <string> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "components/devtools_service/public/interfaces/devtools_service.mojom.h" +#include "mandoline/tab/frame.h" +#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" + +namespace base { +class DictionaryValue; +} + +namespace mojo { +class ApplicationImpl; +} + +namespace web_view { + +class FrameDevToolsAgentDelegate; + +// FrameDevToolsAgent relays messages between the DevTools service and the +// DevTools agent of the frame that it attaches to. +// It persists state across frame navigations and also intercepts +// "Page.navigate" requests. +class FrameDevToolsAgent : public devtools_service::DevToolsAgent { + public: + FrameDevToolsAgent(mojo::ApplicationImpl* app, + FrameDevToolsAgentDelegate* delegate); + ~FrameDevToolsAgent() override; + + // Attaches this agent to a new frame. |forward_agent| is the DevToolsAgent + // interface provided by the frame. DevTools-related properties are added to + // |devtools_properties|, which should be passed to the frame to initialize + // its DevTools agent. + void AttachFrame(devtools_service::DevToolsAgentPtr forward_agent, + mandoline::Frame::ClientPropertyMap* devtools_properties); + + private: + class FrameDevToolsAgentClient; + + void RegisterAgentIfNecessary(); + + void HandlePageNavigateRequest(const base::DictionaryValue* request); + + // devtools_service::DevToolsAgent implementation. + void SetClient(devtools_service::DevToolsAgentClientPtr client) override; + void DispatchProtocolMessage(const mojo::String& message) override; + + // The following methods are called by |client_impl_|. + void OnReceivedClientMessage(int32_t call_id, + const mojo::String& message, + const mojo::String& state); + void OnForwardClientClosed(); + + mojo::ApplicationImpl* const app_; + FrameDevToolsAgentDelegate* const delegate_; + + const std::string id_; + + scoped_ptr<FrameDevToolsAgentClient> client_impl_; + + mojo::Binding<DevToolsAgent> binding_; + // The DevToolsAgent interface provided by the frame. This class will forward + // DevToolsAgent method calls it receives to |forward_agent_|. + devtools_service::DevToolsAgentPtr forward_agent_; + + std::string state_; + std::map<int, std::string> pending_messages_; + + DISALLOW_COPY_AND_ASSIGN(FrameDevToolsAgent); +}; + +} // namespace web_view + +#endif // MANDOLINE_TAB_FRAME_DEVTOOLS_AGENT_H_ diff --git a/mandoline/tab/frame_devtools_agent_delegate.h b/mandoline/tab/frame_devtools_agent_delegate.h new file mode 100644 index 0000000..cf6c846 --- /dev/null +++ b/mandoline/tab/frame_devtools_agent_delegate.h @@ -0,0 +1,21 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MANDOLINE_TAB_FRAME_DEVTOOLS_AGENT_DELEGATE_H_ +#define MANDOLINE_TAB_FRAME_DEVTOOLS_AGENT_DELEGATE_H_ + +class GURL; + +namespace web_view { + +class FrameDevToolsAgentDelegate { + public: + virtual ~FrameDevToolsAgentDelegate() {} + + virtual void HandlePageNavigateRequest(const GURL& url) = 0; +}; + +} // namespace web_view + +#endif // MANDOLINE_TAB_FRAME_DEVTOOLS_AGENT_DELEGATE_H_ diff --git a/mandoline/tab/frame_tree.cc b/mandoline/tab/frame_tree.cc index df84f15..0ef0e43 100644 --- a/mandoline/tab/frame_tree.cc +++ b/mandoline/tab/frame_tree.cc @@ -12,7 +12,8 @@ namespace mandoline { FrameTree::FrameTree(mojo::View* view, FrameTreeDelegate* delegate, FrameTreeClient* root_client, - scoped_ptr<FrameUserData> user_data) + scoped_ptr<FrameUserData> user_data, + const Frame::ClientPropertyMap& client_properties) : view_(view), delegate_(delegate), root_(this, @@ -21,7 +22,7 @@ FrameTree::FrameTree(mojo::View* view, ViewOwnership::DOESNT_OWN_VIEW, root_client, user_data.Pass(), - Frame::ClientPropertyMap()), + client_properties), progress_(0.f), change_id_(1u) { root_.Init(nullptr); diff --git a/mandoline/tab/frame_tree.h b/mandoline/tab/frame_tree.h index f4f4c9e5..886fbd6 100644 --- a/mandoline/tab/frame_tree.h +++ b/mandoline/tab/frame_tree.h @@ -27,10 +27,12 @@ class FrameTree { public: // |view| is the view to do the initial embedding in. It is assumed |view| // outlives FrameTree. + // |client_properties| is the client properties for the root frame. FrameTree(mojo::View* view, FrameTreeDelegate* delegate, FrameTreeClient* root_client, - scoped_ptr<FrameUserData> user_data); + scoped_ptr<FrameUserData> user_data, + const Frame::ClientPropertyMap& client_properties); ~FrameTree(); Frame* root() { return &root_; } diff --git a/mandoline/tab/web_view_impl.cc b/mandoline/tab/web_view_impl.cc index 3f00839..09c494e 100644 --- a/mandoline/tab/web_view_impl.cc +++ b/mandoline/tab/web_view_impl.cc @@ -5,13 +5,17 @@ #include "mandoline/tab/web_view_impl.h" #include "base/callback.h" +#include "base/command_line.h" +#include "components/devtools_service/public/cpp/switches.h" #include "components/view_manager/public/cpp/view.h" #include "components/view_manager/public/cpp/view_manager.h" #include "mandoline/tab/frame.h" #include "mandoline/tab/frame_connection.h" +#include "mandoline/tab/frame_devtools_agent.h" #include "mandoline/tab/frame_tree.h" #include "mojo/application/public/cpp/application_impl.h" #include "mojo/converters/geometry/geometry_type_converters.h" +#include "url/gurl.h" // TODO(beng): remove once these classes are in the web_view namespace. using mandoline::FrameConnection; @@ -19,6 +23,14 @@ using mandoline::FrameTreeClient; using mandoline::FrameUserData; namespace web_view { +namespace { + +bool EnableRemoteDebugging() { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + devtools_service::kRemoteDebuggingPort); +} + +} // namespace //////////////////////////////////////////////////////////////////////////////// // WebViewImpl, public: @@ -31,6 +43,8 @@ WebViewImpl::WebViewImpl(mojo::ApplicationImpl* app, binding_(this, request.Pass()), content_(nullptr), view_manager_client_factory_(app->shell(), this) { + if (EnableRemoteDebugging()) + devtools_agent_.reset(new FrameDevToolsAgent(app_, this)); } WebViewImpl::~WebViewImpl() {} @@ -47,9 +61,18 @@ void WebViewImpl::LoadRequest(mojo::URLRequestPtr request) { scoped_ptr<FrameConnection> frame_connection(new FrameConnection); mojo::ViewManagerClientPtr view_manager_client; frame_connection->Init(app_, request.Pass(), &view_manager_client); + + Frame::ClientPropertyMap client_properties; + if (devtools_agent_) { + devtools_service::DevToolsAgentPtr forward_agent; + frame_connection->application_connection()->ConnectToService( + &forward_agent); + devtools_agent_->AttachFrame(forward_agent.Pass(), &client_properties); + } + FrameTreeClient* frame_tree_client = frame_connection->frame_tree_client(); frame_tree_.reset(new FrameTree(content_, this, frame_tree_client, - frame_connection.Pass())); + frame_connection.Pass(), client_properties)); content_->Embed(view_manager_client.Pass()); } @@ -134,4 +157,13 @@ bool WebViewImpl::CanNavigateFrame( void WebViewImpl::DidStartNavigation(Frame* frame) {} +//////////////////////////////////////////////////////////////////////////////// +// WebViewImpl, FrameDevToolsAgentDelegate implementation: + +void WebViewImpl::HandlePageNavigateRequest(const GURL& url) { + mojo::URLRequestPtr request(mojo::URLRequest::New()); + request->url = url.spec(); + client_->TopLevelNavigate(request.Pass()); +} + } // namespace web_view diff --git a/mandoline/tab/web_view_impl.h b/mandoline/tab/web_view_impl.h index 17b8244..bb85eaa 100644 --- a/mandoline/tab/web_view_impl.h +++ b/mandoline/tab/web_view_impl.h @@ -10,6 +10,7 @@ #include "components/view_manager/public/cpp/view_manager_client_factory.h" #include "components/view_manager/public/cpp/view_manager_delegate.h" #include "components/view_manager/public/cpp/view_observer.h" +#include "mandoline/tab/frame_devtools_agent_delegate.h" #include "mandoline/tab/frame_tree_delegate.h" #include "mandoline/tab/public/interfaces/web_view.mojom.h" #include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" @@ -31,10 +32,13 @@ using mandoline::HTMLMessageEvent; namespace web_view { +class FrameDevToolsAgent; + class WebViewImpl : public mojom::WebView, public mojo::ViewManagerDelegate, public mojo::ViewObserver, - public mandoline::FrameTreeDelegate { + public mandoline::FrameTreeDelegate, + public FrameDevToolsAgentDelegate { public: WebViewImpl(mojo::ApplicationImpl* app, mojom::WebViewClientPtr client, @@ -73,6 +77,9 @@ class WebViewImpl : public mojom::WebView, mojo::ViewManagerClientPtr* view_manager_client) override; void DidStartNavigation(Frame* frame) override; + // Overridden from FrameDevToolsAgent::Delegate: + void HandlePageNavigateRequest(const GURL& url) override; + mojo::ApplicationImpl* app_; mojom::WebViewClientPtr client_; mojo::StrongBinding<WebView> binding_; @@ -82,6 +89,8 @@ class WebViewImpl : public mojom::WebView, mojo::URLRequestPtr pending_request_; + scoped_ptr<FrameDevToolsAgent> devtools_agent_; + DISALLOW_COPY_AND_ASSIGN(WebViewImpl); }; |