mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-i to m-c, a=merge
This commit is contained in:
commit
0c01dead3d
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Bugs 1101331 - See if a CLOBBER helps the situation any.
|
||||
bug 1114669 removes nsIStreamCipher.idl, which requires a clobber according to bug 1114669
|
||||
|
@ -102,7 +102,8 @@ var gSimpleTraversalRoles =
|
||||
Roles.LISTITEM,
|
||||
Roles.GRID_CELL,
|
||||
Roles.COLUMNHEADER,
|
||||
Roles.ROWHEADER];
|
||||
Roles.ROWHEADER,
|
||||
Roles.STATUSBAR];
|
||||
|
||||
var gSimpleMatchFunc = function gSimpleMatchFunc(aAccessible) {
|
||||
// An object is simple, if it either has a single child lineage,
|
||||
@ -151,6 +152,7 @@ var gSimpleMatchFunc = function gSimpleMatchFunc(aAccessible) {
|
||||
case Roles.HEADING:
|
||||
case Roles.COLUMNHEADER:
|
||||
case Roles.ROWHEADER:
|
||||
case Roles.STATUSBAR:
|
||||
if ((aAccessible.childCount > 0 || aAccessible.name) &&
|
||||
(isSingleLineage(aAccessible) || isFlatSubtree(aAccessible))) {
|
||||
return Filters.MATCH | Filters.IGNORE_SUBTREE;
|
||||
|
@ -141,5 +141,7 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="statusbar-1" role="status">Last sync:<span>2 days ago</span></div>
|
||||
<div aria-label="Last sync: 30min ago" id="statusbar-2" role="status"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -477,6 +477,17 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
|
||||
{"string": "stateHasPopup"}, {"string": "combobox"}]],
|
||||
expectedBraille: [[{"string": "comboboxAbbr"}, "Never", "Intervals"],
|
||||
["Intervals", "Never", {"string": "comboboxAbbr"}]]
|
||||
}, {
|
||||
accOrElmOrID: "statusbar-1",
|
||||
expectedUtterance: [["Last sync:", "2 days ago"],
|
||||
["Last sync:", "2 days ago"]],
|
||||
expectedBraille: [["Last sync:", "2 days ago"],
|
||||
["Last sync:", "2 days ago"]]
|
||||
}, {
|
||||
accOrElmOrID: "statusbar-2",
|
||||
expectedUtterance: [["Last sync: 30min ago"],
|
||||
["Last sync: 30min ago"]],
|
||||
expectedBraille: [["Last sync: 30min ago"], ["Last sync: 30min ago"]]
|
||||
}];
|
||||
|
||||
// Test all possible utterance order preference values.
|
||||
@ -632,6 +643,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
|
||||
<option value="30">Every 30 min</option>
|
||||
<option value="null" selected>Never</option>
|
||||
</select>
|
||||
<div id="statusbar-1" role="status">Last sync:<span>2 days ago</span></div>
|
||||
<div aria-label="Last sync: 30min ago" id="statusbar-2" role="status"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -122,7 +122,7 @@
|
||||
'1', 'Sunday', 'M', 'Week 1', '3', '4', '7', '2',
|
||||
'5 8', 'gridcell4', 'Just an innocuous separator',
|
||||
'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt',
|
||||
'Dirt', 'Messy Stuff']);
|
||||
'Dirt', 'Messy Stuff', 'statusbar-1', 'statusbar-2']);
|
||||
|
||||
gQueue.invoke();
|
||||
}
|
||||
|
@ -193,7 +193,8 @@ const WorkerSandbox = Class({
|
||||
// `addon` in document is equivalent to `self` in content script.
|
||||
if (requiresAddonGlobal(worker)) {
|
||||
Object.defineProperty(getUnsafeWindow(window), 'addon', {
|
||||
value: content.self
|
||||
value: content.self,
|
||||
configurable: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -238,7 +238,8 @@ const WorkerSandbox = EventEmitter.compose({
|
||||
if (worker._injectInDocument) {
|
||||
let win = window.wrappedJSObject ? window.wrappedJSObject : window;
|
||||
Object.defineProperty(win, "addon", {
|
||||
value: content.self
|
||||
value: content.self,
|
||||
configurable: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -216,6 +216,10 @@ toolbar[customizing] > .overflow-button {
|
||||
|
||||
#titlebar {
|
||||
-moz-binding: url("chrome://global/content/bindings/general.xml#windowdragbox");
|
||||
|
||||
%ifdef XP_MACOSX
|
||||
-moz-window-dragging: drag;
|
||||
%endif
|
||||
}
|
||||
|
||||
#titlebar-spacer {
|
||||
|
@ -2393,19 +2393,24 @@ DebuggerController.HitCounts = new HitCounts();
|
||||
*/
|
||||
Object.defineProperties(window, {
|
||||
"gTarget": {
|
||||
get: function() DebuggerController._target
|
||||
get: function() DebuggerController._target,
|
||||
configurable: true
|
||||
},
|
||||
"gHostType": {
|
||||
get: function() DebuggerView._hostType
|
||||
get: function() DebuggerView._hostType,
|
||||
configurable: true
|
||||
},
|
||||
"gClient": {
|
||||
get: function() DebuggerController.client
|
||||
get: function() DebuggerController.client,
|
||||
configurable: true
|
||||
},
|
||||
"gThreadClient": {
|
||||
get: function() DebuggerController.activeThread
|
||||
get: function() DebuggerController.activeThread,
|
||||
configurable: true
|
||||
},
|
||||
"gCallStackPageSize": {
|
||||
get: function() CALL_STACK_PAGE_SIZE
|
||||
get: function() CALL_STACK_PAGE_SIZE,
|
||||
configurable: true
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -815,7 +815,8 @@ NetMonitorController.NetworkEventsHandler = new NetworkEventsHandler();
|
||||
*/
|
||||
Object.defineProperties(window, {
|
||||
"gNetwork": {
|
||||
get: function() NetMonitorController.NetworkEventsHandler
|
||||
get: function() NetMonitorController.NetworkEventsHandler,
|
||||
configurable: true
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -75,10 +75,6 @@
|
||||
|
||||
/** Begin titlebar **/
|
||||
|
||||
#titlebar {
|
||||
-moz-window-dragging: drag;
|
||||
}
|
||||
|
||||
#titlebar-buttonbox > .titlebar-button {
|
||||
display: none;
|
||||
}
|
||||
|
@ -13187,7 +13187,7 @@ nsDocShell::OnLinkClick(nsIContent* aContent,
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "wrong thread");
|
||||
|
||||
if (!IsOKToLoadURI(aURI)) {
|
||||
if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -13243,7 +13243,7 @@ nsDocShell::OnLinkClickSync(nsIContent *aContent,
|
||||
*aRequest = nullptr;
|
||||
}
|
||||
|
||||
if (!IsOKToLoadURI(aURI)) {
|
||||
if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -113,6 +113,29 @@ var testFns = [
|
||||
e.target.location.replace('otherpage-location-replaced.html');
|
||||
return "stop";
|
||||
},
|
||||
function(e) {
|
||||
var link = e.target.createElement('a');
|
||||
link.href = "otherpage.html";
|
||||
e.target.body.appendChild(link);
|
||||
link.click();
|
||||
return "stop";
|
||||
},
|
||||
function(e) {
|
||||
var link = e.target.createElement('a');
|
||||
link.href = "otherpage.html";
|
||||
link.setAttribute("target", "_blank");
|
||||
e.target.body.appendChild(link);
|
||||
link.click();
|
||||
return "stop";
|
||||
},
|
||||
function(e) {
|
||||
var link = e.target.createElement('a');
|
||||
link.href = e.target.location.href;
|
||||
e.target.body.appendChild(link);
|
||||
link.setAttribute("target", "somearbitrarywindow");
|
||||
link.click();
|
||||
return "stop";
|
||||
},
|
||||
];
|
||||
|
||||
function runNextTest() {
|
||||
|
@ -787,6 +787,14 @@ nsOuterWindowProxy::defineProperty(JSContext* cx,
|
||||
return true;
|
||||
}
|
||||
|
||||
// For now, allow chrome code to define non-configurable properties
|
||||
// on windows, until we sort out what exactly the addon SDK is
|
||||
// doing. In the meantime, this still allows us to test web compat
|
||||
// behavior.
|
||||
if (false && desc.isPermanent() && !nsContentUtils::IsCallerChrome()) {
|
||||
return ThrowErrorMessage(cx, MSG_DEFINE_NON_CONFIGURABLE_PROP_ON_WINDOW);
|
||||
}
|
||||
|
||||
return js::Wrapper::defineProperty(cx, proxy, id, desc);
|
||||
}
|
||||
|
||||
|
@ -256,6 +256,7 @@ skip-if = buildapp == 'mulet'
|
||||
[test_bug999456.html]
|
||||
[test_bug1022229.html]
|
||||
[test_bug1043106.html]
|
||||
[test_bug1060938.html]
|
||||
[test_bug1064481.html]
|
||||
[test_clearTimeoutIntervalNoArg.html]
|
||||
[test_consoleEmptyStack.html]
|
||||
@ -767,3 +768,5 @@ support-files = file_bug1011748_redirect.sjs file_bug1011748_OK.sjs
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
|
||||
[test_bug1081686.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s
|
||||
[test_window_define_nonconfigurable.html]
|
||||
skip-if = true # bug 1107443 - code for newly-added test was disabled
|
||||
|
44
dom/base/test/test_bug1060938.html
Normal file
44
dom/base/test/test_bug1060938.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1060938
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title> Test for Bug 1060938 </title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"> </script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"> </script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1060938"> Mozilla Bug 1060938 </a>
|
||||
<p id="display"></p>
|
||||
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 1060938 **/
|
||||
// test: Element.removeAttributeNode()
|
||||
|
||||
parent = document.getElementsByTagName("p")[0];
|
||||
parent.setAttributeNS("www.test1.com", "ID", "Test1");
|
||||
parent.setAttributeNS("www.test2.com", "Class", "Test2");
|
||||
parent.setAttribute("id", "www.test3.com");
|
||||
parent.className = "www.test4.com";
|
||||
|
||||
allAttributes = parent.attributes;
|
||||
|
||||
function removeAttr(iter){
|
||||
var removed_attribute = allAttributes[0];
|
||||
is(removed_attribute, parent.removeAttributeNode(removed_attribute),
|
||||
"(" + iter + ")" + " Returned attribute and remove attribute should be same.");
|
||||
}
|
||||
|
||||
removeAttr(1);
|
||||
removeAttr(2);
|
||||
removeAttr(3);
|
||||
removeAttr(4);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
42
dom/base/test/test_window_define_nonconfigurable.html
Normal file
42
dom/base/test/test_window_define_nonconfigurable.html
Normal file
@ -0,0 +1,42 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1107443
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1107443</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 1107443 **/
|
||||
try {
|
||||
Object.defineProperty(window, "nosuchprop", { value: 5 });
|
||||
throw "didn't throw";
|
||||
} catch (e) {
|
||||
ise(e instanceof TypeError, true,
|
||||
"defineProperty(window) with a non-configurable property should " +
|
||||
"throw a TypeError, instead got: " + e);
|
||||
ise(Object.getOwnPropertyDescriptor(window, "nosuchprop"), undefined,
|
||||
'Window should not have property after an attempt to define it failed');
|
||||
}
|
||||
|
||||
Object.defineProperty(window, "nosuchprop", { value: 7, configurable: true });
|
||||
var desc = Object.getOwnPropertyDescriptor(window, "nosuchprop");
|
||||
ise(typeof(desc), "object", "Should have a property now");
|
||||
ise(desc.configurable, true, "Property should be configurable");
|
||||
ise(desc.writable, false, "Property should be readonly");
|
||||
ise(desc.value, 7, "Property should have the right value");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1107443">Mozilla Bug 1107443</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -409,6 +409,11 @@ DOMInterfaces = {
|
||||
},
|
||||
},
|
||||
|
||||
'ExtendableEvent': {
|
||||
'headerFile': 'mozilla/dom/ServiceWorkerEvents.h',
|
||||
'nativeType': 'mozilla::dom::workers::ExtendableEvent',
|
||||
},
|
||||
|
||||
'FileList': {
|
||||
'headerFile': 'mozilla/dom/File.h',
|
||||
},
|
||||
@ -605,11 +610,6 @@ DOMInterfaces = {
|
||||
'workers': True,
|
||||
}],
|
||||
|
||||
'InstallPhaseEvent': {
|
||||
'headerFile': 'ServiceWorkerEvents.h',
|
||||
'nativeType': 'mozilla::dom::workers::InstallPhaseEvent',
|
||||
},
|
||||
|
||||
'InstallEvent': {
|
||||
'headerFile': 'ServiceWorkerEvents.h',
|
||||
'nativeType': 'mozilla::dom::workers::InstallEvent',
|
||||
|
@ -63,3 +63,6 @@ MSG_DEF(MSG_MISSING_REQUIRED_DICTIONARY_MEMBER, 1, "Missing required {0}.")
|
||||
MSG_DEF(MSG_INVALID_REQUEST_METHOD, 1, "Invalid request method {0}.")
|
||||
MSG_DEF(MSG_REQUEST_BODY_CONSUMED_ERROR, 0, "Request body has already been consumed.")
|
||||
MSG_DEF(MSG_RESPONSE_INVALID_STATUSTEXT_ERROR, 0, "Response statusText may not contain newline or carriage return.")
|
||||
MSG_DEF(MSG_FETCH_FAILED, 0, "NetworkError when attempting to fetch resource.")
|
||||
MSG_DEF(MSG_NO_BODY_ALLOWED_FOR_GET_AND_HEAD, 0, "HEAD or GET Request cannot have a body.")
|
||||
MSG_DEF(MSG_DEFINE_NON_CONFIGURABLE_PROP_ON_WINDOW, 0, "Not allowed to define a non-configurable property on the WindowProxy object")
|
||||
|
@ -186,6 +186,18 @@ GetCurrentJSStack()
|
||||
return stack.forget();
|
||||
}
|
||||
|
||||
AutoForceSetExceptionOnContext::AutoForceSetExceptionOnContext(JSContext* aCx)
|
||||
: mCx(aCx)
|
||||
{
|
||||
mOldValue = JS::ContextOptionsRef(mCx).autoJSAPIOwnsErrorReporting();
|
||||
JS::ContextOptionsRef(mCx).setAutoJSAPIOwnsErrorReporting(true);
|
||||
}
|
||||
|
||||
AutoForceSetExceptionOnContext::~AutoForceSetExceptionOnContext()
|
||||
{
|
||||
JS::ContextOptionsRef(mCx).setAutoJSAPIOwnsErrorReporting(mOldValue);
|
||||
}
|
||||
|
||||
namespace exceptions {
|
||||
|
||||
class StackFrame : public nsIStackFrame
|
||||
|
@ -44,6 +44,19 @@ CreateException(JSContext* aCx, nsresult aRv, const char* aMessage = nullptr);
|
||||
already_AddRefed<nsIStackFrame>
|
||||
GetCurrentJSStack();
|
||||
|
||||
// Throwing a TypeError on an ErrorResult may result in SpiderMonkey using its
|
||||
// own error reporting mechanism instead of just setting the exception on the
|
||||
// context. This happens if no script is running. Bug 1107777 adds a flag that
|
||||
// forcibly turns this behaviour off. This is a stack helper to set the flag.
|
||||
class MOZ_STACK_CLASS AutoForceSetExceptionOnContext {
|
||||
private:
|
||||
JSContext* mCx;
|
||||
bool mOldValue;
|
||||
public:
|
||||
explicit AutoForceSetExceptionOnContext(JSContext* aCx);
|
||||
~AutoForceSetExceptionOnContext();
|
||||
};
|
||||
|
||||
// Internal stuff not intended to be widely used.
|
||||
namespace exceptions {
|
||||
|
||||
|
@ -66,6 +66,7 @@ ToJSValue(JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aValue)
|
||||
{
|
||||
MOZ_ASSERT(aArgument.Failed());
|
||||
AutoForceSetExceptionOnContext forceExn(aCx);
|
||||
DebugOnly<bool> throwResult = ThrowMethodFailedWithDetails(aCx, aArgument, "", "");
|
||||
MOZ_ASSERT(!throwResult);
|
||||
DebugOnly<bool> getPendingResult = JS_GetPendingException(aCx, aValue);
|
||||
|
@ -21,7 +21,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=936056
|
||||
}
|
||||
get = desc.get;
|
||||
ok(get, "Couldn't find document getter");
|
||||
Object.defineProperty(frames[0], "foo", { get: get });
|
||||
Object.defineProperty(frames[0], "foo", { get: get, configurable: true });
|
||||
|
||||
var barewordFunc = frames[0].eval("(function (count) { var doc; for (var i = 0; i < count; ++i) doc = foo; return doc.documentElement; })");
|
||||
var qualifiedFunc = frames[0].eval("(function (count) { var doc; for (var i = 0; i < count; ++i) doc = window.document; return doc.documentElement; })");
|
||||
|
@ -66,6 +66,27 @@ GetOrigin(nsIPrincipal* aPrincipal, nsAString& aOrigin, ErrorResult& aRv)
|
||||
}
|
||||
|
||||
aOrigin = tmp;
|
||||
if (aOrigin.EqualsASCII("null")) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
aRv = aPrincipal->GetURI(getter_AddRefs(uri));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!uri)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString spec;
|
||||
aRv = uri->GetSpec(spec);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
aOrigin = NS_ConvertUTF8toUTF16(spec);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -438,6 +459,9 @@ BroadcastChannel::Constructor(const GlobalObject& aGlobal,
|
||||
}
|
||||
|
||||
GetOrigin(principal, origin, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aRv = PrincipalToPrincipalInfo(principal, &principalInfo);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
|
@ -525,6 +525,7 @@ fail-if = (os == 'linux')
|
||||
[webgl-conformance/_wrappers/test_conformance__extensions__webgl-debug-shaders.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__extensions__webgl-compressed-texture-etc1.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__extensions__webgl-compressed-texture-s3tc.html]
|
||||
fail-if = (os == 'mac' && os_version == '10.10')
|
||||
[webgl-conformance/_wrappers/test_conformance__extensions__ext-sRGB.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__extensions__ext-shader-texture-lod.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__glsl__functions__glsl-function.html]
|
||||
@ -704,6 +705,7 @@ skip-if = (os == 'android') || (os == 'b2g') || (os == 'linux')
|
||||
[webgl-conformance/_wrappers/test_conformance__misc__null-object-behaviour.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__misc__functions-returning-strings.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__misc__object-deletion-behaviour.html]
|
||||
fail-if = (os == 'mac' && os_version == '10.10')
|
||||
[webgl-conformance/_wrappers/test_conformance__misc__shader-precision-format.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__misc__type-conversion-test.html]
|
||||
skip-if = (os == 'android') || (os == 'b2g') || (os == 'linux')
|
||||
@ -732,8 +734,10 @@ skip-if = os == 'android'
|
||||
[webgl-conformance/_wrappers/test_conformance__rendering__gl-scissor-test.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__rendering__more-than-65536-indices.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__rendering__point-size.html]
|
||||
fail-if = (os == 'mac' && os_version == '10.10')
|
||||
[webgl-conformance/_wrappers/test_conformance__rendering__triangle.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__rendering__line-loop-tri-fan.html]
|
||||
fail-if = (os == 'mac' && os_version == '10.10')
|
||||
[webgl-conformance/_wrappers/test_conformance__state__gl-enable-enum-test.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__state__gl-enum-tests.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__state__gl-get-calls.html]
|
||||
@ -742,6 +746,7 @@ skip-if = os == 'android'
|
||||
[webgl-conformance/_wrappers/test_conformance__state__gl-object-get-calls.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__textures__compressed-tex-image.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__textures__copy-tex-image-and-sub-image-2d.html]
|
||||
fail-if = (os == 'mac' && os_version == '10.10')
|
||||
[webgl-conformance/_wrappers/test_conformance__textures__gl-pixelstorei.html]
|
||||
[webgl-conformance/_wrappers/test_conformance__textures__gl-teximage.html]
|
||||
skip-if = (os == 'android') || (os == 'b2g') || (os == 'linux')
|
||||
|
@ -88,6 +88,21 @@ fail-if = (os == 'linux')
|
||||
[_wrappers/test_conformance__canvas__drawingbuffer-static-canvas-test.html]
|
||||
# Intermittent crash on OSX.
|
||||
skip-if = os == 'mac'
|
||||
[_wrappers/test_conformance__extensions__webgl-compressed-texture-s3tc.html]
|
||||
# Fails on OS X 10.10
|
||||
fail-if = (os == 'mac' && os_version == '10.10')
|
||||
[_wrappers/test_conformance__misc__object-deletion-behaviour.html]
|
||||
# Fails on OS X 10.10
|
||||
fail-if = (os == 'mac' && os_version == '10.10')
|
||||
[_wrappers/test_conformance__rendering__line-loop-tri-fan.html]
|
||||
# Fails on OS X 10.10
|
||||
fail-if = (os == 'mac' && os_version == '10.10')
|
||||
[_wrappers/test_conformance__rendering__point-size.html]
|
||||
# Fails on OS X 10.10
|
||||
fail-if = (os == 'mac' && os_version == '10.10')
|
||||
[_wrappers/test_conformance__textures__copy-tex-image-and-sub-image-2d.html]
|
||||
# Fails on OS X 10.10
|
||||
fail-if = (os == 'mac' && os_version == '10.10')
|
||||
|
||||
########################################################################
|
||||
# Win
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "mozilla/dom/Exceptions.h"
|
||||
#include "mozilla/dom/FetchDriver.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/Headers.h"
|
||||
@ -266,9 +267,15 @@ MainThreadFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
|
||||
NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
|
||||
mResponse = new Response(go, aResponse);
|
||||
mPromise->MaybeResolve(mResponse);
|
||||
if (aResponse->Type() != ResponseType::Error) {
|
||||
nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
|
||||
mResponse = new Response(go, aResponse);
|
||||
mPromise->MaybeResolve(mResponse);
|
||||
} else {
|
||||
ErrorResult result;
|
||||
result.ThrowTypeError(MSG_FETCH_FAILED);
|
||||
mPromise->MaybeReject(result);
|
||||
}
|
||||
}
|
||||
|
||||
MainThreadFetchResolver::~MainThreadFetchResolver()
|
||||
@ -296,11 +303,18 @@ public:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(aWorkerPrivate == mResolver->GetWorkerPrivate());
|
||||
|
||||
nsRefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope();
|
||||
mResolver->mResponse = new Response(global, mInternalResponse);
|
||||
|
||||
nsRefPtr<Promise> promise = mResolver->mFetchPromise.forget();
|
||||
promise->MaybeResolve(mResolver->mResponse);
|
||||
|
||||
if (mInternalResponse->Type() != ResponseType::Error) {
|
||||
nsRefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope();
|
||||
mResolver->mResponse = new Response(global, mInternalResponse);
|
||||
|
||||
promise->MaybeResolve(mResolver->mResponse);
|
||||
} else {
|
||||
ErrorResult result;
|
||||
result.ThrowTypeError(MSG_FETCH_FAILED);
|
||||
promise->MaybeReject(result);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -322,7 +336,6 @@ public:
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(aWorkerPrivate == mResolver->GetWorkerPrivate());
|
||||
MOZ_ASSERT(mResolver->mResponse);
|
||||
|
||||
mResolver->CleanUp(aCx);
|
||||
return true;
|
||||
@ -724,7 +737,7 @@ public:
|
||||
nsISupports* aCtxt,
|
||||
nsresult aStatus,
|
||||
uint32_t aResultLength,
|
||||
const uint8_t* aResult)
|
||||
const uint8_t* aResult) MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@ -1066,9 +1079,9 @@ FetchBody<Derived>::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength
|
||||
// Finish successfully consuming body according to type.
|
||||
MOZ_ASSERT(aResult);
|
||||
|
||||
AutoJSAPI api;
|
||||
api.Init(DerivedClass()->GetParentObject());
|
||||
JSContext* cx = api.cx();
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init(DerivedClass()->GetParentObject());
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
switch (mConsumeType) {
|
||||
case CONSUME_ARRAYBUFFER: {
|
||||
@ -1115,13 +1128,20 @@ FetchBody<Derived>::ContinueConsumeBody(nsresult aStatus, uint32_t aResultLength
|
||||
return;
|
||||
}
|
||||
|
||||
AutoForceSetExceptionOnContext forceExn(cx);
|
||||
JS::Rooted<JS::Value> json(cx);
|
||||
if (!JS_ParseJSON(cx, decoded.get(), decoded.Length(), &json)) {
|
||||
JS::Rooted<JS::Value> exn(cx);
|
||||
if (JS_GetPendingException(cx, &exn)) {
|
||||
JS_ClearPendingException(cx);
|
||||
localPromise->MaybeReject(cx, exn);
|
||||
if (!JS_IsExceptionPending(cx)) {
|
||||
localPromise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> exn(cx);
|
||||
DebugOnly<bool> gotException = JS_GetPendingException(cx, &exn);
|
||||
MOZ_ASSERT(gotException);
|
||||
|
||||
JS_ClearPendingException(cx);
|
||||
localPromise->MaybeReject(cx, exn);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -316,30 +316,6 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
||||
// FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
|
||||
// set based on the Request's flag.
|
||||
|
||||
// From here on we create a channel and set its properties with the
|
||||
// information from the InternalRequest. This is an implementation detail.
|
||||
MOZ_ASSERT(mLoadGroup);
|
||||
nsCOMPtr<nsIChannel> chan;
|
||||
rv = NS_NewChannel(getter_AddRefs(chan),
|
||||
uri,
|
||||
mPrincipal,
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
mRequest->GetContext(),
|
||||
mLoadGroup,
|
||||
nullptr, /* aCallbacks */
|
||||
nsIRequest::LOAD_NORMAL,
|
||||
ios);
|
||||
mLoadGroup = nullptr;
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Insert ourselves into the notification callbacks chain so we can handle
|
||||
// cross-origin redirects.
|
||||
chan->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
|
||||
chan->SetNotificationCallbacks(this);
|
||||
|
||||
// Step 3.1 "If the CORS preflight flag is set and one of these conditions is
|
||||
// true..." is handled by the CORS proxy.
|
||||
//
|
||||
@ -360,6 +336,36 @@ FetchDriver::HttpFetch(bool aCORSFlag, bool aCORSPreflightFlag, bool aAuthentica
|
||||
useCredentials = true;
|
||||
}
|
||||
|
||||
// This is effectivetly the opposite of the use credentials flag in "HTTP
|
||||
// network or cache fetch" in the spec and decides whether to transmit
|
||||
// cookies and other identifying information. LOAD_ANONYMOUS also prevents
|
||||
// new cookies sent by the server from being stored.
|
||||
const nsLoadFlags credentialsFlag = useCredentials ? 0 : nsIRequest::LOAD_ANONYMOUS;
|
||||
|
||||
// From here on we create a channel and set its properties with the
|
||||
// information from the InternalRequest. This is an implementation detail.
|
||||
MOZ_ASSERT(mLoadGroup);
|
||||
nsCOMPtr<nsIChannel> chan;
|
||||
rv = NS_NewChannel(getter_AddRefs(chan),
|
||||
uri,
|
||||
mPrincipal,
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
mRequest->GetContext(),
|
||||
mLoadGroup,
|
||||
nullptr, /* aCallbacks */
|
||||
nsIRequest::LOAD_NORMAL | credentialsFlag,
|
||||
ios);
|
||||
mLoadGroup = nullptr;
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailWithNetworkError();
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Insert ourselves into the notification callbacks chain so we can handle
|
||||
// cross-origin redirects.
|
||||
chan->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
|
||||
chan->SetNotificationCallbacks(this);
|
||||
|
||||
// FIXME(nsm): Bug 1120715.
|
||||
// Step 3.4 "If request's cache mode is default and request's header list
|
||||
// contains a header named `If-Modified-Since`, `If-None-Match`,
|
||||
|
@ -218,6 +218,15 @@ Request::Constructor(const GlobalObject& aGlobal,
|
||||
}
|
||||
|
||||
if (aInit.mBody.WasPassed()) {
|
||||
// HEAD and GET are not allowed to have a body.
|
||||
nsAutoCString method;
|
||||
request->GetMethod(method);
|
||||
// method is guaranteed to be uppercase due to step 14.2 above.
|
||||
if (method.EqualsLiteral("HEAD") || method.EqualsLiteral("GET")) {
|
||||
aRv.ThrowTypeError(MSG_NO_BODY_ALLOWED_FOR_GET_AND_HEAD);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const OwningArrayBufferOrArrayBufferViewOrBlobOrUSVStringOrURLSearchParams& bodyInit = aInit.mBody.Value();
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
nsCString contentType;
|
||||
|
2
dom/indexedDB/test/extensions/bootstrap.js
vendored
2
dom/indexedDB/test/extensions/bootstrap.js
vendored
@ -11,7 +11,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
function testForExpectedSymbols(stage, data) {
|
||||
const expectedSymbols = [ "IDBKeyRange", "indexedDB" ];
|
||||
for each (var symbol in expectedSymbols) {
|
||||
for (var symbol of expectedSymbols) {
|
||||
Services.prefs.setBoolPref("indexeddbtest.bootstrap." + stage + "." +
|
||||
symbol, symbol in this);
|
||||
}
|
||||
|
@ -47,7 +47,7 @@
|
||||
|
||||
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||
|
||||
for each (let data in objectStoreData) {
|
||||
for (let data of objectStoreData) {
|
||||
objectStore.add(data.blobs, data.key);
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
for each (let data in objectStoreData) {
|
||||
for (let data of objectStoreData) {
|
||||
objectStore = db.transaction([objectStoreName])
|
||||
.objectStore(objectStoreName);
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
|
||||
|
||||
let databases = [];
|
||||
for each (let info in databaseInfo) {
|
||||
for (let info of databaseInfo) {
|
||||
let request = indexedDB.open(info.name, 1);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
@ -47,7 +47,7 @@
|
||||
}
|
||||
|
||||
let refResult;
|
||||
for each (let db in databases) {
|
||||
for (let db of databases) {
|
||||
let request = db.transaction([objectStoreName])
|
||||
.objectStore(objectStoreName).get(fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
|
@ -34,7 +34,7 @@
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
for each (let info in objectStoreInfo) {
|
||||
for (let info of objectStoreInfo) {
|
||||
let objectStore = db.createObjectStore(info.name, info.options);
|
||||
objectStore.add(fileData.file, fileData.key);
|
||||
}
|
||||
@ -44,7 +44,7 @@
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let refResult;
|
||||
for each (let info in objectStoreInfo) {
|
||||
for (let info of objectStoreInfo) {
|
||||
let objectStore = db.transaction([info.name])
|
||||
.objectStore(info.name);
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
const testFile = getRandomFile("random.bin", 100000);
|
||||
|
||||
let databases = [];
|
||||
for each (let info in databaseInfo) {
|
||||
for (let info of databaseInfo) {
|
||||
let request = indexedDB.open(info.name, 1);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
|
@ -29,8 +29,8 @@
|
||||
addon.uninstall();
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
for each (var stage in [ "install", "startup", "shutdown", "uninstall" ]) {
|
||||
for each (var symbol in [ "IDBKeyRange", "indexedDB" ]) {
|
||||
for (var stage of [ "install", "startup", "shutdown", "uninstall" ]) {
|
||||
for (var symbol of [ "IDBKeyRange", "indexedDB" ]) {
|
||||
let pref;
|
||||
try {
|
||||
pref = Services.prefs.getBoolPref("indexeddbtest.bootstrap." + stage +
|
||||
|
@ -16,11 +16,11 @@ function testSteps()
|
||||
let db = event.target.result;
|
||||
let trans = event.target.transaction;
|
||||
|
||||
for each (let autoincrement in [true, false]) {
|
||||
for each (let keypath in [false, true, "missing", "invalid"]) {
|
||||
for each (let method in ["put", "add"]) {
|
||||
for each (let explicit in [true, false, undefined, "invalid"]) {
|
||||
for each (let existing in [true, false]) {
|
||||
for (let autoincrement of [true, false]) {
|
||||
for (let keypath of [false, true, "missing", "invalid"]) {
|
||||
for (let method of ["put", "add"]) {
|
||||
for (let explicit of [true, false, undefined, "invalid"]) {
|
||||
for (let existing of [true, false]) {
|
||||
let speccedNoKey = (keypath == false || keypath == "missing") &&
|
||||
!explicit;
|
||||
|
||||
|
@ -44,7 +44,7 @@ function testSteps()
|
||||
objectStore.createIndex(indexData.name, indexData.keyPath,
|
||||
indexData.options);
|
||||
|
||||
for each (let data in objectStoreData) {
|
||||
for (let data of objectStoreData) {
|
||||
objectStore.add(data.value, data.key);
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ function testSteps()
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
for each (let autoIncrement in [false, true]) {
|
||||
for (let autoIncrement of [false, true]) {
|
||||
let objectStore =
|
||||
db.createObjectStore(autoIncrement, { keyPath: "id",
|
||||
autoIncrement: autoIncrement });
|
||||
@ -27,7 +27,7 @@ function testSteps()
|
||||
objectStore.add({ id: i, index: i });
|
||||
}
|
||||
|
||||
for each (let unique in [false, true]) {
|
||||
for (let unique of [false, true]) {
|
||||
objectStore.createIndex(unique, "index", { unique: unique });
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ function testSteps()
|
||||
event = yield undefined;
|
||||
is(event.type, "success", "expect a success event");
|
||||
|
||||
for each (let autoIncrement in [false, true]) {
|
||||
for (let autoIncrement of [false, true]) {
|
||||
let objectStore = db.transaction(autoIncrement)
|
||||
.objectStore(autoIncrement);
|
||||
|
||||
@ -51,7 +51,7 @@ function testSteps()
|
||||
let objectStoreCount = event.target.result;
|
||||
let indexCount = event.target.result;
|
||||
|
||||
for each (let unique in [false, true]) {
|
||||
for (let unique of [false, true]) {
|
||||
let index = db.transaction(autoIncrement, "readwrite")
|
||||
.objectStore(autoIncrement)
|
||||
.index(unique);
|
||||
|
@ -340,7 +340,7 @@ function testSteps()
|
||||
let objectStore = db.createObjectStore(objectStoreName, objectStoreOptions);
|
||||
let index = objectStore.createIndex(indexName, indexName, indexOptions);
|
||||
|
||||
for each (let data in dbData) {
|
||||
for (let data of dbData) {
|
||||
objectStore.add(data);
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ function testSteps()
|
||||
|
||||
const version = 1;
|
||||
|
||||
for each (let name in names) {
|
||||
for (let name of names) {
|
||||
let request = indexedDB.open(name, version);
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
|
@ -37,7 +37,7 @@ function testSteps()
|
||||
let objectStore = db.createObjectStore(osName, { keyPath: "ssn" });
|
||||
objectStore.createIndex(indexName, "weight", { unique: false });
|
||||
|
||||
for each (let i in data) {
|
||||
for (let i of data) {
|
||||
objectStore.add(i);
|
||||
}
|
||||
|
||||
@ -227,7 +227,7 @@ function testSteps()
|
||||
|
||||
event.target.transaction.oncomplete = grabEventAndContinueHandler;
|
||||
|
||||
for each (let i in data) {
|
||||
for (let i of data) {
|
||||
objectStore.add(i);
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ function testSteps()
|
||||
|
||||
let db = event.target.result;
|
||||
|
||||
for each (let autoIncrement in [false, true]) {
|
||||
for (let autoIncrement of [false, true]) {
|
||||
let objectStore =
|
||||
db.createObjectStore(autoIncrement, { keyPath: "id",
|
||||
autoIncrement: autoIncrement });
|
||||
@ -30,7 +30,7 @@ function testSteps()
|
||||
event = yield undefined;
|
||||
is(event.type, "success", "expect a success event");
|
||||
|
||||
for each (let autoIncrement in [false, true]) {
|
||||
for (let autoIncrement of [false, true]) {
|
||||
objectStore = db.transaction(autoIncrement, "readwrite")
|
||||
.objectStore(autoIncrement);
|
||||
|
||||
|
@ -17,7 +17,7 @@ interface nsIServiceWorkerUnregisterCallback : nsISupports
|
||||
[noscript] void UnregisterFailed();
|
||||
};
|
||||
|
||||
[builtinclass, uuid(78fdfe7c-0e2c-4157-ac6e-3952b61df36a)]
|
||||
[builtinclass, uuid(430f02bf-63d0-44ab-9a59-7bd49c608949)]
|
||||
interface nsIServiceWorkerManager : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -50,8 +50,8 @@ interface nsIServiceWorkerManager : nsISupports
|
||||
void removeReadyPromise(in nsIDOMWindow aWindow);
|
||||
|
||||
// aTarget MUST be a ServiceWorkerRegistration.
|
||||
[noscript] void AddRegistrationEventListener(in nsIURI aPageURI, in nsIDOMEventTarget aTarget);
|
||||
[noscript] void RemoveRegistrationEventListener(in nsIURI aPageURI, in nsIDOMEventTarget aTarget);
|
||||
[noscript] void AddRegistrationEventListener(in DOMString aScope, in nsIDOMEventTarget aTarget);
|
||||
[noscript] void RemoveRegistrationEventListener(in DOMString aScope, in nsIDOMEventTarget aTarget);
|
||||
|
||||
/**
|
||||
* Call this to request that document `aDoc` be controlled by a ServiceWorker
|
||||
|
@ -115,6 +115,12 @@ parent:
|
||||
*/
|
||||
sync PPluginWidget();
|
||||
|
||||
/**
|
||||
* Return native data of root widget
|
||||
*/
|
||||
sync GetWidgetNativeData() returns (WindowsHandle value);
|
||||
|
||||
parent:
|
||||
/**
|
||||
* When child sends this message, parent should move focus to
|
||||
* the next or previous focusable element.
|
||||
@ -302,11 +308,6 @@ parent:
|
||||
*/
|
||||
sync GetDefaultScale() returns (double value);
|
||||
|
||||
/**
|
||||
* Return native data of root widget
|
||||
*/
|
||||
sync GetWidgetNativeData() returns (WindowsHandle value);
|
||||
|
||||
/**
|
||||
* Set the native cursor.
|
||||
* @param value
|
||||
|
@ -149,7 +149,7 @@ public:
|
||||
NS_IMETHOD TerminatePlugin() MOZ_OVERRIDE;
|
||||
NS_IMETHOD TerminateProcess() MOZ_OVERRIDE;
|
||||
|
||||
NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult);
|
||||
NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) MOZ_OVERRIDE;
|
||||
|
||||
void Clear() { mContentParent = nullptr; mActor = nullptr; }
|
||||
|
||||
|
@ -91,9 +91,9 @@ public:
|
||||
// Return true if the transport layer supports seeking.
|
||||
virtual bool IsMediaSeekable() = 0;
|
||||
|
||||
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDromant) = 0;
|
||||
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDormant) = 0;
|
||||
virtual void QueueMetadata(int64_t aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) = 0;
|
||||
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDromant) = 0;
|
||||
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDormant) = 0;
|
||||
|
||||
virtual void RemoveMediaTracks() = 0;
|
||||
|
||||
@ -165,17 +165,17 @@ protected:
|
||||
MetadataContainer(AbstractMediaDecoder* aDecoder,
|
||||
nsAutoPtr<MediaInfo> aInfo,
|
||||
nsAutoPtr<MetadataTags> aTags,
|
||||
bool aRestoredFromDromant)
|
||||
bool aRestoredFromDormant)
|
||||
: mDecoder(aDecoder),
|
||||
mInfo(aInfo),
|
||||
mTags(aTags),
|
||||
mRestoredFromDromant(aRestoredFromDromant)
|
||||
mRestoredFromDormant(aRestoredFromDormant)
|
||||
{}
|
||||
|
||||
nsRefPtr<AbstractMediaDecoder> mDecoder;
|
||||
nsAutoPtr<MediaInfo> mInfo;
|
||||
nsAutoPtr<MetadataTags> mTags;
|
||||
bool mRestoredFromDromant;
|
||||
bool mRestoredFromDormant;
|
||||
};
|
||||
|
||||
class MetadataEventRunner : public nsRunnable, private MetadataContainer
|
||||
@ -184,13 +184,13 @@ public:
|
||||
MetadataEventRunner(AbstractMediaDecoder* aDecoder,
|
||||
nsAutoPtr<MediaInfo> aInfo,
|
||||
nsAutoPtr<MetadataTags> aTags,
|
||||
bool aRestoredFromDromant = false)
|
||||
: MetadataContainer(aDecoder, aInfo, aTags, aRestoredFromDromant)
|
||||
bool aRestoredFromDormant = false)
|
||||
: MetadataContainer(aDecoder, aInfo, aTags, aRestoredFromDormant)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||
{
|
||||
mDecoder->MetadataLoaded(mInfo, mTags, mRestoredFromDromant);
|
||||
mDecoder->MetadataLoaded(mInfo, mTags, mRestoredFromDormant);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
@ -200,13 +200,13 @@ class FirstFrameLoadedEventRunner : public nsRunnable, private MetadataContainer
|
||||
public:
|
||||
FirstFrameLoadedEventRunner(AbstractMediaDecoder* aDecoder,
|
||||
nsAutoPtr<MediaInfo> aInfo,
|
||||
bool aRestoredFromDromant = false)
|
||||
: MetadataContainer(aDecoder, aInfo, nsAutoPtr<MetadataTags>(nullptr), aRestoredFromDromant)
|
||||
bool aRestoredFromDormant = false)
|
||||
: MetadataContainer(aDecoder, aInfo, nsAutoPtr<MetadataTags>(nullptr), aRestoredFromDormant)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||
{
|
||||
mDecoder->FirstFrameLoaded(mInfo, mRestoredFromDromant);
|
||||
mDecoder->FirstFrameLoaded(mInfo, mRestoredFromDormant);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
@ -217,16 +217,16 @@ public:
|
||||
MetadataUpdatedEventRunner(AbstractMediaDecoder* aDecoder,
|
||||
nsAutoPtr<MediaInfo> aInfo,
|
||||
nsAutoPtr<MetadataTags> aTags,
|
||||
bool aRestoredFromDromant = false)
|
||||
: MetadataContainer(aDecoder, aInfo, aTags, aRestoredFromDromant)
|
||||
bool aRestoredFromDormant = false)
|
||||
: MetadataContainer(aDecoder, aInfo, aTags, aRestoredFromDormant)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE
|
||||
{
|
||||
nsAutoPtr<MediaInfo> info(new MediaInfo());
|
||||
*info = *mInfo;
|
||||
mDecoder->MetadataLoaded(info, mTags, mRestoredFromDromant);
|
||||
mDecoder->FirstFrameLoaded(mInfo, mRestoredFromDromant);
|
||||
mDecoder->MetadataLoaded(info, mTags, mRestoredFromDormant);
|
||||
mDecoder->FirstFrameLoaded(mInfo, mRestoredFromDormant);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
@ -59,7 +59,8 @@ IdpChannel.prototype = {
|
||||
aSandbox._frame.contentWindow.wrappedJSObject,
|
||||
"rtcwebIdentityPort",
|
||||
{
|
||||
value: this.messagechannel.port2
|
||||
value: this.messagechannel.port2,
|
||||
configurable: true
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
|
@ -717,7 +717,7 @@ MediaDecoder::IsExpectingMoreData()
|
||||
|
||||
void MediaDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
nsAutoPtr<MetadataTags> aTags,
|
||||
bool aRestoredFromDromant)
|
||||
bool aRestoredFromDormant)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@ -747,14 +747,14 @@ void MediaDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
// Make sure the element and the frame (if any) are told about
|
||||
// our new size.
|
||||
Invalidate();
|
||||
if (!aRestoredFromDromant) {
|
||||
if (!aRestoredFromDormant) {
|
||||
mOwner->MetadataLoaded(mInfo, nsAutoPtr<const MetadataTags>(aTags.forget()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MediaDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
bool aRestoredFromDromant)
|
||||
bool aRestoredFromDormant)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@ -770,7 +770,7 @@ void MediaDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
|
||||
if (mOwner) {
|
||||
Invalidate();
|
||||
if (!aRestoredFromDromant) {
|
||||
if (!aRestoredFromDormant) {
|
||||
mOwner->FirstFrameLoaded();
|
||||
}
|
||||
}
|
||||
|
@ -777,12 +777,12 @@ public:
|
||||
// state machine. Call on the main thread only.
|
||||
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
nsAutoPtr<MetadataTags> aTags,
|
||||
bool aRestoredFromDromant) MOZ_OVERRIDE;
|
||||
bool aRestoredFromDormant) MOZ_OVERRIDE;
|
||||
|
||||
// Called when the first audio and/or video from the media file has been loaded
|
||||
// by the state machine. Call on the main thread only.
|
||||
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
bool aRestoredFromDromant) MOZ_OVERRIDE;
|
||||
bool aRestoredFromDormant) MOZ_OVERRIDE;
|
||||
|
||||
// Called from MetadataLoaded(). Creates audio tracks and adds them to its
|
||||
// owner's audio track list, and implies to video tracks respectively.
|
||||
|
@ -354,6 +354,8 @@ void MediaDecoderStateMachine::SendStreamAudio(AudioData* aAudio,
|
||||
if (offset >= aAudio->mFrames)
|
||||
return;
|
||||
|
||||
size_t framesToWrite = aAudio->mFrames - offset;
|
||||
|
||||
aAudio->EnsureAudioBuffer();
|
||||
nsRefPtr<SharedBuffer> buffer = aAudio->mAudioBuffer;
|
||||
AudioDataValue* bufferData = static_cast<AudioDataValue*>(buffer->Data());
|
||||
@ -361,10 +363,11 @@ void MediaDecoderStateMachine::SendStreamAudio(AudioData* aAudio,
|
||||
for (uint32_t i = 0; i < aAudio->mChannels; ++i) {
|
||||
channels.AppendElement(bufferData + i*aAudio->mFrames + offset);
|
||||
}
|
||||
aOutput->AppendFrames(buffer.forget(), channels, aAudio->mFrames);
|
||||
VERBOSE_LOG("writing %d frames of data to MediaStream for AudioData at %lld",
|
||||
aAudio->mFrames - int32_t(offset), aAudio->mTime);
|
||||
aStream->mAudioFramesWritten += aAudio->mFrames - int32_t(offset);
|
||||
aOutput->AppendFrames(buffer.forget(), channels, framesToWrite);
|
||||
VERBOSE_LOG("writing %u frames of data to MediaStream for AudioData at %lld",
|
||||
static_cast<unsigned>(framesToWrite),
|
||||
aAudio->mTime);
|
||||
aStream->mAudioFramesWritten += framesToWrite;
|
||||
aOutput->ApplyVolume(mVolume);
|
||||
}
|
||||
|
||||
|
@ -486,7 +486,8 @@ MediaSource::DurationChange(double aOldDuration, double aNewDuration)
|
||||
MSE_DEBUG("MediaSource(%p)::DurationChange(aOldDuration=%f, aNewDuration=%f)", this, aOldDuration, aNewDuration);
|
||||
|
||||
if (aNewDuration < aOldDuration) {
|
||||
mSourceBuffers->RangeRemoval(aNewDuration, aOldDuration);
|
||||
// Remove all buffered data from aNewDuration.
|
||||
mSourceBuffers->RangeRemoval(aNewDuration, PositiveInfinity<double>());
|
||||
}
|
||||
// TODO: If partial audio frames/text cues exist, clamp duration based on mSourceBuffers.
|
||||
}
|
||||
|
@ -240,6 +240,9 @@ MediaSourceDecoder::DoSetMediaSourceDuration(double aDuration)
|
||||
mDecoderStateMachine->SetDuration(INT64_MAX);
|
||||
mMediaSourceDuration = PositiveInfinity<double>();
|
||||
}
|
||||
if (mReader) {
|
||||
mReader->SetMediaSourceDuration(mMediaSourceDuration);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -56,6 +56,7 @@ MediaSourceReader::MediaSourceReader(MediaSourceDecoder* aDecoder)
|
||||
, mDropAudioBeforeThreshold(false)
|
||||
, mDropVideoBeforeThreshold(false)
|
||||
, mEnded(false)
|
||||
, mMediaSourceDuration(0)
|
||||
, mHasEssentialTrackBuffers(false)
|
||||
#ifdef MOZ_FMP4
|
||||
, mSharedDecoderManager(new SharedDecoderManager())
|
||||
@ -123,13 +124,23 @@ MediaSourceReader::RequestAudioData()
|
||||
mAudioPromise.Reject(CANCELED, __func__);
|
||||
return p;
|
||||
}
|
||||
if (SwitchAudioReader(mLastAudioTime)) {
|
||||
mAudioReader->Seek(mLastAudioTime, 0)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::RequestAudioDataComplete,
|
||||
&MediaSourceReader::RequestAudioDataFailed);
|
||||
} else {
|
||||
RequestAudioDataComplete(0);
|
||||
SwitchReaderResult ret = SwitchAudioReader(mLastAudioTime);
|
||||
switch (ret) {
|
||||
case READER_NEW:
|
||||
mAudioReader->Seek(mLastAudioTime, 0)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::RequestAudioDataComplete,
|
||||
&MediaSourceReader::RequestAudioDataFailed);
|
||||
break;
|
||||
case READER_ERROR:
|
||||
if (mLastAudioTime) {
|
||||
CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime);
|
||||
break;
|
||||
}
|
||||
// Fallback to using current reader
|
||||
default:
|
||||
RequestAudioDataComplete(0);
|
||||
break;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@ -220,7 +231,7 @@ MediaSourceReader::OnAudioNotDecoded(NotDecodedReason aReason)
|
||||
// See if we can find a different reader that can pick up where we left off. We use the
|
||||
// EOS_FUZZ_US to allow for the fact that our end time can be inaccurate due to bug
|
||||
// 1065207.
|
||||
if (SwitchAudioReader(mLastAudioTime, EOS_FUZZ_US)) {
|
||||
if (SwitchAudioReader(mLastAudioTime, EOS_FUZZ_US) == READER_NEW) {
|
||||
mAudioReader->Seek(mLastAudioTime, 0)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::RequestAudioDataComplete,
|
||||
@ -228,15 +239,7 @@ MediaSourceReader::OnAudioNotDecoded(NotDecodedReason aReason)
|
||||
return;
|
||||
}
|
||||
|
||||
// If the entire MediaSource is done, generate an EndOfStream.
|
||||
if (IsEnded()) {
|
||||
mAudioPromise.Reject(END_OF_STREAM, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't have the data the caller wants. Tell that we're waiting for JS to
|
||||
// give us more data.
|
||||
mAudioPromise.Reject(WAITING_FOR_DATA, __func__);
|
||||
CheckForWaitOrEndOfStream(MediaData::AUDIO_DATA, mLastAudioTime);
|
||||
}
|
||||
|
||||
|
||||
@ -261,15 +264,25 @@ MediaSourceReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThres
|
||||
mVideoPromise.Reject(CANCELED, __func__);
|
||||
return p;
|
||||
}
|
||||
if (SwitchVideoReader(mLastVideoTime)) {
|
||||
mVideoReader->Seek(mLastVideoTime, 0)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::RequestVideoDataComplete,
|
||||
&MediaSourceReader::RequestVideoDataFailed);
|
||||
} else {
|
||||
mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded);
|
||||
SwitchReaderResult ret = SwitchVideoReader(mLastVideoTime);
|
||||
switch (ret) {
|
||||
case READER_NEW:
|
||||
mVideoReader->Seek(mLastVideoTime, 0)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::RequestVideoDataComplete,
|
||||
&MediaSourceReader::RequestVideoDataFailed);
|
||||
break;
|
||||
case READER_ERROR:
|
||||
if (mLastVideoTime) {
|
||||
CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime);
|
||||
break;
|
||||
}
|
||||
// Fallback to using current reader.
|
||||
default:
|
||||
mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded);
|
||||
break;
|
||||
}
|
||||
|
||||
return p;
|
||||
@ -335,7 +348,7 @@ MediaSourceReader::OnVideoNotDecoded(NotDecodedReason aReason)
|
||||
// See if we can find a different reader that can pick up where we left off. We use the
|
||||
// EOS_FUZZ_US to allow for the fact that our end time can be inaccurate due to bug
|
||||
// 1065207.
|
||||
if (SwitchVideoReader(mLastVideoTime, EOS_FUZZ_US)) {
|
||||
if (SwitchVideoReader(mLastVideoTime, EOS_FUZZ_US) == READER_NEW) {
|
||||
mVideoReader->Seek(mLastVideoTime, 0)
|
||||
->Then(GetTaskQueue(), __func__, this,
|
||||
&MediaSourceReader::RequestVideoDataComplete,
|
||||
@ -343,15 +356,29 @@ MediaSourceReader::OnVideoNotDecoded(NotDecodedReason aReason)
|
||||
return;
|
||||
}
|
||||
|
||||
CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime);
|
||||
}
|
||||
|
||||
void
|
||||
MediaSourceReader::CheckForWaitOrEndOfStream(MediaData::Type aType, int64_t aTime)
|
||||
{
|
||||
// If the entire MediaSource is done, generate an EndOfStream.
|
||||
if (IsEnded()) {
|
||||
mVideoPromise.Reject(END_OF_STREAM, __func__);
|
||||
if (IsNearEnd(aTime)) {
|
||||
if (aType == MediaData::AUDIO_DATA) {
|
||||
mAudioPromise.Reject(END_OF_STREAM, __func__);
|
||||
} else {
|
||||
mVideoPromise.Reject(END_OF_STREAM, __func__);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't have the data the caller wants. Tell that we're waiting for JS to
|
||||
// give us more data.
|
||||
mVideoPromise.Reject(WAITING_FOR_DATA, __func__);
|
||||
if (aType == MediaData::AUDIO_DATA) {
|
||||
// We don't have the data the caller wants. Tell that we're waiting for JS to
|
||||
// give us more data.
|
||||
mAudioPromise.Reject(WAITING_FOR_DATA, __func__);
|
||||
} else {
|
||||
mVideoPromise.Reject(WAITING_FOR_DATA, __func__);
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<ShutdownPromise>
|
||||
@ -447,40 +474,40 @@ MediaSourceReader::HaveData(int64_t aTarget, MediaData::Type aType)
|
||||
return !!reader;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaSourceReader::SwitchReaderResult
|
||||
MediaSourceReader::SwitchAudioReader(int64_t aTarget, int64_t aError)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
// XXX: Can't handle adding an audio track after ReadMetadata.
|
||||
if (!mAudioTrack) {
|
||||
return false;
|
||||
return READER_ERROR;
|
||||
}
|
||||
nsRefPtr<MediaDecoderReader> newReader = SelectReader(aTarget, aError, mAudioTrack->Decoders());
|
||||
if (newReader && newReader != mAudioReader) {
|
||||
mAudioReader->SetIdle();
|
||||
mAudioReader = newReader;
|
||||
MSE_DEBUGV("MediaSourceReader(%p)::SwitchAudioReader switched reader to %p", this, mAudioReader.get());
|
||||
return true;
|
||||
return READER_NEW;
|
||||
}
|
||||
return false;
|
||||
return newReader ? READER_EXISTING : READER_ERROR;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaSourceReader::SwitchReaderResult
|
||||
MediaSourceReader::SwitchVideoReader(int64_t aTarget, int64_t aError)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
// XXX: Can't handle adding a video track after ReadMetadata.
|
||||
if (!mVideoTrack) {
|
||||
return false;
|
||||
return READER_ERROR;
|
||||
}
|
||||
nsRefPtr<MediaDecoderReader> newReader = SelectReader(aTarget, aError, mVideoTrack->Decoders());
|
||||
if (newReader && newReader != mVideoReader) {
|
||||
mVideoReader->SetIdle();
|
||||
mVideoReader = newReader;
|
||||
MSE_DEBUGV("MediaSourceReader(%p)::SwitchVideoReader switched reader to %p", this, mVideoReader.get());
|
||||
return true;
|
||||
return READER_NEW;
|
||||
}
|
||||
return false;
|
||||
return newReader ? READER_EXISTING : READER_ERROR;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -920,6 +947,20 @@ MediaSourceReader::IsEnded()
|
||||
return mEnded;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaSourceReader::IsNearEnd(int64_t aTime)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
return mEnded && aTime >= (mMediaSourceDuration * USECS_PER_S - EOS_FUZZ_US);
|
||||
}
|
||||
|
||||
void
|
||||
MediaSourceReader::SetMediaSourceDuration(double aDuration)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mMediaSourceDuration = aDuration;
|
||||
}
|
||||
|
||||
#ifdef MOZ_EME
|
||||
nsresult
|
||||
MediaSourceReader::SetCDMProxy(CDMProxy* aProxy)
|
||||
|
@ -129,6 +129,10 @@ public:
|
||||
|
||||
// Return true if the Ended method has been called
|
||||
bool IsEnded();
|
||||
bool IsNearEnd(int64_t aTime /* microseconds */);
|
||||
|
||||
// Set the duration of the attached mediasource element.
|
||||
void SetMediaSourceDuration(double aDuration /* seconds */);
|
||||
|
||||
#ifdef MOZ_EME
|
||||
nsresult SetCDMProxy(CDMProxy* aProxy);
|
||||
@ -146,12 +150,20 @@ private:
|
||||
// Switch the current audio/video reader to the reader that
|
||||
// contains aTarget (or up to aError after target). Both
|
||||
// aTarget and aError are in microseconds.
|
||||
bool SwitchAudioReader(int64_t aTarget, int64_t aError = 0);
|
||||
bool SwitchVideoReader(int64_t aTarget, int64_t aError = 0);
|
||||
enum SwitchReaderResult {
|
||||
READER_ERROR = -1,
|
||||
READER_EXISTING = 0,
|
||||
READER_NEW = 1,
|
||||
};
|
||||
SwitchReaderResult SwitchAudioReader(int64_t aTarget, int64_t aError = 0);
|
||||
SwitchReaderResult SwitchVideoReader(int64_t aTarget, int64_t aError = 0);
|
||||
void RequestAudioDataComplete(int64_t aTime);
|
||||
void RequestAudioDataFailed(nsresult aResult);
|
||||
void RequestVideoDataComplete(int64_t aTime);
|
||||
void RequestVideoDataFailed(nsresult aResult);
|
||||
// Will reject the MediaPromise with END_OF_STREAM if mediasource has ended
|
||||
// or with WAIT_FOR_DATA otherwise.
|
||||
void CheckForWaitOrEndOfStream(MediaData::Type aType, int64_t aTime /* microseconds */);
|
||||
|
||||
// Return a reader from the set available in aTrackDecoders that has data
|
||||
// available in the range requested by aTarget.
|
||||
@ -202,6 +214,7 @@ private:
|
||||
bool mDropVideoBeforeThreshold;
|
||||
|
||||
bool mEnded;
|
||||
double mMediaSourceDuration;
|
||||
|
||||
bool mHasEssentialTrackBuffers;
|
||||
|
||||
|
@ -36,10 +36,37 @@ extern PRLogModuleInfo* GetMediaSourceAPILog();
|
||||
#define MSE_API(...)
|
||||
#endif
|
||||
|
||||
// RangeRemoval must be synchronous if appendBuffer is also synchronous.
|
||||
// While waiting for bug 1118589 to land, ensure RangeRemoval is synchronous
|
||||
#define APPENDBUFFER_IS_SYNCHRONOUS
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
||||
class RangeRemovalRunnable : public nsRunnable {
|
||||
public:
|
||||
RangeRemovalRunnable(SourceBuffer* aSourceBuffer,
|
||||
double aStart,
|
||||
double aEnd)
|
||||
: mSourceBuffer(aSourceBuffer)
|
||||
, mStart(aStart)
|
||||
, mEnd(aEnd)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
|
||||
|
||||
mSourceBuffer->DoRangeRemoval(mStart, mEnd);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<SourceBuffer> mSourceBuffer;
|
||||
double mStart;
|
||||
double mEnd;
|
||||
};
|
||||
|
||||
void
|
||||
SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
|
||||
{
|
||||
@ -165,10 +192,7 @@ SourceBuffer::Abort(ErrorResult& aRv)
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
if (mUpdating) {
|
||||
// TODO: Abort segment parser loop, buffer append, and stream append loop algorithms.
|
||||
AbortUpdating();
|
||||
}
|
||||
Abort();
|
||||
mTrackBuffer->ResetParserState();
|
||||
mAppendWindowStart = 0;
|
||||
mAppendWindowEnd = PositiveInfinity<double>();
|
||||
@ -177,6 +201,15 @@ SourceBuffer::Abort(ErrorResult& aRv)
|
||||
mTrackBuffer->DiscardDecoder();
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::Abort()
|
||||
{
|
||||
if (mUpdating) {
|
||||
// TODO: Abort segment parser loop, buffer append, and stream append loop algorithms.
|
||||
AbortUpdating();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
|
||||
{
|
||||
@ -206,13 +239,37 @@ void
|
||||
SourceBuffer::RangeRemoval(double aStart, double aEnd)
|
||||
{
|
||||
StartUpdating();
|
||||
/// TODO: Run coded frame removal algorithm.
|
||||
|
||||
// Run the final step of the coded frame removal algorithm asynchronously
|
||||
// to ensure the SourceBuffer's updating flag transition behaves as
|
||||
// required by the spec.
|
||||
#if defined(APPENDBUFFER_IS_SYNCHRONOUS)
|
||||
DoRangeRemoval(aStart, aEnd);
|
||||
|
||||
// Run the final step of the buffer append algorithm asynchronously to
|
||||
// ensure the SourceBuffer's updating flag transition behaves as required
|
||||
// by the spec.
|
||||
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating);
|
||||
NS_DispatchToMainThread(event);
|
||||
#else
|
||||
nsRefPtr<nsIRunnable> task = new RangeRemovalRunnable(this, aStart, aEnd);
|
||||
NS_DispatchToMainThread(task);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::DoRangeRemoval(double aStart, double aEnd)
|
||||
{
|
||||
if (!mUpdating) {
|
||||
// abort was called in between.
|
||||
return;
|
||||
}
|
||||
if (mTrackBuffer && !IsInfinite(aStart)) {
|
||||
int64_t start = aStart * USECS_PER_S;
|
||||
int64_t end = IsInfinite(aEnd) ? INT64_MAX : (int64_t)(aEnd * USECS_PER_S);
|
||||
mTrackBuffer->RangeRemoval(start, end);
|
||||
}
|
||||
|
||||
#if !defined(APPENDBUFFER_IS_SYNCHRONOUS)
|
||||
StopUpdating();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -220,6 +277,7 @@ SourceBuffer::Detach()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("SourceBuffer(%p)::Detach", this);
|
||||
Abort();
|
||||
if (mTrackBuffer) {
|
||||
mTrackBuffer->Detach();
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ public:
|
||||
void AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv);
|
||||
|
||||
void Abort(ErrorResult& aRv);
|
||||
void Abort();
|
||||
|
||||
void Remove(double aStart, double aEnd, ErrorResult& aRv);
|
||||
/** End WebIDL Methods. */
|
||||
@ -109,7 +110,9 @@ public:
|
||||
double GetBufferedEnd();
|
||||
|
||||
// Runs the range removal algorithm as defined by the MSE spec.
|
||||
// RangeRemoval will queue a call to DoRangeRemoval.
|
||||
void RangeRemoval(double aStart, double aEnd);
|
||||
void DoRangeRemoval(double aStart, double aEnd);
|
||||
|
||||
#if defined(DEBUG)
|
||||
void Dump(const char* aPath);
|
||||
|
@ -40,6 +40,8 @@ SourceBufferDecoder::SourceBufferDecoder(MediaResource* aResource,
|
||||
, mReader(nullptr)
|
||||
, mTimestampOffset(aTimestampOffset)
|
||||
, mMediaDuration(-1)
|
||||
, mRealMediaDuration(0)
|
||||
, mTrimmedOffset(-1)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_COUNT_CTOR(SourceBufferDecoder);
|
||||
@ -94,14 +96,14 @@ SourceBufferDecoder::IsMediaSeekable()
|
||||
void
|
||||
SourceBufferDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
nsAutoPtr<MetadataTags> aTags,
|
||||
bool aRestoredFromDromant)
|
||||
bool aRestoredFromDormant)
|
||||
{
|
||||
MSE_DEBUG("SourceBufferDecoder(%p)::MetadataLoaded UNIMPLEMENTED", this);
|
||||
}
|
||||
|
||||
void
|
||||
SourceBufferDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
bool aRestoredFromDromant)
|
||||
bool aRestoredFromDormant)
|
||||
{
|
||||
MSE_DEBUG("SourceBufferDecoder(%p)::FirstFrameLoaded UNIMPLEMENTED", this);
|
||||
}
|
||||
@ -190,6 +192,18 @@ SourceBufferDecoder::SetMediaDuration(int64_t aDuration)
|
||||
mMediaDuration = aDuration;
|
||||
}
|
||||
|
||||
void
|
||||
SourceBufferDecoder::SetRealMediaDuration(int64_t aDuration)
|
||||
{
|
||||
mRealMediaDuration = aDuration;
|
||||
}
|
||||
|
||||
void
|
||||
SourceBufferDecoder::Trim(int64_t aDuration)
|
||||
{
|
||||
mTrimmedOffset = (double)aDuration / USECS_PER_S;
|
||||
}
|
||||
|
||||
void
|
||||
SourceBufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
|
||||
{
|
||||
@ -229,7 +243,17 @@ SourceBufferDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, in
|
||||
nsresult
|
||||
SourceBufferDecoder::GetBuffered(dom::TimeRanges* aBuffered)
|
||||
{
|
||||
return mReader->GetBuffered(aBuffered);
|
||||
nsresult rv = mReader->GetBuffered(aBuffered);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!WasTrimmed()) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsRefPtr<dom::TimeRanges> tr = new dom::TimeRanges();
|
||||
tr->Add(0, mTrimmedOffset);
|
||||
aBuffered->Intersection(tr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int64_t
|
||||
@ -244,12 +268,12 @@ SourceBufferDecoder::ConvertToByteOffset(double aTime)
|
||||
// purposes of eviction this should be adequate since we have the
|
||||
// byte threshold as well to ensure data actually gets evicted and
|
||||
// we ensure we don't evict before the current playable point.
|
||||
if (mMediaDuration <= 0) {
|
||||
if (mRealMediaDuration <= 0) {
|
||||
return -1;
|
||||
}
|
||||
int64_t length = GetResource()->GetLength();
|
||||
MOZ_ASSERT(length > 0);
|
||||
int64_t offset = (aTime / (double(mMediaDuration) / USECS_PER_S)) * length;
|
||||
int64_t offset = (aTime / (double(mRealMediaDuration) / USECS_PER_S)) * length;
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
@ -49,8 +49,8 @@ public:
|
||||
virtual SourceBufferResource* GetResource() const MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual ReentrantMonitor& GetReentrantMonitor() MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual VideoFrameContainer* GetVideoFrameContainer() MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDromant) MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDromant) MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDormant) MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDormant) MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_FINAL MOZ_OVERRIDE;
|
||||
@ -121,6 +121,26 @@ public:
|
||||
// cached data. Returns -1 if no such value is computable.
|
||||
int64_t ConvertToByteOffset(double aTime);
|
||||
|
||||
// All durations are in usecs.
|
||||
|
||||
// We can't at this stage, accurately remove coded frames.
|
||||
// Trim is a work around that hides data located after a given time by
|
||||
// preventing playback beyond the trim point.
|
||||
// No data is actually removed.
|
||||
// aDuration is were data will be trimmed from.
|
||||
void Trim(int64_t aDuration);
|
||||
bool WasTrimmed()
|
||||
{
|
||||
return mTrimmedOffset >= 0;
|
||||
}
|
||||
|
||||
// returns the real duration of the resource, including trimmed data.
|
||||
void SetRealMediaDuration(int64_t aDuration);
|
||||
int64_t GetRealMediaDuration()
|
||||
{
|
||||
return mRealMediaDuration;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~SourceBufferDecoder();
|
||||
|
||||
@ -131,8 +151,14 @@ private:
|
||||
|
||||
AbstractMediaDecoder* mParentDecoder;
|
||||
nsRefPtr<MediaDecoderReader> mReader;
|
||||
// in microseconds
|
||||
int64_t mTimestampOffset;
|
||||
// mMediaDuration contains the apparent buffer duration, excluding trimmed data.
|
||||
int64_t mMediaDuration;
|
||||
// mRealMediaDuration contains the real buffer duration, including trimmed data.
|
||||
int64_t mRealMediaDuration;
|
||||
// in seconds
|
||||
double mTrimmedOffset;
|
||||
|
||||
#ifdef MOZ_EME
|
||||
nsRefPtr<CDMProxy> mCDMProxy;
|
||||
|
@ -35,7 +35,7 @@ extern PRLogModuleInfo* GetMediaSourceAPILog();
|
||||
|
||||
// Time in seconds to substract from the current time when deciding the
|
||||
// time point to evict data before in a decoder. This is used to help
|
||||
// precent evicting the current playback point.
|
||||
// prevent evicting the current playback point.
|
||||
#define MSE_EVICT_THRESHOLD_TIME 2.0
|
||||
|
||||
namespace mozilla {
|
||||
@ -167,7 +167,7 @@ TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength, int64_t aTimesta
|
||||
mLastEndTimestamp &&
|
||||
(!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
|
||||
mLastTimestampOffset != aTimestampOffset ||
|
||||
mDecoderPerSegment)) {
|
||||
mDecoderPerSegment || mCurrentDecoder->WasTrimmed())) {
|
||||
MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
|
||||
this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
|
||||
|
||||
@ -178,7 +178,7 @@ TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength, int64_t aTimesta
|
||||
}
|
||||
MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
|
||||
const nsTArray<uint8_t>& initData = mParser->InitData();
|
||||
AppendDataToCurrentResource(initData.Elements(), initData.Length());
|
||||
AppendDataToCurrentResource(initData.Elements(), initData.Length(), end - start);
|
||||
mLastStartTimestamp = start;
|
||||
} else {
|
||||
MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
|
||||
@ -188,7 +188,7 @@ TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength, int64_t aTimesta
|
||||
mLastEndTimestamp.emplace(end);
|
||||
}
|
||||
|
||||
if (!AppendDataToCurrentResource(aData, aLength)) {
|
||||
if (!AppendDataToCurrentResource(aData, aLength, end - start)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -199,7 +199,7 @@ TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength, int64_t aTimesta
|
||||
}
|
||||
|
||||
bool
|
||||
TrackBuffer::AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength)
|
||||
TrackBuffer::AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength, uint32_t aDuration)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mCurrentDecoder) {
|
||||
@ -209,6 +209,7 @@ TrackBuffer::AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength)
|
||||
SourceBufferResource* resource = mCurrentDecoder->GetResource();
|
||||
int64_t appendOffset = resource->GetLength();
|
||||
resource->AppendData(aData, aLength);
|
||||
mCurrentDecoder->SetRealMediaDuration(mCurrentDecoder->GetRealMediaDuration() + aDuration);
|
||||
// XXX: For future reference: NDA call must run on the main thread.
|
||||
mCurrentDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
|
||||
aLength, appendOffset);
|
||||
@ -288,8 +289,8 @@ TrackBuffer::EvictData(double aPlaybackTime,
|
||||
onCurrent ? "true" : "false",
|
||||
pastCurrentDecoder ? "true" : "false");
|
||||
|
||||
if (pastCurrentDecoder
|
||||
&& !mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
|
||||
if (pastCurrentDecoder &&
|
||||
!mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
|
||||
// Remove data from older decoders than the current one.
|
||||
// Don't remove data if it is currently active.
|
||||
MSE_DEBUG("TrackBuffer(%p)::EvictData evicting all before start "
|
||||
@ -720,4 +721,46 @@ TrackBuffer::RemoveDecoder(SourceBufferDecoder* aDecoder)
|
||||
aDecoder->GetReader()->GetTaskQueue()->Dispatch(task);
|
||||
}
|
||||
|
||||
bool
|
||||
TrackBuffer::RangeRemoval(int64_t aStart, int64_t aEnd)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
|
||||
|
||||
nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
|
||||
int64_t bufferedEnd = Buffered(buffered) * USECS_PER_S;
|
||||
int64_t bufferedStart = buffered->GetStartTime() * USECS_PER_S;
|
||||
|
||||
if (bufferedStart < 0 || aStart > bufferedEnd || aEnd < bufferedStart) {
|
||||
// Nothing to remove.
|
||||
return false;
|
||||
}
|
||||
if (aEnd < bufferedEnd) {
|
||||
// TODO. We only handle trimming.
|
||||
NS_WARNING("TrackBuffer::RangeRemoval unsupported arguments. "
|
||||
"Can only handle trimming");
|
||||
return false;
|
||||
}
|
||||
|
||||
nsTArray<SourceBufferDecoder*> decoders;
|
||||
decoders.AppendElements(mInitializedDecoders);
|
||||
|
||||
// Only trimming existing buffers.
|
||||
for (size_t i = 0; i < decoders.Length(); ++i) {
|
||||
decoders[i]->Trim(aStart);
|
||||
if (aStart <= buffered->GetStartTime()) {
|
||||
// We've completely emptied it, can clear the data.
|
||||
int64_t size = decoders[i]->GetResource()->GetSize();
|
||||
decoders[i]->GetResource()->EvictData(size, size);
|
||||
if (decoders[i] == mCurrentDecoder ||
|
||||
mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
|
||||
continue;
|
||||
}
|
||||
MSE_DEBUG("TrackBuffer(%p):RangeRemoval remove empty decoders=%d", this, i);
|
||||
RemoveDecoder(decoders[i]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -92,6 +92,13 @@ public:
|
||||
// TODO: Refactor to a cleaner interface between TrackBuffer and MediaSourceReader.
|
||||
const nsTArray<nsRefPtr<SourceBufferDecoder>>& Decoders();
|
||||
|
||||
// Runs MSE range removal algorithm.
|
||||
// http://w3c.github.io/media-source/#sourcebuffer-coded-frame-removal
|
||||
// Implementation is only partial, we can only trim a buffer.
|
||||
// Returns true if data was evicted.
|
||||
// Times are in microseconds.
|
||||
bool RangeRemoval(int64_t aStart, int64_t aEnd);
|
||||
|
||||
#ifdef MOZ_EME
|
||||
nsresult SetCDMProxy(CDMProxy* aProxy);
|
||||
#endif
|
||||
@ -113,7 +120,8 @@ private:
|
||||
|
||||
// Helper for AppendData, ensures NotifyDataArrived is called whenever
|
||||
// data is appended to the current decoder's SourceBufferResource.
|
||||
bool AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength);
|
||||
bool AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength,
|
||||
uint32_t aDuration /* microseconds */);
|
||||
|
||||
// Queue execution of InitializeDecoder on mTaskQueue.
|
||||
bool QueueInitializeDecoder(SourceBufferDecoder* aDecoder);
|
||||
|
@ -58,10 +58,10 @@ MediaOmxCommonDecoder::CheckDecoderCanOffloadAudio()
|
||||
|
||||
void
|
||||
MediaOmxCommonDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
bool aRestoredFromDromant)
|
||||
bool aRestoredFromDormant)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MediaDecoder::FirstFrameLoaded(aInfo, aRestoredFromDromant);
|
||||
MediaDecoder::FirstFrameLoaded(aInfo, aRestoredFromDormant);
|
||||
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
if (!CheckDecoderCanOffloadAudio()) {
|
||||
|
@ -24,7 +24,7 @@ public:
|
||||
MediaOmxCommonDecoder();
|
||||
|
||||
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
|
||||
bool aRestoredFromDromant);
|
||||
bool aRestoredFromDormant);
|
||||
virtual void ChangeState(PlayState aState);
|
||||
virtual void ApplyStateToStateMachine(PlayState aState);
|
||||
virtual void SetVolume(double aVolume);
|
||||
|
@ -140,13 +140,13 @@ BufferDecoder::IsMediaSeekable()
|
||||
}
|
||||
|
||||
void
|
||||
BufferDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDromant)
|
||||
BufferDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDormant)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
void
|
||||
BufferDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDromant)
|
||||
BufferDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDormant)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
@ -59,9 +59,9 @@ public:
|
||||
|
||||
virtual bool IsMediaSeekable() MOZ_FINAL MOZ_OVERRIDE;
|
||||
|
||||
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDromant) MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, bool aRestoredFromDormant) MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual void QueueMetadata(int64_t aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDromant) MOZ_FINAL MOZ_OVERRIDE;
|
||||
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, bool aRestoredFromDormant) MOZ_FINAL MOZ_OVERRIDE;
|
||||
|
||||
virtual void RemoveMediaTracks() MOZ_FINAL MOZ_OVERRIDE;
|
||||
|
||||
|
@ -219,9 +219,13 @@ MediaEngineTabVideoSource::Draw() {
|
||||
IntSize size;
|
||||
// maintain source aspect ratio
|
||||
if (mBufWidthMax/innerWidth < mBufHeightMax/innerHeight) {
|
||||
size = IntSize(mBufWidthMax, (mBufWidthMax * ((float) innerHeight/innerWidth)));
|
||||
// adjust width to be divisible by 4 to work around bug 1125393
|
||||
int32_t width = mBufWidthMax - (mBufWidthMax % 4);
|
||||
size = IntSize(width, (width * ((float) innerHeight/innerWidth)));
|
||||
} else {
|
||||
size = IntSize((mBufHeightMax * ((float) innerWidth/innerHeight)), mBufHeightMax);
|
||||
int32_t tmpWidth = (mBufHeightMax * ((float) innerWidth/innerHeight));
|
||||
int32_t width = tmpWidth - (tmpWidth % 4);
|
||||
size = IntSize(width, (width * ((float) innerHeight/innerWidth)));
|
||||
}
|
||||
|
||||
gfxImageFormat format = gfxImageFormat::RGB24;
|
||||
|
@ -662,7 +662,6 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetNetscapeWindow(void *value)
|
||||
nsViewManager* vm = mPluginFrame->PresContext()->GetPresShell()->GetViewManager();
|
||||
if (!vm)
|
||||
return NS_ERROR_FAILURE;
|
||||
#if defined(XP_WIN)
|
||||
// This property is provided to allow a "windowless" plugin to determine the window it is drawing
|
||||
// in, so it can translate mouse coordinates it receives directly from the operating system
|
||||
// to coordinates relative to itself.
|
||||
@ -681,7 +680,8 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetNetscapeWindow(void *value)
|
||||
// we only attempt to get the nearest window if this really is a "windowless" plugin so as not
|
||||
// to change any behaviour for the much more common windowed plugins,
|
||||
// though why this method would even be being called for a windowed plugin escapes me.
|
||||
if (mPluginWindow && mPluginWindow->type == NPWindowTypeDrawable) {
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Content &&
|
||||
mPluginWindow && mPluginWindow->type == NPWindowTypeDrawable) {
|
||||
// it turns out that flash also uses this window for determining focus, and is currently
|
||||
// unable to show a caret correctly if we return the enclosing window. Therefore for
|
||||
// now we only return the enclosing window when there is an actual offset which
|
||||
@ -706,12 +706,11 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetNetscapeWindow(void *value)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// simply return the topmost document window
|
||||
nsCOMPtr<nsIWidget> widget;
|
||||
vm->GetRootWidget(getter_AddRefs(widget));
|
||||
if (widget) {
|
||||
*pvalue = (void*)widget->GetNativeData(NS_NATIVE_WINDOW);
|
||||
*pvalue = (void*)widget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW);
|
||||
} else {
|
||||
NS_ASSERTION(widget, "couldn't get doc's widget in getting doc's window handle");
|
||||
}
|
||||
|
@ -65,6 +65,7 @@ const wchar_t * kMozillaWindowClass = L"MozillaWindowClass";
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
// see PluginModuleChild::GetChrome()
|
||||
PluginModuleChild* gChromeInstance = nullptr;
|
||||
nsTArray<PluginModuleChild*>* gAllInstances;
|
||||
}
|
||||
@ -191,6 +192,9 @@ PluginModuleChild::~PluginModuleChild()
|
||||
PluginModuleChild*
|
||||
PluginModuleChild::GetChrome()
|
||||
{
|
||||
// A special PluginModuleChild instance that talks to the chrome process
|
||||
// during startup and shutdown. Synchronous messages to or from this actor
|
||||
// should be avoided because they may lead to hangs.
|
||||
MOZ_ASSERT(gChromeInstance);
|
||||
return gChromeInstance;
|
||||
}
|
||||
@ -230,7 +234,6 @@ PluginModuleChild::InitForContent(base::ProcessHandle aParentProcessHandle,
|
||||
mTransport = aChannel;
|
||||
|
||||
mLibrary = GetChrome()->mLibrary;
|
||||
mQuirks = GetChrome()->mQuirks;
|
||||
mFunctions = GetChrome()->mFunctions;
|
||||
|
||||
return true;
|
||||
@ -2062,7 +2065,13 @@ PluginModuleChild::AllocPPluginInstanceChild(const nsCString& aMimeType,
|
||||
PLUGIN_LOG_DEBUG_METHOD;
|
||||
AssertPluginThread();
|
||||
|
||||
InitQuirksModes(aMimeType);
|
||||
// In e10s, gChromeInstance hands out quirks to instances, but never
|
||||
// allocates an instance on its own. Make sure it gets the latest copy
|
||||
// of quirks once we have them. Also note, with process-per-tab, we may
|
||||
// have multiple PluginModuleChilds in the same plugin process, so only
|
||||
// initialize this once in gChromeInstance, which is a singleton.
|
||||
GetChrome()->InitQuirksModes(aMimeType);
|
||||
mQuirks = GetChrome()->mQuirks;
|
||||
|
||||
#ifdef XP_WIN
|
||||
if ((mQuirks & QUIRK_FLASH_HOOK_GETWINDOWINFO) &&
|
||||
|
@ -24,7 +24,7 @@ public:
|
||||
|
||||
static already_AddRefed<RequestSyncWifiService> GetInstance();
|
||||
|
||||
void Notify(const hal::NetworkInformation& aNetworkInfo);
|
||||
void Notify(const hal::NetworkInformation& aNetworkInfo) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
RequestSyncWifiService()
|
||||
|
@ -380,7 +380,8 @@ function testPercentUnitChangeOnLength()
|
||||
// can be updated.
|
||||
todo_is(gCircle.cy.animVal.value, 100,
|
||||
"Checking animated length=100% after animating but before reflow");
|
||||
gSvg.forceRedraw();
|
||||
// force a layout flush (Bug 627594)
|
||||
gSvg.getCTM();
|
||||
// Even after doing a reflow though we'll still fail due to bug 508206
|
||||
// (Relative units used in animation don't update immediately)
|
||||
todo_is(gCircle.cy.animVal.value, 100,
|
||||
@ -391,7 +392,8 @@ function testPercentUnitChangeOnLength()
|
||||
"Checking animated length=100% after animating");
|
||||
|
||||
gSvg.setAttribute("height", "50px"); // Change: height: 50px
|
||||
gSvg.forceRedraw(); // Bug 627594
|
||||
// force a layout flush (Bug 627594)
|
||||
gSvg.getCTM();
|
||||
gSvg.setCurrentTime(0); // Bug 508206
|
||||
is(gCircle.cy.animVal.value, 50,
|
||||
"Checking animated length=100% after updating context");
|
||||
|
@ -305,15 +305,9 @@ SVGSVGElement::UnsuspendRedrawAll()
|
||||
}
|
||||
|
||||
void
|
||||
SVGSVGElement::ForceRedraw(ErrorResult& rv)
|
||||
SVGSVGElement::ForceRedraw()
|
||||
{
|
||||
nsIDocument* doc = GetComposedDoc();
|
||||
if (!doc) {
|
||||
rv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
doc->FlushPendingNotifications(Flush_Display);
|
||||
// no-op
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -246,7 +246,7 @@ public:
|
||||
uint32_t SuspendRedraw(uint32_t max_wait_milliseconds);
|
||||
void UnsuspendRedraw(uint32_t suspend_handle_id);
|
||||
void UnsuspendRedrawAll();
|
||||
void ForceRedraw(ErrorResult& rv);
|
||||
void ForceRedraw();
|
||||
void PauseAnimations();
|
||||
void UnpauseAnimations();
|
||||
bool AnimationsPaused();
|
||||
|
@ -23,7 +23,9 @@ function testAboutURL() {
|
||||
});
|
||||
|
||||
var p2 = fetch('about:config').then(function(res) {
|
||||
is(res.type, "error", "about:config should fail");
|
||||
ok(false, "about:config should fail");
|
||||
}, function(e) {
|
||||
ok(e instanceof TypeError, "about:config should fail");
|
||||
});
|
||||
|
||||
return Promise.all([p1, p2]);
|
||||
|
@ -43,8 +43,9 @@ function testURLFail() {
|
||||
var promises = [];
|
||||
failFiles.forEach(function(entry) {
|
||||
var p = fetch(entry[0]).then(function(res) {
|
||||
ok(res.type === "error", "Response should be an error for " + entry[0]);
|
||||
is(res.status, 0, "Response status should be 0 for " + entry[0]);
|
||||
ok(false, "Response should be an error for " + entry[0]);
|
||||
}, function(e) {
|
||||
ok(e instanceof TypeError, "Response should be an error for " + entry[0]);
|
||||
});
|
||||
promises.push(p);
|
||||
});
|
||||
@ -108,21 +109,20 @@ function testResponses() {
|
||||
resolve(p);
|
||||
}),
|
||||
|
||||
// FIXME(nsm): Enable once Bug 1107777 and Bug 1072144 have been fixed.
|
||||
//new Promise((resolve, reject) => {
|
||||
// var req = new Request(path + 'responseIdentical.sjs', {
|
||||
// method: 'POST',
|
||||
// body: '{',
|
||||
// });
|
||||
// var p = fetch(req).then((res) => {
|
||||
// is(res.status, 200, "wrong status");
|
||||
// return res.json().then(
|
||||
// (v) => ok(false, "expected json parse failure"),
|
||||
// (e) => ok(true, "expected json parse failure")
|
||||
// );
|
||||
// });
|
||||
// resolve(p);
|
||||
//}),
|
||||
new Promise((resolve, reject) => {
|
||||
var req = new Request(path + 'responseIdentical.sjs', {
|
||||
method: 'POST',
|
||||
body: '{',
|
||||
});
|
||||
var p = fetch(req).then((res) => {
|
||||
is(res.status, 200, "wrong status");
|
||||
return res.json().then(
|
||||
(v) => ok(false, "expected json parse failure"),
|
||||
(e) => ok(true, "expected json parse failure")
|
||||
);
|
||||
});
|
||||
resolve(p);
|
||||
}),
|
||||
];
|
||||
|
||||
return Promise.all(fetches);
|
||||
|
@ -12,10 +12,6 @@ if (typeof is !== "function") {
|
||||
|
||||
var path = "/tests/dom/base/test/";
|
||||
|
||||
function isNetworkError(response) {
|
||||
return response.type == "error" && response.status === 0 && response.statusText === "";
|
||||
}
|
||||
|
||||
function isOpaqueResponse(response) {
|
||||
return response.type == "opaque" && response.status === 0 && response.statusText === "";
|
||||
}
|
||||
@ -24,7 +20,9 @@ function testModeSameOrigin() {
|
||||
// Fetch spec Section 4, step 4, "request's mode is same-origin".
|
||||
var req = new Request("http://example.com", { mode: "same-origin" });
|
||||
return fetch(req).then(function(res) {
|
||||
ok(isNetworkError(res), "Attempting to fetch a resource from a different origin with mode same-origin should fail.");
|
||||
ok(false, "Attempting to fetch a resource from a different origin with mode same-origin should fail.");
|
||||
}, function(e) {
|
||||
ok(e instanceof TypeError, "Attempting to fetch a resource from a different origin with mode same-origin should fail.");
|
||||
});
|
||||
}
|
||||
|
||||
@ -66,6 +64,101 @@ function testModeNoCors() {
|
||||
});
|
||||
}
|
||||
|
||||
function testSameOriginCredentials() {
|
||||
var cookieStr = "type=chocolatechip";
|
||||
var tests = [
|
||||
{
|
||||
// Initialize by setting a cookie.
|
||||
pass: 1,
|
||||
setCookie: cookieStr,
|
||||
withCred: "same-origin",
|
||||
},
|
||||
{
|
||||
// Default mode is "omit".
|
||||
pass: 1,
|
||||
noCookie: 1,
|
||||
},
|
||||
{
|
||||
pass: 1,
|
||||
noCookie: 1,
|
||||
withCred: "omit",
|
||||
},
|
||||
{
|
||||
pass: 1,
|
||||
cookie: cookieStr,
|
||||
withCred: "same-origin",
|
||||
},
|
||||
{
|
||||
pass: 1,
|
||||
cookie: cookieStr,
|
||||
withCred: "include",
|
||||
},
|
||||
];
|
||||
|
||||
var finalPromiseResolve, finalPromiseReject;
|
||||
var finalPromise = new Promise(function(res, rej) {
|
||||
finalPromiseResolve = res;
|
||||
finalPromiseReject = rej;
|
||||
});
|
||||
|
||||
function makeRequest(test) {
|
||||
req = {
|
||||
// Add a default query param just to make formatting the actual params
|
||||
// easier.
|
||||
url: corsServerPath + "a=b",
|
||||
method: test.method,
|
||||
headers: test.headers,
|
||||
withCred: test.withCred,
|
||||
};
|
||||
|
||||
if (test.setCookie)
|
||||
req.url += "&setCookie=" + escape(test.setCookie);
|
||||
if (test.cookie)
|
||||
req.url += "&cookie=" + escape(test.cookie);
|
||||
if (test.noCookie)
|
||||
req.url += "&noCookie";
|
||||
|
||||
return new Request(req.url, { method: req.method,
|
||||
headers: req.headers,
|
||||
credentials: req.withCred });
|
||||
}
|
||||
|
||||
function testResponse(res, test) {
|
||||
ok(test.pass, "Expected test to pass " + test.toSource());
|
||||
is(res.status, 200, "wrong status in test for " + test.toSource());
|
||||
is(res.statusText, "OK", "wrong status text for " + test.toSource());
|
||||
return res.text().then(function(v) {
|
||||
is(v, "<res>hello pass</res>\n",
|
||||
"wrong text in test for " + test.toSource());
|
||||
});
|
||||
}
|
||||
|
||||
function runATest(tests, i) {
|
||||
var test = tests[i];
|
||||
var request = makeRequest(test);
|
||||
console.log(request.url);
|
||||
fetch(request).then(function(res) {
|
||||
testResponse(res, test);
|
||||
if (i < tests.length-1) {
|
||||
runATest(tests, i+1);
|
||||
} else {
|
||||
finalPromiseResolve();
|
||||
}
|
||||
}, function(e) {
|
||||
ok(!test.pass, "Expected test to fail " + test.toSource());
|
||||
ok(e instanceof TypeError, "Test should fail " + test.toSource());
|
||||
if (i < tests.length-1) {
|
||||
runATest(tests, i+1);
|
||||
} else {
|
||||
finalPromiseResolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
runATest(tests, 0);
|
||||
return finalPromise;
|
||||
}
|
||||
|
||||
function testModeCors() {
|
||||
var tests = [// Plain request
|
||||
{ pass: 1,
|
||||
@ -660,62 +753,43 @@ function testModeCors() {
|
||||
headers: req.headers, body: req.body });
|
||||
fetches.push((function(test, request) {
|
||||
return fetch(request).then(function(res) {
|
||||
dump("Response for " + request.url + "\n");
|
||||
if (test.pass) {
|
||||
ok(!isNetworkError(res),
|
||||
"shouldn't have failed in test for " + test.toSource());
|
||||
if (test.status) {
|
||||
is(res.status, test.status, "wrong status in test for " + test.toSource());
|
||||
is(res.statusText, test.statusMessage, "wrong status text for " + test.toSource());
|
||||
}
|
||||
else {
|
||||
is(res.status, 200, "wrong status in test for " + test.toSource());
|
||||
is(res.statusText, "OK", "wrong status text for " + test.toSource());
|
||||
}
|
||||
if (test.responseHeaders) {
|
||||
for (header in test.responseHeaders) {
|
||||
if (test.expectedResponseHeaders.indexOf(header) == -1) {
|
||||
is(res.headers.has(header), false,
|
||||
"|Headers.has()|wrong response header (" + header + ") in test for " +
|
||||
test.toSource());
|
||||
}
|
||||
else {
|
||||
is(res.headers.get(header), test.responseHeaders[header],
|
||||
"|Headers.get()|wrong response header (" + header + ") in test for " +
|
||||
test.toSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res.text().then(function(v) {
|
||||
if (test.method !== "HEAD") {
|
||||
is(v, "<res>hello pass</res>\n",
|
||||
"wrong responseText in test for " + test.toSource());
|
||||
}
|
||||
else {
|
||||
is(v, "",
|
||||
"wrong responseText in HEAD test for " + test.toSource());
|
||||
}
|
||||
});
|
||||
ok(test.pass, "Expected test to pass for " + test.toSource());
|
||||
if (test.status) {
|
||||
is(res.status, test.status, "wrong status in test for " + test.toSource());
|
||||
is(res.statusText, test.statusMessage, "wrong status text for " + test.toSource());
|
||||
}
|
||||
else {
|
||||
ok(isNetworkError(res),
|
||||
"should have failed in test for " + test.toSource());
|
||||
is(res.status, 0, "wrong status in test for " + test.toSource());
|
||||
is(res.statusText, "", "wrong status text for " + test.toSource());
|
||||
if (test.responseHeaders) {
|
||||
for (header in test.responseHeaders) {
|
||||
is(res.headers.get(header), null,
|
||||
"wrong response header (" + header + ") in test for " +
|
||||
is(res.status, 200, "wrong status in test for " + test.toSource());
|
||||
is(res.statusText, "OK", "wrong status text for " + test.toSource());
|
||||
}
|
||||
if (test.responseHeaders) {
|
||||
for (header in test.responseHeaders) {
|
||||
if (test.expectedResponseHeaders.indexOf(header) == -1) {
|
||||
is(res.headers.has(header), false,
|
||||
"|Headers.has()|wrong response header (" + header + ") in test for " +
|
||||
test.toSource());
|
||||
}
|
||||
else {
|
||||
is(res.headers.get(header), test.responseHeaders[header],
|
||||
"|Headers.get()|wrong response header (" + header + ") in test for " +
|
||||
test.toSource());
|
||||
}
|
||||
}
|
||||
|
||||
return res.text().then(function(v) {
|
||||
is(v, "",
|
||||
"wrong responseText in test for " + test.toSource());
|
||||
});
|
||||
}
|
||||
|
||||
return res.text().then(function(v) {
|
||||
if (test.method !== "HEAD") {
|
||||
is(v, "<res>hello pass</res>\n",
|
||||
"wrong responseText in test for " + test.toSource());
|
||||
}
|
||||
else {
|
||||
is(v, "",
|
||||
"wrong responseText in HEAD test for " + test.toSource());
|
||||
}
|
||||
});
|
||||
}, function(e) {
|
||||
ok(!test.pass, "Expected test failure for " + test.toSource());
|
||||
ok(e instanceof TypeError, "Exception should be TypeError for " + test.toSource());
|
||||
});
|
||||
})(test, request));
|
||||
}
|
||||
@ -723,80 +797,98 @@ function testModeCors() {
|
||||
return Promise.all(fetches);
|
||||
}
|
||||
|
||||
function testCredentials() {
|
||||
function testCrossOriginCredentials() {
|
||||
var tests = [
|
||||
{ pass: 1,
|
||||
method: "GET",
|
||||
withCred: 1,
|
||||
withCred: "include",
|
||||
allowCred: 1,
|
||||
},
|
||||
{ pass: 0,
|
||||
method: "GET",
|
||||
withCred: 1,
|
||||
withCred: "include",
|
||||
allowCred: 0,
|
||||
},
|
||||
{ pass: 0,
|
||||
method: "GET",
|
||||
withCred: 1,
|
||||
withCred: "include",
|
||||
allowCred: 1,
|
||||
origin: "*",
|
||||
},
|
||||
{ pass: 1,
|
||||
method: "GET",
|
||||
withCred: 0,
|
||||
withCred: "omit",
|
||||
allowCred: 1,
|
||||
origin: "*",
|
||||
},
|
||||
{ pass: 1,
|
||||
method: "GET",
|
||||
setCookie: "a=1",
|
||||
withCred: 1,
|
||||
withCred: "include",
|
||||
allowCred: 1,
|
||||
},
|
||||
{ pass: 1,
|
||||
method: "GET",
|
||||
cookie: "a=1",
|
||||
withCred: 1,
|
||||
withCred: "include",
|
||||
allowCred: 1,
|
||||
},
|
||||
{ pass: 1,
|
||||
method: "GET",
|
||||
noCookie: 1,
|
||||
withCred: 0,
|
||||
withCred: "omit",
|
||||
allowCred: 1,
|
||||
},
|
||||
{ pass: 0,
|
||||
method: "GET",
|
||||
noCookie: 1,
|
||||
withCred: 1,
|
||||
withCred: "include",
|
||||
allowCred: 1,
|
||||
},
|
||||
{ pass: 1,
|
||||
method: "GET",
|
||||
setCookie: "a=2",
|
||||
withCred: 0,
|
||||
withCred: "omit",
|
||||
allowCred: 1,
|
||||
},
|
||||
{ pass: 1,
|
||||
method: "GET",
|
||||
cookie: "a=1",
|
||||
withCred: 1,
|
||||
withCred: "include",
|
||||
allowCred: 1,
|
||||
},
|
||||
{ pass: 1,
|
||||
method: "GET",
|
||||
setCookie: "a=2",
|
||||
withCred: 1,
|
||||
withCred: "include",
|
||||
allowCred: 1,
|
||||
},
|
||||
{ pass: 1,
|
||||
method: "GET",
|
||||
cookie: "a=2",
|
||||
withCred: 1,
|
||||
withCred: "include",
|
||||
allowCred: 1,
|
||||
},
|
||||
{
|
||||
// When credentials mode is same-origin, but mode is cors, no
|
||||
// cookie should be sent cross origin.
|
||||
pass: 0,
|
||||
method: "GET",
|
||||
cookie: "a=2",
|
||||
withCred: "same-origin",
|
||||
allowCred: 1,
|
||||
},
|
||||
{
|
||||
// When credentials mode is same-origin, but mode is cors, no
|
||||
// cookie should be sent cross origin. This test checks the same
|
||||
// thing as above, but uses the noCookie check on the server
|
||||
// instead, and expects a valid response.
|
||||
pass: 1,
|
||||
method: "GET",
|
||||
noCookie: 1,
|
||||
withCred: "same-origin",
|
||||
},
|
||||
];
|
||||
// FIXME(nsm): Add "same-origin" credentials test
|
||||
|
||||
var baseURL = "http://example.org" + corsServerPath;
|
||||
var origin = "http://mochi.test:8888";
|
||||
@ -832,44 +924,41 @@ function testCredentials() {
|
||||
|
||||
return new Request(req.url, { method: req.method,
|
||||
headers: req.headers,
|
||||
credentials: req.withCred ? "include" : "omit" });
|
||||
credentials: req.withCred });
|
||||
}
|
||||
|
||||
function testResponse(res, test) {
|
||||
if (test.pass) {
|
||||
is(isNetworkError(res), false,
|
||||
"shouldn't have failed in test for " + test.toSource());
|
||||
is(res.status, 200, "wrong status in test for " + test.toSource());
|
||||
is(res.statusText, "OK", "wrong status text for " + test.toSource());
|
||||
return res.text().then(function(v) {
|
||||
is(v, "<res>hello pass</res>\n",
|
||||
"wrong text in test for " + test.toSource());
|
||||
});
|
||||
}
|
||||
else {
|
||||
is(isNetworkError(res), true,
|
||||
"should have failed in test for " + test.toSource());
|
||||
return res.text().then(function(v) {
|
||||
is(v, "",
|
||||
"wrong text in test for " + test.toSource());
|
||||
});
|
||||
}
|
||||
ok(test.pass, "Expected test to pass for " + test.toSource());
|
||||
is(res.status, 200, "wrong status in test for " + test.toSource());
|
||||
is(res.statusText, "OK", "wrong status text for " + test.toSource());
|
||||
return res.text().then(function(v) {
|
||||
is(v, "<res>hello pass</res>\n",
|
||||
"wrong text in test for " + test.toSource());
|
||||
});
|
||||
}
|
||||
|
||||
function runATest(i) {
|
||||
function runATest(tests, i) {
|
||||
var test = tests[i];
|
||||
var request = makeRequest(test);
|
||||
fetch(request).then(function(res) {
|
||||
testResponse(res, test);
|
||||
if (i < tests.length-1) {
|
||||
runATest(i+1);
|
||||
runATest(tests, i+1);
|
||||
} else {
|
||||
finalPromiseResolve();
|
||||
}
|
||||
}, finalPromiseReject);
|
||||
}, function(e) {
|
||||
ok(!test.pass, "Expected test failure for " + test.toSource());
|
||||
ok(e instanceof TypeError, "Exception should be TypeError for " + test.toSource());
|
||||
if (i < tests.length-1) {
|
||||
runATest(tests, i+1);
|
||||
} else {
|
||||
finalPromiseResolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
runATest(0);
|
||||
runATest(tests, 0);
|
||||
return finalPromise;
|
||||
}
|
||||
|
||||
@ -1153,27 +1242,17 @@ function testRedirects() {
|
||||
body: req.body });
|
||||
fetches.push((function(request, test) {
|
||||
return fetch(request).then(function(res) {
|
||||
if (test.pass) {
|
||||
is(isNetworkError(res), false,
|
||||
"shouldn't have failed in test for " + test.toSource());
|
||||
is(res.status, 200, "wrong status in test for " + test.toSource());
|
||||
is(res.statusText, "OK", "wrong status text for " + test.toSource());
|
||||
is((new URL(res.url)).host, (new URL(test.hops[test.hops.length-1].server)).host, "Response URL should be redirected URL");
|
||||
return res.text().then(function(v) {
|
||||
is(v, "<res>hello pass</res>\n",
|
||||
"wrong responseText in test for " + test.toSource());
|
||||
});
|
||||
}
|
||||
else {
|
||||
is(isNetworkError(res), true,
|
||||
"should have failed in test for " + test.toSource());
|
||||
is(res.status, 0, "wrong status in test for " + test.toSource());
|
||||
is(res.statusText, "", "wrong status text for " + test.toSource());
|
||||
return res.text().then(function(v) {
|
||||
is(v, "",
|
||||
"wrong responseText in test for " + test.toSource());
|
||||
});
|
||||
}
|
||||
ok(test.pass, "Expected test to pass for " + test.toSource());
|
||||
is(res.status, 200, "wrong status in test for " + test.toSource());
|
||||
is(res.statusText, "OK", "wrong status text for " + test.toSource());
|
||||
is((new URL(res.url)).host, (new URL(test.hops[test.hops.length-1].server)).host, "Response URL should be redirected URL");
|
||||
return res.text().then(function(v) {
|
||||
is(v, "<res>hello pass</res>\n",
|
||||
"wrong responseText in test for " + test.toSource());
|
||||
});
|
||||
}, function(e) {
|
||||
ok(!test.pass, "Expected test failure for " + test.toSource());
|
||||
ok(e instanceof TypeError, "Exception should be TypeError for " + test.toSource());
|
||||
});
|
||||
})(request, test));
|
||||
}
|
||||
@ -1196,7 +1275,8 @@ function runTest() {
|
||||
.then(testModeSameOrigin)
|
||||
.then(testModeNoCors)
|
||||
.then(testModeCors)
|
||||
.then(testCredentials)
|
||||
.then(testSameOriginCredentials)
|
||||
.then(testCrossOriginCredentials)
|
||||
.then(testRedirects)
|
||||
// Put more promise based tests here.
|
||||
.then(done)
|
||||
|
@ -7,12 +7,13 @@
|
||||
* http://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html
|
||||
*/
|
||||
|
||||
// While not explicitly restricted to ServiceWorkerGlobalScope, it probably
|
||||
// should be. https://github.com/slightlyoff/ServiceWorker/issues/254
|
||||
[Constructor(DOMString type, optional EventInit eventInitDict),
|
||||
Func="mozilla::dom::workers::ServiceWorkerEventsVisible",
|
||||
Exposed=(ServiceWorker,Window)]
|
||||
interface InstallPhaseEvent : Event {
|
||||
[Constructor(DOMString type, optional ExtendableEventInit eventInitDict),
|
||||
Exposed=ServiceWorker]
|
||||
interface ExtendableEvent : Event {
|
||||
// https://github.com/slightlyoff/ServiceWorker/issues/261
|
||||
void waitUntil(Promise<any> p);
|
||||
};
|
||||
|
||||
dictionary ExtendableEventInit : EventInit {
|
||||
// Defined for the forward compatibility across the derived events
|
||||
};
|
@ -7,20 +7,9 @@
|
||||
* http://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html
|
||||
*/
|
||||
|
||||
// While not explicitly restricted to ServiceWorkerGlobalScope, it probably
|
||||
// should be. https://github.com/slightlyoff/ServiceWorker/issues/254
|
||||
[Constructor(DOMString type, optional InstallEventInit eventInitDict),
|
||||
Func="mozilla::dom::workers::ServiceWorkerEventsVisible",
|
||||
// XXXbz I have no idea where this should be exposed. The spec makes
|
||||
// no sense. But since it returns a ServiceWorker and that's only
|
||||
// exposed in Window, let's say Window.
|
||||
Exposed=Window]
|
||||
interface InstallEvent : InstallPhaseEvent {
|
||||
// The currently active worker for this scope when this worker is asked to
|
||||
// install itself.
|
||||
// This may be null when a ServiceWorker is being installed for a previously
|
||||
// uncontrolled scope.
|
||||
// https://github.com/slightlyoff/ServiceWorker/issues/260
|
||||
Exposed=ServiceWorker]
|
||||
interface InstallEvent : ExtendableEvent {
|
||||
readonly attribute ServiceWorker? activeWorker;
|
||||
void replace();
|
||||
};
|
||||
|
@ -28,10 +28,13 @@ interface SVGSVGElement : SVGGraphicsElement {
|
||||
attribute float currentScale;
|
||||
readonly attribute SVGPoint currentTranslate;
|
||||
|
||||
[DependsOn=Nothing, Affects=Nothing]
|
||||
unsigned long suspendRedraw(unsigned long maxWaitMilliseconds);
|
||||
[DependsOn=Nothing, Affects=Nothing]
|
||||
void unsuspendRedraw(unsigned long suspendHandleID);
|
||||
[DependsOn=Nothing, Affects=Nothing]
|
||||
void unsuspendRedrawAll();
|
||||
[Throws]
|
||||
[DependsOn=Nothing, Affects=Nothing]
|
||||
void forceRedraw();
|
||||
void pauseAnimations();
|
||||
void unpauseAnimations();
|
||||
|
@ -10,9 +10,9 @@
|
||||
|
||||
// Still unclear what should be subclassed.
|
||||
// https://github.com/slightlyoff/ServiceWorker/issues/189
|
||||
[Pref="dom.serviceWorkers.enabled",
|
||||
// FIXME(nsm): Bug 1113522. Should also be exposed on Workers too.
|
||||
Exposed=Window]
|
||||
[Func="mozilla::dom::workers::ServiceWorkerVisible",
|
||||
// FIXME(nsm): Bug 1113522. This is exposed to satisfy webidl constraints, but it won't actually work.
|
||||
Exposed=(ServiceWorker,Window)]
|
||||
interface ServiceWorker : EventTarget {
|
||||
readonly attribute USVString scriptURL;
|
||||
readonly attribute ServiceWorkerState state;
|
||||
|
@ -130,6 +130,7 @@ WEBIDL_FILES = [
|
||||
'EventListener.webidl',
|
||||
'EventSource.webidl',
|
||||
'EventTarget.webidl',
|
||||
'ExtendableEvent.webidl',
|
||||
'Fetch.webidl',
|
||||
'File.webidl',
|
||||
'FileList.webidl',
|
||||
@ -245,7 +246,6 @@ WEBIDL_FILES = [
|
||||
'InputMethod.webidl',
|
||||
'InspectorUtils.webidl',
|
||||
'InstallEvent.webidl',
|
||||
'InstallPhaseEvent.webidl',
|
||||
'InterAppConnection.webidl',
|
||||
'InterAppConnectionRequest.webidl',
|
||||
'InterAppMessagePort.webidl',
|
||||
|
@ -9,10 +9,28 @@
|
||||
#include "SharedWorker.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
USING_WORKERS_NAMESPACE
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
bool
|
||||
ServiceWorkerVisible(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return Preferences::GetBool("dom.serviceWorkers.enabled", false);
|
||||
}
|
||||
|
||||
ServiceWorkerGlobalScope* scope = nullptr;
|
||||
nsresult rv = UnwrapObject<prototypes::id::ServiceWorkerGlobalScope_workers,
|
||||
mozilla::dom::ServiceWorkerGlobalScopeBinding_workers::NativeType>(aObj, scope);
|
||||
return NS_SUCCEEDED(rv);
|
||||
}
|
||||
|
||||
ServiceWorker::ServiceWorker(nsPIDOMWindow* aWindow,
|
||||
SharedWorker* aSharedWorker)
|
||||
@ -55,3 +73,7 @@ ServiceWorker::GetWorkerPrivate() const
|
||||
MOZ_ASSERT(mSharedWorker);
|
||||
return mSharedWorker->GetWorkerPrivate();
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -21,6 +21,9 @@ namespace workers {
|
||||
|
||||
class SharedWorker;
|
||||
|
||||
bool
|
||||
ServiceWorkerVisible(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
class ServiceWorker MOZ_FINAL : public DOMEventTargetHelper
|
||||
{
|
||||
friend class RuntimeService;
|
||||
@ -40,12 +43,24 @@ public:
|
||||
return mState;
|
||||
}
|
||||
|
||||
void
|
||||
SetState(ServiceWorkerState aState)
|
||||
{
|
||||
mState = aState;
|
||||
}
|
||||
|
||||
void
|
||||
GetScriptURL(nsString& aURL) const
|
||||
{
|
||||
aURL = mURL;
|
||||
}
|
||||
|
||||
void
|
||||
DispatchStateChange()
|
||||
{
|
||||
DOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("statechange"));
|
||||
}
|
||||
|
||||
WorkerPrivate*
|
||||
GetWorkerPrivate() const;
|
||||
|
||||
|
@ -11,28 +11,18 @@
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/WorkerScope.h"
|
||||
#include "mozilla/dom/workers/bindings/ServiceWorker.h"
|
||||
#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
bool
|
||||
ServiceWorkerEventsVisible(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
ServiceWorkerGlobalScope* scope = nullptr;
|
||||
nsresult rv = UnwrapObject<prototypes::id::ServiceWorkerGlobalScope_workers,
|
||||
mozilla::dom::ServiceWorkerGlobalScopeBinding_workers::NativeType>(aObj, scope);
|
||||
return NS_SUCCEEDED(rv) && scope;
|
||||
}
|
||||
|
||||
InstallPhaseEvent::InstallPhaseEvent(EventTarget* aOwner)
|
||||
ExtendableEvent::ExtendableEvent(EventTarget* aOwner)
|
||||
: Event(aOwner, nullptr, nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
InstallPhaseEvent::WaitUntil(Promise& aPromise)
|
||||
ExtendableEvent::WaitUntil(Promise& aPromise)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
@ -42,26 +32,26 @@ InstallPhaseEvent::WaitUntil(Promise& aPromise)
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(InstallPhaseEvent, Event)
|
||||
NS_IMPL_RELEASE_INHERITED(InstallPhaseEvent, Event)
|
||||
NS_IMPL_ADDREF_INHERITED(ExtendableEvent, Event)
|
||||
NS_IMPL_RELEASE_INHERITED(ExtendableEvent, Event)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(InstallPhaseEvent)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ExtendableEvent)
|
||||
NS_INTERFACE_MAP_END_INHERITING(Event)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(InstallPhaseEvent, Event, mPromise)
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(ExtendableEvent, Event, mPromise)
|
||||
|
||||
InstallEvent::InstallEvent(EventTarget* aOwner)
|
||||
: InstallPhaseEvent(aOwner)
|
||||
: ExtendableEvent(aOwner)
|
||||
, mActivateImmediately(false)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(InstallEvent, InstallPhaseEvent)
|
||||
NS_IMPL_RELEASE_INHERITED(InstallEvent, InstallPhaseEvent)
|
||||
NS_IMPL_ADDREF_INHERITED(InstallEvent, ExtendableEvent)
|
||||
NS_IMPL_RELEASE_INHERITED(InstallEvent, ExtendableEvent)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(InstallEvent)
|
||||
NS_INTERFACE_MAP_END_INHERITING(InstallPhaseEvent)
|
||||
NS_INTERFACE_MAP_END_INHERITING(ExtendableEvent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(InstallEvent, InstallPhaseEvent, mActiveWorker)
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(InstallEvent, ExtendableEvent, mActiveWorker)
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
@ -7,7 +7,7 @@
|
||||
#define mozilla_dom_workers_serviceworkerevents_h__
|
||||
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/InstallPhaseEventBinding.h"
|
||||
#include "mozilla/dom/ExtendableEventBinding.h"
|
||||
#include "mozilla/dom/InstallEventBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -20,40 +20,37 @@ BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class ServiceWorker;
|
||||
|
||||
bool
|
||||
ServiceWorkerEventsVisible(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
class InstallPhaseEvent : public Event
|
||||
class ExtendableEvent : public Event
|
||||
{
|
||||
nsRefPtr<Promise> mPromise;
|
||||
|
||||
protected:
|
||||
explicit InstallPhaseEvent(mozilla::dom::EventTarget* aOwner);
|
||||
~InstallPhaseEvent() {}
|
||||
explicit ExtendableEvent(mozilla::dom::EventTarget* aOwner);
|
||||
~ExtendableEvent() {}
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InstallPhaseEvent, Event)
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ExtendableEvent, Event)
|
||||
NS_FORWARD_TO_EVENT
|
||||
|
||||
virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE
|
||||
{
|
||||
return mozilla::dom::InstallPhaseEventBinding::Wrap(aCx, this);
|
||||
return mozilla::dom::ExtendableEventBinding::Wrap(aCx, this);
|
||||
}
|
||||
|
||||
static already_AddRefed<InstallPhaseEvent>
|
||||
static already_AddRefed<ExtendableEvent>
|
||||
Constructor(mozilla::dom::EventTarget* aOwner,
|
||||
const nsAString& aType,
|
||||
const EventInit& aOptions)
|
||||
{
|
||||
nsRefPtr<InstallPhaseEvent> e = new InstallPhaseEvent(aOwner);
|
||||
nsRefPtr<ExtendableEvent> e = new ExtendableEvent(aOwner);
|
||||
bool trusted = e->Init(aOwner);
|
||||
e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
|
||||
e->SetTrusted(trusted);
|
||||
return e.forget();
|
||||
}
|
||||
|
||||
static already_AddRefed<InstallPhaseEvent>
|
||||
static already_AddRefed<ExtendableEvent>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aType,
|
||||
const EventInit& aOptions,
|
||||
@ -74,7 +71,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class InstallEvent MOZ_FINAL : public InstallPhaseEvent
|
||||
class InstallEvent MOZ_FINAL : public ExtendableEvent
|
||||
{
|
||||
// FIXME(nsm): Bug 982787 will allow actually populating this.
|
||||
nsRefPtr<ServiceWorker> mActiveWorker;
|
||||
@ -86,7 +83,7 @@ protected:
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InstallEvent, InstallPhaseEvent)
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InstallEvent, ExtendableEvent)
|
||||
NS_FORWARD_TO_EVENT
|
||||
|
||||
virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE
|
||||
|
@ -19,9 +19,11 @@
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
#include "mozilla/dom/ErrorEvent.h"
|
||||
#include "mozilla/dom/InstallEventBinding.h"
|
||||
#include "mozilla/dom/Navigator.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsTArray.h"
|
||||
@ -29,6 +31,7 @@
|
||||
#include "RuntimeService.h"
|
||||
#include "ServiceWorker.h"
|
||||
#include "ServiceWorkerClient.h"
|
||||
#include "ServiceWorkerContainer.h"
|
||||
#include "ServiceWorkerRegistration.h"
|
||||
#include "ServiceWorkerEvents.h"
|
||||
#include "WorkerInlines.h"
|
||||
@ -65,21 +68,20 @@ ServiceWorkerRegistrationInfo::Clear()
|
||||
{
|
||||
if (mInstallingWorker) {
|
||||
// FIXME(nsm): Terminate installing worker.
|
||||
// Bug 1043701 Set state to redundant.
|
||||
// Fire statechange.
|
||||
mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
mInstallingWorker = nullptr;
|
||||
// FIXME(nsm): Abort any inflight requests from installing worker.
|
||||
}
|
||||
|
||||
if (mWaitingWorker) {
|
||||
// FIXME(nsm): Bug 1043701 Set state to redundant.
|
||||
mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
// Fire statechange.
|
||||
mWaitingWorker = nullptr;
|
||||
mWaitingToActivate = false;
|
||||
}
|
||||
|
||||
if (mActiveWorker) {
|
||||
// FIXME(nsm): Bug 1043701 Set state to redundant.
|
||||
mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
mActiveWorker = nullptr;
|
||||
}
|
||||
|
||||
@ -234,6 +236,10 @@ public:
|
||||
virtual
|
||||
void UpdateFailed(nsresult aStatus)
|
||||
{ }
|
||||
|
||||
virtual
|
||||
void UpdateFailed(const ErrorEventInit& aDesc)
|
||||
{ }
|
||||
};
|
||||
|
||||
class ServiceWorkerResolveWindowPromiseOnUpdateCallback MOZ_FINAL : public ServiceWorkerUpdateFinishCallback
|
||||
@ -267,6 +273,84 @@ public:
|
||||
{
|
||||
mPromise->MaybeReject(aStatus);
|
||||
}
|
||||
|
||||
void
|
||||
UpdateFailed(const ErrorEventInit& aErrorDesc) MOZ_OVERRIDE
|
||||
{
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init(mWindow);
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
JS::Rooted<JSString*> stack(cx, JS_GetEmptyString(JS_GetRuntime(cx)));
|
||||
|
||||
JS::Rooted<JS::Value> fnval(cx);
|
||||
if (!ToJSValue(cx, aErrorDesc.mFilename, &fnval)) {
|
||||
JS_ClearPendingException(cx);
|
||||
mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
JS::Rooted<JSString*> fn(cx, fnval.toString());
|
||||
|
||||
JS::Rooted<JS::Value> msgval(cx);
|
||||
if (!ToJSValue(cx, aErrorDesc.mMessage, &msgval)) {
|
||||
JS_ClearPendingException(cx);
|
||||
mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
JS::Rooted<JSString*> msg(cx, msgval.toString());
|
||||
|
||||
JS::Rooted<JS::Value> error(cx);
|
||||
if (!JS::CreateError(cx, JSEXN_ERR, stack, fn, aErrorDesc.mLineno,
|
||||
aErrorDesc.mColno, nullptr, msg, &error)) {
|
||||
JS_ClearPendingException(cx);
|
||||
mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
mPromise->MaybeReject(cx, error);
|
||||
}
|
||||
};
|
||||
|
||||
class ContinueUpdateRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
nsMainThreadPtrHandle<nsISupports> mJob;
|
||||
public:
|
||||
explicit ContinueUpdateRunnable(const nsMainThreadPtrHandle<nsISupports> aJob)
|
||||
: mJob(aJob)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
}
|
||||
|
||||
NS_IMETHOD Run();
|
||||
};
|
||||
|
||||
class CheckWorkerEvaluationAndContinueUpdateWorkerRunnable MOZ_FINAL : public WorkerRunnable
|
||||
{
|
||||
const nsMainThreadPtrHandle<nsISupports> mJob;
|
||||
public:
|
||||
CheckWorkerEvaluationAndContinueUpdateWorkerRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
const nsMainThreadPtrHandle<nsISupports> aJob)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
||||
, mJob(aJob)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
if (aWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
|
||||
nsRefPtr<ContinueUpdateRunnable> r = new ContinueUpdateRunnable(mJob);
|
||||
nsresult rv = NS_DispatchToMainThread(r);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to dispatch ContinueUpdateRunnable to main thread.");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ServiceWorkerRegisterJob MOZ_FINAL : public ServiceWorkerJob,
|
||||
@ -325,7 +409,7 @@ public:
|
||||
|
||||
if (mRegistration) {
|
||||
nsRefPtr<ServiceWorkerInfo> newest = mRegistration->Newest();
|
||||
if (newest && mScriptSpec.Equals(newest->GetScriptSpec()) &&
|
||||
if (newest && mScriptSpec.Equals(newest->ScriptSpec()) &&
|
||||
mScriptSpec.Equals(mRegistration->mScriptSpec)) {
|
||||
mRegistration->mPendingUninstall = false;
|
||||
Succeed();
|
||||
@ -378,10 +462,104 @@ public:
|
||||
// FIXME(nsm): "Extract mime type..."
|
||||
// FIXME(nsm): Byte match to aString.
|
||||
NS_WARNING("Byte wise check is disabled, just using new one");
|
||||
ContinueInstall();
|
||||
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
|
||||
swm->GetDomainInfo(mRegistration->mScope);
|
||||
MOZ_ASSERT(domainInfo);
|
||||
MOZ_ASSERT(!domainInfo->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
|
||||
domainInfo->mSetOfScopesBeingUpdated.Put(mRegistration->mScope, true);
|
||||
// We have to create a ServiceWorker here simply to ensure there are no
|
||||
// errors. Ideally we should just pass this worker on to ContinueInstall.
|
||||
nsRefPtr<ServiceWorker> serviceWorker;
|
||||
rv = swm->CreateServiceWorker(mRegistration->mScriptSpec,
|
||||
mRegistration->mScope,
|
||||
getter_AddRefs(serviceWorker));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Fail(NS_ERROR_DOM_ABORT_ERR);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerJob> upcasted = this;
|
||||
nsMainThreadPtrHandle<nsISupports> handle(
|
||||
new nsMainThreadPtrHolder<nsISupports>(upcasted));
|
||||
|
||||
nsRefPtr<CheckWorkerEvaluationAndContinueUpdateWorkerRunnable> r =
|
||||
new CheckWorkerEvaluationAndContinueUpdateWorkerRunnable(serviceWorker->GetWorkerPrivate(), handle);
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
bool ok = r->Dispatch(jsapi.cx());
|
||||
if (NS_WARN_IF(!ok)) {
|
||||
Fail(NS_ERROR_DOM_ABORT_ERR);
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Public so our error handling code can use it.
|
||||
void
|
||||
Fail(const ErrorEventInit& aError)
|
||||
{
|
||||
MOZ_ASSERT(mCallback);
|
||||
mCallback->UpdateFailed(aError);
|
||||
mCallback = nullptr;
|
||||
Done(NS_ERROR_DOM_JS_EXCEPTION);
|
||||
}
|
||||
|
||||
// Public so our error handling code can continue with a successful worker.
|
||||
void
|
||||
ContinueInstall()
|
||||
{
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
|
||||
swm->GetDomainInfo(mRegistration->mScope);
|
||||
MOZ_ASSERT(domainInfo);
|
||||
MOZ_ASSERT(domainInfo->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
|
||||
domainInfo->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
|
||||
|
||||
if (mRegistration->mInstallingWorker) {
|
||||
// FIXME(nsm): Terminate and stuff
|
||||
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
}
|
||||
|
||||
swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
|
||||
WhichServiceWorker::INSTALLING_WORKER);
|
||||
mRegistration->mInstallingWorker = new ServiceWorkerInfo(mRegistration, mRegistration->mScriptSpec);
|
||||
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
|
||||
|
||||
Succeed();
|
||||
|
||||
nsRefPtr<QueueFireUpdateFoundRunnable> upr =
|
||||
new QueueFireUpdateFoundRunnable(mRegistration);
|
||||
NS_DispatchToMainThread(upr);
|
||||
|
||||
// XXXnsm this leads to double fetches right now, ideally we'll be able to
|
||||
// use the persistent cache later.
|
||||
nsRefPtr<ServiceWorkerJob> upcasted = this;
|
||||
nsMainThreadPtrHandle<nsISupports> handle(
|
||||
new nsMainThreadPtrHolder<nsISupports>(upcasted));
|
||||
|
||||
nsRefPtr<ServiceWorker> serviceWorker;
|
||||
nsresult rv =
|
||||
swm->CreateServiceWorker(mRegistration->mInstallingWorker->ScriptSpec(),
|
||||
mRegistration->mScope,
|
||||
getter_AddRefs(serviceWorker));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ContinueAfterInstallEvent(false /* success */, false /* activate immediately */);
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<InstallEventRunnable> r =
|
||||
new InstallEventRunnable(serviceWorker->GetWorkerPrivate(), handle, mRegistration->mScope);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
r->Dispatch(jsapi.cx());
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
Update()
|
||||
@ -400,7 +578,7 @@ private:
|
||||
AssertIsOnMainThread();
|
||||
if (mRegistration->mInstallingWorker) {
|
||||
// FIXME(nsm): "Terminate installing worker".
|
||||
// "Run the update state"
|
||||
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
mRegistration->mInstallingWorker = nullptr;
|
||||
}
|
||||
|
||||
@ -468,50 +646,6 @@ private:
|
||||
Done(rv);
|
||||
}
|
||||
|
||||
void
|
||||
ContinueInstall()
|
||||
{
|
||||
if (mRegistration->mInstallingWorker) {
|
||||
// FIXME(nsm): Terminate and stuff
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
|
||||
WhichServiceWorker::INSTALLING_WORKER);
|
||||
mRegistration->mInstallingWorker = new ServiceWorkerInfo(mRegistration->mScriptSpec);
|
||||
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
|
||||
|
||||
Succeed();
|
||||
|
||||
nsRefPtr<QueueFireUpdateFoundRunnable> upr =
|
||||
new QueueFireUpdateFoundRunnable(mRegistration);
|
||||
NS_DispatchToMainThread(upr);
|
||||
|
||||
// XXXnsm this leads to double fetches right now, ideally we'll be able to
|
||||
// use the persistent cache later.
|
||||
nsRefPtr<ServiceWorkerJob> upcasted = this;
|
||||
nsMainThreadPtrHandle<nsISupports> handle(
|
||||
new nsMainThreadPtrHolder<nsISupports>(upcasted));
|
||||
|
||||
nsRefPtr<ServiceWorker> serviceWorker;
|
||||
nsresult rv =
|
||||
swm->CreateServiceWorker(mRegistration->mInstallingWorker->GetScriptSpec(),
|
||||
mRegistration->mScope,
|
||||
getter_AddRefs(serviceWorker));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// FIXME(nsm):
|
||||
MOZ_CRASH("Shouldn't happen yet");
|
||||
}
|
||||
|
||||
nsRefPtr<InstallEventRunnable> r =
|
||||
new InstallEventRunnable(serviceWorker->GetWorkerPrivate(), handle, mRegistration->mScope);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
r->Dispatch(jsapi.cx());
|
||||
}
|
||||
|
||||
void
|
||||
ContinueAfterInstallEvent(bool aSuccess, bool aActivateImmediately)
|
||||
{
|
||||
@ -540,9 +674,15 @@ private:
|
||||
// FIXME(nsm): Terminate
|
||||
mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
}
|
||||
|
||||
// Although the spec first sets waiting worker and then updates its state,
|
||||
// our ServiceWorkerInfo does not hold a list of associated ServiceWorker
|
||||
// objects in content JS. This means if we want to fire an event on
|
||||
// ServiceWorkerRegistration.installing, we need to do it first, before
|
||||
// swapping it with waiting worker.
|
||||
mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installed);
|
||||
mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget();
|
||||
mRegistration->mWaitingToActivate = false;
|
||||
mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
|
||||
swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
|
||||
WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER);
|
||||
|
||||
@ -553,7 +693,17 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(ServiceWorkerRegisterJob, ServiceWorkerJob, nsIStreamLoaderObserver)
|
||||
NS_IMPL_ISUPPORTS_INHERITED(ServiceWorkerRegisterJob, ServiceWorkerJob, nsIStreamLoaderObserver);
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContinueUpdateRunnable::Run()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsRefPtr<ServiceWorkerJob> job = static_cast<ServiceWorkerJob*>(mJob.get());
|
||||
nsRefPtr<ServiceWorkerRegisterJob> upjob = static_cast<ServiceWorkerRegisterJob*>(job.get());
|
||||
upjob->ContinueInstall();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we return an error code here, the ServiceWorkerContainer will
|
||||
// automatically reject the Promise.
|
||||
@ -778,15 +928,22 @@ InstallEventRunnable::DispatchInstallEvent(JSContext* aCx, WorkerPrivate* aWorke
|
||||
result = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> sgo = aWorkerPrivate->GlobalScope();
|
||||
if (!result.Failed()) {
|
||||
WidgetEvent* internalEvent = event->GetInternalNSEvent();
|
||||
if (!result.Failed() && !internalEvent->mFlags.mExceptionHasBeenRisen) {
|
||||
waitUntilPromise = event->GetPromise();
|
||||
if (!waitUntilPromise) {
|
||||
ErrorResult result;
|
||||
waitUntilPromise =
|
||||
Promise::Resolve(sgo, aCx, JS::UndefinedHandleValue, result);
|
||||
if (NS_WARN_IF(result.Failed())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Continue with a canceled install.
|
||||
// Although the spec has different routines to deal with popping stuff
|
||||
// off it's internal queues, we can reuse the ContinueAfterInstallEvent()
|
||||
// logic.
|
||||
waitUntilPromise = Promise::Reject(sgo, aCx,
|
||||
JS::UndefinedHandleValue, result);
|
||||
}
|
||||
@ -794,7 +951,6 @@ InstallEventRunnable::DispatchInstallEvent(JSContext* aCx, WorkerPrivate* aWorke
|
||||
if (result.Failed()) {
|
||||
return false;
|
||||
}
|
||||
// FIXME(nsm): handle script errors.
|
||||
|
||||
nsRefPtr<FinishInstallHandler> handler =
|
||||
new FinishInstallHandler(mJob, event->ActivateImmediately());
|
||||
@ -887,8 +1043,8 @@ private:
|
||||
EventInit init;
|
||||
init.mBubbles = false;
|
||||
init.mCancelable = true;
|
||||
nsRefPtr<InstallPhaseEvent> event =
|
||||
InstallPhaseEvent::Constructor(target, NS_LITERAL_STRING("activate"), init);
|
||||
nsRefPtr<ExtendableEvent> event =
|
||||
ExtendableEvent::Constructor(target, NS_LITERAL_STRING("activate"), init);
|
||||
|
||||
event->SetTrusted(true);
|
||||
|
||||
@ -897,7 +1053,8 @@ private:
|
||||
// FIXME(nsm): Install error handler for any listener errors.
|
||||
ErrorResult result;
|
||||
result = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
|
||||
if (!result.Failed()) {
|
||||
WidgetEvent* internalEvent = event->GetInternalNSEvent();
|
||||
if (!result.Failed() && !internalEvent->mFlags.mExceptionHasBeenRisen) {
|
||||
waitUntilPromise = event->GetPromise();
|
||||
if (!waitUntilPromise) {
|
||||
nsCOMPtr<nsIGlobalObject> global =
|
||||
@ -973,12 +1130,12 @@ ServiceWorkerRegistrationInfo::Activate()
|
||||
MOZ_ASSERT(mActiveWorker);
|
||||
nsRefPtr<ServiceWorker> serviceWorker;
|
||||
nsresult rv =
|
||||
swm->CreateServiceWorker(mActiveWorker->GetScriptSpec(),
|
||||
swm->CreateServiceWorker(mActiveWorker->ScriptSpec(),
|
||||
mScope,
|
||||
getter_AddRefs(serviceWorker));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// FIXME(nsm): Do the same thing as when "activate" event failed to
|
||||
// dispatch.
|
||||
FinishActivate(false /* success */);
|
||||
return;
|
||||
}
|
||||
|
||||
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> handle(
|
||||
@ -1413,10 +1570,10 @@ ServiceWorkerManager::FinishFetch(ServiceWorkerRegistrationInfo* aRegistration)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
ServiceWorkerManager::HandleError(JSContext* aCx,
|
||||
const nsACString& aScope,
|
||||
const nsAString& aWorkerURL,
|
||||
const nsCString& aScope,
|
||||
const nsString& aWorkerURL,
|
||||
nsString aMessage,
|
||||
nsString aFilename,
|
||||
nsString aLine,
|
||||
@ -1426,21 +1583,20 @@ ServiceWorkerManager::HandleError(JSContext* aCx,
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), aScope, nullptr, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aScope);
|
||||
MOZ_ASSERT(domainInfo);
|
||||
|
||||
if (!domainInfo->mSetOfScopesBeingUpdated.Contains(aScope)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(uri);
|
||||
if (!domainInfo) {
|
||||
return;
|
||||
}
|
||||
domainInfo->mSetOfScopesBeingUpdated.Remove(aScope);
|
||||
|
||||
nsCString scope;
|
||||
scope.Assign(aScope);
|
||||
nsRefPtr<ServiceWorkerRegistrationInfo> registration = domainInfo->GetRegistration(scope);
|
||||
MOZ_ASSERT(registration);
|
||||
ServiceWorkerJobQueue* queue = domainInfo->mJobQueues.Get(aScope);
|
||||
MOZ_ASSERT(queue);
|
||||
ServiceWorkerJob* job = queue->Peek();
|
||||
ServiceWorkerRegisterJob* regJob = static_cast<ServiceWorkerRegisterJob*>(job);
|
||||
MOZ_ASSERT(regJob);
|
||||
|
||||
RootedDictionary<ErrorEventInit> init(aCx);
|
||||
init.mMessage = aMessage;
|
||||
@ -1448,7 +1604,8 @@ ServiceWorkerManager::HandleError(JSContext* aCx,
|
||||
init.mLineno = aLineNumber;
|
||||
init.mColno = aColumnNumber;
|
||||
|
||||
MOZ_CRASH("FIX THIS");
|
||||
regJob->Fail(init);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
@ -1463,6 +1620,48 @@ ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::QueueStateChangeEvent(ServiceWorkerInfo* aInfo,
|
||||
ServiceWorkerState aState) const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aInfo);
|
||||
MOZ_ASSERT(aInfo == mInstallingWorker ||
|
||||
aInfo == mWaitingWorker ||
|
||||
aInfo == mActiveWorker);
|
||||
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
|
||||
swm->GetDomainInfo(mScope);
|
||||
|
||||
if (domainInfo) {
|
||||
WhichServiceWorker whichOne;
|
||||
if (aInfo == mInstallingWorker) {
|
||||
whichOne = WhichServiceWorker::INSTALLING_WORKER;
|
||||
} else if (aInfo == mWaitingWorker) {
|
||||
whichOne = WhichServiceWorker::WAITING_WORKER;
|
||||
} else if (aInfo == mActiveWorker) {
|
||||
whichOne = WhichServiceWorker::ACTIVE_WORKER;
|
||||
} else {
|
||||
MOZ_CRASH("Hit unexpected case");
|
||||
}
|
||||
|
||||
// Refactor this iteration pattern across this and 2 other call-sites.
|
||||
nsTObserverArray<ServiceWorkerRegistration*>::ForwardIterator it(domainInfo->mServiceWorkerRegistrations);
|
||||
while (it.HasMore()) {
|
||||
nsRefPtr<ServiceWorkerRegistration> target = it.GetNext();
|
||||
nsAutoString regScope;
|
||||
target->GetScope(regScope);
|
||||
MOZ_ASSERT(!regScope.IsEmpty());
|
||||
|
||||
NS_ConvertUTF16toUTF8 utf8Scope(regScope);
|
||||
if (utf8Scope.Equals(mScope)) {
|
||||
target->QueueStateChangeEvent(whichOne, aState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
|
||||
const nsACString& aScriptSpec,
|
||||
@ -1689,14 +1888,19 @@ ServiceWorkerManager::GetScopeForUrl(const nsAString& aUrl, nsAString& aScope)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::AddRegistrationEventListener(nsIURI* aDocumentURI, nsIDOMEventTarget* aListener)
|
||||
ServiceWorkerManager::AddRegistrationEventListener(const nsAString& aScope, nsIDOMEventTarget* aListener)
|
||||
{
|
||||
MOZ_ASSERT(aDocumentURI);
|
||||
AssertIsOnMainThread();
|
||||
nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aDocumentURI);
|
||||
nsAutoCString scope = NS_ConvertUTF16toUTF8(aScope);
|
||||
nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(scope);
|
||||
if (!domainInfo) {
|
||||
nsCOMPtr<nsIURI> scopeAsURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(scopeAsURI), scope, nullptr, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
nsAutoCString domain;
|
||||
nsresult rv = aDocumentURI->GetHost(domain);
|
||||
rv = scopeAsURI->GetHost(domain);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -1710,22 +1914,36 @@ ServiceWorkerManager::AddRegistrationEventListener(nsIURI* aDocumentURI, nsIDOME
|
||||
// TODO: this is very very bad:
|
||||
ServiceWorkerRegistration* registration = static_cast<ServiceWorkerRegistration*>(aListener);
|
||||
MOZ_ASSERT(!domainInfo->mServiceWorkerRegistrations.Contains(registration));
|
||||
#ifdef DEBUG
|
||||
// Ensure a registration is only listening for it's own scope.
|
||||
nsAutoString regScope;
|
||||
registration->GetScope(regScope);
|
||||
MOZ_ASSERT(!regScope.IsEmpty());
|
||||
MOZ_ASSERT(scope.Equals(NS_ConvertUTF16toUTF8(regScope)));
|
||||
#endif
|
||||
domainInfo->mServiceWorkerRegistrations.AppendElement(registration);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerManager::RemoveRegistrationEventListener(nsIURI* aDocumentURI, nsIDOMEventTarget* aListener)
|
||||
ServiceWorkerManager::RemoveRegistrationEventListener(const nsAString& aScope, nsIDOMEventTarget* aListener)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aDocumentURI);
|
||||
nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aDocumentURI);
|
||||
nsCString scope = NS_ConvertUTF16toUTF8(aScope);
|
||||
nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(scope);
|
||||
if (!domainInfo) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
ServiceWorkerRegistration* registration = static_cast<ServiceWorkerRegistration*>(aListener);
|
||||
MOZ_ASSERT(domainInfo->mServiceWorkerRegistrations.Contains(registration));
|
||||
#ifdef DEBUG
|
||||
// Ensure a registration is unregistering for it's own scope.
|
||||
nsAutoString regScope;
|
||||
registration->GetScope(regScope);
|
||||
MOZ_ASSERT(!regScope.IsEmpty());
|
||||
MOZ_ASSERT(scope.Equals(NS_ConvertUTF16toUTF8(regScope)));
|
||||
#endif
|
||||
domainInfo->mServiceWorkerRegistrations.RemoveElement(registration);
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1737,31 +1955,23 @@ ServiceWorkerManager::FireEventOnServiceWorkerRegistrations(
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsRefPtr<ServiceWorkerDomainInfo> domainInfo =
|
||||
GetDomainInfo(aRegistration->mScriptSpec);
|
||||
GetDomainInfo(aRegistration->mScope);
|
||||
|
||||
if (domainInfo) {
|
||||
nsTObserverArray<ServiceWorkerRegistration*>::ForwardIterator it(domainInfo->mServiceWorkerRegistrations);
|
||||
while (it.HasMore()) {
|
||||
nsRefPtr<ServiceWorkerRegistration> target = it.GetNext();
|
||||
nsIURI* targetURI = target->GetDocumentURI();
|
||||
if (!targetURI) {
|
||||
NS_WARNING("Controlled domain cannot have page with null URI!");
|
||||
continue;
|
||||
}
|
||||
nsAutoString regScope;
|
||||
target->GetScope(regScope);
|
||||
MOZ_ASSERT(!regScope.IsEmpty());
|
||||
|
||||
nsCString path;
|
||||
nsresult rv = targetURI->GetSpec(path);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
continue;
|
||||
NS_ConvertUTF16toUTF8 utf8Scope(regScope);
|
||||
if (utf8Scope.Equals(aRegistration->mScope)) {
|
||||
nsresult rv = target->DispatchTrustedEvent(aName);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// Warn only.
|
||||
}
|
||||
}
|
||||
|
||||
nsCString scope = FindScopeForPath(domainInfo->mOrderedScopes, path);
|
||||
if (scope.IsEmpty() ||
|
||||
!scope.Equals(aRegistration->mScope)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
target->DispatchTrustedEvent(aName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1787,7 +1997,7 @@ ServiceWorkerManager::GetServiceWorkerForScope(nsIDOMWindow* aWindow,
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Security check
|
||||
nsCString scope = NS_ConvertUTF16toUTF8(aScope);
|
||||
nsAutoCString scope = NS_ConvertUTF16toUTF8(aScope);
|
||||
nsCOMPtr<nsIURI> scopeURI;
|
||||
// We pass nullptr as the base URI since scopes obtained from
|
||||
// ServiceWorkerRegistrations MUST be fully qualified URIs.
|
||||
@ -1832,13 +2042,14 @@ ServiceWorkerManager::GetServiceWorkerForScope(nsIDOMWindow* aWindow,
|
||||
|
||||
nsRefPtr<ServiceWorker> serviceWorker;
|
||||
rv = CreateServiceWorkerForWindow(window,
|
||||
info->GetScriptSpec(),
|
||||
info->ScriptSpec(),
|
||||
registration->mScope,
|
||||
getter_AddRefs(serviceWorker));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
serviceWorker->SetState(info->State());
|
||||
serviceWorker.forget(aServiceWorker);
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1876,7 +2087,7 @@ ServiceWorkerManager::GetDocumentController(nsIDOMWindow* aWindow, nsISupports**
|
||||
|
||||
nsRefPtr<ServiceWorker> serviceWorker;
|
||||
nsresult rv = CreateServiceWorkerForWindow(window,
|
||||
registration->mActiveWorker->GetScriptSpec(),
|
||||
registration->mActiveWorker->ScriptSpec(),
|
||||
registration->mScope,
|
||||
getter_AddRefs(serviceWorker));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
@ -1990,21 +2201,15 @@ ServiceWorkerManager::InvalidateServiceWorkerRegistrationWorker(ServiceWorkerReg
|
||||
nsTObserverArray<ServiceWorkerRegistration*>::ForwardIterator it(domainInfo->mServiceWorkerRegistrations);
|
||||
while (it.HasMore()) {
|
||||
nsRefPtr<ServiceWorkerRegistration> target = it.GetNext();
|
||||
nsAutoString regScope;
|
||||
target->GetScope(regScope);
|
||||
MOZ_ASSERT(!regScope.IsEmpty());
|
||||
|
||||
nsIURI* targetURI = target->GetDocumentURI();
|
||||
nsCString path;
|
||||
nsresult rv = targetURI->GetSpec(path);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
continue;
|
||||
NS_ConvertUTF16toUTF8 utf8Scope(regScope);
|
||||
|
||||
if (utf8Scope.Equals(aRegistration->mScope)) {
|
||||
target->InvalidateWorkerReference(aWhichOnes);
|
||||
}
|
||||
|
||||
nsCString scope = FindScopeForPath(domainInfo->mOrderedScopes, path);
|
||||
if (scope.IsEmpty() ||
|
||||
!scope.Equals(aRegistration->mScope)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
target->InvalidateWorkerReference(aWhichOnes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2082,6 +2287,45 @@ EnumControlledDocuments(nsISupports* aKey,
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
FireControllerChangeOnMatchingDocument(nsISupports* aKey,
|
||||
ServiceWorkerRegistrationInfo* aValue,
|
||||
void* aData)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
ServiceWorkerRegistrationInfo* contextReg = static_cast<ServiceWorkerRegistrationInfo*>(aData);
|
||||
if (aValue != contextReg) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aKey);
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> w = doc->GetWindow();
|
||||
MOZ_ASSERT(w);
|
||||
auto* window = static_cast<nsGlobalWindow*>(w.get());
|
||||
if (NS_WARN_IF(!window)) {
|
||||
NS_WARNING("No valid nsGlobalWindow");
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
ErrorResult result;
|
||||
dom::Navigator* navigator = window->GetNavigator(result);
|
||||
if (NS_WARN_IF(result.Failed())) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerContainer> container = navigator->ServiceWorker();
|
||||
result = container->DispatchTrustedEvent(NS_LITERAL_STRING("controllerchange"));
|
||||
if (result.Failed()) {
|
||||
NS_WARNING("Failed to dispatch controllerchange event");
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
@ -2106,6 +2350,9 @@ ServiceWorkerManager::GetServicedClients(const nsCString& aScope,
|
||||
void
|
||||
ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
|
||||
{
|
||||
// FIXME(nsm): Fill this out.
|
||||
nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aRegistration->mScope);
|
||||
MOZ_ASSERT(domainInfo);
|
||||
domainInfo->mControlledDocuments.EnumerateRead(FireControllerChangeOnMatchingDocument,
|
||||
aRegistration);
|
||||
}
|
||||
END_WORKERS_NAMESPACE
|
||||
|
@ -18,10 +18,11 @@
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState
|
||||
#include "mozilla/dom/ServiceWorkerCommon.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
#include "nsTObserverArray.h"
|
||||
#include "nsClassHashtable.h"
|
||||
|
||||
class nsIScriptError;
|
||||
|
||||
@ -33,54 +34,7 @@ class ServiceWorkerRegistration;
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorker;
|
||||
|
||||
/*
|
||||
* Wherever the spec treats a worker instance and a description of said worker
|
||||
* as the same thing; i.e. "Resolve foo with
|
||||
* _GetNewestWorker(serviceWorkerRegistration)", we represent the description
|
||||
* by this class and spawn a ServiceWorker in the right global when required.
|
||||
*/
|
||||
class ServiceWorkerInfo MOZ_FINAL
|
||||
{
|
||||
nsCString mScriptSpec;
|
||||
ServiceWorkerState mState;
|
||||
|
||||
~ServiceWorkerInfo()
|
||||
{ }
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerInfo)
|
||||
|
||||
const nsCString&
|
||||
GetScriptSpec() const
|
||||
{
|
||||
return mScriptSpec;
|
||||
}
|
||||
|
||||
explicit ServiceWorkerInfo(const nsACString& aScriptSpec)
|
||||
: mScriptSpec(aScriptSpec)
|
||||
, mState(ServiceWorkerState::EndGuard_)
|
||||
{ }
|
||||
|
||||
void
|
||||
UpdateState(ServiceWorkerState aState)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// Any state can directly transition to redundant, but everything else is
|
||||
// ordered.
|
||||
if (aState != ServiceWorkerState::Redundant) {
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::EndGuard_, aState == ServiceWorkerState::Installing);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Installing, aState == ServiceWorkerState::Installed);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Installed, aState == ServiceWorkerState::Activating);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Activating, aState == ServiceWorkerState::Activated);
|
||||
}
|
||||
// Activated can only go to redundant.
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Activated, aState == ServiceWorkerState::Redundant);
|
||||
#endif
|
||||
mState = aState;
|
||||
// FIXME(nsm): Inform all relevant ServiceWorker instances.
|
||||
}
|
||||
};
|
||||
class ServiceWorkerInfo;
|
||||
|
||||
class ServiceWorkerJobQueue;
|
||||
|
||||
@ -117,8 +71,9 @@ class ServiceWorkerJobQueue MOZ_FINAL
|
||||
public:
|
||||
~ServiceWorkerJobQueue()
|
||||
{
|
||||
// FIXME(nsm): Clean up jobs.
|
||||
MOZ_ASSERT(mJobs.IsEmpty());
|
||||
if (!mJobs.IsEmpty()) {
|
||||
NS_WARNING("Pending/running jobs still around on shutdown!");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -133,6 +88,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Only used by HandleError, keep it that way!
|
||||
ServiceWorkerJob*
|
||||
Peek()
|
||||
{
|
||||
MOZ_ASSERT(!mJobs.IsEmpty());
|
||||
return mJobs[0];
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
Pop()
|
||||
@ -225,6 +188,70 @@ public:
|
||||
|
||||
void
|
||||
FinishActivate(bool aSuccess);
|
||||
|
||||
void
|
||||
QueueStateChangeEvent(ServiceWorkerInfo* aInfo,
|
||||
ServiceWorkerState aState) const;
|
||||
};
|
||||
|
||||
/*
|
||||
* Wherever the spec treats a worker instance and a description of said worker
|
||||
* as the same thing; i.e. "Resolve foo with
|
||||
* _GetNewestWorker(serviceWorkerRegistration)", we represent the description
|
||||
* by this class and spawn a ServiceWorker in the right global when required.
|
||||
*/
|
||||
class ServiceWorkerInfo MOZ_FINAL
|
||||
{
|
||||
private:
|
||||
const ServiceWorkerRegistrationInfo* mRegistration;
|
||||
nsCString mScriptSpec;
|
||||
ServiceWorkerState mState;
|
||||
|
||||
~ServiceWorkerInfo()
|
||||
{ }
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerInfo)
|
||||
|
||||
const nsCString&
|
||||
ScriptSpec() const
|
||||
{
|
||||
return mScriptSpec;
|
||||
}
|
||||
|
||||
explicit ServiceWorkerInfo(ServiceWorkerRegistrationInfo* aReg,
|
||||
const nsACString& aScriptSpec)
|
||||
: mRegistration(aReg)
|
||||
, mScriptSpec(aScriptSpec)
|
||||
, mState(ServiceWorkerState::EndGuard_)
|
||||
{
|
||||
MOZ_ASSERT(mRegistration);
|
||||
}
|
||||
|
||||
ServiceWorkerState
|
||||
State() const
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
void
|
||||
UpdateState(ServiceWorkerState aState)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// Any state can directly transition to redundant, but everything else is
|
||||
// ordered.
|
||||
if (aState != ServiceWorkerState::Redundant) {
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::EndGuard_, aState == ServiceWorkerState::Installing);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Installing, aState == ServiceWorkerState::Installed);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Installed, aState == ServiceWorkerState::Activating);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Activating, aState == ServiceWorkerState::Activated);
|
||||
}
|
||||
// Activated can only go to redundant.
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Activated, aState == ServiceWorkerState::Redundant);
|
||||
#endif
|
||||
mState = aState;
|
||||
mRegistration->QueueStateChangeEvent(this, mState);
|
||||
}
|
||||
};
|
||||
|
||||
#define NS_SERVICEWORKERMANAGER_IMPL_IID \
|
||||
@ -293,6 +320,8 @@ public:
|
||||
|
||||
nsClassHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues;
|
||||
|
||||
nsDataHashtable<nsCStringHashKey, bool> mSetOfScopesBeingUpdated;
|
||||
|
||||
ServiceWorkerDomainInfo()
|
||||
{ }
|
||||
|
||||
@ -342,11 +371,12 @@ public:
|
||||
void
|
||||
FinishFetch(ServiceWorkerRegistrationInfo* aRegistration);
|
||||
|
||||
|
||||
void
|
||||
// Returns true if the error was handled, false if normal worker error
|
||||
// handling should continue.
|
||||
bool
|
||||
HandleError(JSContext* aCx,
|
||||
const nsACString& aScope,
|
||||
const nsAString& aWorkerURL,
|
||||
const nsCString& aScope,
|
||||
const nsString& aWorkerURL,
|
||||
nsString aMessage,
|
||||
nsString aFilename,
|
||||
nsString aLine,
|
||||
|
@ -264,6 +264,28 @@ ServiceWorkerRegistration::InvalidateWorkerReference(WhichServiceWorker aWhichOn
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistration::QueueStateChangeEvent(WhichServiceWorker aWhichOne,
|
||||
ServiceWorkerState aState) const
|
||||
{
|
||||
nsRefPtr<ServiceWorker> worker;
|
||||
if (aWhichOne == WhichServiceWorker::INSTALLING_WORKER) {
|
||||
worker = mInstallingWorker;
|
||||
} else if (aWhichOne == WhichServiceWorker::WAITING_WORKER) {
|
||||
worker = mWaitingWorker;
|
||||
} else if (aWhichOne == WhichServiceWorker::ACTIVE_WORKER) {
|
||||
worker = mActiveWorker;
|
||||
} else {
|
||||
MOZ_CRASH("Invalid case");
|
||||
}
|
||||
|
||||
if (worker) {
|
||||
worker->SetState(aState);
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(worker, &ServiceWorker::DispatchStateChange);
|
||||
NS_DispatchToMainThread(r);
|
||||
}
|
||||
}
|
||||
|
||||
// XXXnsm, maybe this can be optimized to only add when a event handler is
|
||||
// registered.
|
||||
void
|
||||
@ -271,7 +293,7 @@ ServiceWorkerRegistration::StartListeningForEvents()
|
||||
{
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
if (swm) {
|
||||
swm->AddRegistrationEventListener(GetDocumentURI(), this);
|
||||
swm->AddRegistrationEventListener(mScope, this);
|
||||
mListeningForEvents = true;
|
||||
}
|
||||
}
|
||||
@ -285,17 +307,10 @@ ServiceWorkerRegistration::StopListeningForEvents()
|
||||
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
|
||||
if (swm) {
|
||||
swm->RemoveRegistrationEventListener(GetDocumentURI(), this);
|
||||
swm->RemoveRegistrationEventListener(mScope, this);
|
||||
mListeningForEvents = false;
|
||||
}
|
||||
}
|
||||
|
||||
nsIURI*
|
||||
ServiceWorkerRegistration::GetDocumentURI() const
|
||||
{
|
||||
MOZ_ASSERT(GetOwner());
|
||||
return GetOwner()->GetDocumentURI();
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
@ -55,13 +55,12 @@ public:
|
||||
Unregister(ErrorResult& aRv);
|
||||
|
||||
// Useful methods for ServiceWorkerManager:
|
||||
|
||||
nsIURI*
|
||||
GetDocumentURI() const;
|
||||
|
||||
void
|
||||
InvalidateWorkerReference(WhichServiceWorker aWhichOnes);
|
||||
|
||||
void
|
||||
QueueStateChangeEvent(WhichServiceWorker aWhichOne, ServiceWorkerState aState) const;
|
||||
|
||||
// DOMEventTargethelper
|
||||
virtual void DisconnectFromOwner() MOZ_OVERRIDE;
|
||||
|
||||
|
@ -1318,15 +1318,20 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aWorkerPrivate->IsServiceWorker()) {
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
MOZ_ASSERT(swm);
|
||||
swm->HandleError(aCx, aWorkerPrivate->SharedWorkerName(),
|
||||
aWorkerPrivate->ScriptURL(),
|
||||
mMessage,
|
||||
mFilename, mLine, mLineNumber, mColumnNumber, mFlags);
|
||||
return true;
|
||||
} else if (aWorkerPrivate->IsSharedWorker()) {
|
||||
if (aWorkerPrivate->IsServiceWorker() || aWorkerPrivate->IsSharedWorker()) {
|
||||
if (aWorkerPrivate->IsServiceWorker()) {
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
MOZ_ASSERT(swm);
|
||||
bool handled = swm->HandleError(aCx, aWorkerPrivate->SharedWorkerName(),
|
||||
aWorkerPrivate->ScriptURL(),
|
||||
mMessage,
|
||||
mFilename, mLine, mLineNumber,
|
||||
mColumnNumber, mFlags);
|
||||
if (handled) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, mMessage, mFilename,
|
||||
mLine, mLineNumber,
|
||||
mColumnNumber, mFlags);
|
||||
|
@ -494,10 +494,17 @@ class WorkerScopeUnregisterRunnable MOZ_FINAL : public nsRunnable
|
||||
, public WorkerFeature
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsRefPtr<Promise> mWorkerPromise;
|
||||
nsString mScope;
|
||||
|
||||
// Worker thread only.
|
||||
nsRefPtr<Promise> mWorkerPromise;
|
||||
bool mCleanedUp;
|
||||
|
||||
~WorkerScopeUnregisterRunnable()
|
||||
{
|
||||
MOZ_ASSERT(mCleanedUp);
|
||||
}
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
@ -505,8 +512,8 @@ public:
|
||||
Promise* aWorkerPromise,
|
||||
const nsAString& aScope)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
, mWorkerPromise(aWorkerPromise)
|
||||
, mScope(aScope)
|
||||
, mWorkerPromise(aWorkerPromise)
|
||||
, mCleanedUp(false)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
@ -515,6 +522,7 @@ public:
|
||||
|
||||
if (!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), this)) {
|
||||
MOZ_ASSERT(false, "cannot add the worker feature!");
|
||||
mWorkerPromise = nullptr;
|
||||
mCleanedUp = true;
|
||||
return;
|
||||
}
|
||||
@ -524,6 +532,7 @@ public:
|
||||
WorkerPromise() const
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(!mCleanedUp);
|
||||
return mWorkerPromise;
|
||||
}
|
||||
|
||||
@ -561,15 +570,10 @@ public:
|
||||
}
|
||||
|
||||
mWorkerPrivate->RemoveFeature(aCx, this);
|
||||
mWorkerPromise = nullptr;
|
||||
mCleanedUp = true;
|
||||
}
|
||||
|
||||
private:
|
||||
~WorkerScopeUnregisterRunnable()
|
||||
{
|
||||
MOZ_ASSERT(mCleanedUp);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
Run() MOZ_OVERRIDE
|
||||
{
|
||||
@ -599,8 +603,7 @@ private:
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(aStatus > workers::Running);
|
||||
|
||||
mCleanedUp = true;
|
||||
CleanUp(aCx);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@ -614,11 +617,10 @@ UnregisterResultRunnable::WorkerRun(JSContext* aCx,
|
||||
{
|
||||
if (mState == Failed) {
|
||||
mRunnable->WorkerPromise()->MaybeReject(aCx, JS::UndefinedHandleValue);
|
||||
mRunnable->CleanUp(aCx);
|
||||
return true;
|
||||
} else {
|
||||
mRunnable->WorkerPromise()->MaybeResolve(mValue);
|
||||
}
|
||||
|
||||
mRunnable->WorkerPromise()->MaybeResolve(mValue);
|
||||
mRunnable->CleanUp(aCx);
|
||||
return true;
|
||||
}
|
||||
@ -638,7 +640,13 @@ ServiceWorkerGlobalScope::Unregister(ErrorResult& aRv)
|
||||
|
||||
nsRefPtr<WorkerScopeUnregisterRunnable> runnable =
|
||||
new WorkerScopeUnregisterRunnable(mWorkerPrivate, promise, mScope);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
|
||||
// Ensure the AddFeature succeeded before dispatching.
|
||||
// Otherwise we let the promise remain pending since script is going to stop
|
||||
// soon anyway.
|
||||
if (runnable->WorkerPromise()) {
|
||||
NS_DispatchToMainThread(runnable);
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
EXPORTS.mozilla.dom += [
|
||||
'ServiceWorkerCommon.h',
|
||||
'ServiceWorkerContainer.h',
|
||||
'ServiceWorkerEvents.h',
|
||||
'ServiceWorkerRegistration.h',
|
||||
'WorkerPrivate.h',
|
||||
'WorkerRunnable.h',
|
||||
|
@ -44,7 +44,7 @@ function testClone() {
|
||||
|
||||
function testUsedRequest() {
|
||||
// Passing a used request should fail.
|
||||
var req = new Request("", { body: "This is foo" });
|
||||
var req = new Request("", { method: 'post', body: "This is foo" });
|
||||
var p1 = req.text().then(function(v) {
|
||||
try {
|
||||
var req2 = new Request(req);
|
||||
@ -55,7 +55,7 @@ function testUsedRequest() {
|
||||
});
|
||||
|
||||
// Passing a request should set the request as used.
|
||||
var reqA = new Request("", { body: "This is foo" });
|
||||
var reqA = new Request("", { method: 'post', body: "This is foo" });
|
||||
var reqB = new Request(reqA);
|
||||
is(reqA.bodyUsed, true, "Passing a Request to another Request should set the former as used");
|
||||
return p1;
|
||||
@ -133,6 +133,24 @@ function testMethod() {
|
||||
ok(true, "Method " + forbiddenNoCors[i] + " should be forbidden in no-cors mode");
|
||||
}
|
||||
}
|
||||
|
||||
// HEAD/GET requests cannot have a body.
|
||||
try {
|
||||
var r = new Request("", { method: "get", body: "hello" });
|
||||
ok(false, "HEAD/GET request cannot have a body");
|
||||
} catch(e) {
|
||||
is(e.name, "TypeError", "HEAD/GET request cannot have a body");
|
||||
}
|
||||
|
||||
try {
|
||||
var r = new Request("", { method: "head", body: "hello" });
|
||||
ok(false, "HEAD/GET request cannot have a body");
|
||||
} catch(e) {
|
||||
is(e.name, "TypeError", "HEAD/GET request cannot have a body");
|
||||
}
|
||||
|
||||
// Non HEAD/GET should not throw.
|
||||
var r = new Request("", { method: "patch", body: "hello" });
|
||||
}
|
||||
|
||||
function testUrlFragment() {
|
||||
@ -141,7 +159,7 @@ function testUrlFragment() {
|
||||
}
|
||||
|
||||
function testBodyUsed() {
|
||||
var req = new Request("./bodyused", { body: "Sample body" });
|
||||
var req = new Request("./bodyused", { method: 'post', body: "Sample body" });
|
||||
is(req.bodyUsed, false, "bodyUsed is initially false.");
|
||||
return req.text().then((v) => {
|
||||
is(v, "Sample body", "Body should match");
|
||||
@ -157,23 +175,23 @@ function testBodyUsed() {
|
||||
|
||||
function testBodyCreation() {
|
||||
var text = "κόσμε";
|
||||
var req1 = new Request("", { body: text });
|
||||
var req1 = new Request("", { method: 'post', body: text });
|
||||
var p1 = req1.text().then(function(v) {
|
||||
ok(typeof v === "string", "Should resolve to string");
|
||||
is(text, v, "Extracted string should match");
|
||||
});
|
||||
|
||||
var req2 = new Request("", { body: new Uint8Array([72, 101, 108, 108, 111]) });
|
||||
var req2 = new Request("", { method: 'post', body: new Uint8Array([72, 101, 108, 108, 111]) });
|
||||
var p2 = req2.text().then(function(v) {
|
||||
is("Hello", v, "Extracted string should match");
|
||||
});
|
||||
|
||||
var req2b = new Request("", { body: (new Uint8Array([72, 101, 108, 108, 111])).buffer });
|
||||
var req2b = new Request("", { method: 'post', body: (new Uint8Array([72, 101, 108, 108, 111])).buffer });
|
||||
var p2b = req2b.text().then(function(v) {
|
||||
is("Hello", v, "Extracted string should match");
|
||||
});
|
||||
|
||||
var reqblob = new Request("", { body: new Blob([text]) });
|
||||
var reqblob = new Request("", { method: 'post', body: new Blob([text]) });
|
||||
var pblob = reqblob.text().then(function(v) {
|
||||
is(v, text, "Extracted string should match");
|
||||
});
|
||||
@ -182,7 +200,7 @@ function testBodyCreation() {
|
||||
params.append("item", "Geckos");
|
||||
params.append("feature", "stickyfeet");
|
||||
params.append("quantity", "700");
|
||||
var req3 = new Request("", { body: params });
|
||||
var req3 = new Request("", { method: 'post', body: params });
|
||||
var p3 = req3.text().then(function(v) {
|
||||
var extracted = new URLSearchParams(v);
|
||||
is(extracted.get("item"), "Geckos", "Param should match");
|
||||
@ -195,7 +213,7 @@ function testBodyCreation() {
|
||||
|
||||
function testBodyExtraction() {
|
||||
var text = "κόσμε";
|
||||
var newReq = function() { return new Request("", { body: text }); }
|
||||
var newReq = function() { return new Request("", { method: 'post', body: text }); }
|
||||
return newReq().text().then(function(v) {
|
||||
ok(typeof v === "string", "Should resolve to string");
|
||||
is(text, v, "Extracted string should match");
|
||||
@ -206,12 +224,11 @@ function testBodyExtraction() {
|
||||
is(fs.readAsText(v), text, "Decoded Blob should match original");
|
||||
});
|
||||
}).then(function() {
|
||||
// FIXME(nsm): Enable once Bug 1107777 and Bug 1072144 have been fixed.
|
||||
//return newReq().json().then(function(v) {
|
||||
// ok(false, "Invalid json should reject");
|
||||
//}, function(e) {
|
||||
// ok(true, "Invalid json should reject");
|
||||
//})
|
||||
return newReq().json().then(function(v) {
|
||||
ok(false, "Invalid json should reject");
|
||||
}, function(e) {
|
||||
ok(true, "Invalid json should reject");
|
||||
})
|
||||
}).then(function() {
|
||||
return newReq().arrayBuffer().then(function(v) {
|
||||
ok(v instanceof ArrayBuffer, "Should resolve to ArrayBuffer");
|
||||
|
@ -118,12 +118,11 @@ function testBodyExtraction() {
|
||||
is(fs.readAsText(v), text, "Decoded Blob should match original");
|
||||
});
|
||||
}).then(function() {
|
||||
// FIXME(nsm): Enable once Bug 1107777 and Bug 1072144 have been fixed.
|
||||
//return newRes().json().then(function(v) {
|
||||
// ok(false, "Invalid json should reject");
|
||||
//}, function(e) {
|
||||
// ok(true, "Invalid json should reject");
|
||||
//})
|
||||
return newRes().json().then(function(v) {
|
||||
ok(false, "Invalid json should reject");
|
||||
}, function(e) {
|
||||
ok(true, "Invalid json should reject");
|
||||
})
|
||||
}).then(function() {
|
||||
return newRes().arrayBuffer().then(function(v) {
|
||||
ok(v instanceof ArrayBuffer, "Should resolve to ArrayBuffer");
|
||||
|
@ -0,0 +1,4 @@
|
||||
// Worker that errors on receiving an activate event.
|
||||
onactivate = function(e) {
|
||||
undefined.doSomething;
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
// Worker that errors on receiving an install event.
|
||||
oninstall = function(e) {
|
||||
undefined.doSomething;
|
||||
}
|
@ -5,14 +5,18 @@ support-files =
|
||||
worker2.js
|
||||
worker3.js
|
||||
parse_error_worker.js
|
||||
activate_event_error_worker.js
|
||||
install_event_worker.js
|
||||
install_event_error_worker.js
|
||||
simpleregister/index.html
|
||||
simpleregister/ready.html
|
||||
controller/index.html
|
||||
unregister/index.html
|
||||
unregister/unregister.html
|
||||
workerUpdate/update.html
|
||||
sw_clients/simple.html
|
||||
get_serviced_worker.js
|
||||
worker_unregister.js
|
||||
worker_update.js
|
||||
|
||||
[test_unregister.html]
|
||||
@ -27,3 +31,5 @@ skip-if = true # bug 1124743
|
||||
[test_controller.html]
|
||||
[test_workerUpdate.html]
|
||||
skip-if = true # Enable after Bug 982726 postMessage is landed.
|
||||
[test_workerUnregister.html]
|
||||
skip-if = true # Enable after Bug 982726 postMessage is landed.
|
||||
|
@ -19,7 +19,7 @@
|
||||
for (var i = 0; i < a.length; ++i) {
|
||||
window.parent.postMessage({ type: "check", status: a[i] instanceof ServiceWorkerRegistration,
|
||||
msg: "getRegistrations returns an array of ServiceWorkerRegistration objects" }, "*");
|
||||
if (a[i].scope.match(/simpleregister\/\*/)) {
|
||||
if (a[i].scope.match(/simpleregister\//)) {
|
||||
a[i].onupdatefound = function(e) {
|
||||
eventReceived();
|
||||
}
|
||||
|
@ -5,7 +5,9 @@
|
||||
|
||||
window.addEventListener('message', function(evt) {
|
||||
navigator.serviceWorker.ready.then(function() {
|
||||
evt.ports[0].postMessage("WOW!");
|
||||
navigator.serviceWorker.oncontrollerchange = function(e) {
|
||||
evt.ports[0].postMessage("WOW!");
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
|
||||
|
@ -16,23 +16,70 @@
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function simpleRegister() {
|
||||
var p = navigator.serviceWorker.register("worker.js", { scope: "./" });
|
||||
var p = navigator.serviceWorker.register("worker.js", { scope: "./install_event" });
|
||||
return p;
|
||||
}
|
||||
|
||||
function nextRegister(reg) {
|
||||
var p = navigator.serviceWorker.register("install_event_worker.js", { scope: "./" });
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
reg.onupdatefound = function(e) {
|
||||
ok(true, "Received onupdatefound");
|
||||
resolve();
|
||||
};
|
||||
ok(reg instanceof ServiceWorkerRegistration, "reg should be a ServiceWorkerRegistration");
|
||||
var p = navigator.serviceWorker.register("install_event_worker.js", { scope: "./install_event" });
|
||||
return p.then(function(swr) {
|
||||
ok(reg.scope === swr.scope, "Scope for registrations should match.");
|
||||
return new Promise(function(resolve, reject) {
|
||||
swr.addEventListener('updatefound', function(e) {
|
||||
ok(true, "Received onupdatefound");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}, function(e) {
|
||||
ok(false, "Unexpected Error in nextRegister! " + e);
|
||||
});
|
||||
}
|
||||
|
||||
function installError() {
|
||||
// Silence worker errors so they don't cause the test to fail.
|
||||
window.onerror = function() { }
|
||||
return navigator.serviceWorker.register("install_event_error_worker.js", { scope: "./install_event" })
|
||||
.then(function(swr) {
|
||||
ok(swr.installing.state == "installing", "Installing worker's state should be 'installing'");
|
||||
return new Promise(function(resolve, reject) {
|
||||
swr.installing.onstatechange = function(e) {
|
||||
ok(e.target.state == "redundant", "Installation of worker with error should fail.");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}).then(function() {
|
||||
return navigator.serviceWorker.getRegistration("./install_event").then(function(swr) {
|
||||
var newest = swr.waiting || swr.active;
|
||||
ok(newest, "Waiting or active worker should still exist");
|
||||
ok(newest.scriptURL.match(/install_event_worker.js$/), "Previous worker should remain the newest worker");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function activateError() {
|
||||
// Silence worker errors so they don't cause the test to fail.
|
||||
window.onerror = function() { }
|
||||
return navigator.serviceWorker.register("activate_event_error_worker.js", { scope: "./activate_error" })
|
||||
.then(function(swr) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
ok(swr.installing.state == "installing", "activateError(): Installing worker's state should be 'installing'");
|
||||
swr.installing.onstatechange = function(e) {
|
||||
ok(swr.active, "transition to active successfully");
|
||||
is(swr.active.state, "activating", "should be activating");
|
||||
swr.active.onstatechange = function(e) {
|
||||
is(e.target.state, "redundant", "Activation of worker with error in activate event handler should fail.");
|
||||
resolve(swr);
|
||||
}
|
||||
}
|
||||
});
|
||||
}).then(function(swr) {
|
||||
return swr.unregister();
|
||||
});
|
||||
}
|
||||
|
||||
function unregister() {
|
||||
return navigator.serviceWorker.getRegistration("./").then(function(reg) {
|
||||
return navigator.serviceWorker.getRegistration("./install_event").then(function(reg) {
|
||||
return reg.unregister();
|
||||
});
|
||||
}
|
||||
@ -40,6 +87,8 @@
|
||||
function runTest() {
|
||||
simpleRegister()
|
||||
.then(nextRegister)
|
||||
.then(installError)
|
||||
.then(activateError)
|
||||
.then(unregister)
|
||||
.then(function() {
|
||||
SimpleTest.finish();
|
||||
|
@ -95,15 +95,15 @@
|
||||
}
|
||||
|
||||
function networkError404() {
|
||||
return navigator.serviceWorker.register("404.js").then(function(w) {
|
||||
todo(false, "Should fail with NetworkError");
|
||||
return navigator.serviceWorker.register("404.js", { scope: "network_error/"}).then(function(w) {
|
||||
ok(false, "Should fail with NetworkError");
|
||||
}, function(e) {
|
||||
todo(e.name === "NetworkError", "Should fail with NetworkError");
|
||||
ok(e.name === "NetworkError", "Should fail with NetworkError");
|
||||
});
|
||||
}
|
||||
|
||||
function parseError() {
|
||||
var p = navigator.serviceWorker.register("parse_error_worker.js");
|
||||
var p = navigator.serviceWorker.register("parse_error_worker.js", { scope: "parse_error/" });
|
||||
return p.then(function(wr) {
|
||||
ok(false, "Registration should fail with parse error");
|
||||
}, function(e) {
|
||||
|
57
dom/workers/test/serviceworkers/test_workerUnregister.html
Normal file
57
dom/workers/test/serviceworkers/test_workerUnregister.html
Normal file
@ -0,0 +1,57 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 982728 - Test ServiceWorkerGlobalScope.unregister</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function simpleRegister() {
|
||||
return navigator.serviceWorker.register("worker_unregister.js", { scope: "unregister/" });
|
||||
}
|
||||
|
||||
function waitForMessages(sw) {
|
||||
var p = new Promise(function(resolve, reject) {
|
||||
window.onmessage = function(e) {
|
||||
if (e.data === "DONE") {
|
||||
ok(true, "The worker has unregistered itself");
|
||||
} else if (e.data === "ERROR") {
|
||||
ok(false, "The worker has unregistered itself");
|
||||
} else if (e.data === "FINISH") {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var frame = document.createElement("iframe");
|
||||
frame.setAttribute("src", "unregister/unregister.html");
|
||||
document.body.appendChild(frame);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
simpleRegister().then(waitForMessages).catch(function(e) {
|
||||
ok(false, "Something went wrong.");
|
||||
}).then(function() {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true]
|
||||
]}, runTest);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user