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 We currently have 4 build bots that produce the following builds
- **[buildbot-linux]** Ubuntu 18.04 machine - **[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 - **[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 - **[buildbot-mac-10.15]** machine
- builds: `WebKit-mac-10.15` - builds: `webkit-mac-10.15`
- **[buildbot-windows]** Windows 10 machine - **[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 This document describes setting up bots infrastructure to produce
browser builds. 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`. 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 ## Running Build Loop
1. Launch `c:\mozilla-build/start-shell.bat` 1. Launch `c:\mozilla-build/start-shell.bat`

View file

@ -55,7 +55,9 @@ if [[ -n $(git status -s) ]]; then
fi fi
git pull origin master 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 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 fi
git pull origin master 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 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 fi
git pull origin master 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 )) iteration=$(( iteration + 1 ))
echo "== ITERATION ${iteration} ==" echo "== ITERATION ${iteration} =="
git pull origin master git pull origin master
../checkout_build_archive_upload.sh webkit || true ../checkout_build_archive_upload.sh webkit-win64 || true
git pull origin master git pull origin master
../checkout_build_archive_upload.sh firefox || true ../checkout_build_archive_upload.sh firefox-win32 || true
git pull origin master git pull origin master
../checkout_build_archive_upload.sh firefox-win64 || true ../checkout_build_archive_upload.sh firefox-win64 || true
newTimestamp=$(date +%s) newTimestamp=$(date +%s)

View file

@ -3,7 +3,7 @@ set -e
set +x set +x
if [[ ($1 == '--help') || ($1 == '-h') ]]; then 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
echo "Prepares checkout under browser folder, applies patches, builds, archives, and uploades if build is missing." 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." 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 fi
if [[ $# == 0 ]]; then if [[ $# == 0 ]]; then
echo "missing browser: 'firefox' or 'webkit'" echo "missing build flavor!"
echo "try './$(basename $0) --help' for more information" echo "try './$(basename $0) --help' for more information"
exit 1 exit 1
fi 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="" BROWSER_NAME=""
EXTRA_BUILD_ARGS="" 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" 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" BROWSER_NAME="firefox"
EXTRA_BUILD_ARGS="--win64" EXTRA_BUILD_ARGS="--win64"
elif [[ ("$1" == "webkit") || ("$1" == "webkit/") ]]; then EXPECTED_HOST_OS="MINGW"
elif [[ "$BUILD_FLAVOR" == "webkit-gtk" ]]; then
BROWSER_NAME="webkit" 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 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 exit 1
fi 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. # pull from upstream and check if a new build has to be uploaded.
if ! [[ ($2 == '-f') || ($2 == '--force') ]]; then 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." echo "Build is already uploaded - no changes."
exit 0 exit 0
else else
@ -69,36 +122,46 @@ cd -
source ./buildbots/send_telegram_message.sh source ./buildbots/send_telegram_message.sh
LAST_COMMIT_MESSAGE=$(git log --format=%s -n 1 HEAD -- ./$BROWSER_NAME/BUILD_NUMBER) 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 ⏳" send_telegram_message "$BUILD_ALIAS -- started ⏳"
echo "-- preparing checkout" if [[ "$BUILD_FLAVOR" == "webkit-gtk-wpe" ]]; then
if ! ./prepare_checkout.sh $BROWSER_NAME; 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! ❌" send_telegram_message "$BUILD_ALIAS -- ./prepare_checkout.sh failed! ❌"
exit 1 exit 1
fi fi
echo "-- cleaning" echo "-- cleaning"
if ! ./$BROWSER_NAME/clean.sh; then if ! ./$BROWSER_NAME/clean.sh; then
send_telegram_message "$BUILD_ALIAS -- ./clean.sh failed! ❌" send_telegram_message "$BUILD_ALIAS -- ./clean.sh failed! ❌"
exit 1 exit 1
fi fi
echo "-- building" echo "-- building"
if ! ./$BROWSER_NAME/build.sh "$EXTRA_BUILD_ARGS"; then if ! ./$BROWSER_NAME/build.sh "$EXTRA_BUILD_ARGS"; then
send_telegram_message "$BUILD_ALIAS -- ./build.sh failed! ❌" send_telegram_message "$BUILD_ALIAS -- ./build.sh failed! ❌"
exit 1 exit 1
fi fi
echo "-- archiving to $ZIP_PATH" echo "-- archiving to $ZIP_PATH"
if ! ./$BROWSER_NAME/archive.sh $ZIP_PATH; then if ! ./$BROWSER_NAME/archive.sh $ZIP_PATH "$EXTRA_ARCHIVE_ARGS"; then
send_telegram_message "$BUILD_ALIAS -- ./archive.sh failed! ❌" send_telegram_message "$BUILD_ALIAS -- ./archive.sh failed! ❌"
exit 1 exit 1
fi
fi fi
echo "-- uploading" 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! ❌" send_telegram_message "$BUILD_ALIAS -- ./upload.sh failed! ❌"
exit 1 exit 1
fi 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 exit 0
fi fi
if [[ $# != 1 ]]; then
echo "error: missing zip output path"
echo "try '$(basename $0) --help' for details"
exit 1
fi
ZIP_PATH=$1 ZIP_PATH=$1
if [[ $ZIP_PATH != /* ]]; then if [[ $ZIP_PATH != /* ]]; then
echo "ERROR: path $ZIP_PATH is not absolute" 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 diff --git a/testing/juggler/content/PageAgent.js b/testing/juggler/content/PageAgent.js
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..03c4c9717148169110f7e7d19306a76984ed4860 index 0000000000000000000000000000000000000000..37ab5f56739cfd16200a4ada9f4cf83436688eba
--- /dev/null --- /dev/null
+++ b/testing/juggler/content/PageAgent.js +++ b/testing/juggler/content/PageAgent.js
@@ -0,0 +1,721 @@ @@ -0,0 +1,843 @@
+"use strict"; +"use strict";
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const Ci = Components.interfaces; +const Ci = Components.interfaces;
@ -1839,11 +1839,28 @@ index 0000000000000000000000000000000000000000..03c4c9717148169110f7e7d19306a769
+ +
+const helper = new Helper(); +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 { +class FrameData {
+ constructor(agent, frame) { + constructor(agent, frame) {
+ this._agent = agent; + this._agent = agent;
+ this._frame = frame; + this._frame = frame;
+ this._isolatedWorlds = new Map(); + this._isolatedWorlds = new Map();
+ this._workers = new Map();
+ this.reset(); + this.reset();
+ } + }
+ +
@ -1913,6 +1930,59 @@ index 0000000000000000000000000000000000000000..03c4c9717148169110f7e7d19306a769
+ } + }
+ throw new Error('Cannot find object with id = ' + objectId); + 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 { +class PageAgent {
@ -1934,6 +2004,24 @@ index 0000000000000000000000000000000000000000..03c4c9717148169110f7e7d19306a769
+ this._docShell = docShell; + this._docShell = docShell;
+ this._initialDPPX = docShell.contentViewer.overrideDPPX; + this._initialDPPX = docShell.contentViewer.overrideDPPX;
+ this._customScrollbars = null; + 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}) { + async awaitViewportDimensions({width, height}) {
@ -2042,12 +2130,43 @@ index 0000000000000000000000000000000000000000..03c4c9717148169110f7e7d19306a769
+ helper.on(this._frameTree, 'navigationaborted', this._onNavigationAborted.bind(this)), + helper.on(this._frameTree, 'navigationaborted', this._onNavigationAborted.bind(this)),
+ helper.on(this._frameTree, 'samedocumentnavigation', this._onSameDocumentNavigation.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}) { + setInterceptFileChooserDialog({enabled}) {
+ this._docShell.fileInputInterceptionEnabled = !!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) { + _filePickerShown(inputElement) {
+ if (inputElement.ownerGlobal.docShell !== this._docShell) + if (inputElement.ownerGlobal.docShell !== this._docShell)
+ return; + return;
@ -2166,7 +2285,10 @@ index 0000000000000000000000000000000000000000..03c4c9717148169110f7e7d19306a769
+ } + }
+ +
+ dispose() { + dispose() {
+ for (const frameData of this._frameData.values())
+ frameData.dispose();
+ helper.removeListeners(this._eventListeners); + helper.removeListeners(this._eventListeners);
+ this._wdm.removeListener(this._wdmListener);
+ } + }
+ +
+ async navigate({frameId, url, referer}) { + 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 diff --git a/testing/juggler/content/RuntimeAgent.js b/testing/juggler/content/RuntimeAgent.js
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac357fb9b60 index 0000000000000000000000000000000000000000..5765d5c3b1de7b9383a80435b37b034d6951d981
--- /dev/null --- /dev/null
+++ b/testing/juggler/content/RuntimeAgent.js +++ b/testing/juggler/content/RuntimeAgent.js
@@ -0,0 +1,478 @@ @@ -0,0 +1,545 @@
+"use strict"; +"use strict";
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); +// Note: this file should be loadabale with eval() into worker environment.
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); +// Avoid Components.*, ChromeUtils and global const variables.
+const {addDebuggerToGlobal} = ChromeUtils.import("resource://gre/modules/jsdebugger.jsm", {});
+ +
+const Ci = Components.interfaces; +if (!this.Debugger) {
+const Cr = Components.results; + // Worker has a Debugger defined already.
+const Cu = Components.utils; + const {addDebuggerToGlobal} = ChromeUtils.import("resource://gre/modules/jsdebugger.jsm", {});
+addDebuggerToGlobal(Cu.getGlobalForObject(this)); + addDebuggerToGlobal(Components.utils.getGlobalForObject(this));
+const helper = new Helper(); +}
+
+let lastId = 0;
+function generateId() {
+ return 'id-' + (++lastId);
+}
+ +
+const consoleLevelToProtocolType = { +const consoleLevelToProtocolType = {
+ 'dir': 'dir', + 'dir': 'dir',
@ -2603,13 +2729,39 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+]); +]);
+ +
+class RuntimeAgent { +class RuntimeAgent {
+ constructor(session) { + constructor(session, onWorkerConsoleMessage) {
+ this._debugger = new Debugger(); + this._debugger = new Debugger();
+ this._pendingPromises = new Map(); + this._pendingPromises = new Map();
+ this._session = session; + this._session = session;
+ this._executionContexts = new Map(); + this._executionContexts = new Map();
+ this._windowToExecutionContext = 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]), + QueryInterface: ChromeUtils.generateQI([Ci.nsIConsoleListener]),
+ +
+ observe: message => { + observe: message => {
@ -2618,6 +2770,11 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ return; + return;
+ } + }
+ const errorWindow = Services.wm.getOuterWindowWithId(message.outerWindowID); + 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); + const executionContext = this._windowToExecutionContext.get(errorWindow);
+ if (!executionContext) + if (!executionContext)
+ return; + return;
@ -2641,47 +2798,67 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ }); + });
+ }, + },
+ }; + };
+ + Services.console.registerListener(consoleServiceListener);
+ this._eventListeners = []; + this._eventListeners.push(() => Services.console.unregisterListener(consoleServiceListener));
+ this._enabled = false;
+ } + }
+ +
+ _consoleAPICalled({wrappedJSObject}, topic, data) { + _registerConsoleObserver(Services) {
+ const type = consoleLevelToProtocolType[wrappedJSObject.level]; + const consoleObserver = ({wrappedJSObject}, topic, data) => {
+ if (!type) + const hash = this._consoleMessageHash(wrappedJSObject);
+ if (this._filteredConsoleMessageHashes.has(hash)) {
+ this._filteredConsoleMessageHashes.delete(hash);
+ return; + return;
+ }
+ const executionContext = Array.from(this._executionContexts.values()).find(context => { + const executionContext = Array.from(this._executionContexts.values()).find(context => {
+ const domWindow = context._domWindow; + const domWindow = context._domWindow;
+ return domWindow && domWindow.windowUtils.currentInnerWindowID === wrappedJSObject.innerID; + return domWindow && domWindow.windowUtils.currentInnerWindowID === wrappedJSObject.innerID;
+ }); + });
+ if (!executionContext) + if (!executionContext)
+ return; + 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', { + this._session.emitEvent('Runtime.console', {
+ args, + args,
+ type, + type,
+ executionContextId: executionContext.id(), + executionContextId: executionContext.id(),
+ location: { + location: {
+ lineNumber: wrappedJSObject.lineNumber - 1, + lineNumber: message.lineNumber - 1,
+ columnNumber: wrappedJSObject.columnNumber - 1, + columnNumber: message.columnNumber - 1,
+ url: wrappedJSObject.filename, + 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) { + _notifyExecutionContextCreated(executionContext) {
+ if (!this._enabled) + if (!this._enabled)
+ return; + return;
@ -2700,7 +2877,9 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ } + }
+ +
+ dispose() { + dispose() {
+ helper.removeListeners(this._eventListeners); + for (const tearDown of this._eventListeners)
+ tearDown.call(null);
+ this._eventListeners = [];
+ } + }
+ +
+ async _awaitPromise(executionContext, obj, exceptionDetails = {}) { + async _awaitPromise(executionContext, obj, exceptionDetails = {}) {
@ -2742,8 +2921,10 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ } + }
+ +
+ createExecutionContext(domWindow, contextGlobal, auxData) { + 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); + this._executionContexts.set(context._id, context);
+ if (domWindow)
+ this._windowToExecutionContext.set(domWindow, context); + this._windowToExecutionContext.set(domWindow, context);
+ this._notifyExecutionContextCreated(context); + this._notifyExecutionContextCreated(context);
+ return context; + return context;
@ -2765,8 +2946,9 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ } + }
+ if (!this._pendingPromises.size) + if (!this._pendingPromises.size)
+ this._debugger.onPromiseSettled = undefined; + this._debugger.onPromiseSettled = undefined;
+ this._debugger.removeDebuggee(destroyedContext._domWindow); + this._debugger.removeDebuggee(destroyedContext._contextGlobal);
+ this._executionContexts.delete(destroyedContext._id); + this._executionContexts.delete(destroyedContext._id);
+ if (destroyedContext._domWindow)
+ this._windowToExecutionContext.delete(destroyedContext._domWindow); + this._windowToExecutionContext.delete(destroyedContext._domWindow);
+ this._notifyExecutionContextDestroyed(destroyedContext); + this._notifyExecutionContextDestroyed(destroyedContext);
+ } + }
@ -2813,12 +2995,13 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+} +}
+ +
+class ExecutionContext { +class ExecutionContext {
+ constructor(runtime, domWindow, global, auxData) { + constructor(runtime, domWindow, contextGlobal, global, auxData) {
+ this._runtime = runtime; + this._runtime = runtime;
+ this._domWindow = domWindow; + this._domWindow = domWindow;
+ this._contextGlobal = contextGlobal;
+ this._global = global; + this._global = global;
+ this._remoteObjects = new Map(); + this._remoteObjects = new Map();
+ this._id = helper.generateId(); + this._id = generateId();
+ this._auxData = auxData; + this._auxData = auxData;
+ this._jsonStringifyObject = this._global.executeInGlobal(`((stringify, dateProto, object) => { + this._jsonStringifyObject = this._global.executeInGlobal(`((stringify, dateProto, object) => {
+ const oldToJson = dateProto.toJSON; + const oldToJson = dateProto.toJSON;
@ -2839,9 +3022,9 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ } + }
+ +
+ async evaluateScript(script, exceptionDetails = {}) { + 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); + let {success, obj} = this._getResult(this._global.executeInGlobal(script), exceptionDetails);
+ userInputHelper.destruct(); + userInputHelper && userInputHelper.destruct();
+ if (!success) + if (!success)
+ return null; + return null;
+ if (obj && obj.isPromise) { + if (obj && obj.isPromise) {
@ -2873,9 +3056,9 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ default: return this._toDebugger(arg.value); + 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); + let {success, obj} = this._getResult(funEvaluation.obj.apply(null, args), exceptionDetails);
+ userInputHelper.destruct(); + userInputHelper && userInputHelper.destruct();
+ if (!success) + if (!success)
+ return null; + return null;
+ if (obj && obj.isPromise) { + if (obj && obj.isPromise) {
@ -2898,9 +3081,15 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ return this._createRemoteObject(debuggerObj); + 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) { + _createRemoteObject(debuggerObj) {
+ if (debuggerObj instanceof Debugger.Object) { + if (debuggerObj instanceof Debugger.Object) {
+ const objectId = helper.generateId(); + const objectId = generateId();
+ this._remoteObjects.set(objectId, debuggerObj); + this._remoteObjects.set(objectId, debuggerObj);
+ const rawObj = debuggerObj.unsafeDereference(); + const rawObj = debuggerObj.unsafeDereference();
+ const type = typeof rawObj; + const type = typeof rawObj;
@ -2911,35 +3100,35 @@ index 0000000000000000000000000000000000000000..262011d8fda346078a6cfcb7aae5dac3
+ subtype = 'array'; + subtype = 'array';
+ else if (Object.is(rawObj, null)) + else if (Object.is(rawObj, null))
+ subtype = 'null'; + subtype = 'null';
+ else if (rawObj instanceof this._domWindow.Node) + else if (this._instanceOf(debuggerObj, rawObj, 'Node'))
+ subtype = 'node'; + subtype = 'node';
+ else if (rawObj instanceof this._domWindow.RegExp) + else if (this._instanceOf(debuggerObj, rawObj, 'RegExp'))
+ subtype = 'regexp'; + subtype = 'regexp';
+ else if (rawObj instanceof this._domWindow.Date) + else if (this._instanceOf(debuggerObj, rawObj, 'Date'))
+ subtype = 'date'; + subtype = 'date';
+ else if (rawObj instanceof this._domWindow.Map) + else if (this._instanceOf(debuggerObj, rawObj, 'Map'))
+ subtype = 'map'; + subtype = 'map';
+ else if (rawObj instanceof this._domWindow.Set) + else if (this._instanceOf(debuggerObj, rawObj, 'Set'))
+ subtype = 'set'; + subtype = 'set';
+ else if (rawObj instanceof this._domWindow.WeakMap) + else if (this._instanceOf(debuggerObj, rawObj, 'WeakMap'))
+ subtype = 'weakmap'; + subtype = 'weakmap';
+ else if (rawObj instanceof this._domWindow.WeakSet) + else if (this._instanceOf(debuggerObj, rawObj, 'WeakSet'))
+ subtype = 'weakset'; + subtype = 'weakset';
+ else if (rawObj instanceof this._domWindow.Error) + else if (this._instanceOf(debuggerObj, rawObj, 'Error'))
+ subtype = 'error'; + subtype = 'error';
+ else if (rawObj instanceof this._domWindow.Promise) + else if (this._instanceOf(debuggerObj, rawObj, 'Promise'))
+ subtype = 'promise'; + subtype = 'promise';
+ else if ((rawObj instanceof this._domWindow.Int8Array) || (rawObj instanceof this._domWindow.Uint8Array) || + else if ((this._instanceOf(debuggerObj, rawObj, 'Int8Array')) || (this._instanceOf(debuggerObj, rawObj, 'Uint8Array')) ||
+ (rawObj instanceof this._domWindow.Uint8ClampedArray) || (rawObj instanceof this._domWindow.Int16Array) || + (this._instanceOf(debuggerObj, rawObj, 'Uint8ClampedArray')) || (this._instanceOf(debuggerObj, rawObj, 'Int16Array')) ||
+ (rawObj instanceof this._domWindow.Uint16Array) || (rawObj instanceof this._domWindow.Int32Array) || + (this._instanceOf(debuggerObj, rawObj, 'Uint16Array')) || (this._instanceOf(debuggerObj, rawObj, 'Int32Array')) ||
+ (rawObj instanceof this._domWindow.Uint32Array) || (rawObj instanceof this._domWindow.Float32Array) || + (this._instanceOf(debuggerObj, rawObj, 'Uint32Array')) || (this._instanceOf(debuggerObj, rawObj, 'Float32Array')) ||
+ (rawObj instanceof this._domWindow.Float64Array)) { + (this._instanceOf(debuggerObj, rawObj, 'Float64Array'))) {
+ subtype = 'typedarray'; + subtype = 'typedarray';
+ } + }
+ return {objectId, type, subtype}; + return {objectId, type, subtype};
+ } + }
+ if (typeof debuggerObj === 'symbol') { + if (typeof debuggerObj === 'symbol') {
+ const objectId = helper.generateId(); + const objectId = generateId();
+ this._remoteObjects.set(objectId, debuggerObj); + this._remoteObjects.set(objectId, debuggerObj);
+ return {objectId, type: 'symbol'}; + return {objectId, type: 'symbol'};
+ } + }
@ -3124,6 +3313,79 @@ index 0000000000000000000000000000000000000000..caee4df323d0a526ed7e38947c41c643
+var EXPORTED_SYMBOLS = ['ScrollbarManager']; +var EXPORTED_SYMBOLS = ['ScrollbarManager'];
+this.ScrollbarManager = 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 diff --git a/testing/juggler/content/floating-scrollbars.css b/testing/juggler/content/floating-scrollbars.css
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..7709bdd34c65062fc63684ef17fc792d3991d965 index 0000000000000000000000000000000000000000..7709bdd34c65062fc63684ef17fc792d3991d965
@ -3243,10 +3505,10 @@ index 0000000000000000000000000000000000000000..8585092e04e7e763a0c115c28363e505
+ +
diff --git a/testing/juggler/jar.mn b/testing/juggler/jar.mn diff --git a/testing/juggler/jar.mn b/testing/juggler/jar.mn
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..27f5a15fd7f14385bb1f080d5965d90e60423d1a index 0000000000000000000000000000000000000000..76377927a8c9af3cac3b028ff754491966d03ba3
--- /dev/null --- /dev/null
+++ b/testing/juggler/jar.mn +++ 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 +# 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 +# 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/. +# 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/NetworkMonitor.js (content/NetworkMonitor.js)
+ content/content/PageAgent.js (content/PageAgent.js) + content/content/PageAgent.js (content/PageAgent.js)
+ content/content/RuntimeAgent.js (content/RuntimeAgent.js) + content/content/RuntimeAgent.js (content/RuntimeAgent.js)
+ content/content/WorkerMain.js (content/WorkerMain.js)
+ content/content/ScrollbarManager.js (content/ScrollbarManager.js) + content/content/ScrollbarManager.js (content/ScrollbarManager.js)
+ content/content/floating-scrollbars.css (content/floating-scrollbars.css) + content/content/floating-scrollbars.css (content/floating-scrollbars.css)
+ content/content/hidden-scrollbars.css (content/hidden-scrollbars.css) + content/content/hidden-scrollbars.css (content/hidden-scrollbars.css)
@ -3392,10 +3655,10 @@ index 0000000000000000000000000000000000000000..708059a95b3a01f3d9c7b7ef4714ee6f
+this.BrowserHandler = BrowserHandler; +this.BrowserHandler = BrowserHandler;
diff --git a/testing/juggler/protocol/Dispatcher.js b/testing/juggler/protocol/Dispatcher.js diff --git a/testing/juggler/protocol/Dispatcher.js b/testing/juggler/protocol/Dispatcher.js
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..7b3a6fa4fe7a2b50a78ed446fbf5537504994798 index 0000000000000000000000000000000000000000..956988738079272be8d3998dcbbaa91abc415fcc
--- /dev/null --- /dev/null
+++ b/testing/juggler/protocol/Dispatcher.js +++ b/testing/juggler/protocol/Dispatcher.js
@@ -0,0 +1,255 @@ @@ -0,0 +1,254 @@
+const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js"); +const {TargetRegistry} = ChromeUtils.import("chrome://juggler/content/TargetRegistry.js");
+const {protocol, checkScheme} = ChromeUtils.import("chrome://juggler/content/protocol/Protocol.js"); +const {protocol, checkScheme} = ChromeUtils.import("chrome://juggler/content/protocol/Protocol.js");
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); +const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
@ -3630,7 +3893,6 @@ index 0000000000000000000000000000000000000000..7b3a6fa4fe7a2b50a78ed446fbf55375
+ +
+ _onMessage({data}) { + _onMessage({data}) {
+ if (data.id) { + if (data.id) {
+ let id = data.id;
+ const {resolve, reject} = this._pendingMessages.get(data.id); + const {resolve, reject} = this._pendingMessages.get(data.id);
+ this._pendingMessages.delete(data.id); + this._pendingMessages.delete(data.id);
+ if (data.error) + if (data.error)
@ -3813,10 +4075,10 @@ index 0000000000000000000000000000000000000000..f5e7e919594b3778fd3046bf69d34878
+this.NetworkHandler = NetworkHandler; +this.NetworkHandler = NetworkHandler;
diff --git a/testing/juggler/protocol/PageHandler.js b/testing/juggler/protocol/PageHandler.js diff --git a/testing/juggler/protocol/PageHandler.js b/testing/juggler/protocol/PageHandler.js
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..bf59b2afa8692d02fd0ce664eec2e9827a8209d2 index 0000000000000000000000000000000000000000..23a32be2200e90e2e05d31aec85874a829cb1bbe
--- /dev/null --- /dev/null
+++ b/testing/juggler/protocol/PageHandler.js +++ b/testing/juggler/protocol/PageHandler.js
@@ -0,0 +1,281 @@ @@ -0,0 +1,285 @@
+"use strict"; +"use strict";
+ +
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); +const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
@ -4039,6 +4301,10 @@ index 0000000000000000000000000000000000000000..bf59b2afa8692d02fd0ce664eec2e982
+ async handleFileChooser(options) { + async handleFileChooser(options) {
+ return await this._contentSession.send('Page.handleFileChooser', options); + return await this._contentSession.send('Page.handleFileChooser', options);
+ } + }
+
+ async sendMessageToWorker(options) {
+ return await this._contentSession.send('Page.sendMessageToWorker', options);
+ }
+} +}
+ +
+class Dialog { +class Dialog {
@ -4249,10 +4515,10 @@ index 0000000000000000000000000000000000000000..78b6601b91d0b7fcda61114e6846aa07
+this.EXPORTED_SYMBOLS = ['t', 'checkScheme']; +this.EXPORTED_SYMBOLS = ['t', 'checkScheme'];
diff --git a/testing/juggler/protocol/Protocol.js b/testing/juggler/protocol/Protocol.js diff --git a/testing/juggler/protocol/Protocol.js b/testing/juggler/protocol/Protocol.js
new file mode 100644 new file mode 100644
index 0000000000000000000000000000000000000000..75b5276d085bd4217389cd05099895ebec2438b6 index 0000000000000000000000000000000000000000..1eecb6120f101cb7506fcf8d40c177089e62671b
--- /dev/null --- /dev/null
+++ b/testing/juggler/protocol/Protocol.js +++ 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'); +const {t, checkScheme} = ChromeUtils.import('chrome://juggler/content/protocol/PrimitiveTypes.js');
+ +
+// Protocol-specific types. +// Protocol-specific types.
@ -4738,6 +5004,18 @@ index 0000000000000000000000000000000000000000..75b5276d085bd4217389cd05099895eb
+ executionContextId: t.String, + executionContextId: t.String,
+ element: runtimeTypes.RemoteObject + 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: { + methods: {
@ -4940,6 +5218,13 @@ index 0000000000000000000000000000000000000000..75b5276d085bd4217389cd05099895eb
+ enabled: t.Boolean, + 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_REVISION=$(cat ../webkit/BUILD_NUMBER)
WK_ARCHIVES=( 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.14.zip"
"$HOST/webkit/%s/minibrowser-mac-10.15.zip" "$HOST/webkit/%s/minibrowser-mac-10.15.zip"
"$HOST/webkit/%s/minibrowser-win64.zip" "$HOST/webkit/%s/minibrowser-win64.zip"
) )
WK_ALIASES=( WK_ALIASES=(
"WK-LINUX" "WK-GTK"
"WK-WPE"
"WK-MAC-10.14" "WK-MAC-10.14"
"WK-MAC-10.15" "WK-MAC-10.15"
"WK-WIN64" "WK-WIN64"

View file

@ -6,7 +6,7 @@ trap "cd $(pwd -P)" EXIT
cd "$(dirname "$0")" cd "$(dirname "$0")"
if [[ ($1 == '--help') || ($1 == '-h') ]]; then 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
echo "Upload .zip as a browser build." echo "Upload .zip as a browser build."
echo echo
@ -30,65 +30,48 @@ if [[ $# < 1 ]]; then
echo "try '$(basename $0) --help' for more information" echo "try '$(basename $0) --help' for more information"
exit 1 exit 1
fi fi
BUILD_FLAVOR="$1"
BROWSER_NAME="" BROWSER_NAME=""
BUILD_NUMBER=""
BLOB_NAME="" BLOB_NAME=""
ALIAS="" if [[ "$BUILD_FLAVOR" == "firefox-linux" ]]; then
if [[ ("$1" == "firefox") || ("$1" == "firefox/") ]]; then
BUILD_NUMBER=$(cat "$PWD/firefox/BUILD_NUMBER")
BROWSER_NAME="firefox" 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" BLOB_NAME="firefox-linux.zip"
ALIAS="ff-linux r$BUILD_NUMBER" elif [[ "$BUILD_FLAVOR" == "firefox-mac" ]]; then
elif [[ "$(uname)" == MINGW* ]]; then BROWSER_NAME="firefox"
BLOB_NAME="firefox-win32.zip" BLOB_NAME="firefox-mac.zip"
ALIAS="ff-win32 r$BUILD_NUMBER" elif [[ "$BUILD_FLAVOR" == "firefox-win32" ]]; then
else BROWSER_NAME="firefox"
echo "ERROR: unsupported platform - $(uname)" BLOB_NAME="firefox-win32.zip"
exit 1 elif [[ "$BUILD_FLAVOR" == "firefox-win64" ]]; then
fi
elif [[ ("$1" == "firefox-win64") || ("$1" == "firefox-win64/") ]]; then
BUILD_NUMBER=$(cat "$PWD/firefox/BUILD_NUMBER")
BROWSER_NAME="firefox" BROWSER_NAME="firefox"
if [[ "$(uname)" == MINGW* ]]; then
BLOB_NAME="firefox-win64.zip" BLOB_NAME="firefox-win64.zip"
ALIAS="ff-win64 r$BUILD_NUMBER" elif [[ "$BUILD_FLAVOR" == "webkit-gtk" ]]; then
else BROWSER_NAME="webkit"
echo "ERROR: unsupported platform for browser '$1' - $(uname)" BLOB_NAME="minibrowser-gtk.zip"
exit 1 elif [[ "$BUILD_FLAVOR" == "webkit-wpe" ]]; then
fi BROWSER_NAME="webkit"
elif [[ ("$1" == "webkit") || ("$1" == "webkit/") ]]; then BLOB_NAME="minibrowser-wpe.zip"
BUILD_NUMBER=$(cat "$PWD/webkit/BUILD_NUMBER") 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" 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" BLOB_NAME="minibrowser-win64.zip"
ALIAS="webkit-win64 r$BUILD_NUMBER" elif [[ "$BUILD_FLAVOR" == "webkit-mac-10.14" ]]; then
else BROWSER_NAME="webkit"
echo "ERROR: unsupported platform - $(uname)" BLOB_NAME="minibrowser-mac-10.14.zip"
exit 1 elif [[ "$BUILD_FLAVOR" == "webkit-mac-10.15" ]]; then
fi BROWSER_NAME="webkit"
BLOB_NAME="minibrowser-mac-10.15.zip"
else else
echo ERROR: unknown browser to export - "$1" echo ERROR: unknown build flavor - "$BUILD_FLAVOR"
exit 1 exit 1
fi fi
if [[ ("$2" == '--show-alias') || ("$3" == '--show-alias') ]]; then BUILD_NUMBER=$(cat ./$BROWSER_NAME/BUILD_NUMBER)
echo $ALIAS
exit 0
fi
BLOB_PATH="$BROWSER_NAME/$BUILD_NUMBER/$BLOB_NAME" BLOB_PATH="$BROWSER_NAME/$BUILD_NUMBER/$BLOB_NAME"
if [[ ("$2" == '--check') || ("$3" == '--check') ]]; then 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") 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 if [[ $EXISTS == "true" ]]; then
@ -103,7 +86,9 @@ if [[ $# < 2 ]]; then
echo "try '$(basename $0) --help' for more information" echo "try '$(basename $0) --help' for more information"
exit 1 exit 1
fi fi
ZIP_PATH=$2
ZIP_PATH="$2"
if ! [[ -f $ZIP_PATH ]]; then if ! [[ -f $ZIP_PATH ]]; then
echo "ERROR: $ZIP_PATH does not exist" echo "ERROR: $ZIP_PATH does not exist"
exit 1 exit 1

View file

@ -1 +1 @@
1097 1100

View file

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

View file

@ -10,11 +10,17 @@ if [[ "$(uname)" == "Darwin" ]]; then
./Tools/Scripts/build-webkit --release --touch-events ./Tools/Scripts/build-webkit --release --touch-events
elif [[ "$(uname)" == "Linux" ]]; then elif [[ "$(uname)" == "Linux" ]]; then
cd "checkout" cd "checkout"
# Check that WebKitBuild exists and is not empty. if [[ "$1" == "--wpe" ]]; then
if ! [[ (-d ./WebKitBuild) && (-n $(ls -1 ./WebKitBuild/)) ]]; 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 yes | DEBIAN_FRONTEND=noninteractive ./Tools/Scripts/update-webkitgtk-libs
fi fi
./Tools/Scripts/build-webkit --gtk --release --touch-events MiniBrowser ./Tools/Scripts/build-webkit --gtk --release --touch-events MiniBrowser
fi
elif [[ "$(uname)" == MINGW* ]]; then elif [[ "$(uname)" == MINGW* ]]; then
/c/Windows/System32/cmd.exe "/c buildwin.bat" /c/Windows/System32/cmd.exe "/c buildwin.bat"
else 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 // 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. // 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 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 --- a/Source/cmake/OptionsWPE.cmake
+++ b/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. # Changing these options is completely unsupported.
WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_ASYNC_SCROLLING PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_ASYNC_SCROLLING PRIVATE ON)
WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_CONTENT_EXTENSIONS 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_MHTML PRIVATE ON)
WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_NETSCAPE_PLUGIN_API PRIVATE OFF) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_NETSCAPE_PLUGIN_API PRIVATE OFF)
diff --git a/Source/cmake/OptionsWin.cmake b/Source/cmake/OptionsWin.cmake 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 --- a/Source/cmake/OptionsWin.cmake
+++ b/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_PUBLIC_SUFFIX_LIST PRIVATE ON)
WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_USER_MESSAGE_HANDLERS PRIVATE ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_USER_MESSAGE_HANDLERS PRIVATE ON)
WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEBGL PUBLIC ON) WEBKIT_OPTION_DEFAULT_PORT_VALUE(ENABLE_WEBGL PUBLIC ON)

View file

@ -2,11 +2,11 @@
function runOSX() { function runOSX() {
# if script is run as-is # 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" 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" 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" DYLIB_PATH="$SCRIPT_PATH/WebKitBuild/Release"
else else
echo "Cannot find a MiniBrowser.app in neither location" 1>&2 echo "Cannot find a MiniBrowser.app in neither location" 1>&2
@ -18,14 +18,23 @@ function runOSX() {
function runLinux() { function runLinux() {
# if script is run as-is # if script is run as-is
if [ -d $SCRIPT_PATH/checkout/WebKitBuild ]; then DEPENDENCIES_FOLDER="DependenciesGTK"
LD_PATH="$SCRIPT_PATH/checkout/WebKitBuild/DependenciesGTK/Root/lib:$SCRIPT_PATH/checkout/WebKitBuild/Release/bin" 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" MINIBROWSER="$SCRIPT_PATH/checkout/WebKitBuild/Release/bin/MiniBrowser"
elif [ -f $SCRIPT_PATH/MiniBrowser ]; then elif [[ -f $SCRIPT_PATH/MiniBrowser ]]; then
LD_PATH="$SCRIPT_PATH" LD_PATH="$SCRIPT_PATH"
MINIBROWSER="$SCRIPT_PATH/MiniBrowser" MINIBROWSER="$SCRIPT_PATH/MiniBrowser"
elif [ -d $SCRIPT_PATH/WebKitBuild ]; then elif [[ -d $SCRIPT_PATH/WebKitBuild ]]; then
LD_PATH="$SCRIPT_PATH/WebKitBuild/DependenciesGTK/Root/lib:$SCRIPT_PATH/WebKitBuild/Release/bin" LD_PATH="$SCRIPT_PATH/WebKitBuild/$DEPENDENCIES_FOLDER/Root/lib:$SCRIPT_PATH/WebKitBuild/Release/bin"
MINIBROWSER="$SCRIPT_PATH/WebKitBuild/Release/bin/MiniBrowser" MINIBROWSER="$SCRIPT_PATH/WebKitBuild/Release/bin/MiniBrowser"
else else
echo "Cannot find a MiniBrowser.app in neither location" 1>&2 echo "Cannot find a MiniBrowser.app in neither location" 1>&2
@ -35,9 +44,9 @@ function runLinux() {
} }
SCRIPT_PATH="$(cd "$(dirname "$0")" ; pwd -P)" SCRIPT_PATH="$(cd "$(dirname "$0")" ; pwd -P)"
if [ "$(uname)" == "Darwin" ]; then if [[ "$(uname)" == "Darwin" ]]; then
runOSX "$@" runOSX "$@"
elif [ "$(uname)" == "Linux" ]; then elif [[ "$(uname)" == "Linux" ]]; then
runLinux "$@" runLinux "$@"
else else
echo "ERROR: cannot run on this platform!" 1>&2 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]) #### webkitPlaywright.defaultArgs([options])
- `options` <[Object]> Set of configurable options to set on the browser. Can have the following fields: - `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. - `args` <[Array]<[string]>> Additional arguments to pass to the browser instance.
- returns: <[Array]<[string]>> - 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`. - `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. - `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. - `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. - `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`. - `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`. - `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`. - `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. - `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. - `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. - `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`. - `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`. - `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.

View file

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

View file

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

View file

@ -97,6 +97,7 @@ export class Request {
_redirectChain: Request[]; _redirectChain: Request[];
_finalRequest: Request; _finalRequest: Request;
readonly _documentId?: string; readonly _documentId?: string;
readonly _isFavicon: boolean;
private _failureText: string | null = null; private _failureText: string | null = null;
private _url: string; private _url: string;
private _resourceType: string; private _resourceType: string;
@ -127,6 +128,7 @@ export class Request {
this._headers = headers; this._headers = headers;
this._waitForResponsePromise = new Promise(f => this._waitForResponsePromiseCallback = f); this._waitForResponsePromise = new Promise(f => this._waitForResponsePromiseCallback = f);
this._waitForFinishedPromise = new Promise(f => this._waitForFinishedPromiseCallback = f); this._waitForFinishedPromise = new Promise(f => this._waitForFinishedPromiseCallback = f);
this._isFavicon = url.endsWith('/favicon.ico');
} }
_setFailureText(failureText: string) { _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. // 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. // 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 { _createBrowserFetcher(options?: BrowserFetcherOptions): BrowserFetcher {
const downloadURLs = { 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', mac: '%s/builds/webkit/%s/minibrowser-mac-%s.zip',
win64: '%s/builds/webkit/%s/minibrowser-win64.zip', win64: '%s/builds/webkit/%s/minibrowser-win64.zip',
}; };

View file

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

View file

@ -43,10 +43,9 @@ export class WKPage implements PageDelegate {
_session: WKSession; _session: WKSession;
readonly _page: Page; readonly _page: Page;
private readonly _pageProxySession: WKSession; private readonly _pageProxySession: WKSession;
private readonly _networkManager: WKNetworkManager; readonly _networkManager: WKNetworkManager;
private readonly _workers: WKWorkers; private readonly _workers: WKWorkers;
private readonly _contextIdToContext: Map<number, dom.FrameExecutionContext>; private readonly _contextIdToContext: Map<number, dom.FrameExecutionContext>;
private _isolatedWorlds: Set<string>;
private _sessionListeners: RegisteredListener[] = []; private _sessionListeners: RegisteredListener[] = [];
private readonly _bootstrapScripts: string[] = []; private readonly _bootstrapScripts: string[] = [];
@ -55,14 +54,13 @@ export class WKPage implements PageDelegate {
this.rawKeyboard = new RawKeyboardImpl(pageProxySession); this.rawKeyboard = new RawKeyboardImpl(pageProxySession);
this.rawMouse = new RawMouseImpl(pageProxySession); this.rawMouse = new RawMouseImpl(pageProxySession);
this._contextIdToContext = new Map(); this._contextIdToContext = new Map();
this._isolatedWorlds = new Set();
this._page = new Page(this, browserContext); this._page = new Page(this, browserContext);
this._networkManager = new WKNetworkManager(this._page, pageProxySession); this._networkManager = new WKNetworkManager(this._page, pageProxySession);
this._workers = new WKWorkers(this._page); this._workers = new WKWorkers(this._page);
this._session = undefined as any as WKSession; this._session = undefined as any as WKSession;
} }
async _initializePageProxySession() { private async _initializePageProxySession() {
const promises : Promise<any>[] = [ const promises : Promise<any>[] = [
this._pageProxySession.send('Dialog.enable'), this._pageProxySession.send('Dialog.enable'),
this._networkManager.initializePageProxySession(this._page._state.credentials) this._networkManager.initializePageProxySession(this._page._state.credentials)
@ -83,38 +81,46 @@ export class WKPage implements PageDelegate {
this._addSessionListeners(); this._addSessionListeners();
this._networkManager.setSession(session); this._networkManager.setSession(session);
this._workers.setSession(session); this._workers.setSession(session);
this._isolatedWorlds = new Set();
// New bootstrap scripts may have been added during provisional load, push them // New bootstrap scripts may have been added during provisional load, push them
// again to be on the safe side. // again to be on the safe side.
if (this._bootstrapScripts.length) if (this._bootstrapScripts.length)
this._setBootstrapScripts(session).catch(e => debugError(e)); 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 // 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. // 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>[] = [ const promises : Promise<any>[] = [
// Page agent must be enabled before Runtime. // Page agent must be enabled before Runtime.
session.send('Page.enable'), 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. // 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('Console.enable'),
session.send('Page.setInterceptFileChooserDialog', { enabled: true }), session.send('Page.setInterceptFileChooserDialog', { enabled: true }),
this._networkManager.initializeSession(session, this._page._state.interceptNetwork, this._page._state.offlineMode), 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; const contextOptions = this._page.browserContext()._options;
if (contextOptions.userAgent) if (contextOptions.userAgent)
promises.push(session.send('Page.overrideUserAgent', { value: contextOptions.userAgent })); promises.push(session.send('Page.overrideUserAgent', { value: contextOptions.userAgent }));
if (this._page._state.mediaType || this._page._state.colorScheme) 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) if (isProvisional)
promises.push(this._setBootstrapScripts(session)); promises.push(this._setBootstrapScripts(session));
if (contextOptions.bypassCSP) if (contextOptions.bypassCSP)
promises.push(session.send('Page.setBypassCSP', { enabled: true })); promises.push(session.send('Page.setBypassCSP', { enabled: true }));
if (this._page._state.extraHTTPHeaders !== null) 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) if (this._page._state.hasTouch)
promises.push(session.send('Page.setTouchEmulationEnabled', { enabled: true })); promises.push(session.send('Page.setTouchEmulationEnabled', { enabled: true }));
await Promise.all(promises).catch(e => { await Promise.all(promises).catch(e => {
@ -285,21 +291,11 @@ export class WKPage implements PageDelegate {
this._page._onFileChooserOpened(handle); this._page._onFileChooserOpened(handle);
} }
async _ensureIsolatedWorld(name: string) { private static async _setExtraHTTPHeaders(session: WKSession, headers: network.Headers): Promise<void> {
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> {
await session.send('Network.setExtraHTTPHeaders', { headers }); 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 = []; const promises = [];
promises.push(session.send('Page.setEmulatedMedia', { media: mediaType || '' })); promises.push(session.send('Page.setEmulatedMedia', { media: mediaType || '' }));
if (colorScheme !== null) { if (colorScheme !== null) {
@ -314,11 +310,11 @@ export class WKPage implements PageDelegate {
} }
async setExtraHTTPHeaders(headers: network.Headers): Promise<void> { 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> { 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> { 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'; import { BrowserContext } from '../browserContext';
@ -9,17 +22,16 @@ import { WKSession } from './wkConnection';
import { WKPage } from './wkPage'; import { WKPage } from './wkPage';
import { RegisteredListener, helper, assert, debugError } from '../helper'; import { RegisteredListener, helper, assert, debugError } from '../helper';
import { Events } from '../events'; import { Events } from '../events';
import { WKProvisionalPage } from './wkProvisionalPage';
// We keep provisional messages on the session instace until provisional const isPovisionalSymbol = Symbol('isPovisional');
// target is committed. Non-provisional target (there should be just one)
// has undefined instead.
const provisionalMessagesSymbol = Symbol('provisionalMessages');
export class WKPageProxy { export class WKPageProxy {
private readonly _pageProxySession: WKSession; private readonly _pageProxySession: WKSession;
readonly _browserContext: BrowserContext; readonly _browserContext: BrowserContext;
private _pagePromise: Promise<Page> | null = null; private _pagePromise: Promise<Page> | null = null;
private _wkPage: WKPage | null = null; private _wkPage: WKPage | null = null;
private _provisionalPage: WKProvisionalPage | null = null;
private readonly _firstTargetPromise: Promise<void>; private readonly _firstTargetPromise: Promise<void>;
private _firstTargetCallback?: () => void; private _firstTargetCallback?: () => void;
private readonly _sessions = new Map<string, WKSession>(); private readonly _sessions = new Map<string, WKSession>();
@ -56,6 +68,10 @@ export class WKPageProxy {
for (const session of this._sessions.values()) for (const session of this._sessions.values())
session.dispose(); session.dispose();
this._sessions.clear(); this._sessions.clear();
if (this._provisionalPage) {
this._provisionalPage.dispose();
this._provisionalPage = null;
}
if (this._wkPage) if (this._wkPage)
this._wkPage.didDisconnect(); this._wkPage.didDisconnect();
} }
@ -66,7 +82,7 @@ export class WKPageProxy {
private _isProvisionalCrossProcessLoadInProgress() : boolean { private _isProvisionalCrossProcessLoadInProgress() : boolean {
for (const anySession of this._sessions.values()) { for (const anySession of this._sessions.values()) {
if ((anySession as any)[provisionalMessagesSymbol]) if ((anySession as any)[isPovisionalSymbol])
return true; return true;
} }
return false; return false;
@ -100,7 +116,7 @@ export class WKPageProxy {
await this._firstTargetPromise; await this._firstTargetPromise;
let session: WKSession | undefined; let session: WKSession | undefined;
for (const anySession of this._sessions.values()) { for (const anySession of this._sessions.values()) {
if (!(anySession as any)[provisionalMessagesSymbol]) { if (!(anySession as any)[isPovisionalSymbol]) {
session = anySession; session = anySession;
break; break;
} }
@ -108,10 +124,7 @@ export class WKPageProxy {
assert(session, 'One non-provisional target session must exist'); assert(session, 'One non-provisional target session must exist');
this._wkPage = new WKPage(this._browserContext, this._pageProxySession); this._wkPage = new WKPage(this._browserContext, this._pageProxySession);
this._wkPage.setSession(session!); this._wkPage.setSession(session!);
await Promise.all([ await this._wkPage.initialize();
this._wkPage._initializePageProxySession(),
this._wkPage._initializeSession(session!, false),
]);
return this._wkPage._page; return this._wkPage._page;
} }
@ -131,9 +144,11 @@ export class WKPageProxy {
this._firstTargetCallback = undefined; this._firstTargetCallback = undefined;
} }
if (targetInfo.isProvisional) if (targetInfo.isProvisional)
(session as any)[provisionalMessagesSymbol] = []; (session as any)[isPovisionalSymbol] = true;
if (targetInfo.isProvisional && this._wkPage) if (targetInfo.isProvisional && this._wkPage) {
this._wkPage._initializeSession(session, true); assert(!this._provisionalPage);
this._provisionalPage = new WKProvisionalPage(session, this._wkPage);
}
if (targetInfo.isPaused) if (targetInfo.isPaused)
this._pageProxySession.send('Target.resume', { targetId: targetInfo.targetId }).catch(debugError); this._pageProxySession.send('Target.resume', { targetId: targetInfo.targetId }).catch(debugError);
} }
@ -144,6 +159,10 @@ export class WKPageProxy {
if (session) if (session)
session.dispose(); session.dispose();
this._sessions.delete(targetId); 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) if (this._wkPage && this._wkPage._session === session && crashed)
this._wkPage.didClose(crashed); this._wkPage.didClose(crashed);
} }
@ -152,10 +171,6 @@ export class WKPageProxy {
const { targetId, message } = event; const { targetId, message } = event;
const session = this._sessions.get(targetId); const session = this._sessions.get(targetId);
assert(session, 'Unknown target: ' + targetId); assert(session, 'Unknown target: ' + targetId);
const provisionalMessages = (session as any)[provisionalMessagesSymbol];
if (provisionalMessages)
provisionalMessages.push(message);
else
session!.dispatchMessage(JSON.parse(message)); session!.dispatchMessage(JSON.parse(message));
} }
@ -167,11 +182,13 @@ export class WKPageProxy {
assert(oldSession, 'Unknown old target: ' + oldTargetId); assert(oldSession, 'Unknown old target: ' + oldTargetId);
// TODO: make some calls like screenshot catch swapped out error and retry. // TODO: make some calls like screenshot catch swapped out error and retry.
oldSession!.errorText = 'Target was swapped out.'; oldSession!.errorText = 'Target was swapped out.';
const provisionalMessages = (newSession as any)[provisionalMessagesSymbol]; (newSession as any)[isPovisionalSymbol] = undefined;
assert(provisionalMessages, 'Committing target must be provisional'); if (this._provisionalPage) {
(newSession as any)[provisionalMessagesSymbol] = undefined; this._provisionalPage.commit();
for (const message of provisionalMessages) this._provisionalPage.dispose();
newSession!.dispatchMessage(JSON.parse(message)); this._provisionalPage = null;
this._wkPage!.setSession(newSession!); }
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}) => { it('should intercept', async({page, server}) => {
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
if (utils.isFavicon(request)) {
request.continue();
return;
}
expect(request.url()).toContain('empty.html'); expect(request.url()).toContain('empty.html');
expect(request.headers()['user-agent']).toBeTruthy(); expect(request.headers()['user-agent']).toBeTruthy();
expect(request.method()).toBe('GET'); expect(request.method()).toBe('GET');
@ -94,7 +90,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
await page.setRequestInterception(true); await page.setRequestInterception(true);
const requests = []; const requests = [];
page.on('request', request => { page.on('request', request => {
if (!utils.isFavicon(request))
requests.push(request); requests.push(request);
request.continue(); request.continue();
}); });
@ -217,7 +212,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
const requests = []; const requests = [];
page.on('request', request => { page.on('request', request => {
request.continue(); request.continue();
if (!utils.isFavicon(request))
requests.push(request); requests.push(request);
}); });
server.setRedirect('/non-existing-page.html', '/non-existing-page-2.html'); 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 = []; const requests = [];
page.on('request', request => { page.on('request', request => {
request.continue(); request.continue();
if (!utils.isFavicon(request))
requests.push(request); requests.push(request);
}); });
server.setRedirect('/one-style.css', '/two-style.css'); server.setRedirect('/one-style.css', '/two-style.css');
@ -274,10 +267,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
let spinner = false; let spinner = false;
// Cancel 2nd request. // Cancel 2nd request.
page.on('request', request => { page.on('request', request => {
if (utils.isFavicon(request)) {
request.continue();
return;
}
spinner ? request.abort() : request.continue(); spinner ? request.abort() : request.continue();
spinner = !spinner; spinner = !spinner;
}); });
@ -292,7 +281,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
await page.setRequestInterception(true); await page.setRequestInterception(true);
const requests = []; const requests = [];
page.on('request', request => { page.on('request', request => {
if (!utils.isFavicon(request))
requests.push(request); requests.push(request);
request.continue(); request.continue();
}); });
@ -306,7 +294,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
await page.setRequestInterception(true); await page.setRequestInterception(true);
const requests = []; const requests = [];
page.on('request', request => { page.on('request', request => {
if (!utils.isFavicon(request))
requests.push(request); requests.push(request);
request.continue(); request.continue();
}); });
@ -319,7 +306,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
await page.setRequestInterception(true); await page.setRequestInterception(true);
const requests = []; const requests = [];
page.on('request', request => { page.on('request', request => {
if (!utils.isFavicon(request))
requests.push(request); requests.push(request);
request.continue(); request.continue();
}); });
@ -351,7 +337,6 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
const requests = []; const requests = [];
page.on('request', request => { page.on('request', request => {
request.continue(); request.continue();
if (!utils.isFavicon(request))
requests.push(request); requests.push(request);
}); });
const response = await page.goto(`data:text/html,<link rel="stylesheet" href="${server.PREFIX}/fonts?helvetica|arial"/>`); 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); await page.goto(server.EMPTY_PAGE);
expect(error.message).toContain('Request Interception is not enabled'); 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.goto(server.EMPTY_PAGE);
await page.setRequestInterception(true); await page.setRequestInterception(true);
let intercepted = false; 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); const notAnError = await request.continue().then(() => null).catch(e => e);
expect(notAnError).toBe(null); 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); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
if (request.url() !== server.PREFIX + '/one-style.css') 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'}); const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'domcontentloaded'});
expect(response.status()).toBe(200); 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.goto(server.EMPTY_PAGE);
await page.evaluate(() => { await page.evaluate(() => {
window.addEventListener('beforeunload', () => history.replaceState(null, 'initial', window.location.href), false); 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}) => { it('should navigate to dataURL and not fire dataURL requests', async({page, server}) => {
const requests = []; 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 dataURL = 'data:text/html,<div>yo</div>';
const response = await page.goto(dataURL); const response = await page.goto(dataURL);
expect(response).toBe(null); 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}) => { it('should navigate to URL with hash and fire requests without hash', async({page, server}) => {
const requests = []; 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'); const response = await page.goto(server.EMPTY_PAGE + '#hash');
expect(response.status()).toBe(200); expect(response.status()).toBe(200);
expect(response.url()).toBe(server.EMPTY_PAGE); 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() { describe('Page.Events.Request', function() {
it('should fire for navigation requests', async({page, server}) => { it('should fire for navigation requests', async({page, server}) => {
const requests = []; 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.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1); expect(requests.length).toBe(1);
}); });
it('should fire for iframes', async({page, server}) => { it('should fire for iframes', async({page, server}) => {
const requests = []; 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.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
expect(requests.length).toBe(2); expect(requests.length).toBe(2);
}); });
it('should fire for fetches', async({page, server}) => { it('should fire for fetches', async({page, server}) => {
const requests = []; 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.goto(server.EMPTY_PAGE);
await page.evaluate(() => fetch('/empty.html')); await page.evaluate(() => fetch('/empty.html'));
expect(requests.length).toBe(2); expect(requests.length).toBe(2);
@ -50,7 +50,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
describe('Request.frame', function() { describe('Request.frame', function() {
it('should work for main frame navigation request', async({page, server}) => { it('should work for main frame navigation request', async({page, server}) => {
const requests = []; 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.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1); expect(requests.length).toBe(1);
expect(requests[0].frame()).toBe(page.mainFrame()); 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}) => { it('should work for subframe navigation request', async({page, server}) => {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const requests = []; 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); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
expect(requests.length).toBe(1); expect(requests.length).toBe(1);
expect(requests[0].frame()).toBe(page.frames()[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}) => { it('should work for fetch requests', async({page, server}) => {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
let requests = []; 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')); await page.evaluate(() => fetch('/digits/1.png'));
requests = requests.filter(request => !request.url().includes('favicon')); requests = requests.filter(request => !request.url().includes('favicon'));
expect(requests.length).toBe(1); 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')); page.on('requestfinished', r => requestFinished = requestFinished || r.url().includes('/get'));
// send request and wait for server response // send request and wait for server response
const [pageResponse] = await Promise.all([ const [pageResponse] = await Promise.all([
page.waitForEvent('response', { predicate: r => !utils.isFavicon(r.request()) }), page.waitForEvent('response'),
page.evaluate(() => fetch('./get', { method: 'GET'})), page.evaluate(() => fetch('./get', { method: 'GET'})),
server.waitForRequest('/get'), server.waitForRequest('/get'),
]); ]);
@ -207,7 +207,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
describe('Network Events', function() { describe('Network Events', function() {
it('Page.Events.Request', async({page, server}) => { it('Page.Events.Request', async({page, server}) => {
const requests = []; 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.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1); expect(requests.length).toBe(1);
expect(requests[0].url()).toBe(server.EMPTY_PAGE); 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. // FIXME: WebKit doesn't provide remoteIPAddress in the response.
it.skip(WEBKIT)('Page.Events.Response', async({page, server}) => { it.skip(WEBKIT)('Page.Events.Response', async({page, server}) => {
const responses = []; 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); await page.goto(server.EMPTY_PAGE);
expect(responses.length).toBe(1); expect(responses.length).toBe(1);
expect(responses[0].url()).toBe(server.EMPTY_PAGE); 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}) => { it('Page.Events.RequestFinished', async({page, server}) => {
const requests = []; 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); await page.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1); expect(requests.length).toBe(1);
expect(requests[0].url()).toBe(server.EMPTY_PAGE); 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}) => { it('should fire events in proper order', async({page, server}) => {
const events = []; const events = [];
page.on('request', request => !utils.isFavicon(request) && events.push('request')); page.on('request', request => events.push('request'));
page.on('response', response => !utils.isFavicon(response.request()) && events.push('response')); page.on('response', response => events.push('response'));
page.on('requestfinished', request => !utils.isFavicon(request) && events.push('requestfinished')); page.on('requestfinished', request => events.push('requestfinished'));
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
expect(events).toEqual(['request', 'response', 'requestfinished']); expect(events).toEqual(['request', 'response', 'requestfinished']);
}); });
it('should support redirects', async({page, server}) => { it('should support redirects', async({page, server}) => {
const events = []; const events = [];
page.on('request', request => !utils.isFavicon(request) && events.push(`${request.method()} ${request.url()}`)); page.on('request', request => events.push(`${request.method()} ${request.url()}`));
page.on('response', response => !utils.isFavicon(response.request()) && events.push(`${response.status()} ${response.url()}`)); page.on('response', response => events.push(`${response.status()} ${response.url()}`));
page.on('requestfinished', request => !utils.isFavicon(request) && events.push(`DONE ${request.url()}`)); page.on('requestfinished', request => events.push(`DONE ${request.url()}`));
page.on('requestfailed', request => !utils.isFavicon(request) && events.push(`FAIL ${request.url()}`)); page.on('requestfailed', request => events.push(`FAIL ${request.url()}`));
server.setRedirect('/foo.html', '/empty.html'); server.setRedirect('/foo.html', '/empty.html');
const FOO_URL = server.PREFIX + '/foo.html'; const FOO_URL = server.PREFIX + '/foo.html';
const response = await page.goto(FOO_URL); 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 page.evaluate(() => !!window.opener)).toBe(false);
expect(await popup.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.goto(server.EMPTY_PAGE);
await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>'); await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>');
const [popup] = await Promise.all([ const [popup] = await Promise.all([

View file

@ -122,19 +122,11 @@ const utils = module.exports = {
frame.src = url; frame.src = url;
frame.id = frameId; frame.id = frameId;
document.body.appendChild(frame); 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); await new Promise(x => frame.onload = x);
return frame; return frame;
} }
}, },
isFavicon: function(request) {
return request.url().includes('favicon.ico');
},
/** /**
* @param {!Page} page * @param {!Page} page
* @param {string} frameId * @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 fs = require('fs');
const path = require('path'); const path = require('path');