Browse Source

Merge pull request #10793 from ahmedmohamedali/master

Add support for pdf in sub frames (https://github.com/electron/electr…
Cheng Zhao 7 years ago
parent
commit
cd5785c410

+ 38 - 24
atom/browser/atom_resource_dispatcher_host_delegate.cc

@@ -14,6 +14,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_manager.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/stream_info.h"
 #include "net/base/escape.h"
 #include "net/ssl/client_cert_store.h"
@@ -34,8 +35,7 @@ namespace atom {
 
 namespace {
 
-void OnOpenExternal(const GURL& escaped_url,
-                    bool allowed) {
+void OnOpenExternal(const GURL& escaped_url, bool allowed) {
   if (allowed)
     platform_util::OpenExternal(
 #if defined(OS_WIN)
@@ -66,6 +66,8 @@ void HandleExternalProtocolInUI(
 
 void OnPdfResourceIntercepted(
     const GURL& original_url,
+    int render_process_host_id,
+    int render_frame_id,
     const content::ResourceRequestInfo::WebContentsGetter&
         web_contents_getter) {
   content::WebContents* web_contents = web_contents_getter.Run();
@@ -75,7 +77,7 @@ void OnPdfResourceIntercepted(
   if (!WebContentsPreferences::IsPluginsEnabled(web_contents)) {
     auto browser_context = web_contents->GetBrowserContext();
     auto download_manager =
-      content::BrowserContext::GetDownloadManager(browser_context);
+        content::BrowserContext::GetDownloadManager(browser_context);
 
     download_manager->DownloadUrl(
         content::DownloadUrlParameters::CreateForWebContentsMainFrame(
@@ -86,26 +88,29 @@ void OnPdfResourceIntercepted(
   // 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,
-          net::EscapeUrlEncodedData(original_url.spec(), false).c_str())));
+  content::NavigationController::LoadURLParams params(GURL(base::StringPrintf(
+      "%sindex.html?%s=%s", kPdfViewerUIOrigin, kPdfPluginSrc,
+      net::EscapeUrlEncodedData(original_url.spec(), false).c_str())));
+
+  content::RenderFrameHost* frame_host =
+      content::RenderFrameHost::FromID(render_process_host_id, render_frame_id);
+  if (!frame_host) {
+    return;
+  }
+
+  params.frame_tree_node_id = frame_host->GetFrameTreeNodeId();
   web_contents->GetController().LoadURLWithParams(params);
 }
 
 }  // namespace
 
-AtomResourceDispatcherHostDelegate::AtomResourceDispatcherHostDelegate() {
-}
+AtomResourceDispatcherHostDelegate::AtomResourceDispatcherHostDelegate() {}
 
 bool AtomResourceDispatcherHostDelegate::HandleExternalProtocol(
     const GURL& url,
     content::ResourceRequestInfo* info) {
   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                          base::Bind(&HandleExternalProtocolInUI,
-                                     url,
+                          base::Bind(&HandleExternalProtocolInUI, url,
                                      info->GetWebContentsGetterForRequest(),
                                      info->HasUserGesture()));
   return true;
@@ -121,16 +126,16 @@ AtomResourceDispatcherHostDelegate::CreateLoginDelegate(
 std::unique_ptr<net::ClientCertStore>
 AtomResourceDispatcherHostDelegate::CreateClientCertStore(
     content::ResourceContext* resource_context) {
-  #if defined(USE_NSS_CERTS)
-    return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreNSS(
-        net::ClientCertStoreNSS::PasswordDelegateFactory()));
-  #elif defined(OS_WIN)
-    return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin());
-  #elif defined(OS_MACOSX)
-    return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac());
-  #elif defined(USE_OPENSSL)
-    return std::unique_ptr<net::ClientCertStore>();
-  #endif
+#if defined(USE_NSS_CERTS)
+  return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreNSS(
+      net::ClientCertStoreNSS::PasswordDelegateFactory()));
+#elif defined(OS_WIN)
+  return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin());
+#elif defined(OS_MACOSX)
+  return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac());
+#elif defined(USE_OPENSSL)
+  return std::unique_ptr<net::ClientCertStore>();
+#endif
 }
 
 bool AtomResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream(
@@ -141,11 +146,20 @@ bool AtomResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream(
     std::string* payload) {
   const content::ResourceRequestInfo* info =
       content::ResourceRequestInfo::ForRequest(request);
-  if (mime_type == "application/pdf" && info->IsMainFrame()) {
+
+  int render_process_host_id;
+  int render_frame_id;
+  if (!info->GetAssociatedRenderFrame(&render_process_host_id,
+                                      &render_frame_id)) {
+    return false;
+  }
+
+  if (mime_type == "application/pdf") {
     *origin = GURL(kPdfViewerUIOrigin);
     content::BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
         base::Bind(&OnPdfResourceIntercepted, request->url(),
+                   render_process_host_id, render_frame_id,
                    info->GetWebContentsGetterForRequest()));
     return true;
   }

+ 17 - 7
atom/browser/ui/webui/pdf_viewer_handler.cc

@@ -58,7 +58,9 @@ void PopulateStreamInfo(base::DictionaryValue* stream_info,
 PdfViewerHandler::PdfViewerHandler(const std::string& src)
     : stream_(nullptr), original_url_(src) {}
 
-PdfViewerHandler::~PdfViewerHandler() {}
+PdfViewerHandler::~PdfViewerHandler() {
+  RemoveObserver();
+}
 
 void PdfViewerHandler::SetPdfResourceStream(content::StreamInfo* stream) {
   stream_ = stream;
@@ -90,15 +92,11 @@ void PdfViewerHandler::RegisterMessages() {
 }
 
 void PdfViewerHandler::OnJavascriptAllowed() {
-  auto zoom_controller = WebContentsZoomController::FromWebContents(
-    web_ui()->GetWebContents());
-  zoom_controller->AddObserver(this);
+  AddObserver();
 }
 
 void PdfViewerHandler::OnJavascriptDisallowed() {
-  auto zoom_controller = WebContentsZoomController::FromWebContents(
-    web_ui()->GetWebContents());
-  zoom_controller->RemoveObserver(this);
+  RemoveObserver();
 }
 
 void PdfViewerHandler::Initialize(const base::ListValue* args) {
@@ -214,4 +212,16 @@ void PdfViewerHandler::OnZoomLevelChanged(content::WebContents* web_contents,
   }
 }
 
+void PdfViewerHandler::AddObserver() {
+  auto zoom_controller =
+      WebContentsZoomController::FromWebContents(web_ui()->GetWebContents());
+  zoom_controller->AddObserver(this);
+}
+
+void PdfViewerHandler::RemoveObserver() {
+  auto zoom_controller =
+      WebContentsZoomController::FromWebContents(web_ui()->GetWebContents());
+  zoom_controller->RemoveObserver(this);
+}
+
 }  // namespace atom

+ 2 - 1
atom/browser/ui/webui/pdf_viewer_handler.h

@@ -45,7 +45,8 @@ class PdfViewerHandler : public content::WebUIMessageHandler,
   void Reload(const base::ListValue* args);
   void OnZoomLevelChanged(content::WebContents* web_contents, double level,
       bool is_temporary);
-
+  void AddObserver();
+  void RemoveObserver();
   std::unique_ptr<base::Value> initialize_callback_id_;
   content::StreamInfo* stream_;
   std::string original_url_;

+ 36 - 6
spec/chromium-spec.js

@@ -955,18 +955,40 @@ describe('chromium feature', () => {
       slashes: true
     })
 
-    function createBrowserWindow ({plugins}) {
+    function createBrowserWindow ({plugins, preload}) {
       w = new BrowserWindow({
         show: false,
         webPreferences: {
-          preload: path.join(fixtures, 'module', 'preload-pdf-loaded.js'),
+          preload: path.join(fixtures, 'module', preload),
           plugins: plugins
         }
       })
     }
 
+    function testPDFIsLoadedInSubFrame (page, preloadFile, done) {
+      const pagePath = url.format({
+        pathname: path.join(fixtures, 'pages', page).replace(/\\/g, '/'),
+        protocol: 'file',
+        slashes: true
+      })
+
+      createBrowserWindow({plugins: true, preload: preloadFile})
+      ipcMain.once('pdf-loaded', (event, state) => {
+        assert.equal(state, 'success')
+        done()
+      })
+      w.webContents.on('page-title-updated', () => {
+        const parsedURL = url.parse(w.webContents.getURL(), true)
+        assert.equal(parsedURL.protocol, 'chrome:')
+        assert.equal(parsedURL.hostname, 'pdf-viewer')
+        assert.equal(parsedURL.query.src, pagePath)
+        assert.equal(w.webContents.getTitle(), 'cat.pdf')
+      })
+      w.webContents.loadURL(pagePath)
+    }
+
     it('opens when loading a pdf resource as top level navigation', (done) => {
-      createBrowserWindow({plugins: true})
+      createBrowserWindow({plugins: true, preload: 'preload-pdf-loaded.js'})
       ipcMain.once('pdf-loaded', (event, state) => {
         assert.equal(state, 'success')
         done()
@@ -982,7 +1004,7 @@ describe('chromium feature', () => {
     })
 
     it('opens a pdf link given params, the query string should be escaped', (done) => {
-      createBrowserWindow({plugins: true})
+      createBrowserWindow({plugins: true, preload: 'preload-pdf-loaded.js'})
       ipcMain.once('pdf-loaded', (event, state) => {
         assert.equal(state, 'success')
         done()
@@ -1000,7 +1022,7 @@ describe('chromium feature', () => {
     })
 
     it('should download a pdf when plugins are disabled', (done) => {
-      createBrowserWindow({plugins: false})
+      createBrowserWindow({plugins: false, preload: 'preload-pdf-loaded.js'})
       ipcRenderer.sendSync('set-download-option', false, false)
       ipcRenderer.once('download-done', (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) => {
         assert.equal(state, 'completed')
@@ -1013,7 +1035,7 @@ describe('chromium feature', () => {
     })
 
     it('should not open when pdf is requested as sub resource', (done) => {
-      createBrowserWindow({plugins: true})
+      createBrowserWindow({plugins: true, preload: 'preload-pdf-loaded.js'})
       webFrame.registerURLSchemeAsPrivileged('file', {
         secure: false,
         bypassCSP: false,
@@ -1026,6 +1048,14 @@ describe('chromium feature', () => {
         done()
       }).catch((e) => done(e))
     })
+
+    it('opens when loading a pdf resource in a iframe', (done) => {
+      testPDFIsLoadedInSubFrame('pdf-in-iframe.html', 'preload-pdf-loaded-in-subframe.js', done)
+    })
+
+    it('opens when loading a pdf resource in a nested iframe', (done) => {
+      testPDFIsLoadedInSubFrame('pdf-in-nested-iframe.html', 'preload-pdf-loaded-in-nested-subframe.js', done)
+    })
   })
 
   describe('window.alert(message, title)', () => {

+ 15 - 0
spec/fixtures/module/preload-pdf-loaded-in-nested-subframe.js

@@ -0,0 +1,15 @@
+const {ipcRenderer} = require('electron')
+
+document.addEventListener('DOMContentLoaded', (event) => {
+  var outerFrame = document.getElementById('outer-frame')
+  if (outerFrame) {
+    outerFrame.onload = function () {
+      var pdframe = outerFrame.contentWindow.document.getElementById('pdf-frame')
+      if (pdframe) {
+        pdframe.contentWindow.addEventListener('pdf-loaded', (event) => {
+          ipcRenderer.send('pdf-loaded', event.detail)
+        })
+      }
+    }
+  }
+})

+ 10 - 0
spec/fixtures/module/preload-pdf-loaded-in-subframe.js

@@ -0,0 +1,10 @@
+const {ipcRenderer} = require('electron')
+
+document.addEventListener('DOMContentLoaded', (event) => {
+  var subframe = document.getElementById('pdf-frame')
+  if (subframe) {
+    subframe.contentWindow.addEventListener('pdf-loaded', (event) => {
+      ipcRenderer.send('pdf-loaded', event.detail)
+    })
+  }
+})

+ 6 - 0
spec/fixtures/pages/pdf-in-iframe.html

@@ -0,0 +1,6 @@
+<html>
+<body>
+<iframe  id="pdf-frame" src="../assets/cat.pdf"/>
+</script>
+</body>
+</html>

+ 5 - 0
spec/fixtures/pages/pdf-in-nested-iframe.html

@@ -0,0 +1,5 @@
+<html>
+<body>
+<iframe id="outer-frame" src="./pdf-in-iframe.html"/>
+</body>
+</html>

+ 1 - 1
vendor/libchromiumcontent

@@ -1 +1 @@
-Subproject commit 0784acb6519ea45c0cef26d8599fa8c7f6b9c7ca
+Subproject commit b0c0a9e10bfac39d6da64a9e66e3509731d6fa69