Merge pull request #65 from atom/custom-protocol

Support custom protocols
This commit is contained in:
Cheng Zhao 2013-09-04 03:33:09 -07:00
commit 3b7dd85d3f
25 changed files with 1184 additions and 24 deletions

View file

@ -19,6 +19,7 @@
'browser/api/lib/menu.coffee', 'browser/api/lib/menu.coffee',
'browser/api/lib/menu-item.coffee', 'browser/api/lib/menu-item.coffee',
'browser/api/lib/power-monitor.coffee', 'browser/api/lib/power-monitor.coffee',
'browser/api/lib/protocol.coffee',
'browser/atom/atom.coffee', 'browser/atom/atom.coffee',
'browser/atom/objects-registry.coffee', 'browser/atom/objects-registry.coffee',
'browser/atom/rpc-server.coffee', 'browser/atom/rpc-server.coffee',
@ -54,6 +55,8 @@
'browser/api/atom_api_menu_win.h', 'browser/api/atom_api_menu_win.h',
'browser/api/atom_api_power_monitor.cc', 'browser/api/atom_api_power_monitor.cc',
'browser/api/atom_api_power_monitor.h', '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.cc',
'browser/api/atom_api_window.h', 'browser/api/atom_api_window.h',
'browser/api/atom_browser_bindings.cc', 'browser/api/atom_browser_bindings.cc',
@ -94,6 +97,12 @@
'browser/native_window_win.cc', 'browser/native_window_win.cc',
'browser/native_window_win.h', 'browser/native_window_win.h',
'browser/native_window_observer.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.cc',
'browser/ui/accelerator_util.h', 'browser/ui/accelerator_util.h',
'browser/ui/accelerator_util_mac.mm', 'browser/ui/accelerator_util_mac.mm',

View 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)

View 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_

View 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

View file

@ -44,10 +44,13 @@ unwrapArgs = (processId, routingId, args) ->
when 'remote-object' then objectsRegistry.get meta.id when 'remote-object' then objectsRegistry.get meta.id
when 'array' then unwrapArgs processId, routingId, meta.value when 'array' then unwrapArgs processId, routingId, meta.value
when 'object' when 'object'
ret = {} ret = v8Util.createObjectWithName meta.name
for member in meta.members for member in meta.members
ret[member.name] = metaToValue(member.value) ret[member.name] = metaToValue(member.value)
ret ret
when 'function-with-return-value'
returnValue = metaToValue meta.value
-> returnValue
when 'function' when 'function'
ret = -> ret = ->
ipc.sendChannel processId, routingId, 'ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(processId, routingId, arguments) 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 catch e
event.result = errorToMeta 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) -> ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, processId, routingId, id, method, args) ->
try try
args = unwrapArgs processId, routingId, args args = unwrapArgs processId, routingId, args

View file

@ -5,6 +5,13 @@
#include "browser/atom_browser_client.h" #include "browser/atom_browser_client.h"
#include "browser/atom_browser_main_parts.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" #include "webkit/glue/webpreferences.h"
namespace atom { namespace atom {
@ -15,6 +22,36 @@ AtomBrowserClient::AtomBrowserClient() {
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( void AtomBrowserClient::OverrideWebkitPrefs(
content::RenderViewHost* render_view_host, content::RenderViewHost* render_view_host,
const GURL& url, const GURL& url,

View file

@ -7,6 +7,10 @@
#include "brightray/browser/browser_client.h" #include "brightray/browser/browser_client.h"
namespace net {
class URLRequestContextStorage;
}
namespace atom { namespace atom {
class AtomBrowserClient : public brightray::BrowserClient { class AtomBrowserClient : public brightray::BrowserClient {
@ -15,6 +19,9 @@ class AtomBrowserClient : public brightray::BrowserClient {
virtual ~AtomBrowserClient(); virtual ~AtomBrowserClient();
protected: protected:
net::URLRequestContextGetter* CreateRequestContext(
content::BrowserContext* browser_context,
content::ProtocolHandlerMap* protocol_handlers) OVERRIDE;
virtual void OverrideWebkitPrefs(content::RenderViewHost* render_view_host, virtual void OverrideWebkitPrefs(content::RenderViewHost* render_view_host,
const GURL& url, const GURL& url,
WebPreferences* prefs) OVERRIDE; WebPreferences* prefs) OVERRIDE;
@ -27,6 +34,8 @@ class AtomBrowserClient : public brightray::BrowserClient {
virtual brightray::BrowserMainParts* OverrideCreateBrowserMainParts( virtual brightray::BrowserMainParts* OverrideCreateBrowserMainParts(
const content::MainFunctionParams&) OVERRIDE; const content::MainFunctionParams&) OVERRIDE;
scoped_ptr<net::URLRequestContextStorage> storage_;
DISALLOW_COPY_AND_ASSIGN(AtomBrowserClient); DISALLOW_COPY_AND_ASSIGN(AtomBrowserClient);
}; };

View 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

View 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_

View 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

View 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_

View 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

View 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_

View file

@ -16,6 +16,7 @@ NODE_EXT_LIST_ITEM(atom_browser_dialog)
NODE_EXT_LIST_ITEM(atom_browser_ipc) NODE_EXT_LIST_ITEM(atom_browser_ipc)
NODE_EXT_LIST_ITEM(atom_browser_menu) NODE_EXT_LIST_ITEM(atom_browser_menu)
NODE_EXT_LIST_ITEM(atom_browser_power_monitor) NODE_EXT_LIST_ITEM(atom_browser_power_monitor)
NODE_EXT_LIST_ITEM(atom_browser_protocol)
NODE_EXT_LIST_ITEM(atom_browser_window) NODE_EXT_LIST_ITEM(atom_browser_window)
// Module names start with `atom_renderer_` can only be used by renderer // Module names start with `atom_renderer_` can only be used by renderer

View file

@ -31,8 +31,9 @@ Browser side modules:
* [menu](menu.md) * [menu](menu.md)
* [menu-item](menu-item.md) * [menu-item](menu-item.md)
* [power-monitor](power-monitor.md) * [power-monitor](power-monitor.md)
* [protocol](protocol.md)
Common modules: Common modules:
* [clipboard](clipboard.md) * [clipboard](clipboard.md)
* [shell](shell.md) * [shell](shell.md)

68
docs/protocol.md Normal file
View 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.

View file

@ -13,9 +13,11 @@ wrapArgs = (args) ->
else if value? and typeof value is 'object' and v8Util.getHiddenValue value, 'atomId' else if value? and typeof value is 'object' and v8Util.getHiddenValue value, 'atomId'
type: 'remote-object', id: v8Util.getHiddenValue value, 'atomId' type: 'remote-object', id: v8Util.getHiddenValue value, 'atomId'
else if value? and typeof value is 'object' 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.members.push(name: prop, value: valueToMeta(field)) for prop, field of value
ret 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' else if typeof value is 'function'
type: 'function', id: callbacksRegistry.add(value) type: 'function', id: callbacksRegistry.add(value)
else else
@ -29,8 +31,7 @@ metaToValue = (meta) ->
when 'value' then meta.value when 'value' then meta.value
when 'array' then (metaToValue(el) for el in meta.members) when 'array' then (metaToValue(el) for el in meta.members)
when 'error' when 'error'
console.log meta.stack throw new Error("#{meta.message}\n#{meta.stack}")
throw new Error(meta.message)
else else
if meta.type is 'function' if meta.type is 'function'
# A shadow class to represent the remote function object. # A shadow class to represent the remote function object.
@ -56,10 +57,17 @@ metaToValue = (meta) ->
for member in meta.members for member in meta.members
do (member) -> do (member) ->
if member.type is 'function' if member.type is 'function'
ret[member.name] = -> ret[member.name] =
# Call member function. class RemoteMemberFunction
ret = ipc.sendChannelSync 'ATOM_BROWSER_MEMBER_CALL', meta.id, member.name, wrapArgs(arguments) constructor: ->
metaToValue ret 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 else
ret.__defineSetter__ member.name, (value) -> ret.__defineSetter__ member.name, (value) ->
# Set member data. # Set member data.
@ -121,3 +129,9 @@ processCache = null
exports.__defineGetter__ 'process', -> exports.__defineGetter__ 'process', ->
processCache = exports.getGlobal('process') unless processCache? processCache = exports.getGlobal('process') unless processCache?
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

View file

@ -16,7 +16,14 @@ describe 'ipc', ->
a = remote.require path.join(fixtures, 'module', 'id.js') a = remote.require path.join(fixtures, 'module', 'id.js')
assert.equal a.id, 1127 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', -> it 'can change its properties', ->
property = remote.require path.join(fixtures, 'module', 'property.js') property = remote.require path.join(fixtures, 'module', 'property.js')
assert.equal property.property, 1127 assert.equal property.property, 1127
@ -28,6 +35,17 @@ describe 'ipc', ->
# Restore. # Restore.
property.property = 1127 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', -> describe 'ipc.send', ->
it 'should work when sending an object containing id property', (done) -> it 'should work when sending an object containing id property', (done) ->
obj = id: 1, name: 'ly' obj = id: 1, name: 'ly'

132
spec/api/protocol.coffee Normal file
View 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
View 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
View file

@ -0,0 +1,3 @@
exports.print = function(obj) {
return obj.constructor.name;
}

View file

@ -1,6 +1,8 @@
<html> <html>
<head> <head>
<meta name="referrer" content="always">
<link href="../node_modules/mocha/mocha.css" rel="stylesheet"> <link href="../node_modules/mocha/mocha.css" rel="stylesheet">
<script src="jquery-2.0.3.min.js"></script>
</head> </head>
<body> <body>
@ -24,21 +26,23 @@
} }
require('coffee-script'); // Supports .coffee tests. require('coffee-script'); // Supports .coffee tests.
var ipc = require('ipc');
// Rediret all output to browser. // Rediret all output to browser.
var ipc = require('ipc'); if (isCi) {
global.__defineGetter__('console', function() { global.__defineGetter__('console', function() {
return { return {
log: function() { log: function() {
args = Array.prototype.slice.call(arguments); args = Array.prototype.slice.call(arguments);
ipc.sendChannel('console.log', args); ipc.sendChannel('console.log', args);
}, },
error: function() { error: function() {
args = Array.prototype.slice.call(arguments); args = Array.prototype.slice.call(arguments);
ipc.sendChannel('console.error', args); ipc.sendChannel('console.error', args);
}, },
} }
}); });
}
var Mocha = require('mocha'); var Mocha = require('mocha');

6
spec/jquery-2.0.3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -22,6 +22,10 @@ ipc.on('process.exit', function(pid, rid, code) {
process.exit(code); process.exit(code);
}); });
ipc.on('eval', function(ev, pid, rid, script) {
ev.result = eval(script);
});
process.on('uncaughtException', function() { process.on('uncaughtException', function() {
window.openDevTools(); window.openDevTools();
}); });

2
vendor/brightray vendored

@ -1 +1 @@
Subproject commit c62f91a82e5426996d149dcb63259f967a7e78fb Subproject commit 3cb2782cff77081df2788c68948163dea53530d5