electron_api_web_request.cc 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. // Copyright (c) 2019 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/browser/api/electron_api_web_request.h"
  5. #include <memory>
  6. #include <string>
  7. #include <string_view>
  8. #include <utility>
  9. #include "base/containers/contains.h"
  10. #include "base/containers/fixed_flat_map.h"
  11. #include "base/memory/raw_ptr.h"
  12. #include "base/task/sequenced_task_runner.h"
  13. #include "base/values.h"
  14. #include "extensions/browser/api/web_request/web_request_info.h"
  15. #include "extensions/browser/api/web_request/web_request_resource_type.h"
  16. #include "extensions/common/url_pattern.h"
  17. #include "gin/converter.h"
  18. #include "gin/dictionary.h"
  19. #include "gin/handle.h"
  20. #include "gin/object_template_builder.h"
  21. #include "shell/browser/api/electron_api_session.h"
  22. #include "shell/browser/api/electron_api_web_contents.h"
  23. #include "shell/browser/api/electron_api_web_frame_main.h"
  24. #include "shell/browser/electron_browser_context.h"
  25. #include "shell/browser/javascript_environment.h"
  26. #include "shell/common/gin_converters/callback_converter.h"
  27. #include "shell/common/gin_converters/frame_converter.h"
  28. #include "shell/common/gin_converters/gurl_converter.h"
  29. #include "shell/common/gin_converters/net_converter.h"
  30. #include "shell/common/gin_converters/std_converter.h"
  31. #include "shell/common/gin_converters/value_converter.h"
  32. #include "shell/common/gin_helper/dictionary.h"
  33. #include "shell/common/node_util.h"
  34. static constexpr auto ResourceTypes =
  35. base::MakeFixedFlatMap<std::string_view,
  36. extensions::WebRequestResourceType>({
  37. {"cspReport", extensions::WebRequestResourceType::CSP_REPORT},
  38. {"font", extensions::WebRequestResourceType::FONT},
  39. {"image", extensions::WebRequestResourceType::IMAGE},
  40. {"mainFrame", extensions::WebRequestResourceType::MAIN_FRAME},
  41. {"media", extensions::WebRequestResourceType::MEDIA},
  42. {"object", extensions::WebRequestResourceType::OBJECT},
  43. {"ping", extensions::WebRequestResourceType::PING},
  44. {"script", extensions::WebRequestResourceType::SCRIPT},
  45. {"stylesheet", extensions::WebRequestResourceType::STYLESHEET},
  46. {"subFrame", extensions::WebRequestResourceType::SUB_FRAME},
  47. {"webSocket", extensions::WebRequestResourceType::WEB_SOCKET},
  48. {"xhr", extensions::WebRequestResourceType::XHR},
  49. });
  50. namespace gin {
  51. template <>
  52. struct Converter<extensions::WebRequestResourceType> {
  53. static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
  54. extensions::WebRequestResourceType type) {
  55. for (const auto& [name, val] : ResourceTypes)
  56. if (type == val)
  57. return StringToV8(isolate, name);
  58. return StringToV8(isolate, "other");
  59. }
  60. };
  61. } // namespace gin
  62. namespace electron::api {
  63. namespace {
  64. const char kUserDataKey[] = "WebRequest";
  65. // BrowserContext <=> WebRequest relationship.
  66. struct UserData : public base::SupportsUserData::Data {
  67. explicit UserData(WebRequest* data) : data(data) {}
  68. raw_ptr<WebRequest> data;
  69. };
  70. extensions::WebRequestResourceType ParseResourceType(std::string_view value) {
  71. if (const auto* iter = ResourceTypes.find(value); iter != ResourceTypes.end())
  72. return iter->second;
  73. return extensions::WebRequestResourceType::OTHER;
  74. }
  75. // Convert HttpResponseHeaders to V8.
  76. //
  77. // Note that while we already have converters for HttpResponseHeaders, we can
  78. // not use it because it lowercases the header keys, while the webRequest has
  79. // to pass the original keys.
  80. v8::Local<v8::Value> HttpResponseHeadersToV8(
  81. net::HttpResponseHeaders* headers) {
  82. base::Value::Dict response_headers;
  83. if (headers) {
  84. size_t iter = 0;
  85. std::string key;
  86. std::string value;
  87. while (headers->EnumerateHeaderLines(&iter, &key, &value)) {
  88. response_headers.EnsureList(key)->Append(value);
  89. }
  90. }
  91. return gin::ConvertToV8(v8::Isolate::GetCurrent(), response_headers);
  92. }
  93. // Overloaded by multiple types to fill the |details| object.
  94. void ToDictionary(gin_helper::Dictionary* details,
  95. extensions::WebRequestInfo* info) {
  96. details->Set("id", info->id);
  97. details->Set("url", info->url);
  98. details->Set("method", info->method);
  99. details->Set("timestamp",
  100. base::Time::Now().InSecondsFSinceUnixEpoch() * 1000);
  101. details->Set("resourceType", info->web_request_type);
  102. if (!info->response_ip.empty())
  103. details->Set("ip", info->response_ip);
  104. if (info->response_headers) {
  105. details->Set("fromCache", info->response_from_cache);
  106. details->Set("statusLine", info->response_headers->GetStatusLine());
  107. details->Set("statusCode", info->response_headers->response_code());
  108. details->Set("responseHeaders",
  109. HttpResponseHeadersToV8(info->response_headers.get()));
  110. }
  111. auto* render_frame_host = content::RenderFrameHost::FromID(
  112. info->render_process_id, info->frame_routing_id);
  113. if (render_frame_host) {
  114. details->SetGetter("frame", render_frame_host);
  115. auto* web_contents =
  116. content::WebContents::FromRenderFrameHost(render_frame_host);
  117. auto* api_web_contents = WebContents::From(web_contents);
  118. if (api_web_contents) {
  119. details->Set("webContents", api_web_contents);
  120. details->Set("webContentsId", api_web_contents->ID());
  121. }
  122. }
  123. }
  124. void ToDictionary(gin_helper::Dictionary* details,
  125. const network::ResourceRequest& request) {
  126. details->Set("referrer", request.referrer);
  127. if (request.request_body)
  128. details->Set("uploadData", *request.request_body);
  129. }
  130. void ToDictionary(gin_helper::Dictionary* details,
  131. const net::HttpRequestHeaders& headers) {
  132. details->Set("requestHeaders", headers);
  133. }
  134. void ToDictionary(gin_helper::Dictionary* details, const GURL& location) {
  135. details->Set("redirectURL", location);
  136. }
  137. void ToDictionary(gin_helper::Dictionary* details, int net_error) {
  138. details->Set("error", net::ErrorToString(net_error));
  139. }
  140. // Helper function to fill |details| with arbitrary |args|.
  141. template <typename Arg>
  142. void FillDetails(gin_helper::Dictionary* details, Arg arg) {
  143. ToDictionary(details, arg);
  144. }
  145. template <typename Arg, typename... Args>
  146. void FillDetails(gin_helper::Dictionary* details, Arg arg, Args... args) {
  147. ToDictionary(details, arg);
  148. FillDetails(details, args...);
  149. }
  150. // Modified from extensions/browser/api/web_request/web_request_api_helpers.cc.
  151. std::pair<std::set<std::string>, std::set<std::string>>
  152. CalculateOnBeforeSendHeadersDelta(const net::HttpRequestHeaders* old_headers,
  153. const net::HttpRequestHeaders* new_headers) {
  154. // Newly introduced or overridden request headers.
  155. std::set<std::string> modified_request_headers;
  156. // Keys of request headers to be deleted.
  157. std::set<std::string> deleted_request_headers;
  158. // The event listener might not have passed any new headers if it
  159. // just wanted to cancel the request.
  160. if (new_headers) {
  161. // Find deleted headers.
  162. {
  163. net::HttpRequestHeaders::Iterator i(*old_headers);
  164. while (i.GetNext()) {
  165. if (!new_headers->HasHeader(i.name())) {
  166. deleted_request_headers.insert(i.name());
  167. }
  168. }
  169. }
  170. // Find modified headers.
  171. {
  172. net::HttpRequestHeaders::Iterator i(*new_headers);
  173. while (i.GetNext()) {
  174. if (i.value() != old_headers->GetHeader(i.name())) {
  175. modified_request_headers.insert(i.name());
  176. }
  177. }
  178. }
  179. }
  180. return std::make_pair(modified_request_headers, deleted_request_headers);
  181. }
  182. } // namespace
  183. gin::WrapperInfo WebRequest::kWrapperInfo = {gin::kEmbedderNativeGin};
  184. WebRequest::RequestFilter::RequestFilter(
  185. std::set<URLPattern> include_url_patterns,
  186. std::set<URLPattern> exclude_url_patterns,
  187. std::set<extensions::WebRequestResourceType> types)
  188. : include_url_patterns_(std::move(include_url_patterns)),
  189. exclude_url_patterns_(std::move(exclude_url_patterns)),
  190. types_(std::move(types)) {}
  191. WebRequest::RequestFilter::RequestFilter(const RequestFilter&) = default;
  192. WebRequest::RequestFilter::RequestFilter() = default;
  193. WebRequest::RequestFilter::~RequestFilter() = default;
  194. void WebRequest::RequestFilter::AddUrlPattern(URLPattern pattern,
  195. bool is_match_pattern) {
  196. if (is_match_pattern) {
  197. include_url_patterns_.emplace(std::move(pattern));
  198. } else {
  199. exclude_url_patterns_.emplace(std::move(pattern));
  200. }
  201. }
  202. void WebRequest::RequestFilter::AddType(
  203. extensions::WebRequestResourceType type) {
  204. types_.insert(type);
  205. }
  206. bool WebRequest::RequestFilter::MatchesURL(
  207. const GURL& url,
  208. const std::set<URLPattern>& patterns) const {
  209. if (patterns.empty())
  210. return false;
  211. for (const auto& pattern : patterns) {
  212. if (pattern.MatchesURL(url))
  213. return true;
  214. }
  215. return false;
  216. }
  217. bool WebRequest::RequestFilter::MatchesType(
  218. extensions::WebRequestResourceType type) const {
  219. return types_.empty() || base::Contains(types_, type);
  220. }
  221. bool WebRequest::RequestFilter::MatchesRequest(
  222. extensions::WebRequestInfo* info) const {
  223. // Matches URL and type, and does not match exclude URL.
  224. return MatchesURL(info->url, include_url_patterns_) &&
  225. !MatchesURL(info->url, exclude_url_patterns_) &&
  226. MatchesType(info->web_request_type);
  227. }
  228. void WebRequest::RequestFilter::AddUrlPatterns(
  229. const std::set<std::string>& filter_patterns,
  230. RequestFilter* filter,
  231. gin::Arguments* args,
  232. bool is_match_pattern) {
  233. for (const std::string& filter_pattern : filter_patterns) {
  234. URLPattern pattern(URLPattern::SCHEME_ALL);
  235. const URLPattern::ParseResult result = pattern.Parse(filter_pattern);
  236. if (result == URLPattern::ParseResult::kSuccess) {
  237. filter->AddUrlPattern(std::move(pattern), is_match_pattern);
  238. } else {
  239. const char* error_type = URLPattern::GetParseResultString(result);
  240. args->ThrowTypeError("Invalid url pattern " + filter_pattern + ": " +
  241. error_type);
  242. return;
  243. }
  244. }
  245. }
  246. struct WebRequest::BlockedRequest {
  247. BlockedRequest() = default;
  248. raw_ptr<const extensions::WebRequestInfo> request = nullptr;
  249. net::CompletionOnceCallback callback;
  250. // Only used for onBeforeSendHeaders.
  251. BeforeSendHeadersCallback before_send_headers_callback;
  252. // Only used for onBeforeSendHeaders.
  253. raw_ptr<net::HttpRequestHeaders> request_headers = nullptr;
  254. // Only used for onHeadersReceived.
  255. scoped_refptr<const net::HttpResponseHeaders> original_response_headers;
  256. // Only used for onHeadersReceived.
  257. raw_ptr<scoped_refptr<net::HttpResponseHeaders>> override_response_headers =
  258. nullptr;
  259. std::string status_line;
  260. // Only used for onBeforeRequest.
  261. raw_ptr<GURL> new_url = nullptr;
  262. };
  263. WebRequest::SimpleListenerInfo::SimpleListenerInfo(RequestFilter filter_,
  264. SimpleListener listener_)
  265. : filter(std::move(filter_)), listener(listener_) {}
  266. WebRequest::SimpleListenerInfo::SimpleListenerInfo() = default;
  267. WebRequest::SimpleListenerInfo::~SimpleListenerInfo() = default;
  268. WebRequest::ResponseListenerInfo::ResponseListenerInfo(
  269. RequestFilter filter_,
  270. ResponseListener listener_)
  271. : filter(std::move(filter_)), listener(listener_) {}
  272. WebRequest::ResponseListenerInfo::ResponseListenerInfo() = default;
  273. WebRequest::ResponseListenerInfo::~ResponseListenerInfo() = default;
  274. WebRequest::WebRequest(v8::Isolate* isolate,
  275. content::BrowserContext* browser_context)
  276. : browser_context_(browser_context) {
  277. browser_context_->SetUserData(kUserDataKey, std::make_unique<UserData>(this));
  278. }
  279. WebRequest::~WebRequest() {
  280. browser_context_->RemoveUserData(kUserDataKey);
  281. }
  282. gin::ObjectTemplateBuilder WebRequest::GetObjectTemplateBuilder(
  283. v8::Isolate* isolate) {
  284. return gin::Wrappable<WebRequest>::GetObjectTemplateBuilder(isolate)
  285. .SetMethod(
  286. "onBeforeRequest",
  287. &WebRequest::SetResponseListener<ResponseEvent::kOnBeforeRequest>)
  288. .SetMethod(
  289. "onBeforeSendHeaders",
  290. &WebRequest::SetResponseListener<ResponseEvent::kOnBeforeSendHeaders>)
  291. .SetMethod(
  292. "onHeadersReceived",
  293. &WebRequest::SetResponseListener<ResponseEvent::kOnHeadersReceived>)
  294. .SetMethod("onSendHeaders",
  295. &WebRequest::SetSimpleListener<SimpleEvent::kOnSendHeaders>)
  296. .SetMethod("onBeforeRedirect",
  297. &WebRequest::SetSimpleListener<SimpleEvent::kOnBeforeRedirect>)
  298. .SetMethod(
  299. "onResponseStarted",
  300. &WebRequest::SetSimpleListener<SimpleEvent::kOnResponseStarted>)
  301. .SetMethod("onErrorOccurred",
  302. &WebRequest::SetSimpleListener<SimpleEvent::kOnErrorOccurred>)
  303. .SetMethod("onCompleted",
  304. &WebRequest::SetSimpleListener<SimpleEvent::kOnCompleted>);
  305. }
  306. const char* WebRequest::GetTypeName() {
  307. return GetClassName();
  308. }
  309. bool WebRequest::HasListener() const {
  310. return !(simple_listeners_.empty() && response_listeners_.empty());
  311. }
  312. int WebRequest::OnBeforeRequest(extensions::WebRequestInfo* info,
  313. const network::ResourceRequest& request,
  314. net::CompletionOnceCallback callback,
  315. GURL* new_url) {
  316. return HandleOnBeforeRequestResponseEvent(info, request, std::move(callback),
  317. new_url);
  318. }
  319. int WebRequest::HandleOnBeforeRequestResponseEvent(
  320. extensions::WebRequestInfo* request_info,
  321. const network::ResourceRequest& request,
  322. net::CompletionOnceCallback callback,
  323. GURL* new_url) {
  324. const auto iter = response_listeners_.find(ResponseEvent::kOnBeforeRequest);
  325. if (iter == std::end(response_listeners_))
  326. return net::OK;
  327. const auto& info = iter->second;
  328. if (!info.filter.MatchesRequest(request_info))
  329. return net::OK;
  330. BlockedRequest blocked_request;
  331. blocked_request.callback = std::move(callback);
  332. blocked_request.new_url = new_url;
  333. blocked_requests_[request_info->id] = std::move(blocked_request);
  334. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  335. v8::HandleScope handle_scope(isolate);
  336. gin_helper::Dictionary details(isolate, v8::Object::New(isolate));
  337. FillDetails(&details, request_info, request, *new_url);
  338. ResponseCallback response =
  339. base::BindOnce(&WebRequest::OnBeforeRequestListenerResult,
  340. base::Unretained(this), request_info->id);
  341. info.listener.Run(gin::ConvertToV8(isolate, details), std::move(response));
  342. return net::ERR_IO_PENDING;
  343. }
  344. void WebRequest::OnBeforeRequestListenerResult(uint64_t id,
  345. v8::Local<v8::Value> response) {
  346. const auto iter = blocked_requests_.find(id);
  347. if (iter == std::end(blocked_requests_))
  348. return;
  349. auto& request = iter->second;
  350. int result = net::OK;
  351. if (response->IsObject()) {
  352. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  353. gin::Dictionary dict(isolate, response.As<v8::Object>());
  354. bool cancel = false;
  355. dict.Get("cancel", &cancel);
  356. if (cancel) {
  357. result = net::ERR_BLOCKED_BY_CLIENT;
  358. } else {
  359. dict.Get("redirectURL", request.new_url.get());
  360. }
  361. }
  362. base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
  363. FROM_HERE, base::BindOnce(std::move(request.callback), result));
  364. blocked_requests_.erase(iter);
  365. }
  366. int WebRequest::OnBeforeSendHeaders(extensions::WebRequestInfo* info,
  367. const network::ResourceRequest& request,
  368. BeforeSendHeadersCallback callback,
  369. net::HttpRequestHeaders* headers) {
  370. return HandleOnBeforeSendHeadersResponseEvent(info, request,
  371. std::move(callback), headers);
  372. }
  373. int WebRequest::HandleOnBeforeSendHeadersResponseEvent(
  374. extensions::WebRequestInfo* request_info,
  375. const network::ResourceRequest& request,
  376. BeforeSendHeadersCallback callback,
  377. net::HttpRequestHeaders* headers) {
  378. const auto iter =
  379. response_listeners_.find(ResponseEvent::kOnBeforeSendHeaders);
  380. if (iter == std::end(response_listeners_))
  381. return net::OK;
  382. const auto& info = iter->second;
  383. if (!info.filter.MatchesRequest(request_info))
  384. return net::OK;
  385. BlockedRequest blocked_request;
  386. blocked_request.before_send_headers_callback = std::move(callback);
  387. blocked_request.request_headers = headers;
  388. blocked_requests_[request_info->id] = std::move(blocked_request);
  389. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  390. v8::HandleScope handle_scope(isolate);
  391. gin_helper::Dictionary details(isolate, v8::Object::New(isolate));
  392. FillDetails(&details, request_info, request, *headers);
  393. ResponseCallback response =
  394. base::BindOnce(&WebRequest::OnBeforeSendHeadersListenerResult,
  395. base::Unretained(this), request_info->id);
  396. info.listener.Run(gin::ConvertToV8(isolate, details), std::move(response));
  397. return net::ERR_IO_PENDING;
  398. }
  399. void WebRequest::OnBeforeSendHeadersListenerResult(
  400. uint64_t id,
  401. v8::Local<v8::Value> response) {
  402. const auto iter = blocked_requests_.find(id);
  403. if (iter == std::end(blocked_requests_))
  404. return;
  405. auto& request = iter->second;
  406. net::HttpRequestHeaders* old_headers = request.request_headers;
  407. net::HttpRequestHeaders new_headers;
  408. int result = net::OK;
  409. bool user_modified_headers = false;
  410. if (response->IsObject()) {
  411. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  412. gin::Dictionary dict(isolate, response.As<v8::Object>());
  413. bool cancel = false;
  414. dict.Get("cancel", &cancel);
  415. if (cancel) {
  416. result = net::ERR_BLOCKED_BY_CLIENT;
  417. } else {
  418. v8::Local<v8::Value> value;
  419. if (dict.Get("requestHeaders", &value) && value->IsObject()) {
  420. user_modified_headers = true;
  421. gin::Converter<net::HttpRequestHeaders>::FromV8(isolate, value,
  422. &new_headers);
  423. }
  424. }
  425. }
  426. // If the user passes |cancel|, |new_headers| should be nullptr.
  427. const auto updated_headers = CalculateOnBeforeSendHeadersDelta(
  428. old_headers,
  429. result == net::ERR_BLOCKED_BY_CLIENT ? nullptr : &new_headers);
  430. // Leave |request.request_headers| unchanged if the user didn't modify it.
  431. if (user_modified_headers)
  432. request.request_headers->Swap(&new_headers);
  433. base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
  434. FROM_HERE,
  435. base::BindOnce(std::move(request.before_send_headers_callback),
  436. updated_headers.first, updated_headers.second, result));
  437. blocked_requests_.erase(iter);
  438. }
  439. int WebRequest::OnHeadersReceived(
  440. extensions::WebRequestInfo* info,
  441. const network::ResourceRequest& request,
  442. net::CompletionOnceCallback callback,
  443. const net::HttpResponseHeaders* original_response_headers,
  444. scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
  445. GURL* allowed_unsafe_redirect_url) {
  446. return HandleOnHeadersReceivedResponseEvent(
  447. info, request, std::move(callback), original_response_headers,
  448. override_response_headers);
  449. }
  450. int WebRequest::HandleOnHeadersReceivedResponseEvent(
  451. extensions::WebRequestInfo* request_info,
  452. const network::ResourceRequest& request,
  453. net::CompletionOnceCallback callback,
  454. const net::HttpResponseHeaders* original_response_headers,
  455. scoped_refptr<net::HttpResponseHeaders>* override_response_headers) {
  456. const auto iter = response_listeners_.find(ResponseEvent::kOnHeadersReceived);
  457. if (iter == std::end(response_listeners_))
  458. return net::OK;
  459. const auto& info = iter->second;
  460. if (!info.filter.MatchesRequest(request_info))
  461. return net::OK;
  462. BlockedRequest blocked_request;
  463. blocked_request.callback = std::move(callback);
  464. blocked_request.override_response_headers = override_response_headers;
  465. blocked_request.status_line = original_response_headers
  466. ? original_response_headers->GetStatusLine()
  467. : std::string();
  468. blocked_requests_[request_info->id] = std::move(blocked_request);
  469. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  470. v8::HandleScope handle_scope(isolate);
  471. gin_helper::Dictionary details(isolate, v8::Object::New(isolate));
  472. FillDetails(&details, request_info, request);
  473. ResponseCallback response =
  474. base::BindOnce(&WebRequest::OnHeadersReceivedListenerResult,
  475. base::Unretained(this), request_info->id);
  476. info.listener.Run(gin::ConvertToV8(isolate, details), std::move(response));
  477. return net::ERR_IO_PENDING;
  478. }
  479. void WebRequest::OnHeadersReceivedListenerResult(
  480. uint64_t id,
  481. v8::Local<v8::Value> response) {
  482. const auto iter = blocked_requests_.find(id);
  483. if (iter == std::end(blocked_requests_))
  484. return;
  485. auto& request = iter->second;
  486. int result = net::OK;
  487. bool user_modified_headers = false;
  488. scoped_refptr<net::HttpResponseHeaders> override_headers(
  489. new net::HttpResponseHeaders(""));
  490. if (response->IsObject()) {
  491. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  492. gin::Dictionary dict(isolate, response.As<v8::Object>());
  493. bool cancel = false;
  494. dict.Get("cancel", &cancel);
  495. if (cancel) {
  496. result = net::ERR_BLOCKED_BY_CLIENT;
  497. } else {
  498. std::string status_line;
  499. if (!dict.Get("statusLine", &status_line))
  500. status_line = request.status_line;
  501. v8::Local<v8::Value> value;
  502. if (dict.Get("responseHeaders", &value) && value->IsObject()) {
  503. user_modified_headers = true;
  504. override_headers->ReplaceStatusLine(status_line);
  505. gin::Converter<net::HttpResponseHeaders*>::FromV8(
  506. isolate, value, override_headers.get());
  507. }
  508. }
  509. }
  510. if (user_modified_headers)
  511. request.override_response_headers->swap(override_headers);
  512. base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
  513. FROM_HERE, base::BindOnce(std::move(request.callback), result));
  514. blocked_requests_.erase(iter);
  515. }
  516. void WebRequest::OnSendHeaders(extensions::WebRequestInfo* info,
  517. const network::ResourceRequest& request,
  518. const net::HttpRequestHeaders& headers) {
  519. HandleSimpleEvent(SimpleEvent::kOnSendHeaders, info, request, headers);
  520. }
  521. void WebRequest::OnBeforeRedirect(extensions::WebRequestInfo* info,
  522. const network::ResourceRequest& request,
  523. const GURL& new_location) {
  524. HandleSimpleEvent(SimpleEvent::kOnBeforeRedirect, info, request,
  525. new_location);
  526. }
  527. void WebRequest::OnResponseStarted(extensions::WebRequestInfo* info,
  528. const network::ResourceRequest& request) {
  529. HandleSimpleEvent(SimpleEvent::kOnResponseStarted, info, request);
  530. }
  531. void WebRequest::OnErrorOccurred(extensions::WebRequestInfo* info,
  532. const network::ResourceRequest& request,
  533. int net_error) {
  534. blocked_requests_.erase(info->id);
  535. HandleSimpleEvent(SimpleEvent::kOnErrorOccurred, info, request, net_error);
  536. }
  537. void WebRequest::OnCompleted(extensions::WebRequestInfo* info,
  538. const network::ResourceRequest& request,
  539. int net_error) {
  540. blocked_requests_.erase(info->id);
  541. HandleSimpleEvent(SimpleEvent::kOnCompleted, info, request, net_error);
  542. }
  543. void WebRequest::OnRequestWillBeDestroyed(extensions::WebRequestInfo* info) {
  544. blocked_requests_.erase(info->id);
  545. }
  546. template <WebRequest::SimpleEvent event>
  547. void WebRequest::SetSimpleListener(gin::Arguments* args) {
  548. SetListener<SimpleListener>(event, &simple_listeners_, args);
  549. }
  550. template <WebRequest::ResponseEvent event>
  551. void WebRequest::SetResponseListener(gin::Arguments* args) {
  552. SetListener<ResponseListener>(event, &response_listeners_, args);
  553. }
  554. template <typename Listener, typename Listeners, typename Event>
  555. void WebRequest::SetListener(Event event,
  556. Listeners* listeners,
  557. gin::Arguments* args) {
  558. v8::Local<v8::Value> arg;
  559. // { urls, excludeUrls, types }.
  560. std::set<std::string> filter_include_patterns, filter_exclude_patterns,
  561. filter_types;
  562. RequestFilter filter;
  563. gin::Dictionary dict(args->isolate());
  564. if (args->GetNext(&arg) && !arg->IsFunction()) {
  565. // Note that gin treats Function as Dictionary when doing conversions, so we
  566. // have to explicitly check if the argument is Function before trying to
  567. // convert it to Dictionary.
  568. if (gin::ConvertFromV8(args->isolate(), arg, &dict)) {
  569. if (!dict.Get("urls", &filter_include_patterns)) {
  570. args->ThrowTypeError("Parameter 'filter' must have property 'urls'.");
  571. return;
  572. }
  573. if (filter_include_patterns.empty()) {
  574. util::EmitWarning(
  575. "The urls array in WebRequestFilter is empty, which is deprecated. "
  576. "Please use '<all_urls>' to match all URLs.",
  577. "DeprecationWarning");
  578. filter_include_patterns.insert("<all_urls>");
  579. }
  580. dict.Get("excludeUrls", &filter_exclude_patterns);
  581. dict.Get("types", &filter_types);
  582. args->GetNext(&arg);
  583. }
  584. } else {
  585. // If no filter is defined, create one with <all_urls> so it matches all
  586. // requests
  587. dict = gin::Dictionary::CreateEmpty(args->isolate());
  588. filter_include_patterns.insert("<all_urls>");
  589. dict.Set("urls", filter_include_patterns);
  590. }
  591. filter.AddUrlPatterns(filter_include_patterns, &filter, args);
  592. filter.AddUrlPatterns(filter_exclude_patterns, &filter, args, false);
  593. for (const std::string& filter_type : filter_types) {
  594. auto type = ParseResourceType(filter_type);
  595. if (type != extensions::WebRequestResourceType::OTHER) {
  596. filter.AddType(type);
  597. } else {
  598. args->ThrowTypeError("Invalid type " + filter_type);
  599. return;
  600. }
  601. }
  602. // Function or null.
  603. Listener listener;
  604. if (arg.IsEmpty() ||
  605. !(gin::ConvertFromV8(args->isolate(), arg, &listener) || arg->IsNull())) {
  606. args->ThrowTypeError("Must pass null or a Function");
  607. return;
  608. }
  609. if (listener.is_null())
  610. listeners->erase(event);
  611. else
  612. (*listeners)[event] = {std::move(filter), std::move(listener)};
  613. }
  614. template <typename... Args>
  615. void WebRequest::HandleSimpleEvent(SimpleEvent event,
  616. extensions::WebRequestInfo* request_info,
  617. Args... args) {
  618. const auto iter = simple_listeners_.find(event);
  619. if (iter == std::end(simple_listeners_))
  620. return;
  621. const auto& info = iter->second;
  622. if (!info.filter.MatchesRequest(request_info))
  623. return;
  624. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  625. v8::HandleScope handle_scope(isolate);
  626. gin_helper::Dictionary details(isolate, v8::Object::New(isolate));
  627. FillDetails(&details, request_info, args...);
  628. info.listener.Run(gin::ConvertToV8(isolate, details));
  629. }
  630. // static
  631. gin::Handle<WebRequest> WebRequest::FromOrCreate(
  632. v8::Isolate* isolate,
  633. content::BrowserContext* browser_context) {
  634. gin::Handle<WebRequest> handle = From(isolate, browser_context);
  635. if (handle.IsEmpty()) {
  636. // Make sure the |Session| object has the |webRequest| property created.
  637. v8::Local<v8::Value> web_request =
  638. Session::CreateFrom(
  639. isolate, static_cast<ElectronBrowserContext*>(browser_context))
  640. ->WebRequest(isolate);
  641. gin::ConvertFromV8(isolate, web_request, &handle);
  642. }
  643. DCHECK(!handle.IsEmpty());
  644. return handle;
  645. }
  646. // static
  647. gin::Handle<WebRequest> WebRequest::Create(
  648. v8::Isolate* isolate,
  649. content::BrowserContext* browser_context) {
  650. DCHECK(From(isolate, browser_context).IsEmpty())
  651. << "WebRequest already created";
  652. return gin::CreateHandle(isolate, new WebRequest(isolate, browser_context));
  653. }
  654. // static
  655. gin::Handle<WebRequest> WebRequest::From(
  656. v8::Isolate* isolate,
  657. content::BrowserContext* browser_context) {
  658. if (!browser_context)
  659. return {};
  660. auto* user_data =
  661. static_cast<UserData*>(browser_context->GetUserData(kUserDataKey));
  662. if (!user_data)
  663. return {};
  664. return gin::CreateHandle(isolate, user_data->data.get());
  665. }
  666. } // namespace electron::api