pdf_viewer_ui.cc 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // Copyright (c) 2017 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "atom/browser/ui/webui/pdf_viewer_ui.h"
  5. #include <map>
  6. #include "atom/browser/atom_browser_context.h"
  7. #include "atom/browser/loader/layered_resource_handler.h"
  8. #include "atom/browser/ui/webui/pdf_viewer_handler.h"
  9. #include "atom/common/api/api_messages.h"
  10. #include "atom/common/atom_constants.h"
  11. #include "base/sequenced_task_runner_helpers.h"
  12. #include "content/browser/loader/resource_dispatcher_host_impl.h"
  13. #include "content/browser/loader/resource_request_info_impl.h"
  14. #include "content/browser/loader/stream_resource_handler.h"
  15. #include "content/browser/resource_context_impl.h"
  16. #include "content/browser/streams/stream.h"
  17. #include "content/browser/streams/stream_context.h"
  18. #include "content/public/browser/browser_thread.h"
  19. #include "content/public/browser/render_frame_host.h"
  20. #include "content/public/browser/render_process_host.h"
  21. #include "content/public/browser/render_view_host.h"
  22. #include "content/public/browser/resource_context.h"
  23. #include "content/public/browser/stream_handle.h"
  24. #include "content/public/browser/stream_info.h"
  25. #include "content/public/browser/url_data_source.h"
  26. #include "content/public/browser/web_contents.h"
  27. #include "grit/pdf_viewer_resources_map.h"
  28. #include "net/base/load_flags.h"
  29. #include "net/base/mime_util.h"
  30. #include "net/url_request/url_request.h"
  31. #include "net/url_request/url_request_context.h"
  32. #include "ui/base/resource/resource_bundle.h"
  33. using content::BrowserThread;
  34. namespace atom {
  35. namespace {
  36. // Extracts the path value from the URL without the leading '/',
  37. // which follows the mapping of names in pdf_viewer_resources_map.
  38. std::string PathWithoutParams(const std::string& path) {
  39. return GURL(kPdfViewerUIOrigin + path).path().substr(1);
  40. }
  41. class BundledDataSource : public content::URLDataSource {
  42. public:
  43. BundledDataSource() {
  44. for (size_t i = 0; i < kPdfViewerResourcesSize; ++i) {
  45. std::string resource_path = kPdfViewerResources[i].name;
  46. DCHECK(path_to_resource_id_.find(resource_path) ==
  47. path_to_resource_id_.end());
  48. path_to_resource_id_[resource_path] = kPdfViewerResources[i].value;
  49. }
  50. }
  51. // content::URLDataSource implementation.
  52. std::string GetSource() const override { return kPdfViewerUIHost; }
  53. void StartDataRequest(
  54. const std::string& path,
  55. const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
  56. const GotDataCallback& callback) override {
  57. std::string filename = PathWithoutParams(path);
  58. auto entry = path_to_resource_id_.find(filename);
  59. if (entry != path_to_resource_id_.end()) {
  60. int resource_id = entry->second;
  61. const ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
  62. callback.Run(rb.LoadDataResourceBytes(resource_id));
  63. } else {
  64. LOG(ERROR) << "Unable to find: " << path;
  65. callback.Run(new base::RefCountedString());
  66. }
  67. }
  68. std::string GetMimeType(const std::string& path) const override {
  69. base::FilePath::StringType ext =
  70. base::FilePath::FromUTF8Unsafe(PathWithoutParams(path)).Extension();
  71. std::string mime_type;
  72. if (!ext.empty() &&
  73. net::GetWellKnownMimeTypeFromExtension(ext.substr(1), &mime_type))
  74. return mime_type;
  75. return "text/html";
  76. }
  77. bool ShouldAddContentSecurityPolicy() const override { return false; }
  78. bool ShouldDenyXFrameOptions() const override { return false; }
  79. bool ShouldServeMimeTypeAsContentTypeHeader() const override { return true; }
  80. private:
  81. ~BundledDataSource() override {}
  82. // A map from a resource path to the resource ID.
  83. std::map<std::string, int> path_to_resource_id_;
  84. DISALLOW_COPY_AND_ASSIGN(BundledDataSource);
  85. };
  86. // Helper to convert from OnceCallback to Callback.
  87. template <typename T>
  88. void CallMigrationCallback(T callback,
  89. std::unique_ptr<content::StreamInfo> stream_info) {
  90. std::move(callback).Run(std::move(stream_info));
  91. }
  92. } // namespace
  93. class PdfViewerUI::ResourceRequester
  94. : public base::RefCountedThreadSafe<ResourceRequester,
  95. BrowserThread::DeleteOnIOThread>,
  96. public atom::LayeredResourceHandler::Delegate {
  97. public:
  98. explicit ResourceRequester(StreamResponseCallback cb)
  99. : stream_response_cb_(std::move(cb)) {}
  100. void StartRequest(const GURL& url,
  101. const GURL& origin,
  102. int render_process_id,
  103. int render_view_id,
  104. int render_frame_id,
  105. content::ResourceContext* resource_context) {
  106. DCHECK_CURRENTLY_ON(BrowserThread::IO);
  107. const net::URLRequestContext* request_context =
  108. resource_context->GetRequestContext();
  109. std::unique_ptr<net::URLRequest> request(
  110. request_context->CreateRequest(url, net::DEFAULT_PRIORITY, nullptr));
  111. request->set_method("GET");
  112. content::ResourceDispatcherHostImpl::Get()->InitializeURLRequest(
  113. request.get(), content::Referrer(url, blink::kWebReferrerPolicyDefault),
  114. false, // download.
  115. render_process_id, render_view_id, render_frame_id,
  116. content::PREVIEWS_OFF, resource_context);
  117. content::ResourceRequestInfoImpl* info =
  118. content::ResourceRequestInfoImpl::ForRequest(request.get());
  119. content::StreamContext* stream_context =
  120. content::GetStreamContextForResourceContext(resource_context);
  121. std::unique_ptr<content::ResourceHandler> handler =
  122. std::make_unique<content::StreamResourceHandler>(
  123. request.get(), stream_context->registry(), origin, false);
  124. info->set_is_stream(true);
  125. stream_info_.reset(new content::StreamInfo);
  126. stream_info_->handle =
  127. static_cast<content::StreamResourceHandler*>(handler.get())
  128. ->stream()
  129. ->CreateHandle();
  130. stream_info_->original_url = request->url();
  131. // Helper to fill stream response details.
  132. handler.reset(new atom::LayeredResourceHandler(request.get(),
  133. std::move(handler), this));
  134. content::ResourceDispatcherHostImpl::Get()->BeginURLRequest(
  135. std::move(request), std::move(handler),
  136. false, // download
  137. false, // content_initiated (download specific)
  138. false, // do_not_prompt_for_login (download specific)
  139. resource_context);
  140. }
  141. protected:
  142. // atom::LayeredResourceHandler::Delegate:
  143. void OnResponseStarted(content::ResourceResponse* response) override {
  144. DCHECK_CURRENTLY_ON(BrowserThread::IO);
  145. auto resource_response_head = response->head;
  146. auto headers = resource_response_head.headers;
  147. auto mime_type = resource_response_head.mime_type;
  148. if (headers.get())
  149. stream_info_->response_headers =
  150. new net::HttpResponseHeaders(headers->raw_headers());
  151. stream_info_->mime_type = mime_type;
  152. BrowserThread::PostTask(
  153. BrowserThread::UI, FROM_HERE,
  154. base::Bind(&CallMigrationCallback<StreamResponseCallback>,
  155. base::Passed(&stream_response_cb_),
  156. base::Passed(&stream_info_)));
  157. }
  158. private:
  159. friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
  160. friend class base::DeleteHelper<ResourceRequester>;
  161. ~ResourceRequester() override {}
  162. StreamResponseCallback stream_response_cb_;
  163. std::unique_ptr<content::StreamInfo> stream_info_;
  164. DISALLOW_COPY_AND_ASSIGN(ResourceRequester);
  165. };
  166. PdfViewerUI::PdfViewerUI(content::BrowserContext* browser_context,
  167. content::WebUI* web_ui,
  168. const std::string& src)
  169. : content::WebUIController(web_ui),
  170. content::WebContentsObserver(web_ui->GetWebContents()),
  171. src_(src) {
  172. pdf_handler_ = new PdfViewerHandler(src);
  173. web_ui->AddMessageHandler(
  174. std::unique_ptr<content::WebUIMessageHandler>(pdf_handler_));
  175. content::URLDataSource::Add(browser_context, new BundledDataSource);
  176. }
  177. PdfViewerUI::~PdfViewerUI() {}
  178. bool PdfViewerUI::OnMessageReceived(
  179. const IPC::Message& message,
  180. content::RenderFrameHost* render_frame_host) {
  181. bool handled = true;
  182. IPC_BEGIN_MESSAGE_MAP(PdfViewerUI, message)
  183. IPC_MESSAGE_HANDLER(AtomFrameHostMsg_PDFSaveURLAs, OnSaveURLAs)
  184. IPC_MESSAGE_UNHANDLED(handled = false)
  185. IPC_END_MESSAGE_MAP()
  186. return handled;
  187. }
  188. void PdfViewerUI::OnPdfStreamCreated(
  189. std::unique_ptr<content::StreamInfo> stream) {
  190. stream_ = std::move(stream);
  191. if (pdf_handler_)
  192. pdf_handler_->SetPdfResourceStream(stream_.get());
  193. resource_requester_ = nullptr;
  194. }
  195. void PdfViewerUI::RenderFrameCreated(content::RenderFrameHost* rfh) {
  196. int render_process_id = rfh->GetProcess()->GetID();
  197. int render_frame_id = rfh->GetRoutingID();
  198. int render_view_id = rfh->GetRenderViewHost()->GetRoutingID();
  199. auto resource_context =
  200. web_contents()->GetBrowserContext()->GetResourceContext();
  201. auto callback =
  202. base::BindOnce(&PdfViewerUI::OnPdfStreamCreated, base::Unretained(this));
  203. resource_requester_ = new ResourceRequester(std::move(callback));
  204. BrowserThread::PostTask(
  205. BrowserThread::IO, FROM_HERE,
  206. base::Bind(&ResourceRequester::StartRequest, resource_requester_,
  207. GURL(src_), GURL(kPdfViewerUIOrigin), render_process_id,
  208. render_view_id, render_frame_id, resource_context));
  209. }
  210. void PdfViewerUI::OnSaveURLAs(const GURL& url,
  211. const content::Referrer& referrer) {
  212. web_contents()->SaveFrame(url, referrer);
  213. }
  214. } // namespace atom