feat(api): add method to force garbage collection (#32383)

This commit is contained in:
Matthew Jee 2024-09-13 14:09:36 -07:00 коммит произвёл GitHub
Родитель 5b28d2a84c
Коммит f2a974b045
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
15 изменённых файлов: 93 добавлений и 1 удалений

Просмотреть файл

@ -256,6 +256,13 @@ class PageHandler {
return await this._contentPage.send('disposeObject', options); return await this._contentPage.send('disposeObject', options);
} }
async ['Heap.collectGarbage']() {
Services.obs.notifyObservers(null, "child-gc-request");
Cu.forceGC();
Services.obs.notifyObservers(null, "child-cc-request");
Cu.forceCC();
}
async ['Network.getResponseBody']({requestId}) { async ['Network.getResponseBody']({requestId}) {
return this._pageNetwork.getResponseBody(requestId); return this._pageNetwork.getResponseBody(requestId);
} }

Просмотреть файл

@ -487,6 +487,17 @@ const Browser = {
}, },
}; };
const Heap = {
targets: ['page'],
types: {},
events: {},
methods: {
'collectGarbage': {
params: {},
},
},
};
const Network = { const Network = {
targets: ['page'], targets: ['page'],
types: networkTypes, types: networkTypes,
@ -1002,7 +1013,7 @@ const Accessibility = {
} }
this.protocol = { this.protocol = {
domains: {Browser, Page, Runtime, Network, Accessibility}, domains: {Browser, Heap, Page, Runtime, Network, Accessibility},
}; };
this.checkScheme = checkScheme; this.checkScheme = checkScheme;
this.EXPORTED_SYMBOLS = ['protocol', 'checkScheme']; this.EXPORTED_SYMBOLS = ['protocol', 'checkScheme'];

Просмотреть файл

@ -2333,6 +2333,11 @@ last redirect. If cannot go forward, returns `null`.
Navigate to the next page in history. Navigate to the next page in history.
## async method: Page.forceGarbageCollection
* since: v1.47
Force the browser to perform garbage collection.
### option: Page.goForward.waitUntil = %%-navigation-wait-until-%% ### option: Page.goForward.waitUntil = %%-navigation-wait-until-%%
* since: v1.8 * since: v1.8

Просмотреть файл

@ -468,6 +468,10 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
return Response.fromNullable((await this._channel.goForward({ ...options, waitUntil })).response); return Response.fromNullable((await this._channel.goForward({ ...options, waitUntil })).response);
} }
async forceGarbageCollection() {
await this._channel.forceGarbageCollection();
}
async emulateMedia(options: { media?: 'screen' | 'print' | null, colorScheme?: 'dark' | 'light' | 'no-preference' | null, reducedMotion?: 'reduce' | 'no-preference' | null, forcedColors?: 'active' | 'none' | null } = {}) { async emulateMedia(options: { media?: 'screen' | 'print' | null, colorScheme?: 'dark' | 'light' | 'no-preference' | null, reducedMotion?: 'reduce' | 'no-preference' | null, forcedColors?: 'active' | 'none' | null } = {}) {
await this._channel.emulateMedia({ await this._channel.emulateMedia({
media: options.media === null ? 'no-override' : options.media, media: options.media === null ? 'no-override' : options.media,

Просмотреть файл

@ -1122,6 +1122,8 @@ scheme.PageGoForwardParams = tObject({
scheme.PageGoForwardResult = tObject({ scheme.PageGoForwardResult = tObject({
response: tOptional(tChannel(['Response'])), response: tOptional(tChannel(['Response'])),
}); });
scheme.PageForceGarbageCollectionParams = tOptional(tObject({}));
scheme.PageForceGarbageCollectionResult = tOptional(tObject({}));
scheme.PageRegisterLocatorHandlerParams = tObject({ scheme.PageRegisterLocatorHandlerParams = tObject({
selector: tString, selector: tString,
noWaitAfter: tOptional(tBoolean), noWaitAfter: tOptional(tBoolean),

Просмотреть файл

@ -323,6 +323,10 @@ export class BidiPage implements PageDelegate {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
async forceGarbageCollection(): Promise<void> {
throw new Error('Method not implemented.');
}
async addInitScript(initScript: InitScript): Promise<void> { async addInitScript(initScript: InitScript): Promise<void> {
const { script } = await this._session.send('script.addPreloadScript', { const { script } = await this._session.send('script.addPreloadScript', {
// TODO: remove function call from the source. // TODO: remove function call from the source.

Просмотреть файл

@ -247,6 +247,10 @@ export class CRPage implements PageDelegate {
return this._go(+1); return this._go(+1);
} }
async forceGarbageCollection(): Promise<void> {
await this._mainFrameSession._client.send('HeapProfiler.collectGarbage');
}
async addInitScript(initScript: InitScript, world: types.World = 'main'): Promise<void> { async addInitScript(initScript: InitScript, world: types.World = 'main'): Promise<void> {
await this._forAllFrameSessions(frame => frame._evaluateOnNewDocument(initScript, world)); await this._forAllFrameSessions(frame => frame._evaluateOnNewDocument(initScript, world));
} }

Просмотреть файл

@ -137,6 +137,10 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
return { response: ResponseDispatcher.fromNullable(this.parentScope(), await this._page.goForward(metadata, params)) }; return { response: ResponseDispatcher.fromNullable(this.parentScope(), await this._page.goForward(metadata, params)) };
} }
async forceGarbageCollection(params: channels.PageForceGarbageCollectionParams, metadata: CallMetadata): Promise<channels.PageForceGarbageCollectionResult> {
await this._page.forceGarbageCollection();
}
async registerLocatorHandler(params: channels.PageRegisterLocatorHandlerParams, metadata: CallMetadata): Promise<channels.PageRegisterLocatorHandlerResult> { async registerLocatorHandler(params: channels.PageRegisterLocatorHandlerParams, metadata: CallMetadata): Promise<channels.PageRegisterLocatorHandlerResult> {
const uid = this._page.registerLocatorHandler(params.selector, params.noWaitAfter); const uid = this._page.registerLocatorHandler(params.selector, params.noWaitAfter);
return { uid }; return { uid };

Просмотреть файл

@ -400,6 +400,10 @@ export class FFPage implements PageDelegate {
return success; return success;
} }
async forceGarbageCollection(): Promise<void> {
await this._session.send('Heap.collectGarbage');
}
async addInitScript(initScript: InitScript, worldName?: string): Promise<void> { async addInitScript(initScript: InitScript, worldName?: string): Promise<void> {
this._initScripts.push({ initScript, worldName }); this._initScripts.push({ initScript, worldName });
await this._session.send('Page.setInitScripts', { scripts: this._initScripts.map(s => ({ script: s.initScript.source, worldName: s.worldName })) }); await this._session.send('Page.setInitScripts', { scripts: this._initScripts.map(s => ({ script: s.initScript.source, worldName: s.worldName })) });

Просмотреть файл

@ -54,6 +54,7 @@ export interface PageDelegate {
reload(): Promise<void>; reload(): Promise<void>;
goBack(): Promise<boolean>; goBack(): Promise<boolean>;
goForward(): Promise<boolean>; goForward(): Promise<boolean>;
forceGarbageCollection(): Promise<void>;
addInitScript(initScript: InitScript): Promise<void>; addInitScript(initScript: InitScript): Promise<void>;
removeNonInternalInitScripts(): Promise<void>; removeNonInternalInitScripts(): Promise<void>;
closePage(runBeforeUnload: boolean): Promise<void>; closePage(runBeforeUnload: boolean): Promise<void>;
@ -430,6 +431,10 @@ export class Page extends SdkObject {
}), this._timeoutSettings.navigationTimeout(options)); }), this._timeoutSettings.navigationTimeout(options));
} }
forceGarbageCollection(): Promise<void> {
return this._delegate.forceGarbageCollection();
}
registerLocatorHandler(selector: string, noWaitAfter: boolean | undefined) { registerLocatorHandler(selector: string, noWaitAfter: boolean | undefined) {
const uid = ++this._lastLocatorHandlerUid; const uid = ++this._lastLocatorHandlerUid;
this._locatorHandlers.set(uid, { selector, noWaitAfter }); this._locatorHandlers.set(uid, { selector, noWaitAfter });

Просмотреть файл

@ -768,6 +768,10 @@ export class WKPage implements PageDelegate {
}); });
} }
async forceGarbageCollection(): Promise<void> {
await this._session.send('Heap.gc');
}
async addInitScript(initScript: InitScript): Promise<void> { async addInitScript(initScript: InitScript): Promise<void> {
await this._updateBootstrapScript(); await this._updateBootstrapScript();
} }

5
packages/playwright-core/types/types.d.ts поставляемый
Просмотреть файл

@ -2554,6 +2554,11 @@ export interface Page {
timeout?: number; timeout?: number;
}): Promise<void>; }): Promise<void>;
/**
* Force the browser to perform garbage collection.
*/
forceGarbageCollection(): Promise<void>;
/** /**
* Returns frame matching the specified criteria. Either `name` or `url` must be specified. * Returns frame matching the specified criteria. Either `name` or `url` must be specified.
* *

Просмотреть файл

@ -1933,6 +1933,7 @@ export interface PageChannel extends PageEventTarget, EventTargetChannel {
exposeBinding(params: PageExposeBindingParams, metadata?: CallMetadata): Promise<PageExposeBindingResult>; exposeBinding(params: PageExposeBindingParams, metadata?: CallMetadata): Promise<PageExposeBindingResult>;
goBack(params: PageGoBackParams, metadata?: CallMetadata): Promise<PageGoBackResult>; goBack(params: PageGoBackParams, metadata?: CallMetadata): Promise<PageGoBackResult>;
goForward(params: PageGoForwardParams, metadata?: CallMetadata): Promise<PageGoForwardResult>; goForward(params: PageGoForwardParams, metadata?: CallMetadata): Promise<PageGoForwardResult>;
forceGarbageCollection(params?: PageForceGarbageCollectionParams, metadata?: CallMetadata): Promise<PageForceGarbageCollectionResult>;
registerLocatorHandler(params: PageRegisterLocatorHandlerParams, metadata?: CallMetadata): Promise<PageRegisterLocatorHandlerResult>; registerLocatorHandler(params: PageRegisterLocatorHandlerParams, metadata?: CallMetadata): Promise<PageRegisterLocatorHandlerResult>;
resolveLocatorHandlerNoReply(params: PageResolveLocatorHandlerNoReplyParams, metadata?: CallMetadata): Promise<PageResolveLocatorHandlerNoReplyResult>; resolveLocatorHandlerNoReply(params: PageResolveLocatorHandlerNoReplyParams, metadata?: CallMetadata): Promise<PageResolveLocatorHandlerNoReplyResult>;
unregisterLocatorHandler(params: PageUnregisterLocatorHandlerParams, metadata?: CallMetadata): Promise<PageUnregisterLocatorHandlerResult>; unregisterLocatorHandler(params: PageUnregisterLocatorHandlerParams, metadata?: CallMetadata): Promise<PageUnregisterLocatorHandlerResult>;
@ -2070,6 +2071,9 @@ export type PageGoForwardOptions = {
export type PageGoForwardResult = { export type PageGoForwardResult = {
response?: ResponseChannel, response?: ResponseChannel,
}; };
export type PageForceGarbageCollectionParams = {};
export type PageForceGarbageCollectionOptions = {};
export type PageForceGarbageCollectionResult = void;
export type PageRegisterLocatorHandlerParams = { export type PageRegisterLocatorHandlerParams = {
selector: string, selector: string,
noWaitAfter?: boolean, noWaitAfter?: boolean,

Просмотреть файл

@ -1430,6 +1430,8 @@ Page:
slowMo: true slowMo: true
snapshot: true snapshot: true
forceGarbageCollection:
registerLocatorHandler: registerLocatorHandler:
parameters: parameters:
selector: string selector: string

Просмотреть файл

@ -0,0 +1,27 @@
/**
* Copyright 2024 Adobe Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { test, expect } from './pageTest';
test('should work', async ({ page }) => {
await page.evaluate(() => {
globalThis.objectToDestroy = {};
globalThis.weakRef = new WeakRef(globalThis.objectToDestroy);
});
await page.evaluate(() => globalThis.objectToDestroy = null);
await page.forceGarbageCollection();
expect(await page.evaluate(() => globalThis.weakRef.deref())).toBe(undefined);
});