diff --git a/atom.gyp b/atom.gyp index ca91c896f534..08885b3ef532 100644 --- a/atom.gyp +++ b/atom.gyp @@ -4,7 +4,7 @@ 'product_name%': 'Electron', 'company_name%': 'GitHub, Inc', 'company_abbr%': 'github', - 'version%': '0.30.5', + 'version%': '0.31.0', }, 'includes': [ 'filenames.gypi', @@ -144,7 +144,6 @@ 'destination': '<(PRODUCT_DIR)', 'files': [ '<@(copied_libraries)', - '<(libchromiumcontent_dir)/ffmpegsumo.dll', '<(libchromiumcontent_dir)/libEGL.dll', '<(libchromiumcontent_dir)/libGLESv2.dll', '<(libchromiumcontent_dir)/icudtl.dat', @@ -193,7 +192,6 @@ 'destination': '<(PRODUCT_DIR)', 'files': [ '<@(copied_libraries)', - '<(libchromiumcontent_dir)/libffmpegsumo.so', '<(libchromiumcontent_dir)/icudtl.dat', '<(libchromiumcontent_dir)/content_shell.pak', '<(libchromiumcontent_dir)/natives_blob.bin', @@ -226,8 +224,6 @@ # Defined in Chromium but not exposed in its gyp file. 'V8_USE_EXTERNAL_STARTUP_DATA', 'ENABLE_PLUGINS', - # Needed by Node. - 'NODE_WANT_INTERNALS=1', ], 'sources': [ '<@(lib_sources)', @@ -441,7 +437,6 @@ 'destination': '<(PRODUCT_DIR)/<(product_name) Framework.framework/Versions/A/Libraries', 'files': [ '<@(copied_libraries)', - '<(libchromiumcontent_dir)/ffmpegsumo.so', ], }, { @@ -462,6 +457,16 @@ '${BUILT_PRODUCTS_DIR}/<(product_name) Framework.framework/Versions/A/<(product_name) Framework', ], }, + { + 'postbuild_name': 'Fix path of ffmpeg', + 'action': [ + 'install_name_tool', + '-change', + '@loader_path/libffmpeg.dylib', + '@rpath/libffmpeg.dylib', + '${BUILT_PRODUCTS_DIR}/<(product_name) Framework.framework/Versions/A/<(product_name) Framework', + ], + }, { 'postbuild_name': 'Add symlinks for framework subdirectories', 'action': [ diff --git a/atom/app/node_main.cc b/atom/app/node_main.cc index 7446a0a71106..8823e583107c 100644 --- a/atom/app/node_main.cc +++ b/atom/app/node_main.cc @@ -5,9 +5,11 @@ #include "atom/app/node_main.h" #include "atom/browser/javascript_environment.h" -#include "atom/common/node_includes.h" #include "gin/array_buffer.h" #include "gin/public/isolate_holder.h" +#include "gin/v8_initializer.h" + +#include "atom/common/node_includes.h" namespace atom { @@ -19,7 +21,7 @@ int NodeMain(int argc, char *argv[]) { int exit_code = 1; { - gin::IsolateHolder::LoadV8Snapshot(); + gin::V8Initializer::LoadV8Snapshot(); gin::IsolateHolder::Initialize( gin::IsolateHolder::kNonStrictMode, gin::ArrayBufferAllocator::SharedInstance()); diff --git a/atom/browser/api/atom_api_protocol.cc b/atom/browser/api/atom_api_protocol.cc index d7d308b306ed..f77f3229ed0a 100644 --- a/atom/browser/api/atom_api_protocol.cc +++ b/atom/browser/api/atom_api_protocol.cc @@ -7,15 +7,12 @@ #include "atom/browser/atom_browser_client.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" -#include "atom/browser/api/atom_api_session.h" -#include "atom/browser/net/adapter_request_job.h" -#include "atom/browser/net/atom_url_request_job_factory.h" +#include "atom/browser/net/url_request_async_asar_job.h" +#include "atom/browser/net/url_request_buffer_job.h" +#include "atom/browser/net/url_request_fetch_job.h" +#include "atom/browser/net/url_request_string_job.h" #include "atom/common/native_mate_converters/callback.h" -#include "atom/common/native_mate_converters/file_path_converter.h" -#include "atom/common/native_mate_converters/gurl_converter.h" -#include "content/public/browser/browser_thread.h" #include "native_mate/dictionary.h" -#include "net/url_request/url_request_context.h" #include "atom/common/node_includes.h" @@ -41,230 +38,35 @@ namespace atom { namespace api { -namespace { - -typedef net::URLRequestJobFactory::ProtocolHandler ProtocolHandler; - -scoped_refptr BufferToRefCountedBytes( - v8::Local buf) { - scoped_refptr data(new base::RefCountedBytes); - auto start = reinterpret_cast(node::Buffer::Data(buf)); - data->data().assign(start, start + node::Buffer::Length(buf)); - return data; -} - -class CustomProtocolRequestJob : public AdapterRequestJob { - public: - CustomProtocolRequestJob(Protocol* registry, - ProtocolHandler* protocol_handler, - net::URLRequest* request, - net::NetworkDelegate* network_delegate) - : AdapterRequestJob(protocol_handler, request, network_delegate), - registry_(registry) { - } - - void GetJobTypeInUI(const Protocol::JsProtocolHandler& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - v8::Locker locker(registry_->isolate()); - v8::HandleScope handle_scope(registry_->isolate()); - - // Call the JS handler. - v8::Local result = callback.Run(request()); - - // Determine the type of the job we are going to create. - if (result->IsString()) { - std::string data = mate::V8ToString(result); - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateStringJobAndStart, - GetWeakPtr(), "text/plain", "UTF-8", data)); - return; - } else if (result->IsObject()) { - v8::Local obj = result->ToObject(); - mate::Dictionary dict(registry_->isolate(), obj); - std::string name = mate::V8ToString(obj->GetConstructorName()); - if (name == "RequestStringJob") { - std::string mime_type, charset, data; - dict.Get("mimeType", &mime_type); - dict.Get("charset", &charset); - dict.Get("data", &data); - - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateStringJobAndStart, - GetWeakPtr(), mime_type, charset, data)); - return; - } else if (name == "RequestBufferJob") { - std::string mime_type, encoding; - v8::Local buffer; - dict.Get("mimeType", &mime_type); - dict.Get("encoding", &encoding); - dict.Get("data", &buffer); - - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateBufferJobAndStart, - GetWeakPtr(), mime_type, encoding, - BufferToRefCountedBytes(buffer))); - return; - } else if (name == "RequestFileJob") { - base::FilePath path; - dict.Get("path", &path); - - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateFileJobAndStart, - GetWeakPtr(), path)); - return; - } else if (name == "RequestErrorJob") { - int error = net::ERR_NOT_IMPLEMENTED; - dict.Get("error", &error); - - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateErrorJobAndStart, - GetWeakPtr(), error)); - return; - } else if (name == "RequestHttpJob") { - GURL url; - std::string method, referrer; - dict.Get("url", &url); - dict.Get("method", &method); - dict.Get("referrer", &referrer); - - v8::Local value; - mate::Handle session; - scoped_refptr request_context_getter; - // "session" null -> pass nullptr; - // "session" a Session object -> use passed session. - // "session" undefined -> use current session; - if (dict.Get("session", &session)) - request_context_getter = - session->browser_context()->GetRequestContext(); - else if (dict.Get("session", &value) && value->IsNull()) - request_context_getter = nullptr; - else - request_context_getter = registry_->request_context_getter(); - - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateHttpJobAndStart, GetWeakPtr(), - request_context_getter, url, method, referrer)); - return; - } - } - - // Try the default protocol handler if we have. - if (default_protocol_handler()) { - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateJobFromProtocolHandlerAndStart, - GetWeakPtr())); - return; - } - - // Fallback to the not implemented error. - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AdapterRequestJob::CreateErrorJobAndStart, - GetWeakPtr(), net::ERR_NOT_IMPLEMENTED)); - } - - // AdapterRequestJob: - void GetJobType() override { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&CustomProtocolRequestJob::GetJobTypeInUI, - base::Unretained(this), - registry_->GetProtocolHandler(request()->url().scheme()))); - } - - private: - Protocol* registry_; // Weak, the Protocol class is expected to live forever. -}; - -// 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: - CustomProtocolHandler(api::Protocol* registry, - ProtocolHandler* protocol_handler = NULL) - : registry_(registry), protocol_handler_(protocol_handler) { - } - - net::URLRequestJob* MaybeCreateJob( - net::URLRequest* request, - net::NetworkDelegate* network_delegate) const override { - return new CustomProtocolRequestJob(registry_, protocol_handler_.get(), - request, network_delegate); - } - - ProtocolHandler* ReleaseDefaultProtocolHandler() { - return protocol_handler_.release(); - } - - ProtocolHandler* original_handler() { return protocol_handler_.get(); } - - private: - Protocol* registry_; // Weak, the Protocol class is expected to live forever. - scoped_ptr protocol_handler_; - - DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler); -}; - -std::string ConvertErrorCode(int error_code) { - switch (error_code) { - case Protocol::ERR_SCHEME_REGISTERED: - return "The Scheme is already registered"; - case Protocol::ERR_SCHEME_UNREGISTERED: - return "The Scheme has not been registered"; - case Protocol::ERR_SCHEME_INTERCEPTED: - return "There is no protocol handler to intercept"; - case Protocol::ERR_SCHEME_UNINTERCEPTED: - return "The protocol is not intercepted"; - case Protocol::ERR_NO_SCHEME: - return "The Scheme does not exist."; - case Protocol::ERR_SCHEME: - return "Cannot intercept custom protocols"; - default: - NOTREACHED(); - return std::string(); - } -} - -} // namespace - Protocol::Protocol(AtomBrowserContext* browser_context) : request_context_getter_(browser_context->GetRequestContext()), job_factory_(browser_context->job_factory()) { CHECK(job_factory_); } -Protocol::JsProtocolHandler Protocol::GetProtocolHandler( - const std::string& scheme) { - return protocol_handlers_[scheme]; -} - -void Protocol::OnIOActionCompleted(const JsCompletionCallback& callback, - int error) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - v8::Locker locker(isolate()); - v8::HandleScope handle_scope(isolate()); - - if (error) { - callback.Run(v8::Exception::Error( - mate::StringToV8(isolate(), ConvertErrorCode(error)))); - return; - } - - callback.Run(v8::Null(isolate())); -} - mate::ObjectTemplateBuilder Protocol::GetObjectTemplateBuilder( v8::Isolate* isolate) { return mate::ObjectTemplateBuilder(isolate) .SetMethod("registerStandardSchemes", &Protocol::RegisterStandardSchemes) - .SetMethod("isHandledProtocol", &Protocol::IsHandledProtocol) - .SetMethod("_registerProtocol", &Protocol::RegisterProtocol) - .SetMethod("_unregisterProtocol", &Protocol::UnregisterProtocol) - .SetMethod("_interceptProtocol", &Protocol::InterceptProtocol) - .SetMethod("_uninterceptProtocol", &Protocol::UninterceptProtocol); + .SetMethod("registerStringProtocol", + &Protocol::RegisterProtocol) + .SetMethod("registerBufferProtocol", + &Protocol::RegisterProtocol) + .SetMethod("registerFileProtocol", + &Protocol::RegisterProtocol) + .SetMethod("registerHttpProtocol", + &Protocol::RegisterProtocol) + .SetMethod("unregisterProtocol", &Protocol::UnregisterProtocol) + .SetMethod("isProtocolHandled", &Protocol::IsProtocolHandled) + .SetMethod("interceptStringProtocol", + &Protocol::InterceptProtocol) + .SetMethod("interceptBufferProtocol", + &Protocol::InterceptProtocol) + .SetMethod("interceptFileProtocol", + &Protocol::InterceptProtocol) + .SetMethod("interceptHttpProtocol", + &Protocol::InterceptProtocol) + .SetMethod("uninterceptProtocol", &Protocol::UninterceptProtocol); } void Protocol::RegisterStandardSchemes( @@ -272,131 +74,86 @@ void Protocol::RegisterStandardSchemes( atom::AtomBrowserClient::SetCustomSchemes(schemes); } -void Protocol::IsHandledProtocol(const std::string& scheme, - const net::CompletionCallback& callback) { - BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE, - base::Bind(&AtomURLRequestJobFactory::IsHandledProtocol, - base::Unretained(job_factory_), scheme), +void Protocol::UnregisterProtocol( + const std::string& scheme, mate::Arguments* args) { + CompletionCallback callback; + args->GetNext(&callback); + content::BrowserThread::PostTaskAndReplyWithResult( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&Protocol::UnregisterProtocolInIO, + base::Unretained(this), scheme), + base::Bind(&Protocol::OnIOCompleted, + base::Unretained(this), callback)); +} + +Protocol::ProtocolError Protocol::UnregisterProtocolInIO( + const std::string& scheme) { + if (!job_factory_->HasProtocolHandler(scheme)) + return PROTOCOL_NOT_REGISTERED; + job_factory_->SetProtocolHandler(scheme, nullptr); + return PROTOCOL_OK; +} + +void Protocol::IsProtocolHandled(const std::string& scheme, + const BooleanCallback& callback) { + content::BrowserThread::PostTaskAndReplyWithResult( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&Protocol::IsProtocolHandledInIO, + base::Unretained(this), scheme), callback); } -void Protocol::RegisterProtocol(v8::Isolate* isolate, - const std::string& scheme, - const JsProtocolHandler& handler, - const JsCompletionCallback& callback) { - BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE, - base::Bind(&Protocol::RegisterProtocolInIO, - base::Unretained(this), scheme, handler), - base::Bind(&Protocol::OnIOActionCompleted, - base::Unretained(this), callback)); +bool Protocol::IsProtocolHandledInIO(const std::string& scheme) { + return job_factory_->IsHandledProtocol(scheme); } -void Protocol::UnregisterProtocol(v8::Isolate* isolate, - const std::string& scheme, - const JsCompletionCallback& callback) { - BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE, - base::Bind(&Protocol::UnregisterProtocolInIO, - base::Unretained(this), scheme), - base::Bind(&Protocol::OnIOActionCompleted, - base::Unretained(this), callback)); -} - -void Protocol::InterceptProtocol(v8::Isolate* isolate, - const std::string& scheme, - const JsProtocolHandler& handler, - const JsCompletionCallback& callback) { - BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE, - base::Bind(&Protocol::InterceptProtocolInIO, - base::Unretained(this), scheme, handler), - base::Bind(&Protocol::OnIOActionCompleted, - base::Unretained(this), callback)); -} - -void Protocol::UninterceptProtocol(v8::Isolate* isolate, - const std::string& scheme, - const JsCompletionCallback& callback) { - BrowserThread::PostTaskAndReplyWithResult(BrowserThread::IO, FROM_HERE, +void Protocol::UninterceptProtocol( + const std::string& scheme, mate::Arguments* args) { + CompletionCallback callback; + args->GetNext(&callback); + content::BrowserThread::PostTaskAndReplyWithResult( + content::BrowserThread::IO, FROM_HERE, base::Bind(&Protocol::UninterceptProtocolInIO, base::Unretained(this), scheme), - base::Bind(&Protocol::OnIOActionCompleted, + base::Bind(&Protocol::OnIOCompleted, base::Unretained(this), callback)); } -int Protocol::RegisterProtocolInIO(const std::string& scheme, - const JsProtocolHandler& handler) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - if (ContainsKey(protocol_handlers_, scheme) || - job_factory_->IsHandledProtocol(scheme)) { - return ERR_SCHEME_REGISTERED; - } - - protocol_handlers_[scheme] = handler; - job_factory_->SetProtocolHandler(scheme, new CustomProtocolHandler(this)); - - return OK; +Protocol::ProtocolError Protocol::UninterceptProtocolInIO( + const std::string& scheme) { + if (!original_protocols_.contains(scheme)) + return PROTOCOL_NOT_INTERCEPTED; + job_factory_->ReplaceProtocol(scheme, + original_protocols_.take_and_erase(scheme)); + return PROTOCOL_OK; } -int Protocol::UnregisterProtocolInIO(const std::string& scheme) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); +void Protocol::OnIOCompleted( + const CompletionCallback& callback, ProtocolError error) { + // The completion callback is optional. + if (callback.is_null()) + return; - ProtocolHandlersMap::iterator it(protocol_handlers_.find(scheme)); - if (it == protocol_handlers_.end()) { - return ERR_SCHEME_UNREGISTERED; + v8::Locker locker(isolate()); + v8::HandleScope handle_scope(isolate()); + + if (error == PROTOCOL_OK) { + callback.Run(v8::Null(isolate())); + } else { + std::string str = ErrorCodeToString(error); + callback.Run(v8::Exception::Error(mate::StringToV8(isolate(), str))); } - - protocol_handlers_.erase(it); - job_factory_->SetProtocolHandler(scheme, NULL); - - return OK; } -int Protocol::InterceptProtocolInIO(const std::string& scheme, - const JsProtocolHandler& handler) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - // Force the request context to initialize, otherwise we might have nothing - // to intercept. - request_context_getter_->GetURLRequestContext(); - - if (!job_factory_->HasProtocolHandler(scheme)) - return ERR_NO_SCHEME; - - if (ContainsKey(protocol_handlers_, scheme)) - return ERR_SCHEME; - - protocol_handlers_[scheme] = handler; - ProtocolHandler* original_handler = job_factory_->GetProtocolHandler(scheme); - if (original_handler == nullptr) { - return ERR_SCHEME_INTERCEPTED; +std::string Protocol::ErrorCodeToString(ProtocolError error) { + switch (error) { + case PROTOCOL_FAIL: return "Failed to manipulate protocol factory"; + case PROTOCOL_REGISTERED: return "The scheme has been registred"; + case PROTOCOL_NOT_REGISTERED: return "The scheme has not been registred"; + case PROTOCOL_INTERCEPTED: return "The scheme has been intercepted"; + case PROTOCOL_NOT_INTERCEPTED: return "The scheme has not been intercepted"; + default: return "Unexpected error"; } - - job_factory_->ReplaceProtocol( - scheme, new CustomProtocolHandler(this, original_handler)); - - return OK; -} - -int Protocol::UninterceptProtocolInIO(const std::string& scheme) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - ProtocolHandlersMap::iterator it(protocol_handlers_.find(scheme)); - if (it == protocol_handlers_.end()) - return ERR_SCHEME_UNREGISTERED; - - protocol_handlers_.erase(it); - CustomProtocolHandler* handler = static_cast( - job_factory_->GetProtocolHandler(scheme)); - if (handler->original_handler() == nullptr) { - return ERR_SCHEME_UNINTERCEPTED; - } - - // 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); - - return OK; } // static diff --git a/atom/browser/api/atom_api_protocol.h b/atom/browser/api/atom_api_protocol.h index 4dec17a74340..966fcd8726b3 100644 --- a/atom/browser/api/atom_api_protocol.h +++ b/atom/browser/api/atom_api_protocol.h @@ -9,10 +9,14 @@ #include #include -#include "atom/browser/api/event_emitter.h" +#include "atom/browser/net/atom_url_request_job_factory.h" #include "base/callback.h" +#include "base/containers/scoped_ptr_hash_map.h" +#include "content/public/browser/browser_thread.h" +#include "native_mate/arguments.h" +#include "native_mate/dictionary.h" #include "native_mate/handle.h" -#include "net/base/completion_callback.h" +#include "native_mate/wrappable.h" namespace net { class URLRequest; @@ -26,31 +30,16 @@ class AtomURLRequestJobFactory; namespace api { -class Protocol : public mate::EventEmitter { +class Protocol : public mate::Wrappable { public: - using JsProtocolHandler = - base::Callback(const net::URLRequest*)>; - using JsCompletionCallback = base::Callback)>; - - enum { - OK = 0, - ERR_SCHEME_REGISTERED, - ERR_SCHEME_UNREGISTERED, - ERR_SCHEME_INTERCEPTED, - ERR_SCHEME_UNINTERCEPTED, - ERR_NO_SCHEME, - ERR_SCHEME - }; + using Handler = + base::Callback)>; + using CompletionCallback = base::Callback)>; + using BooleanCallback = base::Callback; static mate::Handle Create( v8::Isolate* isolate, AtomBrowserContext* browser_context); - JsProtocolHandler GetProtocolHandler(const std::string& scheme); - - net::URLRequestContextGetter* request_context_getter() { - return request_context_getter_.get(); - } - protected: explicit Protocol(AtomBrowserContext* browser_context); @@ -59,48 +48,139 @@ class Protocol : public mate::EventEmitter { v8::Isolate* isolate); private: - typedef std::map ProtocolHandlersMap; + // Possible errors. + enum ProtocolError { + PROTOCOL_OK, // no error + PROTOCOL_FAIL, // operation failed, should never occur + PROTOCOL_REGISTERED, + PROTOCOL_NOT_REGISTERED, + PROTOCOL_INTERCEPTED, + PROTOCOL_NOT_INTERCEPTED, + }; - // Callback called after performing action on IO thread. - void OnIOActionCompleted(const JsCompletionCallback& callback, - int error); + // The protocol handler that will create a protocol handler for certain + // request job. + template + class CustomProtocolHandler + : public net::URLRequestJobFactory::ProtocolHandler { + public: + CustomProtocolHandler( + v8::Isolate* isolate, + scoped_refptr request_context, + const Handler& handler) + : isolate_(isolate), + request_context_(request_context), + handler_(handler) {} + ~CustomProtocolHandler() override {} + + net::URLRequestJob* MaybeCreateJob( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const override { + RequestJob* request_job = new RequestJob(request, network_delegate); + request_job->SetHandlerInfo(isolate_, request_context_, handler_); + return request_job; + } + + private: + v8::Isolate* isolate_; + scoped_refptr request_context_; + Protocol::Handler handler_; + + DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler); + }; // Register schemes to standard scheme list. void RegisterStandardSchemes(const std::vector& schemes); - // Returns whether a scheme has been registered. - void IsHandledProtocol(const std::string& scheme, - const net::CompletionCallback& callback); + // Register the protocol with certain request job. + template + void RegisterProtocol(const std::string& scheme, + const Handler& handler, + mate::Arguments* args) { + CompletionCallback callback; + args->GetNext(&callback); + content::BrowserThread::PostTaskAndReplyWithResult( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&Protocol::RegisterProtocolInIO, + base::Unretained(this), scheme, handler), + base::Bind(&Protocol::OnIOCompleted, + base::Unretained(this), callback)); + } + template + ProtocolError RegisterProtocolInIO(const std::string& scheme, + const Handler& handler) { + if (job_factory_->IsHandledProtocol(scheme)) + return PROTOCOL_REGISTERED; + scoped_ptr> protocol_handler( + new CustomProtocolHandler( + isolate(), request_context_getter_, handler)); + if (job_factory_->SetProtocolHandler(scheme, protocol_handler.Pass())) + return PROTOCOL_OK; + else + return PROTOCOL_FAIL; + } - // Register/unregister an networking |scheme| which would be handled by - // |callback|. - void RegisterProtocol(v8::Isolate* isolate, - const std::string& scheme, - const JsProtocolHandler& handler, - const JsCompletionCallback& callback); - void UnregisterProtocol(v8::Isolate* isolate, const std::string& scheme, - const JsCompletionCallback& callback); + // Unregister the protocol handler that handles |scheme|. + void UnregisterProtocol(const std::string& scheme, mate::Arguments* args); + ProtocolError UnregisterProtocolInIO(const std::string& scheme); - // Intercept/unintercept an existing protocol handler. - void InterceptProtocol(v8::Isolate* isolate, - const std::string& scheme, - const JsProtocolHandler& handler, - const JsCompletionCallback& callback); - void UninterceptProtocol(v8::Isolate* isolate, const std::string& scheme, - const JsCompletionCallback& callback); + // Whether the protocol has handler registered. + void IsProtocolHandled(const std::string& scheme, + const BooleanCallback& callback); + bool IsProtocolHandledInIO(const std::string& scheme); - // The networking related operations have to be done in IO thread. - int RegisterProtocolInIO(const std::string& scheme, - const JsProtocolHandler& handler); - int UnregisterProtocolInIO(const std::string& scheme); - int InterceptProtocolInIO(const std::string& scheme, - const JsProtocolHandler& handler); - int UninterceptProtocolInIO(const std::string& scheme); + // Replace the protocol handler with a new one. + template + void InterceptProtocol(const std::string& scheme, + const Handler& handler, + mate::Arguments* args) { + CompletionCallback callback; + args->GetNext(&callback); + content::BrowserThread::PostTaskAndReplyWithResult( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&Protocol::InterceptProtocolInIO, + base::Unretained(this), scheme, handler), + base::Bind(&Protocol::OnIOCompleted, + base::Unretained(this), callback)); + } + template + ProtocolError InterceptProtocolInIO(const std::string& scheme, + const Handler& handler) { + if (!job_factory_->IsHandledProtocol(scheme)) + return PROTOCOL_NOT_REGISTERED; + // It is possible a protocol is handled but can not be intercepted. + if (!job_factory_->HasProtocolHandler(scheme)) + return PROTOCOL_FAIL; + if (ContainsKey(original_protocols_, scheme)) + return PROTOCOL_INTERCEPTED; + scoped_ptr> protocol_handler( + new CustomProtocolHandler( + isolate(), request_context_getter_, handler)); + original_protocols_.set( + scheme, + job_factory_->ReplaceProtocol(scheme, protocol_handler.Pass())); + return PROTOCOL_OK; + } + + // Restore the |scheme| to its original protocol handler. + void UninterceptProtocol(const std::string& scheme, mate::Arguments* args); + ProtocolError UninterceptProtocolInIO(const std::string& scheme); + + // Convert error code to JS exception and call the callback. + void OnIOCompleted(const CompletionCallback& callback, ProtocolError error); + + // Convert error code to string. + std::string ErrorCodeToString(ProtocolError error); scoped_refptr request_context_getter_; - AtomURLRequestJobFactory* job_factory_; - ProtocolHandlersMap protocol_handlers_; + // Map that stores the original protocols of schemes. + using OriginalProtocolsMap = base::ScopedPtrHashMap< + std::string, + scoped_ptr>; + OriginalProtocolsMap original_protocols_; + + AtomURLRequestJobFactory* job_factory_; // weak ref DISALLOW_COPY_AND_ASSIGN(Protocol); }; diff --git a/atom/browser/api/event_emitter.cc b/atom/browser/api/event_emitter.cc index aa0b6c8781a3..be7018dafa44 100644 --- a/atom/browser/api/event_emitter.cc +++ b/atom/browser/api/event_emitter.cc @@ -56,7 +56,7 @@ v8::Local EventEmitter::CreateJSEvent( v8::Local EventEmitter::CreateCustomEvent( v8::Isolate* isolate, v8::Local custom_event) { v8::Local event = CreateEventObject(isolate); - event->SetPrototype(custom_event->CreationContext(), custom_event); + (void)event->SetPrototype(custom_event->CreationContext(), custom_event); mate::Dictionary(isolate, event).Set("sender", GetWrapper(isolate)); return event; } diff --git a/atom/browser/api/lib/protocol.coffee b/atom/browser/api/lib/protocol.coffee index 4a661523509c..13f2a6d10270 100644 --- a/atom/browser/api/lib/protocol.coffee +++ b/atom/browser/api/lib/protocol.coffee @@ -2,63 +2,23 @@ app = require 'app' throw new Error('Can not initialize protocol module before app is ready') unless app.isReady() protocol = process.atomBinding('protocol').protocol -EventEmitter = require('events').EventEmitter -protocol.__proto__ = EventEmitter.prototype - -GetWrappedCallback = (scheme, callback, notification) -> - wrappedCallback = (error) -> - if not callback? - if error - throw error - else - protocol.emit notification, scheme - else - callback error, scheme - -# Compatibility with old api. +# Warn about removed APIs. +logAndThrow = (callback, message) -> + console.error message + if callback then callback(new Error(message)) else throw new Error(message) protocol.registerProtocol = (scheme, handler, callback) -> - protocol._registerProtocol scheme, handler, GetWrappedCallback(scheme, callback, 'registered') - -protocol.unregisterProtocol = (scheme, callback) -> - protocol._unregisterProtocol scheme, GetWrappedCallback(scheme, callback, 'unregistered') - + logAndThrow callback, + 'registerProtocol API has been replaced by the + register[File/Http/Buffer/String]Protocol API family, please + switch to the new APIs.' +protocol.isHandledProtocol = (scheme, callback) -> + logAndThrow callback, + 'isHandledProtocol API has been replaced by isProtocolHandled.' protocol.interceptProtocol = (scheme, handler, callback) -> - protocol._interceptProtocol scheme, handler, GetWrappedCallback(scheme, callback, 'intercepted') - -protocol.uninterceptProtocol = (scheme, callback) -> - protocol._uninterceptProtocol scheme, GetWrappedCallback(scheme, callback, 'unintercepted') - -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.RequestBufferJob = -class RequestBufferJob - constructor: ({mimeType, encoding, data}) -> - if not data instanceof Buffer - throw new TypeError('Data should be Buffer') - - @mimeType = mimeType ? 'application/octet-stream' - @encoding = encoding ? 'utf8' - @data = new Buffer(data) - -protocol.RequestFileJob = -class RequestFileJob - constructor: (@path) -> - -protocol.RequestErrorJob = -class RequestErrorJob - constructor: (@error) -> - -protocol.RequestHttpJob = -class RequestHttpJob - constructor: ({@session, @url, @method, @referrer}) -> + logAndThrow callback, + 'interceptProtocol API has been replaced by the + intercept[File/Http/Buffer/String]Protocol API family, please + switch to the new APIs.' module.exports = protocol diff --git a/atom/browser/atom_browser_client.cc b/atom/browser/atom_browser_client.cc index 65fd7cd031dd..8db71fd3030d 100644 --- a/atom/browser/atom_browser_client.cc +++ b/atom/browser/atom_browser_client.cc @@ -20,6 +20,7 @@ #include "atom/common/options_switches.h" #include "base/command_line.h" #include "base/files/file_util.h" +#include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/string_number_conversions.h" #include "chrome/browser/printing/printing_message_filter.h" @@ -58,20 +59,19 @@ enum ProcessOwner { OWNER_GUEST_WEB_CONTENTS, OWNER_NONE, // it might be devtools though. }; + ProcessOwner GetProcessOwner(int process_id, NativeWindow** window, WebViewManager::WebViewInfo* info) { - auto web_contents = content::WebContents::FromRenderViewHost( + content::WebContents* web_contents = content::WebContents::FromRenderViewHost( content::RenderViewHost::FromID(process_id, kDefaultRoutingID)); if (!web_contents) return OWNER_NONE; // First search for NativeWindow. - for (auto native_window : *WindowList::GetInstance()) - if (web_contents == native_window->web_contents()) { - *window = native_window; - return OWNER_NATIVE_WINDOW; - } + *window = NativeWindow::FromWebContents(web_contents); + if (*window) + return OWNER_NATIVE_WINDOW; // Then search for guest WebContents. if (WebViewManager::GetInfoForWebContents(web_contents, info)) @@ -185,6 +185,13 @@ void AtomBrowserClient::OverrideSiteInstanceForNavigation( return; *new_instance = content::SiteInstance::CreateForURL(browser_context, url); + + // Remember the original renderer process of the pending renderer process. + auto current_process = current_instance->GetProcess(); + auto pending_process = (*new_instance)->GetProcess(); + pending_processes_[pending_process->GetID()] = current_process->GetID(); + // Clear the entry in map when process ends. + current_process->AddObserver(this); } void AtomBrowserClient::AppendExtraCommandLineSwitches( @@ -208,6 +215,10 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches( } #endif + // If the process is a pending process, we should use the old one. + if (ContainsKey(pending_processes_, process_id)) + process_id = pending_processes_[process_id]; + NativeWindow* window; WebViewManager::WebViewInfo info; ProcessOwner owner = GetProcessOwner(process_id, &window, &info); @@ -264,4 +275,15 @@ brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts( return new AtomBrowserMainParts; } +void AtomBrowserClient::RenderProcessHostDestroyed( + content::RenderProcessHost* host) { + int process_id = host->GetID(); + for (const auto& entry : pending_processes_) { + if (entry.first == process_id || entry.second == process_id) { + pending_processes_.erase(entry.first); + break; + } + } +} + } // namespace atom diff --git a/atom/browser/atom_browser_client.h b/atom/browser/atom_browser_client.h index f403a519d2f0..a0217efede9f 100644 --- a/atom/browser/atom_browser_client.h +++ b/atom/browser/atom_browser_client.h @@ -5,10 +5,12 @@ #ifndef ATOM_BROWSER_ATOM_BROWSER_CLIENT_H_ #define ATOM_BROWSER_ATOM_BROWSER_CLIENT_H_ +#include #include #include #include "brightray/browser/browser_client.h" +#include "content/public/browser/render_process_host_observer.h" namespace content { class QuotaPermissionContext; @@ -21,7 +23,8 @@ class SSLCertRequestInfo; namespace atom { -class AtomBrowserClient : public brightray::BrowserClient { +class AtomBrowserClient : public brightray::BrowserClient, + public content::RenderProcessHostObserver { public: AtomBrowserClient(); virtual ~AtomBrowserClient(); @@ -54,10 +57,17 @@ class AtomBrowserClient : public brightray::BrowserClient { net::SSLCertRequestInfo* cert_request_info, scoped_ptr delegate) override; - private: + // brightray::BrowserClient: brightray::BrowserMainParts* OverrideCreateBrowserMainParts( const content::MainFunctionParams&) override; + // content::RenderProcessHostObserver: + void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; + + private: + // pending_render_process => current_render_process. + std::map pending_processes_; + DISALLOW_COPY_AND_ASSIGN(AtomBrowserClient); }; diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index f04fbca747e6..583a6324a8f5 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -85,32 +85,37 @@ net::URLRequestJobFactory* AtomBrowserContext::CreateURLRequestJobFactory( content::URLRequestInterceptorScopedVector* interceptors) { scoped_ptr job_factory(job_factory_); - for (content::ProtocolHandlerMap::iterator it = handlers->begin(); - it != handlers->end(); ++it) - job_factory->SetProtocolHandler(it->first, it->second.release()); + for (auto& it : *handlers) { + job_factory->SetProtocolHandler(it.first, + make_scoped_ptr(it.second.release())); + } handlers->clear(); job_factory->SetProtocolHandler( - url::kDataScheme, new net::DataProtocolHandler); + url::kDataScheme, make_scoped_ptr(new net::DataProtocolHandler)); job_factory->SetProtocolHandler( - url::kFileScheme, new asar::AsarProtocolHandler( + url::kFileScheme, make_scoped_ptr(new asar::AsarProtocolHandler( BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior( - base::SequencedWorkerPool::SKIP_ON_SHUTDOWN))); + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)))); job_factory->SetProtocolHandler( - url::kHttpScheme, new HttpProtocolHandler(url::kHttpScheme)); + url::kHttpScheme, + make_scoped_ptr(new HttpProtocolHandler(url::kHttpScheme))); job_factory->SetProtocolHandler( - url::kHttpsScheme, new HttpProtocolHandler(url::kHttpsScheme)); + url::kHttpsScheme, + make_scoped_ptr(new HttpProtocolHandler(url::kHttpsScheme))); job_factory->SetProtocolHandler( - url::kWsScheme, new HttpProtocolHandler(url::kWsScheme)); + url::kWsScheme, + make_scoped_ptr(new HttpProtocolHandler(url::kWsScheme))); job_factory->SetProtocolHandler( - url::kWssScheme, new HttpProtocolHandler(url::kWssScheme)); + url::kWssScheme, + make_scoped_ptr(new HttpProtocolHandler(url::kWssScheme))); - auto host_resolver = url_request_context_getter() - ->GetURLRequestContext() - ->host_resolver(); + auto host_resolver = + url_request_context_getter()->GetURLRequestContext()->host_resolver(); job_factory->SetProtocolHandler( - url::kFtpScheme, new net::FtpProtocolHandler( - new net::FtpNetworkLayer(host_resolver))); + url::kFtpScheme, + make_scoped_ptr(new net::FtpProtocolHandler( + new net::FtpNetworkLayer(host_resolver)))); // Set up interceptors in the reverse order. scoped_ptr top_job_factory = job_factory.Pass(); diff --git a/atom/browser/javascript_environment.cc b/atom/browser/javascript_environment.cc index 887c59353ae4..3788fcad8b68 100644 --- a/atom/browser/javascript_environment.cc +++ b/atom/browser/javascript_environment.cc @@ -5,6 +5,7 @@ #include "atom/browser/javascript_environment.h" #include "gin/array_buffer.h" +#include "gin/v8_initializer.h" namespace atom { @@ -19,7 +20,7 @@ JavascriptEnvironment::JavascriptEnvironment() } bool JavascriptEnvironment::Initialize() { - gin::IsolateHolder::LoadV8Snapshot(); + gin::V8Initializer::LoadV8Snapshot(); gin::IsolateHolder::Initialize(gin::IsolateHolder::kNonStrictMode, gin::ArrayBufferAllocator::SharedInstance()); return true; diff --git a/atom/browser/lib/chrome-extension.coffee b/atom/browser/lib/chrome-extension.coffee index 884a9e25d6eb..df0d717cd9dd 100644 --- a/atom/browser/lib/chrome-extension.coffee +++ b/atom/browser/lib/chrome-extension.coffee @@ -64,14 +64,16 @@ app.once 'ready', -> catch e # The chrome-extension: can map a extension URL request to real file path. - protocol.registerProtocol 'chrome-extension', (request) -> + chromeExtensionHandler = (request, callback) -> parsed = url.parse request.url - return unless parsed.hostname and parsed.path? - return unless /extension-\d+/.test parsed.hostname + return callback() unless parsed.hostname and parsed.path? + return callback() unless /extension-\d+/.test parsed.hostname directory = getPathForHost parsed.hostname - return unless directory? - return new protocol.RequestFileJob(path.join(directory, parsed.path)) + return callback() unless directory? + callback path.join(directory, parsed.path) + protocol.registerFileProtocol 'chrome-extension', chromeExtensionHandler, (error) -> + console.error 'Unable to register chrome-extension protocol' if error BrowserWindow::_loadDevToolsExtensions = (extensionInfoArray) -> @devToolsWebContents?.executeJavaScript "DevToolsAPI.addExtensions(#{JSON.stringify(extensionInfoArray)});" diff --git a/atom/browser/net/adapter_request_job.cc b/atom/browser/net/adapter_request_job.cc deleted file mode 100644 index ca7dcf2e566b..000000000000 --- a/atom/browser/net/adapter_request_job.cc +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2013 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/net/adapter_request_job.h" - -#include "base/threading/sequenced_worker_pool.h" -#include "atom/browser/net/url_request_buffer_job.h" -#include "atom/browser/net/url_request_fetch_job.h" -#include "atom/browser/net/url_request_string_job.h" -#include "atom/browser/net/asar/url_request_asar_job.h" -#include "atom/common/asar/asar_util.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_.get()); - GetJobType(); -} - -void AdapterRequestJob::Kill() { - if (real_job_.get()) // Kill could happen when real_job_ is created. - real_job_->Kill(); -} - -bool AdapterRequestJob::ReadRawData(net::IOBuffer* buf, - int buf_size, - int *bytes_read) { - DCHECK(!real_job_.get()); - // Read post-filtered data if available. - if (real_job_->HasFilter()) - return real_job_->Read(buf, buf_size, bytes_read); - else - return real_job_->ReadRawData(buf, buf_size, bytes_read); -} - -bool AdapterRequestJob::IsRedirectResponse(GURL* location, - int* http_status_code) { - DCHECK(!real_job_.get()); - return real_job_->IsRedirectResponse(location, http_status_code); -} - -net::Filter* AdapterRequestJob::SetupFilter() const { - DCHECK(!real_job_.get()); - return real_job_->SetupFilter(); -} - -bool AdapterRequestJob::GetMimeType(std::string* mime_type) const { - DCHECK(!real_job_.get()); - return real_job_->GetMimeType(mime_type); -} - -bool AdapterRequestJob::GetCharset(std::string* charset) { - DCHECK(!real_job_.get()); - return real_job_->GetCharset(charset); -} - -void AdapterRequestJob::GetResponseInfo(net::HttpResponseInfo* info) { - real_job_->GetResponseInfo(info); -} - -int AdapterRequestJob::GetResponseCode() const { - return real_job_->GetResponseCode(); -} - -void AdapterRequestJob::GetLoadTimingInfo( - net::LoadTimingInfo* load_timing_info) const { - real_job_->GetLoadTimingInfo(load_timing_info); -} - -base::WeakPtr AdapterRequestJob::GetWeakPtr() { - return weak_factory_.GetWeakPtr(); -} - -void AdapterRequestJob::CreateErrorJobAndStart(int error_code) { - 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) { - real_job_ = new URLRequestStringJob( - request(), network_delegate(), mime_type, charset, data); - real_job_->Start(); -} - -void AdapterRequestJob::CreateBufferJobAndStart( - const std::string& mime_type, - const std::string& charset, - scoped_refptr data) { - real_job_ = new URLRequestBufferJob( - request(), network_delegate(), mime_type, charset, data); - real_job_->Start(); -} - -void AdapterRequestJob::CreateFileJobAndStart(const base::FilePath& path) { - real_job_ = asar::CreateJobFromPath( - path, - request(), - network_delegate(), - content::BrowserThread::GetBlockingPool()-> - GetTaskRunnerWithShutdownBehavior( - base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)); - real_job_->Start(); -} - -void AdapterRequestJob::CreateHttpJobAndStart( - scoped_refptr request_context_getter, - const GURL& url, - const std::string& method, - const std::string& referrer) { - if (!url.is_valid()) { - CreateErrorJobAndStart(net::ERR_INVALID_URL); - return; - } - - real_job_ = new URLRequestFetchJob(request_context_getter, request(), - network_delegate(), url, method, referrer); - real_job_->Start(); -} - -void AdapterRequestJob::CreateJobFromProtocolHandlerAndStart() { - real_job_ = protocol_handler_->MaybeCreateJob(request(), - network_delegate()); - if (!real_job_.get()) { - CreateErrorJobAndStart(net::ERR_NOT_IMPLEMENTED); - } else { - // Copy headers from original request. - real_job_->SetExtraRequestHeaders(request()->extra_request_headers()); - real_job_->Start(); - } -} - -} // namespace atom diff --git a/atom/browser/net/adapter_request_job.h b/atom/browser/net/adapter_request_job.h deleted file mode 100644 index afb9d5f55d07..000000000000 --- a/atom/browser/net/adapter_request_job.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 2013 GitHub, Inc. -// Use of this source code is governed by the MIT 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 - -#include "base/memory/ref_counted_memory.h" -#include "base/memory/weak_ptr.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_context_getter.h" -#include "net/url_request/url_request_job.h" -#include "net/url_request/url_request_job_factory.h" -#include "v8/include/v8.h" - -namespace base { -class FilePath; -} - -namespace atom { - -class AtomBrowserContext; - -// 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: - void Start() override; - void Kill() override; - bool ReadRawData(net::IOBuffer* buf, - int buf_size, - int *bytes_read) override; - bool IsRedirectResponse(GURL* location, - int* http_status_code) override; - net::Filter* SetupFilter() const override; - bool GetMimeType(std::string* mime_type) const override; - bool GetCharset(std::string* charset) override; - void GetResponseInfo(net::HttpResponseInfo* info) override; - int GetResponseCode() const override; - void GetLoadTimingInfo( - net::LoadTimingInfo* load_timing_info) const override; - - base::WeakPtr GetWeakPtr(); - - ProtocolHandler* default_protocol_handler() { return protocol_handler_; } - - // Override this function to determine which job should be started. - virtual void GetJobType() = 0; - - void CreateErrorJobAndStart(int error_code); - void CreateStringJobAndStart(const std::string& mime_type, - const std::string& charset, - const std::string& data); - void CreateBufferJobAndStart(const std::string& mime_type, - const std::string& charset, - scoped_refptr data); - void CreateFileJobAndStart(const base::FilePath& path); - void CreateHttpJobAndStart( - scoped_refptr request_context_getter, - const GURL& url, - const std::string& method, - const std::string& referrer); - void CreateJobFromProtocolHandlerAndStart(); - - private: - // The delegated request job. - scoped_refptr real_job_; - - // Default protocol handler. - ProtocolHandler* protocol_handler_; - - base::WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(AdapterRequestJob); -}; - -} // namespace atom - -#endif // ATOM_BROWSER_NET_ADAPTER_REQUEST_JOB_H_ diff --git a/atom/browser/net/asar/asar_protocol_handler.cc b/atom/browser/net/asar/asar_protocol_handler.cc index 6d2a2cd5bf0a..324f8339c8c9 100644 --- a/atom/browser/net/asar/asar_protocol_handler.cc +++ b/atom/browser/net/asar/asar_protocol_handler.cc @@ -5,45 +5,11 @@ #include "atom/browser/net/asar/asar_protocol_handler.h" #include "atom/browser/net/asar/url_request_asar_job.h" -#include "atom/common/asar/archive.h" -#include "atom/common/asar/asar_util.h" #include "net/base/filename_util.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 asar { -// static -net::URLRequestJob* CreateJobFromPath( - const base::FilePath& full_path, - net::URLRequest* request, - net::NetworkDelegate* network_delegate, - const scoped_refptr file_task_runner) { - // Create asar:// job when the path contains "xxx.asar/", otherwise treat the - // URL request as file://. - base::FilePath asar_path, relative_path; - if (!GetAsarArchivePath(full_path, &asar_path, &relative_path)) - return new net::URLRequestFileJob(request, network_delegate, full_path, - file_task_runner); - - std::shared_ptr archive = GetOrCreateAsarArchive(asar_path); - Archive::FileInfo file_info; - if (!archive || !archive->GetFileInfo(relative_path, &file_info)) - return new net::URLRequestErrorJob(request, network_delegate, - net::ERR_FILE_NOT_FOUND); - - if (file_info.unpacked) { - base::FilePath real_path; - archive->CopyFileOut(relative_path, &real_path); - return new net::URLRequestFileJob(request, network_delegate, real_path, - file_task_runner); - } - - return new URLRequestAsarJob(request, network_delegate, archive, - relative_path, file_info, file_task_runner); -} - AsarProtocolHandler::AsarProtocolHandler( const scoped_refptr& file_task_runner) : file_task_runner_(file_task_runner) {} @@ -56,8 +22,9 @@ net::URLRequestJob* AsarProtocolHandler::MaybeCreateJob( net::NetworkDelegate* network_delegate) const { base::FilePath full_path; net::FileURLToFilePath(request->url(), &full_path); - return CreateJobFromPath(full_path, request, network_delegate, - file_task_runner_); + URLRequestAsarJob* job = new URLRequestAsarJob(request, network_delegate); + job->Initialize(file_task_runner_, full_path); + return job; } bool AsarProtocolHandler::IsSafeRedirectTarget(const GURL& location) const { diff --git a/atom/browser/net/asar/url_request_asar_job.cc b/atom/browser/net/asar/url_request_asar_job.cc index 477c6610b1b1..de019f6d9420 100644 --- a/atom/browser/net/asar/url_request_asar_job.cc +++ b/atom/browser/net/asar/url_request_asar_job.cc @@ -5,48 +5,128 @@ #include "atom/browser/net/asar/url_request_asar_job.h" #include +#include +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/strings/string_util.h" +#include "base/synchronization/lock.h" +#include "base/task_runner.h" +#include "atom/common/asar/archive.h" +#include "atom/common/asar/asar_util.h" #include "net/base/file_stream.h" +#include "net/base/filename_util.h" #include "net/base/io_buffer.h" +#include "net/base/load_flags.h" #include "net/base/mime_util.h" #include "net/base/net_errors.h" +#include "net/filter/filter.h" +#include "net/http/http_util.h" #include "net/url_request/url_request_status.h" +#if defined(OS_WIN) +#include "base/win/shortcut.h" +#endif + namespace asar { +URLRequestAsarJob::FileMetaInfo::FileMetaInfo() + : file_size(0), + mime_type_result(false), + file_exists(false), + is_directory(false) { +} + URLRequestAsarJob::URLRequestAsarJob( net::URLRequest* request, - net::NetworkDelegate* network_delegate, - std::shared_ptr archive, - const base::FilePath& file_path, - const Archive::FileInfo& file_info, - const scoped_refptr& file_task_runner) + net::NetworkDelegate* network_delegate) : net::URLRequestJob(request, network_delegate), - archive_(archive), - file_path_(file_path), - file_info_(file_info), - stream_(new net::FileStream(file_task_runner)), + type_(TYPE_ERROR), remaining_bytes_(0), - file_task_runner_(file_task_runner), weak_ptr_factory_(this) {} URLRequestAsarJob::~URLRequestAsarJob() {} -void URLRequestAsarJob::Start() { - remaining_bytes_ = static_cast(file_info_.size); +void URLRequestAsarJob::Initialize( + const scoped_refptr file_task_runner, + const base::FilePath& file_path) { + // Determine whether it is an asar file. + base::FilePath asar_path, relative_path; + if (!GetAsarArchivePath(file_path, &asar_path, &relative_path)) { + InitializeFileJob(file_task_runner, file_path); + return; + } - int flags = base::File::FLAG_OPEN | - base::File::FLAG_READ | - base::File::FLAG_ASYNC; - int rv = stream_->Open(archive_->path(), flags, - base::Bind(&URLRequestAsarJob::DidOpen, - weak_ptr_factory_.GetWeakPtr())); - if (rv != net::ERR_IO_PENDING) - DidOpen(rv); + std::shared_ptr archive = GetOrCreateAsarArchive(asar_path); + Archive::FileInfo file_info; + if (!archive || !archive->GetFileInfo(relative_path, &file_info)) { + type_ = TYPE_ERROR; + return; + } + + if (file_info.unpacked) { + base::FilePath real_path; + archive->CopyFileOut(relative_path, &real_path); + InitializeFileJob(file_task_runner, real_path); + return; + } + + InitializeAsarJob(file_task_runner, archive, relative_path, file_info); +} + +void URLRequestAsarJob::InitializeAsarJob( + const scoped_refptr file_task_runner, + std::shared_ptr archive, + const base::FilePath& file_path, + const Archive::FileInfo& file_info) { + type_ = TYPE_ASAR; + file_task_runner_ = file_task_runner; + stream_.reset(new net::FileStream(file_task_runner_)); + archive_ = archive; + file_path_ = file_path; + file_info_ = file_info; +} + +void URLRequestAsarJob::InitializeFileJob( + const scoped_refptr file_task_runner, + const base::FilePath& file_path) { + type_ = TYPE_FILE; + file_task_runner_ = file_task_runner; + stream_.reset(new net::FileStream(file_task_runner_)); + file_path_ = file_path; +} + +void URLRequestAsarJob::Start() { + if (type_ == TYPE_ASAR) { + remaining_bytes_ = static_cast(file_info_.size); + + int flags = base::File::FLAG_OPEN | + base::File::FLAG_READ | + base::File::FLAG_ASYNC; + int rv = stream_->Open(archive_->path(), flags, + base::Bind(&URLRequestAsarJob::DidOpen, + weak_ptr_factory_.GetWeakPtr())); + if (rv != net::ERR_IO_PENDING) + DidOpen(rv); + } else if (type_ == TYPE_FILE) { + FileMetaInfo* meta_info = new FileMetaInfo(); + file_task_runner_->PostTaskAndReply( + FROM_HERE, + base::Bind(&URLRequestAsarJob::FetchMetaInfo, file_path_, + base::Unretained(meta_info)), + base::Bind(&URLRequestAsarJob::DidFetchMetaInfo, + weak_ptr_factory_.GetWeakPtr(), + base::Owned(meta_info))); + } else { + NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED, + net::ERR_FILE_NOT_FOUND)); + } } void URLRequestAsarJob::Kill() { + stream_.reset(); weak_ptr_factory_.InvalidateWeakPtrs(); + URLRequestJob::Kill(); } @@ -85,8 +165,97 @@ bool URLRequestAsarJob::ReadRawData(net::IOBuffer* dest, return false; } +bool URLRequestAsarJob::IsRedirectResponse(GURL* location, + int* http_status_code) { + if (type_ != TYPE_FILE) + return false; +#if defined(OS_WIN) + // Follow a Windows shortcut. + // We just resolve .lnk file, ignore others. + if (!LowerCaseEqualsASCII(file_path_.Extension(), ".lnk")) + return false; + + base::FilePath new_path = file_path_; + bool resolved; + resolved = base::win::ResolveShortcut(new_path, &new_path, NULL); + + // If shortcut is not resolved succesfully, do not redirect. + if (!resolved) + return false; + + *location = net::FilePathToFileURL(new_path); + *http_status_code = 301; + return true; +#else + return false; +#endif +} + +net::Filter* URLRequestAsarJob::SetupFilter() const { + // Bug 9936 - .svgz files needs to be decompressed. + return LowerCaseEqualsASCII(file_path_.Extension(), ".svgz") + ? net::Filter::GZipFactory() : NULL; +} + bool URLRequestAsarJob::GetMimeType(std::string* mime_type) const { - return net::GetMimeTypeFromFile(file_path_, mime_type); + if (type_ == TYPE_ASAR) { + return net::GetMimeTypeFromFile(file_path_, mime_type); + } else { + if (meta_info_.mime_type_result) { + *mime_type = meta_info_.mime_type; + return true; + } + return false; + } +} + +void URLRequestAsarJob::SetExtraRequestHeaders( + const net::HttpRequestHeaders& headers) { + std::string range_header; + if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) { + // We only care about "Range" header here. + std::vector ranges; + if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) { + if (ranges.size() == 1) { + byte_range_ = ranges[0]; + } else { + NotifyDone(net::URLRequestStatus( + net::URLRequestStatus::FAILED, + net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); + } + } + } +} + +void URLRequestAsarJob::FetchMetaInfo(const base::FilePath& file_path, + FileMetaInfo* meta_info) { + base::File::Info file_info; + meta_info->file_exists = base::GetFileInfo(file_path, &file_info); + if (meta_info->file_exists) { + meta_info->file_size = file_info.size; + meta_info->is_directory = file_info.is_directory; + } + // On Windows GetMimeTypeFromFile() goes to the registry. Thus it should be + // done in WorkerPool. + meta_info->mime_type_result = + net::GetMimeTypeFromFile(file_path, &meta_info->mime_type); +} + +void URLRequestAsarJob::DidFetchMetaInfo(const FileMetaInfo* meta_info) { + meta_info_ = *meta_info; + if (!meta_info_.file_exists || meta_info_.is_directory) { + DidOpen(net::ERR_FILE_NOT_FOUND); + return; + } + + int flags = base::File::FLAG_OPEN | + base::File::FLAG_READ | + base::File::FLAG_ASYNC; + int rv = stream_->Open(file_path_, flags, + base::Bind(&URLRequestAsarJob::DidOpen, + weak_ptr_factory_.GetWeakPtr())); + if (rv != net::ERR_IO_PENDING) + DidOpen(rv); } void URLRequestAsarJob::DidOpen(int result) { @@ -95,24 +264,59 @@ void URLRequestAsarJob::DidOpen(int result) { return; } - int rv = stream_->Seek(base::File::FROM_BEGIN, - file_info_.offset, - base::Bind(&URLRequestAsarJob::DidSeek, - weak_ptr_factory_.GetWeakPtr())); - if (rv != net::ERR_IO_PENDING) { - // stream_->Seek() failed, so pass an intentionally erroneous value - // into DidSeek(). - DidSeek(-1); + if (type_ == TYPE_ASAR) { + int rv = stream_->Seek(base::File::FROM_BEGIN, + file_info_.offset, + base::Bind(&URLRequestAsarJob::DidSeek, + weak_ptr_factory_.GetWeakPtr())); + if (rv != net::ERR_IO_PENDING) { + // stream_->Seek() failed, so pass an intentionally erroneous value + // into DidSeek(). + DidSeek(-1); + } + } else { + if (!byte_range_.ComputeBounds(meta_info_.file_size)) { + NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, + net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); + return; + } + + remaining_bytes_ = byte_range_.last_byte_position() - + byte_range_.first_byte_position() + 1; + + if (remaining_bytes_ > 0 && byte_range_.first_byte_position() != 0) { + int rv = stream_->Seek(base::File::FROM_BEGIN, + byte_range_.first_byte_position(), + base::Bind(&URLRequestAsarJob::DidSeek, + weak_ptr_factory_.GetWeakPtr())); + if (rv != net::ERR_IO_PENDING) { + // stream_->Seek() failed, so pass an intentionally erroneous value + // into DidSeek(). + DidSeek(-1); + } + } else { + // We didn't need to call stream_->Seek() at all, so we pass to DidSeek() + // the value that would mean seek success. This way we skip the code + // handling seek failure. + DidSeek(byte_range_.first_byte_position()); + } } } void URLRequestAsarJob::DidSeek(int64 result) { - if (result != static_cast(file_info_.offset)) { - NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, - net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); - return; + if (type_ == TYPE_ASAR) { + if (result != static_cast(file_info_.offset)) { + NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, + net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); + return; + } + } else { + if (result != byte_range_.first_byte_position()) { + NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, + net::ERR_REQUEST_RANGE_NOT_SATISFIABLE)); + return; + } } - set_expected_content_size(remaining_bytes_); NotifyHeadersComplete(); } diff --git a/atom/browser/net/asar/url_request_asar_job.h b/atom/browser/net/asar/url_request_asar_job.h index adcac85b37d7..15a723d79e89 100644 --- a/atom/browser/net/asar/url_request_asar_job.h +++ b/atom/browser/net/asar/url_request_asar_job.h @@ -8,10 +8,12 @@ #include #include +#include "atom/browser/net/js_asker.h" #include "atom/common/asar/archive.h" #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +#include "net/http/http_byte_range.h" #include "net/url_request/url_request_job.h" namespace base { @@ -34,11 +36,20 @@ net::URLRequestJob* CreateJobFromPath( class URLRequestAsarJob : public net::URLRequestJob { public: URLRequestAsarJob(net::URLRequest* request, - net::NetworkDelegate* network_delegate, - std::shared_ptr archive, - const base::FilePath& file_path, - const Archive::FileInfo& file_info, - const scoped_refptr& file_task_runner); + net::NetworkDelegate* network_delegate); + + void Initialize(const scoped_refptr file_task_runner, + const base::FilePath& file_path); + + protected: + virtual ~URLRequestAsarJob(); + + void InitializeAsarJob(const scoped_refptr file_task_runner, + std::shared_ptr archive, + const base::FilePath& file_path, + const Archive::FileInfo& file_info); + void InitializeFileJob(const scoped_refptr file_task_runner, + const base::FilePath& file_path); // net::URLRequestJob: void Start() override; @@ -46,12 +57,39 @@ class URLRequestAsarJob : public net::URLRequestJob { bool ReadRawData(net::IOBuffer* buf, int buf_size, int* bytes_read) override; + bool IsRedirectResponse(GURL* location, int* http_status_code) override; + net::Filter* SetupFilter() const override; bool GetMimeType(std::string* mime_type) const override; - - protected: - virtual ~URLRequestAsarJob(); + void SetExtraRequestHeaders(const net::HttpRequestHeaders& headers) override; private: + // Meta information about the file. It's used as a member in the + // URLRequestFileJob and also passed between threads because disk access is + // necessary to obtain it. + struct FileMetaInfo { + FileMetaInfo(); + + // Size of the file. + int64 file_size; + // Mime type associated with the file. + std::string mime_type; + // Result returned from GetMimeTypeFromFile(), i.e. flag showing whether + // obtaining of the mime type was successful. + bool mime_type_result; + // Flag showing whether the file exists. + bool file_exists; + // Flag showing whether the file name actually refers to a directory. + bool is_directory; + }; + + // Fetches file info on a background thread. + static void FetchMetaInfo(const base::FilePath& file_path, + FileMetaInfo* meta_info); + + // Callback after fetching file info on a background thread. + void DidFetchMetaInfo(const FileMetaInfo* meta_info); + + // Callback after opening file on a background thread. void DidOpen(int result); @@ -62,14 +100,24 @@ class URLRequestAsarJob : public net::URLRequestJob { // Callback after data is asynchronously read from the file into |buf|. void DidRead(scoped_refptr buf, int result); + // The type of this job. + enum JobType { + TYPE_ERROR, + TYPE_ASAR, + TYPE_FILE, + }; + JobType type_; + std::shared_ptr archive_; base::FilePath file_path_; Archive::FileInfo file_info_; scoped_ptr stream_; - int64 remaining_bytes_; + FileMetaInfo meta_info_; + scoped_refptr file_task_runner_; - const scoped_refptr file_task_runner_; + net::HttpByteRange byte_range_; + int64 remaining_bytes_; base::WeakPtrFactory weak_ptr_factory_; diff --git a/atom/browser/net/atom_url_request_job_factory.cc b/atom/browser/net/atom_url_request_job_factory.cc index 00942b2ad057..dbd8b4160cfd 100644 --- a/atom/browser/net/atom_url_request_job_factory.cc +++ b/atom/browser/net/atom_url_request_job_factory.cc @@ -23,10 +23,7 @@ AtomURLRequestJobFactory::~AtomURLRequestJobFactory() { } bool AtomURLRequestJobFactory::SetProtocolHandler( - const std::string& scheme, - ProtocolHandler* protocol_handler) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - + const std::string& scheme, scoped_ptr protocol_handler) { if (!protocol_handler) { ProtocolHandlerMap::iterator it = protocol_handler_map_.find(scheme); if (it == protocol_handler_map_.end()) @@ -39,21 +36,17 @@ bool AtomURLRequestJobFactory::SetProtocolHandler( if (ContainsKey(protocol_handler_map_, scheme)) return false; - protocol_handler_map_[scheme] = protocol_handler; + protocol_handler_map_[scheme] = protocol_handler.release(); return true; } -ProtocolHandler* AtomURLRequestJobFactory::ReplaceProtocol( - const std::string& scheme, - ProtocolHandler* protocol_handler) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(protocol_handler); - +scoped_ptr AtomURLRequestJobFactory::ReplaceProtocol( + const std::string& scheme, scoped_ptr protocol_handler) { if (!ContainsKey(protocol_handler_map_, scheme)) return nullptr; ProtocolHandler* original_protocol_handler = protocol_handler_map_[scheme]; - protocol_handler_map_[scheme] = protocol_handler; - return original_protocol_handler; + protocol_handler_map_[scheme] = protocol_handler.release(); + return make_scoped_ptr(original_protocol_handler); } ProtocolHandler* AtomURLRequestJobFactory::GetProtocolHandler( diff --git a/atom/browser/net/atom_url_request_job_factory.h b/atom/browser/net/atom_url_request_job_factory.h index ce2a18a85e66..dde36225b7af 100644 --- a/atom/browser/net/atom_url_request_job_factory.h +++ b/atom/browser/net/atom_url_request_job_factory.h @@ -10,8 +10,7 @@ #include #include -#include "base/basictypes.h" -#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" #include "net/url_request/url_request_job_factory.h" @@ -25,13 +24,13 @@ class AtomURLRequestJobFactory : public net::URLRequestJobFactory { // 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); + bool SetProtocolHandler( + const std::string& scheme, scoped_ptr 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); + scoped_ptr ReplaceProtocol( + const std::string& scheme, scoped_ptr protocol_handler); // Returns the protocol handler registered with scheme. ProtocolHandler* GetProtocolHandler(const std::string& scheme) const; diff --git a/atom/browser/net/js_asker.cc b/atom/browser/net/js_asker.cc new file mode 100644 index 000000000000..d838ae39638f --- /dev/null +++ b/atom/browser/net/js_asker.cc @@ -0,0 +1,131 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/net/js_asker.h" + +#include + +#include "atom/common/native_mate_converters/callback.h" +#include "atom/common/native_mate_converters/v8_value_converter.h" +#include "native_mate/function_template.h" + +namespace atom { + +namespace internal { + +namespace { + +struct CallbackHolder { + ResponseCallback callback; +}; + +// Cached JavaScript version of |HandlerCallback|. +v8::Persistent g_handler_callback_; + +// The callback which is passed to |handler|. +void HandlerCallback(v8::Isolate* isolate, + v8::Local external, + v8::Local state, + mate::Arguments* args) { + // Check if the callback has already been called. + v8::Local called_symbol = mate::StringToSymbol(isolate, "called"); + if (state->Has(called_symbol)) + return; // no nothing + else + state->Set(called_symbol, v8::Boolean::New(isolate, true)); + + // If there is no argument passed then we failed. + scoped_ptr holder( + static_cast(external->Value())); + CHECK(holder); + v8::Local value; + if (!args->GetNext(&value)) { + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind(holder->callback, false, nullptr)); + return; + } + + // Pass whatever user passed to the actaul request job. + V8ValueConverter converter; + v8::Local context = args->isolate()->GetCurrentContext(); + scoped_ptr options(converter.FromV8Value(value, context)); + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind(holder->callback, true, base::Passed(&options))); +} + +// func.bind(func, arg1, arg2). +// NB(zcbenz): Using C++11 version crashes VS. +v8::Local BindFunctionWith(v8::Isolate* isolate, + v8::Local context, + v8::Local func, + v8::Local arg1, + v8::Local arg2) { + v8::MaybeLocal bind = func->Get(mate::StringToV8(isolate, "bind")); + CHECK(!bind.IsEmpty()); + v8::Local bind_func = + v8::Local::Cast(bind.ToLocalChecked()); + v8::Local converted[] = { func, arg1, arg2 }; + return bind_func->Call( + context, func, arraysize(converted), converted).ToLocalChecked(); +} + +// Generate the callback that will be passed to |handler|. +v8::MaybeLocal GenerateCallback(v8::Isolate* isolate, + v8::Local context, + const ResponseCallback& callback) { + // The FunctionTemplate is cached. + if (g_handler_callback_.IsEmpty()) + g_handler_callback_.Reset( + isolate, + mate::CreateFunctionTemplate(isolate, base::Bind(&HandlerCallback))); + + v8::Local handler_callback = + v8::Local::New(isolate, g_handler_callback_); + CallbackHolder* holder = new CallbackHolder; + holder->callback = callback; + return BindFunctionWith(isolate, context, handler_callback->GetFunction(), + v8::External::New(isolate, holder), + v8::Object::New(isolate)); +} + +} // namespace + +void AskForOptions(v8::Isolate* isolate, + const JavaScriptHandler& handler, + net::URLRequest* request, + const ResponseCallback& callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + v8::Locker locker(isolate); + v8::HandleScope handle_scope(isolate); + v8::Local context = isolate->GetCurrentContext(); + v8::Context::Scope context_scope(context); + // We don't convert the callback to C++ directly because creating + // FunctionTemplate will cause memory leak since V8 never releases it. So we + // have to create the function object in JavaScript to work around it. + v8::MaybeLocal wrapped_callback = GenerateCallback( + isolate, context, callback); + if (wrapped_callback.IsEmpty()) { + callback.Run(false, nullptr); + return; + } + handler.Run(request, wrapped_callback.ToLocalChecked()); +} + +bool IsErrorOptions(base::Value* value, int* error) { + if (value->IsType(base::Value::TYPE_DICTIONARY)) { + base::DictionaryValue* dict = static_cast(value); + if (dict->GetInteger("error", error)) + return true; + } else if (value->IsType(base::Value::TYPE_INTEGER)) { + if (value->GetAsInteger(error)) + return true; + } + return false; +} + +} // namespace internal + +} // namespace atom diff --git a/atom/browser/net/js_asker.h b/atom/browser/net/js_asker.h new file mode 100644 index 000000000000..9c45446abcbb --- /dev/null +++ b/atom/browser/net/js_asker.h @@ -0,0 +1,102 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_NET_JS_ASKER_H_ +#define ATOM_BROWSER_NET_JS_ASKER_H_ + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/values.h" +#include "content/public/browser/browser_thread.h" +#include "net/base/net_errors.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request_context_getter.h" +#include "net/url_request/url_request_job.h" +#include "v8/include/v8.h" + +namespace atom { + +using JavaScriptHandler = + base::Callback)>; + +namespace internal { + +using ResponseCallback = + base::Callback options)>; + +// Ask handler for options in UI thread. +void AskForOptions(v8::Isolate* isolate, + const JavaScriptHandler& handler, + net::URLRequest* request, + const ResponseCallback& callback); + +// Test whether the |options| means an error. +bool IsErrorOptions(base::Value* value, int* error); + +} // namespace internal + +template +class JsAsker : public RequestJob { + public: + JsAsker(net::URLRequest* request, net::NetworkDelegate* network_delegate) + : RequestJob(request, network_delegate), weak_factory_(this) {} + + // Called by |CustomProtocolHandler| to store handler related information. + void SetHandlerInfo( + v8::Isolate* isolate, + scoped_refptr request_context_getter, + const JavaScriptHandler& handler) { + isolate_ = isolate; + request_context_getter_ = request_context_getter; + handler_ = handler; + } + + // Subclass should do initailze work here. + virtual void StartAsync(scoped_ptr options) = 0; + + net::URLRequestContextGetter* request_context_getter() const { + return request_context_getter_.get(); + } + + private: + // RequestJob: + void Start() override { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&internal::AskForOptions, + isolate_, + handler_, + RequestJob::request(), + base::Bind(&JsAsker::OnResponse, + weak_factory_.GetWeakPtr()))); + } + void GetResponseInfo(net::HttpResponseInfo* info) override { + info->headers = new net::HttpResponseHeaders(""); + } + + // Called when the JS handler has sent the response, we need to decide whether + // to start, or fail the job. + void OnResponse(bool success, scoped_ptr value) { + int error = net::ERR_NOT_IMPLEMENTED; + if (success && value && !internal::IsErrorOptions(value.get(), &error)) { + StartAsync(value.Pass()); + } else { + RequestJob::NotifyStartError( + net::URLRequestStatus(net::URLRequestStatus::FAILED, error)); + } + } + + v8::Isolate* isolate_; + scoped_refptr request_context_getter_; + JavaScriptHandler handler_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(JsAsker); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_NET_JS_ASKER_H_ diff --git a/atom/browser/net/url_request_async_asar_job.cc b/atom/browser/net/url_request_async_asar_job.cc new file mode 100644 index 000000000000..ba0189e5f6e0 --- /dev/null +++ b/atom/browser/net/url_request_async_asar_job.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/net/url_request_async_asar_job.h" + +namespace atom { + +UrlRequestAsyncAsarJob::UrlRequestAsyncAsarJob( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) + : JsAsker(request, network_delegate) { +} + +void UrlRequestAsyncAsarJob::StartAsync(scoped_ptr options) { + base::FilePath::StringType file_path; + if (options->IsType(base::Value::TYPE_DICTIONARY)) { + static_cast(options.get())->GetString( + "path", &file_path); + } else if (options->IsType(base::Value::TYPE_STRING)) { + options->GetAsString(&file_path); + } + + if (file_path.empty()) { + NotifyStartError(net::URLRequestStatus( + net::URLRequestStatus::FAILED, net::ERR_NOT_IMPLEMENTED)); + } else { + asar::URLRequestAsarJob::Initialize( + content::BrowserThread::GetBlockingPool()-> + GetTaskRunnerWithShutdownBehavior( + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN), + base::FilePath(file_path)); + asar::URLRequestAsarJob::Start(); + } +} + +} // namespace atom diff --git a/atom/browser/net/url_request_async_asar_job.h b/atom/browser/net/url_request_async_asar_job.h new file mode 100644 index 000000000000..748b96d84d28 --- /dev/null +++ b/atom/browser/net/url_request_async_asar_job.h @@ -0,0 +1,27 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_NET_URL_REQUEST_ASYNC_ASAR_JOB_H_ +#define ATOM_BROWSER_NET_URL_REQUEST_ASYNC_ASAR_JOB_H_ + +#include "atom/browser/net/asar/url_request_asar_job.h" +#include "atom/browser/net/js_asker.h" + +namespace atom { + +// Like URLRequestAsarJob, but asks the JavaScript handler for file path. +class UrlRequestAsyncAsarJob : public JsAsker { + public: + UrlRequestAsyncAsarJob(net::URLRequest*, net::NetworkDelegate*); + + // JsAsker: + void StartAsync(scoped_ptr options) override; + + private: + DISALLOW_COPY_AND_ASSIGN(UrlRequestAsyncAsarJob); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_NET_URL_REQUEST_ASYNC_ASAR_JOB_H_ diff --git a/atom/browser/net/url_request_buffer_job.cc b/atom/browser/net/url_request_buffer_job.cc index a8233b88da46..7eb3aaed243c 100644 --- a/atom/browser/net/url_request_buffer_job.cc +++ b/atom/browser/net/url_request_buffer_job.cc @@ -11,15 +11,32 @@ namespace atom { URLRequestBufferJob::URLRequestBufferJob( - net::URLRequest* request, - net::NetworkDelegate* network_delegate, - const std::string& mime_type, - const std::string& charset, - scoped_refptr data) - : net::URLRequestSimpleJob(request, network_delegate), - mime_type_(mime_type), - charset_(charset), - buffer_data_(data) { + net::URLRequest* request, net::NetworkDelegate* network_delegate) + : JsAsker(request, network_delegate) { +} + +void URLRequestBufferJob::StartAsync(scoped_ptr options) { + const base::BinaryValue* binary = nullptr; + if (options->IsType(base::Value::TYPE_DICTIONARY)) { + base::DictionaryValue* dict = + static_cast(options.get()); + dict->GetString("mimeType", &mime_type_); + dict->GetString("charset", &charset_); + dict->GetBinary("data", &binary); + } else if (options->IsType(base::Value::TYPE_BINARY)) { + options->GetAsBinary(&binary); + } + + if (!binary) { + NotifyStartError(net::URLRequestStatus( + net::URLRequestStatus::FAILED, net::ERR_NOT_IMPLEMENTED)); + return; + } + + data_ = new base::RefCountedBytes( + reinterpret_cast(binary->GetBuffer()), + binary->GetSize()); + net::URLRequestSimpleJob::Start(); } int URLRequestBufferJob::GetRefCountedData( @@ -29,7 +46,7 @@ int URLRequestBufferJob::GetRefCountedData( const net::CompletionCallback& callback) const { *mime_type = mime_type_; *charset = charset_; - *data = buffer_data_; + *data = data_; return net::OK; } diff --git a/atom/browser/net/url_request_buffer_job.h b/atom/browser/net/url_request_buffer_job.h index cbdfbfa10d0b..e6fecdba8301 100644 --- a/atom/browser/net/url_request_buffer_job.h +++ b/atom/browser/net/url_request_buffer_job.h @@ -7,19 +7,18 @@ #include +#include "atom/browser/net/js_asker.h" #include "base/memory/ref_counted_memory.h" #include "net/url_request/url_request_simple_job.h" -#include "atom/common/node_includes.h" namespace atom { -class URLRequestBufferJob : public net::URLRequestSimpleJob { +class URLRequestBufferJob : public JsAsker { public: - URLRequestBufferJob(net::URLRequest* request, - net::NetworkDelegate* network_delegate, - const std::string& mime_type, - const std::string& charset, - scoped_refptr data); + URLRequestBufferJob(net::URLRequest*, net::NetworkDelegate*); + + // JsAsker: + void StartAsync(scoped_ptr options) override; // URLRequestSimpleJob: int GetRefCountedData(std::string* mime_type, @@ -30,7 +29,7 @@ class URLRequestBufferJob : public net::URLRequestSimpleJob { private: std::string mime_type_; std::string charset_; - scoped_refptr buffer_data_; + scoped_refptr data_; DISALLOW_COPY_AND_ASSIGN(URLRequestBufferJob); }; diff --git a/atom/browser/net/url_request_fetch_job.cc b/atom/browser/net/url_request_fetch_job.cc index ee4c67b371ee..eacaada19358 100644 --- a/atom/browser/net/url_request_fetch_job.cc +++ b/atom/browser/net/url_request_fetch_job.cc @@ -75,42 +75,65 @@ class ResponsePiper : public net::URLFetcherResponseWriter { } // namespace URLRequestFetchJob::URLRequestFetchJob( - scoped_refptr request_context_getter, - net::URLRequest* request, - net::NetworkDelegate* network_delegate, - const GURL& url, - const std::string& method, - const std::string& referrer) - : net::URLRequestJob(request, network_delegate), + net::URLRequest* request, net::NetworkDelegate* network_delegate) + : JsAsker(request, network_delegate), pending_buffer_size_(0) { +} + +void URLRequestFetchJob::StartAsync(scoped_ptr options) { + if (!options->IsType(base::Value::TYPE_DICTIONARY)) { + NotifyStartError(net::URLRequestStatus( + net::URLRequestStatus::FAILED, net::ERR_NOT_IMPLEMENTED)); + return; + } + + std::string url, method, referrer; + base::Value* session = nullptr; + base::DictionaryValue* dict = + static_cast(options.get()); + dict->GetString("url", &url); + dict->GetString("method", &method); + dict->GetString("referrer", &referrer); + dict->Get("session", &session); + + // Check if URL is valid. + GURL formated_url(url); + if (!formated_url.is_valid()) { + NotifyStartError(net::URLRequestStatus( + net::URLRequestStatus::FAILED, net::ERR_INVALID_URL)); + return; + } + // Use |request|'s method if |method| is not specified. net::URLFetcher::RequestType request_type; if (method.empty()) - request_type = GetRequestType(request->method()); + request_type = GetRequestType(request()->method()); else request_type = GetRequestType(method); - fetcher_.reset(net::URLFetcher::Create(url, request_type, this)); - // Use request context if provided else create one. - if (request_context_getter) - fetcher_->SetRequestContext(request_context_getter.get()); - else - fetcher_->SetRequestContext(GetRequestContext()); - + fetcher_ = net::URLFetcher::Create(formated_url, request_type, this); fetcher_->SaveResponseWithWriter(make_scoped_ptr(new ResponsePiper(this))); + // When |session| is set to |null| we use a new request context for fetch job. + if (session && session->IsType(base::Value::TYPE_NULL)) + fetcher_->SetRequestContext(CreateRequestContext()); + else + fetcher_->SetRequestContext(request_context_getter()); + // Use |request|'s referrer if |referrer| is not specified. - if (referrer.empty()) { - fetcher_->SetReferrer(request->referrer()); - } else { + if (referrer.empty()) + fetcher_->SetReferrer(request()->referrer()); + else fetcher_->SetReferrer(referrer); - } // Use |request|'s headers. - fetcher_->SetExtraRequestHeaders(request->extra_request_headers().ToString()); + fetcher_->SetExtraRequestHeaders( + request()->extra_request_headers().ToString()); + + fetcher_->Start(); } -net::URLRequestContextGetter* URLRequestFetchJob::GetRequestContext() { +net::URLRequestContextGetter* URLRequestFetchJob::CreateRequestContext() { if (!url_request_context_getter_.get()) { auto task_runner = base::ThreadTaskRunnerHandle::Get(); net::URLRequestContextBuilder builder; @@ -150,12 +173,8 @@ int URLRequestFetchJob::DataAvailable(net::IOBuffer* buffer, int num_bytes) { return bytes_read; } -void URLRequestFetchJob::Start() { - fetcher_->Start(); -} - void URLRequestFetchJob::Kill() { - URLRequestJob::Kill(); + JsAsker::Kill(); fetcher_.reset(); } diff --git a/atom/browser/net/url_request_fetch_job.h b/atom/browser/net/url_request_fetch_job.h index a14e8dd1aae3..189cebf01b18 100644 --- a/atom/browser/net/url_request_fetch_job.h +++ b/atom/browser/net/url_request_fetch_job.h @@ -7,6 +7,7 @@ #include +#include "atom/browser/net/js_asker.h" #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_fetcher_delegate.h" #include "net/url_request/url_request_job.h" @@ -15,22 +16,20 @@ namespace atom { class AtomBrowserContext; -class URLRequestFetchJob : public net::URLRequestJob, +class URLRequestFetchJob : public JsAsker, public net::URLFetcherDelegate { public: - URLRequestFetchJob(scoped_refptr context_getter, - net::URLRequest* request, - net::NetworkDelegate* network_delegate, - const GURL& url, - const std::string& method, - const std::string& referrer); + URLRequestFetchJob(net::URLRequest*, net::NetworkDelegate*); - net::URLRequestContextGetter* GetRequestContext(); + // Called by response writer. void HeadersCompleted(); int DataAvailable(net::IOBuffer* buffer, int num_bytes); + protected: + // JsAsker: + void StartAsync(scoped_ptr options) override; + // net::URLRequestJob: - void Start() override; void Kill() override; bool ReadRawData(net::IOBuffer* buf, int buf_size, @@ -43,6 +42,9 @@ class URLRequestFetchJob : public net::URLRequestJob, void OnURLFetchComplete(const net::URLFetcher* source) override; private: + // Create a independent request context. + net::URLRequestContextGetter* CreateRequestContext(); + scoped_refptr url_request_context_getter_; scoped_ptr fetcher_; scoped_refptr pending_buffer_; diff --git a/atom/browser/net/url_request_string_job.cc b/atom/browser/net/url_request_string_job.cc index d0f205789a81..428a87f54b1e 100644 --- a/atom/browser/net/url_request_string_job.cc +++ b/atom/browser/net/url_request_string_job.cc @@ -10,15 +10,22 @@ 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) { +URLRequestStringJob::URLRequestStringJob( + net::URLRequest* request, net::NetworkDelegate* network_delegate) + : JsAsker(request, network_delegate) { +} + +void URLRequestStringJob::StartAsync(scoped_ptr options) { + if (options->IsType(base::Value::TYPE_DICTIONARY)) { + base::DictionaryValue* dict = + static_cast(options.get()); + dict->GetString("mimeType", &mime_type_); + dict->GetString("charset", &charset_); + dict->GetString("data", &data_); + } else if (options->IsType(base::Value::TYPE_STRING)) { + options->GetAsString(&data_); + } + net::URLRequestSimpleJob::Start(); } int URLRequestStringJob::GetData( diff --git a/atom/browser/net/url_request_string_job.h b/atom/browser/net/url_request_string_job.h index 7ad250466aba..713185c0cd1d 100644 --- a/atom/browser/net/url_request_string_job.h +++ b/atom/browser/net/url_request_string_job.h @@ -5,19 +5,19 @@ #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" - #include +#include "atom/browser/net/js_asker.h" +#include "net/url_request/url_request_simple_job.h" + namespace atom { -class URLRequestStringJob : public net::URLRequestSimpleJob { +class URLRequestStringJob : public JsAsker { public: - URLRequestStringJob(net::URLRequest* request, - net::NetworkDelegate* network_delegate, - const std::string& mime_type, - const std::string& charset, - const std::string& data); + URLRequestStringJob(net::URLRequest*, net::NetworkDelegate*); + + // JsAsker: + void StartAsync(scoped_ptr options) override; // URLRequestSimpleJob: int GetData(std::string* mime_type, diff --git a/atom/browser/web_view_guest_delegate.cc b/atom/browser/web_view_guest_delegate.cc index 38f4da4c1f96..8e1810c4a39b 100644 --- a/atom/browser/web_view_guest_delegate.cc +++ b/atom/browser/web_view_guest_delegate.cc @@ -158,9 +158,11 @@ void WebViewGuestDelegate::SetGuestHost(content::GuestHost* guest_host) { void WebViewGuestDelegate::WillAttach( content::WebContents* embedder_web_contents, int element_instance_id, - bool is_full_page_plugin) { + bool is_full_page_plugin, + const base::Closure& completion_callback) { embedder_web_contents_ = embedder_web_contents; is_full_page_plugin_ = is_full_page_plugin; + completion_callback.Run(); } void WebViewGuestDelegate::GuestSizeChangedDueToAutoSize( diff --git a/atom/browser/web_view_guest_delegate.h b/atom/browser/web_view_guest_delegate.h index 3312b46104a0..65e0bcde1916 100644 --- a/atom/browser/web_view_guest_delegate.h +++ b/atom/browser/web_view_guest_delegate.h @@ -70,7 +70,8 @@ class WebViewGuestDelegate : public content::BrowserPluginGuestDelegate, void SetGuestHost(content::GuestHost* guest_host) final; void WillAttach(content::WebContents* embedder_web_contents, int element_instance_id, - bool is_full_page_plugin) final; + bool is_full_page_plugin, + const base::Closure& completion_callback) final; private: // This method is invoked when the contents auto-resized to give the container diff --git a/atom/common/api/atom_api_native_image.cc b/atom/common/api/atom_api_native_image.cc index 5324a6b5486b..879c73394293 100644 --- a/atom/common/api/atom_api_native_image.cc +++ b/atom/common/api/atom_api_native_image.cc @@ -180,17 +180,18 @@ mate::ObjectTemplateBuilder NativeImage::GetObjectTemplateBuilder( v8::Local NativeImage::ToPNG(v8::Isolate* isolate) { scoped_refptr png = image_.As1xPNGBytes(); - return node::Buffer::New(isolate, - reinterpret_cast(png->front()), - png->size()); + return node::Buffer::Copy(isolate, + reinterpret_cast(png->front()), + static_cast(png->size())).ToLocalChecked(); } v8::Local NativeImage::ToJPEG(v8::Isolate* isolate, int quality) { std::vector output; gfx::JPEG1xEncodedDataFromImage(image_, quality, &output); - return node::Buffer::New(isolate, - reinterpret_cast(&output.front()), - output.size()); + return node::Buffer::Copy( + isolate, + reinterpret_cast(&output.front()), + static_cast(output.size())).ToLocalChecked(); } std::string NativeImage::ToDataURL() { diff --git a/atom/common/asar/archive.h b/atom/common/asar/archive.h index 2acd17fd7ab4..dda7aa78e0c0 100644 --- a/atom/common/asar/archive.h +++ b/atom/common/asar/archive.h @@ -73,7 +73,8 @@ class Archive { scoped_ptr header_; // Cached external temporary files. - base::ScopedPtrHashMap external_files_; + base::ScopedPtrHashMap> + external_files_; DISALLOW_COPY_AND_ASSIGN(Archive); }; diff --git a/atom/common/chrome_version.h b/atom/common/chrome_version.h index 093ed4e98b05..54dd6bb5bb12 100644 --- a/atom/common/chrome_version.h +++ b/atom/common/chrome_version.h @@ -8,7 +8,7 @@ #ifndef ATOM_COMMON_CHROME_VERSION_H_ #define ATOM_COMMON_CHROME_VERSION_H_ -#define CHROME_VERSION_STRING "43.0.2357.65" +#define CHROME_VERSION_STRING "44.0.2403.125" #define CHROME_VERSION "v" CHROME_VERSION_STRING #endif // ATOM_COMMON_CHROME_VERSION_H_ diff --git a/atom/common/native_mate_converters/v8_value_converter.cc b/atom/common/native_mate_converters/v8_value_converter.cc index 87bc2e7c030f..2270f0e0fd3d 100644 --- a/atom/common/native_mate_converters/v8_value_converter.cc +++ b/atom/common/native_mate_converters/v8_value_converter.cc @@ -11,6 +11,7 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/values.h" +#include "vendor/node/src/node_buffer.h" namespace atom { @@ -211,7 +212,7 @@ base::Value* V8ValueConverter::FromV8ValueImpl( return NULL; if (val->IsNull()) - return base::Value::CreateNullValue(); + return base::Value::CreateNullValue().release(); if (val->IsBoolean()) return new base::FundamentalValue(val->ToBoolean()->Value()); @@ -258,6 +259,10 @@ base::Value* V8ValueConverter::FromV8ValueImpl( return FromV8Object(val->ToObject(), state, isolate); } + if (node::Buffer::HasInstance(val)) { + return FromNodeBuffer(val, state, isolate); + } + if (val->IsObject()) { return FromV8Object(val->ToObject(), state, isolate); } @@ -271,7 +276,7 @@ base::Value* V8ValueConverter::FromV8Array( FromV8ValueState* state, v8::Isolate* isolate) const { if (!state->UpdateAndCheckUniqueness(val)) - return base::Value::CreateNullValue(); + return base::Value::CreateNullValue().release(); scoped_ptr scope; // If val was created in a different context than our current one, change to @@ -305,12 +310,20 @@ base::Value* V8ValueConverter::FromV8Array( return result; } +base::Value* V8ValueConverter::FromNodeBuffer( + v8::Local value, + FromV8ValueState* state, + v8::Isolate* isolate) const { + return base::BinaryValue::CreateWithCopiedBuffer( + node::Buffer::Data(value), node::Buffer::Length(value)); +} + base::Value* V8ValueConverter::FromV8Object( v8::Local val, FromV8ValueState* state, v8::Isolate* isolate) const { if (!state->UpdateAndCheckUniqueness(val)) - return base::Value::CreateNullValue(); + return base::Value::CreateNullValue().release(); scoped_ptr scope; // If val was created in a different context than our current one, change to diff --git a/atom/common/native_mate_converters/v8_value_converter.h b/atom/common/native_mate_converters/v8_value_converter.h index 3a0f6374ccb4..db108ad9b043 100644 --- a/atom/common/native_mate_converters/v8_value_converter.h +++ b/atom/common/native_mate_converters/v8_value_converter.h @@ -48,7 +48,9 @@ class V8ValueConverter { base::Value* FromV8Array(v8::Local array, FromV8ValueState* state, v8::Isolate* isolate) const; - + base::Value* FromNodeBuffer(v8::Local value, + FromV8ValueState* state, + v8::Isolate* isolate) const; base::Value* FromV8Object(v8::Local object, FromV8ValueState* state, v8::Isolate* isolate) const; diff --git a/atom/renderer/atom_renderer_client.cc b/atom/renderer/atom_renderer_client.cc index 0311f8b540b0..04a89fed3889 100644 --- a/atom/renderer/atom_renderer_client.cc +++ b/atom/renderer/atom_renderer_client.cc @@ -7,6 +7,7 @@ #include #include "atom/common/api/atom_bindings.h" +#include "atom/common/native_mate_converters/callback.h" #include "atom/common/node_bindings.h" #include "atom/common/options_switches.h" #include "atom/renderer/atom_render_view_observer.h" @@ -19,11 +20,14 @@ #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame_observer.h" #include "content/public/renderer/render_thread.h" +#include "third_party/WebKit/public/web/WebArrayBuffer.h" +#include "third_party/WebKit/public/web/WebArrayBufferConverter.h" #include "third_party/WebKit/public/web/WebCustomElement.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebPluginParams.h" #include "third_party/WebKit/public/web/WebKit.h" #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" +#include "third_party/WebKit/Source/wtf/ArrayBufferContents.h" #include "atom/common/node_includes.h" @@ -52,6 +56,37 @@ bool IsGuestFrame(blink::WebFrame* frame) { return frame->uniqueName().utf8() == "ATOM_SHELL_GUEST_WEB_VIEW"; } +// global.Uint8Array; +v8::Local GetUint8ArrayConstructor( + v8::Isolate* isolate, v8::Local context) { + v8::Local constructor = context->Global()->Get( + mate::StringToV8(isolate, "Uint8Array")); + return v8::Local::Cast(constructor); +} + +// new ArrayBuffer(size); +v8::Local BlinkArrayBufferCreate( + v8::Isolate* isolate, size_t size) { + blink::WebArrayBuffer buffer = blink::WebArrayBuffer::create(size, 1); + return v8::Local::Cast( + blink::WebArrayBufferConverter::toV8Value( + &buffer, isolate->GetCurrentContext()->Global(), isolate)); +} + +// new Uint8Array(array_buffer, offset, size); +v8::Local BlinkUint8ArrayCreate( + v8::Local ab, size_t offset, size_t size) { + v8::Local context = ab->CreationContext(); + v8::Isolate* isolate = context->GetIsolate(); + v8::Local constructor = + GetUint8ArrayConstructor(isolate, context); + v8::Local args[] = { + ab, mate::ConvertToV8(isolate, offset), mate::ConvertToV8(isolate, size) + }; + return v8::Local::Cast(constructor->NewInstance( + context, arraysize(args), args).ToLocalChecked()); +} + // Helper class to forward the messages to the client. class AtomRenderFrameObserver : public content::RenderFrameObserver { public: @@ -91,6 +126,10 @@ void AtomRendererClient::WebKitInitialized() { blink::WebCustomElement::addEmbedderCustomElementName("webview"); blink::WebCustomElement::addEmbedderCustomElementName("browserplugin"); + // Override Node's ArrayBuffer with DOM's ArrayBuffer. + node::Buffer::SetArrayBufferCreator(&BlinkArrayBufferCreate, + &BlinkUint8ArrayCreate); + node_bindings_->Initialize(); node_bindings_->PrepareMessageLoop(); diff --git a/atom/renderer/guest_view_container.cc b/atom/renderer/guest_view_container.cc index 7643f72b0e5b..c0bc1427d68c 100644 --- a/atom/renderer/guest_view_container.cc +++ b/atom/renderer/guest_view_container.cc @@ -47,14 +47,12 @@ void GuestViewContainer::SetElementInstanceID(int element_instance_id) { std::make_pair(element_instance_id, this)); } -void GuestViewContainer::DidResizeElement(const gfx::Size& old_size, - const gfx::Size& new_size) { +void GuestViewContainer::DidResizeElement(const gfx::Size& new_size) { if (element_resize_callback_.is_null()) return; base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(element_resize_callback_, old_size, new_size)); + FROM_HERE, base::Bind(element_resize_callback_, new_size)); } } // namespace atom diff --git a/atom/renderer/guest_view_container.h b/atom/renderer/guest_view_container.h index b81f84cbd050..2846265cb2af 100644 --- a/atom/renderer/guest_view_container.h +++ b/atom/renderer/guest_view_container.h @@ -16,8 +16,7 @@ namespace atom { class GuestViewContainer : public content::BrowserPluginDelegate { public: - typedef base::Callback - ResizeCallback; + typedef base::Callback ResizeCallback; explicit GuestViewContainer(content::RenderFrame* render_frame); ~GuestViewContainer() override; @@ -28,8 +27,7 @@ class GuestViewContainer : public content::BrowserPluginDelegate { // content::BrowserPluginDelegate: void SetElementInstanceID(int element_instance_id) final; - void DidResizeElement(const gfx::Size& old_size, - const gfx::Size& new_size) final; + void DidResizeElement(const gfx::Size& new_size) final; private: int element_instance_id_; diff --git a/atom/renderer/lib/web-view/web-view.coffee b/atom/renderer/lib/web-view/web-view.coffee index 35fbe17796f4..f5bcf0496f8c 100644 --- a/atom/renderer/lib/web-view/web-view.coffee +++ b/atom/renderer/lib/web-view/web-view.coffee @@ -123,11 +123,9 @@ class WebViewImpl # changed. @dispatchEvent webViewEvent - onElementResize: (oldSize, newSize) -> + onElementResize: (newSize) -> # Dispatch the 'resize' event. resizeEvent = new Event('resize', bubbles: true) - resizeEvent.oldWidth = oldSize.width - resizeEvent.oldHeight = oldSize.height resizeEvent.newWidth = newSize.width resizeEvent.newHeight = newSize.height @dispatchEvent resizeEvent diff --git a/chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc b/chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc index 248ec6b890ef..8ed234d5e81b 100644 --- a/chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc +++ b/chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc @@ -4,11 +4,14 @@ #include "chrome/browser/extensions/global_shortcut_listener_win.h" +#include "base/bind.h" +#include "base/bind_helpers.h" #include "base/win/win_util.h" #include "content/public/browser/browser_thread.h" #include "ui/base/accelerators/accelerator.h" #include "ui/events/event_constants.h" #include "ui/events/keycodes/keyboard_code_conversion_win.h" +#include "ui/gfx/win/singleton_hwnd.h" using content::BrowserThread; @@ -35,14 +38,17 @@ GlobalShortcutListenerWin::~GlobalShortcutListenerWin() { void GlobalShortcutListenerWin::StartListening() { DCHECK(!is_listening_); // Don't start twice. DCHECK(!hotkey_ids_.empty()); // Also don't start if no hotkey is registered. - gfx::SingletonHwnd::GetInstance()->AddObserver(this); + singleton_hwnd_observer_.reset(new gfx::SingletonHwndObserver( + base::Bind( + &GlobalShortcutListenerWin::OnWndProc, base::Unretained(this)))); + is_listening_ = true; } void GlobalShortcutListenerWin::StopListening() { DCHECK(is_listening_); // No point if we are not already listening. DCHECK(hotkey_ids_.empty()); // Make sure the map is clean before ending. - gfx::SingletonHwnd::GetInstance()->RemoveObserver(this); + singleton_hwnd_observer_.reset(nullptr); is_listening_ = false; } diff --git a/chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h b/chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h index a155d8f8991f..b3917e9a160e 100644 --- a/chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h +++ b/chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h @@ -7,26 +7,24 @@ #include +#include "base/memory/scoped_ptr.h" #include "chrome/browser/extensions/global_shortcut_listener.h" #include "ui/gfx/win/singleton_hwnd.h" +#include "ui/gfx/win/singleton_hwnd_observer.h" namespace extensions { // Windows-specific implementation of the GlobalShortcutListener class that // listens for global shortcuts. Handles setting up a keyboard hook and // forwarding its output to the base class for processing. -class GlobalShortcutListenerWin : public GlobalShortcutListener, - public gfx::SingletonHwnd::Observer { +class GlobalShortcutListenerWin : public GlobalShortcutListener { public: GlobalShortcutListenerWin(); virtual ~GlobalShortcutListenerWin(); private: - // The implementation of our Window Proc, called by SingletonHwnd. - virtual void OnWndProc(HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam) override; + // The implementation of our Window Proc, called by SingletonHwndObserver. + void OnWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); // GlobalShortcutListener implementation. virtual void StartListening() override; @@ -43,6 +41,8 @@ class GlobalShortcutListenerWin : public GlobalShortcutListener, typedef std::map HotkeyIdMap; HotkeyIdMap hotkey_ids_; + scoped_ptr singleton_hwnd_observer_; + DISALLOW_COPY_AND_ASSIGN(GlobalShortcutListenerWin); }; diff --git a/chromium_src/chrome/browser/printing/print_preview_message_handler.cc b/chromium_src/chrome/browser/printing/print_preview_message_handler.cc index ca70ac2c58ef..613f3f2343ce 100644 --- a/chromium_src/chrome/browser/printing/print_preview_message_handler.cc +++ b/chromium_src/chrome/browser/printing/print_preview_message_handler.cc @@ -125,8 +125,8 @@ void PrintPreviewMessageHandler::RunPrintToPDFCallback( v8::Locker locker(isolate); v8::HandleScope handle_scope(isolate); if (data) { - v8::Local buffer = node::Buffer::Use(isolate, - data, static_cast(data_size)); + v8::Local buffer = node::Buffer::New(isolate, + data, static_cast(data_size)).ToLocalChecked(); print_to_pdf_callback_map_[request_id].Run(v8::Null(isolate), buffer); } else { v8::Local error_message = v8::String::NewFromUtf8(isolate, diff --git a/chromium_src/chrome/browser/speech/tts_controller_impl.cc b/chromium_src/chrome/browser/speech/tts_controller_impl.cc index 272cafddb947..6b66b6a61960 100644 --- a/chromium_src/chrome/browser/speech/tts_controller_impl.cc +++ b/chromium_src/chrome/browser/speech/tts_controller_impl.cc @@ -7,7 +7,6 @@ #include #include -#include "base/float_util.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/speech/tts_platform.h" @@ -461,4 +460,4 @@ void TtsControllerImpl::SetTtsEngineDelegate( TtsEngineDelegate* TtsControllerImpl::GetTtsEngineDelegate() { return tts_engine_delegate_; -} \ No newline at end of file +} diff --git a/common.gypi b/common.gypi index 84c2b6c9826a..0735d99dd910 100644 --- a/common.gypi +++ b/common.gypi @@ -12,6 +12,8 @@ 'python': 'python', 'openssl_fips': '', 'openssl_no_asm': 1, + 'node_release_urlbase': 'https://atom.io/download/atom-shell', + 'node_byteorder': 'Response')}); +}, function (error) { + if (error) + console.error('Failed to register protocol') +}); +``` + +## protocol.registerStringProtocol(scheme, handler[, completion]) + +* `scheme` String +* `handler` Function +* `completion` Function + +Registers a protocol of `scheme` that will send `String` as response, the +`callback` should be called with either a `String`, or an object that +has `data`, `mimeType`, `chart` properties. + +## protocol.registerHttpProtocol(scheme, handler[, completion]) + +* `scheme` String +* `handler` Function +* `completion` Function + +Registers a protocol of `scheme` that will send a HTTP request as response, the +`callback` should be called with an object that has `url`, `method`, `referer`, +`session` properties. + +By default the HTTP request will reuse current session, if you want the request +to have different session you should specify `session` to `null`. + +## protocol.unregisterProtocol(scheme[, completion]) + +* `scheme` String +* `completion` Function Unregisters the custom protocol of `scheme`. -## protocol.registerStandardSchemes(value) - -* `value` Array - -`value` is an array of custom schemes to be registered as standard schemes. - -A standard scheme adheres to what RFC 3986 calls -[generic URI syntax](https://tools.ietf.org/html/rfc3986#section-3). This -includes `file:` and `filesystem:`. - -## protocol.isHandledProtocol(scheme, callback) +## protocol.isProtocolHandled(scheme, callback) * `scheme` String * `callback` Function -`callback` returns a boolean whether the `scheme` can be handled already. +The `callback` will be called with a boolean that indicates whether there is +already a handler for `scheme`. -## protocol.interceptProtocol(scheme, handler, callback) +## protocol.interceptFileProtocol(scheme, handler[, completion]) * `scheme` String * `handler` Function * `callback` Function -Intercepts an existing protocol with `scheme`, returning `null` or `undefined` -in `handler` would use the original protocol handler to handle the request. +Intercepts `scheme` protocol and use `handler` as the protocol's new handler +which sends file as response. -## protocol.uninterceptProtocol(scheme, callback) +## protocol.interceptStringProtocol(scheme, handler[, completion]) + +* `scheme` String +* `handler` Function +* `callback` Function + +Intercepts `scheme` protocol and use `handler` as the protocol's new handler +which sends String as response. + +## protocol.interceptBufferProtocol(scheme, handler[, completion]) + +* `scheme` String +* `handler` Function +* `callback` Function + +Intercepts `scheme` protocol and use `handler` as the protocol's new handler +which sends `Buffer` as response. + +## protocol.interceptHttpProtocol(scheme, handler[, completion]) + +* `scheme` String +* `handler` Function +* `callback` Function + +Intercepts `scheme` protocol and use `handler` as the protocol's new handler +which sends a new HTTP request as response. + +## protocol.uninterceptProtocol(scheme[, completion]) * `scheme` String * `callback` Function -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. - -## Class: protocol.RequestBufferJob(options) - -* `options` Object - * `mimeType` String - Default is `application/octet-stream` - * `encoding` String - Default is `UTF-8` - * `data` Buffer - -Create a request job which sends a buffer as response. - -## Class: protocol.RequestHttpJob(options) - -* `options` Object - * `session` [Session](browser-window.md#class-session) - By default it is - the app's default session, setting it to `null` will create a new session - for the requests - * `url` String - * `method` String - Default is `GET` - * `referrer` String - -Send a request to `url` and pipe the response back. - -## Class: protocol.RequestErrorJob(code) - -* `code` Integer - -Create a request job which sets appropriate network error message to console. -Default message is `net::ERR_NOT_IMPLEMENTED`. Code should be in the following -range. - -* Ranges: - * 0- 99 System related errors - * 100-199 Connection related errors - * 200-299 Certificate errors - * 300-399 HTTP errors - * 400-499 Cache errors - * 500-599 ? - * 600-699 FTP errors - * 700-799 Certificate manager errors - * 800-899 DNS resolver errors - -Check the [network error list](https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h) for code and message relations. +Remove the interceptor installed for `scheme` and restore its original handler. diff --git a/filenames.gypi b/filenames.gypi index 40af1ebb1fe7..260c9a4c8a17 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -146,8 +146,6 @@ 'atom/browser/native_window_mac.h', 'atom/browser/native_window_mac.mm', 'atom/browser/native_window_observer.h', - 'atom/browser/net/adapter_request_job.cc', - 'atom/browser/net/adapter_request_job.h', 'atom/browser/net/asar/asar_protocol_handler.cc', 'atom/browser/net/asar/asar_protocol_handler.h', 'atom/browser/net/asar/url_request_asar_job.cc', @@ -156,6 +154,10 @@ 'atom/browser/net/atom_url_request_job_factory.h', 'atom/browser/net/http_protocol_handler.cc', 'atom/browser/net/http_protocol_handler.h', + 'atom/browser/net/js_asker.cc', + 'atom/browser/net/js_asker.h', + 'atom/browser/net/url_request_async_asar_job.cc', + 'atom/browser/net/url_request_async_asar_job.h', 'atom/browser/net/url_request_string_job.cc', 'atom/browser/net/url_request_string_job.h', 'atom/browser/net/url_request_buffer_job.cc', diff --git a/package.json b/package.json index 1942bcbc9a04..2986a380c4ea 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "coffee-script": "^1.9.2", "coffeelint": "^1.9.4", "request": "*", - "runas": "^2.0.0" + "runas": "3.x" }, "private": true, "scripts": { diff --git a/script/cibuild b/script/cibuild index c282bb0275a4..67623beba402 100755 --- a/script/cibuild +++ b/script/cibuild @@ -69,9 +69,9 @@ def main(): run_script('build.py', ['-c', 'R']) run_script('create-dist.py') run_script('upload.py') - elif PLATFORM == 'win32' or target_arch == 'x64': + else: run_script('build.py', ['-c', 'D']) - if PLATFORM != 'win32': + if PLATFORM != 'win32' and target_arch == 'x64': run_script('test.py', ['--ci']) run_script('clean.py') diff --git a/script/create-dist.py b/script/create-dist.py index dbecc0a1eb72..c978641df320 100755 --- a/script/create-dist.py +++ b/script/create-dist.py @@ -31,7 +31,6 @@ TARGET_BINARIES = { '{0}.exe'.format(PROJECT_NAME), # 'electron.exe' 'content_shell.pak', 'd3dcompiler_47.dll', - 'ffmpegsumo.dll', 'icudtl.dat', 'libEGL.dll', 'libGLESv2.dll', @@ -50,7 +49,6 @@ TARGET_BINARIES = { PROJECT_NAME, # 'electron' 'content_shell.pak', 'icudtl.dat', - 'libffmpegsumo.so', 'libnode.so', 'natives_blob.bin', 'snapshot_blob.bin', diff --git a/script/lib/config.py b/script/lib/config.py index 1298651b7edf..c363a83a5f44 100644 --- a/script/lib/config.py +++ b/script/lib/config.py @@ -8,7 +8,7 @@ import sys BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \ 'http://github-janky-artifacts.s3.amazonaws.com/libchromiumcontent' -LIBCHROMIUMCONTENT_COMMIT = 'dd51a41b42246b0b5159bfad5e327c8cf10bc585' +LIBCHROMIUMCONTENT_COMMIT = '42200d8ec0b77c7491d3a09611c23eb771e0862d' PLATFORM = { 'cygwin': 'win32', diff --git a/spec/api-protocol-spec.coffee b/spec/api-protocol-spec.coffee index 4f2fb79da372..f540a63ec406 100644 --- a/spec/api-protocol-spec.coffee +++ b/spec/api-protocol-spec.coffee @@ -1,278 +1,342 @@ assert = require 'assert' -ipc = require 'ipc' http = require 'http' path = require 'path' remote = require 'remote' protocol = remote.require 'protocol' describe 'protocol module', -> - describe 'protocol.registerProtocol', -> - it 'error when scheme is already registered', (done) -> - register = -> - protocol.registerProtocol 'test1', ((request) ->), (error, scheme) -> - if error? - protocol.unregisterProtocol 'test1', (error, scheme) -> - assert.equal scheme, 'test1' - done() - else - assert.equal scheme, 'test1' - register() - register() + protocolName = 'sp' + text = 'valar morghulis' - it 'calls the callback when scheme is visited', (done) -> - protocol.registerProtocol 'test2', (request) -> - assert.equal request.url, 'test2://test2' - protocol.unregisterProtocol 'test2' - done() - $.get 'test2://test2', -> + afterEach (done) -> + protocol.unregisterProtocol protocolName, -> + protocol.uninterceptProtocol 'http', -> done() + + describe 'protocol.register(Any)Protocol', -> + emptyHandler = (request, callback) -> callback() + it 'throws error when scheme is already registered', (done) -> + protocol.registerStringProtocol protocolName, emptyHandler, (error) -> + assert.equal error, null + protocol.registerBufferProtocol protocolName, emptyHandler, (error) -> + assert.notEqual error, null + done() + + it 'does not crash when handler is called twice', (done) -> + doubleHandler = (request, callback) -> + callback(text) + callback() + protocol.registerStringProtocol protocolName, doubleHandler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data) -> + assert.equal data, text + done() + error: (xhr, errorType, error) -> + done(error) + + it 'sends error when callback is called with nothing', (done) -> + protocol.registerBufferProtocol protocolName, emptyHandler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data) -> + done('request succeeded but it should not') + error: (xhr, errorType, error) -> + assert.equal errorType, 'error' + done() + + it 'does not crash when callback is called in next tick', (done) -> + handler = (request, callback) -> + setImmediate -> callback(text) + protocol.registerStringProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data) -> + assert.equal data, text + done() + error: (xhr, errorType, error) -> + done(error) describe 'protocol.unregisterProtocol', -> - it 'throws error when scheme does not exist', -> - protocol.unregisterProtocol 'test3', (->), (error, scheme) -> - if (error) - assert.equal scheme, 'test3' - done() + it 'returns error when scheme does not exist', (done) -> + protocol.unregisterProtocol 'not-exist', (error) -> + assert.notEqual error, null + done() - 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 + describe 'protocol.registerStringProtocol', -> + it 'sends string as response', (done) -> + handler = (request, callback) -> callback(text) + protocol.registerStringProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data) -> + assert.equal data, text + done() + error: (xhr, errorType, error) -> + done(error) - $.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 'sends object as response', (done) -> + handler = (request, callback) -> callback(data: text, mimeType: 'text/html') + protocol.registerStringProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data, statux, request) -> + assert.equal data, text + done() + error: (xhr, errorType, error) -> + done(error) - 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 + it 'fails when sending object other than string', (done) -> + handler = (request, callback) -> callback(new Date) + protocol.registerBufferProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data) -> + done('request succeeded but it should not') + error: (xhr, errorType, error) -> + assert.equal errorType, 'error' + done() - $.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' + describe 'protocol.registerBufferProtocol', -> + buffer = new Buffer(text) - it 'returns RequestErrorJob should send error', (done) -> - data = 'valar morghulis' - job = new protocol.RequestErrorJob(-6) - handler = remote.createFunctionWithReturnValue job - protocol.registerProtocol 'atom-error-job', handler + it 'sends Buffer as response', (done) -> + handler = (request, callback) -> callback(buffer) + protocol.registerBufferProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data) -> + assert.equal data, text + done() + error: (xhr, errorType, error) -> + done(error) - $.ajax - url: 'atom-error-job://fake-host' - success: (response) -> - assert false, 'should not reach here' - error: (xhr, errorType, error) -> - assert errorType, 'error' - protocol.unregisterProtocol 'atom-error-job' - done() + it 'sends object as response', (done) -> + handler = (request, callback) -> callback(data: buffer, mimeType: 'text/html') + protocol.registerBufferProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data, statux, request) -> + assert.equal data, text + done() + error: (xhr, errorType, error) -> + done(error) - it 'returns RequestHttpJob should send respone', (done) -> + it 'fails when sending string', (done) -> + handler = (request, callback) -> callback(text) + protocol.registerBufferProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data) -> + done('request succeeded but it should not') + error: (xhr, errorType, error) -> + assert.equal errorType, 'error' + done() + + describe 'protocol.registerFileProtocol', -> + filePath = path.join __dirname, 'fixtures', 'asar', 'a.asar', 'file1' + fileContent = require('fs').readFileSync(filePath) + + normalPath = path.join __dirname, 'fixtures', 'pages', 'a.html' + normalContent = require('fs').readFileSync(normalPath) + + it 'sends file path as response', (done) -> + handler = (request, callback) -> callback(filePath) + protocol.registerFileProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data) -> + assert.equal data, String(fileContent) + done() + error: (xhr, errorType, error) -> + done(error) + + it 'sends object as response', (done) -> + handler = (request, callback) -> callback(path: filePath) + protocol.registerFileProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data, statux, request) -> + assert.equal data, String(fileContent) + done() + error: (xhr, errorType, error) -> + done(error) + + it 'can send normal file', (done) -> + handler = (request, callback) -> callback(normalPath) + protocol.registerFileProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data) -> + assert.equal data, String(normalContent) + done() + error: (xhr, errorType, error) -> + done(error) + + it 'fails when sending unexist-file', (done) -> + fakeFilePath = path.join __dirname, 'fixtures', 'asar', 'a.asar', 'not-exist' + handler = (request, callback) -> callback(fakeFilePath) + protocol.registerBufferProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data) -> + done('request succeeded but it should not') + error: (xhr, errorType, error) -> + assert.equal errorType, 'error' + done() + + it 'fails when sending unsupported content', (done) -> + handler = (request, callback) -> callback(new Date) + protocol.registerBufferProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data) -> + done('request succeeded but it should not') + error: (xhr, errorType, error) -> + assert.equal errorType, 'error' + done() + + describe 'protocol.registerHttpProtocol', -> + it 'sends url as response', (done) -> server = http.createServer (req, res) -> assert.notEqual req.headers.accept, '' - res.writeHead(200, {'Content-Type': 'text/plain'}) - res.end('hello') + res.end(text) server.close() server.listen 0, '127.0.0.1', -> {port} = server.address() url = "http://127.0.0.1:#{port}" - job = new protocol.RequestHttpJob({url}) - handler = remote.createFunctionWithReturnValue job - protocol.registerProtocol 'atom-http-job', handler + handler = (request, callback) -> callback({url}) + protocol.registerHttpProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data) -> + assert.equal data, text + done() + error: (xhr, errorType, error) -> + done(error) + it 'fails when sending invalid url', (done) -> + handler = (request, callback) -> callback({url: 'url'}) + protocol.registerHttpProtocol protocolName, handler, (error) -> $.ajax - url: 'atom-http-job://fake-host' + url: "#{protocolName}://fake-host" success: (data) -> - assert.equal data, 'hello' - protocol.unregisterProtocol 'atom-http-job' - done() + done('request succeeded but it should not') error: (xhr, errorType, error) -> - assert false, 'Got error: ' + errorType + ' ' + error - protocol.unregisterProtocol 'atom-http-job' + assert.equal errorType, 'error' + done() - it 'returns RequestBufferJob should send buffer', (done) -> - data = new Buffer("hello") - job = new protocol.RequestBufferJob(data: data) - handler = remote.createFunctionWithReturnValue job - protocol.registerProtocol 'atom-buffer-job', handler + it 'fails when sending unsupported content', (done) -> + handler = (request, callback) -> callback(new Date) + protocol.registerHttpProtocol protocolName, handler, (error) -> + $.ajax + url: "#{protocolName}://fake-host" + success: (data) -> + done('request succeeded but it should not') + error: (xhr, errorType, error) -> + assert.equal errorType, 'error' + done() - $.ajax - url: 'atom-buffer-job://fake-host' - success: (response) -> - assert.equal response.length, data.length - buf = new Buffer(response.length) - buf.write(response) - assert buf.equals(data) - protocol.unregisterProtocol 'atom-buffer-job' - done() - error: (xhr, errorType, error) -> - assert false, 'Got error: ' + errorType + ' ' + error - protocol.unregisterProtocol 'atom-buffer-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' - - it 'returns RequestFileJob should send file from asar archive', (done) -> - p = path.join __dirname, 'fixtures', 'asar', 'a.asar', 'file1' - job = new protocol.RequestFileJob(p) - handler = remote.createFunctionWithReturnValue job - protocol.registerProtocol 'atom-file-job', handler - - $.ajax - url: 'atom-file-job://' + p - success: (data) -> - content = require('fs').readFileSync(p) - 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' - - it 'returns RequestFileJob should send file from asar archive with unpacked file', (done) -> - p = path.join __dirname, 'fixtures', 'asar', 'unpack.asar', 'a.txt' - job = new protocol.RequestFileJob(p) - handler = remote.createFunctionWithReturnValue job - protocol.registerProtocol 'atom-file-job', handler - - $.ajax - url: 'atom-file-job://' + p - success: (response) -> - data = require('fs').readFileSync(p) - assert.equal response.length, data.length - buf = new Buffer(response.length) - buf.write(response) - assert buf.equals(data) - 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 file scheme can be handled', (done) -> - protocol.isHandledProtocol 'file', (result) -> + describe 'protocol.isProtocolHandled', -> + it 'returns true for file:', (done) -> + protocol.isProtocolHandled 'file', (result) -> assert.equal result, true done() - it 'returns true if the http scheme can be handled', (done) -> - protocol.isHandledProtocol 'http', (result) -> + + it 'returns true for http:', (done) -> + protocol.isProtocolHandled 'http', (result) -> assert.equal result, true done() - it 'returns true if the https scheme can be handled', (done) -> - protocol.isHandledProtocol 'https', (result) -> + + it 'returns true for https:', (done) -> + protocol.isProtocolHandled 'https', (result) -> assert.equal result, true done() - it 'returns false if the atom scheme cannot be handled', (done) -> - protocol.isHandledProtocol 'atom', (result) -> + + it 'returns false when scheme is not registred', (done) -> + protocol.isProtocolHandled 'no-exist', (result) -> assert.equal result, false done() - describe 'protocol.interceptProtocol', -> - it 'throws error when scheme is not a registered one', (done) -> - protocol.interceptProtocol 'test-intercept', ( ->), (error, scheme) -> - if error? - assert.equal scheme, 'test-intercept' + it 'returns true for custom protocol', (done) -> + emptyHandler = (request, callback) -> callback() + protocol.registerStringProtocol protocolName, emptyHandler, (error) -> + assert.equal error, null + protocol.isProtocolHandled protocolName, (result) -> + assert.equal result, true done() - 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' - protocol.interceptProtocol 'test-intercept', (->), (error, newScheme) -> - if error? - assert.equal newScheme, 'test-intercept' - protocol.unregisterProtocol scheme - protocol.registerProtocol('atom', ->) + it 'returns true for intercepted protocol', (done) -> + emptyHandler = (request, callback) -> callback() + protocol.interceptStringProtocol 'http', emptyHandler, (error) -> + assert.equal error, null + protocol.isProtocolHandled 'http', (result) -> + assert.equal result, true + done() - 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) -> - if process.platform is 'win32' - pathInUrl = path.normalize request.url.substr(8) - assert.equal pathInUrl.toLowerCase(), __filename.toLowerCase() - else - assert.equal request.url, "#{targetScheme}://#{__filename}" + describe 'protocol.intercept(Any)Protocol', -> + emptyHandler = (request, callback) -> callback() - it 'can override original protocol handler', (done) -> - handler = remote.createFunctionWithReturnValue 'valar morghulis' - protocol.once 'intercepted', -> - free = -> protocol.uninterceptProtocol 'file' + it 'throws error when scheme is already intercepted', (done) -> + protocol.interceptStringProtocol 'http', emptyHandler, (error) -> + assert.equal error, null + protocol.interceptBufferProtocol 'http', emptyHandler, (error) -> + assert.notEqual error, null + done() + + it 'does not crash when handler is called twice', (done) -> + doubleHandler = (request, callback) -> + callback(text) + callback() + protocol.interceptStringProtocol 'http', doubleHandler, (error) -> $.ajax - url: 'file://fake-host' + url: 'http://fake-host' success: (data) -> - protocol.once 'unintercepted', -> - assert.equal data, handler() - done() - free() + assert.equal data, text + done() error: (xhr, errorType, error) -> - assert false, 'Got error: ' + errorType + ' ' + error - free() - protocol.interceptProtocol 'file', handler + done(error) - it 'can override http protocol handler', (done) -> - handler = remote.createFunctionWithReturnValue 'valar morghulis' - protocol.once 'intercepted', -> - protocol.uninterceptProtocol 'http' - done() - protocol.interceptProtocol 'http', handler + it 'sends error when callback is called with nothing', (done) -> + protocol.interceptBufferProtocol 'http', emptyHandler, (error) -> + $.ajax + url: 'http://fake-host' + success: (data) -> + done('request succeeded but it should not') + error: (xhr, errorType, error) -> + assert.equal errorType, 'error' + done() - it 'can override https protocol handler', (done) -> - handler = remote.createFunctionWithReturnValue 'valar morghulis' - protocol.once 'intercepted', -> - protocol.uninterceptProtocol 'https' - done() - protocol.interceptProtocol 'https', handler + describe 'protocol.interceptStringProtocol', -> + it 'can intercept http protocol', (done) -> + handler = (request, callback) -> callback(text) + protocol.interceptStringProtocol 'http', handler, (error) -> + $.ajax + url: 'http://fake-host' + success: (data) -> + assert.equal data, text + done() + error: (xhr, errorType, error) -> + done(error) - it 'can override ws protocol handler', (done) -> - handler = remote.createFunctionWithReturnValue 'valar morghulis' - protocol.once 'intercepted', -> - protocol.uninterceptProtocol 'ws' - done() - protocol.interceptProtocol 'ws', handler + describe 'protocol.interceptBufferProtocol', -> + it 'can intercept http protocol', (done) -> + handler = (request, callback) -> callback(new Buffer(text)) + protocol.interceptBufferProtocol 'http', handler, (error) -> + $.ajax + url: 'http://fake-host' + success: (data) -> + assert.equal data, text + done() + error: (xhr, errorType, error) -> + done(error) - it 'can override wss protocol handler', (done) -> - handler = remote.createFunctionWithReturnValue 'valar morghulis' - protocol.once 'intercepted', -> - protocol.uninterceptProtocol 'wss' + describe 'protocol.uninterceptProtocol', -> + it 'returns error when scheme does not exist', (done) -> + protocol.uninterceptProtocol 'not-exist', (error) -> + assert.notEqual error, null + done() + + it 'returns error when scheme is not intercepted', (done) -> + protocol.uninterceptProtocol 'http', (error) -> + assert.notEqual error, null done() - protocol.interceptProtocol 'wss', handler diff --git a/spec/package.json b/spec/package.json index 6343832dcc8e..a3b0d590effa 100644 --- a/spec/package.json +++ b/spec/package.json @@ -9,7 +9,7 @@ "graceful-fs": "3.0.5", "mocha": "2.1.0", "q": "0.9.7", - "runas": "2.x", + "runas": "3.x", "temp": "0.8.1", "walkdir": "0.0.7", "ws": "0.7.2" diff --git a/spec/static/main.js b/spec/static/main.js index 601926716e96..4eb415dab222 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -135,7 +135,7 @@ app.on('ready', function() { app.setApplicationMenu(menu); // Test if using protocol module would crash. - require('protocol').registerProtocol('test-if-crashes', function() {}); + require('protocol').registerStringProtocol('test-if-crashes', function() {}); window = new BrowserWindow({ title: 'Electron Tests', diff --git a/vendor/brightray b/vendor/brightray index f4470ee48a74..939a7b814282 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit f4470ee48a748888bccba21845bfd65caaa1a6ce +Subproject commit 939a7b814282a6433b8d7e3c9cfc74451360c07f diff --git a/vendor/native_mate b/vendor/native_mate index 67d9eaa215e8..b41635e80921 160000 --- a/vendor/native_mate +++ b/vendor/native_mate @@ -1 +1 @@ -Subproject commit 67d9eaa215e8727d86dc7b1f7a10be8699848f1f +Subproject commit b41635e80921bddbf1a36f030490e063cd593477 diff --git a/vendor/node b/vendor/node index 7888f607ce74..205b013ac86e 160000 --- a/vendor/node +++ b/vendor/node @@ -1 +1 @@ -Subproject commit 7888f607ce74cc100ceb78f3af258a55ee2f62c4 +Subproject commit 205b013ac86e5500678a791cd54f305580fa4f4b