Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrate webRequest module to NetworkService (Part 5) #19714

Merged
merged 9 commits into from Aug 13, 2019
4 changes: 3 additions & 1 deletion shell/browser/api/atom_api_session.cc
Expand Up @@ -206,8 +206,10 @@ Session::Session(v8::Isolate* isolate, AtomBrowserContext* browser_context)
Session::~Session() {
content::BrowserContext::GetDownloadManager(browser_context())
->RemoveObserver(this);
// TODO(zcbenz): Now since URLRequestContextGetter is gone, is this still
// needed?
// Refs https://github.com/electron/electron/pull/12305.
DestroyGlobalHandle(isolate(), cookies_);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found this was needed to fix (D)CHECK in destruction order of IO thread when handles were held by UI thread from our modules. But now that they are handled by the network service, this ordering should be pretty much unnecessary. Would be good to check.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api::Cookies and others are keeping strong references to BrowserContext and thus the headache of destructions, I think we can make them use weak reference (plain pointer) and things should be much simpler.

DestroyGlobalHandle(isolate(), web_request_);
DestroyGlobalHandle(isolate(), protocol_);
DestroyGlobalHandle(isolate(), net_log_);
g_sessions.erase(weak_map_id());
Expand Down
6 changes: 4 additions & 2 deletions shell/browser/api/atom_api_session.h
Expand Up @@ -100,12 +100,14 @@ class Session : public mate::TrackableObject<Session>,
download::DownloadItem* item) override;

private:
// Cached object.
// Cached mate::Wrappable objects.
v8::Global<v8::Value> cookies_;
v8::Global<v8::Value> protocol_;
v8::Global<v8::Value> web_request_;
v8::Global<v8::Value> net_log_;

// Cached object.
v8::Global<v8::Value> web_request_;

// The client id to enable the network throttler.
base::UnguessableToken network_emulation_token_;

Expand Down
178 changes: 167 additions & 11 deletions shell/browser/api/atom_api_web_request_ns.cc
Expand Up @@ -4,13 +4,14 @@

#include "shell/browser/api/atom_api_web_request_ns.h"

#include <set>
#include <memory>
#include <string>
#include <utility>

#include "extensions/common/url_pattern.h"
#include "gin/converter.h"
#include "gin/dictionary.h"
#include "gin/object_template_builder.h"
#include "shell/browser/api/atom_api_session.h"
#include "shell/browser/atom_browser_context.h"
#include "shell/common/gin_converters/callback_converter_gin_adapter.h"
#include "shell/common/gin_converters/std_converter.h"
Expand All @@ -37,12 +38,56 @@ namespace electron {

namespace api {

namespace {

const char* kUserDataKey = "WebRequestNS";

// BrowserContext <=> WebRequestNS relationship.
struct UserData : public base::SupportsUserData::Data {
explicit UserData(WebRequestNS* data) : data(data) {}
WebRequestNS* data;
};

// Test whether the URL of |request| matches |patterns|.
bool MatchesFilterCondition(extensions::WebRequestInfo* request,
const std::set<URLPattern>& patterns) {
if (patterns.empty())
return true;

for (const auto& pattern : patterns) {
if (pattern.MatchesURL(request->url))
return true;
}
return false;
}

} // namespace

gin::WrapperInfo WebRequestNS::kWrapperInfo = {gin::kEmbedderNativeGin};

WebRequestNS::SimpleListenerInfo::SimpleListenerInfo(
std::set<URLPattern> patterns_,
SimpleListener listener_)
: url_patterns(std::move(patterns_)), listener(listener_) {}
WebRequestNS::SimpleListenerInfo::SimpleListenerInfo() = default;
WebRequestNS::SimpleListenerInfo::~SimpleListenerInfo() = default;

WebRequestNS::ResponseListenerInfo::ResponseListenerInfo(
std::set<URLPattern> patterns_,
ResponseListener listener_)
: url_patterns(std::move(patterns_)), listener(listener_) {}
WebRequestNS::ResponseListenerInfo::ResponseListenerInfo() = default;
WebRequestNS::ResponseListenerInfo::~ResponseListenerInfo() = default;

WebRequestNS::WebRequestNS(v8::Isolate* isolate,
AtomBrowserContext* browser_context) {}
content::BrowserContext* browser_context)
: browser_context_(browser_context) {
browser_context_->SetUserData(kUserDataKey, std::make_unique<UserData>(this));
}

WebRequestNS::~WebRequestNS() = default;
WebRequestNS::~WebRequestNS() {
browser_context_->RemoveUserData(kUserDataKey);
}

gin::ObjectTemplateBuilder WebRequestNS::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
Expand All @@ -68,18 +113,70 @@ const char* WebRequestNS::GetTypeName() {
return "WebRequest";
}

int WebRequestNS::OnBeforeRequest(extensions::WebRequestInfo* request,
net::CompletionOnceCallback callback,
GURL* new_url) {
return HandleResponseEvent(kOnBeforeRequest, request, std::move(callback),
new_url);
}

int WebRequestNS::OnBeforeSendHeaders(extensions::WebRequestInfo* request,
BeforeSendHeadersCallback callback,
net::HttpRequestHeaders* headers) {
// TODO(zcbenz): Figure out how to handle this generally.
return net::OK;
}

int WebRequestNS::OnHeadersReceived(
extensions::WebRequestInfo* request,
net::CompletionOnceCallback callback,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
GURL* allowed_unsafe_redirect_url) {
return HandleResponseEvent(kOnHeadersReceived, request, std::move(callback),
original_response_headers,
override_response_headers,
allowed_unsafe_redirect_url);
}

void WebRequestNS::OnSendHeaders(extensions::WebRequestInfo* request,
const net::HttpRequestHeaders& headers) {
HandleSimpleEvent(kOnSendHeaders, request, headers);
}

void WebRequestNS::OnBeforeRedirect(extensions::WebRequestInfo* request,
const GURL& new_location) {
HandleSimpleEvent(kOnBeforeRedirect, request, new_location);
}

void WebRequestNS::OnResponseStarted(extensions::WebRequestInfo* request) {
HandleSimpleEvent(kOnResponseStarted, request);
}

void WebRequestNS::OnErrorOccurred(extensions::WebRequestInfo* request,
int net_error) {
HandleSimpleEvent(kOnErrorOccurred, request, net_error);
}

void WebRequestNS::OnCompleted(extensions::WebRequestInfo* request,
int net_error) {
HandleSimpleEvent(kOnCompleted, request, net_error);
}

template <WebRequestNS::SimpleEvent event>
void WebRequestNS::SetSimpleListener(gin::Arguments* args) {
SetListener<SimpleListener, SimpleEvent>(event, args);
SetListener<SimpleListener>(event, &simple_listeners_, args);
}

template <WebRequestNS::ResponseEvent event>
void WebRequestNS::SetResponseListener(gin::Arguments* args) {
SetListener<ResponseListener, ResponseEvent>(event, args);
SetListener<ResponseListener>(event, &response_listeners_, args);
}

template <typename Listener, typename Event>
void WebRequestNS::SetListener(Event event, gin::Arguments* args) {
template <typename Listener, typename Listeners, typename Event>
void WebRequestNS::SetListener(Event event,
Listeners* listeners,
gin::Arguments* args) {
// { urls }.
std::set<URLPattern> patterns;
gin::Dictionary dict(args->isolate());
Expand All @@ -94,17 +191,76 @@ void WebRequestNS::SetListener(Event event, gin::Arguments* args) {
return;
}

// TODO(zcbenz): Actually set the listeners.
args->ThrowTypeError("This API is not implemented yet");
if (listener.is_null())
listeners->erase(event);
else
(*listeners)[event] = {std::move(patterns), std::move(listener)};
}

template <typename... Args>
void WebRequestNS::HandleSimpleEvent(SimpleEvent event,
extensions::WebRequestInfo* request,
Args... args) {
const auto& info = simple_listeners_[event];
if (!MatchesFilterCondition(request, info.url_patterns))
return;

// TODO(zcbenz): Invoke the listener.
}

template <typename Out, typename... Args>
int WebRequestNS::HandleResponseEvent(ResponseEvent event,
extensions::WebRequestInfo* request,
net::CompletionOnceCallback callback,
Out out,
Args... args) {
const auto& info = response_listeners_[event];
if (!MatchesFilterCondition(request, info.url_patterns))
return net::OK;

// TODO(zcbenz): Invoke the listener.
return net::OK;
}

// static
gin::Handle<WebRequestNS> WebRequestNS::FromOrCreate(
v8::Isolate* isolate,
content::BrowserContext* browser_context) {
gin::Handle<WebRequestNS> handle = From(isolate, browser_context);
if (handle.IsEmpty()) {
// Make sure the |Session| object has the |webRequest| property created.
v8::Local<v8::Value> web_request =
Session::CreateFrom(isolate,
static_cast<AtomBrowserContext*>(browser_context))
->WebRequest(isolate);
gin::ConvertFromV8(isolate, web_request, &handle);
}
DCHECK(!handle.IsEmpty());
return handle;
}

// static
gin::Handle<WebRequestNS> WebRequestNS::Create(
v8::Isolate* isolate,
AtomBrowserContext* browser_context) {
content::BrowserContext* browser_context) {
DCHECK(From(isolate, browser_context).IsEmpty())
<< "WebRequestNS already created";
return gin::CreateHandle(isolate, new WebRequestNS(isolate, browser_context));
}

// static
gin::Handle<WebRequestNS> WebRequestNS::From(
v8::Isolate* isolate,
content::BrowserContext* browser_context) {
if (!browser_context)
return gin::Handle<WebRequestNS>();
auto* user_data =
static_cast<UserData*>(browser_context->GetUserData(kUserDataKey));
if (!user_data)
return gin::Handle<WebRequestNS>();
return gin::CreateHandle(isolate, user_data->data);
}

} // namespace api

} // namespace electron
96 changes: 88 additions & 8 deletions shell/browser/api/atom_api_web_request_ns.h
Expand Up @@ -5,35 +5,79 @@
#ifndef SHELL_BROWSER_API_ATOM_API_WEB_REQUEST_NS_H_
#define SHELL_BROWSER_API_ATOM_API_WEB_REQUEST_NS_H_

#include <map>
#include <set>

#include "base/values.h"
#include "extensions/common/url_pattern.h"
#include "gin/arguments.h"
#include "gin/handle.h"
#include "gin/wrappable.h"
#include "native_mate/dictionary.h"
#include "native_mate/handle.h"
#include "shell/browser/net/proxying_url_loader_factory.h"

namespace electron {
namespace content {
class BrowserContext;
}

class AtomBrowserContext;
namespace electron {

namespace api {

class WebRequestNS : public gin::Wrappable<WebRequestNS> {
class WebRequestNS : public gin::Wrappable<WebRequestNS>, public WebRequestAPI {
public:
static gin::WrapperInfo kWrapperInfo;

static gin::Handle<WebRequestNS> Create(v8::Isolate* isolate,
AtomBrowserContext* browser_context);
// Return the WebRequest object attached to |browser_context|, create if there
// is no one.
// Note that the lifetime of WebRequest object is managed by Session, instead
// of the caller.
static gin::Handle<WebRequestNS> FromOrCreate(
v8::Isolate* isolate,
content::BrowserContext* browser_context);

// Return a new WebRequest object, this should only be called by Session.
static gin::Handle<WebRequestNS> Create(
v8::Isolate* isolate,
content::BrowserContext* browser_context);

// Find the WebRequest object attached to |browser_context|.
static gin::Handle<WebRequestNS> From(
v8::Isolate* isolate,
content::BrowserContext* browser_context);

// gin::Wrappable:
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override;
const char* GetTypeName() override;

private:
WebRequestNS(v8::Isolate* isolate, AtomBrowserContext* browser_context);
WebRequestNS(v8::Isolate* isolate, content::BrowserContext* browser_context);
~WebRequestNS() override;

// WebRequestAPI:
int OnBeforeRequest(extensions::WebRequestInfo* request,
net::CompletionOnceCallback callback,
GURL* new_url) override;
int OnBeforeSendHeaders(extensions::WebRequestInfo* request,
BeforeSendHeadersCallback callback,
net::HttpRequestHeaders* headers) override;
int OnHeadersReceived(
extensions::WebRequestInfo* request,
net::CompletionOnceCallback callback,
const net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
GURL* allowed_unsafe_redirect_url) override;
void OnSendHeaders(extensions::WebRequestInfo* request,
const net::HttpRequestHeaders& headers) override;
void OnBeforeRedirect(extensions::WebRequestInfo* request,
const GURL& new_location) override;
void OnResponseStarted(extensions::WebRequestInfo* request) override;
void OnErrorOccurred(extensions::WebRequestInfo* request,
int net_error) override;
void OnCompleted(extensions::WebRequestInfo* request, int net_error) override;

enum SimpleEvent {
kOnSendHeaders,
kOnBeforeRedirect,
Expand All @@ -56,8 +100,44 @@ class WebRequestNS : public gin::Wrappable<WebRequestNS> {
void SetSimpleListener(gin::Arguments* args);
template <ResponseEvent event>
void SetResponseListener(gin::Arguments* args);
template <typename Listener, typename Event>
void SetListener(Event event, gin::Arguments* args);
template <typename Listener, typename Listeners, typename Event>
void SetListener(Event event, Listeners* listeners, gin::Arguments* args);

template <typename... Args>
void HandleSimpleEvent(SimpleEvent event,
extensions::WebRequestInfo* request,
Args... args);
template <typename Out, typename... Args>
int HandleResponseEvent(ResponseEvent event,
extensions::WebRequestInfo* request,
net::CompletionOnceCallback callback,
Out out,
Args... args);

struct SimpleListenerInfo {
std::set<URLPattern> url_patterns;
SimpleListener listener;

SimpleListenerInfo(std::set<URLPattern>, SimpleListener);
SimpleListenerInfo();
~SimpleListenerInfo();
};

struct ResponseListenerInfo {
std::set<URLPattern> url_patterns;
ResponseListener listener;

ResponseListenerInfo(std::set<URLPattern>, ResponseListener);
ResponseListenerInfo();
~ResponseListenerInfo();
};

std::map<SimpleEvent, SimpleListenerInfo> simple_listeners_;
std::map<ResponseEvent, ResponseListenerInfo> response_listeners_;
std::map<uint64_t, net::CompletionOnceCallback> callbacks_;

// Weak-ref, it manages us.
content::BrowserContext* browser_context_;
};

} // namespace api
Expand Down