Merge branch 'master' into ff-use-workers

This commit is contained in:
Andrey Lushnikov 2020-01-17 17:37:21 -08:00 committed by GitHub
commit b9a81f42d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 988 additions and 344 deletions

View file

@ -2,13 +2,13 @@
We currently have 4 build bots that produce the following builds
- **[buildbot-linux]** Ubuntu 18.04 machine
- builds: `Webkit-Linux`, `Firefox-Linux`
- builds: `webkit-gtk`, `webkit-wpe`, `firefox-linux`
- **[buildbot-mac-10.14]** Mac 10.14 machine
- builds: `WebKit-mac-10.14`, `Firefox-Mac`
- builds: `webKit-mac-10.14`, `firefox-mac`
- **[buildbot-mac-10.15]** machine
- builds: `WebKit-mac-10.15`
- builds: `webkit-mac-10.15`
- **[buildbot-windows]** Windows 10 machine
- builds: `Firefox-win32`, `Firefox-win64`, `webkit-win64`
- builds: `firefox-win32`, `firefox-win64`, `webkit-win64`
This document describes setting up bots infrastructure to produce
browser builds.
@ -105,6 +105,18 @@ The `core.longpaths` is needed for webkit since it has some very long layout pat
Run `c:\mozilla-build\start-shell.bat` and checkout PlayWright repo to `/c/playwright`.
### 7. Create a c:\WEBKIT_WIN64_LIBS\ directory with win64 dlls
Create a new `c:\WEBKIT_WIN64_LIBS` folder and copy the following libraries from `C:\Windows\System32` into it:
- `msvcp140.dll`
- `vcruntime140.dll`
- `vcruntime140_1.dll`
> **NOTE**: these libraries are expected by `//browser_patches/webkit/archive.sh`.
This is necessary since mingw is a 32-bit application and cannot access the `C:\Windows\System32` folder due to [Windows FileSystem Redirector](https://docs.microsoft.com/en-us/windows/win32/winprog64/file-system-redirector?redirectedfrom=MSDN). ([StackOverflow question](https://stackoverflow.com/questions/18982551/is-mingw-caching-windows-directory-contents))
## Running Build Loop
1. Launch `c:\mozilla-build/start-shell.bat`

View file

@ -55,7 +55,9 @@ if [[ -n $(git status -s) ]]; then
fi
git pull origin master
../checkout_build_archive_upload.sh firefox >/tmp/$(basename $0)-firefox-log.log || true
../checkout_build_archive_upload.sh firefox-linux >/tmp/$(basename $0)--firefox-linux.log || true
git pull origin master
../checkout_build_archive_upload.sh webkit >/tmp/$(basename $0)-webkit-log.log || true
../checkout_build_archive_upload.sh webkit-gtk >/tmp/$(basename $0)--webkit-gtk.log || true
../checkout_build_archive_upload.sh webkit-wpe >/tmp/$(basename $0)--webkit-wpe.log || true
../checkout_build_archive_upload.sh webkit-gtk-wpe >/tmp/$(basename $0)--webkit-gtk-wpe.log || true

View file

@ -61,7 +61,7 @@ if [[ -n $(git status -s) ]]; then
fi
git pull origin master
../checkout_build_archive_upload.sh firefox >/tmp/$(basename $0)-firefox-log.log || true
../checkout_build_archive_upload.sh firefox-mac >/tmp/$(basename $0)--firefox-mac.log || true
git pull origin master
../checkout_build_archive_upload.sh webkit >/tmp/$(basename $0)-webkit-log.log || true
../checkout_build_archive_upload.sh webkit-mac-10.14 >/tmp/$(basename $0)--webkit-mac-10.14.log || true

View file

@ -62,4 +62,4 @@ if [[ -n $(git status -s) ]]; then
fi
git pull origin master
../checkout_build_archive_upload.sh webkit >/tmp/$(basename $0)-webkit-log.log || true
../checkout_build_archive_upload.sh webkit-mac-10.15 >/tmp/$(basename $0)--webkit-mac-10.15.log || true

View file

@ -45,9 +45,9 @@ while true; do
iteration=$(( iteration + 1 ))
echo "== ITERATION ${iteration} =="
git pull origin master
../checkout_build_archive_upload.sh webkit || true
../checkout_build_archive_upload.sh webkit-win64 || true
git pull origin master
../checkout_build_archive_upload.sh firefox || true
../checkout_build_archive_upload.sh firefox-win32 || true
git pull origin master
../checkout_build_archive_upload.sh firefox-win64 || true
newTimestamp=$(date +%s)

View file

@ -3,7 +3,7 @@ set -e
set +x
if [[ ($1 == '--help') || ($1 == '-h') ]]; then
echo "usage: $(basename $0) [firefox|firefox-win64|webkit] [-f|--force]"
echo "usage: $(basename $0) [firefox-linux|firefox-win32|firefox-win64|webkit-gtk|webkit-wpe|webkit-gtk-wpe|webkit-win64|webkit-mac-10.14|webkit-mac-10.15] [-f|--force]"
echo
echo "Prepares checkout under browser folder, applies patches, builds, archives, and uploades if build is missing."
echo "Script will bail out early if the build for the browser version is already present."
@ -15,22 +15,75 @@ if [[ ($1 == '--help') || ($1 == '-h') ]]; then
fi
if [[ $# == 0 ]]; then
echo "missing browser: 'firefox' or 'webkit'"
echo "missing build flavor!"
echo "try './$(basename $0) --help' for more information"
exit 1
fi
CURRENT_HOST_OS="$(uname)"
CURRENT_HOST_OS_VERSION=""
if [[ "$CURRENT_HOST_OS" == "Darwin" ]]; then
CURRENT_HOST_OS_VERSION=$(sw_vers -productVersion | grep -o '^\d\+.\d\+')
fi
BROWSER_NAME=""
EXTRA_BUILD_ARGS=""
if [[ ("$1" == "firefox") || ("$1" == "firefox/") ]]; then
EXTRA_ARCHIVE_ARGS=""
BUILD_FLAVOR="$1"
EXPECTED_HOST_OS=""
EXPECTED_HOST_OS_VERSION=""
if [[ "$BUILD_FLAVOR" == "firefox-linux" ]]; then
BROWSER_NAME="firefox"
elif [[ ("$1" == "firefox-win64") || ("$1" == "firefox-win64/") ]]; then
EXPECTED_HOST_OS="Linux"
elif [[ "$BUILD_FLAVOR" == "firefox-mac" ]]; then
BROWSER_NAME="firefox"
EXPECTED_HOST_OS="Darwin"
EXPECTED_HOST_OS_VERSION="10.14"
elif [[ "$BUILD_FLAVOR" == "firefox-win32" ]]; then
BROWSER_NAME="firefox"
EXPECTED_HOST_OS="MINGW"
elif [[ "$BUILD_FLAVOR" == "firefox-win64" ]]; then
BROWSER_NAME="firefox"
EXTRA_BUILD_ARGS="--win64"
elif [[ ("$1" == "webkit") || ("$1" == "webkit/") ]]; then
EXPECTED_HOST_OS="MINGW"
elif [[ "$BUILD_FLAVOR" == "webkit-gtk" ]]; then
BROWSER_NAME="webkit"
EXPECTED_HOST_OS="Linux"
elif [[ "$BUILD_FLAVOR" == "webkit-wpe" ]]; then
BROWSER_NAME="webkit"
EXTRA_BUILD_ARGS="--wpe"
EXTRA_ARCHIVE_ARGS="--wpe"
EXPECTED_HOST_OS="Linux"
elif [[ "$BUILD_FLAVOR" == "webkit-gtk-wpe" ]]; then
BROWSER_NAME="webkit"
EXPECTED_HOST_OS="Linux"
elif [[ "$BUILD_FLAVOR" == "webkit-win64" ]]; then
BROWSER_NAME="webkit"
EXPECTED_HOST_OS="MINGW"
elif [[ "$BUILD_FLAVOR" == "webkit-mac-10.14" ]]; then
BROWSER_NAME="webkit"
EXPECTED_HOST_OS="Darwin"
EXPECTED_HOST_OS_VERSION="10.14"
elif [[ "$BUILD_FLAVOR" == "webkit-mac-10.15" ]]; then
BROWSER_NAME="webkit"
EXPECTED_HOST_OS="Darwin"
EXPECTED_HOST_OS_VERSION="10.15"
else
echo ERROR: unknown browser - "$1"
echo ERROR: unknown build flavor - "$BUILD_FLAVOR"
exit 1
fi
if [[ "$CURRENT_HOST_OS" != $EXPECTED_HOST_OS* ]]; then
echo "ERROR: cannot build $BUILD_FLAVOR"
echo " -- expected OS: $EXPECTED_HOST_OS"
echo " -- current OS: $CURRENT_HOST_OS"
exit 1
fi
if [[ "$CURRENT_HOST_OS_VERSION" != "$EXPECTED_HOST_OS_VERSION" ]]; then
echo "ERROR: cannot build $BUILD_FLAVOR"
echo " -- expected OS Version: $EXPECTED_HOST_OS_VERSION"
echo " -- current OS Version: $CURRENT_HOST_OS_VERSION"
exit 1
fi
@ -50,7 +103,7 @@ BUILD_NUMBER=$(cat ./$BROWSER_NAME/BUILD_NUMBER)
# pull from upstream and check if a new build has to be uploaded.
if ! [[ ($2 == '-f') || ($2 == '--force') ]]; then
if ./upload.sh $1 --check; then
if ./upload.sh $BUILD_FLAVOR --check; then
echo "Build is already uploaded - no changes."
exit 0
else
@ -69,36 +122,46 @@ cd -
source ./buildbots/send_telegram_message.sh
LAST_COMMIT_MESSAGE=$(git log --format=%s -n 1 HEAD -- ./$BROWSER_NAME/BUILD_NUMBER)
BUILD_ALIAS="<b>[[$(./upload.sh $1 --show-alias)]]</b> $LAST_COMMIT_MESSAGE"
BUILD_ALIAS="<b>[[$BUILD_FLAVOR r$BUILD_NUMBER]]</b> $LAST_COMMIT_MESSAGE"
send_telegram_message "$BUILD_ALIAS -- started ⏳"
echo "-- preparing checkout"
if ! ./prepare_checkout.sh $BROWSER_NAME; then
if [[ "$BUILD_FLAVOR" == "webkit-gtk-wpe" ]]; then
echo "-- combining binaries together"
if ! ./webkit/download_gtk_and_wpe_and_zip_together.sh $ZIP_PATH; then
send_telegram_message "$BUILD_ALIAS -- ./download_gtk_and_wpe_and_zip_together.sh failed! ❌"
exit 1
fi
else
echo "-- preparing checkout"
if ! ./prepare_checkout.sh $BROWSER_NAME; then
send_telegram_message "$BUILD_ALIAS -- ./prepare_checkout.sh failed! ❌"
exit 1
fi
fi
echo "-- cleaning"
if ! ./$BROWSER_NAME/clean.sh; then
echo "-- cleaning"
if ! ./$BROWSER_NAME/clean.sh; then
send_telegram_message "$BUILD_ALIAS -- ./clean.sh failed! ❌"
exit 1
fi
fi
echo "-- building"
if ! ./$BROWSER_NAME/build.sh "$EXTRA_BUILD_ARGS"; then
echo "-- building"
if ! ./$BROWSER_NAME/build.sh "$EXTRA_BUILD_ARGS"; then
send_telegram_message "$BUILD_ALIAS -- ./build.sh failed! ❌"
exit 1
fi
fi
echo "-- archiving to $ZIP_PATH"
if ! ./$BROWSER_NAME/archive.sh $ZIP_PATH; then
echo "-- archiving to $ZIP_PATH"
if ! ./$BROWSER_NAME/archive.sh $ZIP_PATH "$EXTRA_ARCHIVE_ARGS"; then
send_telegram_message "$BUILD_ALIAS -- ./archive.sh failed! ❌"
exit 1
fi
fi
echo "-- uploading"
if ! ./upload.sh $1 $ZIP_PATH; then
if ! ./upload.sh $BUILD_FLAVOR $ZIP_PATH; then
send_telegram_message "$BUILD_ALIAS -- ./upload.sh failed! ❌"
exit 1
fi
send_telegram_message "$BUILD_ALIAS -- uploaded ✅"
UPLOAD_SIZE=$(du -h "$ZIP_PATH" | awk '{print $1}')
send_telegram_message "$BUILD_ALIAS -- $UPLOAD_SIZE uploaded ✅"

70
browser_patches/download.sh Executable file
View file

@ -0,0 +1,70 @@
#!/bin/bash
set -e
set +x
trap "cd $(pwd -P)" EXIT
cd "$(dirname "$0")"
if [[ ($1 == '--help') || ($1 == '-h') ]]; then
echo "usage: $(basename $0) [webkit-gtk|webkit-wpe] [zip-path]"
echo
echo "Download .zip of a browser build."
echo
echo "NOTE: \$AZ_ACCOUNT_KEY (azure account name) and \$AZ_ACCOUNT_NAME (azure account name)"
echo "env variables are required to download builds from CDN."
exit 0
fi
if [[ (-z $AZ_ACCOUNT_KEY) || (-z $AZ_ACCOUNT_NAME) ]]; then
echo "ERROR: Either \$AZ_ACCOUNT_KEY or \$AZ_ACCOUNT_NAME environment variable is missing."
echo " 'Azure Account Name' and 'Azure Account Key' secrets that are required"
echo " to download builds from Azure CDN."
exit 1
fi
if [[ $# < 1 ]]; then
echo "missing build flavor"
echo "try '$(basename $0) --help' for more information"
exit 1
fi
BUILD_FLAVOR="$1"
BROWSER_NAME=""
BLOB_NAME=""
if [[ "$BUILD_FLAVOR" == "webkit-gtk" ]]; then
BROWSER_NAME="webkit"
BLOB_NAME="minibrowser-gtk.zip"
elif [[ "$BUILD_FLAVOR" == "webkit-wpe" ]]; then
BROWSER_NAME="webkit"
BLOB_NAME="minibrowser-wpe.zip"
else
echo ERROR: unsupported build flavor - "$BUILD_FLAVOR"
exit 1
fi
BUILD_NUMBER=$(cat ./$BROWSER_NAME/BUILD_NUMBER)
BLOB_PATH="$BROWSER_NAME/$BUILD_NUMBER/$BLOB_NAME"
if [[ $# < 2 ]]; then
echo "missing path to zip archive to download to"
echo "try '$(basename $0) --help' for more information"
exit 1
fi
ZIP_PATH="$2"
if [[ -f $ZIP_PATH ]]; then
echo "ERROR: $ZIP_PATH exists"
exit 1
fi
if ! [[ $ZIP_PATH == *.zip ]]; then
echo "ERROR: $ZIP_PATH is not a zip archive (must have a .zip extension)"
exit 1
fi
az storage blob download -c builds --account-key $AZ_ACCOUNT_KEY --account-name $AZ_ACCOUNT_NAME -f $ZIP_PATH -n "$BLOB_PATH"
echo "DOWNLOAD SUCCESSFUL!"
echo "-- SRC: $ZIP_PATH"
echo "-- SIZE: $(du -h "$ZIP_PATH" | awk '{print $1}')"
echo "-- DST: $BLOB_PATH"

View file

@ -1 +1 @@
1014
1016

View file

@ -10,12 +10,6 @@ if [[ ("$1" == "-h") || ("$1" == "--help") ]]; then
exit 0
fi
if [[ $# != 1 ]]; then
echo "error: missing zip output path"
echo "try '$(basename $0) --help' for details"
exit 1
fi
ZIP_PATH=$1
if [[ $ZIP_PATH != /* ]]; then
echo "ERROR: path $ZIP_PATH is not absolute"

View file

@ -1824,10 +1824,10 @@ index 0000000000000000000000000000000000000000..2508cce41565023b7fee9c7b85afe8ec
+
diff --git a/testing/juggler/content/PageAgent.js b/testing/juggler/content/PageAgent.js
new file mode 100644
index 0000000000000000000000000000000000000000..03c4c9717148169110f7e7d19306a76984ed4860
index 0000000000000000000000000000000000000000..37ab5f56739cfd16200a4ada9f4cf83436688eba
--- /dev/null
+++ b/testing/juggler/content/PageAgent.js
@@ -0,0 +1,721 @@
@@ -0,0 +1,843 @@
+"use strict";
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const Ci = Components.interfaces;
@ -1839,11 +1839,28 @@ index 0000000000000000000000000000000000000000..03c4c9717148169110f7e7d19306a769
+
+const helper = new Helper();
+
+const registeredWorkerListeners = new Map();
+const workerListener = {
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIWorkerDebuggerListener]),
+ onMessage: (wrapped) => {
+ const message = JSON.parse(wrapped);
+ const listener = registeredWorkerListeners.get(message.workerId);
+ if (listener)
+ listener(message);
+ },
+ onClose: () => {
+ },
+ onError: (filename, lineno, message) => {
+ dump(`Error in worker: ${message} @${filename}:${lineno}\n`);
+ },
+};
+
+class FrameData {
+ constructor(agent, frame) {
+ this._agent = agent;
+ this._frame = frame;
+ this._isolatedWorlds = new Map();
+ this._workers = new Map();
+ this.reset();
+ }
+
@ -1913,6 +1930,59 @@ index 0000000000000000000000000000000000000000..03c4c9717148169110f7e7d19306a769
+ }
+ throw new Error('Cannot find object with id = ' + objectId);
+ }
+
+ workerCreated(workerDebugger) {
+ const workerId = helper.generateId();
+ this._workers.set(workerId, workerDebugger);
+ this._agent._session.emitEvent('Page.workerCreated', {
+ workerId,
+ frameId: this._frame.id(),
+ url: workerDebugger.url,
+ });
+ // Note: this does not interoperate with firefox devtools.
+ if (!workerDebugger.isInitialized) {
+ workerDebugger.initialize('chrome://juggler/content/content/WorkerMain.js');
+ workerDebugger.addListener(workerListener);
+ }
+ registeredWorkerListeners.set(workerId, message => {
+ if (message.command === 'dispatch') {
+ this._agent._session.emitEvent('Page.dispatchMessageFromWorker', {
+ workerId,
+ message: message.message,
+ });
+ }
+ if (message.command === 'console')
+ this._agent._runtime.filterConsoleMessage(message.hash);
+ });
+ workerDebugger.postMessage(JSON.stringify({command: 'connect', workerId}));
+ }
+
+ workerDestroyed(wd) {
+ for (const [workerId, workerDebugger] of this._workers) {
+ if (workerDebugger === wd) {
+ this._agent._session.emitEvent('Page.workerDestroyed', {
+ workerId,
+ });
+ this._workers.delete(workerId);
+ registeredWorkerListeners.delete(workerId);
+ }
+ }
+ }
+
+ sendMessageToWorker(workerId, message) {
+ const workerDebugger = this._workers.get(workerId);
+ if (!workerDebugger)
+ throw new Error('Cannot find worker with id "' + workerId + '"');
+ workerDebugger.postMessage(JSON.stringify({command: 'dispatch', workerId, message}));
+ }
+
+ dispose() {
+ for (const [workerId, workerDebugger] of this._workers) {
+ workerDebugger.postMessage(JSON.stringify({command: 'disconnect', workerId}));
+ registeredWorkerListeners.delete(workerId);
+ }
+ this._workers.clear();
+ }
+}
+
+class PageAgent {
@ -1934,6 +2004,24 @@ index 0000000000000000000000000000000000000000..03c4c9717148169110f7e7d19306a769
+ this._docShell = docShell;
+ this._initialDPPX = docShell.contentViewer.overrideDPPX;
+ this._customScrollbars = null;
+
+ this._wdm = Cc["@mozilla.org/dom/workers/workerdebuggermanager;1"].createInstance(Ci.nsIWorkerDebuggerManager);
+ this._wdmListener = {
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIWorkerDebuggerManagerListener]),
+ onRegister: this._onWorkerCreated.bind(this),
+ onUnregister: this._onWorkerDestroyed.bind(this),
+ };
+
+ this._runtime.setOnErrorFromWorker((domWindow, message, stack) => {
+ const frame = this._frameTree.frameForDocShell(domWindow.docShell);
+ if (!frame)
+ return;
+ this._session.emitEvent('Page.uncaughtError', {
+ frameId: frame.id(),
+ message,
+ stack,
+ });
+ });
+ }
+
+ async awaitViewportDimensions({width, height}) {
@ -2042,12 +2130,43 @@ index 0000000000000000000000000000000000000000..03c4c9717148169110f7e7d19306a769
+ helper.on(this._frameTree, 'navigationaborted', this._onNavigationAborted.bind(this)),
+ helper.on(this._frameTree, 'samedocumentnavigation', this._onSameDocumentNavigation.bind(this)),
+ ];
+
+ this._wdm.addListener(this._wdmListener);
+ for (const workerDebugger of this._wdm.getWorkerDebuggerEnumerator())
+ this._onWorkerCreated(workerDebugger);
+ }
+
+ setInterceptFileChooserDialog({enabled}) {
+ this._docShell.fileInputInterceptionEnabled = !!enabled;
+ }
+
+ _frameForWorker(workerDebugger) {
+ if (workerDebugger.type !== Ci.nsIWorkerDebugger.TYPE_DEDICATED)
+ return null;
+ const docShell = workerDebugger.window.docShell;
+ const frame = this._frameTree.frameForDocShell(docShell);
+ return frame ? this._frameData.get(frame) : null;
+ }
+
+ _onWorkerCreated(workerDebugger) {
+ const frameData = this._frameForWorker(workerDebugger);
+ if (frameData)
+ frameData.workerCreated(workerDebugger);
+ }
+
+ _onWorkerDestroyed(workerDebugger) {
+ const frameData = this._frameForWorker(workerDebugger);
+ if (frameData)
+ frameData.workerDestroyed(workerDebugger);
+ }
+
+ sendMessageToWorker({frameId, workerId, message}) {
+ const frame = this._frameTree.frame(frameId);
+ if (!frame)
+ throw new Error('Failed to find frame with id = ' + frameId);
+ this._frameData.get(frame).sendMessageToWorker(workerId, message);
+ }
+
+ _filePickerShown(inputElement) {
+ if (inputElement.ownerGlobal.docShell !== this._docShell)
+ return;
@ -2166,7 +2285,10 @@ index 0000000000000000000000000000000000000000..03c4c9717148169110f7e7d19306a769
+ }
+
+ dispose() {
+ for (const frameData of this._frameData.values())
+ frameData.dispose();
+ helper.removeListeners(this._eventListeners);
+ this._wdm.removeListener(this._wdmListener);
+ }
+
+ async navigate({frameId, url, referer}) {
@ -2551,20 +2673,24 @@ index 0000000000000000000000000000000000000000..03c4c9717148169110f7e7d19306a769
+
diff --git a/testing/juggler/content/RuntimeAgent.js b/testing/juggler/content/RuntimeAgent.js
new file mode 100644
index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac357fb9b60
index 0000000000000000000000000000000000000000..5765d5c3b1de7b9383a80435b37b034d6951d981
--- /dev/null
+++ b/testing/juggler/content/RuntimeAgent.js
@@ -0,0 +1,478 @@
@@ -0,0 +1,545 @@
+"use strict";
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
+const {addDebuggerToGlobal} = ChromeUtils.import("resource://gre/modules/jsdebugger.jsm", {});
+// Note: this file should be loadabale with eval() into worker environment.
+// Avoid Components.*, ChromeUtils and global const variables.
+
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+addDebuggerToGlobal(Cu.getGlobalForObject(this));
+const helper = new Helper();
+if (!this.Debugger) {
+ // Worker has a Debugger defined already.
+ const {addDebuggerToGlobal} = ChromeUtils.import("resource://gre/modules/jsdebugger.jsm", {});
+ addDebuggerToGlobal(Components.utils.getGlobalForObject(this));
+}
+
+let lastId = 0;
+function generateId() {
+ return 'id-' + (++lastId);
+}
+
+const consoleLevelToProtocolType = {
+ 'dir': 'dir',
@ -2603,13 +2729,39 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+]);
+
+class RuntimeAgent {
+ constructor(session) {
+ constructor(session, onWorkerConsoleMessage) {
+ this._debugger = new Debugger();
+ this._pendingPromises = new Map();
+ this._session = session;
+ this._executionContexts = new Map();
+ this._windowToExecutionContext = new Map();
+ this._consoleServiceListener = {
+ this._eventListeners = [];
+ this._enabled = false;
+ this._filteredConsoleMessageHashes = new Set();
+ this._onErrorFromWorker = null;
+ this._onWorkerConsoleMessage = onWorkerConsoleMessage;
+ }
+
+ enable() {
+ if (this._enabled)
+ return;
+ this._enabled = true;
+ for (const executionContext of this._executionContexts.values())
+ this._notifyExecutionContextCreated(executionContext);
+
+ const isWorker = !!this._onWorkerConsoleMessage;
+ if (isWorker) {
+ this._registerConsoleEventHandler();
+ } else {
+ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ this._registerConsoleServiceListener(Services);
+ this._registerConsoleObserver(Services);
+ }
+ }
+
+ _registerConsoleServiceListener(Services) {
+ const Ci = Components.interfaces;
+ const consoleServiceListener = {
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIConsoleListener]),
+
+ observe: message => {
@ -2618,6 +2770,11 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ return;
+ }
+ const errorWindow = Services.wm.getOuterWindowWithId(message.outerWindowID);
+ if (message.category === 'Web Worker' && (message.flags & Ci.nsIScriptError.exceptionFlag)) {
+ if (this._onErrorFromWorker)
+ this._onErrorFromWorker(errorWindow, message.message, '' + message.stack);
+ return;
+ }
+ const executionContext = this._windowToExecutionContext.get(errorWindow);
+ if (!executionContext)
+ return;
@ -2641,47 +2798,67 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ });
+ },
+ };
+
+ this._eventListeners = [];
+ this._enabled = false;
+ Services.console.registerListener(consoleServiceListener);
+ this._eventListeners.push(() => Services.console.unregisterListener(consoleServiceListener));
+ }
+
+ _consoleAPICalled({wrappedJSObject}, topic, data) {
+ const type = consoleLevelToProtocolType[wrappedJSObject.level];
+ if (!type)
+ _registerConsoleObserver(Services) {
+ const consoleObserver = ({wrappedJSObject}, topic, data) => {
+ const hash = this._consoleMessageHash(wrappedJSObject);
+ if (this._filteredConsoleMessageHashes.has(hash)) {
+ this._filteredConsoleMessageHashes.delete(hash);
+ return;
+ }
+ const executionContext = Array.from(this._executionContexts.values()).find(context => {
+ const domWindow = context._domWindow;
+ return domWindow && domWindow.windowUtils.currentInnerWindowID === wrappedJSObject.innerID;
+ });
+ if (!executionContext)
+ return;
+ const args = wrappedJSObject.arguments.map(arg => executionContext.rawValueToRemoteObject(arg));
+ this._onConsoleMessage(executionContext, wrappedJSObject);
+ };
+ Services.obs.addObserver(consoleObserver, "console-api-log-event");
+ this._eventListeners.push(() => Services.obs.removeObserver(consoleObserver, "console-api-log-event"));
+ }
+
+ _registerConsoleEventHandler() {
+ setConsoleEventHandler(message => {
+ this._onWorkerConsoleMessage(this._consoleMessageHash(message));
+ const executionContext = Array.from(this._executionContexts.values())[0];
+ this._onConsoleMessage(executionContext, message);
+ });
+ this._eventListeners.push(() => setConsoleEventHandler(null));
+ }
+
+ filterConsoleMessage(messageHash) {
+ this._filteredConsoleMessageHashes.add(messageHash);
+ }
+
+ setOnErrorFromWorker(onErrorFromWorker) {
+ this._onErrorFromWorker = onErrorFromWorker;
+ }
+
+ _consoleMessageHash(message) {
+ return `${message.timeStamp}/${message.filename}/${message.lineNumber}/${message.columnNumber}/${message.sourceId}/${message.level}`;
+ }
+
+ _onConsoleMessage(executionContext, message) {
+ const type = consoleLevelToProtocolType[message.level];
+ if (!type)
+ return;
+ const args = message.arguments.map(arg => executionContext.rawValueToRemoteObject(arg));
+ this._session.emitEvent('Runtime.console', {
+ args,
+ type,
+ executionContextId: executionContext.id(),
+ location: {
+ lineNumber: wrappedJSObject.lineNumber - 1,
+ columnNumber: wrappedJSObject.columnNumber - 1,
+ url: wrappedJSObject.filename,
+ lineNumber: message.lineNumber - 1,
+ columnNumber: message.columnNumber - 1,
+ url: message.filename,
+ },
+ });
+ }
+
+ enable() {
+ if (this._enabled)
+ return;
+ this._enabled = true;
+ for (const executionContext of this._executionContexts.values())
+ this._notifyExecutionContextCreated(executionContext);
+ Services.console.registerListener(this._consoleServiceListener);
+ this._eventListeners = [
+ () => Services.console.unregisterListener(this._consoleServiceListener),
+ helper.addObserver(this._consoleAPICalled.bind(this), "console-api-log-event"),
+ ];
+ }
+
+ _notifyExecutionContextCreated(executionContext) {
+ if (!this._enabled)
+ return;
@ -2700,7 +2877,9 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ }
+
+ dispose() {
+ helper.removeListeners(this._eventListeners);
+ for (const tearDown of this._eventListeners)
+ tearDown.call(null);
+ this._eventListeners = [];
+ }
+
+ async _awaitPromise(executionContext, obj, exceptionDetails = {}) {
@ -2742,8 +2921,10 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ }
+
+ createExecutionContext(domWindow, contextGlobal, auxData) {
+ const context = new ExecutionContext(this, domWindow, this._debugger.addDebuggee(contextGlobal), auxData);
+ // Note: domWindow is null for workers.
+ const context = new ExecutionContext(this, domWindow, contextGlobal, this._debugger.addDebuggee(contextGlobal), auxData);
+ this._executionContexts.set(context._id, context);
+ if (domWindow)
+ this._windowToExecutionContext.set(domWindow, context);
+ this._notifyExecutionContextCreated(context);
+ return context;
@ -2765,8 +2946,9 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ }
+ if (!this._pendingPromises.size)
+ this._debugger.onPromiseSettled = undefined;
+ this._debugger.removeDebuggee(destroyedContext._domWindow);
+ this._debugger.removeDebuggee(destroyedContext._contextGlobal);
+ this._executionContexts.delete(destroyedContext._id);
+ if (destroyedContext._domWindow)
+ this._windowToExecutionContext.delete(destroyedContext._domWindow);
+ this._notifyExecutionContextDestroyed(destroyedContext);
+ }
@ -2813,12 +2995,13 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+}
+
+class ExecutionContext {
+ constructor(runtime, domWindow, global, auxData) {
+ constructor(runtime, domWindow, contextGlobal, global, auxData) {
+ this._runtime = runtime;
+ this._domWindow = domWindow;
+ this._contextGlobal = contextGlobal;
+ this._global = global;
+ this._remoteObjects = new Map();
+ this._id = helper.generateId();
+ this._id = generateId();
+ this._auxData = auxData;
+ this._jsonStringifyObject = this._global.executeInGlobal(`((stringify, dateProto, object) => {
+ const oldToJson = dateProto.toJSON;
@ -2839,9 +3022,9 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ }
+
+ async evaluateScript(script, exceptionDetails = {}) {
+ const userInputHelper = this._domWindow.windowUtils.setHandlingUserInput(true);
+ const userInputHelper = this._domWindow ? this._domWindow.windowUtils.setHandlingUserInput(true) : null;
+ let {success, obj} = this._getResult(this._global.executeInGlobal(script), exceptionDetails);
+ userInputHelper.destruct();
+ userInputHelper && userInputHelper.destruct();
+ if (!success)
+ return null;
+ if (obj && obj.isPromise) {
@ -2873,9 +3056,9 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ default: return this._toDebugger(arg.value);
+ }
+ });
+ const userInputHelper = this._domWindow.windowUtils.setHandlingUserInput(true);
+ const userInputHelper = this._domWindow ? this._domWindow.windowUtils.setHandlingUserInput(true) : null;
+ let {success, obj} = this._getResult(funEvaluation.obj.apply(null, args), exceptionDetails);
+ userInputHelper.destruct();
+ userInputHelper && userInputHelper.destruct();
+ if (!success)
+ return null;
+ if (obj && obj.isPromise) {
@ -2898,9 +3081,15 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ return this._createRemoteObject(debuggerObj);
+ }
+
+ _instanceOf(debuggerObj, rawObj, className) {
+ if (this._domWindow)
+ return rawObj instanceof this._domWindow[className];
+ return this._global.executeInGlobalWithBindings('o instanceof this[className]', {o: debuggerObj, className: this._global.makeDebuggeeValue(className)}).return;
+ }
+
+ _createRemoteObject(debuggerObj) {
+ if (debuggerObj instanceof Debugger.Object) {
+ const objectId = helper.generateId();
+ const objectId = generateId();
+ this._remoteObjects.set(objectId, debuggerObj);
+ const rawObj = debuggerObj.unsafeDereference();
+ const type = typeof rawObj;
@ -2911,35 +3100,35 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ subtype = 'array';
+ else if (Object.is(rawObj, null))
+ subtype = 'null';
+ else if (rawObj instanceof this._domWindow.Node)
+ else if (this._instanceOf(debuggerObj, rawObj, 'Node'))
+ subtype = 'node';
+ else if (rawObj instanceof this._domWindow.RegExp)
+ else if (this._instanceOf(debuggerObj, rawObj, 'RegExp'))
+ subtype = 'regexp';
+ else if (rawObj instanceof this._domWindow.Date)
+ else if (this._instanceOf(debuggerObj, rawObj, 'Date'))
+ subtype = 'date';
+ else if (rawObj instanceof this._domWindow.Map)
+ else if (this._instanceOf(debuggerObj, rawObj, 'Map'))
+ subtype = 'map';
+ else if (rawObj instanceof this._domWindow.Set)
+ else if (this._instanceOf(debuggerObj, rawObj, 'Set'))
+ subtype = 'set';
+ else if (rawObj instanceof this._domWindow.WeakMap)
+ else if (this._instanceOf(debuggerObj, rawObj, 'WeakMap'))
+ subtype = 'weakmap';
+ else if (rawObj instanceof this._domWindow.WeakSet)
+ else if (this._instanceOf(debuggerObj, rawObj, 'WeakSet'))
+ subtype = 'weakset';
+ else if (rawObj instanceof this._domWindow.Error)
+ else if (this._instanceOf(debuggerObj, rawObj, 'Error'))
+ subtype = 'error';
+ else if (rawObj instanceof this._domWindow.Promise)
+ else if (this._instanceOf(debuggerObj, rawObj, 'Promise'))
+ subtype = 'promise';
+ else if ((rawObj instanceof this._domWindow.Int8Array) || (rawObj instanceof this._domWindow.Uint8Array) ||
+ (rawObj instanceof this._domWindow.Uint8ClampedArray) || (rawObj instanceof this._domWindow.Int16Array) ||
+ (rawObj instanceof this._domWindow.Uint16Array) || (rawObj instanceof this._domWindow.Int32Array) ||
+ (rawObj instanceof this._domWindow.Uint32Array) || (rawObj instanceof this._domWindow.Float32Array) ||
+ (rawObj instanceof this._domWindow.Float64Array)) {
+ else if ((this._instanceOf(debuggerObj, rawObj, 'Int8Array')) || (this._instanceOf(debuggerObj, rawObj, 'Uint8Array')) ||
+ (this._instanceOf(debuggerObj, rawObj, 'Uint8ClampedArray')) || (this._instanceOf(debuggerObj, rawObj, 'Int16Array')) ||
+ (this._instanceOf(debuggerObj, rawObj, 'Uint16Array')) || (this._instanceOf(debuggerObj, rawObj, 'Int32Array')) ||
+ (this._instanceOf(debuggerObj, rawObj, 'Uint32Array')) || (this._instanceOf(debuggerObj, rawObj, 'Float32Array')) ||
+ (this._instanceOf(debuggerObj, rawObj, 'Float64Array'))) {
+ subtype = 'typedarray';
+ }
+ return {objectId, type, subtype};
+ }
+ if (typeof debuggerObj === 'symbol') {
+ const objectId = helper.generateId();
+ const objectId = generateId();
+ this._remoteObjects.set(objectId, debuggerObj);
+ return {objectId, type: 'symbol'};
+ }
@ -3124,6 +3313,79 @@ index 0000000000000000000000000000000000000000..caee4df323d0a526ed7e38947c41c643
+var EXPORTED_SYMBOLS = ['ScrollbarManager'];
+this.ScrollbarManager = ScrollbarManager;
+
diff --git a/testing/juggler/content/WorkerMain.js b/testing/juggler/content/WorkerMain.js
new file mode 100644
index 0000000000000000000000000000000000000000..73cdce649608f068e59e1ff7808883c4482bff7e
--- /dev/null
+++ b/testing/juggler/content/WorkerMain.js
@@ -0,0 +1,67 @@
+"use strict";
+loadSubScript('chrome://juggler/content/content/RuntimeAgent.js');
+
+class WorkerSession {
+ constructor(workerId) {
+ this._workerId = workerId;
+ this._agents = {
+ Runtime: new RuntimeAgent(this, hash => this._send({command: 'console', hash})),
+ };
+ this._agents.Runtime.enable();
+ this._agents.Runtime.createExecutionContext(null /* domWindow */, global, {});
+ }
+
+ _send(command) {
+ postMessage(JSON.stringify({...command, workerId: this._workerId}));
+ }
+
+ _dispatchProtocolMessage(protocolMessage) {
+ this._send({command: 'dispatch', message: JSON.stringify(protocolMessage)});
+ }
+
+ emitEvent(eventName, params) {
+ this._dispatchProtocolMessage({method: eventName, params});
+ }
+
+ async _onMessage(message) {
+ const object = JSON.parse(message);
+ const id = object.id;
+ try {
+ const [domainName, methodName] = object.method.split('.');
+ const agent = this._agents[domainName];
+ if (!agent)
+ throw new Error(`unknown domain: ${domainName}`);
+ const handler = agent[methodName];
+ if (!handler)
+ throw new Error(`unknown method: ${domainName}.${methodName}`);
+ const result = await handler.call(agent, object.params);
+ this._dispatchProtocolMessage({id, result});
+ } catch (e) {
+ this._dispatchProtocolMessage({id, error: e.message + '\n' + e.stack});
+ }
+ }
+
+ dispose() {
+ for (const agent of Object.values(this._agents))
+ agent.dispose();
+ }
+}
+
+const workerSessions = new Map();
+
+this.addEventListener('message', event => {
+ const data = JSON.parse(event.data);
+ if (data.command === 'connect') {
+ const session = new WorkerSession(data.workerId);
+ workerSessions.set(data.workerId, session);
+ }
+ if (data.command === 'disconnect') {
+ const session = workerSessions.get(data.workerId);
+ session.dispose();
+ workerSessions.delete(data.workerId);
+ }
+ if (data.command === 'dispatch') {
+ const session = workerSessions.get(data.workerId);
+ session._onMessage(data.message);
+ }
+});
diff --git a/testing/juggler/content/floating-scrollbars.css b/testing/juggler/content/floating-scrollbars.css
new file mode 100644
index 0000000000000000000000000000000000000000..7709bdd34c65062fc63684ef17fc792d3991d965
@ -3243,10 +3505,10 @@ index 0000000000000000000000000000000000000000..8585092e04e7e763a0c115c28363e505
+
diff --git a/testing/juggler/jar.mn b/testing/juggler/jar.mn
new file mode 100644
index 0000000000000000000000000000000000000000..27f5a15fd7f14385bb1f080d5965d90e60423d1a
index 0000000000000000000000000000000000000000..76377927a8c9af3cac3b028ff754491966d03ba3
--- /dev/null
+++ b/testing/juggler/jar.mn
@@ -0,0 +1,29 @@
@@ -0,0 +1,30 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
@ -3272,6 +3534,7 @@ index 0000000000000000000000000000000000000000..27f5a15fd7f14385bb1f080d5965d90e
+ content/content/NetworkMonitor.js (content/NetworkMonitor.js)
+ content/content/PageAgent.js (content/PageAgent.js)
+ content/content/RuntimeAgent.js (content/RuntimeAgent.js)
+ content/content/WorkerMain.js (content/WorkerMain.js)
+ content/content/ScrollbarManager.js (content/ScrollbarManager.js)
+ content/content/floating-scrollbars.css (content/floating-scrollbars.css)
+ content/content/hidden-scrollbars.css (content/hidden-scrollbars.css)
@ -3392,10 +3655,10 @@ index 0000000000000000000000000000000000000000..708059a95b3a01f3d9c7b7ef4714ee6f
+this.BrowserHandler = BrowserHandler;
diff --git a/testing/juggler/protocol/Dispatcher.js b/testing/juggler/protocol/Dispatcher.js
new file mode 100644
index 0000000000000000000000000000000000000000..7b3a6fa4fe7a2b50a78ed446fbf5537504994798
index 0000000000000000000000000000000000000000..956988738079272be8d3998dcbbaa91abc415fcc
--- /dev/null
+++ b/testing/juggler/protocol/Dispatcher.js
@@ -0,0 +1,255 @@
@@ -0,0 +1,254 @@
+const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js");
+const {protocol, checkScheme} = ChromeUtils.import("chrome://juggler/content/protocol/Protocol.js");
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
@ -3630,7 +3893,6 @@ index 0000000000000000000000000000000000000000..7b3a6fa4fe7a2b50a78ed446fbf55375
+
+ _onMessage({data}) {
+ if (data.id) {
+ let id = data.id;
+ const {resolve, reject} = this._pendingMessages.get(data.id);
+ this._pendingMessages.delete(data.id);
+ if (data.error)
@ -3813,10 +4075,10 @@ index 0000000000000000000000000000000000000000..f5e7e919594b3778fd3046bf69d34878
+this.NetworkHandler = NetworkHandler;
diff --git a/testing/juggler/protocol/PageHandler.js b/testing/juggler/protocol/PageHandler.js
new file mode 100644
index 0000000000000000000000000000000000000000..bf59b2afa8692d02fd0ce664eec2e9827a8209d2
index 0000000000000000000000000000000000000000..23a32be2200e90e2e05d31aec85874a829cb1bbe
--- /dev/null
+++ b/testing/juggler/protocol/PageHandler.js
@@ -0,0 +1,281 @@
@@ -0,0 +1,285 @@
+"use strict";
+
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
@ -4039,6 +4301,10 @@ index 0000000000000000000000000000000000000000..bf59b2afa8692d02fd0ce664eec2e982
+ async handleFileChooser(options) {
+ return await this._contentSession.send('Page.handleFileChooser', options);
+ }
+
+ async sendMessageToWorker(options) {
+ return await this._contentSession.send('Page.sendMessageToWorker', options);
+ }
+}
+
+class Dialog {
@ -4249,10 +4515,10 @@ index 0000000000000000000000000000000000000000..78b6601b91d0b7fcda61114e6846aa07
+this.EXPORTED_SYMBOLS = ['t', 'checkScheme'];
diff --git a/testing/juggler/protocol/Protocol.js b/testing/juggler/protocol/Protocol.js
new file mode 100644
index 0000000000000000000000000000000000000000..75b5276d085bd4217389cd05099895ebec2438b6
index 0000000000000000000000000000000000000000..1eecb6120f101cb7506fcf8d40c177089e62671b
--- /dev/null
+++ b/testing/juggler/protocol/Protocol.js
@@ -0,0 +1,712 @@
@@ -0,0 +1,731 @@
+const {t, checkScheme} = ChromeUtils.import('chrome://juggler/content/protocol/PrimitiveTypes.js');
+
+// Protocol-specific types.
@ -4738,6 +5004,18 @@ index 0000000000000000000000000000000000000000..75b5276d085bd4217389cd05099895eb
+ executionContextId: t.String,
+ element: runtimeTypes.RemoteObject
+ },
+ 'workerCreated': {
+ workerId: t.String,
+ frameId: t.String,
+ url: t.String,
+ },
+ 'workerDestroyed': {
+ workerId: t.String,
+ },
+ 'dispatchMessageFromWorker': {
+ workerId: t.String,
+ message: t.String,
+ },
+ },
+
+ methods: {
@ -4940,6 +5218,13 @@ index 0000000000000000000000000000000000000000..75b5276d085bd4217389cd05099895eb
+ enabled: t.Boolean,
+ },
+ },
+ 'sendMessageToWorker': {
+ params: {
+ frameId: t.String,
+ workerId: t.String,
+ message: t.String,
+ },
+ },
+ },
+};
+

View file

@ -36,13 +36,15 @@ FFOX_ALIASES=(
WK_REVISION=$(cat ../webkit/BUILD_NUMBER)
WK_ARCHIVES=(
"$HOST/webkit/%s/minibrowser-linux.zip"
"$HOST/webkit/%s/minibrowser-gtk.zip"
"$HOST/webkit/%s/minibrowser-wpe.zip"
"$HOST/webkit/%s/minibrowser-mac-10.14.zip"
"$HOST/webkit/%s/minibrowser-mac-10.15.zip"
"$HOST/webkit/%s/minibrowser-win64.zip"
)
WK_ALIASES=(
"WK-LINUX"
"WK-GTK"
"WK-WPE"
"WK-MAC-10.14"
"WK-MAC-10.15"
"WK-WIN64"

View file

@ -6,7 +6,7 @@ trap "cd $(pwd -P)" EXIT
cd "$(dirname "$0")"
if [[ ($1 == '--help') || ($1 == '-h') ]]; then
echo "usage: $(basename $0) [firefox|firefox-win64|webkit] [--check] [zip-path]"
echo "usage: $(basename $0) [firefox-linux|firefox-win32|firefox-win64|webkit-gtk|webkit-wpe|webkit-gtk-wpe|webkit-win64|webkit-mac-10.14|webkit-mac-10.15] [--check] [zip-path]"
echo
echo "Upload .zip as a browser build."
echo
@ -30,65 +30,48 @@ if [[ $# < 1 ]]; then
echo "try '$(basename $0) --help' for more information"
exit 1
fi
BUILD_FLAVOR="$1"
BROWSER_NAME=""
BUILD_NUMBER=""
BLOB_NAME=""
ALIAS=""
if [[ ("$1" == "firefox") || ("$1" == "firefox/") ]]; then
BUILD_NUMBER=$(cat "$PWD/firefox/BUILD_NUMBER")
if [[ "$BUILD_FLAVOR" == "firefox-linux" ]]; then
BROWSER_NAME="firefox"
if [[ "$(uname)" == "Darwin" ]]; then
BLOB_NAME="firefox-mac.zip"
ALIAS="firefox-mac r$BUILD_NUMBER"
elif [[ "$(uname)" == "Linux" ]]; then
BLOB_NAME="firefox-linux.zip"
ALIAS="ff-linux r$BUILD_NUMBER"
elif [[ "$(uname)" == MINGW* ]]; then
BLOB_NAME="firefox-win32.zip"
ALIAS="ff-win32 r$BUILD_NUMBER"
else
echo "ERROR: unsupported platform - $(uname)"
exit 1
fi
elif [[ ("$1" == "firefox-win64") || ("$1" == "firefox-win64/") ]]; then
BUILD_NUMBER=$(cat "$PWD/firefox/BUILD_NUMBER")
elif [[ "$BUILD_FLAVOR" == "firefox-mac" ]]; then
BROWSER_NAME="firefox"
BLOB_NAME="firefox-mac.zip"
elif [[ "$BUILD_FLAVOR" == "firefox-win32" ]]; then
BROWSER_NAME="firefox"
BLOB_NAME="firefox-win32.zip"
elif [[ "$BUILD_FLAVOR" == "firefox-win64" ]]; then
BROWSER_NAME="firefox"
if [[ "$(uname)" == MINGW* ]]; then
BLOB_NAME="firefox-win64.zip"
ALIAS="ff-win64 r$BUILD_NUMBER"
else
echo "ERROR: unsupported platform for browser '$1' - $(uname)"
exit 1
fi
elif [[ ("$1" == "webkit") || ("$1" == "webkit/") ]]; then
BUILD_NUMBER=$(cat "$PWD/webkit/BUILD_NUMBER")
elif [[ "$BUILD_FLAVOR" == "webkit-gtk" ]]; then
BROWSER_NAME="webkit"
BLOB_NAME="minibrowser-gtk.zip"
elif [[ "$BUILD_FLAVOR" == "webkit-wpe" ]]; then
BROWSER_NAME="webkit"
BLOB_NAME="minibrowser-wpe.zip"
elif [[ "$BUILD_FLAVOR" == "webkit-gtk-wpe" ]]; then
BROWSER_NAME="webkit"
BLOB_NAME="minibrowser-gtk-wpe.zip"
elif [[ "$BUILD_FLAVOR" == "webkit-win64" ]]; then
BROWSER_NAME="webkit"
if [[ "$(uname)" == "Darwin" ]]; then
MAC_MAJOR_MINOR_VERSION=$(sw_vers -productVersion | grep -o '^\d\+.\d\+')
BLOB_NAME="minibrowser-mac-$MAC_MAJOR_MINOR_VERSION.zip"
ALIAS="webkit-mac-$MAC_MAJOR_MINOR_VERSION r$BUILD_NUMBER"
elif [[ "$(uname)" == "Linux" ]]; then
BLOB_NAME="minibrowser-linux.zip"
ALIAS="webkit-linux r$BUILD_NUMBER"
elif [[ "$(uname)" == MINGW* ]]; then
BLOB_NAME="minibrowser-win64.zip"
ALIAS="webkit-win64 r$BUILD_NUMBER"
else
echo "ERROR: unsupported platform - $(uname)"
exit 1
fi
elif [[ "$BUILD_FLAVOR" == "webkit-mac-10.14" ]]; then
BROWSER_NAME="webkit"
BLOB_NAME="minibrowser-mac-10.14.zip"
elif [[ "$BUILD_FLAVOR" == "webkit-mac-10.15" ]]; then
BROWSER_NAME="webkit"
BLOB_NAME="minibrowser-mac-10.15.zip"
else
echo ERROR: unknown browser to export - "$1"
echo ERROR: unknown build flavor - "$BUILD_FLAVOR"
exit 1
fi
if [[ ("$2" == '--show-alias') || ("$3" == '--show-alias') ]]; then
echo $ALIAS
exit 0
fi
BUILD_NUMBER=$(cat ./$BROWSER_NAME/BUILD_NUMBER)
BLOB_PATH="$BROWSER_NAME/$BUILD_NUMBER/$BLOB_NAME"
if [[ ("$2" == '--check') || ("$3" == '--check') ]]; then
EXISTS=$(az storage blob exists -c builds --account-key $AZ_ACCOUNT_KEY --account-name $AZ_ACCOUNT_NAME -n "$BLOB_PATH" --query "exists")
if [[ $EXISTS == "true" ]]; then
@ -103,7 +86,9 @@ if [[ $# < 2 ]]; then
echo "try '$(basename $0) --help' for more information"
exit 1
fi
ZIP_PATH=$2
ZIP_PATH="$2"
if ! [[ -f $ZIP_PATH ]]; then
echo "ERROR: $ZIP_PATH does not exist"
exit 1

View file

@ -1 +1 @@
1097
1100

View file

@ -3,20 +3,15 @@ set -e
set +x
if [[ ("$1" == "-h") || ("$1" == "--help") ]]; then
echo "usage: $(basename $0) [output-absolute-path]"
echo "usage: $(basename $0) [output-absolute-path] [--wpe]"
echo
echo "Generate distributable .zip archive from ./checkout folder that was previously built."
echo
exit 0
fi
if [[ $# != 1 ]]; then
echo "error: missing zip output path"
echo "try '$(basename $0) --help' for details"
exit 1
fi
ZIP_PATH=$1
USE_WPE=$2
if [[ $ZIP_PATH != /* ]]; then
echo "ERROR: path $ZIP_PATH is not absolute"
exit 1
@ -55,20 +50,35 @@ createZipForLinux() {
local tmpdir=$(mktemp -d -t webkit-deploy-XXXXXXXXXX)
mkdir -p $tmpdir
# copy all relevant binaries
cp -t $tmpdir ./WebKitBuild/Release/bin/MiniBrowser ./WebKitBuild/Release/bin/WebKit*Process
# copy runner
cp -t $tmpdir ../pw_run.sh
# copy protocol
node ../concat_protocol.js > $tmpdir/protocol.json
if [[ -n $USE_WPE ]]; then
# copy all relevant binaries
cp -t $tmpdir ./WebKitBuild/Release/bin/MiniBrowser ./WebKitBuild/Release/bin/WPE*Process
# copy all relevant shared objects
LD_LIBRARY_PATH="$PWD/WebKitBuild/DependenciesWPE/Root/lib" ldd WebKitBuild/Release/bin/MiniBrowser | grep -o '[^ ]*WebKitBuild/[^ ]*' | xargs cp -t $tmpdir
LD_LIBRARY_PATH="$PWD/WebKitBuild/DependenciesWPE/Root/lib" ldd WebKitBuild/Release/bin/WPENetworkProcess | grep -o '[^ ]*WebKitBuild/[^ ]*' | xargs cp -t $tmpdir
LD_LIBRARY_PATH="$PWD/WebKitBuild/DependenciesWPE/Root/lib" ldd WebKitBuild/Release/bin/WPEWebProcess | grep -o '[^ ]*WebKitBuild/[^ ]*' | xargs cp -t $tmpdir
cd $tmpdir
ln -s libWPEBackend-fdo-1.0.so.1 libWPEBackend-fdo-1.0.so
cd -
else
# copy all relevant binaries
cp -t $tmpdir ./WebKitBuild/Release/bin/MiniBrowser ./WebKitBuild/Release/bin/WebKit*Process
# copy all relevant shared objects
LD_LIBRARY_PATH="$PWD/WebKitBuild/DependenciesGTK/Root/lib" ldd WebKitBuild/Release/bin/MiniBrowser | grep -o '[^ ]*WebKitBuild/[^ ]*' | xargs cp -t $tmpdir
# we failed to nicely build libgdk_pixbuf - expect it in the env
rm $tmpdir/libgdk_pixbuf*
fi
# tar resulting directory and cleanup TMP.
zip -jr $ZIP_PATH $tmpdir
cd $tmpdir
zip --symlinks -r $ZIP_PATH ./
cd -
rm -rf $tmpdir
}
@ -80,9 +90,12 @@ createZipForWindows() {
cp -t $tmpdir ./WebKitLibraries/win/bin64/*.dll
cd WebKitBuild/Release/bin64
cp -r -t $tmpdir WebKit.resources
cp -t $tmpdir JavaScriptCore.dll MiniBrowserLib.dll WTF.dll WebKit.dll WebKit2.dll libEGL.dll libGLESv2.dll
cp -t $tmpdir JavaScriptCore.dll MiniBrowserLib.dll WTF.dll WebKit2.dll libEGL.dll libGLESv2.dll
cp -t $tmpdir MiniBrowser.exe WebKitNetworkProcess.exe WebKitWebProcess.exe
cd -
cd /c/WEBKIT_WIN64_LIBS
cp -t $tmpdir msvcp140.dll vcruntime140.dll vcruntime140_1.dll
cd -
# copy protocol
node ../concat_protocol.js > $tmpdir/protocol.json

View file

@ -10,11 +10,17 @@ if [[ "$(uname)" == "Darwin" ]]; then
./Tools/Scripts/build-webkit --release --touch-events
elif [[ "$(uname)" == "Linux" ]]; then
cd "checkout"
# Check that WebKitBuild exists and is not empty.
if ! [[ (-d ./WebKitBuild) && (-n $(ls -1 ./WebKitBuild/)) ]]; then
if [[ "$1" == "--wpe" ]]; then
if ! [[ -d ./WebKitBuild/DependenciesWPE ]]; then
yes | DEBIAN_FRONTEND=noninteractive ./Tools/Scripts/update-webkitwpe-libs
fi
./Tools/Scripts/build-webkit --wpe --release --touch-events MiniBrowser
else
if ! [[ -d ./WebKitBuild/DependenciesGTK ]]; then
yes | DEBIAN_FRONTEND=noninteractive ./Tools/Scripts/update-webkitgtk-libs
fi
./Tools/Scripts/build-webkit --gtk --release --touch-events MiniBrowser
fi
elif [[ "$(uname)" == MINGW* ]]; then
/c/Windows/System32/cmd.exe "/c buildwin.bat"
else

View file

@ -0,0 +1,79 @@
#!/bin/bash
set -e
set +x
if [[ ("$1" == "-h") || ("$1" == "--help") ]]; then
echo "usage: $(basename $0) [ZIP-PATH]"
echo
echo "Generate a single .zip archive that contains both gtk and wpe builds"
echo
exit 0
fi
if [[ "$(uname)" != "Linux" ]]; then
echo "ERROR: this script works only on linux"
echo
exit 1
fi
ZIP_PATH="$1"
if [[ $ZIP_PATH != /* ]]; then
echo "ERROR: path $ZIP_PATH is not absolute"
exit 1
fi
if [[ $ZIP_PATH != *.zip ]]; then
echo "ERROR: path $ZIP_PATH must have .zip extension"
exit 1
fi
if [[ -f $ZIP_PATH ]]; then
echo "ERROR: path $ZIP_PATH exists; can't do anything."
exit 1
fi
if ! [[ -d $(dirname $ZIP_PATH) ]]; then
echo "ERROR: folder for path $($ZIP_PATH) does not exist."
exit 1
fi
trap "cd $(pwd -P)" EXIT
cd "$(dirname "$0")"
# create a TMP directory to copy all necessary files
TMPDIR=$(mktemp -d -t webkit-deploy-XXXXXXXXXX)
GTK_ZIP_PATH=$(mktemp -t -u minibrowser-gtk-XXXXXX.zip)
WPE_ZIP_PATH=$(mktemp -t -u minibrowser-wpe-XXXXXX.zip)
../download.sh webkit-gtk $GTK_ZIP_PATH
../download.sh webkit-wpe $WPE_ZIP_PATH
# Create directory
mkdir -p $TMPDIR
# copy runner
cp -t $TMPDIR ./pw_run.sh
pushd $TMPDIR
# Copy MiniBrowser-GTK
mkdir minibrowser-gtk
pushd minibrowser-gtk
cp $GTK_ZIP_PATH archive.zip
unzip archive.zip
rm archive.zip
popd
# Copy MiniBrowser-WPE
mkdir minibrowser-wpe
pushd minibrowser-wpe
cp $WPE_ZIP_PATH archive.zip
unzip archive.zip
rm archive.zip
popd
mv minibrowser-gtk/protocol.json .
rm minibrowser-wpe/protocol.json
zip --symlinks -r $ZIP_PATH ./
popd
rm -rf $TMPDIR
rm -rf $WPE_ZIP_PATH
rm -rf $GTK_ZIP_PATH

View file

@ -11147,11 +11147,31 @@ index 4c41864d89f40567ed81ed2efefb501f6753db84..b9cab7c400c0c129ea4a9851dc397682
// For backwards compatibility with the WebBackForwardList API, we honor both
// a per-WebView and a per-preferences setting for whether to use the back/forward cache.
diff --git a/Source/cmake/OptionsGTK.cmake b/Source/cmake/OptionsGTK.cmake
index ba7f352a2adcf7180511cd0a404a015c138adfd9..c27a7f1a2faa8189dbf6c53a915f44e2abdffdb0 100644
--- a/Source/cmake/OptionsGTK.cmake
+++ b/Source/cmake/OptionsGTK.cmake
@@ -3,6 +3,7 @@ include(VersioningUtils)
SET_PROJECT_VERSION(2 27 3)
set(WEBKITGTK_API_VERSION 4.0)
+set(ENABLE_WEBKIT_LEGACY OFF)
CALCULATE_LIBRARY_VERSIONS_FROM_LIBTOOL_TRIPLE(WEBKIT 79 0 42)
CALCULATE_LIBRARY_VERSIONS_FROM_LIBTOOL_TRIPLE(JAVASCRIPTCORE 33 2 15)
diff --git a/Source/cmake/OptionsWPE.cmake b/Source/cmake/OptionsWPE.cmake
index 2fbbb581c02b6f4834ae8affa554df0fb2e311e1..1dd53b7970105b3e1191dbfb9545f406ef097646 100644
index 2fbbb581c02b6f4834ae8affa554df0fb2e311e1..d4e2956db6b1ff83ddf5641436770e09fe0c2df7 100644
--- a/Source/cmake/OptionsWPE.cmake
+++ b/Source/cmake/OptionsWPE.cmake
@@ -49,6 +49,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_XSLT PUBLIC ON)
@@ -3,6 +3,7 @@ include(VersioningUtils)
SET_PROJECT_VERSION(2 27 3)
set(WPE_API_VERSION 1.0)
+set(ENABLE_WEBKIT_LEGACY OFF)
CALCULATE_LIBRARY_VERSIONS_FROM_LIBTOOL_TRIPLE(WEBKIT 11 0 8)
@@ -49,6 +50,7 @@ WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_XSLT PUBLIC ON)
# Changing these options is completely unsupported.
WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_ASYNC_SCROLLING PRIVATE ON)
WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_CONTENT_EXTENSIONS PRIVATE ON)
@ -11160,10 +11180,21 @@ index 2fbbb581c02b6f4834ae8affa554df0fb2e311e1..1dd53b7970105b3e1191dbfb9545f406
WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_MHTML PRIVATE ON)
WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_NETSCAPE_PLUGIN_API PRIVATE OFF)
diff --git a/Source/cmake/OptionsWin.cmake b/Source/cmake/OptionsWin.cmake
index 1019fce94d5389a1f7b15675199dc02ccc68fcc3..5335aed3b8fba48b92407d988d5c7b49b94bc0ce 100644
index 1019fce94d5389a1f7b15675199dc02ccc68fcc3..785cc43328c8f306e73382a7aaba619f8fc91fe4 100644
--- a/Source/cmake/OptionsWin.cmake
+++ b/Source/cmake/OptionsWin.cmake
@@ -79,6 +79,8 @@ if (${WTF_PLATFORM_WIN_CAIRO})
@@ -7,8 +7,9 @@ add_definitions(-D_WINDOWS -DWINVER=0x601 -D_WIN32_WINNT=0x601)
add_definitions(-DNOMINMAX)
add_definitions(-DUNICODE -D_UNICODE)
+set(ENABLE_WEBKIT_LEGACY OFF)
+
if ((NOT DEFINED ENABLE_WEBKIT_LEGACY) OR ENABLE_WEBKIT_LEGACY)
- set(ENABLE_WEBKIT_LEGACY ON)
set(ENABLE_WEBKIT OFF)
endif ()
@@ -79,6 +80,8 @@ if (${WTF_PLATFORM_WIN_CAIRO})
WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_PUBLIC_SUFFIX_LIST PRIVATE ON)
WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_USER_MESSAGE_HANDLERS PRIVATE ON)
WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEBGL PUBLIC ON)

View file

@ -2,11 +2,11 @@
function runOSX() {
# if script is run as-is
if [ -d $SCRIPT_PATH/checkout/WebKitBuild/Release/MiniBrowser.app ]; then
if [[ -d $SCRIPT_PATH/checkout/WebKitBuild/Release/MiniBrowser.app ]]; then
DYLIB_PATH="$SCRIPT_PATH/checkout/WebKitBuild/Release"
elif [ -d $SCRIPT_PATH/MiniBrowser.app ]; then
elif [[ -d $SCRIPT_PATH/MiniBrowser.app ]]; then
DYLIB_PATH="$SCRIPT_PATH"
elif [ -d $SCRIPT_PATH/WebKitBuild/Release/MiniBrowser.app ]; then
elif [[ -d $SCRIPT_PATH/WebKitBuild/Release/MiniBrowser.app ]]; then
DYLIB_PATH="$SCRIPT_PATH/WebKitBuild/Release"
else
echo "Cannot find a MiniBrowser.app in neither location" 1>&2
@ -18,14 +18,23 @@ function runOSX() {
function runLinux() {
# if script is run as-is
if [ -d $SCRIPT_PATH/checkout/WebKitBuild ]; then
LD_PATH="$SCRIPT_PATH/checkout/WebKitBuild/DependenciesGTK/Root/lib:$SCRIPT_PATH/checkout/WebKitBuild/Release/bin"
DEPENDENCIES_FOLDER="DependenciesGTK"
MINIBROWSER_FOLDER="minibrowser-gtk";
if [[ "$*" == *--headless* ]]; then
DEPENDENCIES_FOLDER="DependenciesWPE";
MINIBROWSER_FOLDER="minibrowser-wpe";
fi
if [[ -d $SCRIPT_PATH/$MINIBROWSER_FOLDER ]]; then
LD_PATH="$SCRIPT_PATH/$MINIBROWSER_FOLDER"
MINIBROWSER="$SCRIPT_PATH/$MINIBROWSER_FOLDER/MiniBrowser"
elif [[ -d $SCRIPT_PATH/checkout/WebKitBuild ]]; then
LD_PATH="$SCRIPT_PATH/checkout/WebKitBuild/$DEPENDENCIES_FOLDER/Root/lib:$SCRIPT_PATH/checkout/WebKitBuild/Release/bin"
MINIBROWSER="$SCRIPT_PATH/checkout/WebKitBuild/Release/bin/MiniBrowser"
elif [ -f $SCRIPT_PATH/MiniBrowser ]; then
elif [[ -f $SCRIPT_PATH/MiniBrowser ]]; then
LD_PATH="$SCRIPT_PATH"
MINIBROWSER="$SCRIPT_PATH/MiniBrowser"
elif [ -d $SCRIPT_PATH/WebKitBuild ]; then
LD_PATH="$SCRIPT_PATH/WebKitBuild/DependenciesGTK/Root/lib:$SCRIPT_PATH/WebKitBuild/Release/bin"
elif [[ -d $SCRIPT_PATH/WebKitBuild ]]; then
LD_PATH="$SCRIPT_PATH/WebKitBuild/$DEPENDENCIES_FOLDER/Root/lib:$SCRIPT_PATH/WebKitBuild/Release/bin"
MINIBROWSER="$SCRIPT_PATH/WebKitBuild/Release/bin/MiniBrowser"
else
echo "Cannot find a MiniBrowser.app in neither location" 1>&2
@ -35,9 +44,9 @@ function runLinux() {
}
SCRIPT_PATH="$(cd "$(dirname "$0")" ; pwd -P)"
if [ "$(uname)" == "Darwin" ]; then
if [[ "$(uname)" == "Darwin" ]]; then
runOSX "$@"
elif [ "$(uname)" == "Linux" ]; then
elif [[ "$(uname)" == "Linux" ]]; then
runLinux "$@"
else
echo "ERROR: cannot run on this platform!" 1>&2

View file

@ -3565,6 +3565,8 @@ Browser websocket endpoint which can be used as an argument to [firefoxPlaywrigh
#### webkitPlaywright.defaultArgs([options])
- `options` <[Object]> Set of configurable options to set on the browser. Can have the following fields:
- `headless` <[boolean]> Whether to run WebKit in headless mode. Defaults to `true`.
- `userDataDir` <[string]> Path to a User Data Directory, which stores browser session data like cookies and local storage.
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance.
- returns: <[Array]<[string]>>
@ -3575,6 +3577,7 @@ The default flags that WebKit will be launched with.
- `headless` <[boolean]> Whether to run WebKit in headless mode. Defaults to `true`.
- `executablePath` <[string]> Path to a WebKit executable to run instead of the bundled WebKit. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only guaranteed to work with the bundled WebKit, use at your own risk.
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
- `userDataDir` <[string]> Path to a User Data Directory, which stores browser session data like cookies and local storage.
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance.
- `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, then do not use [`webKitPlaywright.defaultArgs()`](#webkitplaywrightdefaultargsoptions). If an array is given, then filter out the given default arguments. Dangerous option; use with care. Defaults to `false`.
- `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.
@ -3598,6 +3601,7 @@ const browser = await playwright.launch({
- `headless` <[boolean]> Whether to run WebKit in headless mode. Defaults to `true`.
- `executablePath` <[string]> Path to a WebKit executable to run instead of the bundled WebKit. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only guaranteed to work with the bundled WebKit, use at your own risk.
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
- `userDataDir` <[string]> Path to a User Data Directory, which stores browser session data like cookies and local storage.
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance.
- `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, then do not use [`webKitPlaywright.defaultArgs()`](#webkitplaywrightdefaultargsoptions). If an array is given, then filter out the given default arguments. Dangerous option; use with care. Defaults to `false`.
- `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.

View file

@ -9,8 +9,8 @@
"main": "index.js",
"playwright": {
"chromium_revision": "724623",
"firefox_revision": "1015",
"webkit_revision": "1097"
"firefox_revision": "1016",
"webkit_revision": "1099"
},
"scripts": {
"unit": "node test/test.js",

View file

@ -191,15 +191,18 @@ export class FrameManager {
for (const watcher of this._lifecycleWatchers)
watcher._onNavigationRequest(frame, request);
}
if (!request._isFavicon)
this._page.emit(Events.Page.Request, request);
}
requestReceivedResponse(response: network.Response) {
if (!response.request()._isFavicon)
this._page.emit(Events.Page.Response, response);
}
requestFinished(request: network.Request) {
this._inflightRequestFinished(request);
if (!request._isFavicon)
this._page.emit(Events.Page.RequestFinished, request);
}
@ -216,6 +219,7 @@ export class FrameManager {
watcher._onAbortedNewDocumentNavigation(frame, request._documentId, errorText);
}
}
if (!request._isFavicon)
this._page.emit(Events.Page.RequestFailed, request);
}
@ -236,7 +240,7 @@ export class FrameManager {
private _inflightRequestFinished(request: network.Request) {
const frame = request.frame();
if (!frame || request.url().endsWith('favicon.ico'))
if (!frame || request._isFavicon)
return;
if (!frame._inflightRequests.has(request))
return;
@ -249,7 +253,7 @@ export class FrameManager {
private _inflightRequestStarted(request: network.Request) {
const frame = request.frame();
if (!frame || request.url().endsWith('favicon.ico'))
if (!frame || request._isFavicon)
return;
frame._inflightRequests.add(request);
if (frame._inflightRequests.size === 1)

View file

@ -97,6 +97,7 @@ export class Request {
_redirectChain: Request[];
_finalRequest: Request;
readonly _documentId?: string;
readonly _isFavicon: boolean;
private _failureText: string | null = null;
private _url: string;
private _resourceType: string;
@ -127,6 +128,7 @@ export class Request {
this._headers = headers;
this._waitForResponsePromise = new Promise(f => this._waitForResponsePromiseCallback = f);
this._waitForFinishedPromise = new Promise(f => this._waitForFinishedPromiseCallback = f);
this._isFavicon = url.endsWith('/favicon.ico');
}
_setFailureText(failureText: string) {

View file

@ -1,5 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
/**
* Copyright (c) Microsoft Corporation.
*
* 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.
*/
// Note: this is the only file outside of src/server which can import external dependencies.
// All dependencies must be listed in web.webpack.config.js to avoid bundling them.

View file

@ -195,7 +195,7 @@ export class WKPlaywright implements Playwright {
_createBrowserFetcher(options?: BrowserFetcherOptions): BrowserFetcher {
const downloadURLs = {
linux: '%s/builds/webkit/%s/minibrowser-linux.zip',
linux: '%s/builds/webkit/%s/minibrowser-gtk-wpe.zip',
mac: '%s/builds/webkit/%s/minibrowser-mac-%s.zip',
win64: '%s/builds/webkit/%s/minibrowser-win64.zip',
};

View file

@ -46,11 +46,11 @@ export class WKNetworkManager {
helper.removeEventListeners(this._sessionListeners);
this._session = session;
this._sessionListeners = [
helper.addEventListener(this._session, 'Network.requestWillBeSent', this._onRequestWillBeSent.bind(this)),
helper.addEventListener(this._session, 'Network.requestIntercepted', this._onRequestIntercepted.bind(this)),
helper.addEventListener(this._session, 'Network.responseReceived', this._onResponseReceived.bind(this)),
helper.addEventListener(this._session, 'Network.loadingFinished', this._onLoadingFinished.bind(this)),
helper.addEventListener(this._session, 'Network.loadingFailed', this._onLoadingFailed.bind(this)),
helper.addEventListener(session, 'Network.requestWillBeSent', e => this._onRequestWillBeSent(session, e)),
helper.addEventListener(session, 'Network.requestIntercepted', e => this._onRequestIntercepted(e)),
helper.addEventListener(session, 'Network.responseReceived', e => this._onResponseReceived(e)),
helper.addEventListener(session, 'Network.loadingFinished', e => this._onLoadingFinished(e)),
helper.addEventListener(session, 'Network.loadingFailed', e => this._onLoadingFailed(e)),
];
}
@ -77,13 +77,13 @@ export class WKNetworkManager {
await this._session.send('Network.setInterceptionEnabled', { enabled, interceptRequests: enabled });
}
async _updateProtocolCacheDisabled() {
private async _updateProtocolCacheDisabled() {
await this._session.send('Network.setResourceCachingDisabled', {
disabled: this._userCacheDisabled
});
}
_onRequestWillBeSent(event: Protocol.Network.requestWillBeSentPayload) {
_onRequestWillBeSent(session: WKSession, event: Protocol.Network.requestWillBeSentPayload) {
if (event.request.url.startsWith('data:'))
return;
let redirectChain: network.Request[] = [];
@ -99,7 +99,7 @@ export class WKNetworkManager {
// TODO(einbinder) this will fail if we are an XHR document request
const isNavigationRequest = event.type === 'Document';
const documentId = isNavigationRequest ? event.loaderId : undefined;
const request = new InterceptableRequest(this._session, !!this._page._state.interceptNetwork, frame, event, redirectChain, documentId);
const request = new InterceptableRequest(session, !!this._page._state.interceptNetwork, frame, event, redirectChain, documentId);
this._requestIdToRequest.set(event.requestId, request);
this._page._frameManager.requestStarted(request.request);
}
@ -110,17 +110,17 @@ export class WKNetworkManager {
request._interceptedCallback();
}
_createResponse(request: InterceptableRequest, responsePayload: Protocol.Network.Response): network.Response {
private static _createResponse(request: InterceptableRequest, responsePayload: Protocol.Network.Response): network.Response {
const remoteAddress: network.RemoteAddress = { ip: '', port: 0 };
const getResponseBody = async () => {
const response = await this._session.send('Network.getResponseBody', { requestId: request._requestId });
const response = await request._session.send('Network.getResponseBody', { requestId: request._requestId });
return platform.Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
};
return new network.Response(request.request, responsePayload.status, responsePayload.statusText, headersObject(responsePayload.headers), remoteAddress, getResponseBody);
}
_handleRequestRedirect(request: InterceptableRequest, responsePayload: Protocol.Network.Response) {
const response = this._createResponse(request, responsePayload);
private _handleRequestRedirect(request: InterceptableRequest, responsePayload: Protocol.Network.Response) {
const response = WKNetworkManager._createResponse(request, responsePayload);
request.request._redirectChain.push(request.request);
response._requestFinished(new Error('Response body is unavailable for redirect responses'));
this._requestIdToRequest.delete(request._requestId);
@ -133,7 +133,7 @@ export class WKNetworkManager {
// FileUpload sends a response without a matching request.
if (!request)
return;
const response = this._createResponse(request, event.response);
const response = WKNetworkManager._createResponse(request, event.response);
this._page._frameManager.requestReceivedResponse(response);
}
@ -194,7 +194,7 @@ const errorReasons: { [reason: string]: string } = {
};
class InterceptableRequest implements network.RequestDelegate {
private _session: WKSession;
readonly _session: WKSession;
readonly request: network.Request;
_requestId: string;
_documentId: string | undefined;

View file

@ -43,10 +43,9 @@ export class WKPage implements PageDelegate {
_session: WKSession;
readonly _page: Page;
private readonly _pageProxySession: WKSession;
private readonly _networkManager: WKNetworkManager;
readonly _networkManager: WKNetworkManager;
private readonly _workers: WKWorkers;
private readonly _contextIdToContext: Map<number, dom.FrameExecutionContext>;
private _isolatedWorlds: Set<string>;
private _sessionListeners: RegisteredListener[] = [];
private readonly _bootstrapScripts: string[] = [];
@ -55,14 +54,13 @@ export class WKPage implements PageDelegate {
this.rawKeyboard = new RawKeyboardImpl(pageProxySession);
this.rawMouse = new RawMouseImpl(pageProxySession);
this._contextIdToContext = new Map();
this._isolatedWorlds = new Set();
this._page = new Page(this, browserContext);
this._networkManager = new WKNetworkManager(this._page, pageProxySession);
this._workers = new WKWorkers(this._page);
this._session = undefined as any as WKSession;
}
async _initializePageProxySession() {
private async _initializePageProxySession() {
const promises : Promise<any>[] = [
this._pageProxySession.send('Dialog.enable'),
this._networkManager.initializePageProxySession(this._page._state.credentials)
@ -83,38 +81,46 @@ export class WKPage implements PageDelegate {
this._addSessionListeners();
this._networkManager.setSession(session);
this._workers.setSession(session);
this._isolatedWorlds = new Set();
// New bootstrap scripts may have been added during provisional load, push them
// again to be on the safe side.
if (this._bootstrapScripts.length)
this._setBootstrapScripts(session).catch(e => debugError(e));
}
async initialize() {
await Promise.all([
this._initializePageProxySession(),
this._initializeSession(this._session, ({frameTree}) => this._handleFrameTree(frameTree)),
]);
}
// This method is called for provisional targets as well. The session passed as the parameter
// may be different from the current session and may be destroyed without becoming current.
async _initializeSession(session: WKSession, isProvisional: boolean) {
async _initializeSession(session: WKSession, resourceTreeHandler: (r: Protocol.Page.getResourceTreeReturnValue) => void) {
const isProvisional = this._session !== session;
const promises : Promise<any>[] = [
// Page agent must be enabled before Runtime.
session.send('Page.enable'),
session.send('Page.getResourceTree').then(({frameTree}) => this._handleFrameTree(frameTree)),
session.send('Page.getResourceTree').then(resourceTreeHandler),
// Resource tree should be received before first execution context.
session.send('Runtime.enable').then(() => this._ensureIsolatedWorld(UTILITY_WORLD_NAME)),
session.send('Runtime.enable'),
session.send('Page.createIsolatedWorld', { name: UTILITY_WORLD_NAME, source: `//# sourceURL=${EVALUATION_SCRIPT_URL}` }),
session.send('Console.enable'),
session.send('Page.setInterceptFileChooserDialog', { enabled: true }),
this._networkManager.initializeSession(session, this._page._state.interceptNetwork, this._page._state.offlineMode),
this._workers.initializeSession(session),
this._workers.initializeSession(session)
];
const contextOptions = this._page.browserContext()._options;
if (contextOptions.userAgent)
promises.push(session.send('Page.overrideUserAgent', { value: contextOptions.userAgent }));
if (this._page._state.mediaType || this._page._state.colorScheme)
promises.push(this._setEmulateMedia(session, this._page._state.mediaType, this._page._state.colorScheme));
promises.push(WKPage._setEmulateMedia(session, this._page._state.mediaType, this._page._state.colorScheme));
if (isProvisional)
promises.push(this._setBootstrapScripts(session));
if (contextOptions.bypassCSP)
promises.push(session.send('Page.setBypassCSP', { enabled: true }));
if (this._page._state.extraHTTPHeaders !== null)
promises.push(this._setExtraHTTPHeaders(session, this._page._state.extraHTTPHeaders));
promises.push(WKPage._setExtraHTTPHeaders(session, this._page._state.extraHTTPHeaders));
if (this._page._state.hasTouch)
promises.push(session.send('Page.setTouchEmulationEnabled', { enabled: true }));
await Promise.all(promises).catch(e => {
@ -285,21 +291,11 @@ export class WKPage implements PageDelegate {
this._page._onFileChooserOpened(handle);
}
async _ensureIsolatedWorld(name: string) {
if (this._isolatedWorlds.has(name))
return;
this._isolatedWorlds.add(name);
await this._session.send('Page.createIsolatedWorld', {
name,
source: `//# sourceURL=${EVALUATION_SCRIPT_URL}`
});
}
private async _setExtraHTTPHeaders(session: WKSession, headers: network.Headers): Promise<void> {
private static async _setExtraHTTPHeaders(session: WKSession, headers: network.Headers): Promise<void> {
await session.send('Network.setExtraHTTPHeaders', { headers });
}
private async _setEmulateMedia(session: WKSession, mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void> {
private static async _setEmulateMedia(session: WKSession, mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void> {
const promises = [];
promises.push(session.send('Page.setEmulatedMedia', { media: mediaType || '' }));
if (colorScheme !== null) {
@ -314,11 +310,11 @@ export class WKPage implements PageDelegate {
}
async setExtraHTTPHeaders(headers: network.Headers): Promise<void> {
await this._setExtraHTTPHeaders(this._session, headers);
await WKPage._setExtraHTTPHeaders(this._session, headers);
}
async setEmulateMedia(mediaType: types.MediaType | null, colorScheme: types.ColorScheme | null): Promise<void> {
await this._setEmulateMedia(this._session, mediaType, colorScheme);
await WKPage._setEmulateMedia(this._session, mediaType, colorScheme);
}
async setViewport(viewport: types.Viewport): Promise<void> {

View file

@ -1,5 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
/**
* Copyright (c) Microsoft Corporation.
*
* 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 { BrowserContext } from '../browserContext';
@ -9,17 +22,16 @@ import { WKSession } from './wkConnection';
import { WKPage } from './wkPage';
import { RegisteredListener, helper, assert, debugError } from '../helper';
import { Events } from '../events';
import { WKProvisionalPage } from './wkProvisionalPage';
// We keep provisional messages on the session instace until provisional
// target is committed. Non-provisional target (there should be just one)
// has undefined instead.
const provisionalMessagesSymbol = Symbol('provisionalMessages');
const isPovisionalSymbol = Symbol('isPovisional');
export class WKPageProxy {
private readonly _pageProxySession: WKSession;
readonly _browserContext: BrowserContext;
private _pagePromise: Promise<Page> | null = null;
private _wkPage: WKPage | null = null;
private _provisionalPage: WKProvisionalPage | null = null;
private readonly _firstTargetPromise: Promise<void>;
private _firstTargetCallback?: () => void;
private readonly _sessions = new Map<string, WKSession>();
@ -56,6 +68,10 @@ export class WKPageProxy {
for (const session of this._sessions.values())
session.dispose();
this._sessions.clear();
if (this._provisionalPage) {
this._provisionalPage.dispose();
this._provisionalPage = null;
}
if (this._wkPage)
this._wkPage.didDisconnect();
}
@ -66,7 +82,7 @@ export class WKPageProxy {
private _isProvisionalCrossProcessLoadInProgress() : boolean {
for (const anySession of this._sessions.values()) {
if ((anySession as any)[provisionalMessagesSymbol])
if ((anySession as any)[isPovisionalSymbol])
return true;
}
return false;
@ -100,7 +116,7 @@ export class WKPageProxy {
await this._firstTargetPromise;
let session: WKSession | undefined;
for (const anySession of this._sessions.values()) {
if (!(anySession as any)[provisionalMessagesSymbol]) {
if (!(anySession as any)[isPovisionalSymbol]) {
session = anySession;
break;
}
@ -108,10 +124,7 @@ export class WKPageProxy {
assert(session, 'One non-provisional target session must exist');
this._wkPage = new WKPage(this._browserContext, this._pageProxySession);
this._wkPage.setSession(session!);
await Promise.all([
this._wkPage._initializePageProxySession(),
this._wkPage._initializeSession(session!, false),
]);
await this._wkPage.initialize();
return this._wkPage._page;
}
@ -131,9 +144,11 @@ export class WKPageProxy {
this._firstTargetCallback = undefined;
}
if (targetInfo.isProvisional)
(session as any)[provisionalMessagesSymbol] = [];
if (targetInfo.isProvisional && this._wkPage)
this._wkPage._initializeSession(session, true);
(session as any)[isPovisionalSymbol] = true;
if (targetInfo.isProvisional && this._wkPage) {
assert(!this._provisionalPage);
this._provisionalPage = new WKProvisionalPage(session, this._wkPage);
}
if (targetInfo.isPaused)
this._pageProxySession.send('Target.resume', { targetId: targetInfo.targetId }).catch(debugError);
}
@ -144,6 +159,10 @@ export class WKPageProxy {
if (session)
session.dispose();
this._sessions.delete(targetId);
if (this._provisionalPage && this._provisionalPage._session === session) {
this._provisionalPage.dispose();
this._provisionalPage = null;
}
if (this._wkPage && this._wkPage._session === session && crashed)
this._wkPage.didClose(crashed);
}
@ -152,10 +171,6 @@ export class WKPageProxy {
const { targetId, message } = event;
const session = this._sessions.get(targetId);
assert(session, 'Unknown target: ' + targetId);
const provisionalMessages = (session as any)[provisionalMessagesSymbol];
if (provisionalMessages)
provisionalMessages.push(message);
else
session!.dispatchMessage(JSON.parse(message));
}
@ -167,11 +182,13 @@ export class WKPageProxy {
assert(oldSession, 'Unknown old target: ' + oldTargetId);
// TODO: make some calls like screenshot catch swapped out error and retry.
oldSession!.errorText = 'Target was swapped out.';
const provisionalMessages = (newSession as any)[provisionalMessagesSymbol];
assert(provisionalMessages, 'Committing target must be provisional');
(newSession as any)[provisionalMessagesSymbol] = undefined;
for (const message of provisionalMessages)
newSession!.dispatchMessage(JSON.parse(message));
this._wkPage!.setSession(newSession!);
(newSession as any)[isPovisionalSymbol] = undefined;
if (this._provisionalPage) {
this._provisionalPage.commit();
this._provisionalPage.dispose();
this._provisionalPage = null;
}
if (this._wkPage)
this._wkPage.setSession(newSession!);
}
}

View file

@ -0,0 +1,67 @@
/**
* Copyright (c) Microsoft Corporation.
*
* 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 { WKSession } from './wkConnection';
import { WKPage } from './wkPage';
import { RegisteredListener, helper, assert } from '../helper';
import { Protocol } from './protocol';
export class WKProvisionalPage {
readonly _session: WKSession;
private readonly _wkPage: WKPage;
private _sessionListeners: RegisteredListener[] = [];
private _mainFrameId: string | null = null;
constructor(session: WKSession, page: WKPage) {
this._session = session;
this._wkPage = page;
const overrideFrameId = (handler: (p: any) => void) => {
return (payload: any) => {
// Pretend that the events happened in the same process.
if (payload.frameId)
payload.frameId = this._wkPage._page._frameManager.mainFrame()._id;
handler(payload);
};
};
const networkManager = this._wkPage._networkManager;
this._sessionListeners = [
helper.addEventListener(session, 'Network.requestWillBeSent', overrideFrameId(e => networkManager._onRequestWillBeSent(session, e))),
helper.addEventListener(session, 'Network.requestIntercepted', overrideFrameId(e => networkManager._onRequestIntercepted(e))),
helper.addEventListener(session, 'Network.responseReceived', overrideFrameId(e => networkManager._onResponseReceived(e))),
helper.addEventListener(session, 'Network.loadingFinished', overrideFrameId(e => networkManager._onLoadingFinished(e))),
helper.addEventListener(session, 'Network.loadingFailed', overrideFrameId(e => networkManager._onLoadingFailed(e))),
];
this._wkPage._initializeSession(session, ({frameTree}) => this._handleFrameTree(frameTree));
}
dispose() {
helper.removeEventListeners(this._sessionListeners);
}
commit() {
assert(this._mainFrameId);
this._wkPage._onFrameAttached(this._mainFrameId!, null);
}
private _handleFrameTree(frameTree: Protocol.Page.FrameResourceTree) {
assert(!frameTree.frame.parentId);
this._mainFrameId = frameTree.frame.id;
}
}

View file

@ -28,10 +28,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
it('should intercept', async({page, server}) => {
await page.setRequestInterception(true);
page.on('request', request => {
if (utils.isFavicon(request)) {
request.continue();
return;
}
expect(request.url()).toContain('empty.html');
expect(request.headers()['user-agent']).toBeTruthy();
expect(request.method()).toBe('GET');
@ -94,7 +90,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
await page.setRequestInterception(true);
const requests = [];
page.on('request', request => {
if (!utils.isFavicon(request))
requests.push(request);
request.continue();
});
@ -217,7 +212,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
const requests = [];
page.on('request', request => {
request.continue();
if (!utils.isFavicon(request))
requests.push(request);
});
server.setRedirect('/non-existing-page.html', '/non-existing-page-2.html');
@ -245,7 +239,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
const requests = [];
page.on('request', request => {
request.continue();
if (!utils.isFavicon(request))
requests.push(request);
});
server.setRedirect('/one-style.css', '/two-style.css');
@ -274,10 +267,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
let spinner = false;
// Cancel 2nd request.
page.on('request', request => {
if (utils.isFavicon(request)) {
request.continue();
return;
}
spinner ? request.abort() : request.continue();
spinner = !spinner;
});
@ -292,7 +281,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
await page.setRequestInterception(true);
const requests = [];
page.on('request', request => {
if (!utils.isFavicon(request))
requests.push(request);
request.continue();
});
@ -306,7 +294,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
await page.setRequestInterception(true);
const requests = [];
page.on('request', request => {
if (!utils.isFavicon(request))
requests.push(request);
request.continue();
});
@ -319,7 +306,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
await page.setRequestInterception(true);
const requests = [];
page.on('request', request => {
if (!utils.isFavicon(request))
requests.push(request);
request.continue();
});
@ -351,7 +337,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
const requests = [];
page.on('request', request => {
request.continue();
if (!utils.isFavicon(request))
requests.push(request);
});
const response = await page.goto(`data:text/html,<link rel="stylesheet" href="${server.PREFIX}/fonts?helvetica|arial"/>`);
@ -386,7 +371,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
await page.goto(server.EMPTY_PAGE);
expect(error.message).toContain('Request Interception is not enabled');
});
it.skip(WEBKIT)('should intercept main resource during cross-process navigation', async({page, server}) => {
it('should intercept main resource during cross-process navigation', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(true);
let intercepted = false;
@ -414,7 +399,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
const notAnError = await request.continue().then(() => null).catch(e => e);
expect(notAnError).toBe(null);
});
it.skip(WEBKIT)('should not throw when continued after cross-process navigation', async({page, server}) => {
it('should not throw when continued after cross-process navigation', async({page, server}) => {
await page.setRequestInterception(true);
page.on('request', request => {
if (request.url() !== server.PREFIX + '/one-style.css')

View file

@ -125,7 +125,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'domcontentloaded'});
expect(response.status()).toBe(200);
});
it.skip(WEBKIT)('should work when page calls history API in beforeunload', async({page, server}) => {
it('should work when page calls history API in beforeunload', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => {
window.addEventListener('beforeunload', () => history.replaceState(null, 'initial', window.location.href), false);
@ -280,7 +280,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
});
it('should navigate to dataURL and not fire dataURL requests', async({page, server}) => {
const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request));
page.on('request', request => requests.push(request));
const dataURL = 'data:text/html,<div>yo</div>';
const response = await page.goto(dataURL);
expect(response).toBe(null);
@ -288,7 +288,7 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
});
it('should navigate to URL with hash and fire requests without hash', async({page, server}) => {
const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request));
page.on('request', request => requests.push(request));
const response = await page.goto(server.EMPTY_PAGE + '#hash');
expect(response.status()).toBe(200);
expect(response.url()).toBe(server.EMPTY_PAGE);

View file

@ -27,20 +27,20 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
describe('Page.Events.Request', function() {
it('should fire for navigation requests', async({page, server}) => {
const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request));
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1);
});
it('should fire for iframes', async({page, server}) => {
const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request));
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
expect(requests.length).toBe(2);
});
it('should fire for fetches', async({page, server}) => {
const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request));
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => fetch('/empty.html'));
expect(requests.length).toBe(2);
@ -50,7 +50,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
describe('Request.frame', function() {
it('should work for main frame navigation request', async({page, server}) => {
const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request));
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1);
expect(requests[0].frame()).toBe(page.mainFrame());
@ -58,7 +58,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
it('should work for subframe navigation request', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request));
page.on('request', request => requests.push(request));
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
expect(requests.length).toBe(1);
expect(requests[0].frame()).toBe(page.frames()[1]);
@ -66,7 +66,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
it('should work for fetch requests', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
let requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request));
page.on('request', request => requests.push(request));
await page.evaluate(() => fetch('/digits/1.png'));
requests = requests.filter(request => !request.url().includes('favicon'));
expect(requests.length).toBe(1);
@ -151,7 +151,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
page.on('requestfinished', r => requestFinished = requestFinished || r.url().includes('/get'));
// send request and wait for server response
const [pageResponse] = await Promise.all([
page.waitForEvent('response', { predicate: r => !utils.isFavicon(r.request()) }),
page.waitForEvent('response'),
page.evaluate(() => fetch('./get', { method: 'GET'})),
server.waitForRequest('/get'),
]);
@ -207,7 +207,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
describe('Network Events', function() {
it('Page.Events.Request', async({page, server}) => {
const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request));
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1);
expect(requests[0].url()).toBe(server.EMPTY_PAGE);
@ -220,7 +220,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
// FIXME: WebKit doesn't provide remoteIPAddress in the response.
it.skip(WEBKIT)('Page.Events.Response', async({page, server}) => {
const responses = [];
page.on('response', response => !utils.isFavicon(response.request()) && responses.push(response));
page.on('response', response => responses.push(response));
await page.goto(server.EMPTY_PAGE);
expect(responses.length).toBe(1);
expect(responses[0].url()).toBe(server.EMPTY_PAGE);
@ -261,7 +261,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
});
it('Page.Events.RequestFinished', async({page, server}) => {
const requests = [];
page.on('requestfinished', request => !utils.isFavicon(request) && requests.push(request));
page.on('requestfinished', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1);
expect(requests[0].url()).toBe(server.EMPTY_PAGE);
@ -271,18 +271,18 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
});
it('should fire events in proper order', async({page, server}) => {
const events = [];
page.on('request', request => !utils.isFavicon(request) && events.push('request'));
page.on('response', response => !utils.isFavicon(response.request()) && events.push('response'));
page.on('requestfinished', request => !utils.isFavicon(request) && events.push('requestfinished'));
page.on('request', request => events.push('request'));
page.on('response', response => events.push('response'));
page.on('requestfinished', request => events.push('requestfinished'));
await page.goto(server.EMPTY_PAGE);
expect(events).toEqual(['request', 'response', 'requestfinished']);
});
it('should support redirects', async({page, server}) => {
const events = [];
page.on('request', request => !utils.isFavicon(request) && events.push(`${request.method()} ${request.url()}`));
page.on('response', response => !utils.isFavicon(response.request()) && events.push(`${response.status()} ${response.url()}`));
page.on('requestfinished', request => !utils.isFavicon(request) && events.push(`DONE ${request.url()}`));
page.on('requestfailed', request => !utils.isFavicon(request) && events.push(`FAIL ${request.url()}`));
page.on('request', request => events.push(`${request.method()} ${request.url()}`));
page.on('response', response => events.push(`${response.status()} ${response.url()}`));
page.on('requestfinished', request => events.push(`DONE ${request.url()}`));
page.on('requestfailed', request => events.push(`FAIL ${request.url()}`));
server.setRedirect('/foo.html', '/empty.html');
const FOO_URL = server.PREFIX + '/foo.html';
const response = await page.goto(FOO_URL);

View file

@ -172,7 +172,7 @@ module.exports.describe = function({testRunner, expect, headless, playwright, FF
expect(await page.evaluate(() => !!window.opener)).toBe(false);
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
});
it.skip(WEBKIT || FFOX)('should not treat navigations as new popups', async({page, server}) => {
it.skip(FFOX)('should not treat navigations as new popups', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>');
const [popup] = await Promise.all([

View file

@ -122,19 +122,11 @@ const utils = module.exports = {
frame.src = url;
frame.id = frameId;
document.body.appendChild(frame);
// TODO(einbinder) do this right
// Access a scriptable global object to ensure JS context is
// initialized. WebKit will create it lazily only as need be.
frame.contentWindow;
await new Promise(x => frame.onload = x);
return frame;
}
},
isFavicon: function(request) {
return request.url().includes('favicon.ico');
},
/**
* @param {!Page} page
* @param {string} frameId

View file

@ -1,5 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
/**
* Copyright (c) Microsoft Corporation.
*
* 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.
*/
const fs = require('fs');
const path = require('path');