diff --git a/.gitmodules b/.gitmodules index 5bc253bad5c..0cabff390f4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "vendor/boto"] path = vendor/boto url = https://github.com/boto/boto.git +[submodule "vendor/pdf_viewer"] + path = vendor/pdf_viewer + url = https://github.com/electron/pdf-viewer.git diff --git a/atom/app/atom_content_client.cc b/atom/app/atom_content_client.cc index 760a42732b3..954d239b503 100644 --- a/atom/app/atom_content_client.cc +++ b/atom/app/atom_content_client.cc @@ -7,6 +7,7 @@ #include #include +#include "atom/common/atom_constants.h" #include "atom/common/atom_version.h" #include "atom/common/chrome_version.h" #include "atom/common/options_switches.h" @@ -18,6 +19,7 @@ #include "content/public/common/content_constants.h" #include "content/public/common/pepper_plugin_info.h" #include "content/public/common/user_agent.h" +#include "pdf/pdf.h" #include "ppapi/shared_impl/ppapi_permissions.h" #include "third_party/widevine/cdm/stub/widevine_cdm_version.h" #include "ui/base/l10n/l10n_util.h" @@ -108,6 +110,25 @@ content::PepperPluginInfo CreateWidevineCdmInfo(const base::FilePath& path, } #endif +void ComputeBuiltInPlugins(std::vector* plugins) { + content::PepperPluginInfo pdf_info; + pdf_info.is_internal = true; + pdf_info.is_out_of_process = true; + pdf_info.name = "Chromium PDF Viewer"; + pdf_info.description = "Portable Document Format"; + pdf_info.path = base::FilePath::FromUTF8Unsafe(kPdfPluginPath); + content::WebPluginMimeType pdf_mime_type(kPdfPluginMimeType, "pdf", + "Portable Document Format"); + pdf_info.mime_types.push_back(pdf_mime_type); + pdf_info.internal_entry_points.get_interface = chrome_pdf::PPP_GetInterface; + pdf_info.internal_entry_points.initialize_module = + chrome_pdf::PPP_InitializeModule; + pdf_info.internal_entry_points.shutdown_module = + chrome_pdf::PPP_ShutdownModule; + pdf_info.permissions = ppapi::PERMISSION_PRIVATE | ppapi::PERMISSION_DEV; + plugins->push_back(pdf_info); +} + void ConvertStringWithSeparatorToVector(std::vector* vec, const char* separator, const char* cmd_switch) { @@ -190,6 +211,7 @@ void AtomContentClient::AddPepperPlugins( #if defined(WIDEVINE_CDM_AVAILABLE) && BUILDFLAG(ENABLE_PEPPER_CDMS) AddWidevineCdmFromCommandLine(plugins); #endif + ComputeBuiltInPlugins(plugins); } void AtomContentClient::AddServiceWorkerSchemes( diff --git a/atom/browser/atom_browser_main_parts.cc b/atom/browser/atom_browser_main_parts.cc index 4031fb27cf3..eeae1d59111 100644 --- a/atom/browser/atom_browser_main_parts.cc +++ b/atom/browser/atom_browser_main_parts.cc @@ -8,6 +8,7 @@ #include "atom/browser/atom_access_token_store.h" #include "atom/browser/atom_browser_client.h" #include "atom/browser/atom_browser_context.h" +#include "atom/browser/atom_web_ui_controller_factory.h" #include "atom/browser/bridge_task_runner.h" #include "atom/browser/browser.h" #include "atom/browser/javascript_environment.h" @@ -166,6 +167,9 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() { base::Bind(&v8::Isolate::LowMemoryNotification, base::Unretained(js_env_->isolate()))); + content::WebUIControllerFactory::RegisterFactory( + AtomWebUIControllerFactory::GetInstance()); + brightray::BrowserMainParts::PreMainMessageLoopRun(); bridge_task_runner_->MessageLoopIsReady(); bridge_task_runner_ = nullptr; diff --git a/atom/browser/atom_resource_dispatcher_host_delegate.cc b/atom/browser/atom_resource_dispatcher_host_delegate.cc index 78589112c55..abd2fb53f1e 100644 --- a/atom/browser/atom_resource_dispatcher_host_delegate.cc +++ b/atom/browser/atom_resource_dispatcher_host_delegate.cc @@ -6,11 +6,15 @@ #include "atom/browser/login_handler.h" #include "atom/browser/web_contents_permission_helper.h" +#include "atom/common/atom_constants.h" #include "atom/common/platform_util.h" +#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" #include "url/gurl.h" #if defined(USE_NSS_CERTS) @@ -57,6 +61,23 @@ void HandleExternalProtocolInUI( permission_helper->RequestOpenExternalPermission(callback, has_user_gesture); } +void OnPdfResourceIntercepted( + const GURL& original_url, + const content::ResourceRequestInfo::WebContentsGetter& + web_contents_getter) { + content::WebContents* web_contents = web_contents_getter.Run(); + if (!web_contents) + return; + + // 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()))); + web_contents->GetController().LoadURLWithParams(params); +} + } // namespace AtomResourceDispatcherHostDelegate::AtomResourceDispatcherHostDelegate() { @@ -95,4 +116,23 @@ AtomResourceDispatcherHostDelegate::CreateClientCertStore( #endif } +bool AtomResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream( + net::URLRequest* request, + const base::FilePath& plugin_path, + const std::string& mime_type, + GURL* origin, + std::string* payload) { + const content::ResourceRequestInfo* info = + content::ResourceRequestInfo::ForRequest(request); + if (mime_type == "application/pdf" && info->IsMainFrame()) { + *origin = GURL(kPdfViewerUIOrigin); + content::BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&OnPdfResourceIntercepted, request->url(), + info->GetWebContentsGetterForRequest())); + return true; + } + return false; +} + } // namespace atom diff --git a/atom/browser/atom_resource_dispatcher_host_delegate.h b/atom/browser/atom_resource_dispatcher_host_delegate.h index 681fec6f6fb..eda744db516 100644 --- a/atom/browser/atom_resource_dispatcher_host_delegate.h +++ b/atom/browser/atom_resource_dispatcher_host_delegate.h @@ -5,6 +5,8 @@ #ifndef ATOM_BROWSER_ATOM_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ #define ATOM_BROWSER_ATOM_RESOURCE_DISPATCHER_HOST_DELEGATE_H_ +#include + #include "content/public/browser/resource_dispatcher_host_delegate.h" namespace atom { @@ -22,6 +24,14 @@ class AtomResourceDispatcherHostDelegate net::URLRequest* request) override; std::unique_ptr CreateClientCertStore( content::ResourceContext* resource_context) override; + bool ShouldInterceptResourceAsStream(net::URLRequest* request, + const base::FilePath& plugin_path, + const std::string& mime_type, + GURL* origin, + std::string* payload) override; + + private: + DISALLOW_COPY_AND_ASSIGN(AtomResourceDispatcherHostDelegate); }; } // namespace atom diff --git a/atom/browser/atom_web_ui_controller_factory.cc b/atom/browser/atom_web_ui_controller_factory.cc new file mode 100644 index 00000000000..a73eed19a11 --- /dev/null +++ b/atom/browser/atom_web_ui_controller_factory.cc @@ -0,0 +1,66 @@ +// 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/atom_web_ui_controller_factory.h" + +#include + +#include "atom/browser/ui/webui/pdf_viewer_ui.h" +#include "atom/common/atom_constants.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "content/public/browser/web_contents.h" + +namespace atom { + +// static +AtomWebUIControllerFactory* AtomWebUIControllerFactory::GetInstance() { + return base::Singleton::get(); +} + +AtomWebUIControllerFactory::AtomWebUIControllerFactory() {} + +AtomWebUIControllerFactory::~AtomWebUIControllerFactory() {} + +content::WebUI::TypeID AtomWebUIControllerFactory::GetWebUIType( + content::BrowserContext* browser_context, + const GURL& url) const { + if (url.host() == kPdfViewerUIHost) { + return const_cast(this); + } + + return content::WebUI::kNoWebUI; +} + +bool AtomWebUIControllerFactory::UseWebUIForURL( + content::BrowserContext* browser_context, + const GURL& url) const { + return GetWebUIType(browser_context, url) != content::WebUI::kNoWebUI; +} + +bool AtomWebUIControllerFactory::UseWebUIBindingsForURL( + content::BrowserContext* browser_context, + const GURL& url) const { + return UseWebUIForURL(browser_context, url); +} + +content::WebUIController* +AtomWebUIControllerFactory::CreateWebUIControllerForURL(content::WebUI* web_ui, + const GURL& url) const { + if (url.host() == kPdfViewerUIHost) { + base::StringPairs toplevel_params; + base::SplitStringIntoKeyValuePairs(url.query(), '=', '&', &toplevel_params); + std::string stream_id, src; + for (const auto& param : toplevel_params) { + if (param.first == kPdfPluginSrc) { + src = param.second; + } + } + auto browser_context = web_ui->GetWebContents()->GetBrowserContext(); + return new PdfViewerUI(browser_context, web_ui, src); + } + return nullptr; +} + +} // namespace atom diff --git a/atom/browser/atom_web_ui_controller_factory.h b/atom/browser/atom_web_ui_controller_factory.h new file mode 100644 index 00000000000..41819daf8cc --- /dev/null +++ b/atom/browser/atom_web_ui_controller_factory.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_ATOM_WEB_UI_CONTROLLER_FACTORY_H_ +#define ATOM_BROWSER_ATOM_WEB_UI_CONTROLLER_FACTORY_H_ + +#include "base/macros.h" +#include "base/memory/singleton.h" +#include "content/public/browser/web_ui_controller_factory.h" + +namespace atom { + +class AtomWebUIControllerFactory : public content::WebUIControllerFactory { + public: + static AtomWebUIControllerFactory* GetInstance(); + + AtomWebUIControllerFactory(); + virtual ~AtomWebUIControllerFactory(); + + // content::WebUIControllerFactory: + content::WebUI::TypeID GetWebUIType(content::BrowserContext* browser_context, + const GURL& url) const override; + bool UseWebUIForURL(content::BrowserContext* browser_context, + const GURL& url) const override; + bool UseWebUIBindingsForURL(content::BrowserContext* browser_context, + const GURL& url) const override; + content::WebUIController* CreateWebUIControllerForURL( + content::WebUI* web_ui, + const GURL& url) const override; + + private: + friend struct base::DefaultSingletonTraits; + + DISALLOW_COPY_AND_ASSIGN(AtomWebUIControllerFactory); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_ATOM_WEB_UI_CONTROLLER_FACTORY_H_ 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 new file mode 100644 index 00000000000..be2f3373055 --- /dev/null +++ b/atom/browser/ui/webui/pdf_viewer_handler.cc @@ -0,0 +1,212 @@ +// 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/ui/webui/pdf_viewer_handler.h" + +#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" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_ui.h" +#include "content/public/common/page_zoom.h" +#include "content/public/common/url_constants.h" +#include "net/http/http_response_headers.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/webui/web_ui_util.h" + +namespace atom { + +namespace { + +void CreateResponseHeadersDictionary(const net::HttpResponseHeaders* headers, + base::DictionaryValue* result) { + if (!headers) + return; + + size_t iter = 0; + std::string header_name; + std::string header_value; + while (headers->EnumerateHeaderLines(&iter, &header_name, &header_value)) { + base::Value* existing_value = nullptr; + if (result->Get(header_name, &existing_value)) { + base::StringValue* existing_string_value = + static_cast(existing_value); + existing_string_value->GetString()->append(", ").append(header_value); + } else { + result->SetString(header_name, header_value); + } + } +} + +void PopulateStreamInfo(base::DictionaryValue* stream_info, + content::StreamInfo* stream, + const std::string& original_url) { + auto headers_dict = base::MakeUnique(); + 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 std::string& src) + : stream_(nullptr), original_url_(src) {} + +PdfViewerHandler::~PdfViewerHandler() {} + +void PdfViewerHandler::SetPdfResourceStream(content::StreamInfo* stream) { + stream_ = stream; + if (!!initialize_callback_id_.get()) { + auto list = base::MakeUnique(); + list->Set(0, std::move(initialize_callback_id_)); + Initialize(list.get()); + } +} + +void PdfViewerHandler::RegisterMessages() { + web_ui()->RegisterMessageCallback( + "initialize", + base::Bind(&PdfViewerHandler::Initialize, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "getDefaultZoom", + base::Bind(&PdfViewerHandler::GetInitialZoom, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "getInitialZoom", + base::Bind(&PdfViewerHandler::GetInitialZoom, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "setZoom", + base::Bind(&PdfViewerHandler::SetZoom, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "getStrings", + base::Bind(&PdfViewerHandler::GetStrings, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "reload", base::Bind(&PdfViewerHandler::Reload, base::Unretained(this))); +} + +void PdfViewerHandler::OnJavascriptAllowed() { + auto host_zoom_map = + content::HostZoomMap::GetForWebContents(web_ui()->GetWebContents()); + host_zoom_map_subscription_ = + host_zoom_map->AddZoomLevelChangedCallback(base::Bind( + &PdfViewerHandler::OnZoomLevelChanged, base::Unretained(this))); +} + +void PdfViewerHandler::OnJavascriptDisallowed() { + host_zoom_map_subscription_.reset(); +} + +void PdfViewerHandler::Initialize(const base::ListValue* args) { + CHECK_EQ(1U, args->GetSize()); + const base::Value* callback_id; + CHECK(args->Get(0, &callback_id)); + + if (stream_) { + CHECK(!initialize_callback_id_.get()); + AllowJavascript(); + + auto stream_info = base::MakeUnique(); + PopulateStreamInfo(stream_info.get(), stream_, original_url_); + ResolveJavascriptCallback(*callback_id, *stream_info); + } else { + initialize_callback_id_ = callback_id->CreateDeepCopy(); + } +} + +void PdfViewerHandler::GetDefaultZoom(const base::ListValue* args) { + if (!IsJavascriptAllowed()) + return; + CHECK_EQ(1U, args->GetSize()); + const base::Value* callback_id; + CHECK(args->Get(0, &callback_id)); + + auto host_zoom_map = + content::HostZoomMap::GetForWebContents(web_ui()->GetWebContents()); + double zoom_level = host_zoom_map->GetDefaultZoomLevel(); + ResolveJavascriptCallback( + *callback_id, + base::FundamentalValue(content::ZoomLevelToZoomFactor(zoom_level))); +} + +void PdfViewerHandler::GetInitialZoom(const base::ListValue* args) { + if (!IsJavascriptAllowed()) + return; + CHECK_EQ(1U, args->GetSize()); + const base::Value* callback_id; + CHECK(args->Get(0, &callback_id)); + + double zoom_level = + content::HostZoomMap::GetZoomLevel(web_ui()->GetWebContents()); + ResolveJavascriptCallback( + *callback_id, + base::FundamentalValue(content::ZoomLevelToZoomFactor(zoom_level))); +} + +void PdfViewerHandler::SetZoom(const base::ListValue* args) { + if (!IsJavascriptAllowed()) + return; + CHECK_EQ(2U, args->GetSize()); + const base::Value* callback_id; + CHECK(args->Get(0, &callback_id)); + double zoom_level = 0.0; + CHECK(args->GetDouble(1, &zoom_level)); + + content::HostZoomMap::SetZoomLevel(web_ui()->GetWebContents(), + zoom_level); + ResolveJavascriptCallback(*callback_id, base::FundamentalValue(zoom_level)); +} + +void PdfViewerHandler::GetStrings(const base::ListValue* args) { + if (!IsJavascriptAllowed()) + return; + CHECK_EQ(1U, args->GetSize()); + const base::Value* callback_id; + CHECK(args->Get(0, &callback_id)); + + auto result = base::MakeUnique(); +// TODO(deepak1556): Generate strings from components/pdf_strings.grdp. +#define SET_STRING(id, resource) result->SetString(id, resource) + SET_STRING("passwordPrompt", + "This document is password protected. Please enter a password."); + SET_STRING("passwordSubmit", "Submit"); + SET_STRING("passwordInvalid", "Incorrect password"); + SET_STRING("pageLoading", "Loading..."); + SET_STRING("pageLoadFailed", "Failed to load PDF document"); + SET_STRING("pageReload", "Reload"); + SET_STRING("bookmarks", "Bookmarks"); + SET_STRING("labelPageNumber", "Page number"); + SET_STRING("tooltipRotateCW", "Rotate clockwise"); + SET_STRING("tooltipDownload", "Download"); + SET_STRING("tooltipFitToPage", "Fit to page"); + SET_STRING("tooltipFitToWidth", "Fit to width"); + SET_STRING("tooltipZoomIn", "Zoom in"); + SET_STRING("tooltipZoomOut", "Zoom out"); +#undef SET_STRING + + webui::SetLoadTimeDataDefaults(l10n_util::GetApplicationLocale(""), + result.get()); + ResolveJavascriptCallback(*callback_id, *result); +} + +void PdfViewerHandler::Reload(const base::ListValue* args) { + CHECK_EQ(0U, args->GetSize()); + web_ui()->GetWebContents()->ReloadFocusedFrame(false); +} + +void PdfViewerHandler::OnZoomLevelChanged( + const content::HostZoomMap::ZoomLevelChange& change) { + if (change.host == kPdfViewerUIHost) { + CallJavascriptFunction( + "cr.webUIListenerCallback", base::StringValue("onZoomLevelChanged"), + base::FundamentalValue( + content::ZoomLevelToZoomFactor(change.zoom_level))); + } +} + +} // namespace atom diff --git a/atom/browser/ui/webui/pdf_viewer_handler.h b/atom/browser/ui/webui/pdf_viewer_handler.h new file mode 100644 index 00000000000..7576b06c851 --- /dev/null +++ b/atom/browser/ui/webui/pdf_viewer_handler.h @@ -0,0 +1,58 @@ +// 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_UI_WEBUI_PDF_VIEWER_HANDLER_H_ +#define ATOM_BROWSER_UI_WEBUI_PDF_VIEWER_HANDLER_H_ + +#include + +#include "base/macros.h" +#include "content/public/browser/host_zoom_map.h" +#include "content/public/browser/web_ui_message_handler.h" + +namespace base { +class ListValue; +} + +namespace content { +struct StreamInfo; +} + +namespace atom { + +class PdfViewerHandler : public content::WebUIMessageHandler { + public: + explicit PdfViewerHandler(const std::string& original_url); + ~PdfViewerHandler() override; + + void SetPdfResourceStream(content::StreamInfo* stream); + + protected: + // WebUIMessageHandler implementation. + void RegisterMessages() override; + void OnJavascriptAllowed() override; + void OnJavascriptDisallowed() override; + + private: + void Initialize(const base::ListValue* args); + void GetDefaultZoom(const base::ListValue* args); + void GetInitialZoom(const base::ListValue* args); + void SetZoom(const base::ListValue* args); + void GetStrings(const base::ListValue* args); + void Reload(const base::ListValue* args); + void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change); + + // Keeps track of events related to zooming. + std::unique_ptr + host_zoom_map_subscription_; + std::unique_ptr initialize_callback_id_; + content::StreamInfo* stream_; + std::string original_url_; + + DISALLOW_COPY_AND_ASSIGN(PdfViewerHandler); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_UI_WEBUI_PDF_VIEWER_HANDLER_H_ diff --git a/atom/browser/ui/webui/pdf_viewer_ui.cc b/atom/browser/ui/webui/pdf_viewer_ui.cc new file mode 100644 index 00000000000..580d1831e58 --- /dev/null +++ b/atom/browser/ui/webui/pdf_viewer_ui.cc @@ -0,0 +1,251 @@ +// 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/ui/webui/pdf_viewer_ui.h" + +#include + +#include "atom/browser/atom_browser_context.h" +#include "atom/browser/loader/layered_resource_handler.h" +#include "atom/browser/ui/webui/pdf_viewer_handler.h" +#include "atom/common/atom_constants.h" +#include "base/sequenced_task_runner_helpers.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/stream_info.h" +#include "content/public/browser/url_data_source.h" +#include "content/public/browser/web_contents.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 { + +// Extracts the path value from the URL without the leading '/', +// which follows the mapping of names in pdf_viewer_resources_map. +std::string PathWithoutParams(const std::string& path) { + return GURL(kPdfViewerUIOrigin + path).path().substr(1); +} + +class BundledDataSource : public content::URLDataSource { + public: + BundledDataSource() { + for (size_t i = 0; i < kPdfViewerResourcesSize; ++i) { + std::string resource_path = kPdfViewerResources[i].name; + DCHECK(path_to_resource_id_.find(resource_path) == + path_to_resource_id_.end()); + path_to_resource_id_[resource_path] = kPdfViewerResources[i].value; + } + } + + // content::URLDataSource implementation. + std::string GetSource() const override { return kPdfViewerUIHost; } + + void StartDataRequest( + const std::string& path, + const content::ResourceRequestInfo::WebContentsGetter& wc_getter, + const GotDataCallback& callback) override { + std::string filename = PathWithoutParams(path); + auto entry = path_to_resource_id_.find(filename); + + if (entry != path_to_resource_id_.end()) { + int resource_id = entry->second; + const ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + callback.Run(rb.LoadDataResourceBytes(resource_id)); + } else { + LOG(ERROR) << "Unable to find: " << path; + callback.Run(new base::RefCountedString()); + } + } + + std::string GetMimeType(const std::string& path) const override { + std::string filename = PathWithoutParams(path); + std::string mime_type; + net::GetMimeTypeFromFile( + base::FilePath::FromUTF8Unsafe(filename), &mime_type); + return mime_type; + } + + bool ShouldAddContentSecurityPolicy() const override { return false; } + + bool ShouldDenyXFrameOptions() const override { return false; } + + bool ShouldServeMimeTypeAsContentTypeHeader() const override { return true; } + + private: + ~BundledDataSource() override {} + + // A map from a resource path to the resource ID. + std::map path_to_resource_id_; + + DISALLOW_COPY_AND_ASSIGN(BundledDataSource); +}; + +// Helper to convert from OnceCallback to Callback. +template +void CallMigrationCallback(T callback, + std::unique_ptr stream_info) { + std::move(callback).Run(std::move(stream_info)); +} + +} // namespace + +class PdfViewerUI::ResourceRequester + : public base::RefCountedThreadSafe, + public atom::LayeredResourceHandler::Delegate { + public: + explicit ResourceRequester(StreamResponseCallback cb) + : stream_response_cb_(std::move(cb)) {} + + void StartRequest(const GURL& url, + const GURL& origin, + int render_process_id, + int render_view_id, + int render_frame_id, + content::ResourceContext* resource_context) { + 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 = + base::MakeUnique( + request.get(), stream_context->registry(), origin); + info->set_is_stream(true); + stream_info_.reset(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), this)); + + 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); + } + + protected: + // atom::LayeredResourceHandler::Delegate: + void OnResponseStarted(content::ResourceResponse* response) override { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + auto resource_response_head = response->head; + auto headers = resource_response_head.headers; + auto mime_type = resource_response_head.mime_type; + if (headers.get()) + stream_info_->response_headers = + new net::HttpResponseHeaders(headers->raw_headers()); + stream_info_->mime_type = mime_type; + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&CallMigrationCallback, + base::Passed(&stream_response_cb_), + base::Passed(&stream_info_))); + } + + private: + friend struct BrowserThread::DeleteOnThread; + friend class base::DeleteHelper; + ~ResourceRequester() override {} + + StreamResponseCallback stream_response_cb_; + std::unique_ptr stream_info_; + + DISALLOW_COPY_AND_ASSIGN(ResourceRequester); +}; + +PdfViewerUI::PdfViewerUI(content::BrowserContext* browser_context, + content::WebUI* web_ui, + const std::string& src) + : content::WebUIController(web_ui), + content::WebContentsObserver(web_ui->GetWebContents()), + src_(src) { + pdf_handler_ = new PdfViewerHandler(src); + web_ui->AddMessageHandler(pdf_handler_); + content::URLDataSource::Add(browser_context, new BundledDataSource); +} + +PdfViewerUI::~PdfViewerUI() {} + +bool PdfViewerUI::OnMessageReceived( + const IPC::Message& message, + content::RenderFrameHost* render_frame_host) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PdfViewerUI, message) + IPC_MESSAGE_HANDLER(PDFHostMsg_PDFSaveURLAs, OnSaveURLAs) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void PdfViewerUI::OnPdfStreamCreated( + std::unique_ptr stream) { + stream_ = std::move(stream); + if (pdf_handler_) + pdf_handler_->SetPdfResourceStream(stream_.get()); + resource_requester_ = nullptr; +} + +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(); + auto callback = + base::BindOnce(&PdfViewerUI::OnPdfStreamCreated, base::Unretained(this)); + resource_requester_ = new ResourceRequester(std::move(callback)); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(&ResourceRequester::StartRequest, resource_requester_, + GURL(src_), GURL(kPdfViewerUIOrigin), render_process_id, + render_view_id, render_frame_id, resource_context)); +} + +void PdfViewerUI::OnSaveURLAs(const GURL& url, + const content::Referrer& referrer) { + web_contents()->SaveFrame(url, referrer); +} + +} // namespace atom diff --git a/atom/browser/ui/webui/pdf_viewer_ui.h b/atom/browser/ui/webui/pdf_viewer_ui.h new file mode 100644 index 00000000000..2f514f5114b --- /dev/null +++ b/atom/browser/ui/webui/pdf_viewer_ui.h @@ -0,0 +1,60 @@ +// 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_UI_WEBUI_PDF_VIEWER_UI_H_ +#define ATOM_BROWSER_UI_WEBUI_PDF_VIEWER_UI_H_ + +#include + +#include "base/macros.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_ui_controller.h" +#include "ipc/ipc_message.h" + +namespace content { +class BrowserContext; +struct StreamInfo; +} + +namespace atom { + +class PdfViewerHandler; + +class PdfViewerUI : public content::WebUIController, + public content::WebContentsObserver { + public: + PdfViewerUI(content::BrowserContext* browser_context, + content::WebUI* web_ui, + 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; + + private: + using StreamResponseCallback = + base::OnceCallback)>; + class ResourceRequester; + + void OnPdfStreamCreated(std::unique_ptr stream_info); + void OnSaveURLAs(const GURL& url, const content::Referrer& referrer); + + // Source URL from where the PDF originates. + std::string src_; + + PdfViewerHandler* pdf_handler_; + + scoped_refptr resource_requester_; + + // Pdf Resource stream. + std::unique_ptr stream_; + + DISALLOW_COPY_AND_ASSIGN(PdfViewerUI); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_UI_WEBUI_PDF_VIEWER_UI_H_ diff --git a/atom/common/atom_constants.cc b/atom/common/atom_constants.cc index f66c947aa24..85307ded9e7 100644 --- a/atom/common/atom_constants.cc +++ b/atom/common/atom_constants.cc @@ -24,4 +24,11 @@ const char kSecureProtocolDescription[] = "The connection to this site is using a strong protocol version " "and cipher suite."; +const char kPdfPluginMimeType[] = "application/x-google-chrome-pdf"; +const char kPdfPluginPath[] = "chrome://pdf-viewer/"; +const char kPdfPluginSrc[] = "src"; + +const char kPdfViewerUIOrigin[] = "chrome://pdf-viewer/"; +const char kPdfViewerUIHost[] = "pdf-viewer"; + } // namespace atom diff --git a/atom/common/atom_constants.h b/atom/common/atom_constants.h index b67b7b2e4ae..f507214d9d8 100644 --- a/atom/common/atom_constants.h +++ b/atom/common/atom_constants.h @@ -20,6 +20,15 @@ extern const char kValidCertificateDescription[]; extern const char kSecureProtocol[]; extern const char kSecureProtocolDescription[]; +// The MIME type used for the PDF plugin. +extern const char kPdfPluginMimeType[]; +extern const char kPdfPluginPath[]; +extern const char kPdfPluginSrc[]; + +// Constants for PDF viewer webui. +extern const char kPdfViewerUIOrigin[]; +extern const char kPdfViewerUIHost[]; + } // namespace atom #endif // ATOM_COMMON_ATOM_CONSTANTS_H_ diff --git a/atom/common/common_message_generator.h b/atom/common/common_message_generator.h index 64206956863..a63c40b9623 100644 --- a/atom/common/common_message_generator.h +++ b/atom/common/common_message_generator.h @@ -9,3 +9,4 @@ #include "chrome/common/tts_messages.h" #include "chrome/common/widevine_cdm_messages.h" #include "chrome/common/chrome_utility_messages.h" +#include "components/pdf/common/pdf_messages.h" diff --git a/atom/renderer/atom_renderer_client.cc b/atom/renderer/atom_renderer_client.cc index 637792c2aed..9b3d2214b4c 100644 --- a/atom/renderer/atom_renderer_client.cc +++ b/atom/renderer/atom_renderer_client.cc @@ -12,6 +12,7 @@ #include "atom/common/api/api_messages.h" #include "atom/common/api/atom_bindings.h" #include "atom/common/api/event_emitter_caller.h" +#include "atom/common/atom_constants.h" #include "atom/common/color_util.h" #include "atom/common/native_mate_converters/value_converter.h" #include "atom/common/node_bindings.h" @@ -274,6 +275,10 @@ void AtomRendererClient::RenderFrameCreated( // This is required for widevine plugin detection provided during runtime. blink::resetPluginCache(); + // Allow access to file scheme from pdf viewer. + blink::WebSecurityPolicy::addOriginAccessWhitelistEntry( + GURL(kPdfViewerUIOrigin), "file", "", true); + // Parse --secure-schemes=scheme1,scheme2 std::vector secure_schemes_list = ParseSchemesCLISwitch(switches::kSecureSchemes); @@ -339,6 +344,7 @@ bool AtomRendererClient::OverrideCreatePlugin( blink::WebPlugin** plugin) { base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (params.mimeType.utf8() == content::kBrowserPluginMimeType || + params.mimeType.utf8() == kPdfPluginMimeType || command_line->HasSwitch(switches::kEnablePlugins)) return false; diff --git a/chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.cc b/chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.cc index bcf6debc4cf..f5829b9e599 100644 --- a/chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.cc +++ b/chromium_src/chrome/renderer/pepper/chrome_renderer_pepper_host_factory.cc @@ -5,10 +5,12 @@ #include "chrome/renderer/pepper/chrome_renderer_pepper_host_factory.h" #include "base/logging.h" +#include "base/memory/ptr_util.h" #include "chrome/renderer/pepper/pepper_flash_font_file_host.h" #include "chrome/renderer/pepper/pepper_flash_fullscreen_host.h" #include "chrome/renderer/pepper/pepper_flash_menu_host.h" #include "chrome/renderer/pepper/pepper_flash_renderer_host.h" +#include "components/pdf/renderer/pepper_pdf_host.h" #include "content/public/renderer/renderer_ppapi_host.h" #include "ppapi/host/ppapi_host.h" #include "ppapi/host/resource_host.h" @@ -79,5 +81,14 @@ std::unique_ptr ChromeRendererPepperHostFactory::CreateResourceHos } } + if (host_->GetPpapiHost()->permissions().HasPermission( + ppapi::PERMISSION_PRIVATE)) { + switch (message.type()) { + case PpapiHostMsg_PDF_Create::ID: { + return base::MakeUnique(host_, instance, resource); + } + } + } + return std::unique_ptr(); } diff --git a/chromium_src/components/pdf/common/pdf_messages.h b/chromium_src/components/pdf/common/pdf_messages.h new file mode 100644 index 00000000000..c6325be4e1b --- /dev/null +++ b/chromium_src/components/pdf/common/pdf_messages.h @@ -0,0 +1,19 @@ +// Copyright 2014 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. + +// Multiply-included file, no traditional include guard. +#include + +#include "content/public/common/common_param_traits_macros.h" +#include "content/public/common/referrer.h" +#include "ipc/ipc_message_macros.h" +#include "url/gurl.h" +#include "url/ipc/url_param_traits.h" + +#define IPC_MESSAGE_START PDFMsgStart + +// Brings up SaveAs... dialog to save specified URL. +IPC_MESSAGE_ROUTED2(PDFHostMsg_PDFSaveURLAs, + GURL /* url */, + content::Referrer /* referrer */) diff --git a/chromium_src/components/pdf/renderer/pepper_pdf_host.cc b/chromium_src/components/pdf/renderer/pepper_pdf_host.cc new file mode 100644 index 00000000000..5f0e9afe548 --- /dev/null +++ b/chromium_src/components/pdf/renderer/pepper_pdf_host.cc @@ -0,0 +1,113 @@ +// Copyright 2014 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 "components/pdf/renderer/pepper_pdf_host.h" + +#include "base/memory/ptr_util.h" +#include "components/pdf/common/pdf_messages.h" +#include "content/public/common/referrer.h" +#include "content/public/renderer/pepper_plugin_instance.h" +#include "content/public/renderer/render_frame.h" +#include "content/public/renderer/renderer_ppapi_host.h" +#include "ppapi/host/dispatch_host_message.h" +#include "ppapi/proxy/ppapi_messages.h" + +namespace pdf { + +PepperPDFHost::PepperPDFHost(content::RendererPpapiHost* host, + PP_Instance instance, + PP_Resource resource) + : ppapi::host::ResourceHost(host->GetPpapiHost(), instance, resource), + host_(host) {} + +PepperPDFHost::~PepperPDFHost() {} + +int32_t PepperPDFHost::OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) { + PPAPI_BEGIN_MESSAGE_MAP(PepperPDFHost, msg) + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_DidStartLoading, + OnHostMsgDidStartLoading) + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_DidStopLoading, + OnHostMsgDidStopLoading) + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_PDF_SaveAs, + OnHostMsgSaveAs) + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_PDF_SetSelectedText, + OnHostMsgSetSelectedText) + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_PDF_SetLinkUnderCursor, + OnHostMsgSetLinkUnderCursor) + PPAPI_END_MESSAGE_MAP() + return PP_ERROR_FAILED; +} + +int32_t PepperPDFHost::OnHostMsgDidStartLoading( + ppapi::host::HostMessageContext* context) { + content::RenderFrame* render_frame = GetRenderFrame(); + if (!render_frame) + return PP_ERROR_FAILED; + + render_frame->DidStartLoading(); + return PP_OK; +} + +int32_t PepperPDFHost::OnHostMsgDidStopLoading( + ppapi::host::HostMessageContext* context) { + content::RenderFrame* render_frame = GetRenderFrame(); + if (!render_frame) + return PP_ERROR_FAILED; + + render_frame->DidStopLoading(); + return PP_OK; +} + +int32_t PepperPDFHost::OnHostMsgSaveAs( + ppapi::host::HostMessageContext* context) { + content::PepperPluginInstance* instance = + host_->GetPluginInstance(pp_instance()); + if (!instance) + return PP_ERROR_FAILED; + + content::RenderFrame* render_frame = instance->GetRenderFrame(); + if (!render_frame) + return PP_ERROR_FAILED; + + GURL url = instance->GetPluginURL(); + content::Referrer referrer; + referrer.url = url; + referrer.policy = blink::WebReferrerPolicyDefault; + referrer = content::Referrer::SanitizeForRequest(url, referrer); + render_frame->Send( + new PDFHostMsg_PDFSaveURLAs(render_frame->GetRoutingID(), url, referrer)); + return PP_OK; +} + +int32_t PepperPDFHost::OnHostMsgSetSelectedText( + ppapi::host::HostMessageContext* context, + const base::string16& selected_text) { + content::PepperPluginInstance* instance = + host_->GetPluginInstance(pp_instance()); + if (!instance) + return PP_ERROR_FAILED; + instance->SetSelectedText(selected_text); + return PP_OK; +} + +int32_t PepperPDFHost::OnHostMsgSetLinkUnderCursor( + ppapi::host::HostMessageContext* context, + const std::string& url) { + content::PepperPluginInstance* instance = + host_->GetPluginInstance(pp_instance()); + if (!instance) + return PP_ERROR_FAILED; + instance->SetLinkUnderCursor(url); + return PP_OK; +} + +content::RenderFrame* PepperPDFHost::GetRenderFrame() { + content::PepperPluginInstance* instance = + host_->GetPluginInstance(pp_instance()); + return instance ? instance->GetRenderFrame() : nullptr; +} + +} // namespace pdf diff --git a/chromium_src/components/pdf/renderer/pepper_pdf_host.h b/chromium_src/components/pdf/renderer/pepper_pdf_host.h new file mode 100644 index 00000000000..1b0d35102dd --- /dev/null +++ b/chromium_src/components/pdf/renderer/pepper_pdf_host.h @@ -0,0 +1,52 @@ +// Copyright 2014 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 COMPONENTS_PDF_RENDERER_PEPPER_PDF_HOST_H_ +#define COMPONENTS_PDF_RENDERER_PEPPER_PDF_HOST_H_ + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "ppapi/host/resource_host.h" + +namespace content { +class RenderFrame; +class RendererPpapiHost; +} + +namespace pdf { + +class PdfAccessibilityTree; + +class PepperPDFHost : public ppapi::host::ResourceHost { + public: + PepperPDFHost(content::RendererPpapiHost* host, + PP_Instance instance, + PP_Resource resource); + ~PepperPDFHost() override; + + // ppapi::host::ResourceHost: + int32_t OnResourceMessageReceived( + const IPC::Message& msg, + ppapi::host::HostMessageContext* context) override; + + private: + int32_t OnHostMsgDidStartLoading(ppapi::host::HostMessageContext* context); + int32_t OnHostMsgDidStopLoading(ppapi::host::HostMessageContext* context); + int32_t OnHostMsgSaveAs(ppapi::host::HostMessageContext* context); + int32_t OnHostMsgSetSelectedText(ppapi::host::HostMessageContext* context, + const base::string16& selected_text); + int32_t OnHostMsgSetLinkUnderCursor(ppapi::host::HostMessageContext* context, + const std::string& url); + + content::RenderFrame* GetRenderFrame(); + + content::RendererPpapiHost* const host_; + + DISALLOW_COPY_AND_ASSIGN(PepperPDFHost); +}; + +} // namespace pdf + +#endif // COMPONENTS_PDF_RENDERER_PEPPER_PDF_HOST_H_ diff --git a/electron.gyp b/electron.gyp index 47bc67b98e6..85b3daf9ddf 100644 --- a/electron.gyp +++ b/electron.gyp @@ -210,6 +210,7 @@ 'type': 'static_library', 'dependencies': [ 'atom_js2c', + 'vendor/pdf_viewer/pdf_viewer.gyp:pdf_viewer', 'vendor/brightray/brightray.gyp:brightray', 'vendor/node/node.gyp:node', ], @@ -543,6 +544,7 @@ '<(libchromiumcontent_dir)/icudtl.dat', '<(libchromiumcontent_dir)/natives_blob.bin', '<(libchromiumcontent_dir)/snapshot_blob.bin', + '<(PRODUCT_DIR)/pdf_viewer_resources.pak', ], 'xcode_settings': { 'ATOM_BUNDLE_ID': 'com.<(company_abbr).<(project_name).framework', diff --git a/filenames.gypi b/filenames.gypi index 44a6fbfc431..04dfe114e03 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -195,6 +195,8 @@ 'atom/browser/atom_resource_dispatcher_host_delegate.h', 'atom/browser/atom_speech_recognition_manager_delegate.cc', 'atom/browser/atom_speech_recognition_manager_delegate.h', + 'atom/browser/atom_web_ui_controller_factory.cc', + 'atom/browser/atom_web_ui_controller_factory.h', 'atom/browser/bridge_task_runner.cc', 'atom/browser/bridge_task_runner.h', 'atom/browser/browser.cc', @@ -211,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', @@ -324,6 +328,10 @@ 'atom/browser/ui/views/submenu_button.h', 'atom/browser/ui/views/win_frame_view.cc', 'atom/browser/ui/views/win_frame_view.h', + 'atom/browser/ui/webui/pdf_viewer_handler.cc', + 'atom/browser/ui/webui/pdf_viewer_handler.h', + 'atom/browser/ui/webui/pdf_viewer_ui.cc', + 'atom/browser/ui/webui/pdf_viewer_ui.h', 'atom/browser/ui/win/atom_desktop_native_widget_aura.cc', 'atom/browser/ui/win/atom_desktop_native_widget_aura.h', 'atom/browser/ui/win/atom_desktop_window_tree_host_win.cc', @@ -605,6 +613,9 @@ 'chromium_src/chrome/renderer/tts_dispatcher.cc', 'chromium_src/chrome/renderer/tts_dispatcher.h', 'chromium_src/chrome/utility/utility_message_handler.h', + 'chromium_src/components/pdf/common/pdf_messages.h', + 'chromium_src/components/pdf/renderer/pepper_pdf_host.cc', + 'chromium_src/components/pdf/renderer/pepper_pdf_host.h', 'chromium_src/extensions/browser/app_window/size_constraints.cc', 'chromium_src/extensions/browser/app_window/size_constraints.h', 'chromium_src/extensions/common/url_pattern.cc', @@ -617,6 +628,8 @@ 'chromium_src/net/test/embedded_test_server/tcp_listen_socket.h', '<@(native_mate_files)', '<(SHARED_INTERMEDIATE_DIR)/atom_natives.h', + '<(SHARED_INTERMEDIATE_DIR)/grit/pdf_viewer_resources_map.cc', + '<(SHARED_INTERMEDIATE_DIR)/grit/pdf_viewer_resources_map.h', ], 'lib_sources_linux': [ 'chromium_src/chrome/browser/icon_loader_auralinux.cc', diff --git a/lib/renderer/init.js b/lib/renderer/init.js index 24e19b9d2a6..892be116d9a 100644 --- a/lib/renderer/init.js +++ b/lib/renderer/init.js @@ -80,6 +80,9 @@ if (window.location.protocol === 'chrome-devtools:') { // Add implementations of chrome API. require('./chrome-api').injectTo(window.location.hostname, isBackgroundPage, window) nodeIntegration = 'false' +} else if (window.location.protocol === 'chrome:') { + // Disable node integration for chrome UI scheme. + nodeIntegration = 'false' } else { // Override default web functions. require('./override') diff --git a/script/create-dist.py b/script/create-dist.py index 5a63b20707d..35a109cfa3f 100755 --- a/script/create-dist.py +++ b/script/create-dist.py @@ -31,6 +31,7 @@ TARGET_BINARIES = { 'win32': [ '{0}.exe'.format(PROJECT_NAME), # 'electron.exe' 'content_shell.pak', + 'pdf_viewer_resources.pak', 'd3dcompiler_47.dll', 'icudtl.dat', 'libEGL.dll', @@ -48,6 +49,7 @@ TARGET_BINARIES = { 'linux': [ PROJECT_NAME, # 'electron' 'content_shell.pak', + 'pdf_viewer_resources.pak', 'icudtl.dat', 'libffmpeg.so', 'libnode.so', diff --git a/script/lib/config.py b/script/lib/config.py index ee47030cf9b..b0ef9c229dc 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -9,7 +9,7 @@ import sys BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \ 'https://s3.amazonaws.com/github-janky-artifacts/libchromiumcontent' LIBCHROMIUMCONTENT_COMMIT = os.getenv('LIBCHROMIUMCONTENT_COMMIT') or \ - '4f5b89374df7ee69095b9f7d50b30fb46ddd7407' + '82ea4bfcb7e594aa7b49e02c9bee32500688a62a' PLATFORM = { 'cygwin': 'win32', diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index 1a7bf3538b7..f5a4cbf56c2 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -313,11 +313,11 @@ describe('session module', function () { fs.unlinkSync(downloadFilePath) } - it('can download using BrowserWindow.loadURL', function (done) { + it('can download using WebContents.downloadURL', function (done) { downloadServer.listen(0, '127.0.0.1', function () { var port = downloadServer.address().port ipcRenderer.sendSync('set-download-option', false, false) - w.loadURL(url + ':' + port) + w.webContents.downloadURL(url + ':' + port) ipcRenderer.once('download-done', function (event, state, url, mimeType, receivedBytes, totalBytes, disposition, @@ -355,7 +355,7 @@ describe('session module', function () { downloadServer.listen(0, '127.0.0.1', function () { var port = downloadServer.address().port ipcRenderer.sendSync('set-download-option', true, false) - w.loadURL(url + ':' + port + '/') + w.webContents.downloadURL(url + ':' + port + '/') ipcRenderer.once('download-done', function (event, state, url, mimeType, receivedBytes, totalBytes, disposition, @@ -378,7 +378,7 @@ describe('session module', function () { downloadServer.listen(0, '127.0.0.1', function () { var port = downloadServer.address().port ipcRenderer.sendSync('set-download-option', true, false) - w.loadURL(url + ':' + port + '/?testFilename') + w.webContents.downloadURL(url + ':' + port + '/?testFilename') ipcRenderer.once('download-done', function (event, state, url, mimeType, receivedBytes, totalBytes, disposition, diff --git a/spec/chromium-spec.js b/spec/chromium-spec.js index bb33b272ece..d6dfe18509c 100644 --- a/spec/chromium-spec.js +++ b/spec/chromium-spec.js @@ -3,7 +3,7 @@ const http = require('http') const path = require('path') const ws = require('ws') const url = require('url') -const {ipcRenderer, remote} = require('electron') +const {ipcRenderer, remote, webFrame} = require('electron') const {closeWindow} = require('./window-helpers') const {app, BrowserWindow, ipcMain, protocol, session, webContents} = remote @@ -802,4 +802,64 @@ describe('chromium feature', function () { }) }) }) + + describe('PDF Viewer', function () { + let w = null + const pdfSource = url.format({ + pathname: path.join(fixtures, 'assets', 'cat.pdf').replace(/\\/g, '/'), + protocol: 'file', + slashes: true + }) + + beforeEach(function () { + w = new BrowserWindow({ + show: false, + webPreferences: { + preload: path.join(fixtures, 'module', 'preload-inject-ipc.js') + } + }) + }) + + afterEach(function () { + return closeWindow(w).then(function () { w = null }) + }) + + it('opens when loading a pdf resource as top level navigation', function (done) { + ipcMain.once('pdf-loaded', function (event, success) { + if (success) done() + }) + w.webContents.on('page-title-updated', function () { + const source = ` + if (window.viewer) { + window.viewer.setLoadCallback(function(success) { + window.ipcRenderer.send('pdf-loaded', success); + }); + } + ` + const parsedURL = url.parse(w.webContents.getURL(), true) + assert.equal(parsedURL.protocol, 'chrome:') + assert.equal(parsedURL.hostname, 'pdf-viewer') + assert.equal(parsedURL.query.src, pdfSource) + assert.equal(w.webContents.getTitle(), 'cat.pdf') + w.webContents.executeJavaScript(source) + }) + w.webContents.loadURL(pdfSource) + }) + + it('should not open when pdf is requested as sub resource', function (done) { + webFrame.registerURLSchemeAsPrivileged('file', { + secure: false, + bypassCSP: false, + allowServiceWorkers: false, + corsEnabled: false + }) + fetch(pdfSource).then(function (res) { + assert.equal(res.status, 200) + assert.notEqual(document.title, 'cat.pdf') + done() + }).catch(function (e) { + done(e) + }) + }) + }) }) diff --git a/spec/fixtures/assets/cat.pdf b/spec/fixtures/assets/cat.pdf new file mode 100644 index 00000000000..b635bd785d1 Binary files /dev/null and b/spec/fixtures/assets/cat.pdf differ diff --git a/spec/fixtures/module/preload-inject-ipc.js b/spec/fixtures/module/preload-inject-ipc.js new file mode 100644 index 00000000000..3475cd69a63 --- /dev/null +++ b/spec/fixtures/module/preload-inject-ipc.js @@ -0,0 +1,2 @@ +const {ipcRenderer} = require('electron') +window.ipcRenderer = ipcRenderer diff --git a/vendor/brightray b/vendor/brightray index eccdb0f1cb0..6ccb1aaf352 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit eccdb0f1cb0fe8feaeeaf1b1a1a1cc2c3f3c7126 +Subproject commit 6ccb1aaf35248f3fff4ec63e07b110675dce9de6 diff --git a/vendor/pdf_viewer b/vendor/pdf_viewer new file mode 160000 index 00000000000..a050a339cfe --- /dev/null +++ b/vendor/pdf_viewer @@ -0,0 +1 @@ +Subproject commit a050a339cfeabcfb5f07c313161d2ee27b6c3a39