Merge m-i to m-c, a=merge

This commit is contained in:
Phil Ringnalda 2015-01-24 08:27:17 -08:00
commit 0c01dead3d
563 changed files with 14301 additions and 9558 deletions

View File

@ -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

View File

@ -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;

View File

@ -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>

View File

@ -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>

View File

@ -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();
}

View File

@ -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
}
);
}

View File

@ -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
}
);
}

View File

@ -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 {

View File

@ -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
}
});

View File

@ -815,7 +815,8 @@ NetMonitorController.NetworkEventsHandler = new NetworkEventsHandler();
*/
Object.defineProperties(window, {
"gNetwork": {
get: function() NetMonitorController.NetworkEventsHandler
get: function() NetMonitorController.NetworkEventsHandler,
configurable: true
}
});

View File

@ -75,10 +75,6 @@
/** Begin titlebar **/
#titlebar {
-moz-window-dragging: drag;
}
#titlebar-buttonbox > .titlebar-button {
display: none;
}

View File

@ -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;
}

View File

@ -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() {

View File

@ -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);
}

View File

@ -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

View 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>

View 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>

View File

@ -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',

View File

@ -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")

View File

@ -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

View File

@ -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 {

View File

@ -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);

View File

@ -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; })");

View File

@ -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())) {

View File

@ -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')

View File

@ -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

View File

@ -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;
}

View File

@ -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`,

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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 +

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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; }

View File

@ -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;
}
};

View File

@ -59,7 +59,8 @@ IdpChannel.prototype = {
aSandbox._frame.contentWindow.wrappedJSObject,
"rtcwebIdentityPort",
{
value: this.messagechannel.port2
value: this.messagechannel.port2,
configurable: true
}
);
} catch (e) {

View File

@ -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();
}
}

View File

@ -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.

View File

@ -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);
}

View File

@ -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.
}

View File

@ -240,6 +240,9 @@ MediaSourceDecoder::DoSetMediaSourceDuration(double aDuration)
mDecoderStateMachine->SetDuration(INT64_MAX);
mMediaSourceDuration = PositiveInfinity<double>();
}
if (mReader) {
mReader->SetMediaSourceDuration(mMediaSourceDuration);
}
}
void

View File

@ -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)

View File

@ -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;

View File

@ -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();
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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()) {

View File

@ -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);

View File

@ -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
}

View File

@ -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;

View File

@ -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;

View File

@ -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");
}

View File

@ -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) &&

View File

@ -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()

View File

@ -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");

View File

@ -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

View File

@ -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();

View File

@ -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]);

View File

@ -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);

View File

@ -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)

View File

@ -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
};

View File

@ -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();
};

View File

@ -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();

View File

@ -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;

View File

@ -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',

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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();
}

View File

@ -8,6 +8,7 @@
EXPORTS.mozilla.dom += [
'ServiceWorkerCommon.h',
'ServiceWorkerContainer.h',
'ServiceWorkerEvents.h',
'ServiceWorkerRegistration.h',
'WorkerPrivate.h',
'WorkerRunnable.h',

View File

@ -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");

View File

@ -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");

View File

@ -0,0 +1,4 @@
// Worker that errors on receiving an activate event.
onactivate = function(e) {
undefined.doSomething;
}

View File

@ -0,0 +1,4 @@
// Worker that errors on receiving an install event.
oninstall = function(e) {
undefined.doSomething;
}

View File

@ -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.

View File

@ -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();
}

View File

@ -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);

View File

@ -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();

View File

@ -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) {

View 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