diff --git a/src/core/devtools_frontend_qt.cpp b/src/core/devtools_frontend_qt.cpp index 4531f80a8dbd5bfa5d3a112dcf37167bb4fac252..9eedee42ac991c63b45f1c97211e957aabf34958 100644 --- a/src/core/devtools_frontend_qt.cpp +++ b/src/core/devtools_frontend_qt.cpp @@ -48,6 +48,7 @@ #include "profile_qt.h" #include "web_contents_adapter.h" +#include "base/base64.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/json/string_escape.h" @@ -73,12 +74,10 @@ #include "content/public/common/content_client.h" #include "content/public/common/url_constants.h" #include "ipc/ipc_channel.h" -#include "net/base/io_buffer.h" -#include "net/base/net_errors.h" #include "net/http/http_response_headers.h" #include "net/traffic_annotation/network_traffic_annotation.h" -#include "net/url_request/url_fetcher.h" -#include "net/url_request/url_fetcher_response_writer.h" +#include "services/network/public/cpp/simple_url_loader.h" +#include "services/network/public/cpp/simple_url_loader_stream_consumer.h" #include <QDebug> using namespace QtWebEngineCore; @@ -103,66 +102,77 @@ std::unique_ptr<base::DictionaryValue> BuildObjectForResponse(const net::HttpRes return response; } -// ResponseWriter ------------------------------------------------------------- - -class ResponseWriter : public net::URLFetcherResponseWriter { -public: - ResponseWriter(base::WeakPtr<DevToolsFrontendQt> shell_devtools_, int stream_id); - ~ResponseWriter() override; - - // URLFetcherResponseWriter overrides: - int Initialize(net::CompletionOnceCallback callback) override; - int Write(net::IOBuffer *buffer, int num_bytes, net::CompletionOnceCallback callback) override; - int Finish(int net_error, net::CompletionOnceCallback callback) override; +static std::string GetFrontendURL() +{ + return "chrome-devtools://devtools/bundled/devtools_app.html"; +} -private: - base::WeakPtr<DevToolsFrontendQt> shell_devtools_; - int stream_id_; +} // namespace - DISALLOW_COPY_AND_ASSIGN(ResponseWriter); -}; +namespace QtWebEngineCore { -ResponseWriter::ResponseWriter(base::WeakPtr<DevToolsFrontendQt> shell_devtools, int stream_id) - : shell_devtools_(shell_devtools), stream_id_(stream_id) -{} +class DevToolsFrontendQt::NetworkResourceLoader + : public network::SimpleURLLoaderStreamConsumer { +public: + NetworkResourceLoader(int stream_id, + int request_id, + DevToolsFrontendQt *bindings, + std::unique_ptr<network::SimpleURLLoader> loader, + network::mojom::URLLoaderFactory *url_loader_factory) + : stream_id_(stream_id), + request_id_(request_id), + bindings_(bindings), + loader_(std::move(loader)) + { + loader_->SetOnResponseStartedCallback(base::BindOnce( + &NetworkResourceLoader::OnResponseStarted, base::Unretained(this))); + loader_->DownloadAsStream(url_loader_factory, this); + } -ResponseWriter::~ResponseWriter() {} +private: + void OnResponseStarted(const GURL &final_url, + const network::ResourceResponseHead &response_head) + { + response_headers_ = response_head.headers; + } -int ResponseWriter::Initialize(net::CompletionOnceCallback callback) -{ - return net::OK; -} + void OnDataReceived(base::StringPiece chunk, base::OnceClosure resume) override + { + base::Value chunkValue; + + bool encoded = !base::IsStringUTF8(chunk); + if (encoded) { + std::string encoded_string; + base::Base64Encode(chunk, &encoded_string); + chunkValue = base::Value(std::move(encoded_string)); + } else { + chunkValue = base::Value(chunk); + } + base::Value id(stream_id_); + base::Value encodedValue(encoded); -int ResponseWriter::Write(net::IOBuffer *buffer, int num_bytes, net::CompletionOnceCallback callback) -{ - std::string chunk = std::string(buffer->data(), num_bytes); - if (!base::IsStringUTF8(chunk)) - return num_bytes; - - base::Value *id = new base::Value(stream_id_); - base::Value *chunkValue = new base::Value(chunk); - - base::PostTaskWithTraits( - FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&DevToolsFrontendQt::CallClientFunction, - shell_devtools_, "DevToolsAPI.streamWrite", - base::Owned(id), base::Owned(chunkValue), nullptr)); - return num_bytes; -} + bindings_->CallClientFunction("DevToolsAPI.streamWrite", &id, &chunkValue, &encodedValue); + std::move(resume).Run(); + } -int ResponseWriter::Finish(int net_error, net::CompletionOnceCallback callback) -{ - return net::OK; -} + void OnComplete(bool success) override + { + Q_UNUSED(success); + auto response = BuildObjectForResponse(response_headers_.get()); + bindings_->SendMessageAck(request_id_, response.get()); + bindings_->m_loaders.erase(bindings_->m_loaders.find(this)); + } -static std::string GetFrontendURL() -{ - return "chrome-devtools://devtools/bundled/devtools_app.html"; -} + void OnRetry(base::OnceClosure start_retry) override { NOTREACHED(); } -} // namespace + const int stream_id_; + const int request_id_; + DevToolsFrontendQt *const bindings_; + std::unique_ptr<network::SimpleURLLoader> loader_; + scoped_refptr<net::HttpResponseHeaders> response_headers_; -namespace QtWebEngineCore { + DISALLOW_COPY_AND_ASSIGN(NetworkResourceLoader); +}; // This constant should be in sync with // the constant at devtools_ui_bindings.cc. @@ -222,8 +232,6 @@ DevToolsFrontendQt::DevToolsFrontendQt(QSharedPointer<WebContentsAdapter> webCon DevToolsFrontendQt::~DevToolsFrontendQt() { - for (const auto &pair : m_pendingRequests) - delete pair.first; } void DevToolsFrontendQt::Activate() @@ -406,14 +414,23 @@ void DevToolsFrontendQt::HandleMessageFromDevToolsFrontend(const std::string &me } } })"); - net::URLFetcher *fetcher = net::URLFetcher::Create(gurl, net::URLFetcher::GET, this, traffic_annotation).release(); - m_pendingRequests[fetcher] = request_id; - fetcher->SetRequestContext(content::BrowserContext::GetDefaultStoragePartition( - web_contents()->GetBrowserContext())->GetURLRequestContext()); - fetcher->SetExtraRequestHeaders(headers); - fetcher->SaveResponseWithWriter(std::unique_ptr<net::URLFetcherResponseWriter>( - new ResponseWriter(m_weakFactory.GetWeakPtr(), stream_id))); - fetcher->Start(); + auto resource_request = std::make_unique<network::ResourceRequest>(); + resource_request->url = gurl; + // TODO(caseq): this preserves behavior of URLFetcher-based implementation. + // We really need to pass proper first party origin from the front-end. + resource_request->site_for_cookies = gurl; + resource_request->headers.AddHeadersFromString(headers); + + auto *partition = content::BrowserContext::GetStoragePartitionForSite( + web_contents()->GetBrowserContext(), gurl); + auto factory = partition->GetURLLoaderFactoryForBrowserProcess(); + + auto simple_url_loader = network::SimpleURLLoader::Create( + std::move(resource_request), traffic_annotation); + auto resource_loader = std::make_unique<NetworkResourceLoader>( + stream_id, request_id, this, std::move(simple_url_loader), + factory.get()); + m_loaders.insert(std::move(resource_loader)); return; } else if (method == "getPreferences") { m_preferences = std::move(*m_prefStore->GetValues()); @@ -493,21 +510,6 @@ void DevToolsFrontendQt::DispatchProtocolMessage(content::DevToolsAgentHost *age } } -void DevToolsFrontendQt::OnURLFetchComplete(const net::URLFetcher *source) -{ - // TODO(pfeldman): this is a copy of chrome's devtools_ui_bindings.cc. - // We should handle some of the commands including this one in content. - DCHECK(source); - PendingRequestsMap::iterator it = m_pendingRequests.find(source); - DCHECK(it != m_pendingRequests.end()); - - auto response = BuildObjectForResponse(source->GetResponseHeaders()); - - SendMessageAck(it->second, response.get()); - m_pendingRequests.erase(it); - delete source; -} - void DevToolsFrontendQt::CallClientFunction(const std::string &function_name, const base::Value *arg1, const base::Value *arg2, diff --git a/src/core/devtools_frontend_qt.h b/src/core/devtools_frontend_qt.h index 88cc7aeac833ee33343d49ed1a51838f0a6dc257..fed2d47fc62f17e94f8822763d160615e5c12255 100644 --- a/src/core/devtools_frontend_qt.h +++ b/src/core/devtools_frontend_qt.h @@ -41,10 +41,12 @@ #define DEVTOOLS_FRONTEND_QT_H #include <memory> +#include <set> #include "web_contents_delegate_qt.h" #include "base/compiler_specific.h" +#include "base/containers/unique_ptr_adapters.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" @@ -52,7 +54,6 @@ #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/devtools_frontend_host.h" #include "content/public/browser/web_contents_observer.h" -#include "net/url_request/url_fetcher_delegate.h" namespace base { class Value; @@ -69,8 +70,7 @@ class PersistentPrefStore; namespace QtWebEngineCore { class DevToolsFrontendQt : public content::WebContentsObserver - , public content::DevToolsAgentHostClient - , public net::URLFetcherDelegate { + , public content::DevToolsAgentHostClient { public: static DevToolsFrontendQt *Show(QSharedPointer<WebContentsAdapter> frontendAdapter, content::WebContents *inspectedContents); @@ -108,9 +108,6 @@ private: void DocumentAvailableInMainFrame() override; void WebContentsDestroyed() override; - // net::URLFetcherDelegate overrides. - void OnURLFetchComplete(const net::URLFetcher* source) override; - void SendMessageAck(int request_id, const base::Value* arg1); void SetPreference(const std::string &name, const std::string &value); void RemovePreference(const std::string &name); @@ -125,8 +122,10 @@ private: int m_inspect_element_at_x; int m_inspect_element_at_y; std::unique_ptr<content::DevToolsFrontendHost> m_frontendHost; - using PendingRequestsMap = std::map<const net::URLFetcher*, int>; - PendingRequestsMap m_pendingRequests; + + class NetworkResourceLoader; + std::set<std::unique_ptr<NetworkResourceLoader>, base::UniquePtrComparator> m_loaders; + base::DictionaryValue m_preferences; scoped_refptr<PersistentPrefStore> m_prefStore; base::WeakPtrFactory<DevToolsFrontendQt> m_weakFactory;