protocol: cleanup

This commit is contained in:
Robo 2015-07-05 23:23:07 +05:30
parent d661099322
commit da00329d78
6 changed files with 138 additions and 74 deletions

View file

@ -25,12 +25,16 @@ namespace mate {
template<> template<>
struct Converter<const net::URLRequest*> { struct Converter<const net::URLRequest*> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
const net::URLRequest* val) { const net::URLRequest* val) {
return mate::ObjectTemplateBuilder(isolate) if (val) {
.SetValue("method", val->method()) return mate::ObjectTemplateBuilder(isolate)
.SetValue("url", val->url().spec()) .SetValue("method", val->method())
.SetValue("referrer", val->referrer()) .SetValue("url", val->url().spec())
.Build()->NewInstance(); .SetValue("referrer", val->referrer())
.Build()->NewInstance();
} else {
return v8::Null(isolate);
}
} }
}; };
@ -65,7 +69,7 @@ class CustomProtocolRequestJob : public AdapterRequestJob {
// AdapterRequestJob: // AdapterRequestJob:
void GetJobTypeInUI() override { void GetJobTypeInUI() override {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK_CURRENTLY_ON(BrowserThread::UI);
v8::Locker locker(registry_->isolate()); v8::Locker locker(registry_->isolate());
v8::HandleScope handle_scope(registry_->isolate()); v8::HandleScope handle_scope(registry_->isolate());
@ -73,7 +77,8 @@ class CustomProtocolRequestJob : public AdapterRequestJob {
// Call the JS handler. // Call the JS handler.
Protocol::JsProtocolHandler callback = Protocol::JsProtocolHandler callback =
registry_->GetProtocolHandler(request()->url().scheme()); registry_->GetProtocolHandler(request()->url().scheme());
v8::Local<v8::Value> result = callback.Run(request()); v8::Local<v8::Value> result =
callback.Run(v8::Null(registry_->isolate()), request());
// Determine the type of the job we are going to create. // Determine the type of the job we are going to create.
if (result->IsString()) { if (result->IsString()) {
@ -202,6 +207,51 @@ Protocol::JsProtocolHandler Protocol::GetProtocolHandler(
return protocol_handlers_[scheme]; return protocol_handlers_[scheme];
} }
void Protocol::OnRegisterProtocol(const std::string& scheme,
const JsProtocolHandler& callback,
bool is_handled) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
if (is_handled || ContainsKey(protocol_handlers_, scheme)) {
callback.Run(v8::Exception::Error(
mate::StringToV8(isolate(), "The Scheme is already registered")),
nullptr);
return;
}
protocol_handlers_[scheme] = callback;
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&Protocol::RegisterProtocolInIO,
base::Unretained(this), scheme));
}
void Protocol::OnInterceptProtocol(const std::string& scheme,
const JsProtocolHandler& callback,
bool is_handled) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
if (!is_handled) {
callback.Run(v8::Exception::Error(
mate::StringToV8(isolate(), "Scheme does not exist.")), nullptr);
return;
}
if (ContainsKey(protocol_handlers_, scheme)) {
callback.Run(v8::Exception::Error(
mate::StringToV8(isolate(), "Cannot intercept custom protocols.")),
nullptr);
return;
}
protocol_handlers_[scheme] = callback;
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&Protocol::InterceptProtocolInIO,
base::Unretained(this), scheme));
}
mate::ObjectTemplateBuilder Protocol::GetObjectTemplateBuilder( mate::ObjectTemplateBuilder Protocol::GetObjectTemplateBuilder(
v8::Isolate* isolate) { v8::Isolate* isolate) {
return mate::ObjectTemplateBuilder(isolate) return mate::ObjectTemplateBuilder(isolate)
@ -216,15 +266,10 @@ mate::ObjectTemplateBuilder Protocol::GetObjectTemplateBuilder(
void Protocol::RegisterProtocol(v8::Isolate* isolate, void Protocol::RegisterProtocol(v8::Isolate* isolate,
const std::string& scheme, const std::string& scheme,
const JsProtocolHandler& callback) { const JsProtocolHandler& callback) {
if (ContainsKey(protocol_handlers_, scheme) || IsHandledProtocol(scheme,
job_factory_->IsHandledProtocol(scheme)) base::Bind(&Protocol::OnRegisterProtocol,
return node::ThrowError(isolate, "The scheme is already registered"); base::Unretained(this),
scheme, callback));
protocol_handlers_[scheme] = callback;
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&Protocol::RegisterProtocolInIO,
base::Unretained(this), scheme));
} }
void Protocol::UnregisterProtocol(v8::Isolate* isolate, void Protocol::UnregisterProtocol(v8::Isolate* isolate,
@ -245,24 +290,22 @@ void Protocol::RegisterStandardSchemes(
atom::AtomBrowserClient::SetCustomSchemes(schemes); atom::AtomBrowserClient::SetCustomSchemes(schemes);
} }
bool Protocol::IsHandledProtocol(const std::string& scheme) { void Protocol::IsHandledProtocol(const std::string& scheme,
return job_factory_->IsHandledProtocol(scheme); const CompletionCallback& callback) {
BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequestJobFactory::IsHandledProtocol,
base::Unretained(job_factory_), scheme),
callback);
} }
void Protocol::InterceptProtocol(v8::Isolate* isolate, void Protocol::InterceptProtocol(v8::Isolate* isolate,
const std::string& scheme, const std::string& scheme,
const JsProtocolHandler& callback) { const JsProtocolHandler& callback) {
if (!job_factory_->HasProtocolHandler(scheme)) BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE,
return node::ThrowError(isolate, "Scheme does not exist."); base::Bind(&AtomURLRequestJobFactory::HasProtocolHandler,
base::Unretained(job_factory_), scheme),
if (ContainsKey(protocol_handlers_, scheme)) base::Bind(&Protocol::OnInterceptProtocol,
return node::ThrowError(isolate, "Cannot intercept custom procotols"); base::Unretained(this), scheme, callback));
protocol_handlers_[scheme] = callback;
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&Protocol::InterceptProtocolInIO,
base::Unretained(this), scheme));
} }
void Protocol::UninterceptProtocol(v8::Isolate* isolate, void Protocol::UninterceptProtocol(v8::Isolate* isolate,
@ -279,7 +322,7 @@ void Protocol::UninterceptProtocol(v8::Isolate* isolate,
} }
void Protocol::RegisterProtocolInIO(const std::string& scheme) { void Protocol::RegisterProtocolInIO(const std::string& scheme) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK_CURRENTLY_ON(BrowserThread::IO);
job_factory_->SetProtocolHandler(scheme, new CustomProtocolHandler(this)); job_factory_->SetProtocolHandler(scheme, new CustomProtocolHandler(this));
BrowserThread::PostTask(BrowserThread::UI, BrowserThread::PostTask(BrowserThread::UI,
@ -290,7 +333,7 @@ void Protocol::RegisterProtocolInIO(const std::string& scheme) {
} }
void Protocol::UnregisterProtocolInIO(const std::string& scheme) { void Protocol::UnregisterProtocolInIO(const std::string& scheme) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK_CURRENTLY_ON(BrowserThread::IO);
job_factory_->SetProtocolHandler(scheme, NULL); job_factory_->SetProtocolHandler(scheme, NULL);
BrowserThread::PostTask(BrowserThread::UI, BrowserThread::PostTask(BrowserThread::UI,
@ -301,7 +344,7 @@ void Protocol::UnregisterProtocolInIO(const std::string& scheme) {
} }
void Protocol::InterceptProtocolInIO(const std::string& scheme) { void Protocol::InterceptProtocolInIO(const std::string& scheme) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK_CURRENTLY_ON(BrowserThread::IO);
ProtocolHandler* original_handler = job_factory_->GetProtocolHandler(scheme); ProtocolHandler* original_handler = job_factory_->GetProtocolHandler(scheme);
if (original_handler == NULL) { if (original_handler == NULL) {
@ -322,7 +365,7 @@ void Protocol::InterceptProtocolInIO(const std::string& scheme) {
} }
void Protocol::UninterceptProtocolInIO(const std::string& scheme) { void Protocol::UninterceptProtocolInIO(const std::string& scheme) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK_CURRENTLY_ON(BrowserThread::IO);
CustomProtocolHandler* handler = static_cast<CustomProtocolHandler*>( CustomProtocolHandler* handler = static_cast<CustomProtocolHandler*>(
job_factory_->GetProtocolHandler(scheme)); job_factory_->GetProtocolHandler(scheme));

View file

@ -26,8 +26,10 @@ namespace api {
class Protocol : public mate::EventEmitter { class Protocol : public mate::EventEmitter {
public: public:
typedef base::Callback<v8::Local<v8::Value>(const net::URLRequest*)> using JsProtocolHandler =
JsProtocolHandler; base::Callback<v8::Local<v8::Value>(v8::Local<v8::Value>,
const net::URLRequest*)>;
using CompletionCallback = base::Callback<void(bool)>;
static mate::Handle<Protocol> Create( static mate::Handle<Protocol> Create(
v8::Isolate* isolate, AtomBrowserContext* browser_context); v8::Isolate* isolate, AtomBrowserContext* browser_context);
@ -46,6 +48,15 @@ class Protocol : public mate::EventEmitter {
private: private:
typedef std::map<std::string, JsProtocolHandler> ProtocolHandlersMap; typedef std::map<std::string, JsProtocolHandler> ProtocolHandlersMap;
// Callback called if protocol can be registered.
void OnRegisterProtocol(const std::string& scheme,
const JsProtocolHandler& callback,
bool is_handled);
// Callback called if protocol can be intercepted.
void OnInterceptProtocol(const std::string& scheme,
const JsProtocolHandler& callback,
bool is_handled);
// Register schemes to standard scheme list. // Register schemes to standard scheme list.
void RegisterStandardSchemes(const std::vector<std::string>& schemes); void RegisterStandardSchemes(const std::vector<std::string>& schemes);
@ -57,9 +68,8 @@ class Protocol : public mate::EventEmitter {
void UnregisterProtocol(v8::Isolate* isolate, const std::string& scheme); void UnregisterProtocol(v8::Isolate* isolate, const std::string& scheme);
// Returns whether a scheme has been registered. // Returns whether a scheme has been registered.
// FIXME Should accept a callback and be asynchronous so we do not have to use void IsHandledProtocol(const std::string& scheme,
// locks. const CompletionCallback& callback);
bool IsHandledProtocol(const std::string& scheme);
// Intercept/unintercept an existing protocol handler. // Intercept/unintercept an existing protocol handler.
void InterceptProtocol(v8::Isolate* isolate, void InterceptProtocol(v8::Isolate* isolate,

View file

@ -6,9 +6,12 @@
#include "atom/browser/net/atom_url_request_job_factory.h" #include "atom/browser/net/atom_url_request_job_factory.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/load_flags.h" #include "net/base/load_flags.h"
#include "net/url_request/url_request.h" #include "net/url_request/url_request.h"
using content::BrowserThread;
namespace atom { namespace atom {
typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler; typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;
@ -22,9 +25,7 @@ AtomURLRequestJobFactory::~AtomURLRequestJobFactory() {
bool AtomURLRequestJobFactory::SetProtocolHandler( bool AtomURLRequestJobFactory::SetProtocolHandler(
const std::string& scheme, const std::string& scheme,
ProtocolHandler* protocol_handler) { ProtocolHandler* protocol_handler) {
DCHECK(CalledOnValidThread()); DCHECK_CURRENTLY_ON(BrowserThread::IO);
base::AutoLock locked(lock_);
if (!protocol_handler) { if (!protocol_handler) {
ProtocolHandlerMap::iterator it = protocol_handler_map_.find(scheme); ProtocolHandlerMap::iterator it = protocol_handler_map_.find(scheme);
@ -45,10 +46,9 @@ bool AtomURLRequestJobFactory::SetProtocolHandler(
ProtocolHandler* AtomURLRequestJobFactory::ReplaceProtocol( ProtocolHandler* AtomURLRequestJobFactory::ReplaceProtocol(
const std::string& scheme, const std::string& scheme,
ProtocolHandler* protocol_handler) { ProtocolHandler* protocol_handler) {
DCHECK(CalledOnValidThread()); DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(protocol_handler); DCHECK(protocol_handler);
base::AutoLock locked(lock_);
if (!ContainsKey(protocol_handler_map_, scheme)) if (!ContainsKey(protocol_handler_map_, scheme))
return nullptr; return nullptr;
ProtocolHandler* original_protocol_handler = protocol_handler_map_[scheme]; ProtocolHandler* original_protocol_handler = protocol_handler_map_[scheme];
@ -58,9 +58,8 @@ ProtocolHandler* AtomURLRequestJobFactory::ReplaceProtocol(
ProtocolHandler* AtomURLRequestJobFactory::GetProtocolHandler( ProtocolHandler* AtomURLRequestJobFactory::GetProtocolHandler(
const std::string& scheme) const { const std::string& scheme) const {
DCHECK(CalledOnValidThread()); DCHECK_CURRENTLY_ON(BrowserThread::IO);
base::AutoLock locked(lock_);
ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme); ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme);
if (it == protocol_handler_map_.end()) if (it == protocol_handler_map_.end())
return nullptr; return nullptr;
@ -69,7 +68,6 @@ ProtocolHandler* AtomURLRequestJobFactory::GetProtocolHandler(
bool AtomURLRequestJobFactory::HasProtocolHandler( bool AtomURLRequestJobFactory::HasProtocolHandler(
const std::string& scheme) const { const std::string& scheme) const {
base::AutoLock locked(lock_);
return ContainsKey(protocol_handler_map_, scheme); return ContainsKey(protocol_handler_map_, scheme);
} }
@ -77,9 +75,8 @@ net::URLRequestJob* AtomURLRequestJobFactory::MaybeCreateJobWithProtocolHandler(
const std::string& scheme, const std::string& scheme,
net::URLRequest* request, net::URLRequest* request,
net::NetworkDelegate* network_delegate) const { net::NetworkDelegate* network_delegate) const {
DCHECK(CalledOnValidThread()); DCHECK_CURRENTLY_ON(BrowserThread::IO);
base::AutoLock locked(lock_);
ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme); ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme);
if (it == protocol_handler_map_.end()) if (it == protocol_handler_map_.end())
return nullptr; return nullptr;
@ -101,7 +98,8 @@ net::URLRequestJob* AtomURLRequestJobFactory::MaybeInterceptResponse(
bool AtomURLRequestJobFactory::IsHandledProtocol( bool AtomURLRequestJobFactory::IsHandledProtocol(
const std::string& scheme) const { const std::string& scheme) const {
DCHECK(CalledOnValidThread()); DCHECK_CURRENTLY_ON(BrowserThread::IO);
return HasProtocolHandler(scheme) || return HasProtocolHandler(scheme) ||
net::URLRequest::IsHandledProtocol(scheme); net::URLRequest::IsHandledProtocol(scheme);
} }

View file

@ -56,12 +56,10 @@ class AtomURLRequestJobFactory : public net::URLRequestJobFactory {
bool IsSafeRedirectTarget(const GURL& location) const override; bool IsSafeRedirectTarget(const GURL& location) const override;
private: private:
typedef std::map<std::string, ProtocolHandler*> ProtocolHandlerMap; using ProtocolHandlerMap = std::map<std::string, ProtocolHandler*>;
ProtocolHandlerMap protocol_handler_map_; ProtocolHandlerMap protocol_handler_map_;
mutable base::Lock lock_;
DISALLOW_COPY_AND_ASSIGN(AtomURLRequestJobFactory); DISALLOW_COPY_AND_ASSIGN(AtomURLRequestJobFactory);
}; };

View file

@ -28,7 +28,7 @@ was emitted.
* `handler` Function * `handler` Function
Registers a custom protocol of `scheme`, the `handler` would be called with Registers a custom protocol of `scheme`, the `handler` would be called with
`handler(request)` when the a request with registered `scheme` is made. `handler(error, request)` when the a request with registered `scheme` is made.
You need to return a request job in the `handler` to specify which type of You need to return a request job in the `handler` to specify which type of
response you would like to send. response you would like to send.
@ -45,11 +45,12 @@ Unregisters the custom protocol of `scheme`.
`value` is an array of custom schemes to be registered to the standard. `value` is an array of custom schemes to be registered to the standard.
## protocol.isHandledProtocol(scheme) ## protocol.isHandledProtocol(scheme, callback)
* `scheme` String * `scheme` String
* `callback` Function
Returns whether the `scheme` can be handled already. `callback` returns a boolean whether the `scheme` can be handled already.
## protocol.interceptProtocol(scheme, handler) ## protocol.interceptProtocol(scheme, handler)

View file

@ -7,17 +7,19 @@ protocol = remote.require 'protocol'
describe 'protocol module', -> describe 'protocol module', ->
describe 'protocol.registerProtocol', -> describe 'protocol.registerProtocol', ->
it 'throws error when scheme is already registered', (done) -> it 'error when scheme is already registered', (done) ->
register = -> protocol.registerProtocol('test1', ->) register = ->
protocol.registerProtocol 'test1', (error, request) ->
assert error instanceof Error
protocol.unregisterProtocol 'test1'
done()
protocol.once 'registered', (event, scheme) -> protocol.once 'registered', (event, scheme) ->
assert.equal scheme, 'test1' assert.equal scheme, 'test1'
assert.throws register, /The scheme is already registered/ register()
protocol.unregisterProtocol 'test1'
done()
register() register()
it 'calls the callback when scheme is visited', (done) -> it 'calls the callback when scheme is visited', (done) ->
protocol.registerProtocol 'test2', (request) -> protocol.registerProtocol 'test2', (error, request) ->
assert.equal request.url, 'test2://test2' assert.equal request.url, 'test2://test2'
protocol.unregisterProtocol 'test2' protocol.unregisterProtocol 'test2'
done() done()
@ -169,16 +171,28 @@ describe 'protocol module', ->
protocol.unregisterProtocol 'atom-file-job' protocol.unregisterProtocol 'atom-file-job'
describe 'protocol.isHandledProtocol', -> describe 'protocol.isHandledProtocol', ->
it 'returns true if the scheme can be handled', -> it 'returns true if the file scheme can be handled', (done) ->
assert.equal protocol.isHandledProtocol('file'), true protocol.isHandledProtocol 'file', (result) ->
assert.equal protocol.isHandledProtocol('http'), true assert.equal result, true
assert.equal protocol.isHandledProtocol('https'), true done()
assert.equal protocol.isHandledProtocol('atom'), false it 'returns true if the http scheme can be handled', (done) ->
protocol.isHandledProtocol 'http', (result) ->
assert.equal result, true
done()
it 'returns true if the https scheme can be handled', (done) ->
protocol.isHandledProtocol 'https', (result) ->
assert.equal result, true
done()
it 'returns false if the atom scheme cannot be handled', (done) ->
protocol.isHandledProtocol 'atom', (result) ->
assert.equal result, false
done()
describe 'protocol.interceptProtocol', -> describe 'protocol.interceptProtocol', ->
it 'throws error when scheme is not a registered one', -> it 'throws error when scheme is not a registered one', (done) ->
register = -> protocol.interceptProtocol('test-intercept', ->) protocol.interceptProtocol 'test-intercept', (error, request) ->
assert.throws register, /Scheme does not exist/ assert error instanceof Error
done()
it 'throws error when scheme is a custom protocol', (done) -> it 'throws error when scheme is a custom protocol', (done) ->
protocol.once 'unregistered', (event, scheme) -> protocol.once 'unregistered', (event, scheme) ->
@ -186,9 +200,9 @@ describe 'protocol module', ->
done() done()
protocol.once 'registered', (event, scheme) -> protocol.once 'registered', (event, scheme) ->
assert.equal scheme, 'atom' assert.equal scheme, 'atom'
register = -> protocol.interceptProtocol('test-intercept', ->) protocol.interceptProtocol 'test-intercept', (error, request) ->
assert.throws register, /Scheme does not exist/ assert error instanceof Error
protocol.unregisterProtocol scheme protocol.unregisterProtocol scheme
protocol.registerProtocol('atom', ->) protocol.registerProtocol('atom', ->)
it 'returns original job when callback returns nothing', (done) -> it 'returns original job when callback returns nothing', (done) ->
@ -206,7 +220,7 @@ describe 'protocol module', ->
error: (xhr, errorType, error) -> error: (xhr, errorType, error) ->
free() free()
assert false, 'Got error: ' + errorType + ' ' + error assert false, 'Got error: ' + errorType + ' ' + error
protocol.interceptProtocol targetScheme, (request) -> protocol.interceptProtocol targetScheme, (error, request) ->
if process.platform is 'win32' if process.platform is 'win32'
pathInUrl = path.normalize request.url.substr(8) pathInUrl = path.normalize request.url.substr(8)
assert.equal pathInUrl.toLowerCase(), __filename.toLowerCase() assert.equal pathInUrl.toLowerCase(), __filename.toLowerCase()