feat: add WebFrameMain.visibilityState (#28706)
* feat: add WebFrameMain.visibilityState * docs: mention other page visibility APIs * test: delay visibilityState check after hiding * test: add waitForTrue to avoid flaky visibilityState test * refactor: waitForTrue -> waitUntil
This commit is contained in:
parent
93311c8686
commit
43d27cc4d1
5 changed files with 98 additions and 0 deletions
|
@ -182,3 +182,9 @@ This is not the same as the OS process ID; to read that use `frame.osProcessId`.
|
||||||
An `Integer` representing the unique frame id in the current renderer process.
|
An `Integer` representing the unique frame id in the current renderer process.
|
||||||
Distinct `WebFrameMain` instances that refer to the same underlying frame will
|
Distinct `WebFrameMain` instances that refer to the same underlying frame will
|
||||||
have the same `routingId`.
|
have the same `routingId`.
|
||||||
|
|
||||||
|
#### `frame.visibilityState` _Readonly_
|
||||||
|
|
||||||
|
A `string` representing the [visibility state](https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilityState) of the frame.
|
||||||
|
|
||||||
|
See also how the [Page Visibility API](browser-window.md#page-visibility) is affected by other Electron APIs.
|
||||||
|
|
|
@ -30,6 +30,28 @@
|
||||||
#include "shell/common/node_includes.h"
|
#include "shell/common/node_includes.h"
|
||||||
#include "shell/common/v8_value_serializer.h"
|
#include "shell/common/v8_value_serializer.h"
|
||||||
|
|
||||||
|
namespace gin {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Converter<blink::mojom::PageVisibilityState> {
|
||||||
|
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||||
|
blink::mojom::PageVisibilityState val) {
|
||||||
|
std::string visibility;
|
||||||
|
switch (val) {
|
||||||
|
case blink::mojom::PageVisibilityState::kVisible:
|
||||||
|
visibility = "visible";
|
||||||
|
break;
|
||||||
|
case blink::mojom::PageVisibilityState::kHidden:
|
||||||
|
case blink::mojom::PageVisibilityState::kHiddenButPainting:
|
||||||
|
visibility = "hidden";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return gin::ConvertToV8(isolate, visibility);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gin
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
namespace api {
|
namespace api {
|
||||||
|
@ -228,6 +250,12 @@ GURL WebFrameMain::URL() const {
|
||||||
return render_frame_->GetLastCommittedURL();
|
return render_frame_->GetLastCommittedURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blink::mojom::PageVisibilityState WebFrameMain::VisibilityState() const {
|
||||||
|
if (!CheckRenderFrame())
|
||||||
|
return blink::mojom::PageVisibilityState::kHidden;
|
||||||
|
return render_frame_->GetVisibilityState();
|
||||||
|
}
|
||||||
|
|
||||||
content::RenderFrameHost* WebFrameMain::Top() const {
|
content::RenderFrameHost* WebFrameMain::Top() const {
|
||||||
if (!CheckRenderFrame())
|
if (!CheckRenderFrame())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -331,6 +359,7 @@ v8::Local<v8::ObjectTemplate> WebFrameMain::FillObjectTemplate(
|
||||||
.SetProperty("processId", &WebFrameMain::ProcessID)
|
.SetProperty("processId", &WebFrameMain::ProcessID)
|
||||||
.SetProperty("routingId", &WebFrameMain::RoutingID)
|
.SetProperty("routingId", &WebFrameMain::RoutingID)
|
||||||
.SetProperty("url", &WebFrameMain::URL)
|
.SetProperty("url", &WebFrameMain::URL)
|
||||||
|
.SetProperty("visibilityState", &WebFrameMain::VisibilityState)
|
||||||
.SetProperty("top", &WebFrameMain::Top)
|
.SetProperty("top", &WebFrameMain::Top)
|
||||||
.SetProperty("parent", &WebFrameMain::Parent)
|
.SetProperty("parent", &WebFrameMain::Parent)
|
||||||
.SetProperty("frames", &WebFrameMain::Frames)
|
.SetProperty("frames", &WebFrameMain::Frames)
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "gin/wrappable.h"
|
#include "gin/wrappable.h"
|
||||||
#include "shell/common/gin_helper/constructible.h"
|
#include "shell/common/gin_helper/constructible.h"
|
||||||
#include "shell/common/gin_helper/pinnable.h"
|
#include "shell/common/gin_helper/pinnable.h"
|
||||||
|
#include "third_party/blink/public/mojom/page/page_visibility_state.mojom-forward.h"
|
||||||
|
|
||||||
class GURL;
|
class GURL;
|
||||||
|
|
||||||
|
@ -95,6 +96,7 @@ class WebFrameMain : public gin::Wrappable<WebFrameMain>,
|
||||||
int ProcessID() const;
|
int ProcessID() const;
|
||||||
int RoutingID() const;
|
int RoutingID() const;
|
||||||
GURL URL() const;
|
GURL URL() const;
|
||||||
|
blink::mojom::PageVisibilityState VisibilityState() const;
|
||||||
|
|
||||||
content::RenderFrameHost* Top() const;
|
content::RenderFrameHost* Top() const;
|
||||||
content::RenderFrameHost* Parent() const;
|
content::RenderFrameHost* Parent() const;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { BrowserWindow, WebFrameMain, webFrameMain, ipcMain } from 'electron/mai
|
||||||
import { closeAllWindows } from './window-helpers';
|
import { closeAllWindows } from './window-helpers';
|
||||||
import { emittedOnce, emittedNTimes } from './events-helpers';
|
import { emittedOnce, emittedNTimes } from './events-helpers';
|
||||||
import { AddressInfo } from 'net';
|
import { AddressInfo } from 'net';
|
||||||
|
import { waitUntil } from './spec-helpers';
|
||||||
|
|
||||||
describe('webFrameMain module', () => {
|
describe('webFrameMain module', () => {
|
||||||
const fixtures = path.resolve(__dirname, '..', 'spec-main', 'fixtures');
|
const fixtures = path.resolve(__dirname, '..', 'spec-main', 'fixtures');
|
||||||
|
@ -135,6 +136,20 @@ describe('webFrameMain module', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('WebFrame.visibilityState', () => {
|
||||||
|
it('should match window state', async () => {
|
||||||
|
const w = new BrowserWindow({ show: true });
|
||||||
|
await w.loadURL('about:blank');
|
||||||
|
const webFrame = w.webContents.mainFrame;
|
||||||
|
|
||||||
|
expect(webFrame.visibilityState).to.equal('visible');
|
||||||
|
w.hide();
|
||||||
|
await expect(
|
||||||
|
waitUntil(() => webFrame.visibilityState === 'hidden')
|
||||||
|
).to.eventually.be.fulfilled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('WebFrame.executeJavaScript', () => {
|
describe('WebFrame.executeJavaScript', () => {
|
||||||
it('can inject code into any subframe', async () => {
|
it('can inject code into any subframe', async () => {
|
||||||
const w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
|
const w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
|
||||||
|
|
|
@ -86,3 +86,49 @@ export async function startRemoteControlApp () {
|
||||||
defer(() => { appProcess.kill('SIGINT'); });
|
defer(() => { appProcess.kill('SIGINT'); });
|
||||||
return new RemoteControlApp(appProcess, port);
|
return new RemoteControlApp(appProcess, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function waitUntil (
|
||||||
|
callback: () => boolean,
|
||||||
|
opts: { rate?: number, timeout?: number } = {}
|
||||||
|
) {
|
||||||
|
const { rate = 10, timeout = 10000 } = opts;
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
let intervalId: NodeJS.Timeout | undefined; // eslint-disable-line prefer-const
|
||||||
|
let timeoutId: NodeJS.Timeout | undefined;
|
||||||
|
|
||||||
|
const cleanup = () => {
|
||||||
|
if (intervalId) clearInterval(intervalId);
|
||||||
|
if (timeoutId) clearTimeout(timeoutId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const check = () => {
|
||||||
|
let result;
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = callback();
|
||||||
|
} catch (e) {
|
||||||
|
cleanup();
|
||||||
|
reject(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result === true) {
|
||||||
|
cleanup();
|
||||||
|
resolve();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (check()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
intervalId = setInterval(check, rate);
|
||||||
|
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
timeoutId = undefined;
|
||||||
|
cleanup();
|
||||||
|
reject(new Error(`waitUntil timed out after ${timeout}ms`));
|
||||||
|
}, timeout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue