Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
99cd82c7
Commit
99cd82c7
authored
Feb 07, 2022
by
Robert Knight
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add test for Safari <= 15 workaround PortRPC "close" event
parent
512c9d2c
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
113 additions
and
13 deletions
+113
-13
port-rpc.js
src/shared/messaging/port-rpc.js
+28
-12
port-rpc-test.js
src/shared/messaging/test/port-rpc-test.js
+85
-1
No files found.
src/shared/messaging/port-rpc.js
View file @
99cd82c7
...
...
@@ -79,10 +79,13 @@ function sendCall(port, method, args = [], sequence = -1) {
* in the host frame to ensure delivery of "close" notifications from "guest"
* frames.
*
* @param {string} [userAgent] - Test seam
* @return {() => void} - Callback that removes the listener
*/
export
function
installPortCloseWorkaroundForSafari
()
{
if
(
!
shouldUseSafariWorkaround
())
{
export
function
installPortCloseWorkaroundForSafari
(
userAgent
=
navigator
.
userAgent
)
{
if
(
!
shouldUseSafariWorkaround
(
userAgent
))
{
return
()
=>
{};
}
...
...
@@ -101,11 +104,11 @@ export function installPortCloseWorkaroundForSafari() {
/**
* Test whether this browser needs the workaround for https://bugs.webkit.org/show_bug.cgi?id=231167.
*
* @param {string} userAgent
*/
function
shouldUseSafariWorkaround
()
{
const
webkitVersionMatch
=
navigator
.
userAgent
.
match
(
/
\b
AppleWebKit
\/([
0-9
]
+
)\b
/
);
function
shouldUseSafariWorkaround
(
userAgent
)
{
const
webkitVersionMatch
=
userAgent
.
match
(
/
\b
AppleWebKit
\/([
0-9
]
+
)\b
/
);
if
(
!
webkitVersionMatch
)
{
return
false
;
}
...
...
@@ -148,7 +151,15 @@ function shouldUseSafariWorkaround() {
* @implements {Destroyable}
*/
export
class
PortRPC
{
constructor
()
{
/**
* @param {object} options
* @param {string} [options.userAgent] - Test seam
* @param {Window} [options.currentWindow] - Test seam
*/
constructor
({
userAgent
=
navigator
.
userAgent
,
currentWindow
=
window
,
}
=
{})
{
/** @type {MessagePort|null} */
this
.
_port
=
null
;
...
...
@@ -161,7 +172,7 @@ export class PortRPC {
this
.
_callbacks
=
new
Map
();
this
.
_listeners
=
new
ListenerCollection
();
this
.
_listeners
.
add
(
w
indow
,
'unload'
,
()
=>
{
this
.
_listeners
.
add
(
currentW
indow
,
'unload'
,
()
=>
{
if
(
this
.
_port
)
{
// Send "close" notification directly. This works in Chrome, Firefox and
// Safari >= 16.
...
...
@@ -170,10 +181,15 @@ export class PortRPC {
// To work around a bug in Safari <= 15 which prevents sending messages
// while a window is unloading, try transferring the port to the parent frame
// and re-sending the "close" event from there.
if
(
window
!==
window
.
parent
&&
shouldUseSafariWorkaround
())
{
window
.
parent
.
postMessage
({
type
:
'hypothesisPortClosed'
},
'*'
,
[
this
.
_port
,
]);
if
(
currentWindow
!==
currentWindow
.
parent
&&
shouldUseSafariWorkaround
(
userAgent
)
)
{
currentWindow
.
parent
.
postMessage
(
{
type
:
'hypothesisPortClosed'
},
'*'
,
[
this
.
_port
]
);
}
}
});
...
...
src/shared/messaging/test/port-rpc-test.js
View file @
99cd82c7
import
{
PortRPC
}
from
'../port-rpc'
;
import
{
PortRPC
,
installPortCloseWorkaroundForSafari
}
from
'../port-rpc'
;
describe
(
'PortRPC'
,
()
=>
{
let
port1
;
...
...
@@ -245,4 +245,88 @@ describe('PortRPC', () => {
assert
.
calledOnce
(
closeHandler
);
});
/** Transfer a MessagePort to another frame and return the transferred port. */
async
function
transferPort
(
port
,
targetWindow
)
{
const
transferredPort
=
new
Promise
(
resolve
=>
{
targetWindow
.
addEventListener
(
'message'
,
e
=>
{
if
(
e
.
ports
[
0
])
{
resolve
(
e
.
ports
[
0
]);
}
});
});
targetWindow
.
postMessage
({},
'*'
,
[
port
]);
return
transferredPort
;
}
describe
(
'Safari <= 15 workaround'
,
()
=>
{
const
safariUserAgent
=
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15'
;
let
removeWorkaround
;
let
childFrame
;
beforeEach
(()
=>
{
childFrame
=
document
.
createElement
(
'iframe'
);
document
.
body
.
append
(
childFrame
);
removeWorkaround
=
installPortCloseWorkaroundForSafari
(
safariUserAgent
);
});
afterEach
(()
=>
{
removeWorkaround
();
childFrame
.
remove
();
});
it
(
'transfers port to parent frame and sends "close" event from there when window is unloaded'
,
async
()
=>
{
const
{
port1
,
port2
}
=
new
MessageChannel
();
const
transferredPort
=
await
transferPort
(
port1
,
childFrame
.
contentWindow
);
const
sender
=
new
PortRPC
({
userAgent
:
safariUserAgent
,
currentWindow
:
childFrame
.
contentWindow
,
});
const
receiver
=
new
PortRPC
();
const
closeHandler
=
sinon
.
stub
();
receiver
.
on
(
'close'
,
closeHandler
);
receiver
.
connect
(
port2
);
sender
.
connect
(
transferredPort
);
await
waitForMessageDelivery
();
closeHandler
.
resetHistory
();
// Emulate the Safari bug by disabling `postMessage` on the sending port
// in the original frame. When the port is transferred to the "parent"
// frame, it will reconstituted as a new MessagePort instance.
transferredPort
.
postMessage
=
()
=>
{};
// Unload the child frame. The "unload" handler will transfer the port to
// the parent frame and the "close" event will be sent from there.
childFrame
.
remove
();
await
waitForMessageDelivery
();
assert
.
called
(
closeHandler
);
});
});
[
// Chrome 100
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4867.0 Safari/537.36'
,
// Firefox 96
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:96.0) Gecko/20100101 Firefox/96.0'
,
].
forEach
(
userAgent
=>
{
it
(
'does not use workaround in unaffected browsers'
,
()
=>
{
sinon
.
stub
(
window
,
'addEventListener'
);
const
removeWorkaround
=
installPortCloseWorkaroundForSafari
(
userAgent
);
removeWorkaround
();
assert
.
notCalled
(
window
.
addEventListener
);
window
.
addEventListener
.
restore
();
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment