2013-08-24 07:26:10 +00:00
|
|
|
// 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"
|
|
|
|
|
2013-08-24 12:32:12 +00:00
|
|
|
#include "base/memory/weak_ptr.h"
|
2013-08-24 09:59:34 +00:00
|
|
|
#include "browser/atom_browser_context.h"
|
2013-08-24 08:38:19 +00:00
|
|
|
#include "content/public/browser/browser_thread.h"
|
2013-08-24 09:59:34 +00:00
|
|
|
#include "net/url_request/url_request_context.h"
|
|
|
|
#include "net/url_request/url_request_context_getter.h"
|
2013-08-24 11:33:08 +00:00
|
|
|
#include "net/url_request/url_request_error_job.h"
|
|
|
|
#include "net/url_request/url_request_file_job.h"
|
2013-08-24 09:59:34 +00:00
|
|
|
#include "net/url_request/url_request_job_factory_impl.h"
|
2013-08-24 11:33:08 +00:00
|
|
|
#include "net/url_request/url_request_simple_job.h"
|
2013-08-24 07:26:10 +00:00
|
|
|
#include "vendor/node/src/node.h"
|
|
|
|
|
|
|
|
namespace atom {
|
|
|
|
|
|
|
|
namespace api {
|
|
|
|
|
2013-08-24 09:59:34 +00:00
|
|
|
namespace {
|
|
|
|
|
2013-08-24 12:18:12 +00:00
|
|
|
typedef std::map<std::string, v8::Persistent<v8::Function>> HandlersMap;
|
|
|
|
static HandlersMap g_handlers;
|
|
|
|
|
2013-08-24 09:59:34 +00:00
|
|
|
net::URLRequestJobFactoryImpl* GetRequestJobFactory() {
|
|
|
|
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
|
|
|
// Get the job factory.
|
|
|
|
net::URLRequestJobFactoryImpl* job_factory =
|
|
|
|
static_cast<net::URLRequestJobFactoryImpl*>(
|
|
|
|
const_cast<net::URLRequestJobFactory*>(
|
|
|
|
static_cast<content::BrowserContext*>(AtomBrowserContext::Get())->
|
|
|
|
GetRequestContext()->GetURLRequestContext()->job_factory()));
|
|
|
|
return job_factory;
|
|
|
|
}
|
|
|
|
|
2013-08-25 04:36:06 +00:00
|
|
|
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)
|
|
|
|
: net::URLRequestSimpleJob(request, network_delegate),
|
|
|
|
mime_type_(mime_type),
|
|
|
|
charset_(charset),
|
|
|
|
data_(data) {
|
|
|
|
}
|
|
|
|
|
|
|
|
// URLRequestSimpleJob:
|
|
|
|
virtual int GetData(std::string* mime_type,
|
|
|
|
std::string* charset,
|
|
|
|
std::string* data,
|
|
|
|
const net::CompletionCallback& callback) const OVERRIDE {
|
|
|
|
*mime_type = mime_type_;
|
|
|
|
*charset = charset_;
|
|
|
|
*data = data_;
|
|
|
|
return net::OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::string mime_type_;
|
|
|
|
std::string charset_;
|
|
|
|
std::string data_;
|
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(URLRequestStringJob);
|
|
|
|
};
|
|
|
|
|
2013-08-24 11:33:08 +00:00
|
|
|
// Ask JS which type of job it wants, and then delegate corresponding methods.
|
|
|
|
class AdapterRequestJob : public net::URLRequestJob {
|
|
|
|
public:
|
|
|
|
AdapterRequestJob(net::URLRequest* request,
|
|
|
|
net::NetworkDelegate* network_delegate)
|
2013-08-24 12:32:12 +00:00
|
|
|
: URLRequestJob(request, network_delegate),
|
|
|
|
weak_factory_(this) {
|
|
|
|
}
|
2013-08-24 11:33:08 +00:00
|
|
|
|
|
|
|
protected:
|
2013-08-25 04:36:06 +00:00
|
|
|
// net::URLRequestJob:
|
2013-08-24 11:33:08 +00:00
|
|
|
virtual void Start() OVERRIDE {
|
|
|
|
DCHECK(!real_job_);
|
2013-08-24 11:46:38 +00:00
|
|
|
content::BrowserThread::PostTask(
|
|
|
|
content::BrowserThread::UI,
|
|
|
|
FROM_HERE,
|
2013-08-24 12:32:12 +00:00
|
|
|
base::Bind(&AdapterRequestJob::GetJobTypeInUI,
|
|
|
|
weak_factory_.GetWeakPtr()));
|
2013-08-24 11:33:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Kill() OVERRIDE {
|
|
|
|
DCHECK(real_job_);
|
|
|
|
real_job_->Kill();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool ReadRawData(net::IOBuffer* buf,
|
|
|
|
int buf_size,
|
|
|
|
int *bytes_read) OVERRIDE {
|
|
|
|
DCHECK(real_job_);
|
|
|
|
// The ReadRawData is a protected method.
|
|
|
|
switch (type_) {
|
2013-08-25 04:36:06 +00:00
|
|
|
case REQUEST_STRING_JOB:
|
|
|
|
return static_cast<URLRequestStringJob*>(real_job_.get())->
|
2013-08-24 11:33:08 +00:00
|
|
|
ReadRawData(buf, buf_size, bytes_read);
|
2013-08-25 08:06:29 +00:00
|
|
|
case REQUEST_FILE_JOB:
|
|
|
|
return static_cast<net::URLRequestFileJob*>(real_job_.get())->
|
|
|
|
ReadRawData(buf, buf_size, bytes_read);
|
2013-08-24 11:33:08 +00:00
|
|
|
default:
|
|
|
|
return net::URLRequestJob::ReadRawData(buf, buf_size, bytes_read);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool IsRedirectResponse(GURL* location,
|
|
|
|
int* http_status_code) OVERRIDE {
|
|
|
|
DCHECK(real_job_);
|
|
|
|
return real_job_->IsRedirectResponse(location, http_status_code);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual net::Filter* SetupFilter() const OVERRIDE {
|
|
|
|
DCHECK(real_job_);
|
|
|
|
return real_job_->SetupFilter();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool GetMimeType(std::string* mime_type) const OVERRIDE {
|
|
|
|
DCHECK(real_job_);
|
|
|
|
return real_job_->GetMimeType(mime_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool GetCharset(std::string* charset) OVERRIDE {
|
|
|
|
DCHECK(real_job_);
|
|
|
|
return real_job_->GetCharset(charset);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
enum JOB_TYPE {
|
2013-08-25 04:36:06 +00:00
|
|
|
REQUEST_ERROR_JOB,
|
|
|
|
REQUEST_STRING_JOB,
|
|
|
|
REQUEST_FILE_JOB,
|
2013-08-24 11:33:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
void GetJobTypeInUI() {
|
2013-08-24 11:46:38 +00:00
|
|
|
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
2013-08-24 12:18:12 +00:00
|
|
|
|
|
|
|
// Call the JS handler.
|
|
|
|
v8::HandleScope scope;
|
|
|
|
v8::Handle<v8::Value> argv[] = {
|
|
|
|
v8::String::New(request()->url().spec().c_str()),
|
|
|
|
v8::String::New(request()->referrer().c_str()),
|
|
|
|
};
|
|
|
|
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()) {
|
2013-08-25 04:36:06 +00:00
|
|
|
std::string data = *v8::String::Utf8Value(result);
|
|
|
|
content::BrowserThread::PostTask(
|
|
|
|
content::BrowserThread::IO,
|
|
|
|
FROM_HERE,
|
|
|
|
base::Bind(&AdapterRequestJob::CreateStringJobAndStart,
|
|
|
|
weak_factory_.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,
|
|
|
|
weak_factory_.GetWeakPtr(),
|
|
|
|
mime_type,
|
|
|
|
charset,
|
|
|
|
data));
|
|
|
|
return;
|
2013-08-25 08:06:29 +00:00
|
|
|
} 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,
|
|
|
|
weak_factory_.GetWeakPtr(),
|
|
|
|
path));
|
|
|
|
return;
|
2013-08-25 04:36:06 +00:00
|
|
|
}
|
2013-08-24 12:18:12 +00:00
|
|
|
}
|
|
|
|
|
2013-08-25 04:36:06 +00:00
|
|
|
// Fallback to the not implemented error.
|
2013-08-24 11:46:38 +00:00
|
|
|
content::BrowserThread::PostTask(
|
|
|
|
content::BrowserThread::IO,
|
|
|
|
FROM_HERE,
|
2013-08-25 04:36:06 +00:00
|
|
|
base::Bind(&AdapterRequestJob::CreateErrorJobAndStart,
|
|
|
|
weak_factory_.GetWeakPtr(),
|
|
|
|
net::ERR_NOT_IMPLEMENTED));
|
2013-08-24 11:33:08 +00:00
|
|
|
}
|
|
|
|
|
2013-08-25 04:36:06 +00:00
|
|
|
void CreateErrorJobAndStart(int error_code) {
|
2013-08-24 11:46:38 +00:00
|
|
|
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
2013-08-25 04:36:06 +00:00
|
|
|
|
|
|
|
type_ = REQUEST_ERROR_JOB;
|
2013-08-24 11:46:38 +00:00
|
|
|
real_job_ = new net::URLRequestErrorJob(
|
2013-08-25 04:36:06 +00:00
|
|
|
request(), network_delegate(), error_code);
|
|
|
|
real_job_->Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CreateStringJobAndStart(const std::string& mime_type,
|
|
|
|
const std::string& charset,
|
|
|
|
const std::string& data) {
|
|
|
|
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
|
|
|
|
|
|
|
type_ = REQUEST_STRING_JOB;
|
|
|
|
real_job_ = new URLRequestStringJob(
|
|
|
|
request(), network_delegate(), mime_type, charset, data);
|
2013-08-24 11:46:38 +00:00
|
|
|
real_job_->Start();
|
2013-08-24 11:33:08 +00:00
|
|
|
}
|
|
|
|
|
2013-08-25 08:06:29 +00:00
|
|
|
void CreateFileJobAndStart(const base::FilePath& path) {
|
|
|
|
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
|
|
|
|
|
|
|
type_ = REQUEST_FILE_JOB;
|
|
|
|
real_job_ = new net::URLRequestFileJob(request(), network_delegate(), path);
|
|
|
|
real_job_->Start();
|
|
|
|
}
|
|
|
|
|
2013-08-24 11:33:08 +00:00
|
|
|
scoped_refptr<net::URLRequestJob> real_job_;
|
|
|
|
|
2013-08-25 04:36:06 +00:00
|
|
|
// Type of the delegated url request job.
|
2013-08-24 11:33:08 +00:00
|
|
|
JOB_TYPE type_;
|
2013-08-24 12:32:12 +00:00
|
|
|
|
|
|
|
base::WeakPtrFactory<AdapterRequestJob> weak_factory_;
|
2013-08-24 11:33:08 +00:00
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(AdapterRequestJob);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Always return the same AdapterRequestJob 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.
|
2013-08-24 09:59:34 +00:00
|
|
|
class AdapterProtocolHandler
|
|
|
|
: public net::URLRequestJobFactory::ProtocolHandler {
|
|
|
|
public:
|
|
|
|
AdapterProtocolHandler() {}
|
|
|
|
virtual net::URLRequestJob* MaybeCreateJob(
|
|
|
|
net::URLRequest* request,
|
|
|
|
net::NetworkDelegate* network_delegate) const OVERRIDE {
|
2013-08-24 11:33:08 +00:00
|
|
|
return new AdapterRequestJob(request, network_delegate);
|
2013-08-24 09:59:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(AdapterProtocolHandler);
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2013-08-24 07:26:10 +00:00
|
|
|
// static
|
|
|
|
v8::Handle<v8::Value> Protocol::RegisterProtocol(const v8::Arguments& args) {
|
2013-08-24 08:38:19 +00:00
|
|
|
std::string scheme(*v8::String::Utf8Value(args[0]));
|
2013-08-24 12:18:12 +00:00
|
|
|
if (g_handlers.find(scheme) != g_handlers.end())
|
2013-08-24 08:38:19 +00:00
|
|
|
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");
|
2013-08-24 12:18:12 +00:00
|
|
|
g_handlers[scheme] = v8::Persistent<v8::Function>::New(
|
2013-08-24 08:38:19 +00:00
|
|
|
node::node_isolate, v8::Handle<v8::Function>::Cast(args[1]));
|
|
|
|
|
|
|
|
content::BrowserThread::PostTask(content::BrowserThread::IO,
|
2013-08-24 11:46:38 +00:00
|
|
|
FROM_HERE,
|
2013-08-24 08:38:19 +00:00
|
|
|
base::Bind(&RegisterProtocolInIO, scheme));
|
|
|
|
|
2013-08-24 07:26:10 +00:00
|
|
|
return v8::Undefined();
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
v8::Handle<v8::Value> Protocol::UnregisterProtocol(const v8::Arguments& args) {
|
2013-08-24 08:38:19 +00:00
|
|
|
std::string scheme(*v8::String::Utf8Value(args[0]));
|
|
|
|
|
|
|
|
// Erase the handler from map.
|
2013-08-24 12:18:12 +00:00
|
|
|
HandlersMap::iterator it(g_handlers.find(scheme));
|
|
|
|
if (it == g_handlers.end())
|
2013-08-24 08:38:19 +00:00
|
|
|
return node::ThrowError("The scheme has not been registered");
|
2013-08-24 12:18:12 +00:00
|
|
|
g_handlers.erase(it);
|
2013-08-24 08:38:19 +00:00
|
|
|
|
|
|
|
content::BrowserThread::PostTask(content::BrowserThread::IO,
|
2013-08-24 11:46:38 +00:00
|
|
|
FROM_HERE,
|
2013-08-24 08:38:19 +00:00
|
|
|
base::Bind(&UnregisterProtocolInIO, scheme));
|
|
|
|
|
2013-08-24 07:26:10 +00:00
|
|
|
return v8::Undefined();
|
|
|
|
}
|
|
|
|
|
2013-08-24 08:38:19 +00:00
|
|
|
// static
|
|
|
|
void Protocol::RegisterProtocolInIO(const std::string& scheme) {
|
|
|
|
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
2013-08-24 09:59:34 +00:00
|
|
|
net::URLRequestJobFactoryImpl* job_factory(GetRequestJobFactory());
|
|
|
|
job_factory->SetProtocolHandler(scheme, new AdapterProtocolHandler);
|
2013-08-24 08:38:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
void Protocol::UnregisterProtocolInIO(const std::string& scheme) {
|
|
|
|
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
2013-08-24 09:59:34 +00:00
|
|
|
net::URLRequestJobFactoryImpl* job_factory(GetRequestJobFactory());
|
|
|
|
job_factory->SetProtocolHandler(scheme, NULL);
|
2013-08-24 08:38:19 +00:00
|
|
|
}
|
|
|
|
|
2013-08-24 07:26:10 +00:00
|
|
|
// static
|
|
|
|
void Protocol::Initialize(v8::Handle<v8::Object> target) {
|
|
|
|
node::SetMethod(target, "registerProtocol", RegisterProtocol);
|
|
|
|
node::SetMethod(target, "unregisterProtocol", UnregisterProtocol);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace api
|
|
|
|
|
|
|
|
} // namespace atom
|
|
|
|
|
|
|
|
NODE_MODULE(atom_browser_protocol, atom::api::Protocol::Initialize)
|