electron_api_session.cc 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869
  1. // Copyright (c) 2015 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_session.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include <set>
  8. #include <string>
  9. #include <utility>
  10. #include <vector>
  11. #include "base/command_line.h"
  12. #include "base/containers/fixed_flat_map.h"
  13. #include "base/files/file_enumerator.h"
  14. #include "base/files/file_path.h"
  15. #include "base/files/file_util.h"
  16. #include "base/scoped_observation.h"
  17. #include "base/strings/string_util.h"
  18. #include "base/uuid.h"
  19. #include "chrome/browser/browser_process.h"
  20. #include "chrome/browser/predictors/preconnect_manager.h"
  21. #include "chrome/common/chrome_switches.h"
  22. #include "chrome/common/pref_names.h"
  23. #include "components/download/public/common/download_danger_type.h"
  24. #include "components/download/public/common/download_url_parameters.h"
  25. #include "components/prefs/pref_service.h"
  26. #include "components/prefs/value_map_pref_store.h"
  27. #include "components/proxy_config/proxy_config_dictionary.h"
  28. #include "components/proxy_config/proxy_config_pref_names.h"
  29. #include "components/proxy_config/proxy_prefs.h"
  30. #include "content/browser/code_cache/generated_code_cache_context.h" // nogncheck
  31. #include "content/public/browser/browser_task_traits.h"
  32. #include "content/public/browser/browser_thread.h"
  33. #include "content/public/browser/browsing_data_filter_builder.h"
  34. #include "content/public/browser/browsing_data_remover.h"
  35. #include "content/public/browser/download_item_utils.h"
  36. #include "content/public/browser/download_manager_delegate.h"
  37. #include "content/public/browser/network_service_instance.h"
  38. #include "content/public/browser/storage_partition.h"
  39. #include "gin/arguments.h"
  40. #include "gin/converter.h"
  41. #include "gin/handle.h"
  42. #include "mojo/public/cpp/bindings/pending_remote.h"
  43. #include "mojo/public/cpp/bindings/self_owned_receiver.h"
  44. #include "net/base/completion_repeating_callback.h"
  45. #include "net/base/load_flags.h"
  46. #include "net/base/network_anonymization_key.h"
  47. #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
  48. #include "net/http/http_auth_handler_factory.h"
  49. #include "net/http/http_auth_preferences.h"
  50. #include "net/http/http_cache.h"
  51. #include "net/http/http_util.h"
  52. #include "services/network/network_service.h"
  53. #include "services/network/public/cpp/features.h"
  54. #include "services/network/public/cpp/request_destination.h"
  55. #include "services/network/public/mojom/clear_data_filter.mojom.h"
  56. #include "shell/browser/api/electron_api_app.h"
  57. #include "shell/browser/api/electron_api_cookies.h"
  58. #include "shell/browser/api/electron_api_data_pipe_holder.h"
  59. #include "shell/browser/api/electron_api_download_item.h"
  60. #include "shell/browser/api/electron_api_net_log.h"
  61. #include "shell/browser/api/electron_api_protocol.h"
  62. #include "shell/browser/api/electron_api_service_worker_context.h"
  63. #include "shell/browser/api/electron_api_web_frame_main.h"
  64. #include "shell/browser/api/electron_api_web_request.h"
  65. #include "shell/browser/browser.h"
  66. #include "shell/browser/electron_browser_context.h"
  67. #include "shell/browser/electron_browser_main_parts.h"
  68. #include "shell/browser/electron_permission_manager.h"
  69. #include "shell/browser/javascript_environment.h"
  70. #include "shell/browser/media/media_device_id_salt.h"
  71. #include "shell/browser/net/cert_verifier_client.h"
  72. #include "shell/browser/net/resolve_host_function.h"
  73. #include "shell/browser/session_preferences.h"
  74. #include "shell/common/gin_converters/callback_converter.h"
  75. #include "shell/common/gin_converters/content_converter.h"
  76. #include "shell/common/gin_converters/file_path_converter.h"
  77. #include "shell/common/gin_converters/gurl_converter.h"
  78. #include "shell/common/gin_converters/media_converter.h"
  79. #include "shell/common/gin_converters/net_converter.h"
  80. #include "shell/common/gin_converters/time_converter.h"
  81. #include "shell/common/gin_converters/usb_protected_classes_converter.h"
  82. #include "shell/common/gin_converters/value_converter.h"
  83. #include "shell/common/gin_helper/dictionary.h"
  84. #include "shell/common/gin_helper/error_thrower.h"
  85. #include "shell/common/gin_helper/object_template_builder.h"
  86. #include "shell/common/gin_helper/promise.h"
  87. #include "shell/common/node_includes.h"
  88. #include "shell/common/node_util.h"
  89. #include "shell/common/options_switches.h"
  90. #include "third_party/abseil-cpp/absl/strings/str_format.h"
  91. #include "third_party/blink/public/common/storage_key/storage_key.h"
  92. #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
  93. #include "ui/base/l10n/l10n_util.h"
  94. #include "url/origin.h"
  95. #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
  96. #include "shell/browser/api/electron_api_extensions.h"
  97. #endif
  98. #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
  99. #include "chrome/browser/spellchecker/spellcheck_factory.h" // nogncheck
  100. #include "chrome/browser/spellchecker/spellcheck_service.h" // nogncheck
  101. #include "components/spellcheck/browser/pref_names.h"
  102. #include "components/spellcheck/common/spellcheck_common.h"
  103. #if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
  104. #include "components/spellcheck/browser/spellcheck_platform.h"
  105. #include "components/spellcheck/common/spellcheck_features.h"
  106. #endif
  107. #endif
  108. using content::BrowserThread;
  109. using content::BrowsingDataFilterBuilder;
  110. using content::BrowsingDataRemover;
  111. using content::StoragePartition;
  112. namespace {
  113. struct ClearStorageDataOptions {
  114. blink::StorageKey storage_key;
  115. uint32_t storage_types = StoragePartition::REMOVE_DATA_MASK_ALL;
  116. uint32_t quota_types = StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL;
  117. };
  118. uint32_t GetStorageMask(const std::vector<std::string>& storage_types) {
  119. static constexpr auto Lookup =
  120. base::MakeFixedFlatMap<std::string_view, uint32_t>(
  121. {{"cookies", StoragePartition::REMOVE_DATA_MASK_COOKIES},
  122. {"filesystem", StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS},
  123. {"indexdb", StoragePartition::REMOVE_DATA_MASK_INDEXEDDB},
  124. {"localstorage", StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE},
  125. {"shadercache", StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE},
  126. {"serviceworkers",
  127. StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS},
  128. {"cachestorage", StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE}});
  129. uint32_t storage_mask = 0;
  130. for (const auto& it : storage_types) {
  131. auto type = base::ToLowerASCII(it);
  132. if (Lookup.contains(type))
  133. storage_mask |= Lookup.at(type);
  134. }
  135. return storage_mask;
  136. }
  137. uint32_t GetQuotaMask(const std::vector<std::string>& quota_types) {
  138. uint32_t quota_mask = 0;
  139. for (const auto& it : quota_types) {
  140. auto type = base::ToLowerASCII(it);
  141. if (type == "temporary")
  142. quota_mask |= StoragePartition::QUOTA_MANAGED_STORAGE_MASK_TEMPORARY;
  143. else if (type == "syncable")
  144. quota_mask |= StoragePartition::QUOTA_MANAGED_STORAGE_MASK_SYNCABLE;
  145. }
  146. return quota_mask;
  147. }
  148. constexpr BrowsingDataRemover::DataType kClearDataTypeAll =
  149. ~0ULL & ~BrowsingDataRemover::DATA_TYPE_AVOID_CLOSING_CONNECTIONS;
  150. constexpr BrowsingDataRemover::OriginType kClearOriginTypeAll =
  151. BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB |
  152. BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB;
  153. constexpr auto kDataTypeLookup =
  154. base::MakeFixedFlatMap<std::string_view, BrowsingDataRemover::DataType>({
  155. {"backgroundFetch", BrowsingDataRemover::DATA_TYPE_BACKGROUND_FETCH},
  156. {"cache", BrowsingDataRemover::DATA_TYPE_CACHE |
  157. BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE},
  158. {"cookies", BrowsingDataRemover::DATA_TYPE_COOKIES},
  159. {"downloads", BrowsingDataRemover::DATA_TYPE_DOWNLOADS},
  160. {"fileSystems", BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS},
  161. {"indexedDB", BrowsingDataRemover::DATA_TYPE_INDEXED_DB},
  162. {"localStorage", BrowsingDataRemover::DATA_TYPE_LOCAL_STORAGE},
  163. {"serviceWorkers", BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS},
  164. });
  165. BrowsingDataRemover::DataType GetDataTypeMask(
  166. const std::vector<std::string>& data_types) {
  167. BrowsingDataRemover::DataType mask = 0u;
  168. for (const auto& type : data_types) {
  169. if (kDataTypeLookup.contains(type)) {
  170. mask |= kDataTypeLookup.at(type);
  171. }
  172. }
  173. return mask;
  174. }
  175. std::vector<std::string> GetDataTypesFromMask(
  176. BrowsingDataRemover::DataType mask) {
  177. std::vector<std::string> results;
  178. for (const auto [type, flag] : kDataTypeLookup) {
  179. if (mask & flag) {
  180. results.emplace_back(type);
  181. }
  182. }
  183. return results;
  184. }
  185. // Represents a task to clear browsing data for the `clearData` API method.
  186. //
  187. // This type manages its own lifetime, deleting itself once the task finishes
  188. // completely.
  189. class ClearDataTask {
  190. public:
  191. // Starts running a task. This function will return before the task is
  192. // finished, but will resolve or reject the |promise| when it finishes.
  193. static void Run(
  194. BrowsingDataRemover* remover,
  195. gin_helper::Promise<void> promise,
  196. BrowsingDataRemover::DataType data_type_mask,
  197. std::vector<url::Origin> origins,
  198. BrowsingDataFilterBuilder::Mode filter_mode,
  199. BrowsingDataFilterBuilder::OriginMatchingMode origin_matching_mode) {
  200. std::shared_ptr<ClearDataTask> task(new ClearDataTask(std::move(promise)));
  201. // This method counts as an operation. This is important so we can call
  202. // `OnOperationFinished` at the end of this method as a fallback if all the
  203. // other operations finished while this method was still executing
  204. task->operations_running_ = 1;
  205. // Cookies are scoped more broadly than other types of data, so if we are
  206. // filtering then we need to do it at the registrable domain level
  207. if (!origins.empty() &&
  208. data_type_mask & BrowsingDataRemover::DATA_TYPE_COOKIES) {
  209. data_type_mask &= ~BrowsingDataRemover::DATA_TYPE_COOKIES;
  210. auto cookies_filter_builder =
  211. BrowsingDataFilterBuilder::Create(filter_mode);
  212. for (const url::Origin& origin : origins) {
  213. std::string domain = GetDomainAndRegistry(
  214. origin,
  215. net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
  216. if (domain.empty()) {
  217. domain = origin.host();
  218. }
  219. cookies_filter_builder->AddRegisterableDomain(domain);
  220. }
  221. StartOperation(task, remover, BrowsingDataRemover::DATA_TYPE_COOKIES,
  222. std::move(cookies_filter_builder));
  223. }
  224. // If cookies aren't the only data type and weren't handled above, then we
  225. // can start an operation that is scoped to origins
  226. if (data_type_mask) {
  227. auto filter_builder =
  228. BrowsingDataFilterBuilder::Create(filter_mode, origin_matching_mode);
  229. for (auto const& origin : origins) {
  230. filter_builder->AddOrigin(origin);
  231. }
  232. StartOperation(task, remover, data_type_mask, std::move(filter_builder));
  233. }
  234. // This static method counts as an operation.
  235. task->OnOperationFinished(std::nullopt);
  236. }
  237. private:
  238. // An individual |content::BrowsingDataRemover::Remove...| operation as part
  239. // of a full |ClearDataTask|. This class manages its own lifetime, cleaning
  240. // itself up after the operation completes and notifies the task of the
  241. // result.
  242. class ClearDataOperation : private BrowsingDataRemover::Observer {
  243. public:
  244. static void Run(std::shared_ptr<ClearDataTask> task,
  245. BrowsingDataRemover* remover,
  246. BrowsingDataRemover::DataType data_type_mask,
  247. std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) {
  248. auto* operation = new ClearDataOperation(task, remover);
  249. remover->RemoveWithFilterAndReply(base::Time::Min(), base::Time::Max(),
  250. data_type_mask, kClearOriginTypeAll,
  251. std::move(filter_builder), operation);
  252. }
  253. // BrowsingDataRemover::Observer:
  254. void OnBrowsingDataRemoverDone(
  255. BrowsingDataRemover::DataType failed_data_types) override {
  256. task_->OnOperationFinished(failed_data_types);
  257. delete this;
  258. }
  259. private:
  260. ClearDataOperation(std::shared_ptr<ClearDataTask> task,
  261. BrowsingDataRemover* remover)
  262. : task_(task) {
  263. observation_.Observe(remover);
  264. }
  265. std::shared_ptr<ClearDataTask> task_;
  266. base::ScopedObservation<BrowsingDataRemover, BrowsingDataRemover::Observer>
  267. observation_{this};
  268. };
  269. explicit ClearDataTask(gin_helper::Promise<void> promise)
  270. : promise_(std::move(promise)) {}
  271. static void StartOperation(
  272. std::shared_ptr<ClearDataTask> task,
  273. BrowsingDataRemover* remover,
  274. BrowsingDataRemover::DataType data_type_mask,
  275. std::unique_ptr<BrowsingDataFilterBuilder> filter_builder) {
  276. // Track this operation
  277. task->operations_running_ += 1;
  278. ClearDataOperation::Run(task, remover, data_type_mask,
  279. std::move(filter_builder));
  280. }
  281. void OnOperationFinished(
  282. std::optional<BrowsingDataRemover::DataType> failed_data_types) {
  283. DCHECK_GT(operations_running_, 0);
  284. operations_running_ -= 1;
  285. if (failed_data_types.has_value()) {
  286. failed_data_types_ |= failed_data_types.value();
  287. }
  288. // If this is the last operation, then the task is finished
  289. if (operations_running_ == 0) {
  290. OnTaskFinished();
  291. }
  292. }
  293. void OnTaskFinished() {
  294. if (failed_data_types_ == 0ULL) {
  295. promise_.Resolve();
  296. } else {
  297. v8::Isolate* isolate = promise_.isolate();
  298. v8::Local<v8::Value> failed_data_types_array =
  299. gin::ConvertToV8(isolate, GetDataTypesFromMask(failed_data_types_));
  300. // Create a rich error object with extra detail about what data types
  301. // failed
  302. auto error = v8::Exception::Error(
  303. gin::StringToV8(isolate, "Failed to clear data"));
  304. error.As<v8::Object>()
  305. ->Set(promise_.GetContext(),
  306. gin::StringToV8(isolate, "failedDataTypes"),
  307. failed_data_types_array)
  308. .Check();
  309. promise_.Reject(error);
  310. }
  311. }
  312. int operations_running_ = 0;
  313. BrowsingDataRemover::DataType failed_data_types_ = 0ULL;
  314. gin_helper::Promise<void> promise_;
  315. };
  316. base::Value::Dict createProxyConfig(ProxyPrefs::ProxyMode proxy_mode,
  317. std::string const& pac_url,
  318. std::string const& proxy_server,
  319. std::string const& bypass_list) {
  320. if (proxy_mode == ProxyPrefs::MODE_DIRECT) {
  321. return ProxyConfigDictionary::CreateDirect();
  322. }
  323. if (proxy_mode == ProxyPrefs::MODE_SYSTEM) {
  324. return ProxyConfigDictionary::CreateSystem();
  325. }
  326. if (proxy_mode == ProxyPrefs::MODE_AUTO_DETECT) {
  327. return ProxyConfigDictionary::CreateAutoDetect();
  328. }
  329. if (proxy_mode == ProxyPrefs::MODE_PAC_SCRIPT) {
  330. const bool pac_mandatory = true;
  331. return ProxyConfigDictionary::CreatePacScript(pac_url, pac_mandatory);
  332. }
  333. return ProxyConfigDictionary::CreateFixedServers(proxy_server, bypass_list);
  334. }
  335. } // namespace
  336. namespace gin {
  337. template <>
  338. struct Converter<ClearStorageDataOptions> {
  339. static bool FromV8(v8::Isolate* isolate,
  340. v8::Local<v8::Value> val,
  341. ClearStorageDataOptions* out) {
  342. gin_helper::Dictionary options;
  343. if (!ConvertFromV8(isolate, val, &options))
  344. return false;
  345. if (GURL storage_origin; options.Get("origin", &storage_origin))
  346. out->storage_key = blink::StorageKey::CreateFirstParty(
  347. url::Origin::Create(storage_origin));
  348. std::vector<std::string> types;
  349. if (options.Get("storages", &types))
  350. out->storage_types = GetStorageMask(types);
  351. if (options.Get("quotas", &types))
  352. out->quota_types = GetQuotaMask(types);
  353. return true;
  354. }
  355. };
  356. bool SSLProtocolVersionFromString(const std::string& version_str,
  357. network::mojom::SSLVersion* version) {
  358. if (version_str == switches::kSSLVersionTLSv12) {
  359. *version = network::mojom::SSLVersion::kTLS12;
  360. return true;
  361. }
  362. if (version_str == switches::kSSLVersionTLSv13) {
  363. *version = network::mojom::SSLVersion::kTLS13;
  364. return true;
  365. }
  366. return false;
  367. }
  368. template <>
  369. struct Converter<uint16_t> {
  370. static bool FromV8(v8::Isolate* isolate,
  371. v8::Local<v8::Value> val,
  372. uint16_t* out) {
  373. auto maybe = val->IntegerValue(isolate->GetCurrentContext());
  374. if (maybe.IsNothing())
  375. return false;
  376. *out = maybe.FromJust();
  377. return true;
  378. }
  379. };
  380. template <>
  381. struct Converter<network::mojom::SSLConfigPtr> {
  382. static bool FromV8(v8::Isolate* isolate,
  383. v8::Local<v8::Value> val,
  384. network::mojom::SSLConfigPtr* out) {
  385. gin_helper::Dictionary options;
  386. if (!ConvertFromV8(isolate, val, &options))
  387. return false;
  388. *out = network::mojom::SSLConfig::New();
  389. std::string version_min_str;
  390. if (options.Get("minVersion", &version_min_str)) {
  391. if (!SSLProtocolVersionFromString(version_min_str, &(*out)->version_min))
  392. return false;
  393. }
  394. std::string version_max_str;
  395. if (options.Get("maxVersion", &version_max_str)) {
  396. if (!SSLProtocolVersionFromString(version_max_str,
  397. &(*out)->version_max) ||
  398. (*out)->version_max < network::mojom::SSLVersion::kTLS12)
  399. return false;
  400. }
  401. if (options.Has("disabledCipherSuites") &&
  402. !options.Get("disabledCipherSuites", &(*out)->disabled_cipher_suites)) {
  403. return false;
  404. }
  405. std::ranges::sort((*out)->disabled_cipher_suites);
  406. // TODO(nornagon): also support other SSLConfig properties?
  407. return true;
  408. }
  409. };
  410. } // namespace gin
  411. namespace electron::api {
  412. namespace {
  413. const char kPersistPrefix[] = "persist:";
  414. void DownloadIdCallback(content::DownloadManager* download_manager,
  415. const base::FilePath& path,
  416. const std::vector<GURL>& url_chain,
  417. const std::string& mime_type,
  418. int64_t offset,
  419. int64_t length,
  420. const std::string& last_modified,
  421. const std::string& etag,
  422. const base::Time& start_time,
  423. uint32_t id) {
  424. download_manager->CreateDownloadItem(
  425. base::Uuid::GenerateRandomV4().AsLowercaseString(), id, path, path,
  426. url_chain, GURL(),
  427. content::StoragePartitionConfig::CreateDefault(
  428. download_manager->GetBrowserContext()),
  429. GURL(), GURL(), std::nullopt, mime_type, mime_type, start_time,
  430. base::Time(), etag, last_modified, offset, length, std::string(),
  431. download::DownloadItem::INTERRUPTED,
  432. download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
  433. download::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT, false, base::Time(),
  434. false, std::vector<download::DownloadItem::ReceivedSlice>());
  435. }
  436. #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
  437. class DictionaryObserver final : public SpellcheckCustomDictionary::Observer {
  438. private:
  439. std::unique_ptr<gin_helper::Promise<std::set<std::string>>> promise_;
  440. base::WeakPtr<SpellcheckService> spellcheck_;
  441. public:
  442. DictionaryObserver(gin_helper::Promise<std::set<std::string>> promise,
  443. base::WeakPtr<SpellcheckService> spellcheck)
  444. : spellcheck_(spellcheck) {
  445. promise_ = std::make_unique<gin_helper::Promise<std::set<std::string>>>(
  446. std::move(promise));
  447. if (spellcheck_)
  448. spellcheck_->GetCustomDictionary()->AddObserver(this);
  449. }
  450. ~DictionaryObserver() {
  451. if (spellcheck_)
  452. spellcheck_->GetCustomDictionary()->RemoveObserver(this);
  453. }
  454. void OnCustomDictionaryLoaded() override {
  455. if (spellcheck_) {
  456. promise_->Resolve(spellcheck_->GetCustomDictionary()->GetWords());
  457. } else {
  458. promise_->RejectWithErrorMessage(
  459. "Spellcheck in unexpected state: failed to load custom dictionary.");
  460. }
  461. delete this;
  462. }
  463. void OnCustomDictionaryChanged(
  464. const SpellcheckCustomDictionary::Change& dictionary_change) override {
  465. // noop
  466. }
  467. };
  468. #endif // BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
  469. struct UserDataLink : base::SupportsUserData::Data {
  470. explicit UserDataLink(base::WeakPtr<Session> session_in)
  471. : session{std::move(session_in)} {}
  472. base::WeakPtr<Session> session;
  473. };
  474. const void* kElectronApiSessionKey = &kElectronApiSessionKey;
  475. } // namespace
  476. gin::WrapperInfo Session::kWrapperInfo = {gin::kEmbedderNativeGin};
  477. Session::Session(v8::Isolate* isolate, ElectronBrowserContext* browser_context)
  478. : isolate_(isolate),
  479. network_emulation_token_(base::UnguessableToken::Create()),
  480. browser_context_{
  481. raw_ref<ElectronBrowserContext>::from_ptr(browser_context)} {
  482. // Observe DownloadManager to get download notifications.
  483. browser_context->GetDownloadManager()->AddObserver(this);
  484. SessionPreferences::CreateForBrowserContext(browser_context);
  485. protocol_.Reset(isolate, Protocol::Create(isolate, browser_context).ToV8());
  486. browser_context->SetUserData(
  487. kElectronApiSessionKey,
  488. std::make_unique<UserDataLink>(weak_factory_.GetWeakPtr()));
  489. #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
  490. if (auto* service =
  491. SpellcheckServiceFactory::GetForContext(browser_context)) {
  492. service->SetHunspellObserver(this);
  493. }
  494. #endif
  495. }
  496. Session::~Session() {
  497. browser_context()->GetDownloadManager()->RemoveObserver(this);
  498. #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
  499. if (auto* service =
  500. SpellcheckServiceFactory::GetForContext(browser_context())) {
  501. service->SetHunspellObserver(nullptr);
  502. }
  503. #endif
  504. }
  505. void Session::OnDownloadCreated(content::DownloadManager* manager,
  506. download::DownloadItem* item) {
  507. if (item->IsSavePackageDownload())
  508. return;
  509. v8::HandleScope handle_scope(isolate_);
  510. auto handle = DownloadItem::FromOrCreate(isolate_, item);
  511. if (item->GetState() == download::DownloadItem::INTERRUPTED)
  512. handle->SetSavePath(item->GetTargetFilePath());
  513. content::WebContents* web_contents =
  514. content::DownloadItemUtils::GetWebContents(item);
  515. bool prevent_default = Emit("will-download", handle, web_contents);
  516. if (prevent_default) {
  517. item->Cancel(true);
  518. item->Remove();
  519. }
  520. }
  521. #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
  522. void Session::OnHunspellDictionaryInitialized(const std::string& language) {
  523. Emit("spellcheck-dictionary-initialized", language);
  524. }
  525. void Session::OnHunspellDictionaryDownloadBegin(const std::string& language) {
  526. Emit("spellcheck-dictionary-download-begin", language);
  527. }
  528. void Session::OnHunspellDictionaryDownloadSuccess(const std::string& language) {
  529. Emit("spellcheck-dictionary-download-success", language);
  530. }
  531. void Session::OnHunspellDictionaryDownloadFailure(const std::string& language) {
  532. Emit("spellcheck-dictionary-download-failure", language);
  533. }
  534. #endif
  535. v8::Local<v8::Promise> Session::ResolveProxy(gin::Arguments* args) {
  536. v8::Isolate* isolate = args->isolate();
  537. gin_helper::Promise<std::string> promise(isolate);
  538. v8::Local<v8::Promise> handle = promise.GetHandle();
  539. GURL url;
  540. args->GetNext(&url);
  541. browser_context_->GetResolveProxyHelper()->ResolveProxy(
  542. url, base::BindOnce(gin_helper::Promise<std::string>::ResolvePromise,
  543. std::move(promise)));
  544. return handle;
  545. }
  546. v8::Local<v8::Promise> Session::ResolveHost(
  547. std::string host,
  548. std::optional<network::mojom::ResolveHostParametersPtr> params) {
  549. gin_helper::Promise<gin_helper::Dictionary> promise(isolate_);
  550. v8::Local<v8::Promise> handle = promise.GetHandle();
  551. auto fn = base::MakeRefCounted<ResolveHostFunction>(
  552. browser_context(), std::move(host),
  553. params ? std::move(params.value()) : nullptr,
  554. base::BindOnce(
  555. [](gin_helper::Promise<gin_helper::Dictionary> promise,
  556. int64_t net_error, const std::optional<net::AddressList>& addrs) {
  557. if (net_error < 0) {
  558. promise.RejectWithErrorMessage(net::ErrorToString(net_error));
  559. } else {
  560. DCHECK(addrs.has_value() && !addrs->empty());
  561. v8::HandleScope handle_scope(promise.isolate());
  562. auto dict =
  563. gin_helper::Dictionary::CreateEmpty(promise.isolate());
  564. dict.Set("endpoints", addrs->endpoints());
  565. promise.Resolve(dict);
  566. }
  567. },
  568. std::move(promise)));
  569. fn->Run();
  570. return handle;
  571. }
  572. v8::Local<v8::Promise> Session::GetCacheSize() {
  573. gin_helper::Promise<int64_t> promise(isolate_);
  574. auto handle = promise.GetHandle();
  575. browser_context_->GetDefaultStoragePartition()
  576. ->GetNetworkContext()
  577. ->ComputeHttpCacheSize(
  578. base::Time(), base::Time::Max(),
  579. base::BindOnce(
  580. [](gin_helper::Promise<int64_t> promise, bool is_upper_bound,
  581. int64_t size_or_error) {
  582. if (size_or_error < 0) {
  583. promise.RejectWithErrorMessage(
  584. net::ErrorToString(size_or_error));
  585. } else {
  586. promise.Resolve(size_or_error);
  587. }
  588. },
  589. std::move(promise)));
  590. return handle;
  591. }
  592. v8::Local<v8::Promise> Session::ClearCache() {
  593. gin_helper::Promise<void> promise(isolate_);
  594. auto handle = promise.GetHandle();
  595. browser_context_->GetDefaultStoragePartition()
  596. ->GetNetworkContext()
  597. ->ClearHttpCache(base::Time(), base::Time::Max(), nullptr,
  598. base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
  599. std::move(promise)));
  600. return handle;
  601. }
  602. v8::Local<v8::Promise> Session::ClearStorageData(gin::Arguments* args) {
  603. v8::Isolate* isolate = args->isolate();
  604. gin_helper::Promise<void> promise(isolate);
  605. v8::Local<v8::Promise> handle = promise.GetHandle();
  606. ClearStorageDataOptions options;
  607. args->GetNext(&options);
  608. auto* storage_partition = browser_context()->GetStoragePartition(nullptr);
  609. if (options.storage_types & StoragePartition::REMOVE_DATA_MASK_COOKIES) {
  610. // Reset media device id salt when cookies are cleared.
  611. // https://w3c.github.io/mediacapture-main/#dom-mediadeviceinfo-deviceid
  612. MediaDeviceIDSalt::Reset(browser_context()->prefs());
  613. }
  614. storage_partition->ClearData(
  615. options.storage_types, options.quota_types, options.storage_key,
  616. base::Time(), base::Time::Max(),
  617. base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
  618. std::move(promise)));
  619. return handle;
  620. }
  621. void Session::FlushStorageData() {
  622. auto* storage_partition = browser_context()->GetStoragePartition(nullptr);
  623. storage_partition->Flush();
  624. }
  625. v8::Local<v8::Promise> Session::SetProxy(gin::Arguments* args) {
  626. v8::Isolate* isolate = args->isolate();
  627. gin_helper::Promise<void> promise(isolate);
  628. v8::Local<v8::Promise> handle = promise.GetHandle();
  629. gin_helper::Dictionary options;
  630. args->GetNext(&options);
  631. if (!browser_context_->in_memory_pref_store()) {
  632. promise.Resolve();
  633. return handle;
  634. }
  635. std::string mode, proxy_rules, bypass_list, pac_url;
  636. options.Get("pacScript", &pac_url);
  637. options.Get("proxyRules", &proxy_rules);
  638. options.Get("proxyBypassRules", &bypass_list);
  639. ProxyPrefs::ProxyMode proxy_mode = ProxyPrefs::MODE_FIXED_SERVERS;
  640. if (!options.Get("mode", &mode)) {
  641. // pacScript takes precedence over proxyRules.
  642. if (!pac_url.empty()) {
  643. proxy_mode = ProxyPrefs::MODE_PAC_SCRIPT;
  644. } else {
  645. proxy_mode = ProxyPrefs::MODE_FIXED_SERVERS;
  646. }
  647. } else {
  648. if (!ProxyPrefs::StringToProxyMode(mode, &proxy_mode)) {
  649. promise.RejectWithErrorMessage(
  650. "Invalid mode, must be one of direct, auto_detect, pac_script, "
  651. "fixed_servers or system");
  652. return handle;
  653. }
  654. }
  655. browser_context_->in_memory_pref_store()->SetValue(
  656. proxy_config::prefs::kProxy,
  657. base::Value{
  658. createProxyConfig(proxy_mode, pac_url, proxy_rules, bypass_list)},
  659. WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
  660. base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
  661. FROM_HERE, base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
  662. std::move(promise)));
  663. return handle;
  664. }
  665. v8::Local<v8::Promise> Session::ForceReloadProxyConfig() {
  666. gin_helper::Promise<void> promise(isolate_);
  667. auto handle = promise.GetHandle();
  668. browser_context_->GetDefaultStoragePartition()
  669. ->GetNetworkContext()
  670. ->ForceReloadProxyConfig(base::BindOnce(
  671. gin_helper::Promise<void>::ResolvePromise, std::move(promise)));
  672. return handle;
  673. }
  674. void Session::SetDownloadPath(const base::FilePath& path) {
  675. browser_context_->prefs()->SetFilePath(prefs::kDownloadDefaultDirectory,
  676. path);
  677. }
  678. void Session::EnableNetworkEmulation(const gin_helper::Dictionary& options) {
  679. auto conditions = network::mojom::NetworkConditions::New();
  680. options.Get("offline", &conditions->offline);
  681. options.Get("downloadThroughput", &conditions->download_throughput);
  682. options.Get("uploadThroughput", &conditions->upload_throughput);
  683. double latency = 0.0;
  684. if (options.Get("latency", &latency) && latency) {
  685. conditions->latency = base::Milliseconds(latency);
  686. }
  687. auto* network_context =
  688. browser_context_->GetDefaultStoragePartition()->GetNetworkContext();
  689. network_context->SetNetworkConditions(network_emulation_token_,
  690. std::move(conditions));
  691. }
  692. void Session::DisableNetworkEmulation() {
  693. auto* network_context =
  694. browser_context_->GetDefaultStoragePartition()->GetNetworkContext();
  695. network_context->SetNetworkConditions(
  696. network_emulation_token_, network::mojom::NetworkConditions::New());
  697. }
  698. void Session::SetCertVerifyProc(v8::Local<v8::Value> val,
  699. gin::Arguments* args) {
  700. CertVerifierClient::CertVerifyProc proc;
  701. if (!(val->IsNull() || gin::ConvertFromV8(args->isolate(), val, &proc))) {
  702. args->ThrowTypeError("Must pass null or function");
  703. return;
  704. }
  705. mojo::PendingRemote<network::mojom::CertVerifierClient>
  706. cert_verifier_client_remote;
  707. if (proc) {
  708. mojo::MakeSelfOwnedReceiver(
  709. std::make_unique<CertVerifierClient>(proc),
  710. cert_verifier_client_remote.InitWithNewPipeAndPassReceiver());
  711. }
  712. browser_context_->GetDefaultStoragePartition()
  713. ->GetNetworkContext()
  714. ->SetCertVerifierClient(std::move(cert_verifier_client_remote));
  715. }
  716. void Session::SetPermissionRequestHandler(v8::Local<v8::Value> val,
  717. gin::Arguments* args) {
  718. auto* permission_manager = static_cast<ElectronPermissionManager*>(
  719. browser_context()->GetPermissionControllerDelegate());
  720. if (val->IsNull()) {
  721. permission_manager->SetPermissionRequestHandler(
  722. ElectronPermissionManager::RequestHandler());
  723. return;
  724. }
  725. auto handler = std::make_unique<ElectronPermissionManager::RequestHandler>();
  726. if (!gin::ConvertFromV8(args->isolate(), val, handler.get())) {
  727. args->ThrowTypeError("Must pass null or function");
  728. return;
  729. }
  730. permission_manager->SetPermissionRequestHandler(base::BindRepeating(
  731. [](ElectronPermissionManager::RequestHandler* handler,
  732. content::WebContents* web_contents,
  733. blink::PermissionType permission_type,
  734. ElectronPermissionManager::StatusCallback callback,
  735. const base::Value& details) {
  736. handler->Run(web_contents, permission_type, std::move(callback),
  737. details);
  738. },
  739. base::Owned(std::move(handler))));
  740. }
  741. void Session::SetPermissionCheckHandler(v8::Local<v8::Value> val,
  742. gin::Arguments* args) {
  743. ElectronPermissionManager::CheckHandler handler;
  744. if (!(val->IsNull() || gin::ConvertFromV8(args->isolate(), val, &handler))) {
  745. args->ThrowTypeError("Must pass null or function");
  746. return;
  747. }
  748. auto* permission_manager = static_cast<ElectronPermissionManager*>(
  749. browser_context()->GetPermissionControllerDelegate());
  750. permission_manager->SetPermissionCheckHandler(handler);
  751. }
  752. void Session::SetDisplayMediaRequestHandler(v8::Isolate* isolate,
  753. v8::Local<v8::Value> val) {
  754. if (val->IsNull()) {
  755. browser_context_->SetDisplayMediaRequestHandler(
  756. DisplayMediaRequestHandler());
  757. return;
  758. }
  759. DisplayMediaRequestHandler handler;
  760. if (!gin::ConvertFromV8(isolate, val, &handler)) {
  761. gin_helper::ErrorThrower(isolate).ThrowTypeError(
  762. "Display media request handler must be null or a function");
  763. return;
  764. }
  765. browser_context_->SetDisplayMediaRequestHandler(handler);
  766. }
  767. void Session::SetDevicePermissionHandler(v8::Local<v8::Value> val,
  768. gin::Arguments* args) {
  769. ElectronPermissionManager::DeviceCheckHandler handler;
  770. if (!(val->IsNull() || gin::ConvertFromV8(args->isolate(), val, &handler))) {
  771. args->ThrowTypeError("Must pass null or function");
  772. return;
  773. }
  774. auto* permission_manager = static_cast<ElectronPermissionManager*>(
  775. browser_context()->GetPermissionControllerDelegate());
  776. permission_manager->SetDevicePermissionHandler(handler);
  777. }
  778. void Session::SetUSBProtectedClassesHandler(v8::Local<v8::Value> val,
  779. gin::Arguments* args) {
  780. ElectronPermissionManager::ProtectedUSBHandler handler;
  781. if (!(val->IsNull() || gin::ConvertFromV8(args->isolate(), val, &handler))) {
  782. args->ThrowTypeError("Must pass null or function");
  783. return;
  784. }
  785. auto* permission_manager = static_cast<ElectronPermissionManager*>(
  786. browser_context()->GetPermissionControllerDelegate());
  787. permission_manager->SetProtectedUSBHandler(handler);
  788. }
  789. void Session::SetBluetoothPairingHandler(v8::Local<v8::Value> val,
  790. gin::Arguments* args) {
  791. ElectronPermissionManager::BluetoothPairingHandler handler;
  792. if (!(val->IsNull() || gin::ConvertFromV8(args->isolate(), val, &handler))) {
  793. args->ThrowTypeError("Must pass null or function");
  794. return;
  795. }
  796. auto* permission_manager = static_cast<ElectronPermissionManager*>(
  797. browser_context()->GetPermissionControllerDelegate());
  798. permission_manager->SetBluetoothPairingHandler(handler);
  799. }
  800. v8::Local<v8::Promise> Session::ClearHostResolverCache(gin::Arguments* args) {
  801. v8::Isolate* isolate = args->isolate();
  802. gin_helper::Promise<void> promise(isolate);
  803. v8::Local<v8::Promise> handle = promise.GetHandle();
  804. browser_context_->GetDefaultStoragePartition()
  805. ->GetNetworkContext()
  806. ->ClearHostCache(nullptr,
  807. base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
  808. std::move(promise)));
  809. return handle;
  810. }
  811. v8::Local<v8::Promise> Session::ClearAuthCache() {
  812. gin_helper::Promise<void> promise(isolate_);
  813. v8::Local<v8::Promise> handle = promise.GetHandle();
  814. browser_context_->GetDefaultStoragePartition()
  815. ->GetNetworkContext()
  816. ->ClearHttpAuthCache(
  817. base::Time(), base::Time::Max(),
  818. nullptr /*mojom::ClearDataFilterPtr*/,
  819. base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
  820. std::move(promise)));
  821. return handle;
  822. }
  823. void Session::AllowNTLMCredentialsForDomains(const std::string& domains) {
  824. auto* command_line = base::CommandLine::ForCurrentProcess();
  825. network::mojom::HttpAuthDynamicParamsPtr auth_dynamic_params =
  826. network::mojom::HttpAuthDynamicParams::New();
  827. auth_dynamic_params->server_allowlist = domains;
  828. auth_dynamic_params->enable_negotiate_port =
  829. command_line->HasSwitch(electron::switches::kEnableAuthNegotiatePort);
  830. auth_dynamic_params->ntlm_v2_enabled =
  831. !command_line->HasSwitch(electron::switches::kDisableNTLMv2);
  832. content::GetNetworkService()->ConfigureHttpAuthPrefs(
  833. std::move(auth_dynamic_params));
  834. }
  835. void Session::SetUserAgent(const std::string& user_agent,
  836. gin::Arguments* args) {
  837. browser_context_->SetUserAgent(user_agent);
  838. auto* network_context =
  839. browser_context_->GetDefaultStoragePartition()->GetNetworkContext();
  840. network_context->SetUserAgent(user_agent);
  841. std::string accept_lang;
  842. if (args->GetNext(&accept_lang)) {
  843. network_context->SetAcceptLanguage(
  844. net::HttpUtil::GenerateAcceptLanguageHeader(accept_lang));
  845. }
  846. }
  847. std::string Session::GetUserAgent() {
  848. return browser_context_->GetUserAgent();
  849. }
  850. void Session::SetSSLConfig(network::mojom::SSLConfigPtr config) {
  851. browser_context_->SetSSLConfig(std::move(config));
  852. }
  853. bool Session::IsPersistent() {
  854. return !browser_context_->IsOffTheRecord();
  855. }
  856. v8::Local<v8::Promise> Session::GetBlobData(v8::Isolate* isolate,
  857. const std::string& uuid) {
  858. gin::Handle<DataPipeHolder> holder = DataPipeHolder::From(isolate, uuid);
  859. if (holder.IsEmpty()) {
  860. gin_helper::Promise<v8::Local<v8::Value>> promise(isolate);
  861. promise.RejectWithErrorMessage("Could not get blob data handle");
  862. return promise.GetHandle();
  863. }
  864. return holder->ReadAll(isolate);
  865. }
  866. void Session::DownloadURL(const GURL& url, gin::Arguments* args) {
  867. std::map<std::string, std::string> headers;
  868. gin_helper::Dictionary options;
  869. if (args->GetNext(&options)) {
  870. if (options.Has("headers") && !options.Get("headers", &headers)) {
  871. args->ThrowTypeError("Invalid value for headers - must be an object");
  872. return;
  873. }
  874. }
  875. auto download_params = std::make_unique<download::DownloadUrlParameters>(
  876. url, MISSING_TRAFFIC_ANNOTATION);
  877. for (const auto& [name, value] : headers) {
  878. download_params->add_request_header(name, value);
  879. }
  880. auto* download_manager = browser_context()->GetDownloadManager();
  881. download_manager->DownloadUrl(std::move(download_params));
  882. }
  883. void Session::CreateInterruptedDownload(const gin_helper::Dictionary& options) {
  884. int64_t offset = 0, length = 0;
  885. double start_time = base::Time::Now().InSecondsFSinceUnixEpoch();
  886. std::string mime_type, last_modified, etag;
  887. base::FilePath path;
  888. std::vector<GURL> url_chain;
  889. options.Get("path", &path);
  890. options.Get("urlChain", &url_chain);
  891. options.Get("mimeType", &mime_type);
  892. options.Get("offset", &offset);
  893. options.Get("length", &length);
  894. options.Get("lastModified", &last_modified);
  895. options.Get("eTag", &etag);
  896. options.Get("startTime", &start_time);
  897. if (path.empty() || url_chain.empty() || length == 0) {
  898. isolate_->ThrowException(v8::Exception::Error(gin::StringToV8(
  899. isolate_, "Must pass non-empty path, urlChain and length.")));
  900. return;
  901. }
  902. if (offset >= length) {
  903. isolate_->ThrowException(v8::Exception::Error(gin::StringToV8(
  904. isolate_, "Must pass an offset value less than length.")));
  905. return;
  906. }
  907. auto* download_manager = browser_context()->GetDownloadManager();
  908. download_manager->GetNextId(base::BindRepeating(
  909. &DownloadIdCallback, download_manager, path, url_chain, mime_type, offset,
  910. length, last_modified, etag,
  911. base::Time::FromSecondsSinceUnixEpoch(start_time)));
  912. }
  913. std::string Session::RegisterPreloadScript(
  914. gin_helper::ErrorThrower thrower,
  915. const PreloadScript& new_preload_script) {
  916. auto* prefs = SessionPreferences::FromBrowserContext(browser_context());
  917. DCHECK(prefs);
  918. auto& preload_scripts = prefs->preload_scripts();
  919. auto it = std::find_if(preload_scripts.begin(), preload_scripts.end(),
  920. [&new_preload_script](const PreloadScript& script) {
  921. return script.id == new_preload_script.id;
  922. });
  923. if (it != preload_scripts.end()) {
  924. thrower.ThrowError(base::StringPrintf(
  925. "Cannot register preload script with existing ID '%s'",
  926. new_preload_script.id.c_str()));
  927. return "";
  928. }
  929. if (!new_preload_script.file_path.IsAbsolute()) {
  930. // Deprecated preload scripts logged error without throwing.
  931. if (new_preload_script.deprecated) {
  932. LOG(ERROR) << "preload script must have absolute path: "
  933. << new_preload_script.file_path;
  934. } else {
  935. thrower.ThrowError(
  936. base::StringPrintf("Preload script must have absolute path: %s",
  937. new_preload_script.file_path.value().c_str()));
  938. return "";
  939. }
  940. }
  941. preload_scripts.push_back(new_preload_script);
  942. return new_preload_script.id;
  943. }
  944. void Session::UnregisterPreloadScript(gin_helper::ErrorThrower thrower,
  945. const std::string& script_id) {
  946. auto* prefs = SessionPreferences::FromBrowserContext(browser_context());
  947. DCHECK(prefs);
  948. auto& preload_scripts = prefs->preload_scripts();
  949. // Find the preload script by its ID
  950. auto it = std::find_if(preload_scripts.begin(), preload_scripts.end(),
  951. [&script_id](const PreloadScript& script) {
  952. return script.id == script_id;
  953. });
  954. // If the script is found, erase it from the vector
  955. if (it != preload_scripts.end()) {
  956. preload_scripts.erase(it);
  957. return;
  958. }
  959. // If the script is not found, throw an error
  960. thrower.ThrowError(base::StringPrintf(
  961. "Cannot unregister preload script with non-existing ID '%s'",
  962. script_id.c_str()));
  963. }
  964. std::vector<PreloadScript> Session::GetPreloadScripts() const {
  965. auto* prefs = SessionPreferences::FromBrowserContext(browser_context());
  966. DCHECK(prefs);
  967. return prefs->preload_scripts();
  968. }
  969. /**
  970. * Exposes the network service's ClearSharedDictionaryCacheForIsolationKey
  971. * method, allowing clearing the Shared Dictionary cache for a given isolation
  972. * key. Details about the feature available at
  973. * https://developer.chrome.com/blog/shared-dictionary-compression
  974. */
  975. v8::Local<v8::Promise> Session::ClearSharedDictionaryCacheForIsolationKey(
  976. const gin_helper::Dictionary& options) {
  977. gin_helper::Promise<void> promise(isolate_);
  978. auto handle = promise.GetHandle();
  979. GURL frame_origin_url, top_frame_site_url;
  980. if (!options.Get("frameOrigin", &frame_origin_url) ||
  981. !options.Get("topFrameSite", &top_frame_site_url)) {
  982. promise.RejectWithErrorMessage(
  983. "Must provide frameOrigin and topFrameSite strings to "
  984. "`clearSharedDictionaryCacheForIsolationKey`");
  985. return handle;
  986. }
  987. if (!frame_origin_url.is_valid() || !top_frame_site_url.is_valid()) {
  988. promise.RejectWithErrorMessage(
  989. "Invalid URLs provided for frameOrigin or topFrameSite");
  990. return handle;
  991. }
  992. url::Origin frame_origin = url::Origin::Create(frame_origin_url);
  993. net::SchemefulSite top_frame_site(top_frame_site_url);
  994. net::SharedDictionaryIsolationKey isolation_key(frame_origin, top_frame_site);
  995. browser_context_->GetDefaultStoragePartition()
  996. ->GetNetworkContext()
  997. ->ClearSharedDictionaryCacheForIsolationKey(
  998. isolation_key,
  999. base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
  1000. std::move(promise)));
  1001. return handle;
  1002. }
  1003. /**
  1004. * Exposes the network service's ClearSharedDictionaryCache
  1005. * method, allowing clearing the Shared Dictionary cache.
  1006. * https://developer.chrome.com/blog/shared-dictionary-compression
  1007. */
  1008. v8::Local<v8::Promise> Session::ClearSharedDictionaryCache() {
  1009. gin_helper::Promise<void> promise(isolate_);
  1010. auto handle = promise.GetHandle();
  1011. browser_context_->GetDefaultStoragePartition()
  1012. ->GetNetworkContext()
  1013. ->ClearSharedDictionaryCache(
  1014. base::Time(), base::Time::Max(),
  1015. nullptr /*mojom::ClearDataFilterPtr*/,
  1016. base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
  1017. std::move(promise)));
  1018. return handle;
  1019. }
  1020. /**
  1021. * Exposes the network service's GetSharedDictionaryInfo method, allowing
  1022. * inspection of Shared Dictionary information. Details about the feature
  1023. * available at https://developer.chrome.com/blog/shared-dictionary-compression
  1024. */
  1025. v8::Local<v8::Promise> Session::GetSharedDictionaryInfo(
  1026. const gin_helper::Dictionary& options) {
  1027. gin_helper::Promise<std::vector<gin_helper::Dictionary>> promise(isolate_);
  1028. auto handle = promise.GetHandle();
  1029. GURL frame_origin_url, top_frame_site_url;
  1030. if (!options.Get("frameOrigin", &frame_origin_url) ||
  1031. !options.Get("topFrameSite", &top_frame_site_url)) {
  1032. promise.RejectWithErrorMessage(
  1033. "Must provide frameOrigin and topFrameSite strings");
  1034. return handle;
  1035. }
  1036. if (!frame_origin_url.is_valid() || !top_frame_site_url.is_valid()) {
  1037. promise.RejectWithErrorMessage(
  1038. "Invalid URLs provided for frameOrigin or topFrameSite");
  1039. return handle;
  1040. }
  1041. url::Origin frame_origin = url::Origin::Create(frame_origin_url);
  1042. net::SchemefulSite top_frame_site(top_frame_site_url);
  1043. net::SharedDictionaryIsolationKey isolation_key(frame_origin, top_frame_site);
  1044. browser_context_->GetDefaultStoragePartition()
  1045. ->GetNetworkContext()
  1046. ->GetSharedDictionaryInfo(
  1047. isolation_key,
  1048. base::BindOnce(
  1049. [](gin_helper::Promise<std::vector<gin_helper::Dictionary>>
  1050. promise,
  1051. std::vector<network::mojom::SharedDictionaryInfoPtr> info) {
  1052. v8::Isolate* isolate = promise.isolate();
  1053. v8::HandleScope handle_scope(isolate);
  1054. std::vector<gin_helper::Dictionary> result;
  1055. result.reserve(info.size());
  1056. for (const auto& item : info) {
  1057. gin_helper::Dictionary dict =
  1058. gin_helper::Dictionary::CreateEmpty(isolate);
  1059. dict.Set("match", item->match);
  1060. // Convert RequestDestination enum values to strings
  1061. std::vector<std::string> destinations;
  1062. for (const auto& dest : item->match_dest) {
  1063. destinations.push_back(
  1064. network::RequestDestinationToString(dest));
  1065. }
  1066. dict.Set("matchDestinations", destinations);
  1067. dict.Set("id", item->id);
  1068. dict.Set("dictionaryUrl", item->dictionary_url.spec());
  1069. dict.Set("lastFetchTime", item->last_fetch_time);
  1070. dict.Set("responseTime", item->response_time);
  1071. dict.Set("expirationDuration",
  1072. item->expiration.InMillisecondsF());
  1073. dict.Set("lastUsedTime", item->last_used_time);
  1074. dict.Set("size", item->size);
  1075. dict.Set("hash", net::HashValue(item->hash).ToString());
  1076. result.push_back(dict);
  1077. }
  1078. promise.Resolve(result);
  1079. },
  1080. std::move(promise)));
  1081. return handle;
  1082. }
  1083. /**
  1084. * Exposes the network service's GetSharedDictionaryUsageInfo method, allowing
  1085. * inspection of Shared Dictionary information. Details about the feature
  1086. * available at https://developer.chrome.com/blog/shared-dictionary-compression
  1087. */
  1088. v8::Local<v8::Promise> Session::GetSharedDictionaryUsageInfo() {
  1089. gin_helper::Promise<std::vector<gin_helper::Dictionary>> promise(isolate_);
  1090. auto handle = promise.GetHandle();
  1091. browser_context_->GetDefaultStoragePartition()
  1092. ->GetNetworkContext()
  1093. ->GetSharedDictionaryUsageInfo(base::BindOnce(
  1094. [](gin_helper::Promise<std::vector<gin_helper::Dictionary>> promise,
  1095. const std::vector<net::SharedDictionaryUsageInfo>& info) {
  1096. v8::Isolate* isolate = promise.isolate();
  1097. v8::HandleScope handle_scope(isolate);
  1098. std::vector<gin_helper::Dictionary> result;
  1099. result.reserve(info.size());
  1100. for (const auto& item : info) {
  1101. gin_helper::Dictionary dict =
  1102. gin_helper::Dictionary::CreateEmpty(isolate);
  1103. dict.Set("frameOrigin",
  1104. item.isolation_key.frame_origin().Serialize());
  1105. dict.Set("topFrameSite",
  1106. item.isolation_key.top_frame_site().Serialize());
  1107. dict.Set("totalSizeBytes", item.total_size_bytes);
  1108. result.push_back(dict);
  1109. }
  1110. promise.Resolve(result);
  1111. },
  1112. std::move(promise)));
  1113. return handle;
  1114. }
  1115. v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
  1116. if (cookies_.IsEmpty()) {
  1117. auto handle = Cookies::Create(isolate, browser_context());
  1118. cookies_.Reset(isolate, handle.ToV8());
  1119. }
  1120. return cookies_.Get(isolate);
  1121. }
  1122. v8::Local<v8::Value> Session::Extensions(v8::Isolate* isolate) {
  1123. #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
  1124. if (extensions_.IsEmpty()) {
  1125. v8::Local<v8::Value> handle;
  1126. handle = Extensions::Create(isolate, browser_context()).ToV8();
  1127. extensions_.Reset(isolate, handle);
  1128. }
  1129. #endif
  1130. return extensions_.Get(isolate);
  1131. }
  1132. v8::Local<v8::Value> Session::Protocol(v8::Isolate* isolate) {
  1133. return protocol_.Get(isolate);
  1134. }
  1135. v8::Local<v8::Value> Session::ServiceWorkerContext(v8::Isolate* isolate) {
  1136. if (service_worker_context_.IsEmpty()) {
  1137. v8::Local<v8::Value> handle;
  1138. handle = ServiceWorkerContext::Create(isolate, browser_context()).ToV8();
  1139. service_worker_context_.Reset(isolate, handle);
  1140. }
  1141. return service_worker_context_.Get(isolate);
  1142. }
  1143. v8::Local<v8::Value> Session::WebRequest(v8::Isolate* isolate) {
  1144. if (web_request_.IsEmpty()) {
  1145. auto handle = WebRequest::Create(isolate, browser_context());
  1146. web_request_.Reset(isolate, handle.ToV8());
  1147. }
  1148. return web_request_.Get(isolate);
  1149. }
  1150. v8::Local<v8::Value> Session::NetLog(v8::Isolate* isolate) {
  1151. if (net_log_.IsEmpty()) {
  1152. auto handle = NetLog::Create(isolate, browser_context());
  1153. net_log_.Reset(isolate, handle.ToV8());
  1154. }
  1155. return net_log_.Get(isolate);
  1156. }
  1157. static void StartPreconnectOnUI(ElectronBrowserContext* browser_context,
  1158. const GURL& url,
  1159. int num_sockets_to_preconnect) {
  1160. url::Origin origin = url::Origin::Create(url);
  1161. std::vector<predictors::PreconnectRequest> requests = {
  1162. {url::Origin::Create(url), num_sockets_to_preconnect,
  1163. net::NetworkAnonymizationKey::CreateSameSite(
  1164. net::SchemefulSite(origin))}};
  1165. browser_context->GetPreconnectManager()->Start(url, requests);
  1166. }
  1167. void Session::Preconnect(const gin_helper::Dictionary& options,
  1168. gin::Arguments* args) {
  1169. GURL url;
  1170. if (!options.Get("url", &url) || !url.is_valid()) {
  1171. args->ThrowTypeError(
  1172. "Must pass non-empty valid url to session.preconnect.");
  1173. return;
  1174. }
  1175. int num_sockets_to_preconnect = 1;
  1176. if (options.Get("numSockets", &num_sockets_to_preconnect)) {
  1177. const int kMinSocketsToPreconnect = 1;
  1178. const int kMaxSocketsToPreconnect = 6;
  1179. if (num_sockets_to_preconnect < kMinSocketsToPreconnect ||
  1180. num_sockets_to_preconnect > kMaxSocketsToPreconnect) {
  1181. args->ThrowTypeError(
  1182. absl::StrFormat("numSocketsToPreconnect is outside range [%d,%d]",
  1183. kMinSocketsToPreconnect, kMaxSocketsToPreconnect));
  1184. return;
  1185. }
  1186. }
  1187. DCHECK_GT(num_sockets_to_preconnect, 0);
  1188. content::GetUIThreadTaskRunner({})->PostTask(
  1189. FROM_HERE,
  1190. base::BindOnce(&StartPreconnectOnUI, base::Unretained(browser_context()),
  1191. url, num_sockets_to_preconnect));
  1192. }
  1193. v8::Local<v8::Promise> Session::CloseAllConnections() {
  1194. gin_helper::Promise<void> promise(isolate_);
  1195. auto handle = promise.GetHandle();
  1196. browser_context_->GetDefaultStoragePartition()
  1197. ->GetNetworkContext()
  1198. ->CloseAllConnections(base::BindOnce(
  1199. gin_helper::Promise<void>::ResolvePromise, std::move(promise)));
  1200. return handle;
  1201. }
  1202. v8::Local<v8::Value> Session::GetPath(v8::Isolate* isolate) {
  1203. if (browser_context_->IsOffTheRecord()) {
  1204. return v8::Null(isolate);
  1205. }
  1206. return gin::ConvertToV8(isolate, browser_context_->GetPath());
  1207. }
  1208. void Session::SetCodeCachePath(gin::Arguments* args) {
  1209. base::FilePath code_cache_path;
  1210. auto* storage_partition = browser_context_->GetDefaultStoragePartition();
  1211. auto* code_cache_context = storage_partition->GetGeneratedCodeCacheContext();
  1212. if (code_cache_context) {
  1213. if (!args->GetNext(&code_cache_path) || !code_cache_path.IsAbsolute()) {
  1214. args->ThrowTypeError(
  1215. "Absolute path must be provided to store code cache.");
  1216. return;
  1217. }
  1218. code_cache_context->Initialize(
  1219. code_cache_path, 0 /* allows disk_cache to choose the size */);
  1220. }
  1221. }
  1222. v8::Local<v8::Promise> Session::ClearCodeCaches(
  1223. const gin_helper::Dictionary& options) {
  1224. auto* isolate = JavascriptEnvironment::GetIsolate();
  1225. gin_helper::Promise<void> promise(isolate);
  1226. v8::Local<v8::Promise> handle = promise.GetHandle();
  1227. std::set<GURL> url_list;
  1228. base::RepeatingCallback<bool(const GURL&)> url_matcher = base::NullCallback();
  1229. if (options.Get("urls", &url_list) && !url_list.empty()) {
  1230. url_matcher = base::BindRepeating(
  1231. [](const std::set<GURL>& url_list, const GURL& url) {
  1232. return url_list.contains(url);
  1233. },
  1234. url_list);
  1235. }
  1236. browser_context_->GetDefaultStoragePartition()->ClearCodeCaches(
  1237. base::Time(), base::Time::Max(), url_matcher,
  1238. base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
  1239. std::move(promise)));
  1240. return handle;
  1241. }
  1242. v8::Local<v8::Value> Session::ClearData(gin_helper::ErrorThrower thrower,
  1243. gin::Arguments* args) {
  1244. auto* isolate = JavascriptEnvironment::GetIsolate();
  1245. BrowsingDataRemover::DataType data_type_mask = kClearDataTypeAll;
  1246. std::vector<url::Origin> origins;
  1247. BrowsingDataFilterBuilder::OriginMatchingMode origin_matching_mode =
  1248. BrowsingDataFilterBuilder::OriginMatchingMode::kThirdPartiesIncluded;
  1249. BrowsingDataFilterBuilder::Mode filter_mode =
  1250. BrowsingDataFilterBuilder::Mode::kPreserve;
  1251. if (gin_helper::Dictionary options; args->GetNext(&options)) {
  1252. if (std::vector<std::string> data_types;
  1253. options.Get("dataTypes", &data_types)) {
  1254. data_type_mask = GetDataTypeMask(data_types);
  1255. }
  1256. if (bool avoid_closing_connections;
  1257. options.Get("avoidClosingConnections", &avoid_closing_connections) &&
  1258. avoid_closing_connections) {
  1259. data_type_mask |=
  1260. BrowsingDataRemover::DATA_TYPE_AVOID_CLOSING_CONNECTIONS;
  1261. }
  1262. std::vector<GURL> origin_urls;
  1263. {
  1264. bool has_origins_key = options.Get("origins", &origin_urls);
  1265. std::vector<GURL> exclude_origin_urls;
  1266. bool has_exclude_origins_key =
  1267. options.Get("excludeOrigins", &exclude_origin_urls);
  1268. if (has_origins_key && has_exclude_origins_key) {
  1269. thrower.ThrowError(
  1270. "Cannot provide both 'origins' and 'excludeOrigins'");
  1271. return v8::Undefined(isolate);
  1272. }
  1273. if (has_origins_key) {
  1274. filter_mode = BrowsingDataFilterBuilder::Mode::kDelete;
  1275. } else if (has_exclude_origins_key) {
  1276. origin_urls = std::move(exclude_origin_urls);
  1277. }
  1278. }
  1279. if (!origin_urls.empty()) {
  1280. origins.reserve(origin_urls.size());
  1281. for (const GURL& origin_url : origin_urls) {
  1282. auto origin = url::Origin::Create(origin_url);
  1283. // Opaque origins cannot be used with this API
  1284. if (origin.opaque()) {
  1285. thrower.ThrowError(
  1286. absl::StrFormat("Invalid origin: '%s'",
  1287. origin_url.possibly_invalid_spec().c_str()));
  1288. return v8::Undefined(isolate);
  1289. }
  1290. origins.push_back(std::move(origin));
  1291. }
  1292. }
  1293. if (std::string origin_matching_mode_string;
  1294. options.Get("originMatchingMode", &origin_matching_mode_string)) {
  1295. if (origin_matching_mode_string == "third-parties-included") {
  1296. origin_matching_mode = BrowsingDataFilterBuilder::OriginMatchingMode::
  1297. kThirdPartiesIncluded;
  1298. } else if (origin_matching_mode_string == "origin-in-all-contexts") {
  1299. origin_matching_mode =
  1300. BrowsingDataFilterBuilder::OriginMatchingMode::kOriginInAllContexts;
  1301. }
  1302. }
  1303. }
  1304. gin_helper::Promise<void> promise(isolate);
  1305. v8::Local<v8::Promise> promise_handle = promise.GetHandle();
  1306. BrowsingDataRemover* remover = browser_context_->GetBrowsingDataRemover();
  1307. ClearDataTask::Run(remover, std::move(promise), data_type_mask,
  1308. std::move(origins), filter_mode, origin_matching_mode);
  1309. return promise_handle;
  1310. }
  1311. #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
  1312. base::Value Session::GetSpellCheckerLanguages() {
  1313. return browser_context_->prefs()
  1314. ->GetValue(spellcheck::prefs::kSpellCheckDictionaries)
  1315. .Clone();
  1316. }
  1317. void Session::SetSpellCheckerLanguages(
  1318. gin_helper::ErrorThrower thrower,
  1319. const std::vector<std::string>& languages) {
  1320. #if !BUILDFLAG(IS_MAC)
  1321. base::Value::List language_codes;
  1322. for (const std::string& lang : languages) {
  1323. std::string code = spellcheck::GetCorrespondingSpellCheckLanguage(lang);
  1324. if (code.empty()) {
  1325. thrower.ThrowError("Invalid language code provided: \"" + lang +
  1326. "\" is not a valid language code");
  1327. return;
  1328. }
  1329. language_codes.Append(code);
  1330. }
  1331. browser_context_->prefs()->Set(spellcheck::prefs::kSpellCheckDictionaries,
  1332. base::Value(std::move(language_codes)));
  1333. // Enable spellcheck if > 0 languages, disable if no languages set
  1334. browser_context_->prefs()->SetBoolean(spellcheck::prefs::kSpellCheckEnable,
  1335. !languages.empty());
  1336. #endif
  1337. }
  1338. void SetSpellCheckerDictionaryDownloadURL(gin_helper::ErrorThrower thrower,
  1339. const GURL& url) {
  1340. #if !BUILDFLAG(IS_MAC)
  1341. if (!url.is_valid()) {
  1342. thrower.ThrowError(
  1343. "The URL you provided to setSpellCheckerDictionaryDownloadURL is not a "
  1344. "valid URL");
  1345. return;
  1346. }
  1347. SpellcheckHunspellDictionary::SetBaseDownloadURL(url);
  1348. #endif
  1349. }
  1350. v8::Local<v8::Promise> Session::ListWordsInSpellCheckerDictionary() {
  1351. gin_helper::Promise<std::set<std::string>> promise(isolate_);
  1352. v8::Local<v8::Promise> handle = promise.GetHandle();
  1353. SpellcheckService* spellcheck =
  1354. SpellcheckServiceFactory::GetForContext(browser_context());
  1355. if (!spellcheck) {
  1356. promise.RejectWithErrorMessage(
  1357. "Spellcheck in unexpected state: failed to load custom dictionary.");
  1358. return handle;
  1359. }
  1360. if (spellcheck->GetCustomDictionary()->IsLoaded()) {
  1361. promise.Resolve(spellcheck->GetCustomDictionary()->GetWords());
  1362. } else {
  1363. new DictionaryObserver(std::move(promise), spellcheck->GetWeakPtr());
  1364. // Dictionary loads by default asynchronously,
  1365. // call the load function anyways just to be sure.
  1366. spellcheck->GetCustomDictionary()->Load();
  1367. }
  1368. return handle;
  1369. }
  1370. bool Session::AddWordToSpellCheckerDictionary(const std::string& word) {
  1371. // don't let in-memory sessions add spellchecker words
  1372. // because files will persist unintentionally
  1373. bool is_in_memory = browser_context_->IsOffTheRecord();
  1374. if (is_in_memory)
  1375. return false;
  1376. SpellcheckService* service =
  1377. SpellcheckServiceFactory::GetForContext(browser_context());
  1378. if (!service)
  1379. return false;
  1380. #if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
  1381. if (spellcheck::UseBrowserSpellChecker()) {
  1382. spellcheck_platform::AddWord(service->platform_spell_checker(),
  1383. base::UTF8ToUTF16(word));
  1384. }
  1385. #endif
  1386. return service->GetCustomDictionary()->AddWord(word);
  1387. }
  1388. bool Session::RemoveWordFromSpellCheckerDictionary(const std::string& word) {
  1389. // don't let in-memory sessions remove spellchecker words
  1390. // because files will persist unintentionally
  1391. bool is_in_memory = browser_context_->IsOffTheRecord();
  1392. if (is_in_memory)
  1393. return false;
  1394. SpellcheckService* service =
  1395. SpellcheckServiceFactory::GetForContext(browser_context());
  1396. if (!service)
  1397. return false;
  1398. #if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
  1399. if (spellcheck::UseBrowserSpellChecker()) {
  1400. spellcheck_platform::RemoveWord(service->platform_spell_checker(),
  1401. base::UTF8ToUTF16(word));
  1402. }
  1403. #endif
  1404. return service->GetCustomDictionary()->RemoveWord(word);
  1405. }
  1406. void Session::SetSpellCheckerEnabled(bool b) {
  1407. browser_context_->prefs()->SetBoolean(spellcheck::prefs::kSpellCheckEnable,
  1408. b);
  1409. }
  1410. bool Session::IsSpellCheckerEnabled() const {
  1411. return browser_context_->prefs()->GetBoolean(
  1412. spellcheck::prefs::kSpellCheckEnable);
  1413. }
  1414. #endif // BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
  1415. // static
  1416. Session* Session::FromBrowserContext(content::BrowserContext* context) {
  1417. auto* data =
  1418. static_cast<UserDataLink*>(context->GetUserData(kElectronApiSessionKey));
  1419. return data ? data->session.get() : nullptr;
  1420. }
  1421. // static
  1422. gin::Handle<Session> Session::CreateFrom(
  1423. v8::Isolate* isolate,
  1424. ElectronBrowserContext* browser_context) {
  1425. Session* existing = FromBrowserContext(browser_context);
  1426. if (existing)
  1427. return gin::CreateHandle(isolate, existing);
  1428. auto handle =
  1429. gin::CreateHandle(isolate, new Session(isolate, browser_context));
  1430. // The Sessions should never be garbage collected, since the common pattern is
  1431. // to use partition strings, instead of using the Session object directly.
  1432. handle->Pin(isolate);
  1433. v8::TryCatch try_catch(isolate);
  1434. gin_helper::CallMethod(isolate, handle.get(), "_init");
  1435. if (try_catch.HasCaught()) {
  1436. node::errors::TriggerUncaughtException(isolate, try_catch);
  1437. }
  1438. App::Get()->EmitWithoutEvent("session-created", handle);
  1439. return handle;
  1440. }
  1441. // static
  1442. gin::Handle<Session> Session::FromPartition(v8::Isolate* isolate,
  1443. const std::string& partition,
  1444. base::Value::Dict options) {
  1445. ElectronBrowserContext* browser_context;
  1446. if (partition.empty()) {
  1447. browser_context =
  1448. ElectronBrowserContext::From("", false, std::move(options));
  1449. } else if (partition.starts_with(kPersistPrefix)) {
  1450. std::string name = partition.substr(8);
  1451. browser_context =
  1452. ElectronBrowserContext::From(name, false, std::move(options));
  1453. } else {
  1454. browser_context =
  1455. ElectronBrowserContext::From(partition, true, std::move(options));
  1456. }
  1457. return CreateFrom(isolate, browser_context);
  1458. }
  1459. // static
  1460. std::optional<gin::Handle<Session>> Session::FromPath(
  1461. v8::Isolate* isolate,
  1462. const base::FilePath& path,
  1463. base::Value::Dict options) {
  1464. ElectronBrowserContext* browser_context;
  1465. if (path.empty()) {
  1466. gin_helper::Promise<v8::Local<v8::Value>> promise(isolate);
  1467. promise.RejectWithErrorMessage("An empty path was specified");
  1468. return std::nullopt;
  1469. }
  1470. if (!path.IsAbsolute()) {
  1471. gin_helper::Promise<v8::Local<v8::Value>> promise(isolate);
  1472. promise.RejectWithErrorMessage("An absolute path was not provided");
  1473. return std::nullopt;
  1474. }
  1475. browser_context =
  1476. ElectronBrowserContext::FromPath(std::move(path), std::move(options));
  1477. return CreateFrom(isolate, browser_context);
  1478. }
  1479. // static
  1480. gin::Handle<Session> Session::New() {
  1481. gin_helper::ErrorThrower(JavascriptEnvironment::GetIsolate())
  1482. .ThrowError("Session objects cannot be created with 'new'");
  1483. return {};
  1484. }
  1485. void Session::FillObjectTemplate(v8::Isolate* isolate,
  1486. v8::Local<v8::ObjectTemplate> templ) {
  1487. gin::ObjectTemplateBuilder(isolate, GetClassName(), templ)
  1488. .SetMethod("resolveHost", &Session::ResolveHost)
  1489. .SetMethod("resolveProxy", &Session::ResolveProxy)
  1490. .SetMethod("getCacheSize", &Session::GetCacheSize)
  1491. .SetMethod("clearCache", &Session::ClearCache)
  1492. .SetMethod("clearStorageData", &Session::ClearStorageData)
  1493. .SetMethod("flushStorageData", &Session::FlushStorageData)
  1494. .SetMethod("setProxy", &Session::SetProxy)
  1495. .SetMethod("forceReloadProxyConfig", &Session::ForceReloadProxyConfig)
  1496. .SetMethod("setDownloadPath", &Session::SetDownloadPath)
  1497. .SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation)
  1498. .SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation)
  1499. .SetMethod("setCertificateVerifyProc", &Session::SetCertVerifyProc)
  1500. .SetMethod("setPermissionRequestHandler",
  1501. &Session::SetPermissionRequestHandler)
  1502. .SetMethod("setPermissionCheckHandler",
  1503. &Session::SetPermissionCheckHandler)
  1504. .SetMethod("_setDisplayMediaRequestHandler",
  1505. &Session::SetDisplayMediaRequestHandler)
  1506. .SetMethod("setDevicePermissionHandler",
  1507. &Session::SetDevicePermissionHandler)
  1508. .SetMethod("setUSBProtectedClassesHandler",
  1509. &Session::SetUSBProtectedClassesHandler)
  1510. .SetMethod("setBluetoothPairingHandler",
  1511. &Session::SetBluetoothPairingHandler)
  1512. .SetMethod("clearHostResolverCache", &Session::ClearHostResolverCache)
  1513. .SetMethod("clearAuthCache", &Session::ClearAuthCache)
  1514. .SetMethod("allowNTLMCredentialsForDomains",
  1515. &Session::AllowNTLMCredentialsForDomains)
  1516. .SetMethod("isPersistent", &Session::IsPersistent)
  1517. .SetMethod("setUserAgent", &Session::SetUserAgent)
  1518. .SetMethod("getUserAgent", &Session::GetUserAgent)
  1519. .SetMethod("setSSLConfig", &Session::SetSSLConfig)
  1520. .SetMethod("getBlobData", &Session::GetBlobData)
  1521. .SetMethod("downloadURL", &Session::DownloadURL)
  1522. .SetMethod("createInterruptedDownload",
  1523. &Session::CreateInterruptedDownload)
  1524. .SetMethod("registerPreloadScript", &Session::RegisterPreloadScript)
  1525. .SetMethod("unregisterPreloadScript", &Session::UnregisterPreloadScript)
  1526. .SetMethod("getPreloadScripts", &Session::GetPreloadScripts)
  1527. .SetMethod("getSharedDictionaryUsageInfo",
  1528. &Session::GetSharedDictionaryUsageInfo)
  1529. .SetMethod("getSharedDictionaryInfo", &Session::GetSharedDictionaryInfo)
  1530. .SetMethod("clearSharedDictionaryCache",
  1531. &Session::ClearSharedDictionaryCache)
  1532. .SetMethod("clearSharedDictionaryCacheForIsolationKey",
  1533. &Session::ClearSharedDictionaryCacheForIsolationKey)
  1534. #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
  1535. .SetMethod("getSpellCheckerLanguages", &Session::GetSpellCheckerLanguages)
  1536. .SetMethod("setSpellCheckerLanguages", &Session::SetSpellCheckerLanguages)
  1537. .SetProperty("availableSpellCheckerLanguages",
  1538. &spellcheck::SpellCheckLanguages)
  1539. .SetMethod("setSpellCheckerDictionaryDownloadURL",
  1540. &SetSpellCheckerDictionaryDownloadURL)
  1541. .SetMethod("listWordsInSpellCheckerDictionary",
  1542. &Session::ListWordsInSpellCheckerDictionary)
  1543. .SetMethod("addWordToSpellCheckerDictionary",
  1544. &Session::AddWordToSpellCheckerDictionary)
  1545. .SetMethod("removeWordFromSpellCheckerDictionary",
  1546. &Session::RemoveWordFromSpellCheckerDictionary)
  1547. .SetMethod("setSpellCheckerEnabled", &Session::SetSpellCheckerEnabled)
  1548. .SetMethod("isSpellCheckerEnabled", &Session::IsSpellCheckerEnabled)
  1549. .SetProperty("spellCheckerEnabled", &Session::IsSpellCheckerEnabled,
  1550. &Session::SetSpellCheckerEnabled)
  1551. #endif
  1552. .SetMethod("preconnect", &Session::Preconnect)
  1553. .SetMethod("closeAllConnections", &Session::CloseAllConnections)
  1554. .SetMethod("getStoragePath", &Session::GetPath)
  1555. .SetMethod("setCodeCachePath", &Session::SetCodeCachePath)
  1556. .SetMethod("clearCodeCaches", &Session::ClearCodeCaches)
  1557. .SetMethod("clearData", &Session::ClearData)
  1558. .SetProperty("cookies", &Session::Cookies)
  1559. .SetProperty("extensions", &Session::Extensions)
  1560. .SetProperty("netLog", &Session::NetLog)
  1561. .SetProperty("protocol", &Session::Protocol)
  1562. .SetProperty("serviceWorkers", &Session::ServiceWorkerContext)
  1563. .SetProperty("webRequest", &Session::WebRequest)
  1564. .SetProperty("storagePath", &Session::GetPath)
  1565. .Build();
  1566. }
  1567. const char* Session::GetTypeName() {
  1568. return GetClassName();
  1569. }
  1570. void Session::WillBeDestroyed() {
  1571. ClearWeak();
  1572. }
  1573. } // namespace electron::api
  1574. namespace {
  1575. using electron::api::Session;
  1576. v8::Local<v8::Value> FromPartition(const std::string& partition,
  1577. gin::Arguments* args) {
  1578. if (!electron::Browser::Get()->is_ready()) {
  1579. args->ThrowTypeError("Session can only be received when app is ready");
  1580. return v8::Null(args->isolate());
  1581. }
  1582. base::Value::Dict options;
  1583. args->GetNext(&options);
  1584. return Session::FromPartition(args->isolate(), partition, std::move(options))
  1585. .ToV8();
  1586. }
  1587. v8::Local<v8::Value> FromPath(const base::FilePath& path,
  1588. gin::Arguments* args) {
  1589. if (!electron::Browser::Get()->is_ready()) {
  1590. args->ThrowTypeError("Session can only be received when app is ready");
  1591. return v8::Null(args->isolate());
  1592. }
  1593. base::Value::Dict options;
  1594. args->GetNext(&options);
  1595. std::optional<gin::Handle<Session>> session_handle =
  1596. Session::FromPath(args->isolate(), path, std::move(options));
  1597. if (session_handle)
  1598. return session_handle.value().ToV8();
  1599. else
  1600. return v8::Null(args->isolate());
  1601. }
  1602. void Initialize(v8::Local<v8::Object> exports,
  1603. v8::Local<v8::Value> unused,
  1604. v8::Local<v8::Context> context,
  1605. void* priv) {
  1606. v8::Isolate* isolate = context->GetIsolate();
  1607. gin_helper::Dictionary dict(isolate, exports);
  1608. dict.Set("Session", Session::GetConstructor(context));
  1609. dict.SetMethod("fromPartition", &FromPartition);
  1610. dict.SetMethod("fromPath", &FromPath);
  1611. }
  1612. } // namespace
  1613. NODE_LINKED_BINDING_CONTEXT_AWARE(electron_browser_session, Initialize)