diff --git a/atom/browser/atom_resource_dispatcher_host_delegate.cc b/atom/browser/atom_resource_dispatcher_host_delegate.cc index 12abd8e8157..d533ec34f5b 100644 --- a/atom/browser/atom_resource_dispatcher_host_delegate.cc +++ b/atom/browser/atom_resource_dispatcher_host_delegate.cc @@ -4,9 +4,7 @@ #include "atom/browser/atom_resource_dispatcher_host_delegate.h" -#include "atom/browser/atom_browser_context.h" #include "atom/browser/login_handler.h" -#include "atom/browser/stream_manager.h" #include "atom/browser/web_contents_permission_helper.h" #include "atom/common/atom_constants.h" #include "atom/common/platform_util.h" @@ -14,6 +12,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/stream_info.h" #include "net/base/escape.h" #include "net/ssl/client_cert_store.h" #include "net/url_request/url_request.h" @@ -63,30 +62,20 @@ void HandleExternalProtocolInUI( permission_helper->RequestOpenExternalPermission(callback, has_user_gesture); } -void OnPdfStreamCreated( - std::unique_ptr stream, - const std::string& stream_id, - const content::ResourceRequestInfo::WebContentsGetter& web_contents_getter, - int render_process_id, - int render_frame_id) { +void OnPdfStreamCreated(const GURL& original_url, + const content::ResourceRequestInfo::WebContentsGetter& + web_contents_getter) { content::WebContents* web_contents = web_contents_getter.Run(); if (!web_contents) return; - auto browser_context = - static_cast(web_contents->GetBrowserContext()); - auto stream_manager = browser_context->stream_manager(); - GURL original_url = stream->original_url; - stream_manager->AddStream(std::move(stream), stream_id, render_process_id, - render_frame_id); - // The URL passes the stream ID to PDF webui that uses it to extract the - // stream from the StreamManager and also passes the URL that the PDF - // originates from, which is used whenever no stream is available from the - // content layer (this will happen when the webui page is reloaded). - // chrome://pdf-viewer/index.html?streamId=abcd&src=https://somepage/123.pdf - content::NavigationController::LoadURLParams params(GURL(base::StringPrintf( - "%sindex.html?%s=%s&%s=%s", kPdfViewerUIOrigin, kPdfViewerUIStreamId, - stream_id.c_str(), kPdfPluginSrc, original_url.spec().c_str()))); + // The URL passes the original pdf resource url, that will be requested + // by the webui page. + // chrome://pdf-viewer/index.html?src=https://somepage/123.pdf + content::NavigationController::LoadURLParams params( + GURL(base::StringPrintf("%sindex.html?%s=%s", kPdfViewerUIOrigin, + kPdfPluginSrc, original_url.spec().c_str()))); + params.can_load_local_resources = true; web_contents->GetController().LoadURLWithParams(params); } @@ -136,7 +125,6 @@ bool AtomResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream( std::string* payload) { if (mime_type == "application/pdf") { *origin = GURL(kPdfViewerUIOrigin); - stream_info_[request] = base::GenerateGUID(); return true; } return false; @@ -145,17 +133,12 @@ bool AtomResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream( void AtomResourceDispatcherHostDelegate::OnStreamCreated( net::URLRequest* request, std::unique_ptr stream) { - auto it = stream_info_.find(request); - if (it == stream_info_.end()) - return; const content::ResourceRequestInfo* info = content::ResourceRequestInfo::ForRequest(request); content::BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&OnPdfStreamCreated, base::Passed(&stream), it->second, - info->GetWebContentsGetterForRequest(), info->GetChildID(), - info->GetRenderFrameID())); - stream_info_.erase(it); + base::Bind(&OnPdfStreamCreated, stream->original_url, + info->GetWebContentsGetterForRequest())); } } // namespace atom diff --git a/atom/browser/atom_resource_dispatcher_host_delegate.h b/atom/browser/atom_resource_dispatcher_host_delegate.h index e1bb80359c3..b4d77803685 100644 --- a/atom/browser/atom_resource_dispatcher_host_delegate.h +++ b/atom/browser/atom_resource_dispatcher_host_delegate.h @@ -5,7 +5,6 @@ #ifndef ATOM_BROWSER_ATOM_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ #define ATOM_BROWSER_ATOM_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ -#include #include #include "content/public/browser/resource_dispatcher_host_delegate.h" @@ -34,9 +33,6 @@ class AtomResourceDispatcherHostDelegate std::unique_ptr stream) override; private: - // Map between intercepted request and its generated stream id. - std::map stream_info_; - DISALLOW_COPY_AND_ASSIGN(AtomResourceDispatcherHostDelegate); }; diff --git a/atom/browser/atom_web_ui_controller_factory.cc b/atom/browser/atom_web_ui_controller_factory.cc index 4bd1c30a1d7..a73eed19a11 100644 --- a/atom/browser/atom_web_ui_controller_factory.cc +++ b/atom/browser/atom_web_ui_controller_factory.cc @@ -53,14 +53,12 @@ AtomWebUIControllerFactory::CreateWebUIControllerForURL(content::WebUI* web_ui, base::SplitStringIntoKeyValuePairs(url.query(), '=', '&', &toplevel_params); std::string stream_id, src; for (const auto& param : toplevel_params) { - if (param.first == kPdfViewerUIStreamId) { - stream_id = param.second; - } else if (param.first == kPdfPluginSrc) { + if (param.first == kPdfPluginSrc) { src = param.second; } } auto browser_context = web_ui->GetWebContents()->GetBrowserContext(); - return new PdfViewerUI(browser_context, web_ui, stream_id, src); + return new PdfViewerUI(browser_context, web_ui, src); } return nullptr; } diff --git a/atom/browser/loader/layered_resource_handler.cc b/atom/browser/loader/layered_resource_handler.cc new file mode 100644 index 00000000000..8b41b1ad638 --- /dev/null +++ b/atom/browser/loader/layered_resource_handler.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2017 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/loader/layered_resource_handler.h" + +namespace atom { + +LayeredResourceHandler::LayeredResourceHandler( + net::URLRequest* request, + std::unique_ptr next_handler, + Delegate* delegate) + : content::LayeredResourceHandler(request, std::move(next_handler)), + delegate_(delegate) {} + +LayeredResourceHandler::~LayeredResourceHandler() {} + +bool LayeredResourceHandler::OnResponseStarted( + content::ResourceResponse* response, + bool* defer) { + if (delegate_) + delegate_->OnResponseStarted(response); + return next_handler_->OnResponseStarted(response, defer); +} + +} // namespace atom diff --git a/atom/browser/loader/layered_resource_handler.h b/atom/browser/loader/layered_resource_handler.h new file mode 100644 index 00000000000..db3a974eea3 --- /dev/null +++ b/atom/browser/loader/layered_resource_handler.h @@ -0,0 +1,40 @@ +// Copyright (c) 2017 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_LOADER_LAYERED_RESOURCE_HANDLER_H_ +#define ATOM_BROWSER_LOADER_LAYERED_RESOURCE_HANDLER_H_ + +#include "content/browser/loader/layered_resource_handler.h" + +namespace atom { + +// Resource handler that notifies on various stages of a resource request. +class LayeredResourceHandler : public content::LayeredResourceHandler { + public: + class Delegate { + public: + Delegate() {} + virtual ~Delegate() {} + + virtual void OnResponseStarted(content::ResourceResponse* response) = 0; + }; + + LayeredResourceHandler(net::URLRequest* request, + std::unique_ptr next_handler, + Delegate* delegate); + ~LayeredResourceHandler() override; + + // content::LayeredResourceHandler: + bool OnResponseStarted(content::ResourceResponse* response, + bool* defer) override; + + private: + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(LayeredResourceHandler); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_LOADER_LAYERED_RESOURCE_HANDLER_H_ diff --git a/atom/browser/ui/webui/pdf_viewer_handler.cc b/atom/browser/ui/webui/pdf_viewer_handler.cc index 7b8f00b9579..62799e88692 100644 --- a/atom/browser/ui/webui/pdf_viewer_handler.cc +++ b/atom/browser/ui/webui/pdf_viewer_handler.cc @@ -6,6 +6,7 @@ #include "atom/common/atom_constants.h" #include "base/bind.h" +#include "base/memory/ptr_util.h" #include "base/values.h" #include "content/public/browser/stream_handle.h" #include "content/public/browser/stream_info.h" @@ -41,14 +42,35 @@ void CreateResponseHeadersDictionary(const net::HttpResponseHeaders* headers, } } +void PopulateStreamInfo(base::DictionaryValue* stream_info, + content::StreamInfo* stream, + const std::string& original_url) { + std::unique_ptr headers_dict( + new base::DictionaryValue); + auto stream_url = stream->handle->GetURL().spec(); + CreateResponseHeadersDictionary(stream->response_headers.get(), + headers_dict.get()); + stream_info->SetString("streamURL", stream_url); + stream_info->SetString("originalURL", original_url); + stream_info->Set("responseHeaders", std::move(headers_dict)); +} + } // namespace -PdfViewerHandler::PdfViewerHandler(const content::StreamInfo* stream, - const std::string& src) - : stream_(stream), original_url_(src) {} +PdfViewerHandler::PdfViewerHandler(const std::string& src) + : stream_(nullptr), original_url_(src), initialized_(false) {} PdfViewerHandler::~PdfViewerHandler() {} +void PdfViewerHandler::SetPdfResourceStream(content::StreamInfo* stream) { + stream_ = stream; + if (initialized_) { + auto list = base::MakeUnique(); + list->Set(0, std::move(initialize_callback_id_)); + Initialize(list.get()); + } +} + void PdfViewerHandler::RegisterMessages() { web_ui()->RegisterMessageCallback( "initialize", @@ -79,24 +101,23 @@ void PdfViewerHandler::OnJavascriptDisallowed() { } void PdfViewerHandler::Initialize(const base::ListValue* args) { - AllowJavascript(); - CHECK_EQ(1U, args->GetSize()); const base::Value* callback_id; CHECK(args->Get(0, &callback_id)); - std::unique_ptr stream_info(new base::DictionaryValue); - std::unique_ptr headers_dict( - new base::DictionaryValue); - std::string stream_url = original_url_; + if (stream_) { - stream_url = stream_->handle->GetURL().spec(); - CreateResponseHeadersDictionary(stream_->response_headers.get(), - headers_dict.get()); + initialized_ = false; + + AllowJavascript(); + + std::unique_ptr stream_info( + new base::DictionaryValue); + PopulateStreamInfo(stream_info.get(), stream_, original_url_); + ResolveJavascriptCallback(*callback_id, *stream_info); + } else { + initialize_callback_id_ = callback_id->CreateDeepCopy(); + initialized_ = true; } - stream_info->SetString("streamURL", stream_url); - stream_info->SetString("originalURL", original_url_); - stream_info->Set("responseHeaders", std::move(headers_dict)); - ResolveJavascriptCallback(*callback_id, *stream_info); } void PdfViewerHandler::GetDefaultZoom(const base::ListValue* args) { diff --git a/atom/browser/ui/webui/pdf_viewer_handler.h b/atom/browser/ui/webui/pdf_viewer_handler.h index 33e2157d978..74fd3ce68fd 100644 --- a/atom/browser/ui/webui/pdf_viewer_handler.h +++ b/atom/browser/ui/webui/pdf_viewer_handler.h @@ -23,10 +23,12 @@ namespace atom { class PdfViewerHandler : public content::WebUIMessageHandler { public: - PdfViewerHandler(const content::StreamInfo* stream, - const std::string& original_url); + explicit PdfViewerHandler(const std::string& original_url); ~PdfViewerHandler() override; + void SetPdfResourceStream(content::StreamInfo* stream); + + protected: // WebUIMessageHandler implementation. void RegisterMessages() override; void OnJavascriptAllowed() override; @@ -43,8 +45,10 @@ class PdfViewerHandler : public content::WebUIMessageHandler { // Keeps track of events related to zooming. std::unique_ptr host_zoom_map_subscription_; - const content::StreamInfo* stream_; + std::unique_ptr initialize_callback_id_; + content::StreamInfo* stream_; std::string original_url_; + bool initialized_; DISALLOW_COPY_AND_ASSIGN(PdfViewerHandler); }; diff --git a/atom/browser/ui/webui/pdf_viewer_ui.cc b/atom/browser/ui/webui/pdf_viewer_ui.cc index fc1e749a003..d937d5ee25f 100644 --- a/atom/browser/ui/webui/pdf_viewer_ui.cc +++ b/atom/browser/ui/webui/pdf_viewer_ui.cc @@ -11,14 +11,29 @@ #include "atom/browser/ui/webui/pdf_viewer_handler.h" #include "atom/common/atom_constants.h" #include "components/pdf/common/pdf_messages.h" +#include "content/browser/loader/resource_dispatcher_host_impl.h" +#include "content/browser/loader/resource_request_info_impl.h" +#include "content/browser/loader/stream_resource_handler.h" +#include "content/browser/resource_context_impl.h" +#include "content/browser/streams/stream.h" +#include "content/browser/streams/stream_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" +#include "content/public/browser/resource_context.h" +#include "content/public/browser/stream_handle.h" #include "content/public/browser/url_data_source.h" #include "content/public/browser/web_contents.h" -#include "content/public/common/bindings_policy.h" #include "grit/pdf_viewer_resources_map.h" +#include "net/base/load_flags.h" #include "net/base/mime_util.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" #include "ui/base/resource/resource_bundle.h" +using content::BrowserThread; + namespace atom { namespace { @@ -83,19 +98,68 @@ class BundledDataSource : public content::URLDataSource { DISALLOW_COPY_AND_ASSIGN(BundledDataSource); }; +void RequestPdfResource( + const GURL& url, + const GURL& origin, + int render_process_id, + int render_view_id, + int render_frame_id, + content::ResourceContext* resource_context, + base::Callback)> callback, + atom::LayeredResourceHandler::Delegate* delegate) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + const net::URLRequestContext* request_context = + resource_context->GetRequestContext(); + std::unique_ptr request( + request_context->CreateRequest(url, net::DEFAULT_PRIORITY, nullptr)); + request->set_method("GET"); + + content::ResourceDispatcherHostImpl::Get()->InitializeURLRequest( + request.get(), content::Referrer(url, blink::WebReferrerPolicyDefault), + false, // download. + render_process_id, render_view_id, render_frame_id, resource_context); + + content::ResourceRequestInfoImpl* info = + content::ResourceRequestInfoImpl::ForRequest(request.get()); + content::StreamContext* stream_context = + content::GetStreamContextForResourceContext(resource_context); + + std::unique_ptr handler( + new content::StreamResourceHandler(request.get(), + stream_context->registry(), origin)); + info->set_is_stream(true); + std::unique_ptr stream_info(new content::StreamInfo); + stream_info->handle = + static_cast(handler.get()) + ->stream() + ->CreateHandle(); + stream_info->original_url = request->url(); + + // Helper to fill stream response details. + handler.reset(new atom::LayeredResourceHandler(request.get(), + std::move(handler), delegate)); + + content::ResourceDispatcherHostImpl::Get()->BeginURLRequest( + std::move(request), std::move(handler), + false, // download + false, // content_initiated (download specific) + false, // do_not_prompt_for_login (download specific) + resource_context); + + callback.Run(std::move(stream_info)); +} + } // namespace PdfViewerUI::PdfViewerUI(content::BrowserContext* browser_context, content::WebUI* web_ui, - const std::string& stream_id, const std::string& src) : content::WebUIController(web_ui), content::WebContentsObserver(web_ui->GetWebContents()), src_(src) { - auto context = static_cast(browser_context); - auto stream_manager = context->stream_manager(); - stream_ = stream_manager->ReleaseStream(stream_id); - web_ui->AddMessageHandler(new PdfViewerHandler(stream_.get(), src)); + pdf_handler_ = new PdfViewerHandler(src); + web_ui->AddMessageHandler(pdf_handler_); content::URLDataSource::Add(browser_context, new BundledDataSource); } @@ -112,6 +176,47 @@ bool PdfViewerUI::OnMessageReceived( return handled; } +void PdfViewerUI::OnPdfStreamResponseStarted( + scoped_refptr headers, + const std::string& mime_type) { + if (headers.get()) + stream_->response_headers = + new net::HttpResponseHeaders(headers->raw_headers()); + stream_->mime_type = mime_type; + pdf_handler_->SetPdfResourceStream(stream_.get()); +} + +void PdfViewerUI::OnResponseStarted(content::ResourceResponse* response) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + auto resource_response_head = response->head; + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&PdfViewerUI::OnPdfStreamResponseStarted, + base::Unretained(this), resource_response_head.headers, + resource_response_head.mime_type)); +} + +void PdfViewerUI::OnPdfStreamCreated( + std::unique_ptr stream) { + stream_ = std::move(stream); +} + +void PdfViewerUI::RenderFrameCreated(content::RenderFrameHost* rfh) { + int render_process_id = rfh->GetProcess()->GetID(); + int render_frame_id = rfh->GetRoutingID(); + int render_view_id = rfh->GetRenderViewHost()->GetRoutingID(); + auto resource_context = + web_contents()->GetBrowserContext()->GetResourceContext(); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind( + &RequestPdfResource, GURL(src_), GURL(kPdfViewerUIOrigin), + render_process_id, render_view_id, render_frame_id, resource_context, + base::Bind(&PdfViewerUI::OnPdfStreamCreated, base::Unretained(this)), + this)); +} + void PdfViewerUI::OnSaveURLAs(const GURL& url, const content::Referrer& referrer) { web_contents()->SaveFrame(url, referrer); diff --git a/atom/browser/ui/webui/pdf_viewer_ui.h b/atom/browser/ui/webui/pdf_viewer_ui.h index 53d30980ed0..b182e634a50 100644 --- a/atom/browser/ui/webui/pdf_viewer_ui.h +++ b/atom/browser/ui/webui/pdf_viewer_ui.h @@ -7,6 +7,7 @@ #include +#include "atom/browser/loader/layered_resource_handler.h" #include "base/macros.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_ui_controller.h" @@ -14,29 +15,48 @@ namespace content { class BrowserContext; +class ResourceContext; struct StreamInfo; } +namespace net { +class HttpResponseHeaders; +} + namespace atom { +class PdfViewerHandler; + class PdfViewerUI : public content::WebUIController, - public content::WebContentsObserver { + public content::WebContentsObserver, + public LayeredResourceHandler::Delegate { public: PdfViewerUI(content::BrowserContext* browser_context, content::WebUI* web_ui, - const std::string& stream_id, const std::string& src); ~PdfViewerUI() override; // content::WebContentsObserver: bool OnMessageReceived(const IPC::Message& message, content::RenderFrameHost* render_frame_host) override; + void RenderFrameCreated(content::RenderFrameHost* rfh) override; + + // LayeredResourceHandler: + void OnResponseStarted(content::ResourceResponse* response) override; private: + void OnPdfStreamCreated(std::unique_ptr stream_info); + void OnPdfStreamResponseStarted( + scoped_refptr headers, + const std::string& mime_type); void OnSaveURLAs(const GURL& url, const content::Referrer& referrer); // Source URL from where the PDF originates. std::string src_; + + PdfViewerHandler* pdf_handler_; + + // Pdf Resource stream. std::unique_ptr stream_; DISALLOW_COPY_AND_ASSIGN(PdfViewerUI); diff --git a/filenames.gypi b/filenames.gypi index 5501af542f4..1b9e456a55c 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -213,6 +213,8 @@ 'atom/browser/javascript_environment.h', 'atom/browser/lib/bluetooth_chooser.cc', 'atom/browser/lib/bluetooth_chooser.h', + 'atom/browser/loader/layered_resource_handler.cc', + 'atom/browser/loader/layered_resource_handler.h', 'atom/browser/login_handler.cc', 'atom/browser/login_handler.h', 'atom/browser/mac/atom_application.h',