forked from meteor/meteor
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'pr-1023' into pull-requests
- Loading branch information
Showing
11 changed files
with
321 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
local |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Meteor packages used by this project, one per line. | ||
# | ||
# 'meteor add' and 'meteor remove' will edit this file for you, | ||
# but you can also edit it by hand. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Defer in Inactive Tab | ||
|
||
Tests that `Meteor.defer` works in an inactive tab in iOS Safari. | ||
|
||
(`setTimeout` and `setInterval` events aren't delivered to inactive | ||
tabs in iOS Safari until they become active again). | ||
|
||
Sadly we have to run the test manually because scripts aren't allowed | ||
to open windows themselves except in response to user events. | ||
|
||
This test will not run on Chrome for iOS because the storage event is | ||
not implemented in that browser. Also doesn't attempt to run on | ||
versions of IE that don't support `window.addEventListener`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<head> | ||
<title>defer in inactive tab</title> | ||
<meta name="viewport" content="width=device-width"> | ||
</head> | ||
|
||
<body> | ||
{{> route}} | ||
</body> | ||
|
||
<template name="route"> | ||
{{#if isParent}} | ||
{{> parent}} | ||
{{else}} | ||
{{> child}} | ||
{{/if}} | ||
</template> | ||
|
||
<template name="parent"> | ||
<h1>Test Defer in Inactive Tab</h1> | ||
|
||
<p> | ||
Step one: open second tab: | ||
<button id="openTab">Open Tab</button> | ||
</p> | ||
|
||
<p> | ||
Step two: run test: | ||
<button id="runTest">Run Test</button> | ||
</p> | ||
|
||
<p> | ||
In a successful test the test status will immediately change to | ||
"test successful". (If you switch to the child tab yourself and | ||
that makes the test claim to be successful, that's actually an | ||
invalid test because you're letting the child tab become the | ||
active tab). | ||
</p> | ||
|
||
<p style="padding: 1em; outline: 1px solid gray"> | ||
Test status: <b>{{testStatus}}</b> | ||
</p> | ||
|
||
<p> | ||
After the test has run successfully you can close the child tab. | ||
</p> | ||
|
||
</template> | ||
|
||
<template name="child"> | ||
<p>This is the child.</p> | ||
<p>Switch back to the first tab and run the test.</p> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
if (Meteor.isClient) { | ||
|
||
var isParent = (window.location.pathname === '/'); | ||
var isChild = ! isParent; | ||
|
||
Template.route.isParent = function () { | ||
return isParent; | ||
}; | ||
|
||
Template.parent.testStatus = function () { | ||
return Session.get('testStatus'); | ||
}; | ||
|
||
Template.parent.events({ | ||
'click #openTab': function () { | ||
window.open('/child'); | ||
}, | ||
|
||
'click #runTest': function () { | ||
if (localStorage.getItem('ping') === '!' || | ||
localStorage.getItem('pong') === '!') { | ||
Session.set('testStatus', 'Test already run. Close the second tab (if open), refresh this page, and run again.'); | ||
} | ||
else { | ||
localStorage.setItem('ping', '!'); | ||
} | ||
} | ||
}); | ||
|
||
if (isParent) { | ||
Session.set('testStatus', ''); | ||
|
||
Meteor.startup(function () { | ||
localStorage.setItem('ping', null); | ||
localStorage.setItem('pong', null); | ||
}); | ||
window.addEventListener('storage', function (event) { | ||
if (event.key === 'pong' && event.newValue === '!') { | ||
Session.set('testStatus', 'test successful'); | ||
} | ||
}); | ||
} | ||
|
||
if (isChild) { | ||
window.addEventListener('storage', function (event) { | ||
if (event.key === 'ping' && event.newValue === '!') { | ||
// If we used setTimeout here in iOS Safari it wouldn't | ||
// work (unless we switched tabs) because setTimeout and | ||
// setInterval events don't fire in inactive tabs. | ||
Meteor.defer(function () { | ||
localStorage.setItem('pong', '!'); | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
// Chooses one of three setImmediate implementations: | ||
// | ||
// * Native setImmediate (IE 10, Node 0.9+) | ||
// | ||
// * postMessage (many browsers) | ||
// | ||
// * setTimeout (fallback) | ||
// | ||
// The postMessage implementation is based on | ||
// https://github.com/NobleJS/setImmediate/tree/1.0.1 | ||
// | ||
// Don't use `nextTick` for Node since it runs its callbacks before | ||
// I/O, which is stricter than we're looking for. | ||
// | ||
// Not installed as a polyfill, as our public API is `Meteor.defer`. | ||
// Since we're not trying to be a polyfill, we have some | ||
// simplifications: | ||
// | ||
// If one invocation of a setImmediate callback pauses itself by a | ||
// call to alert/prompt/showModelDialog, the NobleJS polyfill | ||
// implementation ensured that no setImmedate callback would run until | ||
// the first invocation completed. While correct per the spec, what it | ||
// would mean for us in practice is that any reactive updates relying | ||
// on Meteor.defer would be hung in the main window until the modal | ||
// dialog was dismissed. Thus we only ensure that a setImmediate | ||
// function is called in a later event loop. | ||
// | ||
// We don't need to support using a string to be eval'ed for the | ||
// callback, arguments to the function, or clearImmediate. | ||
|
||
"use strict"; | ||
|
||
var global = this; | ||
|
||
|
||
// IE 10, Node >= 9.1 | ||
|
||
function useSetImmediate() { | ||
if (! global.setImmediate) | ||
return null; | ||
else { | ||
var setImmediate = function (fn) { | ||
global.setImmediate(fn); | ||
}; | ||
setImmediate.implementation = 'setImmediate'; | ||
return setImmediate; | ||
} | ||
} | ||
|
||
|
||
// Android 2.3.6, Chrome 26, Firefox 20, IE 8-9, iOS 5.1.1 Safari | ||
|
||
function usePostMessage() { | ||
// The test against `importScripts` prevents this implementation | ||
// from being installed inside a web worker, where | ||
// `global.postMessage` means something completely different and | ||
// can't be used for this purpose. | ||
|
||
if (!global.postMessage || global.importScripts) { | ||
return null; | ||
} | ||
|
||
// Avoid synchronous post message implementations. | ||
|
||
var postMessageIsAsynchronous = true; | ||
var oldOnMessage = global.onmessage; | ||
global.onmessage = function () { | ||
postMessageIsAsynchronous = false; | ||
}; | ||
global.postMessage("", "*"); | ||
global.onmessage = oldOnMessage; | ||
|
||
if (! postMessageIsAsynchronous) | ||
return null; | ||
|
||
var funcIndex = 0; | ||
var funcs = {}; | ||
|
||
// Installs an event handler on `global` for the `message` event: see | ||
// * https://developer.mozilla.org/en/DOM/window.postMessage | ||
// * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages | ||
|
||
// XXX use Random.id() here? | ||
var MESSAGE_PREFIX = "Meteor._setImmediate." + Math.random() + '.'; | ||
|
||
function isStringAndStartsWith(string, putativeStart) { | ||
return (typeof string === "string" && | ||
string.substring(0, putativeStart.length) === putativeStart); | ||
} | ||
|
||
function onGlobalMessage(event) { | ||
// This will catch all incoming messages (even from other | ||
// windows!), so we need to try reasonably hard to avoid letting | ||
// anyone else trick us into firing off. We test the origin is | ||
// still this window, and that a (randomly generated) | ||
// unpredictable identifying prefix is present. | ||
if (event.source === global && | ||
isStringAndStartsWith(event.data, MESSAGE_PREFIX)) { | ||
var index = event.data.substring(MESSAGE_PREFIX.length); | ||
try { | ||
if (funcs[index]) | ||
funcs[index](); | ||
} | ||
finally { | ||
delete funcs[index]; | ||
} | ||
} | ||
} | ||
|
||
if (global.addEventListener) { | ||
global.addEventListener("message", onGlobalMessage, false); | ||
} else { | ||
global.attachEvent("onmessage", onGlobalMessage); | ||
} | ||
|
||
var setImmediate = function (fn) { | ||
// Make `global` post a message to itself with the handle and | ||
// identifying prefix, thus asynchronously invoking our | ||
// onGlobalMessage listener above. | ||
++funcIndex; | ||
funcs[funcIndex] = fn; | ||
global.postMessage(MESSAGE_PREFIX + funcIndex, "*"); | ||
}; | ||
setImmediate.implementation = 'postMessage'; | ||
return setImmediate; | ||
} | ||
|
||
|
||
function useTimeout() { | ||
var setImmediate = function (fn) { | ||
global.setTimeout(fn, 0); | ||
}; | ||
setImmediate.implementation = 'setTimeout'; | ||
return setImmediate; | ||
} | ||
|
||
|
||
Meteor._setImmediate = | ||
useSetImmediate() || | ||
usePostMessage() || | ||
useTimeout(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.