diff --git a/atom/browser/api/atom_api_cookies.cc b/atom/browser/api/atom_api_cookies.cc index 3b1bd499be4c..6323e5110c18 100644 --- a/atom/browser/api/atom_api_cookies.cc +++ b/atom/browser/api/atom_api_cookies.cc @@ -7,7 +7,6 @@ #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/gurl_converter.h" #include "atom/common/native_mate_converters/value_converter.h" -#include "base/bind.h" #include "base/time/time.h" #include "base/values.h" #include "content/public/browser/browser_context.h" @@ -20,139 +19,21 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" -using atom::api::Cookies; using content::BrowserThread; -namespace { - -bool GetCookieListFromStore( - net::CookieStore* cookie_store, - const std::string& url, - const net::CookieMonster::GetCookieListCallback& callback) { - DCHECK(cookie_store); - GURL gurl(url); - net::CookieMonster* monster = cookie_store->GetCookieMonster(); - // Empty url will match all url cookies. - if (url.empty()) { - monster->GetAllCookiesAsync(callback); - return true; - } - - if (!gurl.is_valid()) - return false; - - monster->GetAllCookiesForURLAsync(gurl, callback); - return true; -} - -void RunGetCookiesCallbackOnUIThread(v8::Isolate* isolate, - const std::string& error_message, - const net::CookieList& cookie_list, - const Cookies::CookiesCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - v8::Locker locker(isolate); - v8::HandleScope handle_scope(isolate); - - if (!error_message.empty()) { - v8::Local error = mate::ConvertToV8(isolate, error_message); - callback.Run(error, v8::Null(isolate)); - return; - } - callback.Run(v8::Null(isolate), mate::ConvertToV8(isolate, cookie_list)); -} - -void RunRemoveCookiesCallbackOnUIThread( - v8::Isolate* isolate, - const std::string& error_message, - const Cookies::CookiesCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - v8::Locker locker(isolate); - v8::HandleScope handle_scope(isolate); - - if (!error_message.empty()) { - v8::Local error = mate::ConvertToV8(isolate, error_message); - callback.Run(error, v8::Null(isolate)); - return; - } - - callback.Run(v8::Null(isolate), v8::Null(isolate)); -} - -void RunSetCookiesCallbackOnUIThread(v8::Isolate* isolate, - const std::string& error_message, - bool set_success, - const Cookies::CookiesCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - - v8::Locker locker(isolate); - v8::HandleScope handle_scope(isolate); - - if (!error_message.empty()) { - v8::Local error = mate::ConvertToV8(isolate, error_message); - callback.Run(error, v8::Null(isolate)); - return; - } - if (!set_success) { - v8::Local error = mate::ConvertToV8( - isolate, "Failed to set cookies"); - callback.Run(error, v8::Null(isolate)); - } - - callback.Run(v8::Null(isolate), v8::Null(isolate)); -} - -bool MatchesDomain(const base::DictionaryValue* filter, - const std::string& cookie_domain) { - std::string filter_domain; - if (!filter->GetString("domain", &filter_domain)) - return true; - - // Add a leading '.' character to the filter domain if it doesn't exist. - if (net::cookie_util::DomainIsHostOnly(filter_domain)) - filter_domain.insert(0, "."); - - std::string sub_domain(cookie_domain); - // Strip any leading '.' character from the input cookie domain. - if (!net::cookie_util::DomainIsHostOnly(sub_domain)) - sub_domain = sub_domain.substr(1); - - // Now check whether the domain argument is a subdomain of the filter domain. - for (sub_domain.insert(0, "."); - sub_domain.length() >= filter_domain.length();) { - if (sub_domain == filter_domain) { - return true; - } - const size_t next_dot = sub_domain.find('.', 1); // Skip over leading dot. - sub_domain.erase(0, next_dot); - } - return false; -} - -bool MatchesCookie(const base::DictionaryValue* filter, - const net::CanonicalCookie& cookie) { - std::string name, domain, path; - bool is_secure, session; - if (filter->GetString("name", &name) && name != cookie.Name()) - return false; - if (filter->GetString("path", &path) && path != cookie.Path()) - return false; - if (!MatchesDomain(filter, cookie.Domain())) - return false; - if (filter->GetBoolean("secure", &is_secure) && - is_secure != cookie.IsSecure()) - return false; - if (filter->GetBoolean("session", &session) && - session != cookie.IsPersistent()) - return false; - return true; -} - -} // namespace - namespace mate { +template<> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + atom::api::Cookies::Error val) { + if (val == atom::api::Cookies::SUCCESS) + return v8::Null(isolate); + else + return v8::Exception::Error(StringToV8(isolate, "failed")); + } +}; + template<> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, @@ -161,11 +42,11 @@ struct Converter { dict.Set("name", val.Name()); dict.Set("value", val.Value()); dict.Set("domain", val.Domain()); - dict.Set("host_only", net::cookie_util::DomainIsHostOnly(val.Domain())); + dict.Set("hostOnly", net::cookie_util::DomainIsHostOnly(val.Domain())); dict.Set("path", val.Path()); dict.Set("secure", val.IsSecure()); - dict.Set("http_only", val.IsHttpOnly()); - dict.Set("session", val.IsPersistent()); + dict.Set("httpOnly", val.IsHttpOnly()); + dict.Set("session", !val.IsPersistent()); if (!val.IsPersistent()) dict.Set("expirationDate", val.ExpiryDate().ToDoubleT()); return dict.GetHandle(); @@ -178,121 +59,117 @@ namespace atom { namespace api { -Cookies::Cookies(content::BrowserContext* browser_context) - : request_context_getter_(browser_context->GetRequestContext()) { -} +namespace { -Cookies::~Cookies() { -} +// Returns whether |domain| matches |filter|. +bool MatchesDomain(std::string filter, const std::string& domain) { + // Add a leading '.' character to the filter domain if it doesn't exist. + if (net::cookie_util::DomainIsHostOnly(filter)) + filter.insert(0, "."); -void Cookies::Get(const base::DictionaryValue& options, - const CookiesCallback& callback) { - scoped_ptr filter( - options.DeepCopyWithoutEmptyChildren()); + std::string sub_domain(domain); + // Strip any leading '.' character from the input cookie domain. + if (!net::cookie_util::DomainIsHostOnly(sub_domain)) + sub_domain = sub_domain.substr(1); - content::BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&Cookies::GetCookiesOnIOThread, base::Unretained(this), - Passed(&filter), callback)); -} - -void Cookies::GetCookiesOnIOThread(scoped_ptr filter, - const CookiesCallback& callback) { - std::string url; - filter->GetString("url", &url); - if (!GetCookieListFromStore(GetCookieStore(), url, - base::Bind(&Cookies::OnGetCookies, base::Unretained(this), - Passed(&filter), callback))) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&RunGetCookiesCallbackOnUIThread, isolate(), - "URL is not valid", net::CookieList(), callback)); + // Now check whether the domain argument is a subdomain of the filter domain. + for (sub_domain.insert(0, "."); sub_domain.length() >= filter.length();) { + if (sub_domain == filter) + return true; + const size_t next_dot = sub_domain.find('.', 1); // Skip over leading dot. + sub_domain.erase(0, next_dot); } + return false; } -void Cookies::OnGetCookies(scoped_ptr filter, - const CookiesCallback& callback, - const net::CookieList& cookie_list) { +// Returns whether |cookie| matches |filter|. +bool MatchesCookie(const base::DictionaryValue* filter, + const net::CanonicalCookie& cookie) { + std::string str; + bool b; + if (filter->GetString("name", &str) && str != cookie.Name()) + return false; + if (filter->GetString("path", &str) && str != cookie.Path()) + return false; + if (filter->GetString("domain", &str) && !MatchesDomain(str, cookie.Domain())) + return false; + if (filter->GetBoolean("secure", &b) && b != cookie.IsSecure()) + return false; + if (filter->GetBoolean("session", &b) && b != !cookie.IsPersistent()) + return false; + return true; +} + +// Helper to returns the CookieStore. +inline net::CookieStore* GetCookieStore( + scoped_refptr getter) { + return getter->GetURLRequestContext()->cookie_store(); +} + +// Run |callback| on UI thread. +void RunCallbackInUI(const base::Closure& callback) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback); +} + +// Remove cookies from |list| not matching |filter|, and pass it to |callback|. +void FilterCookies(scoped_ptr filter, + const Cookies::GetCallback& callback, + const net::CookieList& list) { net::CookieList result; - for (const auto& cookie : cookie_list) { + for (const auto& cookie : list) { if (MatchesCookie(filter.get(), cookie)) result.push_back(cookie); } - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( - &RunGetCookiesCallbackOnUIThread, isolate(), "", result, callback)); + RunCallbackInUI(base::Bind(callback, Cookies::SUCCESS, result)); } -void Cookies::Remove(const mate::Dictionary& details, - const CookiesCallback& callback) { - GURL url; - std::string name; - std::string error_message; - if (!details.Get("url", &url) || !details.Get("name", &name)) { - error_message = "Details(url, name) of removing cookie are required."; - } - if (error_message.empty() && !url.is_valid()) { - error_message = "URL is not valid."; - } - if (!error_message.empty()) { - RunRemoveCookiesCallbackOnUIThread(isolate(), error_message, callback); - return; - } - content::BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&Cookies::RemoveCookiesOnIOThread, base::Unretained(this), - url, name, callback)); -} - -void Cookies::RemoveCookiesOnIOThread(const GURL& url, const std::string& name, - const CookiesCallback& callback) { - GetCookieStore()->DeleteCookieAsync(url, name, - base::Bind(&Cookies::OnRemoveCookies, base::Unretained(this), callback)); -} - -void Cookies::OnRemoveCookies(const CookiesCallback& callback) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&RunRemoveCookiesCallbackOnUIThread, isolate(), "", callback)); -} - -void Cookies::Set(const base::DictionaryValue& options, - const CookiesCallback& callback) { +// Receives cookies matching |filter| in IO thread. +void GetCookiesOnIO(scoped_refptr getter, + scoped_ptr filter, + const Cookies::GetCallback& callback) { std::string url; - std::string error_message; - if (!options.GetString("url", &url)) { - error_message = "The url field is required."; - } + filter->GetString("url", &url); - GURL gurl(url); - if (error_message.empty() && !gurl.is_valid()) { - error_message = "URL is not valid."; - } + auto filtered_callback = + base::Bind(FilterCookies, base::Passed(&filter), callback); - if (!error_message.empty()) { - RunSetCookiesCallbackOnUIThread(isolate(), error_message, false, callback); - return; - } - - scoped_ptr details( - options.DeepCopyWithoutEmptyChildren()); - - content::BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&Cookies::SetCookiesOnIOThread, base::Unretained(this), - Passed(&details), gurl, callback)); + net::CookieMonster* monster = GetCookieStore(getter)->GetCookieMonster(); + // Empty url will match all url cookies. + if (url.empty()) + monster->GetAllCookiesAsync(filtered_callback); + else + monster->GetAllCookiesForURLAsync(GURL(url), filtered_callback); } -void Cookies::SetCookiesOnIOThread(scoped_ptr details, - const GURL& url, - const CookiesCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); +// Removes cookie with |url| and |name| in IO thread. +void RemoveCookieOnIOThread(scoped_refptr getter, + const GURL& url, const std::string& name, + const base::Closure& callback) { + GetCookieStore(getter)->DeleteCookieAsync( + url, name, base::Bind(RunCallbackInUI, callback)); +} - std::string name, value, domain, path; +// Callback of SetCookie. +void OnSetCookie(const Cookies::SetCallback& callback, bool success) { + RunCallbackInUI( + base::Bind(callback, success ? Cookies::SUCCESS : Cookies::FAILED)); +} + +// Sets cookie with |details| in IO thread. +void SetCookieOnIO(scoped_refptr getter, + scoped_ptr details, + const Cookies::SetCallback& callback) { + std::string url, name, value, domain, path; bool secure = false; bool http_only = false; double expiration_date; - + details->GetString("url", &url); details->GetString("name", &name); details->GetString("value", &value); details->GetString("domain", &domain); details->GetString("path", &path); details->GetBoolean("secure", &secure); - details->GetBoolean("http_only", &http_only); + details->GetBoolean("httpOnly", &http_only); base::Time expiration_time; if (details->GetDouble("expirationDate", &expiration_date)) { @@ -301,29 +178,44 @@ void Cookies::SetCookiesOnIOThread(scoped_ptr details, base::Time::FromDoubleT(expiration_date); } - GetCookieStore()->GetCookieMonster()->SetCookieWithDetailsAsync( - url, - name, - value, - domain, - path, - expiration_time, - secure, - http_only, - false, - net::COOKIE_PRIORITY_DEFAULT, - base::Bind(&Cookies::OnSetCookies, base::Unretained(this), callback)); + GetCookieStore(getter)->GetCookieMonster()->SetCookieWithDetailsAsync( + GURL(url), name, value, domain, path, expiration_time, secure, http_only, + false, net::COOKIE_PRIORITY_DEFAULT, base::Bind(OnSetCookie, callback)); } -void Cookies::OnSetCookies(const CookiesCallback& callback, - bool set_success) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&RunSetCookiesCallbackOnUIThread, isolate(), "", set_success, - callback)); +} // namespace + +Cookies::Cookies(content::BrowserContext* browser_context) + : request_context_getter_(browser_context->GetRequestContext()) { } -net::CookieStore* Cookies::GetCookieStore() { - return request_context_getter_->GetURLRequestContext()->cookie_store(); +Cookies::~Cookies() { +} + +void Cookies::Get(const base::DictionaryValue& filter, + const GetCallback& callback) { + scoped_ptr copied(filter.CreateDeepCopy()); + auto getter = make_scoped_refptr(request_context_getter_); + content::BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(GetCookiesOnIO, getter, Passed(&copied), callback)); +} + +void Cookies::Remove(const GURL& url, const std::string& name, + const base::Closure& callback) { + auto getter = make_scoped_refptr(request_context_getter_); + content::BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(RemoveCookieOnIOThread, getter, url, name, callback)); +} + +void Cookies::Set(const base::DictionaryValue& details, + const SetCallback& callback) { + scoped_ptr copied(details.CreateDeepCopy()); + auto getter = make_scoped_refptr(request_context_getter_); + content::BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(SetCookieOnIO, getter, Passed(&copied), callback)); } // static diff --git a/atom/browser/api/atom_api_cookies.h b/atom/browser/api/atom_api_cookies.h index 5afa1bd23ca6..302fd1b25110 100644 --- a/atom/browser/api/atom_api_cookies.h +++ b/atom/browser/api/atom_api_cookies.h @@ -20,12 +20,7 @@ namespace content { class BrowserContext; } -namespace mate { -class Dictionary; -} - namespace net { -class CookieStore; class URLRequestContextGetter; } @@ -35,9 +30,13 @@ namespace api { class Cookies : public mate::TrackableObject { public: - // node.js style callback function(error, result) - typedef base::Callback, v8::Local)> - CookiesCallback; + enum Error { + SUCCESS, + FAILED, + }; + + using GetCallback = base::Callback; + using SetCallback = base::Callback; static mate::Handle Create(v8::Isolate* isolate, content::BrowserContext* browser_context); @@ -50,34 +49,12 @@ class Cookies : public mate::TrackableObject { explicit Cookies(content::BrowserContext* browser_context); ~Cookies(); - void Get(const base::DictionaryValue& options, - const CookiesCallback& callback); - void Remove(const mate::Dictionary& details, - const CookiesCallback& callback); - void Set(const base::DictionaryValue& details, - const CookiesCallback& callback); - - void GetCookiesOnIOThread(scoped_ptr filter, - const CookiesCallback& callback); - void OnGetCookies(scoped_ptr filter, - const CookiesCallback& callback, - const net::CookieList& cookie_list); - - void RemoveCookiesOnIOThread(const GURL& url, - const std::string& name, - const CookiesCallback& callback); - void OnRemoveCookies(const CookiesCallback& callback); - - void SetCookiesOnIOThread(scoped_ptr details, - const GURL& url, - const CookiesCallback& callback); - void OnSetCookies(const CookiesCallback& callback, - bool set_success); + void Get(const base::DictionaryValue& filter, const GetCallback& callback); + void Remove(const GURL& url, const std::string& name, + const base::Closure& callback); + void Set(const base::DictionaryValue& details, const SetCallback& callback); private: - // Must be called on IO thread. - net::CookieStore* GetCookieStore(); - net::URLRequestContextGetter* request_context_getter_; DISALLOW_COPY_AND_ASSIGN(Cookies); diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index a70b6cf4e0b2..cb89db911f06 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -5,6 +5,7 @@ #include "atom/browser/api/atom_api_web_contents.h" #include +#include #include "atom/browser/api/atom_api_session.h" #include "atom/browser/api/atom_api_window.h" diff --git a/atom/browser/api/atom_api_web_request.cc b/atom/browser/api/atom_api_web_request.cc index 07c0576977dc..a987369ed82d 100644 --- a/atom/browser/api/atom_api_web_request.cc +++ b/atom/browser/api/atom_api_web_request.cc @@ -4,6 +4,8 @@ #include "atom/browser/api/atom_api_web_request.h" +#include + #include "atom/browser/atom_browser_context.h" #include "atom/browser/net/atom_network_delegate.h" #include "atom/common/native_mate_converters/callback.h" @@ -15,6 +17,21 @@ using content::BrowserThread; +namespace mate { + +template<> +struct Converter { + static bool FromV8(v8::Isolate* isolate, v8::Local val, + extensions::URLPattern* out) { + std::string pattern; + if (!ConvertFromV8(isolate, val, &pattern)) + return false; + return out->Parse(pattern) == extensions::URLPattern::PARSE_SUCCESS; + } +}; + +} // namespace mate + namespace atom { namespace api { @@ -26,23 +43,38 @@ WebRequest::WebRequest(AtomBrowserContext* browser_context) WebRequest::~WebRequest() { } -template -void WebRequest::SetListener(mate::Arguments* args) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); +template +void WebRequest::SetSimpleListener(mate::Arguments* args) { + SetListener( + &AtomNetworkDelegate::SetSimpleListenerInIO, type, args); +} - scoped_ptr filter(new base::DictionaryValue()); - args->GetNext(filter.get()); - AtomNetworkDelegate::Listener callback; - if (!args->GetNext(&callback)) { - args->ThrowError("Must pass null or a function"); +template +void WebRequest::SetResponseListener(mate::Arguments* args) { + SetListener( + &AtomNetworkDelegate::SetResponseListenerInIO, type, args); +} + +template +void WebRequest::SetListener(Method method, Event type, mate::Arguments* args) { + // { urls }. + URLPatterns patterns; + mate::Dictionary dict; + args->GetNext(&dict) && dict.Get("urls", &patterns); + + // Function or null. + v8::Local value; + Listener listener; + if (!args->GetNext(&listener) && + !(args->GetNext(&value) && value->IsNull())) { + args->ThrowError("Must pass null or a Function"); return; } auto delegate = browser_context_->network_delegate(); BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&AtomNetworkDelegate::SetListenerInIO, - base::Unretained(delegate), - type, base::Passed(&filter), callback)); + base::Bind(method, base::Unretained(delegate), type, + patterns, listener)); } // static @@ -57,28 +89,28 @@ void WebRequest::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { mate::ObjectTemplateBuilder(isolate, prototype) .SetMethod("onBeforeRequest", - &WebRequest::SetListener< + &WebRequest::SetResponseListener< AtomNetworkDelegate::kOnBeforeRequest>) .SetMethod("onBeforeSendHeaders", - &WebRequest::SetListener< + &WebRequest::SetResponseListener< AtomNetworkDelegate::kOnBeforeSendHeaders>) - .SetMethod("onSendHeaders", - &WebRequest::SetListener< - AtomNetworkDelegate::kOnSendHeaders>) .SetMethod("onHeadersReceived", - &WebRequest::SetListener< + &WebRequest::SetResponseListener< AtomNetworkDelegate::kOnHeadersReceived>) + .SetMethod("onSendHeaders", + &WebRequest::SetSimpleListener< + AtomNetworkDelegate::kOnSendHeaders>) .SetMethod("onBeforeRedirect", - &WebRequest::SetListener< + &WebRequest::SetSimpleListener< AtomNetworkDelegate::kOnBeforeRedirect>) .SetMethod("onResponseStarted", - &WebRequest::SetListener< + &WebRequest::SetSimpleListener< AtomNetworkDelegate::kOnResponseStarted>) .SetMethod("onCompleted", - &WebRequest::SetListener< + &WebRequest::SetSimpleListener< AtomNetworkDelegate::kOnCompleted>) .SetMethod("onErrorOccurred", - &WebRequest::SetListener< + &WebRequest::SetSimpleListener< AtomNetworkDelegate::kOnErrorOccurred>); } diff --git a/atom/browser/api/atom_api_web_request.h b/atom/browser/api/atom_api_web_request.h index 597bb80f7c65..9a6e17a04605 100644 --- a/atom/browser/api/atom_api_web_request.h +++ b/atom/browser/api/atom_api_web_request.h @@ -5,8 +5,6 @@ #ifndef ATOM_BROWSER_API_ATOM_API_WEB_REQUEST_H_ #define ATOM_BROWSER_API_ATOM_API_WEB_REQUEST_H_ -#include - #include "atom/browser/api/trackable_object.h" #include "atom/browser/net/atom_network_delegate.h" #include "native_mate/arguments.h" @@ -31,8 +29,13 @@ class WebRequest : public mate::TrackableObject { explicit WebRequest(AtomBrowserContext* browser_context); ~WebRequest(); - template - void SetListener(mate::Arguments* args); + // C++ can not distinguish overloaded member function. + template + void SetSimpleListener(mate::Arguments* args); + template + void SetResponseListener(mate::Arguments* args); + template + void SetListener(Method method, Event type, mate::Arguments* args); private: scoped_refptr browser_context_; diff --git a/atom/browser/api/lib/session.coffee b/atom/browser/api/lib/session.coffee index 7fc3c2df6f96..5c65aa29cf6a 100644 --- a/atom/browser/api/lib/session.coffee +++ b/atom/browser/api/lib/session.coffee @@ -6,6 +6,7 @@ PERSIST_PERFIX = 'persist:' # Returns the Session from |partition| string. exports.fromPartition = (partition='') -> + return exports.defaultSession if partition is '' if partition.startsWith PERSIST_PERFIX bindings.fromPartition partition.substr(PERSIST_PERFIX.length), false else @@ -14,7 +15,7 @@ exports.fromPartition = (partition='') -> # Returns the default session. Object.defineProperty exports, 'defaultSession', enumerable: true - get: -> exports.fromPartition 'persist:' + get: -> bindings.fromPartition '', false wrapSession = (session) -> # session is an EventEmitter. diff --git a/atom/browser/net/atom_network_delegate.cc b/atom/browser/net/atom_network_delegate.cc index c7a54ee66492..6bc25027db1f 100644 --- a/atom/browser/net/atom_network_delegate.cc +++ b/atom/browser/net/atom_network_delegate.cc @@ -4,7 +4,11 @@ #include "atom/browser/net/atom_network_delegate.h" +#include + #include "atom/common/native_mate_converters/net_converter.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/resource_request_info.h" #include "net/url_request/url_request.h" @@ -15,7 +19,7 @@ namespace atom { namespace { -std::string ResourceTypeToString(content::ResourceType type) { +const char* ResourceTypeToString(content::ResourceType type) { switch (type) { case content::RESOURCE_TYPE_MAIN_FRAME: return "mainFrame"; @@ -36,98 +40,171 @@ std::string ResourceTypeToString(content::ResourceType type) { } } -AtomNetworkDelegate::BlockingResponse RunListener( - const AtomNetworkDelegate::Listener& callback, - scoped_ptr details) { - return callback.Run(*(details.get())); +void RunSimpleListener(const AtomNetworkDelegate::SimpleListener& listener, + scoped_ptr details) { + return listener.Run(*(details.get())); } -bool MatchesFilterCondition( - net::URLRequest* request, - const AtomNetworkDelegate::ListenerInfo& info) { - if (!info.url_patterns.empty()) { - auto url = request->url(); - for (auto& pattern : info.url_patterns) - if (pattern.MatchesURL(url)) - return true; - return false; +void RunResponseListener( + const AtomNetworkDelegate::ResponseListener& listener, + scoped_ptr details, + const AtomNetworkDelegate::ResponseCallback& callback) { + return listener.Run(*(details.get()), callback); +} + +// Test whether the URL of |request| matches |patterns|. +bool MatchesFilterCondition(net::URLRequest* request, + const URLPatterns& patterns) { + if (patterns.empty()) + return true; + + for (const auto& pattern : patterns) { + if (pattern.MatchesURL(request->url())) + return true; } - - return true; + return false; } -scoped_ptr ExtractRequestInfo(net::URLRequest* request) { - scoped_ptr dict(new base::DictionaryValue()); - dict->SetInteger("id", request->identifier()); - dict->SetString("url", request->url().spec()); - dict->SetString("method", request->method()); - content::ResourceType resourceType = content::RESOURCE_TYPE_LAST_TYPE; +// Overloaded by multiple types to fill the |details| object. +void ToDictionary(base::DictionaryValue* details, net::URLRequest* request) { + details->SetInteger("id", request->identifier()); + details->SetString("url", request->url().spec()); + details->SetString("method", request->method()); + details->SetDouble("timestamp", base::Time::Now().ToDoubleT() * 1000); auto info = content::ResourceRequestInfo::ForRequest(request); - if (info) - resourceType = info->GetResourceType(); - dict->SetString("resourceType", ResourceTypeToString(resourceType)); - dict->SetDouble("timestamp", base::Time::Now().ToDoubleT() * 1000); - - return dict.Pass(); + details->SetString("resourceType", + info ? ResourceTypeToString(info->GetResourceType()) + : "other"); } -scoped_ptr GetRequestHeadersDict( - const net::HttpRequestHeaders& headers) { - scoped_ptr header_dict(new base::DictionaryValue()); +void ToDictionary(base::DictionaryValue* details, + const net::HttpRequestHeaders& headers) { + scoped_ptr dict(new base::DictionaryValue); net::HttpRequestHeaders::Iterator it(headers); while (it.GetNext()) - header_dict->SetString(it.name(), it.value()); - return header_dict.Pass(); + dict->SetString(it.name(), it.value()); + details->Set("requestHeaders", dict.Pass()); } -scoped_ptr GetResponseHeadersDict( - const net::HttpResponseHeaders* headers) { - scoped_ptr header_dict(new base::DictionaryValue()); - if (headers) { - void* iter = nullptr; - std::string key; - std::string value; - while (headers->EnumerateHeaderLines(&iter, &key, &value)) - header_dict->SetString(key, value); - } - return header_dict.Pass(); -} +void ToDictionary(base::DictionaryValue* details, + const net::HttpResponseHeaders* headers) { + if (!headers) + return; -void OnBeforeURLRequestResponse( - const net::CompletionCallback& callback, - GURL* new_url, - const AtomNetworkDelegate::BlockingResponse& result) { - if (!result.redirect_url.is_empty()) - *new_url = result.redirect_url; - callback.Run(result.Code()); -} - -void OnBeforeSendHeadersResponse( - const net::CompletionCallback& callback, - net::HttpRequestHeaders* headers, - const AtomNetworkDelegate::BlockingResponse& result) { - if (!result.request_headers.IsEmpty()) - *headers = result.request_headers; - callback.Run(result.Code()); -} - -void OnHeadersReceivedResponse( - const net::CompletionCallback& callback, - const net::HttpResponseHeaders* original_response_headers, - scoped_refptr* override_response_headers, - const AtomNetworkDelegate::BlockingResponse& result) { - if (result.response_headers.get()) { - *override_response_headers = new net::HttpResponseHeaders( - original_response_headers->raw_headers()); - void* iter = nullptr; - std::string key; - std::string value; - while (result.response_headers->EnumerateHeaderLines(&iter, &key, &value)) { - (*override_response_headers)->RemoveHeader(key); - (*override_response_headers)->AddHeader(key + ": " + value); + scoped_ptr dict(new base::DictionaryValue); + void* iter = nullptr; + std::string key; + std::string value; + while (headers->EnumerateHeaderLines(&iter, &key, &value)) { + if (dict->HasKey(key)) { + base::ListValue* values = nullptr; + if (dict->GetList(key, &values)) + values->AppendString(value); + } else { + scoped_ptr values(new base::ListValue); + values->AppendString(value); + dict->Set(key, values.Pass()); } } - callback.Run(result.Code()); + details->Set("responseHeaders", dict.Pass()); + details->SetString("statusLine", headers->GetStatusLine()); + details->SetInteger("statusCode", headers->response_code()); +} + +void ToDictionary(base::DictionaryValue* details, const GURL& location) { + details->SetString("redirectURL", location.spec()); +} + +void ToDictionary(base::DictionaryValue* details, + const net::HostPortPair& host_port) { + if (host_port.host().empty()) + details->SetString("ip", host_port.host()); +} + +void ToDictionary(base::DictionaryValue* details, bool from_cache) { + details->SetBoolean("fromCache", from_cache); +} + +void ToDictionary(base::DictionaryValue* details, + const net::URLRequestStatus& status) { + details->SetString("error", net::ErrorToString(status.error())); +} + +// Helper function to fill |details| with arbitrary |args|. +template +void FillDetailsObject(base::DictionaryValue* details, Arg arg) { + ToDictionary(details, arg); +} + +template +void FillDetailsObject(base::DictionaryValue* details, Arg arg, Args... args) { + ToDictionary(details, arg); + FillDetailsObject(details, args...); +} + +// Fill the native types with the result from the response object. +void ReadFromResponseObject(const base::DictionaryValue& response, + GURL* new_location) { + std::string url; + if (response.GetString("redirectURL", &url)) + *new_location = GURL(url); +} + +void ReadFromResponseObject(const base::DictionaryValue& response, + net::HttpRequestHeaders* headers) { + const base::DictionaryValue* dict; + if (response.GetDictionary("requestHeaders", &dict)) { + for (base::DictionaryValue::Iterator it(*dict); + !it.IsAtEnd(); + it.Advance()) { + std::string value; + if (it.value().GetAsString(&value)) + headers->SetHeader(it.key(), value); + } + } +} + +void ReadFromResponseObject(const base::DictionaryValue& response, + scoped_refptr* headers) { + const base::DictionaryValue* dict; + if (response.GetDictionary("responseHeaders", &dict)) { + *headers = new net::HttpResponseHeaders(""); + for (base::DictionaryValue::Iterator it(*dict); + !it.IsAtEnd(); + it.Advance()) { + const base::ListValue* list; + if (it.value().GetAsList(&list)) { + (*headers)->RemoveHeader(it.key()); + for (size_t i = 0; i < list->GetSize(); ++i) { + std::string value; + if (list->GetString(i, &value)) + (*headers)->AddHeader(it.key() + " : " + value); + } + } + } + } +} + +// Deal with the results of Listener. +template +void OnListenerResultInIO(const net::CompletionCallback& callback, + T out, + scoped_ptr response) { + ReadFromResponseObject(*response.get(), out); + + bool cancel = false; + response->GetBoolean("cancel", &cancel); + callback.Run(cancel ? net::ERR_BLOCKED_BY_CLIENT : net::OK); +} + +template +void OnListenerResultInUI(const net::CompletionCallback& callback, + T out, + const base::DictionaryValue& response) { + scoped_ptr copy = response.CreateDeepCopy(); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::Bind(OnListenerResultInIO, callback, out, base::Passed(©))); } } // namespace @@ -138,256 +215,166 @@ AtomNetworkDelegate::AtomNetworkDelegate() { AtomNetworkDelegate::~AtomNetworkDelegate() { } -void AtomNetworkDelegate::SetListenerInIO( - EventTypes type, - scoped_ptr filter, - const Listener& callback) { - if (callback.is_null()) { - event_listener_map_.erase(type); - return; - } +void AtomNetworkDelegate::SetSimpleListenerInIO( + SimpleEvent type, + const URLPatterns& patterns, + const SimpleListener& callback) { + if (callback.is_null()) + simple_listeners_.erase(type); + else + simple_listeners_[type] = { patterns, callback }; +} - ListenerInfo info; - info.callback = callback; - - const base::ListValue* url_list = nullptr; - if (filter->GetList("urls", &url_list)) { - for (size_t i = 0; i < url_list->GetSize(); ++i) { - std::string url; - extensions::URLPattern pattern; - if (url_list->GetString(i, &url) && - pattern.Parse(url) == extensions::URLPattern::PARSE_SUCCESS) { - info.url_patterns.insert(pattern); - } - } - } - - event_listener_map_[type] = info; +void AtomNetworkDelegate::SetResponseListenerInIO( + ResponseEvent type, + const URLPatterns& patterns, + const ResponseListener& callback) { + if (callback.is_null()) + response_listeners_.erase(type); + else + response_listeners_[type] = { patterns, callback }; } int AtomNetworkDelegate::OnBeforeURLRequest( net::URLRequest* request, const net::CompletionCallback& callback, GURL* new_url) { - auto listener_info = event_listener_map_.find(kOnBeforeRequest); - if (listener_info != event_listener_map_.end()) { - if (!MatchesFilterCondition(request, listener_info->second)) - return net::OK; + if (!ContainsKey(response_listeners_, kOnBeforeRequest)) + return brightray::NetworkDelegate::OnBeforeURLRequest( + request, callback, new_url); - auto wrapped_callback = listener_info->second.callback; - auto details = ExtractRequestInfo(request); - - BrowserThread::PostTaskAndReplyWithResult(BrowserThread::UI, FROM_HERE, - base::Bind(&RunListener, wrapped_callback, base::Passed(&details)), - base::Bind(&OnBeforeURLRequestResponse, - callback, new_url)); - - return net::ERR_IO_PENDING; - } - - return brightray::NetworkDelegate::OnBeforeURLRequest(request, - callback, - new_url); + return HandleResponseEvent(kOnBeforeRequest, request, callback, new_url); } int AtomNetworkDelegate::OnBeforeSendHeaders( net::URLRequest* request, const net::CompletionCallback& callback, net::HttpRequestHeaders* headers) { - auto listener_info = event_listener_map_.find(kOnBeforeSendHeaders); - if (listener_info != event_listener_map_.end()) { - if (!MatchesFilterCondition(request, listener_info->second)) - return net::OK; + if (!ContainsKey(response_listeners_, kOnBeforeSendHeaders)) + return brightray::NetworkDelegate::OnBeforeSendHeaders( + request, callback, headers); - auto wrapped_callback = listener_info->second.callback; - auto details = ExtractRequestInfo(request); - details->Set("requestHeaders", GetRequestHeadersDict(*headers).release()); - - BrowserThread::PostTaskAndReplyWithResult(BrowserThread::UI, FROM_HERE, - base::Bind(&RunListener, wrapped_callback, base::Passed(&details)), - base::Bind(&OnBeforeSendHeadersResponse, - callback, headers)); - - return net::ERR_IO_PENDING; - } - - return brightray::NetworkDelegate::OnBeforeSendHeaders(request, - callback, - headers); + return HandleResponseEvent( + kOnBeforeSendHeaders, request, callback, headers, *headers); } void AtomNetworkDelegate::OnSendHeaders( net::URLRequest* request, const net::HttpRequestHeaders& headers) { - auto listener_info = event_listener_map_.find(kOnSendHeaders); - if (listener_info != event_listener_map_.end()) { - if (!MatchesFilterCondition(request, listener_info->second)) - return; - - auto wrapped_callback = listener_info->second.callback; - auto details = ExtractRequestInfo(request); - details->Set("requestHeaders", GetRequestHeadersDict(headers).release()); - - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(base::IgnoreResult(&RunListener), - wrapped_callback, - base::Passed(&details))); - } else { + if (!ContainsKey(simple_listeners_, kOnSendHeaders)) { brightray::NetworkDelegate::OnSendHeaders(request, headers); + return; } + + HandleSimpleEvent(kOnSendHeaders, request, headers); } int AtomNetworkDelegate::OnHeadersReceived( net::URLRequest* request, const net::CompletionCallback& callback, - const net::HttpResponseHeaders* original_response_headers, - scoped_refptr* override_response_headers, - GURL* allowed_unsafe_redirect_url) { - auto listener_info = event_listener_map_.find(kOnHeadersReceived); - if (listener_info != event_listener_map_.end()) { - if (!MatchesFilterCondition(request, listener_info->second)) - return net::OK; + const net::HttpResponseHeaders* original, + scoped_refptr* override, + GURL* allowed) { + if (!ContainsKey(response_listeners_, kOnHeadersReceived)) + return brightray::NetworkDelegate::OnHeadersReceived( + request, callback, original, override, allowed); - auto wrapped_callback = listener_info->second.callback; - auto details = ExtractRequestInfo(request); - details->SetString("statusLine", - original_response_headers->GetStatusLine()); - details->SetInteger("statusCode", - original_response_headers->response_code()); - details->Set("responseHeaders", - GetResponseHeadersDict(original_response_headers).release()); - - BrowserThread::PostTaskAndReplyWithResult(BrowserThread::UI, FROM_HERE, - base::Bind(&RunListener, wrapped_callback, base::Passed(&details)), - base::Bind(&OnHeadersReceivedResponse, - callback, - original_response_headers, - override_response_headers)); - - return net::ERR_IO_PENDING; - } - - return brightray::NetworkDelegate::OnHeadersReceived( - request, callback, original_response_headers, override_response_headers, - allowed_unsafe_redirect_url); + return HandleResponseEvent( + kOnHeadersReceived, request, callback, override, original); } void AtomNetworkDelegate::OnBeforeRedirect(net::URLRequest* request, - const GURL& new_location) { - auto listener_info = event_listener_map_.find(kOnBeforeRedirect); - if (listener_info != event_listener_map_.end()) { - if (!MatchesFilterCondition(request, listener_info->second)) - return; - - auto wrapped_callback = listener_info->second.callback; - auto details = ExtractRequestInfo(request); - details->SetString("redirectURL", new_location.spec()); - details->SetInteger("statusCode", request->GetResponseCode()); - auto ip = request->GetSocketAddress().host(); - if (!ip.empty()) - details->SetString("ip", ip); - details->SetBoolean("fromCache", request->was_cached()); - details->Set("responseHeaders", - GetResponseHeadersDict(request->response_headers()).release()); - - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(base::IgnoreResult(&RunListener), - wrapped_callback, - base::Passed(&details))); - } else { + const GURL& new_location) { + if (!ContainsKey(simple_listeners_, kOnBeforeRedirect)) { brightray::NetworkDelegate::OnBeforeRedirect(request, new_location); + return; } + + HandleSimpleEvent(kOnBeforeRedirect, request, new_location, + request->response_headers(), request->GetSocketAddress(), + request->was_cached()); } void AtomNetworkDelegate::OnResponseStarted(net::URLRequest* request) { - auto listener_info = event_listener_map_.find(kOnResponseStarted); - if (listener_info != event_listener_map_.end()) { - if (request->status().status() != net::URLRequestStatus::SUCCESS) - return; - - if (!MatchesFilterCondition(request, listener_info->second)) - return; - - auto wrapped_callback = listener_info->second.callback; - auto details = ExtractRequestInfo(request); - details->Set("responseHeaders", - GetResponseHeadersDict(request->response_headers()).release()); - details->SetBoolean("fromCache", request->was_cached()); - - auto response_headers = request->response_headers(); - details->SetInteger("statusCode", - response_headers ? - response_headers->response_code() : 200); - details->SetString("statusLine", - response_headers ? - response_headers->GetStatusLine() : std::string()); - - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(base::IgnoreResult(&RunListener), - wrapped_callback, - base::Passed(&details))); - } else { + if (!ContainsKey(simple_listeners_, kOnResponseStarted)) { brightray::NetworkDelegate::OnResponseStarted(request); + return; } + + if (request->status().status() != net::URLRequestStatus::SUCCESS) + return; + + HandleSimpleEvent(kOnResponseStarted, request, request->response_headers(), + request->was_cached()); } void AtomNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { - auto listener_info = event_listener_map_.find(kOnCompleted); - if (listener_info != event_listener_map_.end()) { - if (request->status().status() == net::URLRequestStatus::FAILED || - request->status().status() == net::URLRequestStatus::CANCELED) { + if (request->status().status() == net::URLRequestStatus::FAILED || + request->status().status() == net::URLRequestStatus::CANCELED) { + // Error event. + if (ContainsKey(simple_listeners_, kOnErrorOccurred)) OnErrorOccurred(request); - return; - } else { - bool is_redirect = request->response_headers() && - net::HttpResponseHeaders::IsRedirectResponseCode( - request->response_headers()->response_code()); - if (is_redirect) - return; - } - - if (!MatchesFilterCondition(request, listener_info->second)) - return; - - auto wrapped_callback = listener_info->second.callback; - auto details = ExtractRequestInfo(request); - details->Set("responseHeaders", - GetResponseHeadersDict(request->response_headers()).release()); - details->SetBoolean("fromCache", request->was_cached()); - - auto response_headers = request->response_headers(); - details->SetInteger("statusCode", - response_headers ? - response_headers->response_code() : 200); - details->SetString("statusLine", - response_headers ? - response_headers->GetStatusLine() : std::string()); - - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(base::IgnoreResult(&RunListener), - wrapped_callback, - base::Passed(&details))); - } else { + else + brightray::NetworkDelegate::OnCompleted(request, started); + return; + } else if (request->response_headers() && + net::HttpResponseHeaders::IsRedirectResponseCode( + request->response_headers()->response_code())) { + // Redirect event. brightray::NetworkDelegate::OnCompleted(request, started); + return; } + + if (!ContainsKey(simple_listeners_, kOnCompleted)) { + brightray::NetworkDelegate::OnCompleted(request, started); + return; + } + + HandleSimpleEvent(kOnCompleted, request, request->response_headers(), + request->was_cached()); } void AtomNetworkDelegate::OnErrorOccurred(net::URLRequest* request) { - auto listener_info = event_listener_map_.find(kOnErrorOccurred); - if (listener_info != event_listener_map_.end()) { - if (!MatchesFilterCondition(request, listener_info->second)) - return; + HandleSimpleEvent(kOnErrorOccurred, request, request->was_cached(), + request->status()); +} - auto wrapped_callback = listener_info->second.callback; - auto details = ExtractRequestInfo(request); - details->SetBoolean("fromCache", request->was_cached()); - details->SetString("error", net::ErrorToString(request->status().error())); +template +int AtomNetworkDelegate::HandleResponseEvent( + ResponseEvent type, + net::URLRequest* request, + const net::CompletionCallback& callback, + Out out, + Args... args) { + const auto& info = response_listeners_[type]; + if (!MatchesFilterCondition(request, info.url_patterns)) + return net::OK; - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(base::IgnoreResult(&RunListener), - wrapped_callback, - base::Passed(&details))); - } + scoped_ptr details(new base::DictionaryValue); + FillDetailsObject(details.get(), request, args...); + + ResponseCallback response = + base::Bind(OnListenerResultInUI, callback, out); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(RunResponseListener, info.listener, base::Passed(&details), + response)); + return net::ERR_IO_PENDING; +} + +template +void AtomNetworkDelegate::HandleSimpleEvent( + SimpleEvent type, net::URLRequest* request, Args... args) { + const auto& info = simple_listeners_[type]; + if (!MatchesFilterCondition(request, info.url_patterns)) + return; + + scoped_ptr details(new base::DictionaryValue); + FillDetailsObject(details.get(), request, args...); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(RunSimpleListener, info.listener, base::Passed(&details))); } } // namespace atom diff --git a/atom/browser/net/atom_network_delegate.h b/atom/browser/net/atom_network_delegate.h index 9f78cc8d92da..8950a1b5110c 100644 --- a/atom/browser/net/atom_network_delegate.h +++ b/atom/browser/net/atom_network_delegate.h @@ -7,7 +7,6 @@ #include #include -#include #include "brightray/browser/network_delegate.h" #include "base/callback.h" @@ -23,53 +22,50 @@ class URLPattern; namespace atom { +using URLPatterns = std::set; + class AtomNetworkDelegate : public brightray::NetworkDelegate { public: - struct BlockingResponse; - using Listener = - base::Callback; + using ResponseCallback = base::Callback; + using SimpleListener = base::Callback; + using ResponseListener = base::Callback; - enum EventTypes { - kInvalidEvent = 0, - kOnBeforeRequest = 1 << 0, - kOnBeforeSendHeaders = 1 << 1, - kOnSendHeaders = 1 << 2, - kOnHeadersReceived = 1 << 3, - kOnBeforeRedirect = 1 << 4, - kOnResponseStarted = 1 << 5, - kOnCompleted = 1 << 6, - kOnErrorOccurred = 1 << 7, + enum SimpleEvent { + kOnSendHeaders, + kOnBeforeRedirect, + kOnResponseStarted, + kOnCompleted, + kOnErrorOccurred, }; - struct ListenerInfo { - std::set url_patterns; - AtomNetworkDelegate::Listener callback; + enum ResponseEvent { + kOnBeforeRequest, + kOnBeforeSendHeaders, + kOnHeadersReceived, }; - struct BlockingResponse { - BlockingResponse() : cancel(false) {} - ~BlockingResponse() {} + struct SimpleListenerInfo { + URLPatterns url_patterns; + SimpleListener listener; + }; - int Code() const { - return cancel ? net::ERR_BLOCKED_BY_CLIENT : net::OK; - } - - bool cancel; - GURL redirect_url; - net::HttpRequestHeaders request_headers; - scoped_refptr response_headers; + struct ResponseListenerInfo { + URLPatterns url_patterns; + ResponseListener listener; }; AtomNetworkDelegate(); ~AtomNetworkDelegate() override; - void SetListenerInIO(EventTypes type, - scoped_ptr filter, - const Listener& callback); + void SetSimpleListenerInIO(SimpleEvent type, + const URLPatterns& patterns, + const SimpleListener& callback); + void SetResponseListenerInIO(ResponseEvent type, + const URLPatterns& patterns, + const ResponseListener& callback); protected: - void OnErrorOccurred(net::URLRequest* request); - // net::NetworkDelegate: int OnBeforeURLRequest(net::URLRequest* request, const net::CompletionCallback& callback, @@ -90,8 +86,22 @@ class AtomNetworkDelegate : public brightray::NetworkDelegate { void OnResponseStarted(net::URLRequest* request) override; void OnCompleted(net::URLRequest* request, bool started) override; + void OnErrorOccurred(net::URLRequest* request); + private: - std::map event_listener_map_; + template + void HandleSimpleEvent(SimpleEvent type, + net::URLRequest* request, + Args... args); + template + int HandleResponseEvent(ResponseEvent type, + net::URLRequest* request, + const net::CompletionCallback& callback, + Out out, + Args... args); + + std::map simple_listeners_; + std::map response_listeners_;; DISALLOW_COPY_AND_ASSIGN(AtomNetworkDelegate); }; diff --git a/atom/common/native_mate_converters/net_converter.cc b/atom/common/native_mate_converters/net_converter.cc index ff0d3be88162..7a1b48d9311a 100644 --- a/atom/common/native_mate_converters/net_converter.cc +++ b/atom/common/native_mate_converters/net_converter.cc @@ -82,38 +82,4 @@ v8::Local Converter>::ToV8( return dict.GetHandle(); } -// static -bool Converter::FromV8( - v8::Isolate* isolate, v8::Local val, - atom::AtomNetworkDelegate::BlockingResponse* out) { - mate::Dictionary dict; - if (!ConvertFromV8(isolate, val, &dict)) - return false; - if (!dict.Get("cancel", &(out->cancel))) - return false; - dict.Get("redirectURL", &(out->redirect_url)); - base::DictionaryValue request_headers; - if (dict.Get("requestHeaders", &request_headers)) { - for (base::DictionaryValue::Iterator it(request_headers); - !it.IsAtEnd(); - it.Advance()) { - std::string value; - CHECK(it.value().GetAsString(&value)); - out->request_headers.SetHeader(it.key(), value); - } - } - base::DictionaryValue response_headers; - if (dict.Get("responseHeaders", &response_headers)) { - out->response_headers = new net::HttpResponseHeaders(""); - for (base::DictionaryValue::Iterator it(response_headers); - !it.IsAtEnd(); - it.Advance()) { - std::string value; - CHECK(it.value().GetAsString(&value)); - out->response_headers->AddHeader(it.key() + " : " + value); - } - } - return true; -} - } // namespace mate diff --git a/atom/common/native_mate_converters/net_converter.h b/atom/common/native_mate_converters/net_converter.h index f251da8c5c8a..b11c55929b98 100644 --- a/atom/common/native_mate_converters/net_converter.h +++ b/atom/common/native_mate_converters/net_converter.h @@ -5,7 +5,6 @@ #ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_NET_CONVERTER_H_ #define ATOM_COMMON_NATIVE_MATE_CONVERTERS_NET_CONVERTER_H_ -#include "atom/browser/net/atom_network_delegate.h" #include "base/memory/ref_counted.h" #include "native_mate/converter.h" @@ -35,13 +34,6 @@ struct Converter> { const scoped_refptr& val); }; -template<> -struct Converter { - static bool FromV8(v8::Isolate* isolate, - v8::Local val, - atom::AtomNetworkDelegate::BlockingResponse* out); -}; - } // namespace mate #endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_NET_CONVERTER_H_ diff --git a/docs/api/session.md b/docs/api/session.md index 9429bc3c2df7..21464bd481e8 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -12,7 +12,7 @@ const BrowserWindow = require('electron').BrowserWindow; var win = new BrowserWindow({ width: 800, height: 600 }); win.loadURL("http://github.com"); -var ses = win.webContents.session +var ses = win.webContents.session; ``` ## Methods @@ -63,7 +63,7 @@ Emitted when Electron is about to download `item` in `webContents`. Calling `event.preventDefault()` will cancel the download. ```javascript -session.on('will-download', function(event, item, webContents) { +session.defaultSession.on('will-download', function(event, item, webContents) { event.preventDefault(); require('request')(item.getURL(), function(data) { require('fs').writeFileSync('/somewhere', data); @@ -80,91 +80,84 @@ The following methods are available on instances of `Session`: The `cookies` gives you ability to query and modify cookies. For example: ```javascript -const BrowserWindow = require('electron').BrowserWindow; +// Query all cookies. +session.defaultSession.cookies.get({}, function(error, cookies) { + console.log(cookies); +}); -var win = new BrowserWindow({ width: 800, height: 600 }); +// Query all cookies associated with a specific url. +session.defaultSession.cookies.get({ url : "http://www.github.com" }, function(error, cookies) { + console.log(cookies); +}); -win.loadURL('https://github.com'); - -win.webContents.on('did-finish-load', function() { - // Query all cookies. - win.webContents.session.cookies.get({}, function(error, cookies) { - if (error) throw error; - console.log(cookies); - }); - - // Query all cookies associated with a specific url. - win.webContents.session.cookies.get({ url : "http://www.github.com" }, - function(error, cookies) { - if (error) throw error; - console.log(cookies); - }); - - // Set a cookie with the given cookie data; - // may overwrite equivalent cookies if they exist. - win.webContents.session.cookies.set( - { url : "http://www.github.com", name : "dummy_name", value : "dummy"}, - function(error, cookies) { - if (error) throw error; - console.log(cookies); - }); +// Set a cookie with the given cookie data; +// may overwrite equivalent cookies if they exist. +var cookie = { url : "http://www.github.com", name : "dummy_name", value : "dummy" }; +session.defaultSession.cookies.set(cookie, function(error) { + if (error) + console.error(error); }); ``` -#### `ses.cookies.get(details, callback)` +#### `ses.cookies.get(filter, callback)` -`details` Object, properties: +* `filter` Object + * `url` String __optional__ - Retrieves cookies which are associated with + `url`. Empty implies retrieving cookies of all urls. + * `name` String __optional__ - Filters cookies by name. + * `domain` String __optional__ - Retrieves cookies whose domains match or are + subdomains of `domains` + * `path` String __optional__ - Retrieves cookies whose path matches `path`. + * `secure` Boolean __optional__ - Filters cookies by their Secure property. + * `session` Boolean __optional__ - Filters out session or persistent cookies. +* `callback` Function -* `url` String - Retrieves cookies which are associated with `url`. - Empty implies retrieving cookies of all urls. -* `name` String - Filters cookies by name -* `domain` String - Retrieves cookies whose domains match or are subdomains of - `domains` -* `path` String - Retrieves cookies whose path matches `path` -* `secure` Boolean - Filters cookies by their Secure property -* `session` Boolean - Filters out session or persistent cookies. -* `callback` Function - function(error, cookies) -* `error` Error -* `cookies` Array - array of `cookie` objects, properties: +Sends a request to get all cookies matching `details`, `callback` will be called +with `callback(error, cookies)` on complete. + +`cookies` is an Array of `cookie` objects. + +* `cookie` Object * `name` String - The name of the cookie. * `value` String - The value of the cookie. * `domain` String - The domain of the cookie. - * `host_only` String - Whether the cookie is a host-only cookie. + * `hostOnly` String - Whether the cookie is a host-only cookie. * `path` String - The path of the cookie. - * `secure` Boolean - Whether the cookie is marked as Secure (typically HTTPS). - * `http_only` Boolean - Whether the cookie is marked as HttpOnly. + * `secure` Boolean - Whether the cookie is marked as secure. + * `httpOnly` Boolean - Whether the cookie is marked as HTTP only. * `session` Boolean - Whether the cookie is a session cookie or a persistent cookie with an expiration date. - * `expirationDate` Double - (Option) The expiration date of the cookie as + * `expirationDate` Double __optional__ - The expiration date of the cookie as the number of seconds since the UNIX epoch. Not provided for session cookies. #### `ses.cookies.set(details, callback)` -`details` Object, properties: - -* `url` String - Retrieves cookies which are associated with `url` -* `name` String - The name of the cookie. Empty by default if omitted. -* `value` String - The value of the cookie. Empty by default if omitted. -* `domain` String - The domain of the cookie. Empty by default if omitted. -* `path` String - The path of the cookie. Empty by default if omitted. -* `secure` Boolean - Whether the cookie should be marked as Secure. Defaults to - false. -* `session` Boolean - Whether the cookie should be marked as HttpOnly. Defaults - to false. -* `expirationDate` Double - The expiration date of the cookie as the number of - seconds since the UNIX epoch. If omitted, the cookie becomes a session cookie. - -* `callback` Function - function(error) - * `error` Error - -#### `ses.cookies.remove(details, callback)` - * `details` Object - * `url` String - The URL associated with the cookie - * `name` String - The name of cookie to remove -* `callback` Function - function(error) - * `error` Error + * `url` String - Retrieves cookies which are associated with `url` + * `name` String - The name of the cookie. Empty by default if omitted. + * `value` String - The value of the cookie. Empty by default if omitted. + * `domain` String - The domain of the cookie. Empty by default if omitted. + * `path` String - The path of the cookie. Empty by default if omitted. + * `secure` Boolean - Whether the cookie should be marked as Secure. Defaults to + false. + * `session` Boolean - Whether the cookie should be marked as HttpOnly. Defaults + to false. + * `expirationDate` Double - The expiration date of the cookie as the number of + seconds since the UNIX epoch. If omitted, the cookie becomes a session cookie. +* `callback` Function + +Sets the cookie with `details`, `callback` will be called with `callback(error)` +on complete. + +#### `ses.cookies.remove(url, name, callback)` + +* `url` String - The URL associated with the cookie. +* `name` String - The name of cookie to remove. +* `callback` Function + +Removes the cookies matching `url` and `name`, `callback` will called with +`callback()` on complete. #### `ses.clearCache(callback)` @@ -289,175 +282,194 @@ myWindow.webContents.session.setCertificateVerifyProc(function(hostname, cert, c #### `ses.webRequest` -The `webRequest` api allows to intercept and modify contents of a request at various -stages of its lifetime. +The `webRequest` API set allows to intercept and modify contents of a request at +various stages of its lifetime. + +Each API accepts an optional `filter` and a `listener`, the `listener` will be +called with `listener(details)` when the API's event has happened, the `details` +is an object that describes the request. Passing `null` as `listener` will +unsubscribe from the event. + +The `filter` is an object that has an `urls` property, which is an Array of URL +patterns that will be used to filter out the requests that do not match the URL +patterns. If the `filter` is omitted then all requests will be matched. + +For certain events the `listener` is passed with a `callback`, which should be +called with an `response` object when `listener` has done its work. ```javascript // Modify the user agent for all requests to the following urls. - var filter = { urls: ["https://*.github.com/*", "*://electron.github.io"] -} +}; -myWindow.webContents.session.webRequest.onBeforeSendHeaders(filter, function(details) { +session.defaultSession.webRequest.onBeforeSendHeaders(filter, function(details, callback) { details.requestHeaders['User-Agent'] = "MyAgent"; - return {cancel: false, requestHeaders: details.requestHeaders}; -}) + callback({cancel: false, requestHeaders: details.requestHeaders}); +}); ``` -#### `ses.webRequest.onBeforeRequest([filter,] listener)` +#### `ses.webRequest.onBeforeRequest([filter, ]listener)` * `filter` Object - * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. * `listener` Function - * `details` Object - * `id` String - Request id. - * `url` String - * `method` String - * `resourceType` String - * `timestamp` Double -* `blockingResponse` Object - * `cancel` Boolean - Whether to continue or block the request. - * `redirectURL` String **optional** - The original request is prevented from being sent or - completed, and is instead redirected to the given URL. -Fired when a request is about to occur. Should return a `blockingResponse`. +The `listener` will be called with `listener(details, callback)` when a request +is about to occur. -#### `ses.webRequest.onBeforeSendHeaders([filter,] listener)` +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + +The `callback` has to be called with an `response` object: + +* `response` Object + * `cancel` Boolean __optional__ + * `redirectURL` String __optional__ - The original request is prevented from + being sent or completed, and is instead redirected to the given URL. + +#### `ses.webRequest.onBeforeSendHeaders([filter, ]listener)` * `filter` Object - * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. * `listener` Function - * `details` Object - * `id` String - Request id. - * `url` String - * `method` String - * `resourceType` String - * `timestamp` Double - * `requestHeaders` Object -* `blockingResponse` Object - * `cancel` Boolean - Whether to continue or block the request. - * `requestHeaders` Object **optional** - When provided, request will be made with these - headers. -Fired before sending an HTTP request, once the request headers are available. This may -occur after a TCP connection is made to the server, but before any http data is sent. -Should return a `blockingResponse`. +The `listener` will be called with `listener(details, callback)` before sending +an HTTP request, once the request headers are available. This may occur after a +TCP connection is made to the server, but before any http data is sent. -#### `ses.webRequest.onSendHeaders([filter,] listener)` +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `requestHeaders` Object + +The `callback` has to be called with an `response` object: + +* `response` Object + * `cancel` Boolean __optional__ + * `requestHeaders` Object __optional__ - When provided, request will be made + with these headers. + +#### `ses.webRequest.onSendHeaders([filter, ]listener)` * `filter` Object - * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. * `listener` Function - * `details` Object - * `id` String - Request id. - * `url` String - * `method` String - * `resourceType` String - * `timestamp` Double - * `requestHeaders` Object -Fired just before a request is going to be sent to the server, modifications of previous -`onBeforeSendHeaders` response are visible by the time this listener is fired. +The `listener` will be called with `listener(details)` just before a request is +going to be sent to the server, modifications of previous `onBeforeSendHeaders` +response are visible by the time this listener is fired. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `requestHeaders` Object #### `ses.webRequest.onHeadersReceived([filter,] listener)` * `filter` Object - * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. * `listener` Function - * `details` Object - * `id` String - Request id. - * `url` String - * `method` String - * `resourceType` String - * `timestamp` Double - * `statusLine` String - * `statusCode` Integer - * `responseHeaders` Object -* `blockingResponse` Object - * `cancel` Boolean - Whether to continue or block the request. - * `responseHeaders` Object **optional** - When provided, the server is assumed to have - responded with these headers. -Fired when HTTP response headers of a request have been received. Should return a -`blockingResponse`. +The `listener` will be called with `listener(details, callback)` when HTTP +response headers of a request have been received. -#### `ses.webRequest.onResponseStarted([filter,] listener)` +* `details` Object + * `id` String + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `statusLine` String + * `statusCode` Integer + * `responseHeaders` Object + +The `callback` has to be called with an `response` object: + +* `response` Object + * `cancel` Boolean + * `responseHeaders` Object __optional__ - When provided, the server is assumed + to have responded with these headers. + +#### `ses.webRequest.onResponseStarted([filter, ]listener)` * `filter` Object - * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. * `listener` Function - * `details` Object - * `id` String - Request id. - * `url` String - * `method` String - * `resourceType` String - * `timestamp` Double - * `responseHeaders` Object - * `fromCache` Boolean - * `statusCode` Integer - * `statusLine` String -Fired when first byte of the response body is received. For HTTP requests, this means that the -status line and response headers are available. +The `listener` will be called with `listener(details)` when first byte of the +response body is received. For HTTP requests, this means that the status line +and response headers are available. -#### `ses.webRequest.onBeforeRedirect([filter,] listener)` +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `responseHeaders` Object + * `fromCache` Boolean - Indicates whether the response was fetched from disk + cache. + * `statusCode` Integer + * `statusLine` String + +#### `ses.webRequest.onBeforeRedirect([filter, ]listener)` * `filter` Object - * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. * `listener` Function - * `details` Object - * `id` String - Request id. - * `url` String - * `method` String - * `resourceType` String - * `timestamp` Double - * `redirectURL` String - * `statusCode` Integer - * `ip` String **optional** - The server IP address that the request was actually sent to. - * `fromCache` Boolean - * `responseHeaders` Object -Fired when a server initiated redirect is about to occur. +The `listener` will be called with `listener(details)` when a server initiated +redirect is about to occur. -#### `ses.webRequest.onCompleted([filter,] listener)` +* `details` Object + * `id` String + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `redirectURL` String + * `statusCode` Integer + * `ip` String __optional__ - The server IP address that the request was + actually sent to. + * `fromCache` Boolean + * `responseHeaders` Object + +#### `ses.webRequest.onCompleted([filter, ]listener)` * `filter` Object - * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. * `listener` Function - * `details` Object - * `id` String - Request id. - * `url` String - * `method` String - * `resourceType` String - * `timestamp` Double - * `responseHeaders` Object - * `fromCache` Boolean - Indicates whether the response was fetched from disk cache. - * `statusCode` Integer - * `statusLine` String -Fired when a request is completed. +The `listener` will be called with `listener(details)` when a request is +completed. -#### `ses.webRequest.onErrorOccurred([filter,] listener)` +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `responseHeaders` Object + * `fromCache` Boolean + * `statusCode` Integer + * `statusLine` String + +#### `ses.webRequest.onErrorOccurred([filter, ]listener)` * `filter` Object - * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. * `listener` Function - * `details` Object - * `id` String - Request id. - * `url` String - * `method` String - * `resourceType` String - * `timestamp` Double - * `fromCache` Boolean - Indicates whether the response was fetched from disk cache. - * `error` String - The error description. -Fired when an error occurs. +The `listener` will be called with `listener(details)` when an error occurs. + +* `details` Object + * `id` Integer + * `url` String + * `method` String + * `resourceType` String + * `timestamp` Double + * `fromCache` Boolean + * `error` String - The error description. diff --git a/spec/api-session-spec.coffee b/spec/api-session-spec.coffee index 98e47e357a58..03c62c1f1db6 100644 --- a/spec/api-session-spec.coffee +++ b/spec/api-session-spec.coffee @@ -49,12 +49,11 @@ describe 'session module', -> it 'should remove cookies', (done) -> session.defaultSession.cookies.set {url: url, name: '2', value: '2'}, (error) -> return done(error) if error - session.defaultSession.cookies.remove {url: url, name: '2'}, (error) -> - return done(error) if error + session.defaultSession.cookies.remove url, '2', -> session.defaultSession.cookies.get {url: url}, (error, list) -> return done(error) if error for cookie in list when cookie.name is '2' - return done('Cookie not deleted') + return done('Cookie not deleted') done() describe 'session.clearStorageData(options)', -> diff --git a/spec/api-web-request-spec.coffee b/spec/api-web-request-spec.coffee new file mode 100644 index 000000000000..a10ff3d1277c --- /dev/null +++ b/spec/api-web-request-spec.coffee @@ -0,0 +1,232 @@ +assert = require 'assert' +http = require 'http' + +{remote} = require 'electron' +{session} = remote + +describe 'webRequest module', -> + ses = session.defaultSession + server = http.createServer (req, res) -> + res.setHeader('Custom', ['Header']) + content = req.url + if req.headers.accept is '*/*;test/header' + content += 'header/received' + res.end content + defaultURL = null + + before (done) -> + server.listen 0, '127.0.0.1', -> + {port} = server.address() + defaultURL = "http://127.0.0.1:#{port}/" + done() + after -> + server.close() + + describe 'webRequest.onBeforeRequest', -> + afterEach -> + ses.webRequest.onBeforeRequest null + + it 'can cancel the request', (done) -> + ses.webRequest.onBeforeRequest (details, callback) -> + callback(cancel: true) + $.ajax + url: defaultURL + success: (data) -> done('unexpected success') + error: (xhr, errorType, error) -> done() + + it 'can filter URLs', (done) -> + filter = urls: ["#{defaultURL}filter/*"] + ses.webRequest.onBeforeRequest filter, (details, callback) -> + callback(cancel: true) + $.ajax + url: "#{defaultURL}nofilter/test" + success: (data) -> + assert.equal data, '/nofilter/test' + $.ajax + url: "#{defaultURL}filter/test" + success: (data) -> done('unexpected success') + error: (xhr, errorType, error) -> done() + error: (xhr, errorType, error) -> done(errorType) + + it 'receives details object', (done) -> + ses.webRequest.onBeforeRequest (details, callback) -> + assert.equal typeof details.id, 'number' + assert.equal typeof details.timestamp, 'number' + assert.equal details.url, defaultURL + assert.equal details.method, 'GET' + assert.equal details.resourceType, 'xhr' + callback({}) + $.ajax + url: defaultURL + success: (data) -> + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + it 'can redirect the request', (done) -> + ses.webRequest.onBeforeRequest (details, callback) -> + if details.url is defaultURL + callback(redirectURL: "#{defaultURL}redirect") + else + callback({}) + $.ajax + url: defaultURL + success: (data) -> + assert.equal data, '/redirect' + done() + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onBeforeSendHeaders', -> + afterEach -> + ses.webRequest.onBeforeSendHeaders null + + it 'receives details object', (done) -> + ses.webRequest.onBeforeSendHeaders (details, callback) -> + assert.equal typeof details.requestHeaders, 'object' + callback({}) + $.ajax + url: defaultURL + success: (data) -> + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + it 'can change the request headers', (done) -> + ses.webRequest.onBeforeSendHeaders (details, callback) -> + {requestHeaders} = details + requestHeaders.Accept = '*/*;test/header' + callback({requestHeaders}) + $.ajax + url: defaultURL + success: (data, textStatus, request) -> + assert.equal data, '/header/received' + done() + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onSendHeaders', -> + afterEach -> + ses.webRequest.onSendHeaders null + + it 'receives details object', (done) -> + ses.webRequest.onSendHeaders (details, callback) -> + assert.equal typeof details.requestHeaders, 'object' + $.ajax + url: defaultURL + success: (data) -> + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onHeadersReceived', -> + afterEach -> + ses.webRequest.onHeadersReceived null + + it 'receives details object', (done) -> + ses.webRequest.onHeadersReceived (details, callback) -> + assert.equal details.statusLine, 'HTTP/1.1 200 OK' + assert.equal details.statusCode, 200 + assert.equal details.responseHeaders['Custom'], 'Header' + callback({}) + $.ajax + url: defaultURL + success: (data) -> + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + it 'can change the response header', (done) -> + ses.webRequest.onHeadersReceived (details, callback) -> + {responseHeaders} = details + responseHeaders['Custom'] = ['Changed'] + callback({responseHeaders}) + $.ajax + url: defaultURL + success: (data, status, xhr) -> + assert.equal xhr.getResponseHeader('Custom'), 'Changed' + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + it 'does not change header by default', (done) -> + ses.webRequest.onHeadersReceived (details, callback) -> + callback({}) + $.ajax + url: defaultURL + success: (data, status, xhr) -> + assert.equal xhr.getResponseHeader('Custom'), 'Header' + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onResponseStarted', -> + afterEach -> + ses.webRequest.onResponseStarted null + + it 'receives details object', (done) -> + ses.webRequest.onResponseStarted (details) -> + assert.equal typeof details.fromCache, 'boolean' + assert.equal details.statusLine, 'HTTP/1.1 200 OK' + assert.equal details.statusCode, 200 + assert.equal details.responseHeaders['Custom'], 'Header' + $.ajax + url: defaultURL + success: (data, status, xhr) -> + assert.equal xhr.getResponseHeader('Custom'), 'Header' + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onBeforeRedirect', -> + afterEach -> + ses.webRequest.onBeforeRedirect null + ses.webRequest.onBeforeRequest null + + it 'receives details object', (done) -> + redirectURL = "#{defaultURL}redirect" + ses.webRequest.onBeforeRequest (details, callback) -> + if details.url is defaultURL + callback({redirectURL}) + else + callback({}) + ses.webRequest.onBeforeRedirect (details) -> + assert.equal typeof details.fromCache, 'boolean' + assert.equal details.statusLine, 'HTTP/1.1 307 Internal Redirect' + assert.equal details.statusCode, 307 + assert.equal details.redirectURL, redirectURL + $.ajax + url: defaultURL + success: (data, status, xhr) -> + assert.equal data, '/redirect' + done() + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onCompleted', -> + afterEach -> + ses.webRequest.onCompleted null + + it 'receives details object', (done) -> + ses.webRequest.onCompleted (details) -> + assert.equal typeof details.fromCache, 'boolean' + assert.equal details.statusLine, 'HTTP/1.1 200 OK' + assert.equal details.statusCode, 200 + $.ajax + url: defaultURL + success: (data, status, xhr) -> + assert.equal data, '/' + done() + error: (xhr, errorType, error) -> done(errorType) + + describe 'webRequest.onErrorOccurred', -> + afterEach -> + ses.webRequest.onErrorOccurred null + ses.webRequest.onBeforeRequest null + + it 'receives details object', (done) -> + ses.webRequest.onBeforeRequest (details, callback) -> + callback(cancel: true) + ses.webRequest.onErrorOccurred (details) -> + assert.equal details.error, 'net::ERR_BLOCKED_BY_CLIENT' + done() + $.ajax + url: defaultURL + success: (data) -> done('unexpected success') diff --git a/spec/chromium-spec.coffee b/spec/chromium-spec.coffee index 8122fb4a7673..2cc2237385be 100644 --- a/spec/chromium-spec.coffee +++ b/spec/chromium-spec.coffee @@ -45,7 +45,7 @@ describe 'chromium feature', -> done() w.loadURL url - describe 'navigator.webkitGetUserMedia', -> + xdescribe 'navigator.webkitGetUserMedia', -> it 'calls its callbacks', (done) -> @timeout 5000 navigator.webkitGetUserMedia audio: true, video: false, diff --git a/vendor/native_mate b/vendor/native_mate index 5e70868fd0c0..a3dcf8ced663 160000 --- a/vendor/native_mate +++ b/vendor/native_mate @@ -1 +1 @@ -Subproject commit 5e70868fd0c005dc2c43bea15ca6e93da0b68741 +Subproject commit a3dcf8ced663e974ac94ad5e50a1d25a43995a9d