electron_api_web_request.cc 28 KB

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