Browse Source

Merge pull request #5904 from deepak1556/extension_partition_patch

browser: set up extension protocol handler for each browser context
Cheng Zhao 8 years ago
parent
commit
2ec5406ca6

+ 3 - 2
atom/browser/api/atom_api_app.cc

@@ -263,13 +263,14 @@ void App::OnContinueUserActivity(
 }
 #endif
 
-void App::OnLogin(LoginHandler* login_handler) {
+void App::OnLogin(LoginHandler* login_handler,
+                  const base::DictionaryValue& request_details) {
   v8::Locker locker(isolate());
   v8::HandleScope handle_scope(isolate());
   bool prevent_default = Emit(
       "login",
       WebContents::CreateFrom(isolate(), login_handler->GetWebContents()),
-      login_handler->request(),
+      request_details,
       login_handler->auth_info(),
       base::Bind(&PassLoginInformation, make_scoped_refptr(login_handler)));
 

+ 2 - 1
atom/browser/api/atom_api_app.h

@@ -70,7 +70,8 @@ class App : public AtomBrowserClient::Delegate,
   void OnActivate(bool has_visible_windows) override;
   void OnWillFinishLaunching() override;
   void OnFinishLaunching() override;
-  void OnLogin(LoginHandler* login_handler) override;
+  void OnLogin(LoginHandler* login_handler,
+               const base::DictionaryValue& request_details) override;
 #if defined(OS_MACOSX)
   void OnContinueUserActivity(
       bool* prevent_default,

+ 1 - 8
atom/browser/api/atom_api_protocol.cc

@@ -12,7 +12,7 @@
 #include "atom/browser/net/url_request_fetch_job.h"
 #include "atom/browser/net/url_request_string_job.h"
 #include "atom/common/native_mate_converters/callback.h"
-#include "atom/common/native_mate_converters/net_converter.h"
+#include "atom/common/native_mate_converters/value_converter.h"
 #include "atom/common/node_includes.h"
 #include "atom/common/options_switches.h"
 #include "base/command_line.h"
@@ -173,17 +173,10 @@ void RegisterStandardSchemes(
                                   base::JoinString(schemes, ","));
 }
 
-mate::Handle<atom::api::Protocol> CreateProtocol(v8::Isolate* isolate) {
-  auto browser_context = static_cast<atom::AtomBrowserContext*>(
-      atom::AtomBrowserMainParts::Get()->browser_context());
-  return atom::api::Protocol::Create(isolate, browser_context);
-}
-
 void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
                 v8::Local<v8::Context> context, void* priv) {
   v8::Isolate* isolate = context->GetIsolate();
   mate::Dictionary dict(isolate, exports);
-  dict.SetMethod("createProtocolObject", base::Bind(&CreateProtocol, isolate));
   dict.SetMethod("registerStandardSchemes", &RegisterStandardSchemes);
 }
 

+ 7 - 3
atom/browser/api/atom_api_protocol.h

@@ -9,6 +9,7 @@
 #include <map>
 #include <vector>
 
+#include "atom/browser/api/trackable_object.h"
 #include "atom/browser/net/atom_url_request_job_factory.h"
 #include "base/callback.h"
 #include "base/containers/scoped_ptr_hash_map.h"
@@ -16,7 +17,10 @@
 #include "native_mate/arguments.h"
 #include "native_mate/dictionary.h"
 #include "native_mate/handle.h"
-#include "native_mate/wrappable.h"
+
+namespace base {
+class DictionaryValue;
+}
 
 namespace net {
 class URLRequest;
@@ -30,10 +34,10 @@ class AtomURLRequestJobFactory;
 
 namespace api {
 
-class Protocol : public mate::Wrappable<Protocol> {
+class Protocol : public mate::TrackableObject<Protocol> {
  public:
   using Handler =
-      base::Callback<void(const net::URLRequest*, v8::Local<v8::Value>)>;
+      base::Callback<void(const base::DictionaryValue&, v8::Local<v8::Value>)>;
   using CompletionCallback = base::Callback<void(v8::Local<v8::Value>)>;
   using BooleanCallback = base::Callback<void(bool)>;
 

+ 10 - 0
atom/browser/api/atom_api_session.cc

@@ -9,6 +9,7 @@
 
 #include "atom/browser/api/atom_api_cookies.h"
 #include "atom/browser/api/atom_api_download_item.h"
+#include "atom/browser/api/atom_api_protocol.h"
 #include "atom/browser/api/atom_api_web_request.h"
 #include "atom/browser/atom_browser_context.h"
 #include "atom/browser/atom_browser_main_parts.h"
@@ -462,6 +463,14 @@ v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
   return v8::Local<v8::Value>::New(isolate, cookies_);
 }
 
+v8::Local<v8::Value> Session::Protocol(v8::Isolate* isolate) {
+  if (protocol_.IsEmpty()) {
+    auto handle = atom::api::Protocol::Create(isolate, browser_context());
+    protocol_.Reset(isolate, handle.ToV8());
+  }
+  return v8::Local<v8::Value>::New(isolate, protocol_);
+}
+
 v8::Local<v8::Value> Session::WebRequest(v8::Isolate* isolate) {
   if (web_request_.IsEmpty()) {
     auto handle = atom::api::WebRequest::Create(isolate, browser_context());
@@ -512,6 +521,7 @@ void Session::BuildPrototype(v8::Isolate* isolate,
       .SetMethod("allowNTLMCredentialsForDomains",
                  &Session::AllowNTLMCredentialsForDomains)
       .SetProperty("cookies", &Session::Cookies)
+      .SetProperty("protocol", &Session::Protocol)
       .SetProperty("webRequest", &Session::WebRequest);
 }
 

+ 2 - 0
atom/browser/api/atom_api_session.h

@@ -81,10 +81,12 @@ class Session: public mate::TrackableObject<Session>,
   void ClearHostResolverCache(mate::Arguments* args);
   void AllowNTLMCredentialsForDomains(const std::string& domains);
   v8::Local<v8::Value> Cookies(v8::Isolate* isolate);
+  v8::Local<v8::Value> Protocol(v8::Isolate* isolate);
   v8::Local<v8::Value> WebRequest(v8::Isolate* isolate);
 
   // Cached object.
   v8::Global<v8::Value> cookies_;
+  v8::Global<v8::Value> protocol_;
   v8::Global<v8::Value> web_request_;
 
   // The X-DevTools-Emulate-Network-Conditions-Client-Id.

+ 5 - 2
atom/browser/atom_resource_dispatcher_host_delegate.cc

@@ -31,10 +31,13 @@ void HandleExternalProtocolInUI(
   if (!web_contents)
     return;
 
-  GURL escaped_url(net::EscapeExternalHandlerValue(url.spec()));
-  auto callback = base::Bind(&OnOpenExternal, escaped_url);
   auto permission_helper =
       WebContentsPermissionHelper::FromWebContents(web_contents);
+  if (!permission_helper)
+    return;
+
+  GURL escaped_url(net::EscapeExternalHandlerValue(url.spec()));
+  auto callback = base::Bind(&OnOpenExternal, escaped_url);
   permission_helper->RequestOpenExternalPermission(callback, has_user_gesture);
 }
 

+ 6 - 2
atom/browser/browser.cc

@@ -151,8 +151,12 @@ void Browser::DidFinishLaunching() {
   FOR_EACH_OBSERVER(BrowserObserver, observers_, OnFinishLaunching());
 }
 
-void Browser::RequestLogin(LoginHandler* login_handler) {
-  FOR_EACH_OBSERVER(BrowserObserver, observers_, OnLogin(login_handler));
+void Browser::RequestLogin(
+    LoginHandler* login_handler,
+    std::unique_ptr<base::DictionaryValue> request_details) {
+  FOR_EACH_OBSERVER(BrowserObserver,
+                    observers_,
+                    OnLogin(login_handler, *(request_details.get())));
 }
 
 void Browser::NotifyAndShutdown() {

+ 3 - 1
atom/browser/browser.h

@@ -21,6 +21,7 @@
 #endif
 
 namespace base {
+class DictionaryValue;
 class FilePath;
 }
 
@@ -165,7 +166,8 @@ class Browser : public WindowListObserver {
   void DidFinishLaunching();
 
   // Request basic auth login.
-  void RequestLogin(LoginHandler* login_handler);
+  void RequestLogin(LoginHandler* login_handler,
+                    std::unique_ptr<base::DictionaryValue> request_details);
 
   void AddObserver(BrowserObserver* obs) {
     observers_.AddObserver(obs);

+ 2 - 1
atom/browser/browser_observer.h

@@ -49,7 +49,8 @@ class BrowserObserver {
   virtual void OnFinishLaunching() {}
 
   // The browser requests HTTP login.
-  virtual void OnLogin(LoginHandler* login_handler) {}
+  virtual void OnLogin(LoginHandler* login_handler,
+                       const base::DictionaryValue& request_details) {}
 
 #if defined(OS_MACOSX)
   // The browser wants to resume a user activity via handoff. (OS X only)

+ 10 - 1
atom/browser/login_handler.cc

@@ -5,6 +5,8 @@
 #include "atom/browser/login_handler.h"
 
 #include "atom/browser/browser.h"
+#include "atom/common/native_mate_converters/net_converter.h"
+#include "base/values.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/resource_dispatcher_host.h"
@@ -37,11 +39,18 @@ LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info,
       render_frame_id_(0) {
   content::ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderFrame(
       &render_process_host_id_,  &render_frame_id_);
+
+  // Fill request details on IO thread.
+  std::unique_ptr<base::DictionaryValue> request_details(
+      new base::DictionaryValue);
+  FillRequestDetails(request_details.get(), request_);
+
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
       base::Bind(&Browser::RequestLogin,
                  base::Unretained(Browser::Get()),
-                 base::RetainedRef(make_scoped_refptr(this))));
+                 base::RetainedRef(make_scoped_refptr(this)),
+                 base::Passed(&request_details)));
 }
 
 LoginHandler::~LoginHandler() {

+ 0 - 1
atom/browser/login_handler.h

@@ -36,7 +36,6 @@ class LoginHandler : public content::ResourceDispatcherHostLoginDelegate {
   void Login(const base::string16& username, const base::string16& password);
 
   const net::AuthChallengeInfo* auth_info() const { return auth_info_.get(); }
-  const net::URLRequest* request() const { return request_; }
 
  protected:
   ~LoginHandler() override;

+ 1 - 6
atom/browser/net/atom_network_delegate.cc

@@ -71,18 +71,13 @@ bool MatchesFilterCondition(net::URLRequest* request,
 
 // Overloaded by multiple types to fill the |details| object.
 void ToDictionary(base::DictionaryValue* details, net::URLRequest* request) {
+  FillRequestDetails(details, request);
   details->SetInteger("id", request->identifier());
-  details->SetString("url", request->url().spec());
-  details->SetString("method", request->method());
   details->SetDouble("timestamp", base::Time::Now().ToDoubleT() * 1000);
   auto info = content::ResourceRequestInfo::ForRequest(request);
   details->SetString("resourceType",
                      info ? ResourceTypeToString(info->GetResourceType())
                           : "other");
-  std::unique_ptr<base::ListValue> list(new base::ListValue);
-  GetUploadData(list.get(), request);
-  if (!list->empty())
-    details->Set("uploadData", std::move(list));
 }
 
 void ToDictionary(base::DictionaryValue* details,

+ 2 - 2
atom/browser/net/js_asker.cc

@@ -44,7 +44,7 @@ void HandlerCallback(const BeforeStartCallback& before_start,
 
 void AskForOptions(v8::Isolate* isolate,
                    const JavaScriptHandler& handler,
-                   net::URLRequest* request,
+                   std::unique_ptr<base::DictionaryValue> request_details,
                    const BeforeStartCallback& before_start,
                    const ResponseCallback& callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -53,7 +53,7 @@ void AskForOptions(v8::Isolate* isolate,
   v8::Local<v8::Context> context = isolate->GetCurrentContext();
   v8::Context::Scope context_scope(context);
   handler.Run(
-      request,
+      *(request_details.get()),
       mate::ConvertToV8(isolate,
                         base::Bind(&HandlerCallback, before_start, callback)));
 }

+ 7 - 3
atom/browser/net/js_asker.h

@@ -5,6 +5,7 @@
 #ifndef ATOM_BROWSER_NET_JS_ASKER_H_
 #define ATOM_BROWSER_NET_JS_ASKER_H_
 
+#include "atom/common/native_mate_converters/net_converter.h"
 #include "base/callback.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
@@ -19,7 +20,7 @@
 namespace atom {
 
 using JavaScriptHandler =
-    base::Callback<void(const net::URLRequest*, v8::Local<v8::Value>)>;
+    base::Callback<void(const base::DictionaryValue&, v8::Local<v8::Value>)>;
 
 namespace internal {
 
@@ -31,7 +32,7 @@ using ResponseCallback =
 // Ask handler for options in UI thread.
 void AskForOptions(v8::Isolate* isolate,
                    const JavaScriptHandler& handler,
-                   net::URLRequest* request,
+                   std::unique_ptr<base::DictionaryValue> request_details,
                    const BeforeStartCallback& before_start,
                    const ResponseCallback& callback);
 
@@ -67,12 +68,15 @@ class JsAsker : public RequestJob {
  private:
   // RequestJob:
   void Start() override {
+    std::unique_ptr<base::DictionaryValue> request_details(
+        new base::DictionaryValue);
+    FillRequestDetails(request_details.get(), RequestJob::request());
     content::BrowserThread::PostTask(
         content::BrowserThread::UI, FROM_HERE,
         base::Bind(&internal::AskForOptions,
                    isolate_,
                    handler_,
-                   RequestJob::request(),
+                   base::Passed(&request_details),
                    base::Bind(&JsAsker::BeforeStartInUI,
                               weak_factory_.GetWeakPtr()),
                    base::Bind(&JsAsker::OnResponse,

+ 13 - 16
atom/common/native_mate_converters/net_converter.cc

@@ -22,22 +22,6 @@
 
 namespace mate {
 
-// static
-v8::Local<v8::Value> Converter<const net::URLRequest*>::ToV8(
-    v8::Isolate* isolate, const net::URLRequest* val) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
-  dict->SetString("method", val->method());
-  std::string url;
-  if (!val->url_chain().empty()) url = val->url().spec();
-  dict->SetStringWithoutPathExpansion("url", url);
-  dict->SetString("referrer", val->referrer());
-  std::unique_ptr<base::ListValue> list(new base::ListValue);
-  atom::GetUploadData(list.get(), val);
-  if (!list->empty())
-    dict->Set("uploadData", std::move(list));
-  return mate::ConvertToV8(isolate, *(dict.get()));
-}
-
 // static
 v8::Local<v8::Value> Converter<const net::AuthChallengeInfo*>::ToV8(
     v8::Isolate* isolate, const net::AuthChallengeInfo* val) {
@@ -69,6 +53,19 @@ v8::Local<v8::Value> Converter<scoped_refptr<net::X509Certificate>>::ToV8(
 
 namespace atom {
 
+void FillRequestDetails(base::DictionaryValue* details,
+                        const net::URLRequest* request) {
+  details->SetString("method", request->method());
+  std::string url;
+  if (!request->url_chain().empty()) url = request->url().spec();
+  details->SetStringWithoutPathExpansion("url", url);
+  details->SetString("referrer", request->referrer());
+  std::unique_ptr<base::ListValue> list(new base::ListValue);
+  GetUploadData(list.get(), request);
+  if (!list->empty())
+    details->Set("uploadData", std::move(list));
+}
+
 void GetUploadData(base::ListValue* upload_data_list,
                    const net::URLRequest* request) {
   const net::UploadDataStream* upload_data = request->get_upload();

+ 4 - 6
atom/common/native_mate_converters/net_converter.h

@@ -9,6 +9,7 @@
 #include "native_mate/converter.h"
 
 namespace base {
+class DictionaryValue;
 class ListValue;
 }
 
@@ -20,12 +21,6 @@ class X509Certificate;
 
 namespace mate {
 
-template<>
-struct Converter<const net::URLRequest*> {
-  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                   const net::URLRequest* val);
-};
-
 template<>
 struct Converter<const net::AuthChallengeInfo*> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
@@ -42,6 +37,9 @@ struct Converter<scoped_refptr<net::X509Certificate>> {
 
 namespace atom {
 
+void FillRequestDetails(base::DictionaryValue* details,
+                        const net::URLRequest* request);
+
 void GetUploadData(base::ListValue* upload_data_list,
                    const net::URLRequest* request);
 

+ 19 - 0
docs/api/session.md

@@ -547,3 +547,22 @@ The `listener` will be called with `listener(details)` when an error occurs.
   * `timestamp` Double
   * `fromCache` Boolean
   * `error` String - The error description.
+
+#### `ses.protocol`
+
+Returns an instance of [protocol](protocol.md) module for this session.
+
+```javascript
+const {app, session} = require('electron')
+const path = require('path')
+
+app.on('ready', function () {
+  const protocol = session.fromPartition(partitionName).protocol
+  protocol.registerFileProtocol('atom', function (request, callback) {
+    var url = request.url.substr(7)
+    callback({path: path.normalize(__dirname + '/' + url)})
+  }, function (error) {
+    if (error)
+      console.error('Failed to register protocol')
+  })
+})

+ 3 - 3
lib/browser/api/protocol.js

@@ -1,5 +1,5 @@
-const {app} = require('electron')
-const {createProtocolObject, registerStandardSchemes} = process.atomBinding('protocol')
+const {app, session} = require('electron')
+const {registerStandardSchemes} = process.atomBinding('protocol')
 
 exports.registerStandardSchemes = function (schemes) {
   if (app.isReady()) {
@@ -10,7 +10,7 @@ exports.registerStandardSchemes = function (schemes) {
 }
 
 app.once('ready', function () {
-  let protocol = createProtocolObject()
+  let protocol = session.defaultSession.protocol
   for (let method in protocol) {
     exports[method] = protocol[method].bind(protocol)
   }

+ 6 - 2
lib/browser/api/session.js

@@ -3,6 +3,7 @@ const electron = require('electron')
 const bindings = process.atomBinding('session')
 
 const PERSIST_PREFIX = 'persist:'
+const Session = new EventEmitter()
 
 // Wrapper of binding.fromPartition that checks for ready event.
 const fromPartition = function (partition, persist) {
@@ -14,7 +15,7 @@ const fromPartition = function (partition, persist) {
 }
 
 // Returns the Session from |partition| string.
-exports.fromPartition = function (partition = '') {
+Session.fromPartition = function (partition = '') {
   if (partition === '') return exports.defaultSession
 
   if (partition.startsWith(PERSIST_PREFIX)) {
@@ -25,7 +26,7 @@ exports.fromPartition = function (partition = '') {
 }
 
 // Returns the default session.
-Object.defineProperty(exports, 'defaultSession', {
+Object.defineProperty(Session, 'defaultSession', {
   enumerable: true,
   get: function () {
     return fromPartition('', false)
@@ -35,6 +36,9 @@ Object.defineProperty(exports, 'defaultSession', {
 const wrapSession = function (session) {
   // Session is an EventEmitter.
   Object.setPrototypeOf(session, EventEmitter.prototype)
+  Session.emit('session-created', session)
 }
 
 bindings._setWrapSession(wrapSession)
+
+module.exports = Session

+ 7 - 5
lib/browser/chrome-extension.js

@@ -1,4 +1,4 @@
-const {app, ipcMain, protocol, webContents, BrowserWindow} = require('electron')
+const {app, ipcMain, session, webContents, BrowserWindow} = require('electron')
 const {getAllWebContents} = process.atomBinding('web_contents')
 const renderProcessPreferences = process.atomBinding('render_process_preferences').forAllBrowserWindow()
 
@@ -280,10 +280,12 @@ app.once('ready', function () {
       }
     })
   }
-  protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
-    if (error) {
-      console.error(`Unable to register chrome-extension protocol: ${error}`)
-    }
+  session.on('session-created', function (ses) {
+    ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
+      if (error) {
+        console.error(`Unable to register chrome-extension protocol: ${error}`)
+      }
+    })
   })
 
   // Load persisted extensions.

+ 39 - 0
spec/api-browser-window-spec.js

@@ -883,6 +883,45 @@ describe('browser-window module', function () {
       })
     })
 
+    it('works when used with partitions', function (done) {
+      this.timeout(10000)
+
+      if (w != null) {
+        w.destroy()
+      }
+      w = new BrowserWindow({
+        show: false,
+        webPreferences: {
+          partition: 'temp'
+        }
+      })
+
+      var extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo')
+      BrowserWindow.removeDevToolsExtension('foo')
+      BrowserWindow.addDevToolsExtension(extensionPath)
+
+      w.webContents.on('devtools-opened', function () {
+        var showPanelIntevalId = setInterval(function () {
+          if (w && w.devToolsWebContents) {
+            w.devToolsWebContents.executeJavaScript('(' + (function () {
+              var lastPanelId = WebInspector.inspectorView._tabbedPane._tabs.peekLast().id
+              WebInspector.inspectorView.showPanel(lastPanelId)
+            }).toString() + ')()')
+          } else {
+            clearInterval(showPanelIntevalId)
+          }
+        }, 100)
+      })
+
+      w.loadURL('about:blank')
+      w.webContents.openDevTools({mode: 'bottom'})
+
+      ipcMain.once('answer', function (event, message) {
+        assert.equal(message.runtimeId, 'foo')
+        done()
+      })
+    })
+
     it('serializes the registered extensions on quit', function () {
       var extensionName = 'foo'
       var extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', extensionName)

+ 50 - 1
spec/api-session-spec.js

@@ -16,8 +16,15 @@ describe('session module', function () {
   var fixtures = path.resolve(__dirname, 'fixtures')
   var w = null
   var url = 'http://127.0.0.1'
+  var partitionName = 'temp'
+  var protocolName = 'sp'
+  const tempProtocol = session.fromPartition(partitionName).protocol
+  const protocol = session.defaultSession.protocol
 
   beforeEach(function () {
+    if (w != null) {
+      w.destroy()
+    }
     w = new BrowserWindow({
       show: false,
       width: 400,
@@ -26,7 +33,10 @@ describe('session module', function () {
   })
 
   afterEach(function () {
-    w.destroy()
+    if (w != null) {
+      w.destroy()
+    }
+    w = null
   })
 
   describe('session.cookies', function () {
@@ -262,4 +272,43 @@ describe('session module', function () {
       })
     })
   })
+
+  describe('session.protocol', function () {
+    beforeEach(function () {
+      if (w != null) {
+        w.destroy()
+      }
+      w = new BrowserWindow({
+        show: false,
+        width: 400,
+        height: 400,
+        webPreferences: {
+          partition: partitionName
+        }
+      })
+    })
+
+    it('handles requests from a partition', function (done) {
+      var handler = function (error, callback) {
+        callback({
+          data: 'test'
+        })
+      }
+      tempProtocol.registerStringProtocol(protocolName, handler, function (error) {
+        if (error) {
+          return done(error)
+        }
+        protocol.isProtocolHandled(protocolName, function (result) {
+          assert.equal(result, false)
+          tempProtocol.isProtocolHandled(protocolName, function (result) {
+            assert.equal(result, true)
+            w.webContents.on('did-finish-load', function () {
+              done()
+            })
+            w.loadURL(protocolName + "://fake-host")
+          })
+        })
+      })
+    })
+  })
 })