electron_api_web_frame_main.cc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. // Copyright (c) 2020 Samuel Maddock <[email protected]>.
  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_frame_main.h"
  5. #include <string>
  6. #include <unordered_map>
  7. #include <utility>
  8. #include <vector>
  9. #include "base/feature_list.h"
  10. #include "base/logging.h"
  11. #include "base/no_destructor.h"
  12. #include "content/browser/renderer_host/render_frame_host_impl.h" // nogncheck
  13. #include "content/browser/renderer_host/render_process_host_impl.h" // nogncheck
  14. #include "content/public/browser/frame_tree_node_id.h"
  15. #include "content/public/browser/render_frame_host.h"
  16. #include "content/public/common/isolated_world_ids.h"
  17. #include "gin/handle.h"
  18. #include "gin/object_template_builder.h"
  19. #include "services/service_manager/public/cpp/interface_provider.h"
  20. #include "shell/browser/api/message_port.h"
  21. #include "shell/browser/browser.h"
  22. #include "shell/browser/javascript_environment.h"
  23. #include "shell/common/api/api.mojom.h"
  24. #include "shell/common/gin_converters/blink_converter.h"
  25. #include "shell/common/gin_converters/frame_converter.h"
  26. #include "shell/common/gin_converters/gurl_converter.h"
  27. #include "shell/common/gin_converters/std_converter.h"
  28. #include "shell/common/gin_converters/value_converter.h"
  29. #include "shell/common/gin_helper/dictionary.h"
  30. #include "shell/common/gin_helper/error_thrower.h"
  31. #include "shell/common/gin_helper/object_template_builder.h"
  32. #include "shell/common/gin_helper/promise.h"
  33. #include "shell/common/node_includes.h"
  34. #include "shell/common/v8_util.h"
  35. namespace {
  36. using LifecycleState = content::RenderFrameHostImpl::LifecycleStateImpl;
  37. // RenderFrameCreated is called for speculative frames which may not be
  38. // used in certain cross-origin navigations. Invoking
  39. // RenderFrameHost::GetLifecycleState currently crashes when called for
  40. // speculative frames so we need to filter it out for now. Check
  41. // https://crbug.com/1183639 for details on when this can be removed.
  42. [[nodiscard]] LifecycleState GetLifecycleState(
  43. const content::RenderFrameHost* rfh) {
  44. const auto* rfh_impl = static_cast<const content::RenderFrameHostImpl*>(rfh);
  45. return rfh_impl->lifecycle_state();
  46. }
  47. // RenderFrameHost (RFH) exists as a child of a FrameTreeNode. When a
  48. // cross-origin navigation occurs, the FrameTreeNode swaps RFHs. After
  49. // swapping, the old RFH will be marked for deletion and run any unload
  50. // listeners. If an IPC is sent during an unload/beforeunload listener,
  51. // it's possible that it arrives after the RFH swap and has been
  52. // detached from the FrameTreeNode.
  53. [[nodiscard]] bool IsDetachedFrameHost(const content::RenderFrameHost* rfh) {
  54. if (!rfh)
  55. return true;
  56. // During cross-origin navigation, a RFH may be swapped out of its
  57. // FrameTreeNode with a new RFH. In these cases, it's marked for
  58. // deletion. As this pending deletion RFH won't be following future
  59. // swaps, we need to indicate that its been detached.
  60. return GetLifecycleState(rfh) == LifecycleState::kRunningUnloadHandlers;
  61. }
  62. } // namespace
  63. namespace gin {
  64. template <>
  65. struct Converter<blink::mojom::PageVisibilityState> {
  66. static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
  67. blink::mojom::PageVisibilityState val) {
  68. std::string visibility;
  69. switch (val) {
  70. case blink::mojom::PageVisibilityState::kVisible:
  71. visibility = "visible";
  72. break;
  73. case blink::mojom::PageVisibilityState::kHidden:
  74. case blink::mojom::PageVisibilityState::kHiddenButPainting:
  75. visibility = "hidden";
  76. break;
  77. }
  78. return gin::ConvertToV8(isolate, visibility);
  79. }
  80. };
  81. } // namespace gin
  82. namespace electron::api {
  83. // FrameTreeNodeId -> WebFrameMain*
  84. // Using FrameTreeNode allows us to track frame across navigations. This
  85. // is most similar to how <iframe> works.
  86. using FrameTreeNodeIdMap =
  87. std::unordered_map<content::FrameTreeNodeId, WebFrameMain*>;
  88. // Token -> WebFrameMain*
  89. // Maps exact RFH to a WebFrameMain instance.
  90. using FrameTokenMap =
  91. std::map<content::GlobalRenderFrameHostToken, WebFrameMain*>;
  92. namespace {
  93. FrameTreeNodeIdMap& GetFrameTreeNodeIdMap() {
  94. static base::NoDestructor<FrameTreeNodeIdMap> instance;
  95. return *instance;
  96. }
  97. FrameTokenMap& GetFrameTokenMap() {
  98. static base::NoDestructor<FrameTokenMap> instance;
  99. return *instance;
  100. }
  101. } // namespace
  102. // static
  103. WebFrameMain* WebFrameMain::FromFrameTreeNodeId(
  104. content::FrameTreeNodeId frame_tree_node_id) {
  105. FrameTreeNodeIdMap& frame_map = GetFrameTreeNodeIdMap();
  106. auto iter = frame_map.find(frame_tree_node_id);
  107. auto* web_frame = iter == frame_map.end() ? nullptr : iter->second;
  108. return web_frame;
  109. }
  110. // static
  111. WebFrameMain* WebFrameMain::FromFrameToken(
  112. content::GlobalRenderFrameHostToken frame_token) {
  113. FrameTokenMap& frame_map = GetFrameTokenMap();
  114. auto iter = frame_map.find(frame_token);
  115. auto* web_frame = iter == frame_map.end() ? nullptr : iter->second;
  116. return web_frame;
  117. }
  118. // static
  119. WebFrameMain* WebFrameMain::FromRenderFrameHost(content::RenderFrameHost* rfh) {
  120. if (!rfh)
  121. return nullptr;
  122. return FromFrameToken(rfh->GetGlobalFrameToken());
  123. }
  124. content::RenderFrameHost* WebFrameMain::render_frame_host() const {
  125. return render_frame_disposed_
  126. ? nullptr
  127. : content::RenderFrameHost::FromFrameToken(frame_token_);
  128. }
  129. gin::WrapperInfo WebFrameMain::kWrapperInfo = {gin::kEmbedderNativeGin};
  130. WebFrameMain::WebFrameMain(content::RenderFrameHost* rfh)
  131. : frame_tree_node_id_(rfh->GetFrameTreeNodeId()),
  132. frame_token_(rfh->GetGlobalFrameToken()),
  133. render_frame_detached_(IsDetachedFrameHost(rfh)) {
  134. // Detached RFH should not insert itself in FTN lookup since it has been
  135. // swapped already.
  136. if (!render_frame_detached_)
  137. GetFrameTreeNodeIdMap().emplace(frame_tree_node_id_, this);
  138. DCHECK(GetFrameTokenMap().find(frame_token_) == GetFrameTokenMap().end());
  139. GetFrameTokenMap().emplace(frame_token_, this);
  140. // WebFrameMain should only be created for active or unloading frames.
  141. DCHECK(GetLifecycleState(rfh) == LifecycleState::kActive ||
  142. GetLifecycleState(rfh) == LifecycleState::kRunningUnloadHandlers);
  143. }
  144. WebFrameMain::~WebFrameMain() {
  145. Destroyed();
  146. }
  147. void WebFrameMain::Destroyed() {
  148. if (FromFrameTreeNodeId(frame_tree_node_id_) == this) {
  149. // WebFrameMain initialized as detached doesn't support FTN lookup and
  150. // shouldn't erase the entry.
  151. DCHECK(!render_frame_detached_ || render_frame_disposed_);
  152. GetFrameTreeNodeIdMap().erase(frame_tree_node_id_);
  153. }
  154. GetFrameTokenMap().erase(frame_token_);
  155. MarkRenderFrameDisposed();
  156. Unpin();
  157. }
  158. void WebFrameMain::MarkRenderFrameDisposed() {
  159. render_frame_detached_ = true;
  160. render_frame_disposed_ = true;
  161. TeardownMojoConnection();
  162. }
  163. // Should only be called when swapping frames.
  164. void WebFrameMain::UpdateRenderFrameHost(content::RenderFrameHost* rfh) {
  165. GetFrameTokenMap().erase(frame_token_);
  166. // Ensure that RFH being swapped in doesn't already exist as its own
  167. // WebFrameMain instance.
  168. frame_token_ = rfh->GetGlobalFrameToken();
  169. DCHECK(GetFrameTokenMap().find(frame_token_) == GetFrameTokenMap().end());
  170. GetFrameTokenMap().emplace(frame_token_, this);
  171. render_frame_disposed_ = false;
  172. TeardownMojoConnection();
  173. MaybeSetupMojoConnection();
  174. }
  175. bool WebFrameMain::CheckRenderFrame() const {
  176. if (!HasRenderFrame()) {
  177. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  178. v8::HandleScope scope(isolate);
  179. gin_helper::ErrorThrower(isolate).ThrowError(
  180. "Render frame was disposed before WebFrameMain could be accessed");
  181. return false;
  182. }
  183. return true;
  184. }
  185. v8::Local<v8::Promise> WebFrameMain::ExecuteJavaScript(
  186. gin::Arguments* args,
  187. const std::u16string& code) {
  188. gin_helper::Promise<base::Value> promise(args->isolate());
  189. v8::Local<v8::Promise> handle = promise.GetHandle();
  190. // Optional userGesture parameter
  191. bool user_gesture;
  192. if (!args->PeekNext().IsEmpty()) {
  193. if (args->PeekNext()->IsBoolean()) {
  194. args->GetNext(&user_gesture);
  195. } else {
  196. args->ThrowTypeError("userGesture must be a boolean");
  197. return handle;
  198. }
  199. } else {
  200. user_gesture = false;
  201. }
  202. if (render_frame_disposed_) {
  203. promise.RejectWithErrorMessage(
  204. "Render frame was disposed before WebFrameMain could be accessed");
  205. return handle;
  206. }
  207. static_cast<content::RenderFrameHostImpl*>(render_frame_host())
  208. ->ExecuteJavaScriptForTests(
  209. code, user_gesture, true /* resolve_promises */,
  210. /*honor_js_content_settings=*/true, content::ISOLATED_WORLD_ID_GLOBAL,
  211. base::BindOnce(
  212. [](gin_helper::Promise<base::Value> promise,
  213. blink::mojom::JavaScriptExecutionResultType type,
  214. base::Value value) {
  215. if (type ==
  216. blink::mojom::JavaScriptExecutionResultType::kSuccess) {
  217. promise.Resolve(value);
  218. } else {
  219. v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
  220. v8::HandleScope scope(isolate);
  221. promise.Reject(gin::ConvertToV8(isolate, value));
  222. }
  223. },
  224. std::move(promise)));
  225. return handle;
  226. }
  227. bool WebFrameMain::Reload() {
  228. if (!CheckRenderFrame())
  229. return false;
  230. return render_frame_host()->Reload();
  231. }
  232. bool WebFrameMain::IsDestroyed() const {
  233. return render_frame_disposed_;
  234. }
  235. void WebFrameMain::Send(v8::Isolate* isolate,
  236. bool internal,
  237. const std::string& channel,
  238. v8::Local<v8::Value> args) {
  239. blink::CloneableMessage message;
  240. if (!gin::ConvertFromV8(isolate, args, &message)) {
  241. isolate->ThrowException(v8::Exception::Error(
  242. gin::StringToV8(isolate, "Failed to serialize arguments")));
  243. return;
  244. }
  245. if (!CheckRenderFrame())
  246. return;
  247. GetRendererApi()->Message(internal, channel, std::move(message));
  248. }
  249. const mojo::Remote<mojom::ElectronRenderer>& WebFrameMain::GetRendererApi() {
  250. MaybeSetupMojoConnection();
  251. return renderer_api_;
  252. }
  253. void WebFrameMain::MaybeSetupMojoConnection() {
  254. if (render_frame_disposed_) {
  255. // RFH may not be set yet if called between when a new RFH is created and
  256. // before it's been swapped with an old RFH.
  257. LOG(INFO) << "Attempt to setup WebFrameMain connection while render frame "
  258. "is disposed";
  259. return;
  260. }
  261. if (!renderer_api_) {
  262. pending_receiver_ = renderer_api_.BindNewPipeAndPassReceiver();
  263. renderer_api_.set_disconnect_handler(base::BindOnce(
  264. &WebFrameMain::OnRendererConnectionError, weak_factory_.GetWeakPtr()));
  265. }
  266. content::RenderFrameHost* rfh = render_frame_host();
  267. DCHECK(rfh);
  268. // Wait for RenderFrame to be created in renderer before accessing remote.
  269. if (pending_receiver_ && rfh && rfh->IsRenderFrameLive()) {
  270. rfh->GetRemoteInterfaces()->GetInterface(std::move(pending_receiver_));
  271. }
  272. }
  273. void WebFrameMain::TeardownMojoConnection() {
  274. renderer_api_.reset();
  275. pending_receiver_.reset();
  276. }
  277. void WebFrameMain::OnRendererConnectionError() {
  278. TeardownMojoConnection();
  279. }
  280. [[nodiscard]] bool WebFrameMain::HasRenderFrame() const {
  281. if (render_frame_disposed_)
  282. return false;
  283. // If RFH is a nullptr, this instance of WebFrameMain is dangling and wasn't
  284. // properly deleted.
  285. CHECK(render_frame_host());
  286. return true;
  287. }
  288. void WebFrameMain::PostMessage(v8::Isolate* isolate,
  289. const std::string& channel,
  290. v8::Local<v8::Value> message_value,
  291. std::optional<v8::Local<v8::Value>> transfer) {
  292. blink::TransferableMessage transferable_message;
  293. if (!electron::SerializeV8Value(isolate, message_value,
  294. &transferable_message)) {
  295. // SerializeV8Value sets an exception.
  296. return;
  297. }
  298. std::vector<gin::Handle<MessagePort>> wrapped_ports;
  299. if (transfer && !transfer.value()->IsUndefined()) {
  300. if (!gin::ConvertFromV8(isolate, *transfer, &wrapped_ports)) {
  301. isolate->ThrowException(v8::Exception::Error(
  302. gin::StringToV8(isolate, "Invalid value for transfer")));
  303. return;
  304. }
  305. }
  306. bool threw_exception = false;
  307. transferable_message.ports =
  308. MessagePort::DisentanglePorts(isolate, wrapped_ports, &threw_exception);
  309. if (threw_exception)
  310. return;
  311. if (!CheckRenderFrame())
  312. return;
  313. GetRendererApi()->ReceivePostMessage(channel,
  314. std::move(transferable_message));
  315. }
  316. bool WebFrameMain::Detached() const {
  317. return render_frame_detached_;
  318. }
  319. content::FrameTreeNodeId WebFrameMain::FrameTreeNodeID() const {
  320. return frame_tree_node_id_;
  321. }
  322. std::string WebFrameMain::Name() const {
  323. if (!CheckRenderFrame())
  324. return {};
  325. return render_frame_host()->GetFrameName();
  326. }
  327. base::ProcessId WebFrameMain::OSProcessID() const {
  328. if (!CheckRenderFrame())
  329. return -1;
  330. base::ProcessHandle process_handle =
  331. render_frame_host()->GetProcess()->GetProcess().Handle();
  332. return base::GetProcId(process_handle);
  333. }
  334. int32_t WebFrameMain::ProcessID() const {
  335. if (!CheckRenderFrame())
  336. return -1;
  337. return render_frame_host()->GetProcess()->GetID().GetUnsafeValue();
  338. }
  339. int WebFrameMain::RoutingID() const {
  340. if (!CheckRenderFrame())
  341. return -1;
  342. return render_frame_host()->GetRoutingID();
  343. }
  344. GURL WebFrameMain::URL() const {
  345. if (!CheckRenderFrame())
  346. return {};
  347. return render_frame_host()->GetLastCommittedURL();
  348. }
  349. std::string WebFrameMain::Origin() const {
  350. if (!CheckRenderFrame())
  351. return {};
  352. return render_frame_host()->GetLastCommittedOrigin().Serialize();
  353. }
  354. blink::mojom::PageVisibilityState WebFrameMain::VisibilityState() const {
  355. if (!CheckRenderFrame())
  356. return blink::mojom::PageVisibilityState::kHidden;
  357. return render_frame_host()->GetVisibilityState();
  358. }
  359. content::RenderFrameHost* WebFrameMain::Top() const {
  360. if (!CheckRenderFrame())
  361. return nullptr;
  362. return render_frame_host()->GetMainFrame();
  363. }
  364. content::RenderFrameHost* WebFrameMain::Parent() const {
  365. if (!CheckRenderFrame())
  366. return nullptr;
  367. return render_frame_host()->GetParent();
  368. }
  369. std::vector<content::RenderFrameHost*> WebFrameMain::Frames() const {
  370. std::vector<content::RenderFrameHost*> frame_hosts;
  371. if (!CheckRenderFrame())
  372. return frame_hosts;
  373. render_frame_host()->ForEachRenderFrameHost(
  374. [&frame_hosts, this](content::RenderFrameHost* rfh) {
  375. if (rfh && rfh->GetParent() == render_frame_host())
  376. frame_hosts.push_back(rfh);
  377. });
  378. return frame_hosts;
  379. }
  380. std::vector<content::RenderFrameHost*> WebFrameMain::FramesInSubtree() const {
  381. std::vector<content::RenderFrameHost*> frame_hosts;
  382. if (!CheckRenderFrame())
  383. return frame_hosts;
  384. render_frame_host()->ForEachRenderFrameHost(
  385. [&frame_hosts](content::RenderFrameHost* rfh) {
  386. frame_hosts.push_back(rfh);
  387. });
  388. return frame_hosts;
  389. }
  390. const char* WebFrameMain::LifecycleStateForTesting() const {
  391. if (!HasRenderFrame())
  392. return {};
  393. return content::RenderFrameHostImpl::LifecycleStateImplToString(
  394. GetLifecycleState(render_frame_host()));
  395. }
  396. v8::Local<v8::Promise> WebFrameMain::CollectDocumentJSCallStack(
  397. gin::Arguments* args) {
  398. gin_helper::Promise<base::Value> promise(args->isolate());
  399. v8::Local<v8::Promise> handle = promise.GetHandle();
  400. if (!HasRenderFrame()) {
  401. promise.RejectWithErrorMessage(
  402. "Render frame was disposed before WebFrameMain could be accessed");
  403. return handle;
  404. }
  405. if (!base::FeatureList::IsEnabled(
  406. blink::features::kDocumentPolicyIncludeJSCallStacksInCrashReports)) {
  407. promise.RejectWithErrorMessage(
  408. "DocumentPolicyIncludeJSCallStacksInCrashReports is not enabled");
  409. return handle;
  410. }
  411. content::RenderProcessHostImpl* rph_impl =
  412. static_cast<content::RenderProcessHostImpl*>(
  413. render_frame_host()->GetProcess());
  414. rph_impl->GetJavaScriptCallStackGeneratorInterface()
  415. ->CollectJavaScriptCallStack(
  416. base::BindOnce(&WebFrameMain::CollectedJavaScriptCallStack,
  417. weak_factory_.GetWeakPtr(), std::move(promise)));
  418. return handle;
  419. }
  420. void WebFrameMain::CollectedJavaScriptCallStack(
  421. gin_helper::Promise<base::Value> promise,
  422. const std::string& untrusted_javascript_call_stack,
  423. const std::optional<blink::LocalFrameToken>& remote_frame_token) {
  424. if (!HasRenderFrame()) {
  425. promise.RejectWithErrorMessage(
  426. "Render frame was disposed before call stack was received");
  427. return;
  428. }
  429. const blink::LocalFrameToken& frame_token =
  430. render_frame_host()->GetFrameToken();
  431. if (remote_frame_token == frame_token) {
  432. base::Value base_value(untrusted_javascript_call_stack);
  433. promise.Resolve(base_value);
  434. } else if (!remote_frame_token) {
  435. // Failed to collect call stack. See logic in:
  436. // third_party/blink/renderer/controller/javascript_call_stack_collector.cc
  437. promise.Resolve(base::Value());
  438. } else {
  439. // Requests for call stacks can be initiated on an old RenderProcessHost
  440. // then be received after a frame swap.
  441. LOG(ERROR) << "Received call stack from old RPH";
  442. promise.Resolve(base::Value());
  443. }
  444. }
  445. void WebFrameMain::DOMContentLoaded() {
  446. Emit("dom-ready");
  447. }
  448. // static
  449. gin::Handle<WebFrameMain> WebFrameMain::New(v8::Isolate* isolate) {
  450. return {};
  451. }
  452. // static
  453. gin::Handle<WebFrameMain> WebFrameMain::From(v8::Isolate* isolate,
  454. content::RenderFrameHost* rfh) {
  455. if (!rfh)
  456. return {};
  457. WebFrameMain* web_frame;
  458. switch (GetLifecycleState(rfh)) {
  459. case LifecycleState::kSpeculative:
  460. case LifecycleState::kPendingCommit:
  461. // RFH is in the process of being swapped. Need to lookup by FTN to avoid
  462. // creating dangling WebFrameMain.
  463. web_frame = FromFrameTreeNodeId(rfh->GetFrameTreeNodeId());
  464. break;
  465. case LifecycleState::kPrerendering:
  466. case LifecycleState::kActive:
  467. case LifecycleState::kInBackForwardCache:
  468. // RFH is already assigned to the FrameTreeNode and can safely be looked
  469. // up directly.
  470. web_frame = FromRenderFrameHost(rfh);
  471. break;
  472. case LifecycleState::kRunningUnloadHandlers:
  473. // Event/IPC emitted for a frame running unload handlers. Return the exact
  474. // RFH so the security origin will be accurate.
  475. web_frame = FromRenderFrameHost(rfh);
  476. break;
  477. case LifecycleState::kReadyToBeDeleted:
  478. // RFH is gone
  479. return {};
  480. }
  481. if (web_frame)
  482. return gin::CreateHandle(isolate, web_frame);
  483. auto handle = gin::CreateHandle(isolate, new WebFrameMain(rfh));
  484. // Prevent garbage collection of frame until it has been deleted internally.
  485. handle->Pin(isolate);
  486. return handle;
  487. }
  488. // static
  489. void WebFrameMain::FillObjectTemplate(v8::Isolate* isolate,
  490. v8::Local<v8::ObjectTemplate> templ) {
  491. gin_helper::ObjectTemplateBuilder(isolate, templ)
  492. .SetMethod("executeJavaScript", &WebFrameMain::ExecuteJavaScript)
  493. .SetMethod("collectJavaScriptCallStack",
  494. &WebFrameMain::CollectDocumentJSCallStack)
  495. .SetMethod("reload", &WebFrameMain::Reload)
  496. .SetMethod("isDestroyed", &WebFrameMain::IsDestroyed)
  497. .SetMethod("_send", &WebFrameMain::Send)
  498. .SetMethod("_postMessage", &WebFrameMain::PostMessage)
  499. .SetProperty("detached", &WebFrameMain::Detached)
  500. .SetProperty("frameTreeNodeId", &WebFrameMain::FrameTreeNodeID)
  501. .SetProperty("name", &WebFrameMain::Name)
  502. .SetProperty("osProcessId", &WebFrameMain::OSProcessID)
  503. .SetProperty("processId", &WebFrameMain::ProcessID)
  504. .SetProperty("routingId", &WebFrameMain::RoutingID)
  505. .SetProperty("url", &WebFrameMain::URL)
  506. .SetProperty("origin", &WebFrameMain::Origin)
  507. .SetProperty("visibilityState", &WebFrameMain::VisibilityState)
  508. .SetProperty("top", &WebFrameMain::Top)
  509. .SetProperty("parent", &WebFrameMain::Parent)
  510. .SetProperty("frames", &WebFrameMain::Frames)
  511. .SetProperty("framesInSubtree", &WebFrameMain::FramesInSubtree)
  512. .SetProperty("_lifecycleStateForTesting",
  513. &WebFrameMain::LifecycleStateForTesting)
  514. .Build();
  515. }
  516. const char* WebFrameMain::GetTypeName() {
  517. return GetClassName();
  518. }
  519. } // namespace electron::api
  520. namespace {
  521. using electron::api::WebFrameMain;
  522. v8::Local<v8::Value> FromID(gin_helper::ErrorThrower thrower,
  523. int render_process_id,
  524. int render_frame_id) {
  525. if (!electron::Browser::Get()->is_ready()) {
  526. thrower.ThrowError("WebFrameMain is available only after app ready");
  527. return v8::Null(thrower.isolate());
  528. }
  529. auto* rfh =
  530. content::RenderFrameHost::FromID(render_process_id, render_frame_id);
  531. if (!rfh)
  532. return v8::Undefined(thrower.isolate());
  533. return WebFrameMain::From(thrower.isolate(), rfh).ToV8();
  534. }
  535. v8::Local<v8::Value> FromIdIfExists(gin_helper::ErrorThrower thrower,
  536. int render_process_id,
  537. int render_frame_id) {
  538. if (!electron::Browser::Get()->is_ready()) {
  539. thrower.ThrowError("WebFrameMain is available only after app ready");
  540. return v8::Null(thrower.isolate());
  541. }
  542. content::RenderFrameHost* rfh =
  543. content::RenderFrameHost::FromID(render_process_id, render_frame_id);
  544. WebFrameMain* web_frame = WebFrameMain::FromRenderFrameHost(rfh);
  545. if (!web_frame)
  546. return v8::Null(thrower.isolate());
  547. return gin::CreateHandle(thrower.isolate(), web_frame).ToV8();
  548. }
  549. v8::Local<v8::Value> FromFtnIdIfExists(gin_helper::ErrorThrower thrower,
  550. int frame_tree_node_id) {
  551. if (!electron::Browser::Get()->is_ready()) {
  552. thrower.ThrowError("WebFrameMain is available only after app ready");
  553. return v8::Null(thrower.isolate());
  554. }
  555. WebFrameMain* web_frame = WebFrameMain::FromFrameTreeNodeId(
  556. content::FrameTreeNodeId(frame_tree_node_id));
  557. if (!web_frame)
  558. return v8::Null(thrower.isolate());
  559. return gin::CreateHandle(thrower.isolate(), web_frame).ToV8();
  560. }
  561. void Initialize(v8::Local<v8::Object> exports,
  562. v8::Local<v8::Value> unused,
  563. v8::Local<v8::Context> context,
  564. void* priv) {
  565. v8::Isolate* isolate = context->GetIsolate();
  566. gin_helper::Dictionary dict(isolate, exports);
  567. dict.Set("WebFrameMain", WebFrameMain::GetConstructor(context));
  568. dict.SetMethod("fromId", &FromID);
  569. dict.SetMethod("_fromIdIfExists", &FromIdIfExists);
  570. dict.SetMethod("_fromFtnIdIfExists", &FromFtnIdIfExists);
  571. }
  572. } // namespace
  573. NODE_LINKED_BINDING_CONTEXT_AWARE(electron_browser_web_frame_main, Initialize)