Merge pull request #65 from atom/custom-protocol
Support custom protocols
This commit is contained in:
commit
3b7dd85d3f
25 changed files with 1184 additions and 24 deletions
9
atom.gyp
9
atom.gyp
|
@ -19,6 +19,7 @@
|
|||
'browser/api/lib/menu.coffee',
|
||||
'browser/api/lib/menu-item.coffee',
|
||||
'browser/api/lib/power-monitor.coffee',
|
||||
'browser/api/lib/protocol.coffee',
|
||||
'browser/atom/atom.coffee',
|
||||
'browser/atom/objects-registry.coffee',
|
||||
'browser/atom/rpc-server.coffee',
|
||||
|
@ -54,6 +55,8 @@
|
|||
'browser/api/atom_api_menu_win.h',
|
||||
'browser/api/atom_api_power_monitor.cc',
|
||||
'browser/api/atom_api_power_monitor.h',
|
||||
'browser/api/atom_api_protocol.cc',
|
||||
'browser/api/atom_api_protocol.h',
|
||||
'browser/api/atom_api_window.cc',
|
||||
'browser/api/atom_api_window.h',
|
||||
'browser/api/atom_browser_bindings.cc',
|
||||
|
@ -94,6 +97,12 @@
|
|||
'browser/native_window_win.cc',
|
||||
'browser/native_window_win.h',
|
||||
'browser/native_window_observer.h',
|
||||
'browser/net/adapter_request_job.cc',
|
||||
'browser/net/adapter_request_job.h',
|
||||
'browser/net/atom_url_request_job_factory.cc',
|
||||
'browser/net/atom_url_request_job_factory.h',
|
||||
'browser/net/url_request_string_job.cc',
|
||||
'browser/net/url_request_string_job.h',
|
||||
'browser/ui/accelerator_util.cc',
|
||||
'browser/ui/accelerator_util.h',
|
||||
'browser/ui/accelerator_util_mac.mm',
|
||||
|
|
363
browser/api/atom_api_protocol.cc
Normal file
363
browser/api/atom_api_protocol.cc
Normal file
|
@ -0,0 +1,363 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/api/atom_api_protocol.h"
|
||||
|
||||
#include "base/stl_util.h"
|
||||
#include "browser/atom_browser_context.h"
|
||||
#include "browser/net/adapter_request_job.h"
|
||||
#include "browser/net/atom_url_request_job_factory.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/url_request/url_request_context.h"
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
#include "vendor/node/src/node.h"
|
||||
#include "vendor/node/src/node_internals.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;
|
||||
|
||||
namespace {
|
||||
|
||||
// The protocol module object.
|
||||
v8::Persistent<v8::Object> g_protocol_object;
|
||||
|
||||
// Registered protocol handlers.
|
||||
typedef std::map<std::string, v8::Persistent<v8::Function>> HandlersMap;
|
||||
static HandlersMap g_handlers;
|
||||
|
||||
// Emit an event for the protocol module.
|
||||
void EmitEventInUI(const std::string& event, const std::string& parameter) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
v8::Local<v8::Value> argv[] = {
|
||||
v8::String::New(event.data(), event.size()),
|
||||
v8::String::New(parameter.data(), parameter.size()),
|
||||
};
|
||||
node::MakeCallback(g_protocol_object, "emit", arraysize(argv), argv);
|
||||
}
|
||||
|
||||
// Convert the URLRequest object to V8 object.
|
||||
v8::Handle<v8::Object> ConvertURLRequestToV8Object(
|
||||
const net::URLRequest* request) {
|
||||
v8::Local<v8::Object> obj = v8::Object::New();
|
||||
obj->Set(v8::String::New("method"),
|
||||
v8::String::New(request->method().c_str()));
|
||||
obj->Set(v8::String::New("url"),
|
||||
v8::String::New(request->url().spec().c_str()));
|
||||
obj->Set(v8::String::New("referrer"),
|
||||
v8::String::New(request->referrer().c_str()));
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Get the job factory.
|
||||
AtomURLRequestJobFactory* GetRequestJobFactory() {
|
||||
return static_cast<AtomURLRequestJobFactory*>(
|
||||
const_cast<net::URLRequestJobFactory*>(
|
||||
static_cast<content::BrowserContext*>(AtomBrowserContext::Get())->
|
||||
GetRequestContext()->GetURLRequestContext()->job_factory()));
|
||||
}
|
||||
|
||||
class CustomProtocolRequestJob : public AdapterRequestJob {
|
||||
public:
|
||||
CustomProtocolRequestJob(ProtocolHandler* protocol_handler,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate)
|
||||
: AdapterRequestJob(protocol_handler, request, network_delegate) {
|
||||
}
|
||||
|
||||
// AdapterRequestJob:
|
||||
virtual void GetJobTypeInUI() OVERRIDE {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
||||
|
||||
// Call the JS handler.
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::Value> argv[] = {
|
||||
ConvertURLRequestToV8Object(request()),
|
||||
};
|
||||
v8::Handle<v8::Value> result = g_handlers[request()->url().scheme()]->Call(
|
||||
v8::Context::GetCurrent()->Global(), arraysize(argv), argv);
|
||||
|
||||
// Determine the type of the job we are going to create.
|
||||
if (result->IsString()) {
|
||||
std::string data = *v8::String::Utf8Value(result);
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::CreateStringJobAndStart,
|
||||
GetWeakPtr(),
|
||||
"text/plain",
|
||||
"UTF-8",
|
||||
data));
|
||||
return;
|
||||
} else if (result->IsObject()) {
|
||||
v8::Handle<v8::Object> obj = result->ToObject();
|
||||
std::string name = *v8::String::Utf8Value(obj->GetConstructorName());
|
||||
if (name == "RequestStringJob") {
|
||||
std::string mime_type = *v8::String::Utf8Value(obj->Get(
|
||||
v8::String::New("mimeType")));
|
||||
std::string charset = *v8::String::Utf8Value(obj->Get(
|
||||
v8::String::New("charset")));
|
||||
std::string data = *v8::String::Utf8Value(obj->Get(
|
||||
v8::String::New("data")));
|
||||
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::CreateStringJobAndStart,
|
||||
GetWeakPtr(),
|
||||
mime_type,
|
||||
charset,
|
||||
data));
|
||||
return;
|
||||
} else if (name == "RequestFileJob") {
|
||||
base::FilePath path = base::FilePath::FromUTF8Unsafe(
|
||||
*v8::String::Utf8Value(obj->Get(v8::String::New("path"))));
|
||||
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::CreateFileJobAndStart,
|
||||
GetWeakPtr(),
|
||||
path));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Try the default protocol handler if we have.
|
||||
if (default_protocol_handler()) {
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::CreateJobFromProtocolHandlerAndStart,
|
||||
GetWeakPtr()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback to the not implemented error.
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::CreateErrorJobAndStart,
|
||||
GetWeakPtr(),
|
||||
net::ERR_NOT_IMPLEMENTED));
|
||||
}
|
||||
};
|
||||
|
||||
// Always return the same CustomProtocolRequestJob for all requests, because
|
||||
// the content API needs the ProtocolHandler to return a job immediately, and
|
||||
// getting the real job from the JS requires asynchronous calls, so we have
|
||||
// to create an adapter job first.
|
||||
// Users can also pass an extra ProtocolHandler as the fallback one when
|
||||
// registered handler doesn't want to deal with the request.
|
||||
class CustomProtocolHandler : public ProtocolHandler {
|
||||
public:
|
||||
explicit CustomProtocolHandler(ProtocolHandler* protocol_handler = NULL)
|
||||
: protocol_handler_(protocol_handler) {
|
||||
}
|
||||
|
||||
virtual net::URLRequestJob* MaybeCreateJob(
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const OVERRIDE {
|
||||
return new CustomProtocolRequestJob(protocol_handler_.get(),
|
||||
request,
|
||||
network_delegate);
|
||||
}
|
||||
|
||||
ProtocolHandler* ReleaseDefaultProtocolHandler() {
|
||||
return protocol_handler_.release();
|
||||
}
|
||||
|
||||
ProtocolHandler* original_handler() { return protocol_handler_.get(); }
|
||||
|
||||
private:
|
||||
scoped_ptr<ProtocolHandler> protocol_handler_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> Protocol::RegisterProtocol(const v8::Arguments& args) {
|
||||
std::string scheme(*v8::String::Utf8Value(args[0]));
|
||||
if (g_handlers.find(scheme) != g_handlers.end() ||
|
||||
net::URLRequest::IsHandledProtocol(scheme))
|
||||
return node::ThrowError("The scheme is already registered");
|
||||
|
||||
// Store the handler in a map.
|
||||
if (!args[1]->IsFunction())
|
||||
return node::ThrowError("Handler must be a function");
|
||||
g_handlers[scheme] = v8::Persistent<v8::Function>::New(
|
||||
node::node_isolate, v8::Handle<v8::Function>::Cast(args[1]));
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&RegisterProtocolInIO, scheme));
|
||||
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> Protocol::UnregisterProtocol(const v8::Arguments& args) {
|
||||
std::string scheme(*v8::String::Utf8Value(args[0]));
|
||||
|
||||
// Erase the handler from map.
|
||||
HandlersMap::iterator it(g_handlers.find(scheme));
|
||||
if (it == g_handlers.end())
|
||||
return node::ThrowError("The scheme has not been registered");
|
||||
g_handlers.erase(it);
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&UnregisterProtocolInIO, scheme));
|
||||
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> Protocol::IsHandledProtocol(const v8::Arguments& args) {
|
||||
return v8::Boolean::New(net::URLRequest::IsHandledProtocol(
|
||||
*v8::String::Utf8Value(args[0])));
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> Protocol::InterceptProtocol(const v8::Arguments& args) {
|
||||
std::string scheme(*v8::String::Utf8Value(args[0]));
|
||||
if (!GetRequestJobFactory()->HasProtocolHandler(scheme))
|
||||
return node::ThrowError("Cannot intercept procotol");
|
||||
|
||||
if (ContainsKey(g_handlers, scheme))
|
||||
return node::ThrowError("Cannot intercept custom procotols");
|
||||
|
||||
// Store the handler in a map.
|
||||
if (!args[1]->IsFunction())
|
||||
return node::ThrowError("Handler must be a function");
|
||||
g_handlers[scheme] = v8::Persistent<v8::Function>::New(
|
||||
node::node_isolate, v8::Handle<v8::Function>::Cast(args[1]));
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&InterceptProtocolInIO, scheme));
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Handle<v8::Value> Protocol::UninterceptProtocol(const v8::Arguments& args) {
|
||||
std::string scheme(*v8::String::Utf8Value(args[0]));
|
||||
|
||||
// Erase the handler from map.
|
||||
HandlersMap::iterator it(g_handlers.find(scheme));
|
||||
if (it == g_handlers.end())
|
||||
return node::ThrowError("The scheme has not been registered");
|
||||
g_handlers.erase(it);
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::IO,
|
||||
FROM_HERE,
|
||||
base::Bind(&UninterceptProtocolInIO,
|
||||
scheme));
|
||||
return v8::Undefined();
|
||||
}
|
||||
|
||||
// static
|
||||
void Protocol::RegisterProtocolInIO(const std::string& scheme) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
|
||||
job_factory->SetProtocolHandler(scheme, new CustomProtocolHandler);
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&EmitEventInUI,
|
||||
"registered",
|
||||
scheme));
|
||||
}
|
||||
|
||||
// static
|
||||
void Protocol::UnregisterProtocolInIO(const std::string& scheme) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
|
||||
job_factory->SetProtocolHandler(scheme, NULL);
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&EmitEventInUI,
|
||||
"unregistered",
|
||||
scheme));
|
||||
}
|
||||
|
||||
// static
|
||||
void Protocol::InterceptProtocolInIO(const std::string& scheme) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
|
||||
ProtocolHandler* original_handler = job_factory->GetProtocolHandler(scheme);
|
||||
if (original_handler == NULL) {
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&EmitEventInUI,
|
||||
"error",
|
||||
"There is no protocol handler to intercpet"));
|
||||
return;
|
||||
}
|
||||
|
||||
job_factory->ReplaceProtocol(scheme,
|
||||
new CustomProtocolHandler(original_handler));
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&EmitEventInUI,
|
||||
"intercepted",
|
||||
scheme));
|
||||
}
|
||||
|
||||
// static
|
||||
void Protocol::UninterceptProtocolInIO(const std::string& scheme) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
AtomURLRequestJobFactory* job_factory(GetRequestJobFactory());
|
||||
|
||||
// Check if the protocol handler is intercepted.
|
||||
CustomProtocolHandler* handler = static_cast<CustomProtocolHandler*>(
|
||||
job_factory->GetProtocolHandler(scheme));
|
||||
if (handler->original_handler() == NULL) {
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&EmitEventInUI,
|
||||
"error",
|
||||
"The protocol is not intercpeted"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the protocol handler to the orignal one and delete current
|
||||
// protocol handler.
|
||||
ProtocolHandler* original_handler = handler->ReleaseDefaultProtocolHandler();
|
||||
delete job_factory->ReplaceProtocol(scheme, original_handler);
|
||||
|
||||
content::BrowserThread::PostTask(content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&EmitEventInUI,
|
||||
"unintercepted",
|
||||
scheme));
|
||||
}
|
||||
|
||||
// static
|
||||
void Protocol::Initialize(v8::Handle<v8::Object> target) {
|
||||
// Remember the protocol object, used for emitting event later.
|
||||
g_protocol_object = v8::Persistent<v8::Object>::New(
|
||||
node::node_isolate, target);
|
||||
|
||||
node::SetMethod(target, "registerProtocol", RegisterProtocol);
|
||||
node::SetMethod(target, "unregisterProtocol", UnregisterProtocol);
|
||||
node::SetMethod(target, "isHandledProtocol", IsHandledProtocol);
|
||||
node::SetMethod(target, "interceptProtocol", InterceptProtocol);
|
||||
node::SetMethod(target, "uninterceptProtocol", UninterceptProtocol);
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
NODE_MODULE(atom_browser_protocol, atom::api::Protocol::Initialize)
|
43
browser/api/atom_api_protocol.h
Normal file
43
browser/api/atom_api_protocol.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_API_ATOM_API_PROTOCOL_H_
|
||||
#define ATOM_BROWSER_API_ATOM_API_PROTOCOL_H_
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
class Protocol {
|
||||
public:
|
||||
static void Initialize(v8::Handle<v8::Object> target);
|
||||
|
||||
private:
|
||||
static v8::Handle<v8::Value> RegisterProtocol(const v8::Arguments& args);
|
||||
static v8::Handle<v8::Value> UnregisterProtocol(const v8::Arguments& args);
|
||||
static v8::Handle<v8::Value> IsHandledProtocol(const v8::Arguments& args);
|
||||
|
||||
static v8::Handle<v8::Value> InterceptProtocol(const v8::Arguments& args);
|
||||
static v8::Handle<v8::Value> UninterceptProtocol(const v8::Arguments& args);
|
||||
|
||||
static void RegisterProtocolInIO(const std::string& scheme);
|
||||
static void UnregisterProtocolInIO(const std::string& scheme);
|
||||
|
||||
static void InterceptProtocolInIO(const std::string& scheme);
|
||||
static void UninterceptProtocolInIO(const std::string& scheme);
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(Protocol);
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_API_ATOM_API_PROTOCOL_H_
|
20
browser/api/lib/protocol.coffee
Normal file
20
browser/api/lib/protocol.coffee
Normal file
|
@ -0,0 +1,20 @@
|
|||
protocol = process.atomBinding 'protocol'
|
||||
EventEmitter = require('events').EventEmitter
|
||||
|
||||
protocol[key] = value for key, value of EventEmitter.prototype
|
||||
|
||||
protocol.RequestStringJob =
|
||||
class RequestStringJob
|
||||
constructor: ({mimeType, charset, data}) ->
|
||||
if typeof data isnt 'string' and not data instanceof Buffer
|
||||
throw new TypeError('Data should be string or Buffer')
|
||||
|
||||
@mimeType = mimeType ? 'text/plain'
|
||||
@charset = charset ? 'UTF-8'
|
||||
@data = String data
|
||||
|
||||
protocol.RequestFileJob =
|
||||
class RequestFileJob
|
||||
constructor: (@path) ->
|
||||
|
||||
module.exports = protocol
|
|
@ -44,10 +44,13 @@ unwrapArgs = (processId, routingId, args) ->
|
|||
when 'remote-object' then objectsRegistry.get meta.id
|
||||
when 'array' then unwrapArgs processId, routingId, meta.value
|
||||
when 'object'
|
||||
ret = {}
|
||||
ret = v8Util.createObjectWithName meta.name
|
||||
for member in meta.members
|
||||
ret[member.name] = metaToValue(member.value)
|
||||
ret
|
||||
when 'function-with-return-value'
|
||||
returnValue = metaToValue meta.value
|
||||
-> returnValue
|
||||
when 'function'
|
||||
ret = ->
|
||||
ipc.sendChannel processId, routingId, 'ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(processId, routingId, arguments)
|
||||
|
@ -101,6 +104,16 @@ ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, processId, routingId, id, args) ->
|
|||
catch e
|
||||
event.result = errorToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, processId, routingId, id, method, args) ->
|
||||
try
|
||||
args = unwrapArgs processId, routingId, args
|
||||
constructor = objectsRegistry.get(id)[method]
|
||||
# Call new with array of arguments.
|
||||
obj = new (Function::bind.apply(constructor, [null].concat(args)))
|
||||
event.result = valueToMeta processId, routingId, obj
|
||||
catch e
|
||||
event.result = errorToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, processId, routingId, id, method, args) ->
|
||||
try
|
||||
args = unwrapArgs processId, routingId, args
|
||||
|
|
|
@ -5,6 +5,13 @@
|
|||
#include "browser/atom_browser_client.h"
|
||||
|
||||
#include "browser/atom_browser_main_parts.h"
|
||||
#include "browser/net/atom_url_request_job_factory.h"
|
||||
#include "content/public/common/url_constants.h"
|
||||
#include "net/url_request/data_protocol_handler.h"
|
||||
#include "net/url_request/file_protocol_handler.h"
|
||||
#include "net/url_request/url_request_context.h"
|
||||
#include "net/url_request/url_request_context_storage.h"
|
||||
#include "vendor/brightray/browser/url_request_context_getter.h"
|
||||
#include "webkit/glue/webpreferences.h"
|
||||
|
||||
namespace atom {
|
||||
|
@ -15,6 +22,36 @@ AtomBrowserClient::AtomBrowserClient() {
|
|||
AtomBrowserClient::~AtomBrowserClient() {
|
||||
}
|
||||
|
||||
net::URLRequestContextGetter* AtomBrowserClient::CreateRequestContext(
|
||||
content::BrowserContext* browser_context,
|
||||
content::ProtocolHandlerMap* protocol_handlers) {
|
||||
content::ProtocolHandlerMap preset_handlers;
|
||||
std::swap(preset_handlers, *protocol_handlers);
|
||||
|
||||
// Create our implementaton of job factory.
|
||||
AtomURLRequestJobFactory* job_factory = new AtomURLRequestJobFactory;
|
||||
content::ProtocolHandlerMap::iterator it;
|
||||
for (it = preset_handlers.begin(); it != preset_handlers.end(); ++it)
|
||||
job_factory->SetProtocolHandler(it->first, it->second.release());
|
||||
job_factory->SetProtocolHandler(chrome::kDataScheme,
|
||||
new net::DataProtocolHandler);
|
||||
job_factory->SetProtocolHandler(chrome::kFileScheme,
|
||||
new net::FileProtocolHandler);
|
||||
|
||||
// Go through default procedure.
|
||||
net::URLRequestContextGetter* request_context_getter =
|
||||
brightray::BrowserClient::CreateRequestContext(browser_context,
|
||||
protocol_handlers);
|
||||
net::URLRequestContext* request_context =
|
||||
request_context_getter->GetURLRequestContext();
|
||||
|
||||
// Replace default job factory.
|
||||
storage_.reset(new net::URLRequestContextStorage(request_context));
|
||||
storage_->set_job_factory(job_factory);
|
||||
|
||||
return request_context_getter;
|
||||
}
|
||||
|
||||
void AtomBrowserClient::OverrideWebkitPrefs(
|
||||
content::RenderViewHost* render_view_host,
|
||||
const GURL& url,
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
#include "brightray/browser/browser_client.h"
|
||||
|
||||
namespace net {
|
||||
class URLRequestContextStorage;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomBrowserClient : public brightray::BrowserClient {
|
||||
|
@ -15,6 +19,9 @@ class AtomBrowserClient : public brightray::BrowserClient {
|
|||
virtual ~AtomBrowserClient();
|
||||
|
||||
protected:
|
||||
net::URLRequestContextGetter* CreateRequestContext(
|
||||
content::BrowserContext* browser_context,
|
||||
content::ProtocolHandlerMap* protocol_handlers) OVERRIDE;
|
||||
virtual void OverrideWebkitPrefs(content::RenderViewHost* render_view_host,
|
||||
const GURL& url,
|
||||
WebPreferences* prefs) OVERRIDE;
|
||||
|
@ -27,6 +34,8 @@ class AtomBrowserClient : public brightray::BrowserClient {
|
|||
virtual brightray::BrowserMainParts* OverrideCreateBrowserMainParts(
|
||||
const content::MainFunctionParams&) OVERRIDE;
|
||||
|
||||
scoped_ptr<net::URLRequestContextStorage> storage_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomBrowserClient);
|
||||
};
|
||||
|
||||
|
|
105
browser/net/adapter_request_job.cc
Normal file
105
browser/net/adapter_request_job.cc
Normal file
|
@ -0,0 +1,105 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/net/adapter_request_job.h"
|
||||
|
||||
#include "browser/net/url_request_string_job.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/base/net_errors.h"
|
||||
#include "net/url_request/url_request_error_job.h"
|
||||
#include "net/url_request/url_request_file_job.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
AdapterRequestJob::AdapterRequestJob(ProtocolHandler* protocol_handler,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate)
|
||||
: URLRequestJob(request, network_delegate),
|
||||
protocol_handler_(protocol_handler),
|
||||
weak_factory_(this) {
|
||||
}
|
||||
|
||||
void AdapterRequestJob::Start() {
|
||||
DCHECK(!real_job_);
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::UI,
|
||||
FROM_HERE,
|
||||
base::Bind(&AdapterRequestJob::GetJobTypeInUI,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void AdapterRequestJob::Kill() {
|
||||
DCHECK(real_job_);
|
||||
real_job_->Kill();
|
||||
}
|
||||
|
||||
bool AdapterRequestJob::ReadRawData(net::IOBuffer* buf,
|
||||
int buf_size,
|
||||
int *bytes_read) {
|
||||
DCHECK(real_job_);
|
||||
return real_job_->ReadRawData(buf, buf_size, bytes_read);
|
||||
}
|
||||
|
||||
bool AdapterRequestJob::IsRedirectResponse(GURL* location,
|
||||
int* http_status_code) {
|
||||
DCHECK(real_job_);
|
||||
return real_job_->IsRedirectResponse(location, http_status_code);
|
||||
}
|
||||
|
||||
net::Filter* AdapterRequestJob::SetupFilter() const {
|
||||
DCHECK(real_job_);
|
||||
return real_job_->SetupFilter();
|
||||
}
|
||||
|
||||
bool AdapterRequestJob::GetMimeType(std::string* mime_type) const {
|
||||
DCHECK(real_job_);
|
||||
return real_job_->GetMimeType(mime_type);
|
||||
}
|
||||
|
||||
bool AdapterRequestJob::GetCharset(std::string* charset) {
|
||||
DCHECK(real_job_);
|
||||
return real_job_->GetCharset(charset);
|
||||
}
|
||||
|
||||
base::WeakPtr<AdapterRequestJob> AdapterRequestJob::GetWeakPtr() {
|
||||
return weak_factory_.GetWeakPtr();
|
||||
}
|
||||
|
||||
void AdapterRequestJob::CreateErrorJobAndStart(int error_code) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
|
||||
real_job_ = new net::URLRequestErrorJob(
|
||||
request(), network_delegate(), error_code);
|
||||
real_job_->Start();
|
||||
}
|
||||
|
||||
void AdapterRequestJob::CreateStringJobAndStart(const std::string& mime_type,
|
||||
const std::string& charset,
|
||||
const std::string& data) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
|
||||
real_job_ = new URLRequestStringJob(
|
||||
request(), network_delegate(), mime_type, charset, data);
|
||||
real_job_->Start();
|
||||
}
|
||||
|
||||
void AdapterRequestJob::CreateFileJobAndStart(const base::FilePath& path) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
|
||||
real_job_ = new net::URLRequestFileJob(request(), network_delegate(), path);
|
||||
real_job_->Start();
|
||||
}
|
||||
|
||||
void AdapterRequestJob::CreateJobFromProtocolHandlerAndStart() {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
DCHECK(protocol_handler_);
|
||||
real_job_ = protocol_handler_->MaybeCreateJob(request(),
|
||||
network_delegate());
|
||||
if (!real_job_.get())
|
||||
CreateErrorJobAndStart(net::ERR_NOT_IMPLEMENTED);
|
||||
else
|
||||
real_job_->Start();
|
||||
}
|
||||
|
||||
} // namespace atom
|
68
browser/net/adapter_request_job.h
Normal file
68
browser/net/adapter_request_job.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ADAPTER_REQUEST_JOB_H_
|
||||
#define ATOM_BROWSER_NET_ADAPTER_REQUEST_JOB_H_
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "net/url_request/url_request_job.h"
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
|
||||
namespace base {
|
||||
class FilePath;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
// Ask JS which type of job it wants, and then delegate corresponding methods.
|
||||
class AdapterRequestJob : public net::URLRequestJob {
|
||||
public:
|
||||
typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;
|
||||
|
||||
AdapterRequestJob(ProtocolHandler* protocol_handler,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate);
|
||||
|
||||
public:
|
||||
// net::URLRequestJob:
|
||||
virtual void Start() OVERRIDE;
|
||||
virtual void Kill() OVERRIDE;
|
||||
virtual bool ReadRawData(net::IOBuffer* buf,
|
||||
int buf_size,
|
||||
int *bytes_read) OVERRIDE;
|
||||
virtual bool IsRedirectResponse(GURL* location,
|
||||
int* http_status_code) OVERRIDE;
|
||||
virtual net::Filter* SetupFilter() const OVERRIDE;
|
||||
virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
|
||||
virtual bool GetCharset(std::string* charset) OVERRIDE;
|
||||
|
||||
base::WeakPtr<AdapterRequestJob> GetWeakPtr();
|
||||
|
||||
ProtocolHandler* default_protocol_handler() { return protocol_handler_; }
|
||||
|
||||
// Override this function to determine which job should be started.
|
||||
virtual void GetJobTypeInUI() = 0;
|
||||
|
||||
void CreateErrorJobAndStart(int error_code);
|
||||
void CreateStringJobAndStart(const std::string& mime_type,
|
||||
const std::string& charset,
|
||||
const std::string& data);
|
||||
void CreateFileJobAndStart(const base::FilePath& path);
|
||||
void CreateJobFromProtocolHandlerAndStart();
|
||||
|
||||
private:
|
||||
// The delegated request job.
|
||||
scoped_refptr<net::URLRequestJob> real_job_;
|
||||
|
||||
// Default protocol handler.
|
||||
ProtocolHandler* protocol_handler_;
|
||||
|
||||
base::WeakPtrFactory<AdapterRequestJob> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AdapterRequestJob);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ADAPTER_REQUEST_JOB_H_
|
105
browser/net/atom_url_request_job_factory.cc
Normal file
105
browser/net/atom_url_request_job_factory.cc
Normal file
|
@ -0,0 +1,105 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/net/atom_url_request_job_factory.h"
|
||||
|
||||
#include "base/stl_util.h"
|
||||
#include "googleurl/src/gurl.h"
|
||||
#include "net/base/load_flags.h"
|
||||
#include "net/url_request/url_request.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler;
|
||||
|
||||
AtomURLRequestJobFactory::AtomURLRequestJobFactory() {}
|
||||
|
||||
AtomURLRequestJobFactory::~AtomURLRequestJobFactory() {
|
||||
STLDeleteValues(&protocol_handler_map_);
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::SetProtocolHandler(
|
||||
const std::string& scheme,
|
||||
ProtocolHandler* protocol_handler) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
|
||||
base::AutoLock locked(lock_);
|
||||
|
||||
if (!protocol_handler) {
|
||||
ProtocolHandlerMap::iterator it = protocol_handler_map_.find(scheme);
|
||||
if (it == protocol_handler_map_.end())
|
||||
return false;
|
||||
|
||||
delete it->second;
|
||||
protocol_handler_map_.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ContainsKey(protocol_handler_map_, scheme))
|
||||
return false;
|
||||
protocol_handler_map_[scheme] = protocol_handler;
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtocolHandler* AtomURLRequestJobFactory::ReplaceProtocol(
|
||||
const std::string& scheme,
|
||||
ProtocolHandler* protocol_handler) {
|
||||
DCHECK(CalledOnValidThread());
|
||||
DCHECK(protocol_handler);
|
||||
|
||||
base::AutoLock locked(lock_);
|
||||
if (!ContainsKey(protocol_handler_map_, scheme))
|
||||
return NULL;
|
||||
ProtocolHandler* original_protocol_handler = protocol_handler_map_[scheme];
|
||||
protocol_handler_map_[scheme] = protocol_handler;
|
||||
return original_protocol_handler;
|
||||
}
|
||||
|
||||
ProtocolHandler* AtomURLRequestJobFactory::GetProtocolHandler(
|
||||
const std::string& scheme) const {
|
||||
DCHECK(CalledOnValidThread());
|
||||
|
||||
base::AutoLock locked(lock_);
|
||||
ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme);
|
||||
if (it == protocol_handler_map_.end())
|
||||
return NULL;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::HasProtocolHandler(
|
||||
const std::string& scheme) const {
|
||||
base::AutoLock locked(lock_);
|
||||
return ContainsKey(protocol_handler_map_, scheme);
|
||||
}
|
||||
|
||||
net::URLRequestJob* AtomURLRequestJobFactory::MaybeCreateJobWithProtocolHandler(
|
||||
const std::string& scheme,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const {
|
||||
DCHECK(CalledOnValidThread());
|
||||
|
||||
base::AutoLock locked(lock_);
|
||||
ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme);
|
||||
if (it == protocol_handler_map_.end())
|
||||
return NULL;
|
||||
return it->second->MaybeCreateJob(request, network_delegate);
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::IsHandledProtocol(
|
||||
const std::string& scheme) const {
|
||||
DCHECK(CalledOnValidThread());
|
||||
return HasProtocolHandler(scheme) ||
|
||||
net::URLRequest::IsHandledProtocol(scheme);
|
||||
}
|
||||
|
||||
bool AtomURLRequestJobFactory::IsHandledURL(const GURL& url) const {
|
||||
if (!url.is_valid()) {
|
||||
// We handle error cases.
|
||||
return true;
|
||||
}
|
||||
return IsHandledProtocol(url.scheme());
|
||||
}
|
||||
|
||||
} // namespace atom
|
61
browser/net/atom_url_request_job_factory.h
Normal file
61
browser/net/atom_url_request_job_factory.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_ATOM_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
|
||||
#define ATOM_BROWSER_NET_ATOM_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "net/url_request/url_request_job_factory.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomURLRequestJobFactory : public net::URLRequestJobFactory {
|
||||
public:
|
||||
AtomURLRequestJobFactory();
|
||||
virtual ~AtomURLRequestJobFactory();
|
||||
|
||||
// Sets the ProtocolHandler for a scheme. Returns true on success, false on
|
||||
// failure (a ProtocolHandler already exists for |scheme|). On success,
|
||||
// URLRequestJobFactory takes ownership of |protocol_handler|.
|
||||
bool SetProtocolHandler(const std::string& scheme,
|
||||
ProtocolHandler* protocol_handler);
|
||||
|
||||
// Intercepts the ProtocolHandler for a scheme. Returns the original protocol
|
||||
// handler on success, otherwise returns NULL.
|
||||
ProtocolHandler* ReplaceProtocol(const std::string& scheme,
|
||||
ProtocolHandler* protocol_handler);
|
||||
|
||||
// Returns the protocol handler registered with scheme.
|
||||
ProtocolHandler* GetProtocolHandler(const std::string& scheme) const;
|
||||
|
||||
// Whether the protocol handler is registered by the job factory.
|
||||
bool HasProtocolHandler(const std::string& scheme) const;
|
||||
|
||||
// URLRequestJobFactory implementation
|
||||
virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
|
||||
const std::string& scheme,
|
||||
net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate) const OVERRIDE;
|
||||
virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE;
|
||||
virtual bool IsHandledURL(const GURL& url) const OVERRIDE;
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, ProtocolHandler*> ProtocolHandlerMap;
|
||||
|
||||
ProtocolHandlerMap protocol_handler_map_;
|
||||
|
||||
mutable base::Lock lock_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AtomURLRequestJobFactory);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_ATOM_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
|
33
browser/net/url_request_string_job.cc
Normal file
33
browser/net/url_request_string_job.cc
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "browser/net/url_request_string_job.h"
|
||||
|
||||
#include "net/base/net_errors.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
URLRequestStringJob::URLRequestStringJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
const std::string& mime_type,
|
||||
const std::string& charset,
|
||||
const std::string& data)
|
||||
: net::URLRequestSimpleJob(request, network_delegate),
|
||||
mime_type_(mime_type),
|
||||
charset_(charset),
|
||||
data_(data) {
|
||||
}
|
||||
|
||||
int URLRequestStringJob::GetData(
|
||||
std::string* mime_type,
|
||||
std::string* charset,
|
||||
std::string* data,
|
||||
const net::CompletionCallback& callback) const {
|
||||
*mime_type = mime_type_;
|
||||
*charset = charset_;
|
||||
*data = data_;
|
||||
return net::OK;
|
||||
}
|
||||
|
||||
} // namespace atom
|
36
browser/net/url_request_string_job.h
Normal file
36
browser/net/url_request_string_job.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2013 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_URL_REQUEST_STRING_JOB_H_
|
||||
#define ATOM_BROWSER_NET_URL_REQUEST_STRING_JOB_H_
|
||||
|
||||
#include "net/url_request/url_request_simple_job.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class URLRequestStringJob : public net::URLRequestSimpleJob {
|
||||
public:
|
||||
URLRequestStringJob(net::URLRequest* request,
|
||||
net::NetworkDelegate* network_delegate,
|
||||
const std::string& mime_type,
|
||||
const std::string& charset,
|
||||
const std::string& data);
|
||||
|
||||
// URLRequestSimpleJob:
|
||||
virtual int GetData(std::string* mime_type,
|
||||
std::string* charset,
|
||||
std::string* data,
|
||||
const net::CompletionCallback& callback) const OVERRIDE;
|
||||
|
||||
private:
|
||||
std::string mime_type_;
|
||||
std::string charset_;
|
||||
std::string data_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(URLRequestStringJob);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_URL_REQUEST_STRING_JOB_H_
|
|
@ -16,6 +16,7 @@ NODE_EXT_LIST_ITEM(atom_browser_dialog)
|
|||
NODE_EXT_LIST_ITEM(atom_browser_ipc)
|
||||
NODE_EXT_LIST_ITEM(atom_browser_menu)
|
||||
NODE_EXT_LIST_ITEM(atom_browser_power_monitor)
|
||||
NODE_EXT_LIST_ITEM(atom_browser_protocol)
|
||||
NODE_EXT_LIST_ITEM(atom_browser_window)
|
||||
|
||||
// Module names start with `atom_renderer_` can only be used by renderer
|
||||
|
|
|
@ -31,8 +31,9 @@ Browser side modules:
|
|||
* [menu](menu.md)
|
||||
* [menu-item](menu-item.md)
|
||||
* [power-monitor](power-monitor.md)
|
||||
* [protocol](protocol.md)
|
||||
|
||||
Common modules:
|
||||
|
||||
* [clipboard](clipboard.md)
|
||||
* [shell](shell.md)
|
||||
* [shell](shell.md)
|
||||
|
|
68
docs/protocol.md
Normal file
68
docs/protocol.md
Normal file
|
@ -0,0 +1,68 @@
|
|||
## Synopsis
|
||||
|
||||
The `protocol` module can register a new protocol or intercept an existing
|
||||
protocol, so you can custom the response to the requests for vairous protocols.
|
||||
|
||||
An example of implementing a protocol that has the same effect with the
|
||||
`file://` protocol:
|
||||
|
||||
```javascript
|
||||
var protocol = require('protocol');
|
||||
protocol.registerProtocol('atom', function(request) {
|
||||
var path = request.url.substr(7)
|
||||
return new protocol.RequestFileJob(path);
|
||||
});
|
||||
```
|
||||
|
||||
## protocol.registerProtocol(scheme, handler)
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
|
||||
Registers a custom protocol of `scheme`, the `handler` would be called with
|
||||
`handler(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
|
||||
response you would like to send.
|
||||
|
||||
## protocol.unregisterProtocol(scheme)
|
||||
|
||||
* `scheme` String
|
||||
|
||||
Unregisters the custom protocol of `scheme`.
|
||||
|
||||
## protocol.isHandledProtocol(scheme)
|
||||
|
||||
* `scheme` String
|
||||
|
||||
Returns whether the `scheme` can be handled already.
|
||||
|
||||
## protocol.interceptProtocol(scheme, handler)
|
||||
|
||||
* `scheme` String
|
||||
* `handler` Function
|
||||
|
||||
Intercepts an existing protocol with `scheme`, returning `null` or `undefined`
|
||||
in `handler` would use the original protocol handler to handle the request.
|
||||
|
||||
## protocol.uninterceptProtocol(scheme)
|
||||
|
||||
* `scheme` String
|
||||
|
||||
Unintercepts a protocol.
|
||||
|
||||
## Class: protocol.RequestFileJob(path)
|
||||
|
||||
* `path` String
|
||||
|
||||
Create a request job which would query a file of `path` and set corresponding
|
||||
mime types.
|
||||
|
||||
## Class: protocol.RequestStringJob(options)
|
||||
|
||||
* `options` Object
|
||||
* `mimeType` String - Default is `text/plain`
|
||||
* `charset` String - Default is `UTF-8`
|
||||
* `data` String
|
||||
|
||||
Create a request job which sends a string as response.
|
|
@ -13,9 +13,11 @@ wrapArgs = (args) ->
|
|||
else if value? and typeof value is 'object' and v8Util.getHiddenValue value, 'atomId'
|
||||
type: 'remote-object', id: v8Util.getHiddenValue value, 'atomId'
|
||||
else if value? and typeof value is 'object'
|
||||
ret = type: 'object', members: []
|
||||
ret = type: 'object', name: value.constructor.name, members: []
|
||||
ret.members.push(name: prop, value: valueToMeta(field)) for prop, field of value
|
||||
ret
|
||||
else if typeof value is 'function' and v8Util.getHiddenValue value, 'returnValue'
|
||||
type: 'function-with-return-value', value: valueToMeta(value())
|
||||
else if typeof value is 'function'
|
||||
type: 'function', id: callbacksRegistry.add(value)
|
||||
else
|
||||
|
@ -29,8 +31,7 @@ metaToValue = (meta) ->
|
|||
when 'value' then meta.value
|
||||
when 'array' then (metaToValue(el) for el in meta.members)
|
||||
when 'error'
|
||||
console.log meta.stack
|
||||
throw new Error(meta.message)
|
||||
throw new Error("#{meta.message}\n#{meta.stack}")
|
||||
else
|
||||
if meta.type is 'function'
|
||||
# A shadow class to represent the remote function object.
|
||||
|
@ -56,10 +57,17 @@ metaToValue = (meta) ->
|
|||
for member in meta.members
|
||||
do (member) ->
|
||||
if member.type is 'function'
|
||||
ret[member.name] = ->
|
||||
# Call member function.
|
||||
ret = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_CALL', meta.id, member.name, wrapArgs(arguments)
|
||||
metaToValue ret
|
||||
ret[member.name] =
|
||||
class RemoteMemberFunction
|
||||
constructor: ->
|
||||
if @constructor is RemoteMemberFunction
|
||||
# Constructor call.
|
||||
obj = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', meta.id, member.name, wrapArgs(arguments)
|
||||
return metaToValue obj
|
||||
else
|
||||
# Call member function.
|
||||
ret = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_CALL', meta.id, member.name, wrapArgs(arguments)
|
||||
return metaToValue ret
|
||||
else
|
||||
ret.__defineSetter__ member.name, (value) ->
|
||||
# Set member data.
|
||||
|
@ -121,3 +129,9 @@ processCache = null
|
|||
exports.__defineGetter__ 'process', ->
|
||||
processCache = exports.getGlobal('process') unless processCache?
|
||||
processCache
|
||||
|
||||
# Create a funtion that will return the specifed value when called in browser.
|
||||
exports.createFunctionWithReturnValue = (returnValue) ->
|
||||
func = -> returnValue
|
||||
v8Util.setHiddenValue func, 'returnValue', true
|
||||
func
|
||||
|
|
|
@ -16,7 +16,14 @@ describe 'ipc', ->
|
|||
a = remote.require path.join(fixtures, 'module', 'id.js')
|
||||
assert.equal a.id, 1127
|
||||
|
||||
describe 'remote object', ->
|
||||
describe 'remote.createFunctionWithReturnValue', ->
|
||||
it 'should be called in browser synchronously', ->
|
||||
buf = new Buffer('test')
|
||||
call = remote.require path.join(fixtures, 'module', 'call.js')
|
||||
result = call.call remote.createFunctionWithReturnValue(buf)
|
||||
assert.equal result.constructor.name, 'Buffer'
|
||||
|
||||
describe 'remote object in renderer', ->
|
||||
it 'can change its properties', ->
|
||||
property = remote.require path.join(fixtures, 'module', 'property.js')
|
||||
assert.equal property.property, 1127
|
||||
|
@ -28,6 +35,17 @@ describe 'ipc', ->
|
|||
# Restore.
|
||||
property.property = 1127
|
||||
|
||||
it 'can construct an object from its member', ->
|
||||
call = remote.require path.join(fixtures, 'module', 'call.js')
|
||||
obj = new call.constructor
|
||||
assert.equal obj.test, 'test'
|
||||
|
||||
describe 'remote value in browser', ->
|
||||
it 'keeps its constructor name for objects', ->
|
||||
buf = new Buffer('test')
|
||||
print_name = remote.require path.join(fixtures, 'module', 'print_name.js')
|
||||
assert.equal print_name.print(buf), 'Buffer'
|
||||
|
||||
describe 'ipc.send', ->
|
||||
it 'should work when sending an object containing id property', (done) ->
|
||||
obj = id: 1, name: 'ly'
|
||||
|
|
132
spec/api/protocol.coffee
Normal file
132
spec/api/protocol.coffee
Normal file
|
@ -0,0 +1,132 @@
|
|||
assert = require 'assert'
|
||||
ipc = require 'ipc'
|
||||
remote = require 'remote'
|
||||
protocol = remote.require 'protocol'
|
||||
|
||||
describe 'protocol API', ->
|
||||
describe 'protocol.registerProtocol', ->
|
||||
it 'throws error when scheme is already registered', (done) ->
|
||||
register = -> protocol.registerProtocol('test1', ->)
|
||||
protocol.once 'registered', (scheme) ->
|
||||
assert.equal scheme, 'test1'
|
||||
assert.throws register, /The scheme is already registered/
|
||||
protocol.unregisterProtocol 'test1'
|
||||
done()
|
||||
register()
|
||||
|
||||
it 'calls the callback when scheme is visited', (done) ->
|
||||
protocol.registerProtocol 'test2', (request) ->
|
||||
assert.equal request.url, 'test2://test2'
|
||||
assert.equal request.referrer, window.location.toString()
|
||||
protocol.unregisterProtocol 'test2'
|
||||
done()
|
||||
$.get 'test2://test2', ->
|
||||
|
||||
describe 'protocol.unregisterProtocol', ->
|
||||
it 'throws error when scheme does not exist', ->
|
||||
unregister = -> protocol.unregisterProtocol 'test3'
|
||||
assert.throws unregister, /The scheme has not been registered/
|
||||
|
||||
describe 'registered protocol callback', ->
|
||||
it 'returns string should send the string as request content', (done) ->
|
||||
handler = remote.createFunctionWithReturnValue 'valar morghulis'
|
||||
protocol.registerProtocol 'atom-string', handler
|
||||
|
||||
$.ajax
|
||||
url: 'atom-string://fake-host'
|
||||
success: (data) ->
|
||||
assert.equal data, handler()
|
||||
protocol.unregisterProtocol 'atom-string'
|
||||
done()
|
||||
error: (xhr, errorType, error) ->
|
||||
assert false, 'Got error: ' + errorType + ' ' + error
|
||||
protocol.unregisterProtocol 'atom-string'
|
||||
|
||||
it 'returns RequestStringJob should send string', (done) ->
|
||||
data = 'valar morghulis'
|
||||
job = new protocol.RequestStringJob(mimeType: 'text/html', data: data)
|
||||
handler = remote.createFunctionWithReturnValue job
|
||||
protocol.registerProtocol 'atom-string-job', handler
|
||||
|
||||
$.ajax
|
||||
url: 'atom-string-job://fake-host'
|
||||
success: (response) ->
|
||||
assert.equal response, data
|
||||
protocol.unregisterProtocol 'atom-string-job'
|
||||
done()
|
||||
error: (xhr, errorType, error) ->
|
||||
assert false, 'Got error: ' + errorType + ' ' + error
|
||||
protocol.unregisterProtocol 'atom-string-job'
|
||||
|
||||
it 'returns RequestFileJob should send file', (done) ->
|
||||
job = new protocol.RequestFileJob(__filename)
|
||||
handler = remote.createFunctionWithReturnValue job
|
||||
protocol.registerProtocol 'atom-file-job', handler
|
||||
|
||||
$.ajax
|
||||
url: 'atom-file-job://' + __filename
|
||||
success: (data) ->
|
||||
content = require('fs').readFileSync __filename
|
||||
assert.equal data, String(content)
|
||||
protocol.unregisterProtocol 'atom-file-job'
|
||||
done()
|
||||
error: (xhr, errorType, error) ->
|
||||
assert false, 'Got error: ' + errorType + ' ' + error
|
||||
protocol.unregisterProtocol 'atom-file-job'
|
||||
|
||||
describe 'protocol.isHandledProtocol', ->
|
||||
it 'returns true if the scheme can be handled', ->
|
||||
assert.equal protocol.isHandledProtocol('file'), true
|
||||
assert.equal protocol.isHandledProtocol('http'), true
|
||||
assert.equal protocol.isHandledProtocol('https'), true
|
||||
assert.equal protocol.isHandledProtocol('atom'), false
|
||||
|
||||
describe 'protocol.interceptProtocol', ->
|
||||
it 'throws error when scheme is not a registered one', ->
|
||||
register = -> protocol.interceptProtocol('test-intercept', ->)
|
||||
assert.throws register, /Cannot intercept procotol/
|
||||
|
||||
it 'throws error when scheme is a custom protocol', (done) ->
|
||||
protocol.once 'unregistered', (scheme) ->
|
||||
assert.equal scheme, 'atom'
|
||||
done()
|
||||
protocol.once 'registered', (scheme) ->
|
||||
assert.equal scheme, 'atom'
|
||||
register = -> protocol.interceptProtocol('test-intercept', ->)
|
||||
assert.throws register, /Cannot intercept procotol/
|
||||
protocol.unregisterProtocol scheme
|
||||
protocol.registerProtocol('atom', ->)
|
||||
|
||||
it 'returns original job when callback returns nothing', (done) ->
|
||||
targetScheme = 'file'
|
||||
protocol.once 'intercepted', (scheme) ->
|
||||
assert.equal scheme, targetScheme
|
||||
free = -> protocol.uninterceptProtocol targetScheme
|
||||
$.ajax
|
||||
url: "#{targetScheme}://#{__filename}",
|
||||
success: ->
|
||||
protocol.once 'unintercepted', (scheme) ->
|
||||
assert.equal scheme, targetScheme
|
||||
done()
|
||||
free()
|
||||
error: (xhr, errorType, error) ->
|
||||
free()
|
||||
assert false, 'Got error: ' + errorType + ' ' + error
|
||||
protocol.interceptProtocol targetScheme, (request) ->
|
||||
assert.equal request.url, "#{targetScheme}://#{__filename}"
|
||||
|
||||
it 'can override original protocol handler', (done) ->
|
||||
handler = remote.createFunctionWithReturnValue 'valar morghulis'
|
||||
protocol.once 'intercepted', ->
|
||||
free = -> protocol.uninterceptProtocol 'file'
|
||||
$.ajax
|
||||
url: 'file://fake-host'
|
||||
success: (data) ->
|
||||
protocol.once 'unintercepted', ->
|
||||
assert.equal data, handler()
|
||||
done()
|
||||
free()
|
||||
error: (xhr, errorType, error) ->
|
||||
assert false, 'Got error: ' + errorType + ' ' + error
|
||||
free()
|
||||
protocol.interceptProtocol 'file', handler
|
7
spec/fixtures/module/call.js
vendored
Normal file
7
spec/fixtures/module/call.js
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
exports.call = function(func) {
|
||||
return func();
|
||||
}
|
||||
|
||||
exports.constructor = function() {
|
||||
this.test = 'test';
|
||||
}
|
3
spec/fixtures/module/print_name.js
vendored
Normal file
3
spec/fixtures/module/print_name.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
exports.print = function(obj) {
|
||||
return obj.constructor.name;
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta name="referrer" content="always">
|
||||
<link href="../node_modules/mocha/mocha.css" rel="stylesheet">
|
||||
<script src="jquery-2.0.3.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
@ -24,21 +26,23 @@
|
|||
}
|
||||
|
||||
require('coffee-script'); // Supports .coffee tests.
|
||||
var ipc = require('ipc');
|
||||
|
||||
// Rediret all output to browser.
|
||||
var ipc = require('ipc');
|
||||
global.__defineGetter__('console', function() {
|
||||
return {
|
||||
log: function() {
|
||||
args = Array.prototype.slice.call(arguments);
|
||||
ipc.sendChannel('console.log', args);
|
||||
},
|
||||
error: function() {
|
||||
args = Array.prototype.slice.call(arguments);
|
||||
ipc.sendChannel('console.error', args);
|
||||
},
|
||||
}
|
||||
});
|
||||
if (isCi) {
|
||||
global.__defineGetter__('console', function() {
|
||||
return {
|
||||
log: function() {
|
||||
args = Array.prototype.slice.call(arguments);
|
||||
ipc.sendChannel('console.log', args);
|
||||
},
|
||||
error: function() {
|
||||
args = Array.prototype.slice.call(arguments);
|
||||
ipc.sendChannel('console.error', args);
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var Mocha = require('mocha');
|
||||
|
||||
|
|
6
spec/jquery-2.0.3.min.js
vendored
Normal file
6
spec/jquery-2.0.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -22,6 +22,10 @@ ipc.on('process.exit', function(pid, rid, code) {
|
|||
process.exit(code);
|
||||
});
|
||||
|
||||
ipc.on('eval', function(ev, pid, rid, script) {
|
||||
ev.result = eval(script);
|
||||
});
|
||||
|
||||
process.on('uncaughtException', function() {
|
||||
window.openDevTools();
|
||||
});
|
||||
|
|
2
vendor/brightray
vendored
2
vendor/brightray
vendored
|
@ -1 +1 @@
|
|||
Subproject commit c62f91a82e5426996d149dcb63259f967a7e78fb
|
||||
Subproject commit 3cb2782cff77081df2788c68948163dea53530d5
|
Loading…
Reference in a new issue