pdf_viewer_ui.cc 9.5 KB

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