From 13b5cab7380c4113e6674ae1fb986cb341594b4b Mon Sep 17 00:00:00 2001 From: Robo Date: Tue, 1 Dec 2015 10:22:22 +0530 Subject: [PATCH 1/6] session: add webrequest api --- atom/browser/api/atom_api_session.cc | 12 +- atom/browser/api/atom_api_session.h | 4 +- atom/browser/api/atom_api_web_request.cc | 116 +++++++++ atom/browser/api/atom_api_web_request.h | 51 ++++ atom/browser/atom_browser_context.cc | 6 + atom/browser/atom_browser_context.h | 5 + atom/browser/net/atom_network_delegate.cc | 220 ++++++++++++++++++ atom/browser/net/atom_network_delegate.h | 89 +++++++ .../native_mate_converters/net_converter.cc | 37 +++ .../native_mate_converters/net_converter.h | 8 + .../native_mate_converters/value_converter.cc | 7 + .../native_mate_converters/value_converter.h | 6 + filenames.gypi | 4 + 13 files changed, 563 insertions(+), 2 deletions(-) create mode 100644 atom/browser/api/atom_api_web_request.cc create mode 100644 atom/browser/api/atom_api_web_request.h create mode 100644 atom/browser/net/atom_network_delegate.cc create mode 100644 atom/browser/net/atom_network_delegate.h diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 9cec7378b8e0..8b73d61622ec 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -10,6 +10,7 @@ #include "atom/browser/api/atom_api_cookies.h" #include "atom/browser/api/atom_api_download_item.h" #include "atom/browser/api/atom_api_web_contents.h" +#include "atom/browser/api/atom_api_web_request.h" #include "atom/browser/api/save_page_handler.h" #include "atom/browser/atom_browser_context.h" #include "atom/browser/atom_browser_main_parts.h" @@ -368,6 +369,14 @@ v8::Local Session::Cookies(v8::Isolate* isolate) { return v8::Local::New(isolate, cookies_); } +v8::Local Session::WebRequest(v8::Isolate* isolate) { + if (web_request_.IsEmpty()) { + auto handle = atom::api::WebRequest::Create(isolate, browser_context()); + web_request_.Reset(isolate, handle.ToV8()); + } + return v8::Local::New(isolate, web_request_); +} + // static mate::Handle Session::CreateFrom( v8::Isolate* isolate, AtomBrowserContext* browser_context) { @@ -401,7 +410,8 @@ void Session::BuildPrototype(v8::Isolate* isolate, .SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation) .SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation) .SetMethod("setCertificateVerifyProc", &Session::SetCertVerifyProc) - .SetProperty("cookies", &Session::Cookies); + .SetProperty("cookies", &Session::Cookies) + .SetProperty("webRequest", &Session::WebRequest); } void ClearWrapSession() { diff --git a/atom/browser/api/atom_api_session.h b/atom/browser/api/atom_api_session.h index e800a992d4b7..0034b1480632 100644 --- a/atom/browser/api/atom_api_session.h +++ b/atom/browser/api/atom_api_session.h @@ -70,9 +70,11 @@ class Session: public mate::TrackableObject, void DisableNetworkEmulation(); void SetCertVerifyProc(v8::Local proc, mate::Arguments* args); v8::Local Cookies(v8::Isolate* isolate); + v8::Local WebRequest(v8::Isolate* isolate); - // Cached object for cookies API. + // Cached object. v8::Global cookies_; + v8::Global web_request_; scoped_refptr browser_context_; diff --git a/atom/browser/api/atom_api_web_request.cc b/atom/browser/api/atom_api_web_request.cc new file mode 100644 index 000000000000..ad772e4c2963 --- /dev/null +++ b/atom/browser/api/atom_api_web_request.cc @@ -0,0 +1,116 @@ +// 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/api/atom_api_web_request.h" + +#include "atom/browser/atom_browser_context.h" +#include "atom/browser/net/atom_network_delegate.h" +#include "atom/common/native_mate_converters/callback.h" +#include "atom/common/native_mate_converters/net_converter.h" +#include "atom/common/native_mate_converters/value_converter.h" +#include "content/public/browser/browser_thread.h" +#include "native_mate/dictionary.h" +#include "native_mate/object_template_builder.h" + +using content::BrowserThread; + +namespace atom { + +namespace api { + +WebRequest::WebRequest(AtomBrowserContext* browser_context) + : browser_context_(browser_context) { +} + +WebRequest::~WebRequest() { +} + +template +void WebRequest::SetListener(mate::Arguments* args) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + base::DictionaryValue* filter = new base::DictionaryValue(); + args->GetNext(filter); + AtomNetworkDelegate::Listener callback; + if (!args->GetNext(&callback)) { + 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, filter, callback)); +} + +mate::ObjectTemplateBuilder WebRequest::GetObjectTemplateBuilder( + v8::Isolate* isolate) { + return mate::ObjectTemplateBuilder(isolate) + .SetMethod("onBeforeRequest", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnBeforeRequest>) + .SetMethod("onBeforeSendHeaders", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnBeforeSendHeaders>) + .SetMethod("onSendHeaders", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnSendHeaders>) + .SetMethod("onHeadersReceived", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnHeadersReceived>) + .SetMethod("onBeforeRedirect", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnBeforeRedirect>) + .SetMethod("onResponseStarted", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnResponseStarted>) + .SetMethod("onCompleted", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnCompleted>) + .SetMethod("onErrorOccurred", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnErrorOccurred>); +} + +// static +mate::Handle WebRequest::Create( + v8::Isolate* isolate, + AtomBrowserContext* browser_context) { + return mate::CreateHandle(isolate, new WebRequest(browser_context)); +} + +// static +void WebRequest::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) { + mate::ObjectTemplateBuilder(isolate, prototype) + .SetMethod("onBeforeRequest", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnBeforeRequest>) + .SetMethod("onBeforeSendHeaders", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnBeforeSendHeaders>) + .SetMethod("onSendHeaders", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnSendHeaders>) + .SetMethod("onHeadersReceived", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnHeadersReceived>) + .SetMethod("onBeforeRedirect", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnBeforeRedirect>) + .SetMethod("onResponseStarted", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnResponseStarted>) + .SetMethod("onCompleted", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnCompleted>) + .SetMethod("onErrorOccurred", + &WebRequest::SetListener< + AtomNetworkDelegate::kOnErrorOccurred>); +} + +} // namespace api + +} // namespace atom diff --git a/atom/browser/api/atom_api_web_request.h b/atom/browser/api/atom_api_web_request.h new file mode 100644 index 000000000000..04fb758a9a7c --- /dev/null +++ b/atom/browser/api/atom_api_web_request.h @@ -0,0 +1,51 @@ +// 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_API_ATOM_API_WEB_REQUEST_H_ +#define ATOM_BROWSER_API_ATOM_API_WEB_REQUEST_H_ + +#include + +#include "atom/browser/net/atom_network_delegate.h" +#include "base/callback.h" +#include "native_mate/arguments.h" +#include "native_mate/wrappable.h" + +namespace atom { + +class AtomBrowserContext; + +namespace api { + +class WebRequest : public mate::TrackableObject { + public: + static mate::Handle Create(v8::Isolate* isolate, + AtomBrowserContext* browser_context); + + // mate::TrackableObject: + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + + protected: + explicit WebRequest(AtomBrowserContext* browser_context); + ~WebRequest(); + + template + void SetListener(mate::Arguments* args); + + // mate::Wrappable: + mate::ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override; + + private: + scoped_refptr browser_context_; + + DISALLOW_COPY_AND_ASSIGN(WebRequest); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_ATOM_API_WEB_REQUEST_H_ diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index ec123825822e..12e0125752eb 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -8,6 +8,7 @@ #include "atom/browser/atom_download_manager_delegate.h" #include "atom/browser/browser.h" #include "atom/browser/net/atom_cert_verifier.h" +#include "atom/browser/net/atom_network_delegate.h" #include "atom/browser/net/atom_ssl_config_service.h" #include "atom/browser/net/atom_url_request_job_factory.h" #include "atom/browser/net/asar/asar_protocol_handler.h" @@ -63,12 +64,17 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition, : brightray::BrowserContext(partition, in_memory), cert_verifier_(nullptr), job_factory_(new AtomURLRequestJobFactory), + network_delegate_(new AtomNetworkDelegate), allow_ntlm_everywhere_(false) { } AtomBrowserContext::~AtomBrowserContext() { } +net::NetworkDelegate* AtomBrowserContext::CreateNetworkDelegate() { + return network_delegate_; +} + std::string AtomBrowserContext::GetUserAgent() { Browser* browser = Browser::Get(); std::string name = RemoveWhitespace(browser->GetName()); diff --git a/atom/browser/atom_browser_context.h b/atom/browser/atom_browser_context.h index 564c9955d917..9c94a60c305b 100644 --- a/atom/browser/atom_browser_context.h +++ b/atom/browser/atom_browser_context.h @@ -13,6 +13,7 @@ namespace atom { class AtomDownloadManagerDelegate; class AtomCertVerifier; +class AtomNetworkDelegate; class AtomURLRequestJobFactory; class WebViewManager; @@ -22,6 +23,7 @@ class AtomBrowserContext : public brightray::BrowserContext { ~AtomBrowserContext() override; // brightray::URLRequestContextGetter::Delegate: + net::NetworkDelegate* CreateNetworkDelegate() override; std::string GetUserAgent() override; scoped_ptr CreateURLRequestJobFactory( content::ProtocolHandlerMap* handlers, @@ -45,6 +47,8 @@ class AtomBrowserContext : public brightray::BrowserContext { AtomURLRequestJobFactory* job_factory() const { return job_factory_; } + AtomNetworkDelegate* network_delegate() const { return network_delegate_; } + private: scoped_ptr download_manager_delegate_; scoped_ptr guest_manager_; @@ -52,6 +56,7 @@ class AtomBrowserContext : public brightray::BrowserContext { // Managed by brightray::BrowserContext. AtomCertVerifier* cert_verifier_; AtomURLRequestJobFactory* job_factory_; + AtomNetworkDelegate* network_delegate_; bool allow_ntlm_everywhere_; diff --git a/atom/browser/net/atom_network_delegate.cc b/atom/browser/net/atom_network_delegate.cc new file mode 100644 index 000000000000..3b3c8f971987 --- /dev/null +++ b/atom/browser/net/atom_network_delegate.cc @@ -0,0 +1,220 @@ +// 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/atom_network_delegate.h" + +#include + +#include "atom/common/native_mate_converters/net_converter.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/resource_request_info.h" +#include "net/base/net_errors.h" +#include "net/url_request/url_request.h" + +using content::BrowserThread; + +namespace atom { + +namespace { + +base::DictionaryValue* ExtractRequestInfo(net::URLRequest* request) { + base::DictionaryValue* 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; + auto info = content::ResourceRequestInfo::ForRequest(request); + if (info) + resourceType = info->GetResourceType(); + dict->SetInteger("resourceType", resourceType); + dict->SetDouble("timestamp", base::Time::Now().ToDoubleT() * 1000); + + return dict; +} + +base::DictionaryValue* GetRequestHeadersDict( + const net::HttpRequestHeaders& headers) { + base::DictionaryValue* header_dict = new base::DictionaryValue(); + net::HttpRequestHeaders::Iterator it(headers); + while (it.GetNext()) + header_dict->SetString(it.name(), it.value()); + return header_dict; +} + +base::DictionaryValue* GetResponseHeadersDict( + const net::HttpResponseHeaders* headers) { + base::DictionaryValue* 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; +} + +void OnBeforeURLRequestResponse( + const net::CompletionCallback& callback, + GURL* new_url, + const AtomNetworkDelegate::BlockingResponse& result) { + if (!result.redirectURL.is_empty()) + *new_url = result.redirectURL; + callback.Run(result.cancel); +} + +void OnBeforeSendHeadersResponse( + const net::CompletionCallback& callback, + net::HttpRequestHeaders* headers, + const AtomNetworkDelegate::BlockingResponse& result) { + if (!result.requestHeaders.IsEmpty()) + *headers = result.requestHeaders; + callback.Run(result.cancel); +} + +void OnHeadersReceivedResponse( + const net::CompletionCallback& callback, + scoped_refptr* override_response_headers, + const AtomNetworkDelegate::BlockingResponse& result) { + if (result.responseHeaders.get()) + *override_response_headers = result.responseHeaders; + callback.Run(result.cancel); +} + +} // namespace + +// static +std::map +AtomNetworkDelegate::event_listener_map_; + +AtomNetworkDelegate::AtomNetworkDelegate() { +} + +AtomNetworkDelegate::~AtomNetworkDelegate() { +} + +void AtomNetworkDelegate::SetListenerInIO( + EventTypes type, + const base::DictionaryValue* filter, + const Listener& callback) { + ListenerInfo info; + info.callback = callback; + event_listener_map_[type] = info; +} + +int AtomNetworkDelegate::OnBeforeURLRequest( + net::URLRequest* request, + const net::CompletionCallback& callback, + GURL* new_url) { + brightray::NetworkDelegate::OnBeforeURLRequest(request, callback, new_url); + + auto listener_info = event_listener_map_[kOnBeforeRequest]; + if (!event_listener_map_.empty() && !listener_info.callback.is_null()) { + auto wrapped_callback = listener_info.callback; + auto details = ExtractRequestInfo(request); + + BrowserThread::PostTaskAndReplyWithResult(BrowserThread::UI, FROM_HERE, + base::Bind(wrapped_callback, details), + base::Bind(&OnBeforeURLRequestResponse, + callback, new_url)); + + return net::ERR_IO_PENDING; + } + + return net::OK; +} + +int AtomNetworkDelegate::OnBeforeSendHeaders( + net::URLRequest* request, + const net::CompletionCallback& callback, + net::HttpRequestHeaders* headers) { + auto listener_info = event_listener_map_[kOnBeforeSendHeaders]; + if (!event_listener_map_.empty() && !listener_info.callback.is_null()) { + auto wrapped_callback = listener_info.callback; + auto details = ExtractRequestInfo(request); + details->Set("requestHeaders", GetRequestHeadersDict(*headers)); + + BrowserThread::PostTaskAndReplyWithResult(BrowserThread::UI, FROM_HERE, + base::Bind(wrapped_callback, details), + base::Bind(&OnBeforeSendHeadersResponse, + callback, headers)); + + return net::ERR_IO_PENDING; + } + + return net::OK; +} + +void AtomNetworkDelegate::OnSendHeaders( + net::URLRequest* request, + const net::HttpRequestHeaders& headers) { + auto listener_info = event_listener_map_[kOnSendHeaders]; + if (!event_listener_map_.empty() && !listener_info.callback.is_null()) { + auto wrapped_callback = listener_info.callback; + auto details = ExtractRequestInfo(request); + details->Set("requestHeaders", GetRequestHeadersDict(headers)); + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(base::IgnoreResult(wrapped_callback), + details)); + } +} + +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_[kOnHeadersReceived]; + if (!event_listener_map_.empty() && !listener_info.callback.is_null()) { + auto wrapped_callback = listener_info.callback; + auto details = ExtractRequestInfo(request); + details->Set("responseHeaders", + GetResponseHeadersDict(original_response_headers)); + + BrowserThread::PostTaskAndReplyWithResult(BrowserThread::UI, FROM_HERE, + base::Bind(wrapped_callback, details), + base::Bind(&OnHeadersReceivedResponse, + callback, + override_response_headers)); + + return net::ERR_IO_PENDING; + } + + return net::OK; +} + +void AtomNetworkDelegate::OnBeforeRedirect(net::URLRequest* request, + const GURL& new_location) { + auto listener_info = event_listener_map_[kOnBeforeRedirect]; + if (!event_listener_map_.empty() && !listener_info.callback.is_null()) { + auto wrapped_callback = listener_info.callback; + auto details = ExtractRequestInfo(request); + details->SetString("redirectURL", new_location.spec()); + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(base::IgnoreResult(wrapped_callback), + details)); + } +} + +void AtomNetworkDelegate::OnResponseStarted(net::URLRequest* request) { + auto listener_info = event_listener_map_[kOnResponseStarted]; + if (!event_listener_map_.empty() && !listener_info.callback.is_null()) { + auto wrapped_callback = listener_info.callback; + auto details = ExtractRequestInfo(request); + details->Set("responseHeaders", + GetResponseHeadersDict(request->response_headers())); + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(base::IgnoreResult(wrapped_callback), + details)); + } +} + +void AtomNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { +} + +} // namespace atom diff --git a/atom/browser/net/atom_network_delegate.h b/atom/browser/net/atom_network_delegate.h new file mode 100644 index 000000000000..e1cfdb210035 --- /dev/null +++ b/atom/browser/net/atom_network_delegate.h @@ -0,0 +1,89 @@ +// 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_ATOM_NETWORK_DELEGATE_H_ +#define ATOM_BROWSER_NET_ATOM_NETWORK_DELEGATE_H_ + +#include +#include + +#include "brightray/browser/network_delegate.h" +#include "base/callback.h" +#include "base/values.h" +#include "net/http/http_request_headers.h" +#include "net/http/http_response_headers.h" + +namespace atom { + +class AtomNetworkDelegate : public brightray::NetworkDelegate { + public: + enum EventTypes { + kInvalidEvent = 0, + kOnBeforeRequest = 1 << 0, + kOnBeforeSendHeaders = 1 << 1, + kOnSendHeaders = 1 << 2, + kOnHeadersReceived = 1 << 3, + kOnBeforeRedirect = 1 << 4, + kOnResponseStarted = 1 << 5, + kOnErrorOccurred = 1 << 6, + kOnCompleted = 1 << 7, + }; + + struct BlockingResponse { + BlockingResponse() {} + ~BlockingResponse() {} + + bool cancel; + GURL redirectURL; + net::HttpRequestHeaders requestHeaders; + scoped_refptr responseHeaders; + }; + + using Listener = + base::Callback; + + AtomNetworkDelegate(); + ~AtomNetworkDelegate() override; + + void SetListenerInIO(EventTypes type, + const base::DictionaryValue* filter, + const Listener& callback); + + protected: + // net::NetworkDelegate: + int OnBeforeURLRequest(net::URLRequest* request, + const net::CompletionCallback& callback, + GURL* new_url) override; + int OnBeforeSendHeaders(net::URLRequest* request, + const net::CompletionCallback& callback, + net::HttpRequestHeaders* headers) override; + void OnSendHeaders(net::URLRequest* request, + const net::HttpRequestHeaders& headers) override; + int OnHeadersReceived( + net::URLRequest* request, + const net::CompletionCallback& callback, + const net::HttpResponseHeaders* original_response_headers, + scoped_refptr* override_response_headers, + GURL* allowed_unsafe_redirect_url) override; + void OnBeforeRedirect(net::URLRequest* request, + const GURL& new_location) override; + void OnResponseStarted(net::URLRequest* request) override; + void OnCompleted(net::URLRequest* request, bool started) override; + + private: + struct ListenerInfo { + ListenerInfo() {} + ~ListenerInfo() {} + + AtomNetworkDelegate::Listener callback; + }; + + static std::map event_listener_map_; + + DISALLOW_COPY_AND_ASSIGN(AtomNetworkDelegate); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_NET_ATOM_NETWORK_DELEGATE_H_ diff --git a/atom/common/native_mate_converters/net_converter.cc b/atom/common/native_mate_converters/net_converter.cc index 4749a4fedfc2..2086e84d5891 100644 --- a/atom/common/native_mate_converters/net_converter.cc +++ b/atom/common/native_mate_converters/net_converter.cc @@ -7,8 +7,11 @@ #include #include "atom/common/node_includes.h" +#include "atom/common/native_mate_converters/gurl_converter.h" +#include "atom/common/native_mate_converters/value_converter.h" #include "native_mate/dictionary.h" #include "net/cert/x509_certificate.h" +#include "net/http/http_response_headers.h" #include "net/url_request/url_request.h" namespace mate { @@ -50,4 +53,38 @@ 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->redirectURL)); + 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->requestHeaders.SetHeader(it.key(), value); + } + } + base::DictionaryValue response_headers; + if (dict.Get("responseHeaders", &response_headers)) { + out->responseHeaders = new net::HttpResponseHeaders(""); + for (base::DictionaryValue::Iterator it(response_headers); + !it.IsAtEnd(); + it.Advance()) { + std::string value; + CHECK(it.value().GetAsString(&value)); + out->responseHeaders->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 b11c55929b98..f251da8c5c8a 100644 --- a/atom/common/native_mate_converters/net_converter.h +++ b/atom/common/native_mate_converters/net_converter.h @@ -5,6 +5,7 @@ #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" @@ -34,6 +35,13 @@ 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/atom/common/native_mate_converters/value_converter.cc b/atom/common/native_mate_converters/value_converter.cc index c9c1a861ba26..431f11fbbdb3 100644 --- a/atom/common/native_mate_converters/value_converter.cc +++ b/atom/common/native_mate_converters/value_converter.cc @@ -30,6 +30,13 @@ v8::Local Converter::ToV8( return converter->ToV8Value(&val, isolate->GetCurrentContext()); } +v8::Local Converter::ToV8( + v8::Isolate* isolate, + const base::DictionaryValue* val) { + scoped_ptr converter(new atom::V8ValueConverter); + return converter->ToV8Value(val, isolate->GetCurrentContext()); +} + bool Converter::FromV8(v8::Isolate* isolate, v8::Local val, base::ListValue* out) { diff --git a/atom/common/native_mate_converters/value_converter.h b/atom/common/native_mate_converters/value_converter.h index 013dd99cc798..f660fd3f1897 100644 --- a/atom/common/native_mate_converters/value_converter.h +++ b/atom/common/native_mate_converters/value_converter.h @@ -23,6 +23,12 @@ struct Converter { const base::DictionaryValue& val); }; +template<> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + const base::DictionaryValue* val); +}; + template<> struct Converter { static bool FromV8(v8::Isolate* isolate, diff --git a/filenames.gypi b/filenames.gypi index aefe493335f1..640607d4017f 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -110,6 +110,8 @@ 'atom/browser/api/atom_api_tray.h', 'atom/browser/api/atom_api_web_contents.cc', 'atom/browser/api/atom_api_web_contents.h', + 'atom/browser/api/atom_api_web_request.cc', + 'atom/browser/api/atom_api_web_request.h', 'atom/browser/api/atom_api_web_view_manager.cc', 'atom/browser/api/atom_api_window.cc', 'atom/browser/api/atom_api_window.h', @@ -178,6 +180,8 @@ 'atom/browser/net/asar/url_request_asar_job.h', 'atom/browser/net/atom_cert_verifier.cc', 'atom/browser/net/atom_cert_verifier.h', + 'atom/browser/net/atom_network_delegate.cc', + 'atom/browser/net/atom_network_delegate.h', 'atom/browser/net/atom_ssl_config_service.cc', 'atom/browser/net/atom_ssl_config_service.h', 'atom/browser/net/atom_url_request_job_factory.cc', From 29f32c5ec71fd4a5fa6903a524486ff91dba6153 Mon Sep 17 00:00:00 2001 From: Robo Date: Fri, 4 Dec 2015 03:54:26 +0530 Subject: [PATCH 2/6] support filtering event with url regex --- atom/browser/api/atom_api_web_request.cc | 29 - atom/browser/api/atom_api_web_request.h | 9 +- atom/browser/net/atom_network_delegate.cc | 159 ++++- atom/browser/net/atom_network_delegate.h | 39 +- chromium_src/extensions/common/url_pattern.cc | 619 ++++++++++++++++++ chromium_src/extensions/common/url_pattern.h | 264 ++++++++ filenames.gypi | 2 + 7 files changed, 1061 insertions(+), 60 deletions(-) create mode 100644 chromium_src/extensions/common/url_pattern.cc create mode 100644 chromium_src/extensions/common/url_pattern.h diff --git a/atom/browser/api/atom_api_web_request.cc b/atom/browser/api/atom_api_web_request.cc index ad772e4c2963..c45cd900ab7a 100644 --- a/atom/browser/api/atom_api_web_request.cc +++ b/atom/browser/api/atom_api_web_request.cc @@ -45,35 +45,6 @@ void WebRequest::SetListener(mate::Arguments* args) { type, filter, callback)); } -mate::ObjectTemplateBuilder WebRequest::GetObjectTemplateBuilder( - v8::Isolate* isolate) { - return mate::ObjectTemplateBuilder(isolate) - .SetMethod("onBeforeRequest", - &WebRequest::SetListener< - AtomNetworkDelegate::kOnBeforeRequest>) - .SetMethod("onBeforeSendHeaders", - &WebRequest::SetListener< - AtomNetworkDelegate::kOnBeforeSendHeaders>) - .SetMethod("onSendHeaders", - &WebRequest::SetListener< - AtomNetworkDelegate::kOnSendHeaders>) - .SetMethod("onHeadersReceived", - &WebRequest::SetListener< - AtomNetworkDelegate::kOnHeadersReceived>) - .SetMethod("onBeforeRedirect", - &WebRequest::SetListener< - AtomNetworkDelegate::kOnBeforeRedirect>) - .SetMethod("onResponseStarted", - &WebRequest::SetListener< - AtomNetworkDelegate::kOnResponseStarted>) - .SetMethod("onCompleted", - &WebRequest::SetListener< - AtomNetworkDelegate::kOnCompleted>) - .SetMethod("onErrorOccurred", - &WebRequest::SetListener< - AtomNetworkDelegate::kOnErrorOccurred>); -} - // static mate::Handle WebRequest::Create( v8::Isolate* isolate, diff --git a/atom/browser/api/atom_api_web_request.h b/atom/browser/api/atom_api_web_request.h index 04fb758a9a7c..053bc1bec97d 100644 --- a/atom/browser/api/atom_api_web_request.h +++ b/atom/browser/api/atom_api_web_request.h @@ -7,10 +7,11 @@ #include +#include "atom/browser/api/trackable_object.h" #include "atom/browser/net/atom_network_delegate.h" #include "base/callback.h" #include "native_mate/arguments.h" -#include "native_mate/wrappable.h" +#include "native_mate/handle.h" namespace atom { @@ -18,7 +19,7 @@ class AtomBrowserContext; namespace api { -class WebRequest : public mate::TrackableObject { +class WebRequest : public mate::TrackableObject { public: static mate::Handle Create(v8::Isolate* isolate, AtomBrowserContext* browser_context); @@ -34,10 +35,6 @@ class WebRequest : public mate::TrackableObject { template void SetListener(mate::Arguments* args); - // mate::Wrappable: - mate::ObjectTemplateBuilder GetObjectTemplateBuilder( - v8::Isolate* isolate) override; - private: scoped_refptr browser_context_; diff --git a/atom/browser/net/atom_network_delegate.cc b/atom/browser/net/atom_network_delegate.cc index 3b3c8f971987..c8d341c57464 100644 --- a/atom/browser/net/atom_network_delegate.cc +++ b/atom/browser/net/atom_network_delegate.cc @@ -4,12 +4,9 @@ #include "atom/browser/net/atom_network_delegate.h" -#include - #include "atom/common/native_mate_converters/net_converter.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/resource_request_info.h" -#include "net/base/net_errors.h" #include "net/url_request/url_request.h" using content::BrowserThread; @@ -18,6 +15,40 @@ namespace atom { namespace { +std::string ResourceTypeToString(content::ResourceType type) { + switch (type) { + case content::RESOURCE_TYPE_MAIN_FRAME: + return "main_frame"; + case content::RESOURCE_TYPE_SUB_FRAME: + return "sub_frame"; + case content::RESOURCE_TYPE_STYLESHEET: + return "stylesheet"; + case content::RESOURCE_TYPE_SCRIPT: + return "script"; + case content::RESOURCE_TYPE_IMAGE: + return "image"; + case content::RESOURCE_TYPE_OBJECT: + return "object"; + case content::RESOURCE_TYPE_XHR: + return "xmlhttprequest"; + default: + return "other"; + } +} + +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; +} + base::DictionaryValue* ExtractRequestInfo(net::URLRequest* request) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("id", request->identifier()); @@ -27,7 +58,7 @@ base::DictionaryValue* ExtractRequestInfo(net::URLRequest* request) { auto info = content::ResourceRequestInfo::ForRequest(request); if (info) resourceType = info->GetResourceType(); - dict->SetInteger("resourceType", resourceType); + dict->SetString("resourceType", ResourceTypeToString(resourceType)); dict->SetDouble("timestamp", base::Time::Now().ToDoubleT() * 1000); return dict; @@ -61,7 +92,7 @@ void OnBeforeURLRequestResponse( const AtomNetworkDelegate::BlockingResponse& result) { if (!result.redirectURL.is_empty()) *new_url = result.redirectURL; - callback.Run(result.cancel); + callback.Run(result.Cancel()); } void OnBeforeSendHeadersResponse( @@ -70,7 +101,7 @@ void OnBeforeSendHeadersResponse( const AtomNetworkDelegate::BlockingResponse& result) { if (!result.requestHeaders.IsEmpty()) *headers = result.requestHeaders; - callback.Run(result.cancel); + callback.Run(result.Cancel()); } void OnHeadersReceivedResponse( @@ -79,7 +110,7 @@ void OnHeadersReceivedResponse( const AtomNetworkDelegate::BlockingResponse& result) { if (result.responseHeaders.get()) *override_response_headers = result.responseHeaders; - callback.Run(result.cancel); + callback.Run(result.Cancel()); } } // namespace @@ -100,6 +131,19 @@ void AtomNetworkDelegate::SetListenerInIO( const Listener& 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; } @@ -110,7 +154,11 @@ int AtomNetworkDelegate::OnBeforeURLRequest( brightray::NetworkDelegate::OnBeforeURLRequest(request, callback, new_url); auto listener_info = event_listener_map_[kOnBeforeRequest]; - if (!event_listener_map_.empty() && !listener_info.callback.is_null()) { + + if (!MatchesFilterCondition(request, listener_info)) + return net::OK; + + if (!listener_info.callback.is_null()) { auto wrapped_callback = listener_info.callback; auto details = ExtractRequestInfo(request); @@ -130,7 +178,11 @@ int AtomNetworkDelegate::OnBeforeSendHeaders( const net::CompletionCallback& callback, net::HttpRequestHeaders* headers) { auto listener_info = event_listener_map_[kOnBeforeSendHeaders]; - if (!event_listener_map_.empty() && !listener_info.callback.is_null()) { + + if (!MatchesFilterCondition(request, listener_info)) + return net::OK; + + if (!listener_info.callback.is_null()) { auto wrapped_callback = listener_info.callback; auto details = ExtractRequestInfo(request); details->Set("requestHeaders", GetRequestHeadersDict(*headers)); @@ -150,7 +202,11 @@ void AtomNetworkDelegate::OnSendHeaders( net::URLRequest* request, const net::HttpRequestHeaders& headers) { auto listener_info = event_listener_map_[kOnSendHeaders]; - if (!event_listener_map_.empty() && !listener_info.callback.is_null()) { + + if (!MatchesFilterCondition(request, listener_info)) + return; + + if (!listener_info.callback.is_null()) { auto wrapped_callback = listener_info.callback; auto details = ExtractRequestInfo(request); details->Set("requestHeaders", GetRequestHeadersDict(headers)); @@ -168,9 +224,17 @@ int AtomNetworkDelegate::OnHeadersReceived( scoped_refptr* override_response_headers, GURL* allowed_unsafe_redirect_url) { auto listener_info = event_listener_map_[kOnHeadersReceived]; - if (!event_listener_map_.empty() && !listener_info.callback.is_null()) { + + if (!MatchesFilterCondition(request, listener_info)) + return net::OK; + + if (!listener_info.callback.is_null()) { auto wrapped_callback = listener_info.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)); @@ -189,10 +253,19 @@ int AtomNetworkDelegate::OnHeadersReceived( void AtomNetworkDelegate::OnBeforeRedirect(net::URLRequest* request, const GURL& new_location) { auto listener_info = event_listener_map_[kOnBeforeRedirect]; - if (!event_listener_map_.empty() && !listener_info.callback.is_null()) { + + if (!MatchesFilterCondition(request, listener_info)) + return; + + if (!listener_info.callback.is_null()) { auto wrapped_callback = listener_info.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()); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(base::IgnoreResult(wrapped_callback), @@ -201,12 +274,23 @@ void AtomNetworkDelegate::OnBeforeRedirect(net::URLRequest* request, } void AtomNetworkDelegate::OnResponseStarted(net::URLRequest* request) { + if (request->status().status() != net::URLRequestStatus::SUCCESS) + return; + auto listener_info = event_listener_map_[kOnResponseStarted]; - if (!event_listener_map_.empty() && !listener_info.callback.is_null()) { + + if (!MatchesFilterCondition(request, listener_info)) + return; + + if (!listener_info.callback.is_null()) { auto wrapped_callback = listener_info.callback; auto details = ExtractRequestInfo(request); details->Set("responseHeaders", GetResponseHeadersDict(request->response_headers())); + details->SetBoolean("fromCache", request->was_cached()); + details->SetInteger("statusCode", + request->response_headers() ? + request->response_headers()->response_code() : 200); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(base::IgnoreResult(wrapped_callback), @@ -215,6 +299,55 @@ void AtomNetworkDelegate::OnResponseStarted(net::URLRequest* request) { } void AtomNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { + if (request->status().status() == net::URLRequestStatus::FAILED || + request->status().status() == net::URLRequestStatus::CANCELED) { + OnErrorOccurred(request); + return; + } else { + bool is_redirect = request->response_headers() && + net::HttpResponseHeaders::IsRedirectResponseCode( + request->response_headers()->response_code()); + if (is_redirect) + return; + } + + auto listener_info = event_listener_map_[kOnCompleted]; + + if (!MatchesFilterCondition(request, listener_info)) + return; + + if (!listener_info.callback.is_null()) { + auto wrapped_callback = listener_info.callback; + auto details = ExtractRequestInfo(request); + details->Set("responseHeaders", + GetResponseHeadersDict(request->response_headers())); + details->SetBoolean("fromCache", request->was_cached()); + details->SetInteger("statusCode", + request->response_headers() ? + request->response_headers()->response_code() : 200); + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(base::IgnoreResult(wrapped_callback), + details)); + } +} + +void AtomNetworkDelegate::OnErrorOccurred(net::URLRequest* request) { + auto listener_info = event_listener_map_[kOnErrorOccurred]; + + if (!MatchesFilterCondition(request, listener_info)) + return; + + if (!listener_info.callback.is_null()) { + auto wrapped_callback = listener_info.callback; + auto details = ExtractRequestInfo(request); + details->SetBoolean("fromCache", request->was_cached()); + details->SetString("error", net::ErrorToString(request->status().error())); + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(base::IgnoreResult(wrapped_callback), + details)); + } } } // namespace atom diff --git a/atom/browser/net/atom_network_delegate.h b/atom/browser/net/atom_network_delegate.h index e1cfdb210035..c48d252b2b0a 100644 --- a/atom/browser/net/atom_network_delegate.h +++ b/atom/browser/net/atom_network_delegate.h @@ -6,18 +6,29 @@ #define ATOM_BROWSER_NET_ATOM_NETWORK_DELEGATE_H_ #include +#include #include #include "brightray/browser/network_delegate.h" #include "base/callback.h" #include "base/values.h" +#include "extensions/common/url_pattern.h" +#include "net/base/net_errors.h" #include "net/http/http_request_headers.h" #include "net/http/http_response_headers.h" +namespace extensions { +class URLPattern; +} + namespace atom { class AtomNetworkDelegate : public brightray::NetworkDelegate { public: + struct BlockingResponse; + using Listener = + base::Callback; + enum EventTypes { kInvalidEvent = 0, kOnBeforeRequest = 1 << 0, @@ -26,23 +37,32 @@ class AtomNetworkDelegate : public brightray::NetworkDelegate { kOnHeadersReceived = 1 << 3, kOnBeforeRedirect = 1 << 4, kOnResponseStarted = 1 << 5, - kOnErrorOccurred = 1 << 6, - kOnCompleted = 1 << 7, + kOnCompleted = 1 << 6, + kOnErrorOccurred = 1 << 7, + }; + + struct ListenerInfo { + ListenerInfo() {} + ~ListenerInfo() {} + + std::set url_patterns; + AtomNetworkDelegate::Listener callback; }; struct BlockingResponse { BlockingResponse() {} ~BlockingResponse() {} + int Cancel() const { + return cancel ? net::ERR_BLOCKED_BY_CLIENT : net::OK; + } + bool cancel; GURL redirectURL; net::HttpRequestHeaders requestHeaders; scoped_refptr responseHeaders; }; - using Listener = - base::Callback; - AtomNetworkDelegate(); ~AtomNetworkDelegate() override; @@ -71,14 +91,9 @@ 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: - struct ListenerInfo { - ListenerInfo() {} - ~ListenerInfo() {} - - AtomNetworkDelegate::Listener callback; - }; - static std::map event_listener_map_; DISALLOW_COPY_AND_ASSIGN(AtomNetworkDelegate); diff --git a/chromium_src/extensions/common/url_pattern.cc b/chromium_src/extensions/common/url_pattern.cc new file mode 100644 index 000000000000..a6e51aa675b7 --- /dev/null +++ b/chromium_src/extensions/common/url_pattern.cc @@ -0,0 +1,619 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/common/url_pattern.h" + +#include + +#include "base/strings/pattern.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "content/public/common/url_constants.h" +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#include "url/gurl.h" +#include "url/url_util.h" + +const char extensions::URLPattern::kAllUrlsPattern[] = ""; +const char kExtensionScheme[] = "chrome-extension"; + +namespace { + +// TODO(aa): What about more obscure schemes like data: and javascript: ? +// Note: keep this array in sync with kValidSchemeMasks. +const char* kValidSchemes[] = { + url::kHttpScheme, + url::kHttpsScheme, + url::kFileScheme, + url::kFtpScheme, + content::kChromeUIScheme, + kExtensionScheme, + url::kFileSystemScheme, +}; + +const int kValidSchemeMasks[] = { + extensions::URLPattern::SCHEME_HTTP, + extensions::URLPattern::SCHEME_HTTPS, + extensions::URLPattern::SCHEME_FILE, + extensions::URLPattern::SCHEME_FTP, + extensions::URLPattern::SCHEME_CHROMEUI, + extensions::URLPattern::SCHEME_EXTENSION, + extensions::URLPattern::SCHEME_FILESYSTEM, +}; + +static_assert(arraysize(kValidSchemes) == arraysize(kValidSchemeMasks), + "must keep these arrays in sync"); + +const char kParseSuccess[] = "Success."; +const char kParseErrorMissingSchemeSeparator[] = "Missing scheme separator."; +const char kParseErrorInvalidScheme[] = "Invalid scheme."; +const char kParseErrorWrongSchemeType[] = "Wrong scheme type."; +const char kParseErrorEmptyHost[] = "Host can not be empty."; +const char kParseErrorInvalidHostWildcard[] = "Invalid host wildcard."; +const char kParseErrorEmptyPath[] = "Empty path."; +const char kParseErrorInvalidPort[] = "Invalid port."; +const char kParseErrorInvalidHost[] = "Invalid host."; + +// Message explaining each URLPattern::ParseResult. +const char* const kParseResultMessages[] = { + kParseSuccess, + kParseErrorMissingSchemeSeparator, + kParseErrorInvalidScheme, + kParseErrorWrongSchemeType, + kParseErrorEmptyHost, + kParseErrorInvalidHostWildcard, + kParseErrorEmptyPath, + kParseErrorInvalidPort, + kParseErrorInvalidHost, +}; + +static_assert(extensions::URLPattern::NUM_PARSE_RESULTS == arraysize(kParseResultMessages), + "must add message for each parse result"); + +const char kPathSeparator[] = "/"; + +bool IsStandardScheme(const std::string& scheme) { + // "*" gets the same treatment as a standard scheme. + if (scheme == "*") + return true; + + return url::IsStandard(scheme.c_str(), + url::Component(0, static_cast(scheme.length()))); +} + +bool IsValidPortForScheme(const std::string& scheme, const std::string& port) { + if (port == "*") + return true; + + // Only accept non-wildcard ports if the scheme uses ports. + if (url::DefaultPortForScheme(scheme.c_str(), scheme.length()) == + url::PORT_UNSPECIFIED) { + return false; + } + + int parsed_port = url::PORT_UNSPECIFIED; + if (!base::StringToInt(port, &parsed_port)) + return false; + return (parsed_port >= 0) && (parsed_port < 65536); +} + +// Returns |path| with the trailing wildcard stripped if one existed. +// +// The functions that rely on this (OverlapsWith and Contains) are only +// called for the patterns inside URLPatternSet. In those cases, we know that +// the path will have only a single wildcard at the end. This makes figuring +// out overlap much easier. It seems like there is probably a computer-sciency +// way to solve the general case, but we don't need that yet. +std::string StripTrailingWildcard(const std::string& path) { + size_t wildcard_index = path.find('*'); + size_t path_last = path.size() - 1; + return wildcard_index == path_last ? path.substr(0, path_last) : path; +} + +} // namespace + +namespace extensions { +// static +bool URLPattern::IsValidSchemeForExtensions(const std::string& scheme) { + for (size_t i = 0; i < arraysize(kValidSchemes); ++i) { + if (scheme == kValidSchemes[i]) + return true; + } + return false; +} + +URLPattern::URLPattern() + : valid_schemes_(SCHEME_ALL), + match_all_urls_(false), + match_subdomains_(false), + port_("*") {} + +URLPattern::URLPattern(int valid_schemes) + : valid_schemes_(valid_schemes), + match_all_urls_(false), + match_subdomains_(false), + port_("*") {} + +URLPattern::URLPattern(int valid_schemes, const std::string& pattern) + // Strict error checking is used, because this constructor is only + // appropriate when we know |pattern| is valid. + : valid_schemes_(valid_schemes), + match_all_urls_(false), + match_subdomains_(false), + port_("*") { + ParseResult result = Parse(pattern); + if (PARSE_SUCCESS != result) + NOTREACHED() << "URLPattern invalid: " << pattern << " result " << result; +} + +URLPattern::~URLPattern() { +} + +bool URLPattern::operator<(const URLPattern& other) const { + return GetAsString() < other.GetAsString(); +} + +bool URLPattern::operator>(const URLPattern& other) const { + return GetAsString() > other.GetAsString(); +} + +bool URLPattern::operator==(const URLPattern& other) const { + return GetAsString() == other.GetAsString(); +} + +std::ostream& operator<<(std::ostream& out, const URLPattern& url_pattern) { + return out << '"' << url_pattern.GetAsString() << '"'; +} + +URLPattern::ParseResult URLPattern::Parse(const std::string& pattern) { + spec_.clear(); + SetMatchAllURLs(false); + SetMatchSubdomains(false); + SetPort("*"); + + // Special case pattern to match every valid URL. + if (pattern == kAllUrlsPattern) { + SetMatchAllURLs(true); + return PARSE_SUCCESS; + } + + // Parse out the scheme. + size_t scheme_end_pos = pattern.find(url::kStandardSchemeSeparator); + bool has_standard_scheme_separator = true; + + // Some urls also use ':' alone as the scheme separator. + if (scheme_end_pos == std::string::npos) { + scheme_end_pos = pattern.find(':'); + has_standard_scheme_separator = false; + } + + if (scheme_end_pos == std::string::npos) + return PARSE_ERROR_MISSING_SCHEME_SEPARATOR; + + if (!SetScheme(pattern.substr(0, scheme_end_pos))) + return PARSE_ERROR_INVALID_SCHEME; + + bool standard_scheme = IsStandardScheme(scheme_); + if (standard_scheme != has_standard_scheme_separator) + return PARSE_ERROR_WRONG_SCHEME_SEPARATOR; + + // Advance past the scheme separator. + scheme_end_pos += + (standard_scheme ? strlen(url::kStandardSchemeSeparator) : 1); + if (scheme_end_pos >= pattern.size()) + return PARSE_ERROR_EMPTY_HOST; + + // Parse out the host and path. + size_t host_start_pos = scheme_end_pos; + size_t path_start_pos = 0; + + if (!standard_scheme) { + path_start_pos = host_start_pos; + } else if (scheme_ == url::kFileScheme) { + size_t host_end_pos = pattern.find(kPathSeparator, host_start_pos); + if (host_end_pos == std::string::npos) { + // Allow hostname omission. + // e.g. file://* is interpreted as file:///*, + // file://foo* is interpreted as file:///foo*. + path_start_pos = host_start_pos - 1; + } else { + // Ignore hostname if scheme is file://. + // e.g. file://localhost/foo is equal to file:///foo. + path_start_pos = host_end_pos; + } + } else { + size_t host_end_pos = pattern.find(kPathSeparator, host_start_pos); + + // Host is required. + if (host_start_pos == host_end_pos) + return PARSE_ERROR_EMPTY_HOST; + + if (host_end_pos == std::string::npos) + return PARSE_ERROR_EMPTY_PATH; + + host_ = pattern.substr(host_start_pos, host_end_pos - host_start_pos); + + // The first component can optionally be '*' to match all subdomains. + std::vector host_components = base::SplitString( + host_, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + + // Could be empty if the host only consists of whitespace characters. + if (host_components.empty() || + (host_components.size() == 1 && host_components[0].empty())) + return PARSE_ERROR_EMPTY_HOST; + + if (host_components[0] == "*") { + match_subdomains_ = true; + host_components.erase(host_components.begin(), + host_components.begin() + 1); + } + host_ = JoinString(host_components, "."); + + path_start_pos = host_end_pos; + } + + SetPath(pattern.substr(path_start_pos)); + + size_t port_pos = host_.find(':'); + if (port_pos != std::string::npos) { + if (!SetPort(host_.substr(port_pos + 1))) + return PARSE_ERROR_INVALID_PORT; + host_ = host_.substr(0, port_pos); + } + + // No other '*' can occur in the host, though. This isn't necessary, but is + // done as a convenience to developers who might otherwise be confused and + // think '*' works as a glob in the host. + if (host_.find('*') != std::string::npos) + return PARSE_ERROR_INVALID_HOST_WILDCARD; + + // Null characters are not allowed in hosts. + if (host_.find('\0') != std::string::npos) + return PARSE_ERROR_INVALID_HOST; + + return PARSE_SUCCESS; +} + +void URLPattern::SetValidSchemes(int valid_schemes) { + spec_.clear(); + valid_schemes_ = valid_schemes; +} + +void URLPattern::SetHost(const std::string& host) { + spec_.clear(); + host_ = host; +} + +void URLPattern::SetMatchAllURLs(bool val) { + spec_.clear(); + match_all_urls_ = val; + + if (val) { + match_subdomains_ = true; + scheme_ = "*"; + host_.clear(); + SetPath("/*"); + } +} + +void URLPattern::SetMatchSubdomains(bool val) { + spec_.clear(); + match_subdomains_ = val; +} + +bool URLPattern::SetScheme(const std::string& scheme) { + spec_.clear(); + scheme_ = scheme; + if (scheme_ == "*") { + valid_schemes_ &= (SCHEME_HTTP | SCHEME_HTTPS); + } else if (!IsValidScheme(scheme_)) { + return false; + } + return true; +} + +bool URLPattern::IsValidScheme(const std::string& scheme) const { + if (valid_schemes_ == SCHEME_ALL) + return true; + + for (size_t i = 0; i < arraysize(kValidSchemes); ++i) { + if (scheme == kValidSchemes[i] && (valid_schemes_ & kValidSchemeMasks[i])) + return true; + } + + return false; +} + +void URLPattern::SetPath(const std::string& path) { + spec_.clear(); + path_ = path; + path_escaped_ = path_; + base::ReplaceSubstringsAfterOffset(&path_escaped_, 0, "\\", "\\\\"); + base::ReplaceSubstringsAfterOffset(&path_escaped_, 0, "?", "\\?"); +} + +bool URLPattern::SetPort(const std::string& port) { + spec_.clear(); + if (IsValidPortForScheme(scheme_, port)) { + port_ = port; + return true; + } + return false; +} + +bool URLPattern::MatchesURL(const GURL& test) const { + const GURL* test_url = &test; + bool has_inner_url = test.inner_url() != NULL; + + if (has_inner_url) { + if (!test.SchemeIsFileSystem()) + return false; // The only nested URLs we handle are filesystem URLs. + test_url = test.inner_url(); + } + + if (!MatchesScheme(test_url->scheme())) + return false; + + if (match_all_urls_) + return true; + + std::string path_for_request = test.PathForRequest(); + if (has_inner_url) + path_for_request = test_url->path() + path_for_request; + + return MatchesSecurityOriginHelper(*test_url) && + MatchesPath(path_for_request); +} + +bool URLPattern::MatchesSecurityOrigin(const GURL& test) const { + const GURL* test_url = &test; + bool has_inner_url = test.inner_url() != NULL; + + if (has_inner_url) { + if (!test.SchemeIsFileSystem()) + return false; // The only nested URLs we handle are filesystem URLs. + test_url = test.inner_url(); + } + + if (!MatchesScheme(test_url->scheme())) + return false; + + if (match_all_urls_) + return true; + + return MatchesSecurityOriginHelper(*test_url); +} + +bool URLPattern::MatchesScheme(const std::string& test) const { + if (!IsValidScheme(test)) + return false; + + return scheme_ == "*" || test == scheme_; +} + +bool URLPattern::MatchesHost(const std::string& host) const { + std::string test(url::kHttpScheme); + test += url::kStandardSchemeSeparator; + test += host; + test += "/"; + return MatchesHost(GURL(test)); +} + +bool URLPattern::MatchesHost(const GURL& test) const { + // If the hosts are exactly equal, we have a match. + if (test.host() == host_) + return true; + + // If we're matching subdomains, and we have no host in the match pattern, + // that means that we're matching all hosts, which means we have a match no + // matter what the test host is. + if (match_subdomains_ && host_.empty()) + return true; + + // Otherwise, we can only match if our match pattern matches subdomains. + if (!match_subdomains_) + return false; + + // We don't do subdomain matching against IP addresses, so we can give up now + // if the test host is an IP address. + if (test.HostIsIPAddress()) + return false; + + // Check if the test host is a subdomain of our host. + if (test.host().length() <= (host_.length() + 1)) + return false; + + if (test.host().compare(test.host().length() - host_.length(), + host_.length(), host_) != 0) + return false; + + return test.host()[test.host().length() - host_.length() - 1] == '.'; +} + +bool URLPattern::ImpliesAllHosts() const { + // Check if it matches all urls or is a pattern like http://*/*. + if (match_all_urls_ || + (match_subdomains_ && host_.empty() && port_ == "*" && path_ == "/*")) { + return true; + } + + // If this doesn't even match subdomains, it can't possibly imply all hosts. + if (!match_subdomains_) + return false; + + // If |host_| is a recognized TLD, this will be 0. We don't include private + // TLDs, so that, e.g., *.appspot.com does not imply all hosts. + size_t registry_length = net::registry_controlled_domains::GetRegistryLength( + host_, + net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, + net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); + // If there was more than just a TLD in the host (e.g., *.foobar.com), it + // doesn't imply all hosts. + if (registry_length > 0) + return false; + + // At this point the host could either be just a TLD ("com") or some unknown + // TLD-like string ("notatld"). To disambiguate between them construct a + // fake URL, and check the registry. This returns 0 if the TLD is + // unrecognized, or the length of the recognized TLD. + registry_length = net::registry_controlled_domains::GetRegistryLength( + base::StringPrintf("foo.%s", host_.c_str()), + net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, + net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); + // If we recognized this TLD, then this is a pattern like *.com, and it + // should imply all hosts. Otherwise, this doesn't imply all hosts. + return registry_length > 0; +} + +bool URLPattern::MatchesSingleOrigin() const { + // Strictly speaking, the port is part of the origin, but in URLPattern it + // defaults to *. It's not very interesting anyway, so leave it out. + return !ImpliesAllHosts() && scheme_ != "*" && !match_subdomains_; +} + +bool URLPattern::MatchesPath(const std::string& test) const { + // Make the behaviour of OverlapsWith consistent with MatchesURL, which is + // need to match hosted apps on e.g. 'google.com' also run on 'google.com/'. + if (test + "/*" == path_escaped_) + return true; + + return base::MatchPattern(test, path_escaped_); +} + +const std::string& URLPattern::GetAsString() const { + if (!spec_.empty()) + return spec_; + + if (match_all_urls_) { + spec_ = kAllUrlsPattern; + return spec_; + } + + bool standard_scheme = IsStandardScheme(scheme_); + + std::string spec = scheme_ + + (standard_scheme ? url::kStandardSchemeSeparator : ":"); + + if (scheme_ != url::kFileScheme && standard_scheme) { + if (match_subdomains_) { + spec += "*"; + if (!host_.empty()) + spec += "."; + } + + if (!host_.empty()) + spec += host_; + + if (port_ != "*") { + spec += ":"; + spec += port_; + } + } + + if (!path_.empty()) + spec += path_; + + spec_ = spec; + return spec_; +} + +bool URLPattern::OverlapsWith(const URLPattern& other) const { + if (match_all_urls() || other.match_all_urls()) + return true; + return (MatchesAnyScheme(other.GetExplicitSchemes()) || + other.MatchesAnyScheme(GetExplicitSchemes())) + && (MatchesHost(other.host()) || other.MatchesHost(host())) + && (MatchesPortPattern(other.port()) || other.MatchesPortPattern(port())) + && (MatchesPath(StripTrailingWildcard(other.path())) || + other.MatchesPath(StripTrailingWildcard(path()))); +} + +bool URLPattern::Contains(const URLPattern& other) const { + if (match_all_urls()) + return true; + return MatchesAllSchemes(other.GetExplicitSchemes()) && + MatchesHost(other.host()) && + (!other.match_subdomains_ || match_subdomains_) && + MatchesPortPattern(other.port()) && + MatchesPath(StripTrailingWildcard(other.path())); +} + +bool URLPattern::MatchesAnyScheme( + const std::vector& schemes) const { + for (std::vector::const_iterator i = schemes.begin(); + i != schemes.end(); ++i) { + if (MatchesScheme(*i)) + return true; + } + + return false; +} + +bool URLPattern::MatchesAllSchemes( + const std::vector& schemes) const { + for (std::vector::const_iterator i = schemes.begin(); + i != schemes.end(); ++i) { + if (!MatchesScheme(*i)) + return false; + } + + return true; +} + +bool URLPattern::MatchesSecurityOriginHelper(const GURL& test) const { + // Ignore hostname if scheme is file://. + if (scheme_ != url::kFileScheme && !MatchesHost(test)) + return false; + + if (!MatchesPortPattern(base::IntToString(test.EffectiveIntPort()))) + return false; + + return true; +} + +bool URLPattern::MatchesPortPattern(const std::string& port) const { + return port_ == "*" || port_ == port; +} + +std::vector URLPattern::GetExplicitSchemes() const { + std::vector result; + + if (scheme_ != "*" && !match_all_urls_ && IsValidScheme(scheme_)) { + result.push_back(scheme_); + return result; + } + + for (size_t i = 0; i < arraysize(kValidSchemes); ++i) { + if (MatchesScheme(kValidSchemes[i])) { + result.push_back(kValidSchemes[i]); + } + } + + return result; +} + +std::vector URLPattern::ConvertToExplicitSchemes() const { + std::vector explicit_schemes = GetExplicitSchemes(); + std::vector result; + + for (std::vector::const_iterator i = explicit_schemes.begin(); + i != explicit_schemes.end(); ++i) { + URLPattern temp = *this; + temp.SetScheme(*i); + temp.SetMatchAllURLs(false); + result.push_back(temp); + } + + return result; +} + +// static +const char* URLPattern::GetParseResultString( + URLPattern::ParseResult parse_result) { + return kParseResultMessages[parse_result]; +} + +} // namespace extensions diff --git a/chromium_src/extensions/common/url_pattern.h b/chromium_src/extensions/common/url_pattern.h new file mode 100644 index 000000000000..fa9b6495e317 --- /dev/null +++ b/chromium_src/extensions/common/url_pattern.h @@ -0,0 +1,264 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#ifndef EXTENSIONS_COMMON_URL_PATTERN_H_ +#define EXTENSIONS_COMMON_URL_PATTERN_H_ + +#include +#include +#include +#include + +class GURL; + +namespace extensions { +// A pattern that can be used to match URLs. A URLPattern is a very restricted +// subset of URL syntax: +// +// := :// | '' +// := '*' | 'http' | 'https' | 'file' | 'ftp' | 'chrome' | +// 'chrome-extension' | 'filesystem' +// := '*' | '*.' + +// := [':' ('*' | )] +// := '/' +// +// * Host is not used when the scheme is 'file'. +// * The path can have embedded '*' characters which act as glob wildcards. +// * '' is a special pattern that matches any URL that contains a +// valid scheme (as specified by valid_schemes_). +// * The '*' scheme pattern excludes file URLs. +// +// Examples of valid patterns: +// - http://*/* +// - http://*/foo* +// - https://*.google.com/foo*bar +// - file://monkey* +// - http://127.0.0.1/* +// +// Examples of invalid patterns: +// - http://* -- path not specified +// - http://*foo/bar -- * not allowed as substring of host component +// - http://foo.*.bar/baz -- * must be first component +// - http:/bar -- scheme separator not found +// - foo://* -- invalid scheme +// - chrome:// -- we don't support chrome internal URLs +class URLPattern { + public: + // A collection of scheme bitmasks for use with valid_schemes. + enum SchemeMasks { + SCHEME_NONE = 0, + SCHEME_HTTP = 1 << 0, + SCHEME_HTTPS = 1 << 1, + SCHEME_FILE = 1 << 2, + SCHEME_FTP = 1 << 3, + SCHEME_CHROMEUI = 1 << 4, + SCHEME_EXTENSION = 1 << 5, + SCHEME_FILESYSTEM = 1 << 6, + + // IMPORTANT! + // SCHEME_ALL will match every scheme, including chrome://, chrome- + // extension://, about:, etc. Because this has lots of security + // implications, third-party extensions should usually not be able to get + // access to URL patterns initialized this way. If there is a reason + // for violating this general rule, document why this it safe. + SCHEME_ALL = -1, + }; + + // Error codes returned from Parse(). + enum ParseResult { + PARSE_SUCCESS = 0, + PARSE_ERROR_MISSING_SCHEME_SEPARATOR, + PARSE_ERROR_INVALID_SCHEME, + PARSE_ERROR_WRONG_SCHEME_SEPARATOR, + PARSE_ERROR_EMPTY_HOST, + PARSE_ERROR_INVALID_HOST_WILDCARD, + PARSE_ERROR_EMPTY_PATH, + PARSE_ERROR_INVALID_PORT, + PARSE_ERROR_INVALID_HOST, + NUM_PARSE_RESULTS + }; + + // The string pattern. + static const char kAllUrlsPattern[]; + + // Returns true if the given |scheme| is considered valid for extensions. + static bool IsValidSchemeForExtensions(const std::string& scheme); + + explicit URLPattern(int valid_schemes); + + // Convenience to construct a URLPattern from a string. If the string is not + // known ahead of time, use Parse() instead, which returns success or failure. + URLPattern(int valid_schemes, const std::string& pattern); + + URLPattern(); + ~URLPattern(); + + bool operator<(const URLPattern& other) const; + bool operator>(const URLPattern& other) const; + bool operator==(const URLPattern& other) const; + + // Initializes this instance by parsing the provided string. Returns + // URLPattern::PARSE_SUCCESS on success, or an error code otherwise. On + // failure, this instance will have some intermediate values and is in an + // invalid state. + ParseResult Parse(const std::string& pattern_str); + + // Gets the bitmask of valid schemes. + int valid_schemes() const { return valid_schemes_; } + void SetValidSchemes(int valid_schemes); + + // Gets the host the pattern matches. This can be an empty string if the + // pattern matches all hosts (the input was ://*/). + const std::string& host() const { return host_; } + void SetHost(const std::string& host); + + // Gets whether to match subdomains of host(). + bool match_subdomains() const { return match_subdomains_; } + void SetMatchSubdomains(bool val); + + // Gets the path the pattern matches with the leading slash. This can have + // embedded asterisks which are interpreted using glob rules. + const std::string& path() const { return path_; } + void SetPath(const std::string& path); + + // Returns true if this pattern matches all urls. + bool match_all_urls() const { return match_all_urls_; } + void SetMatchAllURLs(bool val); + + // Sets the scheme for pattern matches. This can be a single '*' if the + // pattern matches all valid schemes (as defined by the valid_schemes_ + // property). Returns false on failure (if the scheme is not valid). + bool SetScheme(const std::string& scheme); + // Note: You should use MatchesScheme() instead of this getter unless you + // absolutely need the exact scheme. This is exposed for testing. + const std::string& scheme() const { return scheme_; } + + // Returns true if the specified scheme can be used in this URL pattern, and + // false otherwise. Uses valid_schemes_ to determine validity. + bool IsValidScheme(const std::string& scheme) const; + + // Returns true if this instance matches the specified URL. + bool MatchesURL(const GURL& test) const; + + // Returns true if this instance matches the specified security origin. + bool MatchesSecurityOrigin(const GURL& test) const; + + // Returns true if |test| matches our scheme. + // Note that if test is "filesystem", this may fail whereas MatchesURL + // may succeed. MatchesURL is smart enough to look at the inner_url instead + // of the outer "filesystem:" part. + bool MatchesScheme(const std::string& test) const; + + // Returns true if |test| matches our host. + bool MatchesHost(const std::string& test) const; + bool MatchesHost(const GURL& test) const; + + // Returns true if |test| matches our path. + bool MatchesPath(const std::string& test) const; + + // Returns true if the pattern is vague enough that it implies all hosts, + // such as *://*/*. + // This is an expensive method, and should be used sparingly! + // You should probably use URLPatternSet::ShouldWarnAllHosts(), which is + // cached. + bool ImpliesAllHosts() const; + + // Returns true if the pattern only matches a single origin. The pattern may + // include a path. + bool MatchesSingleOrigin() const; + + // Sets the port. Returns false if the port is invalid. + bool SetPort(const std::string& port); + const std::string& port() const { return port_; } + + // Returns a string representing this instance. + const std::string& GetAsString() const; + + // Determines whether there is a URL that would match this instance and + // another instance. This method is symmetrical: Calling + // other.OverlapsWith(this) would result in the same answer. + bool OverlapsWith(const URLPattern& other) const; + + // Returns true if this pattern matches all possible URLs that |other| can + // match. For example, http://*.google.com encompasses http://www.google.com. + bool Contains(const URLPattern& other) const; + + // Converts this URLPattern into an equivalent set of URLPatterns that don't + // use a wildcard in the scheme component. If this URLPattern doesn't use a + // wildcard scheme, then the returned set will contain one element that is + // equivalent to this instance. + std::vector ConvertToExplicitSchemes() const; + + static bool EffectiveHostCompare(const URLPattern& a, const URLPattern& b) { + if (a.match_all_urls_ && b.match_all_urls_) + return false; + return a.host_.compare(b.host_) < 0; + } + + // Used for origin comparisons in a std::set. + class EffectiveHostCompareFunctor { + public: + bool operator()(const URLPattern& a, const URLPattern& b) const { + return EffectiveHostCompare(a, b); + } + }; + + // Get an error string for a ParseResult. + static const char* GetParseResultString(URLPattern::ParseResult parse_result); + + private: + // Returns true if any of the |schemes| items matches our scheme. + bool MatchesAnyScheme(const std::vector& schemes) const; + + // Returns true if all of the |schemes| items matches our scheme. + bool MatchesAllSchemes(const std::vector& schemes) const; + + bool MatchesSecurityOriginHelper(const GURL& test) const; + + // Returns true if our port matches the |port| pattern (it may be "*"). + bool MatchesPortPattern(const std::string& port) const; + + // If the URLPattern contains a wildcard scheme, returns a list of + // equivalent literal schemes, otherwise returns the current scheme. + std::vector GetExplicitSchemes() const; + + // A bitmask containing the schemes which are considered valid for this + // pattern. Parse() uses this to decide whether a pattern contains a valid + // scheme. + int valid_schemes_; + + // True if this is a special-case "" pattern. + bool match_all_urls_; + + // The scheme for the pattern. + std::string scheme_; + + // The host without any leading "*" components. + std::string host_; + + // Whether we should match subdomains of the host. This is true if the first + // component of the pattern's host was "*". + bool match_subdomains_; + + // The port. + std::string port_; + + // The path to match. This is everything after the host of the URL, or + // everything after the scheme in the case of file:// URLs. + std::string path_; + + // The path with "?" and "\" characters escaped for use with the + // MatchPattern() function. + std::string path_escaped_; + + // A string representing this URLPattern. + mutable std::string spec_; +}; + +std::ostream& operator<<(std::ostream& out, const URLPattern& url_pattern); + +typedef std::vector URLPatternList; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_URL_PATTERN_H_ diff --git a/filenames.gypi b/filenames.gypi index 640607d4017f..f1209eeeb0eb 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -476,6 +476,8 @@ 'chromium_src/chrome/utility/utility_message_handler.h', 'chromium_src/extensions/browser/app_window/size_constraints.cc', 'chromium_src/extensions/browser/app_window/size_constraints.h', + 'chromium_src/extensions/common/url_pattern.cc', + 'chromium_src/extensions/common/url_pattern.h', 'chromium_src/library_loaders/libspeechd_loader.cc', 'chromium_src/library_loaders/libspeechd.h', 'chromium_src/net/test/embedded_test_server/stream_listen_socket.cc', From c5b5bbbeb27801255f5f2a4d16deb264b5c89d31 Mon Sep 17 00:00:00 2001 From: Robo Date: Fri, 4 Dec 2015 05:33:56 +0530 Subject: [PATCH 3/6] add documentation --- atom/browser/net/atom_network_delegate.cc | 8 + docs/api/session.md | 175 ++++++++++++++++++++++ 2 files changed, 183 insertions(+) diff --git a/atom/browser/net/atom_network_delegate.cc b/atom/browser/net/atom_network_delegate.cc index c8d341c57464..1ee886ba5d44 100644 --- a/atom/browser/net/atom_network_delegate.cc +++ b/atom/browser/net/atom_network_delegate.cc @@ -266,6 +266,8 @@ void AtomNetworkDelegate::OnBeforeRedirect(net::URLRequest* request, if (!ip.empty()) details->SetString("ip", ip); details->SetBoolean("fromCache", request->was_cached()); + details->Set("responseHeaders", + GetResponseHeadersDict(request->response_headers())); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(base::IgnoreResult(wrapped_callback), @@ -291,6 +293,9 @@ void AtomNetworkDelegate::OnResponseStarted(net::URLRequest* request) { details->SetInteger("statusCode", request->response_headers() ? request->response_headers()->response_code() : 200); + details->SetString("statusLine", + request->response_headers() ? + request->response_headers()->GetStatusLine() : std::string()); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(base::IgnoreResult(wrapped_callback), @@ -325,6 +330,9 @@ void AtomNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { details->SetInteger("statusCode", request->response_headers() ? request->response_headers()->response_code() : 200); + details->SetString("statusLine", + request->response_headers() ? + request->response_headers()->GetStatusLine() : std::string()); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(base::IgnoreResult(wrapped_callback), diff --git a/docs/api/session.md b/docs/api/session.md index a69c5939ed65..d889ace4e73d 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -286,3 +286,178 @@ myWindow.webContents.session.setCertificateVerifyProc(function(hostname, cert, c callback(false); }); ``` + +#### `ses.webRequest` + +The `webRequest` api allows to intercept and modify contents of a request at various +stages of its lifetime. + +```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) { + details.requestHeaders['User-Agent'] = "MyAgent"; + return {cancel: false, requestHeaders: details.requestHeaders}; +}) +``` + +#### `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`. + +#### `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`. + +#### `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. + +#### `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`. + +#### `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. + +#### `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 - 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. + +#### `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. + +#### `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. From 461ee499888d2860c8c849f65d80339866b68dba Mon Sep 17 00:00:00 2001 From: Robo Date: Fri, 4 Dec 2015 07:24:01 +0530 Subject: [PATCH 4/6] fix response headers modification --- atom/browser/net/atom_network_delegate.cc | 38 ++++++++++++++++------- docs/api/session.md | 16 +++++----- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/atom/browser/net/atom_network_delegate.cc b/atom/browser/net/atom_network_delegate.cc index 1ee886ba5d44..d4c83996b9cc 100644 --- a/atom/browser/net/atom_network_delegate.cc +++ b/atom/browser/net/atom_network_delegate.cc @@ -44,9 +44,10 @@ bool MatchesFilterCondition( for (auto& pattern : info.url_patterns) if (pattern.MatchesURL(url)) return true; + return false; } - return false; + return true; } base::DictionaryValue* ExtractRequestInfo(net::URLRequest* request) { @@ -106,10 +107,20 @@ void OnBeforeSendHeadersResponse( void OnHeadersReceivedResponse( const net::CompletionCallback& callback, + const net::HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, const AtomNetworkDelegate::BlockingResponse& result) { - if (result.responseHeaders.get()) - *override_response_headers = result.responseHeaders; + if (result.responseHeaders.get()) { + *override_response_headers = new net::HttpResponseHeaders( + original_response_headers->raw_headers()); + void* iter = nullptr; + std::string key; + std::string value; + while (result.responseHeaders->EnumerateHeaderLines(&iter, &key, &value)) { + (*override_response_headers)->RemoveHeader(key); + (*override_response_headers)->AddHeader(key + ": " + value); + } + } callback.Run(result.Cancel()); } @@ -242,6 +253,7 @@ int AtomNetworkDelegate::OnHeadersReceived( base::Bind(wrapped_callback, details), base::Bind(&OnHeadersReceivedResponse, callback, + original_response_headers, override_response_headers)); return net::ERR_IO_PENDING; @@ -290,12 +302,14 @@ void AtomNetworkDelegate::OnResponseStarted(net::URLRequest* request) { details->Set("responseHeaders", GetResponseHeadersDict(request->response_headers())); details->SetBoolean("fromCache", request->was_cached()); + + auto response_headers = request->response_headers(); details->SetInteger("statusCode", - request->response_headers() ? - request->response_headers()->response_code() : 200); + response_headers ? + response_headers->response_code() : 200); details->SetString("statusLine", - request->response_headers() ? - request->response_headers()->GetStatusLine() : std::string()); + response_headers ? + response_headers->GetStatusLine() : std::string()); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(base::IgnoreResult(wrapped_callback), @@ -327,12 +341,14 @@ void AtomNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { details->Set("responseHeaders", GetResponseHeadersDict(request->response_headers())); details->SetBoolean("fromCache", request->was_cached()); + + auto response_headers = request->response_headers(); details->SetInteger("statusCode", - request->response_headers() ? - request->response_headers()->response_code() : 200); + response_headers ? + response_headers->response_code() : 200); details->SetString("statusLine", - request->response_headers() ? - request->response_headers()->GetStatusLine() : std::string()); + response_headers ? + response_headers->GetStatusLine() : std::string()); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(base::IgnoreResult(wrapped_callback), diff --git a/docs/api/session.md b/docs/api/session.md index d889ace4e73d..b16a9b1f0f83 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -309,7 +309,7 @@ myWindow.webContents.session.webRequest.onBeforeSendHeaders(filter, function(det * `filter` Object * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. + will be filtered out. * `listener` Function * `details` Object * `id` String - Request id. @@ -320,7 +320,7 @@ myWindow.webContents.session.webRequest.onBeforeSendHeaders(filter, function(det * `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. + completed, and is instead redirected to the given URL. Fired when a request is about to occur. Should return a `blockingResponse`. @@ -328,7 +328,7 @@ Fired when a request is about to occur. Should return a `blockingResponse`. * `filter` Object * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. + will be filtered out. * `listener` Function * `details` Object * `id` String - Request id. @@ -340,7 +340,7 @@ Fired when a request is about to occur. Should return a `blockingResponse`. * `blockingResponse` Object * `cancel` Boolean - Whether to continue or block the request. * `requestHeaders` Object **optional** - When provided, request will be made with these - headers. + 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. @@ -350,7 +350,7 @@ Should return a `blockingResponse`. * `filter` Object * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. + will be filtered out. * `listener` Function * `details` Object * `id` String - Request id. @@ -381,7 +381,7 @@ Fired just before a request is going to be sent to the server, modifications of * `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. + responded with these headers. Fired when HTTP response headers of a request have been received. Should return a `blockingResponse`. @@ -430,7 +430,7 @@ Fired when a server initiated redirect is about to occur. * `filter` Object * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. + will be filtered out. * `listener` Function * `details` Object * `id` String - Request id. @@ -449,7 +449,7 @@ Fired when a request is completed. * `filter` Object * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. + will be filtered out. * `listener` Function * `details` Object * `id` String - Request id. From 658accab94f3c9682ae381b3b56ab99e9d67660a Mon Sep 17 00:00:00 2001 From: Robo Date: Thu, 10 Dec 2015 00:46:03 +0530 Subject: [PATCH 5/6] fix pointer usage --- atom/browser/api/atom_api_web_request.cc | 8 +- atom/browser/api/atom_api_web_request.h | 3 +- atom/browser/net/atom_network_delegate.cc | 236 ++++++++++-------- atom/browser/net/atom_network_delegate.h | 23 +- .../native_mate_converters/net_converter.cc | 8 +- .../native_mate_converters/value_converter.cc | 7 - .../native_mate_converters/value_converter.h | 6 - chromium_src/extensions/common/url_pattern.cc | 2 +- docs/api/session.md | 8 +- 9 files changed, 150 insertions(+), 151 deletions(-) diff --git a/atom/browser/api/atom_api_web_request.cc b/atom/browser/api/atom_api_web_request.cc index c45cd900ab7a..07c0576977dc 100644 --- a/atom/browser/api/atom_api_web_request.cc +++ b/atom/browser/api/atom_api_web_request.cc @@ -30,8 +30,8 @@ template void WebRequest::SetListener(mate::Arguments* args) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - base::DictionaryValue* filter = new base::DictionaryValue(); - args->GetNext(filter); + 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"); @@ -42,7 +42,7 @@ void WebRequest::SetListener(mate::Arguments* args) { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&AtomNetworkDelegate::SetListenerInIO, base::Unretained(delegate), - type, filter, callback)); + type, base::Passed(&filter), callback)); } // static @@ -54,7 +54,7 @@ mate::Handle WebRequest::Create( // static void WebRequest::BuildPrototype(v8::Isolate* isolate, - v8::Local prototype) { + v8::Local prototype) { mate::ObjectTemplateBuilder(isolate, prototype) .SetMethod("onBeforeRequest", &WebRequest::SetListener< diff --git a/atom/browser/api/atom_api_web_request.h b/atom/browser/api/atom_api_web_request.h index 053bc1bec97d..597bb80f7c65 100644 --- a/atom/browser/api/atom_api_web_request.h +++ b/atom/browser/api/atom_api_web_request.h @@ -9,7 +9,6 @@ #include "atom/browser/api/trackable_object.h" #include "atom/browser/net/atom_network_delegate.h" -#include "base/callback.h" #include "native_mate/arguments.h" #include "native_mate/handle.h" @@ -22,7 +21,7 @@ namespace api { class WebRequest : public mate::TrackableObject { public: static mate::Handle Create(v8::Isolate* isolate, - AtomBrowserContext* browser_context); + AtomBrowserContext* browser_context); // mate::TrackableObject: static void BuildPrototype(v8::Isolate* isolate, diff --git a/atom/browser/net/atom_network_delegate.cc b/atom/browser/net/atom_network_delegate.cc index d4c83996b9cc..c7a54ee66492 100644 --- a/atom/browser/net/atom_network_delegate.cc +++ b/atom/browser/net/atom_network_delegate.cc @@ -18,9 +18,9 @@ namespace { std::string ResourceTypeToString(content::ResourceType type) { switch (type) { case content::RESOURCE_TYPE_MAIN_FRAME: - return "main_frame"; + return "mainFrame"; case content::RESOURCE_TYPE_SUB_FRAME: - return "sub_frame"; + return "subFrame"; case content::RESOURCE_TYPE_STYLESHEET: return "stylesheet"; case content::RESOURCE_TYPE_SCRIPT: @@ -30,12 +30,18 @@ std::string ResourceTypeToString(content::ResourceType type) { case content::RESOURCE_TYPE_OBJECT: return "object"; case content::RESOURCE_TYPE_XHR: - return "xmlhttprequest"; + return "xhr"; default: return "other"; } } +AtomNetworkDelegate::BlockingResponse RunListener( + const AtomNetworkDelegate::Listener& callback, + scoped_ptr details) { + return callback.Run(*(details.get())); +} + bool MatchesFilterCondition( net::URLRequest* request, const AtomNetworkDelegate::ListenerInfo& info) { @@ -50,8 +56,8 @@ bool MatchesFilterCondition( return true; } -base::DictionaryValue* ExtractRequestInfo(net::URLRequest* request) { - base::DictionaryValue* dict = new base::DictionaryValue(); +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()); @@ -62,21 +68,21 @@ base::DictionaryValue* ExtractRequestInfo(net::URLRequest* request) { dict->SetString("resourceType", ResourceTypeToString(resourceType)); dict->SetDouble("timestamp", base::Time::Now().ToDoubleT() * 1000); - return dict; + return dict.Pass(); } -base::DictionaryValue* GetRequestHeadersDict( +scoped_ptr GetRequestHeadersDict( const net::HttpRequestHeaders& headers) { - base::DictionaryValue* header_dict = new base::DictionaryValue(); + scoped_ptr header_dict(new base::DictionaryValue()); net::HttpRequestHeaders::Iterator it(headers); while (it.GetNext()) header_dict->SetString(it.name(), it.value()); - return header_dict; + return header_dict.Pass(); } -base::DictionaryValue* GetResponseHeadersDict( +scoped_ptr GetResponseHeadersDict( const net::HttpResponseHeaders* headers) { - base::DictionaryValue* header_dict = new base::DictionaryValue(); + scoped_ptr header_dict(new base::DictionaryValue()); if (headers) { void* iter = nullptr; std::string key; @@ -84,25 +90,25 @@ base::DictionaryValue* GetResponseHeadersDict( while (headers->EnumerateHeaderLines(&iter, &key, &value)) header_dict->SetString(key, value); } - return header_dict; + return header_dict.Pass(); } void OnBeforeURLRequestResponse( const net::CompletionCallback& callback, GURL* new_url, const AtomNetworkDelegate::BlockingResponse& result) { - if (!result.redirectURL.is_empty()) - *new_url = result.redirectURL; - callback.Run(result.Cancel()); + 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.requestHeaders.IsEmpty()) - *headers = result.requestHeaders; - callback.Run(result.Cancel()); + if (!result.request_headers.IsEmpty()) + *headers = result.request_headers; + callback.Run(result.Code()); } void OnHeadersReceivedResponse( @@ -110,26 +116,22 @@ void OnHeadersReceivedResponse( const net::HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, const AtomNetworkDelegate::BlockingResponse& result) { - if (result.responseHeaders.get()) { + 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.responseHeaders->EnumerateHeaderLines(&iter, &key, &value)) { + while (result.response_headers->EnumerateHeaderLines(&iter, &key, &value)) { (*override_response_headers)->RemoveHeader(key); (*override_response_headers)->AddHeader(key + ": " + value); } } - callback.Run(result.Cancel()); + callback.Run(result.Code()); } } // namespace -// static -std::map -AtomNetworkDelegate::event_listener_map_; - AtomNetworkDelegate::AtomNetworkDelegate() { } @@ -138,8 +140,13 @@ AtomNetworkDelegate::~AtomNetworkDelegate() { void AtomNetworkDelegate::SetListenerInIO( EventTypes type, - const base::DictionaryValue* filter, + scoped_ptr filter, const Listener& callback) { + if (callback.is_null()) { + event_listener_map_.erase(type); + return; + } + ListenerInfo info; info.callback = callback; @@ -162,69 +169,71 @@ int AtomNetworkDelegate::OnBeforeURLRequest( net::URLRequest* request, const net::CompletionCallback& callback, GURL* new_url) { - brightray::NetworkDelegate::OnBeforeURLRequest(request, callback, 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; - auto listener_info = event_listener_map_[kOnBeforeRequest]; - - if (!MatchesFilterCondition(request, listener_info)) - return net::OK; - - if (!listener_info.callback.is_null()) { - auto wrapped_callback = listener_info.callback; + auto wrapped_callback = listener_info->second.callback; auto details = ExtractRequestInfo(request); BrowserThread::PostTaskAndReplyWithResult(BrowserThread::UI, FROM_HERE, - base::Bind(wrapped_callback, details), + base::Bind(&RunListener, wrapped_callback, base::Passed(&details)), base::Bind(&OnBeforeURLRequestResponse, callback, new_url)); return net::ERR_IO_PENDING; } - return net::OK; + return brightray::NetworkDelegate::OnBeforeURLRequest(request, + callback, + new_url); } int AtomNetworkDelegate::OnBeforeSendHeaders( net::URLRequest* request, const net::CompletionCallback& callback, net::HttpRequestHeaders* headers) { - auto listener_info = event_listener_map_[kOnBeforeSendHeaders]; + 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 (!MatchesFilterCondition(request, listener_info)) - return net::OK; - - if (!listener_info.callback.is_null()) { - auto wrapped_callback = listener_info.callback; + auto wrapped_callback = listener_info->second.callback; auto details = ExtractRequestInfo(request); - details->Set("requestHeaders", GetRequestHeadersDict(*headers)); + details->Set("requestHeaders", GetRequestHeadersDict(*headers).release()); BrowserThread::PostTaskAndReplyWithResult(BrowserThread::UI, FROM_HERE, - base::Bind(wrapped_callback, details), + base::Bind(&RunListener, wrapped_callback, base::Passed(&details)), base::Bind(&OnBeforeSendHeadersResponse, callback, headers)); return net::ERR_IO_PENDING; } - return net::OK; + return brightray::NetworkDelegate::OnBeforeSendHeaders(request, + callback, + headers); } void AtomNetworkDelegate::OnSendHeaders( net::URLRequest* request, const net::HttpRequestHeaders& headers) { - auto listener_info = event_listener_map_[kOnSendHeaders]; + auto listener_info = event_listener_map_.find(kOnSendHeaders); + if (listener_info != event_listener_map_.end()) { + if (!MatchesFilterCondition(request, listener_info->second)) + return; - if (!MatchesFilterCondition(request, listener_info)) - return; - - if (!listener_info.callback.is_null()) { - auto wrapped_callback = listener_info.callback; + auto wrapped_callback = listener_info->second.callback; auto details = ExtractRequestInfo(request); - details->Set("requestHeaders", GetRequestHeadersDict(headers)); + details->Set("requestHeaders", GetRequestHeadersDict(headers).release()); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(base::IgnoreResult(wrapped_callback), - details)); + base::Bind(base::IgnoreResult(&RunListener), + wrapped_callback, + base::Passed(&details))); + } else { + brightray::NetworkDelegate::OnSendHeaders(request, headers); } } @@ -234,23 +243,22 @@ int AtomNetworkDelegate::OnHeadersReceived( const net::HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, GURL* allowed_unsafe_redirect_url) { - auto listener_info = event_listener_map_[kOnHeadersReceived]; + auto listener_info = event_listener_map_.find(kOnHeadersReceived); + if (listener_info != event_listener_map_.end()) { + if (!MatchesFilterCondition(request, listener_info->second)) + return net::OK; - if (!MatchesFilterCondition(request, listener_info)) - return net::OK; - - if (!listener_info.callback.is_null()) { - auto wrapped_callback = listener_info.callback; + 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)); + GetResponseHeadersDict(original_response_headers).release()); BrowserThread::PostTaskAndReplyWithResult(BrowserThread::UI, FROM_HERE, - base::Bind(wrapped_callback, details), + base::Bind(&RunListener, wrapped_callback, base::Passed(&details)), base::Bind(&OnHeadersReceivedResponse, callback, original_response_headers, @@ -259,18 +267,19 @@ int AtomNetworkDelegate::OnHeadersReceived( return net::ERR_IO_PENDING; } - return net::OK; + return brightray::NetworkDelegate::OnHeadersReceived( + request, callback, original_response_headers, override_response_headers, + allowed_unsafe_redirect_url); } void AtomNetworkDelegate::OnBeforeRedirect(net::URLRequest* request, const GURL& new_location) { - auto listener_info = event_listener_map_[kOnBeforeRedirect]; + auto listener_info = event_listener_map_.find(kOnBeforeRedirect); + if (listener_info != event_listener_map_.end()) { + if (!MatchesFilterCondition(request, listener_info->second)) + return; - if (!MatchesFilterCondition(request, listener_info)) - return; - - if (!listener_info.callback.is_null()) { - auto wrapped_callback = listener_info.callback; + auto wrapped_callback = listener_info->second.callback; auto details = ExtractRequestInfo(request); details->SetString("redirectURL", new_location.spec()); details->SetInteger("statusCode", request->GetResponseCode()); @@ -279,28 +288,30 @@ void AtomNetworkDelegate::OnBeforeRedirect(net::URLRequest* request, details->SetString("ip", ip); details->SetBoolean("fromCache", request->was_cached()); details->Set("responseHeaders", - GetResponseHeadersDict(request->response_headers())); + GetResponseHeadersDict(request->response_headers()).release()); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(base::IgnoreResult(wrapped_callback), - details)); + base::Bind(base::IgnoreResult(&RunListener), + wrapped_callback, + base::Passed(&details))); + } else { + brightray::NetworkDelegate::OnBeforeRedirect(request, new_location); } } void AtomNetworkDelegate::OnResponseStarted(net::URLRequest* request) { - if (request->status().status() != net::URLRequestStatus::SUCCESS) - return; + auto listener_info = event_listener_map_.find(kOnResponseStarted); + if (listener_info != event_listener_map_.end()) { + if (request->status().status() != net::URLRequestStatus::SUCCESS) + return; - auto listener_info = event_listener_map_[kOnResponseStarted]; + if (!MatchesFilterCondition(request, listener_info->second)) + return; - if (!MatchesFilterCondition(request, listener_info)) - return; - - if (!listener_info.callback.is_null()) { - auto wrapped_callback = listener_info.callback; + auto wrapped_callback = listener_info->second.callback; auto details = ExtractRequestInfo(request); details->Set("responseHeaders", - GetResponseHeadersDict(request->response_headers())); + GetResponseHeadersDict(request->response_headers()).release()); details->SetBoolean("fromCache", request->was_cached()); auto response_headers = request->response_headers(); @@ -312,34 +323,36 @@ void AtomNetworkDelegate::OnResponseStarted(net::URLRequest* request) { response_headers->GetStatusLine() : std::string()); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(base::IgnoreResult(wrapped_callback), - details)); + base::Bind(base::IgnoreResult(&RunListener), + wrapped_callback, + base::Passed(&details))); + } else { + brightray::NetworkDelegate::OnResponseStarted(request); } } void AtomNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { - if (request->status().status() == net::URLRequestStatus::FAILED || - request->status().status() == net::URLRequestStatus::CANCELED) { - OnErrorOccurred(request); - return; - } else { - bool is_redirect = request->response_headers() && - net::HttpResponseHeaders::IsRedirectResponseCode( - request->response_headers()->response_code()); - if (is_redirect) + 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) { + OnErrorOccurred(request); return; - } + } else { + bool is_redirect = request->response_headers() && + net::HttpResponseHeaders::IsRedirectResponseCode( + request->response_headers()->response_code()); + if (is_redirect) + return; + } - auto listener_info = event_listener_map_[kOnCompleted]; + if (!MatchesFilterCondition(request, listener_info->second)) + return; - if (!MatchesFilterCondition(request, listener_info)) - return; - - if (!listener_info.callback.is_null()) { - auto wrapped_callback = listener_info.callback; + auto wrapped_callback = listener_info->second.callback; auto details = ExtractRequestInfo(request); details->Set("responseHeaders", - GetResponseHeadersDict(request->response_headers())); + GetResponseHeadersDict(request->response_headers()).release()); details->SetBoolean("fromCache", request->was_cached()); auto response_headers = request->response_headers(); @@ -351,26 +364,29 @@ void AtomNetworkDelegate::OnCompleted(net::URLRequest* request, bool started) { response_headers->GetStatusLine() : std::string()); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(base::IgnoreResult(wrapped_callback), - details)); + base::Bind(base::IgnoreResult(&RunListener), + wrapped_callback, + base::Passed(&details))); + } else { + brightray::NetworkDelegate::OnCompleted(request, started); } } void AtomNetworkDelegate::OnErrorOccurred(net::URLRequest* request) { - auto listener_info = event_listener_map_[kOnErrorOccurred]; + auto listener_info = event_listener_map_.find(kOnErrorOccurred); + if (listener_info != event_listener_map_.end()) { + if (!MatchesFilterCondition(request, listener_info->second)) + return; - if (!MatchesFilterCondition(request, listener_info)) - return; - - if (!listener_info.callback.is_null()) { - auto wrapped_callback = listener_info.callback; + 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())); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(base::IgnoreResult(wrapped_callback), - details)); + base::Bind(base::IgnoreResult(&RunListener), + wrapped_callback, + base::Passed(&details))); } } diff --git a/atom/browser/net/atom_network_delegate.h b/atom/browser/net/atom_network_delegate.h index c48d252b2b0a..9f78cc8d92da 100644 --- a/atom/browser/net/atom_network_delegate.h +++ b/atom/browser/net/atom_network_delegate.h @@ -27,7 +27,7 @@ class AtomNetworkDelegate : public brightray::NetworkDelegate { public: struct BlockingResponse; using Listener = - base::Callback; + base::Callback; enum EventTypes { kInvalidEvent = 0, @@ -42,35 +42,34 @@ class AtomNetworkDelegate : public brightray::NetworkDelegate { }; struct ListenerInfo { - ListenerInfo() {} - ~ListenerInfo() {} - std::set url_patterns; AtomNetworkDelegate::Listener callback; }; struct BlockingResponse { - BlockingResponse() {} + BlockingResponse() : cancel(false) {} ~BlockingResponse() {} - int Cancel() const { + int Code() const { return cancel ? net::ERR_BLOCKED_BY_CLIENT : net::OK; } bool cancel; - GURL redirectURL; - net::HttpRequestHeaders requestHeaders; - scoped_refptr responseHeaders; + GURL redirect_url; + net::HttpRequestHeaders request_headers; + scoped_refptr response_headers; }; AtomNetworkDelegate(); ~AtomNetworkDelegate() override; void SetListenerInIO(EventTypes type, - const base::DictionaryValue* filter, + scoped_ptr filter, const Listener& callback); protected: + void OnErrorOccurred(net::URLRequest* request); + // net::NetworkDelegate: int OnBeforeURLRequest(net::URLRequest* request, const net::CompletionCallback& callback, @@ -91,10 +90,8 @@ 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: - static std::map event_listener_map_; + std::map event_listener_map_; 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 2086e84d5891..876b22536ac4 100644 --- a/atom/common/native_mate_converters/net_converter.cc +++ b/atom/common/native_mate_converters/net_converter.cc @@ -62,7 +62,7 @@ bool Converter::FromV8( return false; if (!dict.Get("cancel", &(out->cancel))) return false; - dict.Get("redirectURL", &(out->redirectURL)); + dict.Get("redirectURL", &(out->redirect_url)); base::DictionaryValue request_headers; if (dict.Get("requestHeaders", &request_headers)) { for (base::DictionaryValue::Iterator it(request_headers); @@ -70,18 +70,18 @@ bool Converter::FromV8( it.Advance()) { std::string value; CHECK(it.value().GetAsString(&value)); - out->requestHeaders.SetHeader(it.key(), value); + out->request_headers.SetHeader(it.key(), value); } } base::DictionaryValue response_headers; if (dict.Get("responseHeaders", &response_headers)) { - out->responseHeaders = new net::HttpResponseHeaders(""); + 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->responseHeaders->AddHeader(it.key() + " : " + value); + out->response_headers->AddHeader(it.key() + " : " + value); } } return true; diff --git a/atom/common/native_mate_converters/value_converter.cc b/atom/common/native_mate_converters/value_converter.cc index 431f11fbbdb3..c9c1a861ba26 100644 --- a/atom/common/native_mate_converters/value_converter.cc +++ b/atom/common/native_mate_converters/value_converter.cc @@ -30,13 +30,6 @@ v8::Local Converter::ToV8( return converter->ToV8Value(&val, isolate->GetCurrentContext()); } -v8::Local Converter::ToV8( - v8::Isolate* isolate, - const base::DictionaryValue* val) { - scoped_ptr converter(new atom::V8ValueConverter); - return converter->ToV8Value(val, isolate->GetCurrentContext()); -} - bool Converter::FromV8(v8::Isolate* isolate, v8::Local val, base::ListValue* out) { diff --git a/atom/common/native_mate_converters/value_converter.h b/atom/common/native_mate_converters/value_converter.h index f660fd3f1897..013dd99cc798 100644 --- a/atom/common/native_mate_converters/value_converter.h +++ b/atom/common/native_mate_converters/value_converter.h @@ -23,12 +23,6 @@ struct Converter { const base::DictionaryValue& val); }; -template<> -struct Converter { - static v8::Local ToV8(v8::Isolate* isolate, - const base::DictionaryValue* val); -}; - template<> struct Converter { static bool FromV8(v8::Isolate* isolate, diff --git a/chromium_src/extensions/common/url_pattern.cc b/chromium_src/extensions/common/url_pattern.cc index a6e51aa675b7..4303689fe81c 100644 --- a/chromium_src/extensions/common/url_pattern.cc +++ b/chromium_src/extensions/common/url_pattern.cc @@ -250,7 +250,7 @@ URLPattern::ParseResult URLPattern::Parse(const std::string& pattern) { host_components.erase(host_components.begin(), host_components.begin() + 1); } - host_ = JoinString(host_components, "."); + host_ = base::JoinString(host_components, "."); path_start_pos = host_end_pos; } diff --git a/docs/api/session.md b/docs/api/session.md index b16a9b1f0f83..9429bc3c2df7 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -367,7 +367,7 @@ Fired just before a request is going to be sent to the server, modifications of * `filter` Object * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. + will be filtered out. * `listener` Function * `details` Object * `id` String - Request id. @@ -390,7 +390,7 @@ Fired when HTTP response headers of a request have been received. Should return * `filter` Object * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. + will be filtered out. * `listener` Function * `details` Object * `id` String - Request id. @@ -410,7 +410,7 @@ status line and response headers are available. * `filter` Object * `urls` Array - A list of URLs or URL patterns. Request that cannot match any of the URLs - will be filtered out. + will be filtered out. * `listener` Function * `details` Object * `id` String - Request id. @@ -420,7 +420,7 @@ status line and response headers are available. * `timestamp` Double * `redirectURL` String * `statusCode` Integer - * `ip` String - The server IP address that the request was actually sent to. + * `ip` String **optional** - The server IP address that the request was actually sent to. * `fromCache` Boolean * `responseHeaders` Object From 053c77d6f421051a2fbb3e8749a088dc5167b5c0 Mon Sep 17 00:00:00 2001 From: Robo Date: Fri, 11 Dec 2015 00:27:15 +0530 Subject: [PATCH 6/6] default session should be persistent --- atom/browser/api/lib/session.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atom/browser/api/lib/session.coffee b/atom/browser/api/lib/session.coffee index 6abfe7925e69..7fc3c2df6f96 100644 --- a/atom/browser/api/lib/session.coffee +++ b/atom/browser/api/lib/session.coffee @@ -14,7 +14,7 @@ exports.fromPartition = (partition='') -> # Returns the default session. Object.defineProperty exports, 'defaultSession', enumerable: true - get: -> exports.fromPartition '' + get: -> exports.fromPartition 'persist:' wrapSession = (session) -> # session is an EventEmitter.