Merge m-c to f-t

This commit is contained in:
Phil Ringnalda 2016-02-06 19:17:04 -08:00
commit 21abea4cd0
65 changed files with 2045 additions and 377 deletions

View File

@ -1015,6 +1015,7 @@ pref("security.exthelperapp.disable_background_handling", true);
// Inactivity time in milliseconds after which we shut down the OS.File worker. // Inactivity time in milliseconds after which we shut down the OS.File worker.
pref("osfile.reset_worker_delay", 5000); pref("osfile.reset_worker_delay", 5000);
pref("apz.displayport_expiry_ms", 0);
// APZ physics settings, tuned by UX designers // APZ physics settings, tuned by UX designers
pref("apz.axis_lock.mode", 2); // Use "sticky" axis locking pref("apz.axis_lock.mode", 2); // Use "sticky" axis locking
pref("apz.fling_curve_function_x1", "0.41"); pref("apz.fling_curve_function_x1", "0.41");

View File

@ -77,7 +77,7 @@ run-if = e10s
[browser_cleaner.js] [browser_cleaner.js]
[browser_cookies.js] [browser_cookies.js]
[browser_crashedTabs.js] [browser_crashedTabs.js]
skip-if = !e10s || !crashreporter skip-if = true # bug 1236414
[browser_unrestored_crashedTabs.js] [browser_unrestored_crashedTabs.js]
skip-if = !e10s || !crashreporter skip-if = !e10s || !crashreporter
[browser_revive_crashed_bg_tabs.js] [browser_revive_crashed_bg_tabs.js]

View File

@ -4,18 +4,27 @@
"use strict"; "use strict";
/* static functions */
const DEBUG = true;
function debug(aStr) {
DEBUG && dump("AlarmService: " + aStr + "\n");
}
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AlarmDB.jsm"); Cu.import("resource://gre/modules/AlarmDB.jsm");
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
function getLogger() {
var logger = Log.repository.getLogger("AlarmsService");
logger.addAppender(new Log.DumpAppender(new Log.BasicFormatter()));
logger.level = Log.Level.Debug;
return logger;
}
const logger = getLogger();
/* Only log in B2G */
function debug(aStr) {
AppConstants.MOZ_B2G && logger.debug(aStr);
}
this.EXPORTED_SYMBOLS = ["AlarmService"]; this.EXPORTED_SYMBOLS = ["AlarmService"];

View File

@ -1732,14 +1732,6 @@ nsDOMWindowUtils::GetFocusedInputType(char** aType)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsDOMWindowUtils::FindElementWithViewId(nsViewID aID,
nsIDOMElement** aResult)
{
RefPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aID);
return content ? CallQueryInterface(content, aResult) : NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
nsDOMWindowUtils::GetViewId(nsIDOMElement* aElement, nsViewID* aResult) nsDOMWindowUtils::GetViewId(nsIDOMElement* aElement, nsViewID* aResult)
{ {

View File

@ -969,9 +969,11 @@ nsFocusManager::WindowHidden(mozIDOMWindowProxy* aWindow)
// to the toplevel window. But if the window isn't being destroyed, we are // to the toplevel window. But if the window isn't being destroyed, we are
// likely just loading a new document in it, so we want to maintain the // likely just loading a new document in it, so we want to maintain the
// focused window so that the new document gets properly focused. // focused window so that the new document gets properly focused.
bool beingDestroyed;
nsCOMPtr<nsIDocShell> docShellBeingHidden = window->GetDocShell(); nsCOMPtr<nsIDocShell> docShellBeingHidden = window->GetDocShell();
docShellBeingHidden->IsBeingDestroyed(&beingDestroyed); bool beingDestroyed = !docShellBeingHidden;
if (docShellBeingHidden) {
docShellBeingHidden->IsBeingDestroyed(&beingDestroyed);
}
if (beingDestroyed) { if (beingDestroyed) {
// There is usually no need to do anything if a toplevel window is going // There is usually no need to do anything if a toplevel window is going
// away, as we assume that WindowLowered will be called. However, this may // away, as we assume that WindowLowered will be called. However, this may

View File

@ -159,6 +159,7 @@ CreateException(JSContext* aCx, nsresult aRv, const nsACString& aMessage)
case NS_ERROR_MODULE_DOM_FILEHANDLE: case NS_ERROR_MODULE_DOM_FILEHANDLE:
case NS_ERROR_MODULE_DOM_BLUETOOTH: case NS_ERROR_MODULE_DOM_BLUETOOTH:
case NS_ERROR_MODULE_DOM_ANIM: case NS_ERROR_MODULE_DOM_ANIM:
case NS_ERROR_MODULE_DOM_PUSH:
if (aMessage.IsEmpty()) { if (aMessage.IsEmpty()) {
return DOMException::Create(aRv); return DOMException::Create(aRv);
} }

View File

@ -839,25 +839,31 @@ WebGLContext::GetRenderbufferParameter(GLenum target, GLenum pname)
MakeContextCurrent(); MakeContextCurrent();
switch (pname) { switch (pname) {
case LOCAL_GL_RENDERBUFFER_SAMPLES: case LOCAL_GL_RENDERBUFFER_SAMPLES:
case LOCAL_GL_RENDERBUFFER_WIDTH: if (!IsWebGL2())
case LOCAL_GL_RENDERBUFFER_HEIGHT: break;
case LOCAL_GL_RENDERBUFFER_RED_SIZE: // fallthrough
case LOCAL_GL_RENDERBUFFER_GREEN_SIZE:
case LOCAL_GL_RENDERBUFFER_BLUE_SIZE: case LOCAL_GL_RENDERBUFFER_WIDTH:
case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE: case LOCAL_GL_RENDERBUFFER_HEIGHT:
case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE: case LOCAL_GL_RENDERBUFFER_RED_SIZE:
case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE: case LOCAL_GL_RENDERBUFFER_GREEN_SIZE:
case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT: case LOCAL_GL_RENDERBUFFER_BLUE_SIZE:
{ case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE:
// RB emulation means we have to ask the RB itself. case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE:
GLint i = mBoundRenderbuffer->GetRenderbufferParameter(target, pname); case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE:
return JS::Int32Value(i); case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT:
} {
default: // RB emulation means we have to ask the RB itself.
ErrorInvalidEnumInfo("getRenderbufferParameter: parameter", pname); GLint i = mBoundRenderbuffer->GetRenderbufferParameter(target, pname);
return JS::Int32Value(i);
} }
default:
break;
}
ErrorInvalidEnumInfo("getRenderbufferParameter: parameter", pname);
return JS::NullValue(); return JS::NullValue();
} }

View File

@ -148,7 +148,10 @@ WebGLContext::CreateShaderValidator(GLenum shaderType) const
resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits; resources.MaxCombinedTextureImageUnits = mGLMaxTextureUnits;
resources.MaxTextureImageUnits = mGLMaxTextureImageUnits; resources.MaxTextureImageUnits = mGLMaxTextureImageUnits;
resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors; resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors;
resources.MaxDrawBuffers = mGLMaxDrawBuffers;
const bool hasMRTs = (IsWebGL2() ||
IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers));
resources.MaxDrawBuffers = (hasMRTs ? mGLMaxDrawBuffers : 1);
if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth)) if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth))
resources.EXT_frag_depth = 1; resources.EXT_frag_depth = 1;

View File

@ -27,6 +27,41 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1215072
is(ex, "bah (2d)", "Should have thrown an exception."); is(ex, "bah (2d)", "Should have thrown an exception.");
} }
var gl2;
try {
gl2 = document.createElement("canvas").getContext("webgl", false);
gl2 = document.createElement("canvas").getContext("webgl", 123);
gl2 = document.createElement("canvas").getContext("webgl", "");
gl2 = document.createElement("canvas").getContext("webgl", undefined);
gl2 = document.createElement("canvas").getContext("webgl", null);
ok(true, "Shouldn't have thrown an exception!");
} catch(ex) {
ok(false, "Shouldn't have thrown an exception " + ex);
}
var c2;
try {
c2 = document.createElement("canvas").getContext("2d", false);
is(c2.getImageData(1, 1, 1, 1).data[0], 0);
is(c2.getImageData(1, 1, 1, 1).data[1], 0);
is(c2.getImageData(1, 1, 1, 1).data[2], 0);
is(c2.getImageData(1, 1, 1, 1).data[3], 0);
c2 = document.createElement("canvas").getContext("2d", 123);
c2 = document.createElement("canvas").getContext("2d", "");
c2 = document.createElement("canvas").getContext("2d", undefined);
c2 = document.createElement("canvas").getContext("2d", null);
ok(true, "Shouldn't have thrown an exception!");
c2 = document.createElement("canvas").getContext("2d", { alpha: false });
is(c2.getImageData(1, 1, 1, 1).data[0], 0);
is(c2.getImageData(1, 1, 1, 1).data[1], 0);
is(c2.getImageData(1, 1, 1, 1).data[2], 0);
is(c2.getImageData(1, 1, 1, 1).data[3], 255);
} catch(ex) {
ok(false, "Shouldn't have thrown an exception " + ex);
}
</script> </script>
</head> </head>
<body> <body>

View File

@ -895,7 +895,8 @@ HTMLCanvasElement::GetContext(JSContext* aCx,
} }
return CanvasRenderingContextHelper::GetContext(aCx, aContextId, return CanvasRenderingContextHelper::GetContext(aCx, aContextId,
aContextOptions, aRv); aContextOptions.isObject() ? aContextOptions : JS::NullHandleValue,
aRv);
} }
NS_IMETHODIMP NS_IMETHODIMP

View File

@ -413,8 +413,12 @@ ImageDocument::RestoreImage()
imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::width, true); imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::width, true);
imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::height, true); imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::height, true);
if (mImageIsOverflowing) { if (ImageIsOverflowing()) {
SetModeClass(eOverflowing); if (!mImageIsOverflowingVertically) {
SetModeClass(eOverflowingHorizontalOnly);
} else {
SetModeClass(eOverflowingVertical);
}
} }
else { else {
SetModeClass(eNone); SetModeClass(eNone);
@ -441,7 +445,7 @@ ImageDocument::ToggleImageSize()
ResetZoomLevel(); ResetZoomLevel();
RestoreImage(); RestoreImage();
} }
else if (mImageIsOverflowing) { else if (ImageIsOverflowing()) {
ResetZoomLevel(); ResetZoomLevel();
ShrinkToFit(); ShrinkToFit();
} }
@ -506,10 +510,16 @@ ImageDocument::SetModeClass(eModeClasses mode)
classList->Remove(NS_LITERAL_STRING("shrinkToFit"), rv); classList->Remove(NS_LITERAL_STRING("shrinkToFit"), rv);
} }
if (mode == eOverflowing) { if (mode == eOverflowingVertical) {
classList->Add(NS_LITERAL_STRING("overflowing"), rv); classList->Add(NS_LITERAL_STRING("overflowingVertical"), rv);
} else { } else {
classList->Remove(NS_LITERAL_STRING("overflowing"), rv); classList->Remove(NS_LITERAL_STRING("overflowingVertical"), rv);
}
if (mode == eOverflowingHorizontalOnly) {
classList->Add(NS_LITERAL_STRING("overflowingHorizontalOnly"), rv);
} else {
classList->Remove(NS_LITERAL_STRING("overflowingHorizontalOnly"), rv);
} }
} }
@ -579,7 +589,7 @@ ImageDocument::HandleEvent(nsIDOMEvent* aEvent)
mShouldResize = false; mShouldResize = false;
RestoreImageTo(x, y); RestoreImageTo(x, y);
} }
else if (mImageIsOverflowing) { else if (ImageIsOverflowing()) {
ShrinkToFit(); ShrinkToFit();
} }
} else if (eventType.EqualsLiteral("load")) { } else if (eventType.EqualsLiteral("load")) {
@ -681,18 +691,27 @@ ImageDocument::CheckOverflowing(bool changeState)
mVisibleHeight = nsPresContext::AppUnitsToFloatCSSPixels(visibleArea.height); mVisibleHeight = nsPresContext::AppUnitsToFloatCSSPixels(visibleArea.height);
} }
bool imageWasOverflowing = mImageIsOverflowing; bool imageWasOverflowing = ImageIsOverflowing();
mImageIsOverflowing = bool imageWasOverflowingVertically = mImageIsOverflowingVertically;
mImageWidth > mVisibleWidth || mImageHeight > mVisibleHeight; mImageIsOverflowingHorizontally = mImageWidth > mVisibleWidth;
bool windowBecameBigEnough = imageWasOverflowing && !mImageIsOverflowing; mImageIsOverflowingVertically = mImageHeight > mVisibleHeight;
bool windowBecameBigEnough = imageWasOverflowing && !ImageIsOverflowing();
bool verticalOverflowChanged =
mImageIsOverflowingVertically != imageWasOverflowingVertically;
if (changeState || mShouldResize || mFirstResize || if (changeState || mShouldResize || mFirstResize ||
windowBecameBigEnough) { windowBecameBigEnough || verticalOverflowChanged) {
if (mImageIsOverflowing && (changeState || mShouldResize)) { if (ImageIsOverflowing() && (changeState || mShouldResize)) {
ShrinkToFit(); ShrinkToFit();
} }
else if (mImageIsResized || mFirstResize || windowBecameBigEnough) { else if (mImageIsResized || mFirstResize || windowBecameBigEnough) {
RestoreImage(); RestoreImage();
} else if (!mImageIsResized && verticalOverflowChanged) {
if (mImageIsOverflowingVertically) {
SetModeClass(eOverflowingVertical);
} else {
SetModeClass(eOverflowingHorizontalOnly);
}
} }
} }
mFirstResize = false; mFirstResize = false;

View File

@ -62,7 +62,7 @@ public:
} }
bool ImageIsOverflowing() const bool ImageIsOverflowing() const
{ {
return mImageIsOverflowing; return mImageIsOverflowingHorizontally || mImageIsOverflowingVertically;
} }
bool ImageIsResized() const bool ImageIsResized() const
{ {
@ -101,7 +101,8 @@ protected:
enum eModeClasses { enum eModeClasses {
eNone, eNone,
eShrinkToFit, eShrinkToFit,
eOverflowing eOverflowingVertical, // And maybe horizontal too.
eOverflowingHorizontalOnly
}; };
void SetModeClass(eModeClasses mode); void SetModeClass(eModeClasses mode);
@ -118,7 +119,8 @@ protected:
bool mResizeImageByDefault; bool mResizeImageByDefault;
bool mClickResizingEnabled; bool mClickResizingEnabled;
bool mImageIsOverflowing; bool mImageIsOverflowingHorizontally;
bool mImageIsOverflowingVertically;
// mImageIsResized is true if the image is currently resized // mImageIsResized is true if the image is currently resized
bool mImageIsResized; bool mImageIsResized;
// mShouldResize is true if the image should be resized when it doesn't fit // mShouldResize is true if the image should be resized when it doesn't fit

View File

@ -88,8 +88,53 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=369370
is(kidDoc.body.scrollLeft, 0, "Checking scrollLeft"); is(kidDoc.body.scrollLeft, 0, "Checking scrollLeft");
is(kidDoc.body.scrollTop, 0, "Checking scrollTop"); is(kidDoc.body.scrollTop, 0, "Checking scrollTop");
kidWin.close(); // ========== test 5 ==========
SimpleTest.finish(); // Click in the upper left to zoom in again
event = makeClickFor(25, 25);
img.dispatchEvent(event);
ok(true, "----- click 5 -----");
is(img.width, 800, "image width");
is(img.height, 600, "image height");
is(kidDoc.body.scrollLeft, 0, "Checking scrollLeft");
is(kidDoc.body.scrollTop, 0, "Checking scrollTop");
is(img.getBoundingClientRect().top, 0, "Image is in view vertically");
// ========== test 6 ==========
// Now try resizing the window so the image fits vertically.
function test6() {
kidWin.addEventListener("resize", function resizeListener() {
kidWin.removeEventListener("resize", resizeListener);
// Give the image document time to respond
SimpleTest.executeSoon(function() {
is(img.height, 600, "image height");
is(img.getBoundingClientRect().top, 25, "Image is vertically centered");
test7();
});
});
var decorationSize = kidWin.outerHeight - kidWin.innerHeight;
kidWin.resizeTo(400, 600 + 50 + decorationSize);
}
// ========== test 7 ==========
// Now try resizing the window so the image no longer fits vertically.
function test7() {
kidWin.addEventListener("resize", function resizeListener() {
kidWin.removeEventListener("resize", resizeListener);
// Give the image document time to respond
SimpleTest.executeSoon(function() {
is(img.height, 600, "image height");
is(img.getBoundingClientRect().top, 0, "Image is at top again");
kidWin.close();
SimpleTest.finish();
});
});
var decorationSize = kidWin.outerHeight - kidWin.innerHeight;
kidWin.resizeTo(400, 300 + decorationSize);
}
test6();
} }
var kidWin; var kidWin;
var kidDoc; var kidDoc;

View File

@ -1487,13 +1487,6 @@ interface nsIDOMWindowUtils : nsISupports {
*/ */
readonly attribute string focusedInputType; readonly attribute string focusedInputType;
/**
* Given a view ID from the compositor process, retrieve the element
* associated with a view. For scrollpanes for documents, the root
* element of the document is returned.
*/
nsIDOMElement findElementWithViewId(in nsViewID aId);
/** /**
* Find the view ID for a given element. This is the reverse of * Find the view ID for a given element. This is the reverse of
* findElementWithViewId(). * findElementWithViewId().

View File

@ -678,6 +678,7 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_media_sniffer.html] [test_media_sniffer.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_mediarecorder_avoid_recursion.html] [test_mediarecorder_avoid_recursion.html]
skip-if = os == 'win' && !debug # bug 1228605
tags=msg tags=msg
[test_mediarecorder_bitrate.html] [test_mediarecorder_bitrate.html]
skip-if = (toolkit == 'gonk' || android_version == '10') # B2G emulator is too slow to run this without timing out and Android doesn't like the seek.webm skip-if = (toolkit == 'gonk' || android_version == '10') # B2G emulator is too slow to run this without timing out and Android doesn't like the seek.webm

View File

@ -76,6 +76,8 @@ skip-if = toolkit == 'gonk' # B2G emulator is too slow to handle a two-way audio
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867) skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
[test_peerConnection_basicAudioPcmaPcmuOnly.html] [test_peerConnection_basicAudioPcmaPcmuOnly.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867) skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
[test_peerConnection_basicAudioDynamicPtMissingRtpmap.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
[test_peerConnection_basicAudioVideo.html] [test_peerConnection_basicAudioVideo.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator) skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
[test_peerConnection_basicAudioVideoCombined.html] [test_peerConnection_basicAudioVideoCombined.html]

View File

@ -53,6 +53,10 @@ removeAllRtpMaps: function(sdp) {
return sdp.replace(/a=rtpmap:.*\r\n/g, ""); return sdp.replace(/a=rtpmap:.*\r\n/g, "");
}, },
reduceAudioMLineToDynamicPtAndOpus: function(sdp) {
return sdp.replace(/m=audio .*\r\n/g, "m=audio 9 UDP/TLS/RTP/SAVPF 101 109\r\n");
},
transferSimulcastProperties: function(offer_sdp, answer_sdp) { transferSimulcastProperties: function(offer_sdp, answer_sdp) {
if (!offer_sdp.includes("a=simulcast:")) { if (!offer_sdp.includes("a=simulcast:")) {
return answer_sdp; return answer_sdp;

View File

@ -0,0 +1,36 @@
<!DOCTYPE HTML>
<html>
<head>
<script type="application/javascript" src="pc.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({
bug: "1246011",
title: "Offer with dynamic PT but missing rtpmap"
});
var test;
runNetworkTest(function (options) {
options = options || { };
// we want Opus to get selected and 101 to be ignored
options.opus = true;
test = new PeerConnectionTest(options);
test.chain.insertBefore("PC_REMOTE_GET_OFFER", [
function PC_LOCAL_REDUCE_MLINE_REMOVE_RTPMAPS(test) {
test.originalOffer.sdp =
sdputils.reduceAudioMLineToDynamicPtAndOpus(test.originalOffer.sdp);
test.originalOffer.sdp =
sdputils.removeAllRtpMaps(test.originalOffer.sdp);
test.originalOffer.sdp = test.originalOffer.sdp + "a=rtpmap:109 opus/48000/2\r\n";
info("SDP with dyn PT and no Rtpmap: " + JSON.stringify(test.originalOffer));
}
]);
test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run();
});
</script>
</pre>
</body>
</html>

View File

@ -7,7 +7,7 @@
<pre id="test"> <pre id="test">
<script type="application/javascript"> <script type="application/javascript">
createHTML({ createHTML({
bug: "796892", bug: "1221837",
title: "Only offer PCMA and PMCU in mline (no rtpmaps)" title: "Only offer PCMA and PMCU in mline (no rtpmaps)"
}); });
@ -25,6 +25,11 @@
info("SDP without Rtpmaps: " + JSON.stringify(test.originalOffer)); info("SDP without Rtpmaps: " + JSON.stringify(test.originalOffer));
} }
]); ]);
test.chain.insertAfter("PC_REMOTE_SANE_LOCAL_SDP", [
function PC_REMOTE_VERIFY_PCMU(test) {
ok(test._remote_answer.sdp.includes("a=rtpmap:0 PCMU/8000"), "PCMU codec is present in SDP");
}
]);
test.setMediaConstraints([{audio: true}], [{audio: true}]); test.setMediaConstraints([{audio: true}], [{audio: true}]);
test.run(); test.run();
}); });

View File

@ -105,7 +105,7 @@ public:
if (NS_SUCCEEDED(aStatus)) { if (NS_SUCCEEDED(aStatus)) {
mPromise->MaybeResolve(aSuccess); mPromise->MaybeResolve(aSuccess);
} else { } else {
mPromise->MaybeReject(NS_ERROR_DOM_NETWORK_ERR); mPromise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
} }
return NS_OK; return NS_OK;
@ -403,7 +403,7 @@ public:
if (NS_SUCCEEDED(mStatus)) { if (NS_SUCCEEDED(mStatus)) {
promise->MaybeResolve(mSuccess); promise->MaybeResolve(mSuccess);
} else { } else {
promise->MaybeReject(NS_ERROR_DOM_NETWORK_ERR); promise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
} }
mProxy->CleanUp(aCx); mProxy->CleanUp(aCx);
@ -528,7 +528,7 @@ WorkerPushSubscription::Unsubscribe(ErrorResult &aRv)
RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, p); RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, p);
if (!proxy) { if (!proxy) {
p->MaybeReject(NS_ERROR_DOM_NETWORK_ERR); p->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
return p.forget(); return p.forget();
} }
@ -598,6 +598,8 @@ public:
mRawP256dhKey, mAuthSecret); mRawP256dhKey, mAuthSecret);
promise->MaybeResolve(sub); promise->MaybeResolve(sub);
} }
} else if (NS_ERROR_GET_MODULE(mStatus) == NS_ERROR_MODULE_DOM_PUSH ) {
promise->MaybeReject(mStatus);
} else { } else {
promise->MaybeReject(NS_ERROR_DOM_PUSH_ABORT_ERR); promise->MaybeReject(NS_ERROR_DOM_PUSH_ABORT_ERR);
} }
@ -776,7 +778,7 @@ public:
callback->OnPushSubscriptionError(NS_OK); callback->OnPushSubscriptionError(NS_OK);
return NS_OK; return NS_OK;
} }
callback->OnPushSubscriptionError(NS_ERROR_FAILURE); callback->OnPushSubscriptionError(NS_ERROR_DOM_PUSH_DENIED_ERR);
return NS_OK; return NS_OK;
} }

View File

@ -4,20 +4,18 @@
<meta http-equiv="Content-type" content="text/html;charset=UTF-8"> <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<script> <script>
function waitOnWorkerMessage(type) {
function waitOnPushMessage(pushSubscription) return new Promise(function(res, rej) {
{ function onMessage(e) {
var p = new Promise(function(res, rej) { if (e.data.type == type) {
navigator.serviceWorker.onmessage = function(e) { navigator.serviceWorker.removeEventListener("message", onMessage);
if (e.data.type == "finished") {
(e.data.okay == "yes" ? res : rej)(e.data); (e.data.okay == "yes" ? res : rej)(e.data);
} }
}; }
navigator.serviceWorker.addEventListener("message", onMessage);
}); });
return p;
} }
</script> </script>
</head> </head>
<body> <body>

View File

@ -6,6 +6,7 @@ support-files =
frame.html frame.html
webpush.js webpush.js
lifetime_worker.js lifetime_worker.js
test_utils.js
[test_has_permissions.html] [test_has_permissions.html]
skip-if = os == "android" || toolkit == "gonk" skip-if = os == "android" || toolkit == "gonk"
@ -21,6 +22,8 @@ skip-if = os == "android" || toolkit == "gonk"
skip-if = os == "android" || toolkit == "gonk" skip-if = os == "android" || toolkit == "gonk"
[test_multiple_register_different_scope.html] [test_multiple_register_different_scope.html]
skip-if = os == "android" || toolkit == "gonk" skip-if = os == "android" || toolkit == "gonk"
[test_subscription_change.html]
skip-if = os == "android" || toolkit == "gonk"
[test_data.html] [test_data.html]
skip-if = os == "android" || toolkit == "gonk" skip-if = os == "android" || toolkit == "gonk"
# Disabled for too many intermittent failures (bug 1164432) # Disabled for too many intermittent failures (bug 1164432)

View File

@ -11,6 +11,7 @@ http://creativecommons.org/licenses/publicdomain/
<title>Test for Bug 1185544</title> <title>Test for Bug 1185544</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
<script type="text/javascript" src="/tests/dom/push/test/webpush.js"></script> <script type="text/javascript" src="/tests/dom/push/test/webpush.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8"> <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
@ -25,25 +26,10 @@ http://creativecommons.org/licenses/publicdomain/
<script class="testbody" type="text/javascript"> <script class="testbody" type="text/javascript">
SimpleTest.registerCleanupFunction(() =>
new Promise(resolve => SpecialPowers.popPermissions(resolve))
);
var registration; var registration;
add_task(function* start() { add_task(function* start() {
yield new Promise(resolve => { yield setupPrefs();
SpecialPowers.pushPermissions([ yield setPushPermission(true);
{ type: "desktop-notification", allow: true, context: document },
], resolve);
});
yield new Promise(resolve => {
SpecialPowers.pushPrefEnv({"set": [
["dom.push.enabled", true],
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true]
]}, resolve);
});
var url = "worker.js" + "?" + (Math.random()); var url = "worker.js" + "?" + (Math.random());
registration = yield navigator.serviceWorker.register(url, {scope: "."}); registration = yield navigator.serviceWorker.register(url, {scope: "."});
@ -51,15 +37,7 @@ http://creativecommons.org/licenses/publicdomain/
var controlledFrame; var controlledFrame;
add_task(function* createControlledIFrame() { add_task(function* createControlledIFrame() {
yield new Promise(function(res, rej) { controlledFrame = yield injectControlledFrame();
var iframe = document.createElement('iframe');
iframe.id = "controlledFrame";
iframe.src = "http://mochi.test:8888/tests/dom/push/test/frame.html";
iframe.onload = () => res();
controlledFrame = iframe;
document.body.appendChild(iframe);
});
}); });
var pushSubscription; var pushSubscription;
@ -67,16 +45,6 @@ http://creativecommons.org/licenses/publicdomain/
pushSubscription = yield registration.pushManager.subscribe(); pushSubscription = yield registration.pushManager.subscribe();
}); });
function sendRequestToWorker(request) {
return new Promise((resolve, reject) => {
var channel = new MessageChannel();
channel.port1.onmessage = e => {
(e.data.error ? reject : resolve)(e.data);
};
registration.active.postMessage(request, [channel.port2]);
});
}
function base64UrlDecode(s) { function base64UrlDecode(s) {
s = s.replace(/-/g, '+').replace(/_/g, '/'); s = s.replace(/-/g, '+').replace(/_/g, '/');
@ -138,7 +106,7 @@ http://creativecommons.org/licenses/publicdomain/
function waitForMessage(pushSubscription, message) { function waitForMessage(pushSubscription, message) {
return Promise.all([ return Promise.all([
controlledFrame.contentWindow.waitOnPushMessage(pushSubscription), controlledFrame.waitOnWorkerMessage("finished"),
webpush(pushSubscription, message), webpush(pushSubscription, message),
]).then(([message]) => message); ]).then(([message]) => message);
} }
@ -190,11 +158,10 @@ http://creativecommons.org/licenses/publicdomain/
reader.readAsText(message.data.blob); reader.readAsText(message.data.blob);
}); });
is(text, "Hi! \ud83d\udc40", "Wrong blob data for message with emoji"); is(text, "Hi! \ud83d\udc40", "Wrong blob data for message with emoji");
is(text, "Hi! \ud83d\udc40", "Wrong blob data for message with emoji");
// Send a blank message. // Send a blank message.
var [message] = yield Promise.all([ var [message] = yield Promise.all([
controlledFrame.contentWindow.waitOnPushMessage(pushSubscription), controlledFrame.waitOnWorkerMessage("finished"),
fetch("http://mochi.test:8888/tests/dom/push/test/push-server.sjs", { fetch("http://mochi.test:8888/tests/dom/push/test/push-server.sjs", {
method: "PUT", method: "PUT",
headers: { headers: {
@ -207,8 +174,7 @@ http://creativecommons.org/licenses/publicdomain/
}); });
add_task(function* unsubscribe() { add_task(function* unsubscribe() {
controlledFrame.parentNode.removeChild(controlledFrame); controlledFrame.remove();
controlledFrame = null;
yield pushSubscription.unsubscribe(); yield pushSubscription.unsubscribe();
}); });

View File

@ -11,6 +11,7 @@ http://creativecommons.org/licenses/publicdomain/
<title>Test for Bug 1038811</title> <title>Test for Bug 1038811</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8"> <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head> </head>
@ -30,21 +31,14 @@ http://creativecommons.org/licenses/publicdomain/
var registration; var registration;
add_task(function* start() { add_task(function* start() {
SpecialPowers.addPermission("desktop-notification", false, document); yield setupPrefs();
yield new Promise(resolve => { yield setPushPermission(false);
SpecialPowers.pushPrefEnv({"set": [
["dom.push.enabled", true],
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true]
]}, resolve);
});
var url = "worker.js" + "?" + Math.random(); var url = "worker.js" + "?" + Math.random();
registration = yield navigator.serviceWorker.register(url, {scope: "."}); registration = yield navigator.serviceWorker.register(url, {scope: "."});
}); });
add_task(function* setupPushNotification() { add_task(function* denySubscribe() {
try { try {
yield registration.pushManager.subscribe(); yield registration.pushManager.subscribe();
ok(false, "subscribe() should fail because no permission for push"); ok(false, "subscribe() should fail because no permission for push");
@ -54,6 +48,17 @@ http://creativecommons.org/licenses/publicdomain/
} }
}); });
add_task(function* denySubscribeInWorker() {
// If permission is revoked, `getSubscription()` should return `null`, and
// `subscribe()` should reject immediately. Calling these from the worker
// should not deadlock the main thread (see bug 1228723).
var errorInfo = yield sendRequestToWorker({
type: "denySubscribe",
});
ok(errorInfo.isDOMException, "Wrong exception type");
is(errorInfo.name, "PermissionDeniedError", "Wrong exception name");
});
add_task(function* getEndpoint() { add_task(function* getEndpoint() {
var pushSubscription = yield registration.pushManager.getSubscription(); var pushSubscription = yield registration.pushManager.getSubscription();
is(pushSubscription, null, "getSubscription() should return null because no permission for push"); is(pushSubscription, null, "getSubscription() should return null because no permission for push");
@ -75,12 +80,7 @@ http://creativecommons.org/licenses/publicdomain/
state: "prompt", state: "prompt",
}]; }];
for (var test of tests) { for (var test of tests) {
if (test.action == permissionManager.UNKNOWN_ACTION) { yield setPushPermission(test.action);
SpecialPowers.removePermission("desktop-notification", document);
} else {
SpecialPowers.addPermission("desktop-notification",
test.action, document);
}
var state = yield registration.pushManager.permissionState(); var state = yield registration.pushManager.permissionState();
is(state, test.state, JSON.stringify(test)); is(state, test.state, JSON.stringify(test));
} }

View File

@ -10,6 +10,8 @@ http://creativecommons.org/licenses/publicdomain/
<head> <head>
<title>Test for Bug 1038811</title> <title>Test for Bug 1038811</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8"> <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head> </head>
@ -27,112 +29,63 @@ http://creativecommons.org/licenses/publicdomain/
// console.log(str + "\n"); // console.log(str + "\n");
} }
var controlledFrame;
function createControlledIFrame(swr) {
var p = new Promise(function(res, rej) {
var iframe = document.createElement('iframe');
iframe.id = "controlledFrame";
iframe.src = "http://mochi.test:8888/tests/dom/push/test/frame.html";
iframe.onload = function() {
res(swr)
}
controlledFrame = iframe;
document.body.appendChild(iframe);
});
return p;
}
function checkPermissionState(swr) {
return swr.pushManager.permissionState().then(function(state) {
ok(state === "granted", "permissionState() should resolve to granted.");
return swr;
}).catch(function(e) {
ok(false, "permissionState() should resolve to granted.");
return swr;
});
}
function sendPushToPushServer(pushEndpoint) {
// Work around CORS for now.
var xhr = new XMLHttpRequest();
xhr.open('GET', "http://mochi.test:8888/tests/dom/push/test/push-server.sjs", true);
xhr.setRequestHeader("X-Push-Method", "PUT");
xhr.setRequestHeader("X-Push-Server", pushEndpoint);
xhr.onload = function(e) {
debug("xhr : " + this.status);
}
xhr.onerror = function(e) {
debug("xhr error: " + e);
}
xhr.send("version=24601");
}
var registration; var registration;
add_task(function* start() {
yield setupPrefs();
yield setPushPermission(true);
function start() { var url = "worker.js" + "?" + (Math.random());
return navigator.serviceWorker.register("worker.js" + "?" + (Math.random()), {scope: "."}) registration = yield navigator.serviceWorker.register(url, {scope: "."});
.then((swr) => registration = swr); });
}
function unregister() { var controlledFrame;
return registration.unregister().then(function(result) { add_task(function* createControlledIFrame() {
ok(result, "Unregister should return true."); controlledFrame = yield injectControlledFrame();
}, function(e) { });
dump("Unregistering the SW failed with " + e + "\n");
add_task(function* checkPermissionState() {
var state = yield registration.pushManager.permissionState();
is(state, "granted", "permissionState() should resolve to granted.");
});
var pushSubscription;
add_task(function* subscribe() {
pushSubscription = yield registration.pushManager.subscribe();
});
add_task(function* resubscribe() {
var data = yield sendRequestToWorker({
type: "resubscribe",
endpoint: pushSubscription.endpoint,
}); });
} pushSubscription = yield registration.pushManager.getSubscription();
is(data.endpoint, pushSubscription.endpoint,
"Subscription endpoints should match after resubscribing in worker");
});
function setupPushNotification(swr) { add_task(function* waitForPushNotification() {
var p = new Promise(function(res, rej) { yield Promise.all([
swr.pushManager.subscribe().then( controlledFrame.waitOnWorkerMessage("finished"),
function(pushSubscription) { fetch("http://mochi.test:8888/tests/dom/push/test/push-server.sjs", {
ok(true, "successful registered for push notification"); method: "PUT",
res(pushSubscription); headers: {
}, function(error) { "X-Push-Method": "POST",
ok(false, "could not register for push notification"); "X-Push-Server": pushSubscription.endpoint,
res(null); },
} }),
); ]);
}); });
return p;
}
function unregisterPushNotification(pushSubscription) { add_task(function* unsubscribe() {
controlledFrame.parentNode.removeChild(controlledFrame); controlledFrame.remove();
controlledFrame = null; yield pushSubscription.unsubscribe();
return pushSubscription.unsubscribe(); });
}
function waitForPushNotification(pushSubscription) { add_task(function* unregister() {
var p = controlledFrame.contentWindow.waitOnPushMessage(); var result = yield registration.unregister();
sendPushToPushServer(pushSubscription.endpoint); ok(result, "Unregister should return true.");
return p.then(function() { });
return pushSubscription;
});
}
function runTest() {
start()
.then(createControlledIFrame)
.then(checkPermissionState)
.then(setupPushNotification)
.then(waitForPushNotification)
.then(unregisterPushNotification)
.then(unregister)
.catch(function(e) {
ok(false, "Some test failed with error " + e);
}).then(SimpleTest.finish);
}
SpecialPowers.pushPrefEnv({"set": [
["dom.push.enabled", true],
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true]
]}, runTest);
SpecialPowers.addPermission("desktop-notification", true, document);
SimpleTest.waitForExplicitFinish();
</script> </script>
</body> </body>
</html> </html>

View File

@ -0,0 +1,68 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 1205109: Make `pushsubscriptionchange` extendable.
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<head>
<title>Test for Bug 1205109</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1205109">Mozilla Bug 1205109</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script class="testbody" type="text/javascript">
var registration;
add_task(function* start() {
yield setupPrefs();
yield setPushPermission(true);
var url = "worker.js" + "?" + (Math.random());
registration = yield navigator.serviceWorker.register(url, {scope: "."});
});
var controlledFrame;
add_task(function* createControlledIFrame() {
controlledFrame = yield injectControlledFrame();
});
add_task(function* togglePermission() {
var subscription = yield registration.pushManager.subscribe();
ok(subscription, "Should create a push subscription");
yield setPushPermission(false);
var permissionState = yield registration.pushManager.permissionState();
is(permissionState, "denied", "Should deny push permission");
var subscription = yield registration.pushManager.getSubscription();
is(subscription, null, "Should not return subscription when permission is revoked");
var changePromise = controlledFrame.waitOnWorkerMessage("changed");
yield setPushPermission(true);
yield changePromise;
subscription = yield registration.pushManager.getSubscription();
is(subscription, null, "Should drop subscription after reinstating permission");
});
add_task(function* unsubscribe() {
controlledFrame.remove();
yield registration.unregister();
});
</script>
</body>
</html>

View File

@ -0,0 +1,60 @@
// Remove permissions and prefs when the test finishes.
SimpleTest.registerCleanupFunction(() =>
new Promise(resolve => {
SpecialPowers.flushPermissions(_ => {
SpecialPowers.flushPrefEnv(resolve);
});
})
);
function setPushPermission(allow) {
return new Promise(resolve => {
SpecialPowers.pushPermissions([
{ type: "desktop-notification", allow, context: document },
], resolve);
});
}
function setupPrefs() {
return new Promise(resolve => {
SpecialPowers.pushPrefEnv({"set": [
["dom.push.enabled", true],
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true]
]}, resolve);
});
}
function injectControlledFrame(target = document.body) {
return new Promise(function(res, rej) {
var iframe = document.createElement("iframe");
iframe.src = "/tests/dom/push/test/frame.html";
var controlledFrame = {
remove() {
target.removeChild(iframe);
iframe = null;
},
waitOnWorkerMessage(type) {
return iframe ? iframe.contentWindow.waitOnWorkerMessage(type) :
Promise.reject(new Error("Frame removed from document"));
},
};
iframe.onload = () => res(controlledFrame);
target.appendChild(iframe);
});
}
function sendRequestToWorker(request) {
return navigator.serviceWorker.ready.then(registration => {
return new Promise((resolve, reject) => {
var channel = new MessageChannel();
channel.port1.onmessage = e => {
(e.data.error ? reject : resolve)(e.data);
};
registration.active.postMessage(request, [channel.port2]);
});
});
}

View File

@ -1,8 +1,16 @@
// Any copyright is dedicated to the Public Domain. // Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/ // http://creativecommons.org/licenses/publicdomain/
// This worker is used for two types of tests. `handlePush` sends messages to
// `frame.html`, which verifies that the worker can receive push messages.
// `handleMessage` receives messages from `test_push_manager_worker.html`
// and `test_data.html`, and verifies that `PushManager` can be used from
// the worker.
this.onpush = handlePush; this.onpush = handlePush;
this.onmessage = handleMessage; this.onmessage = handleMessage;
this.onpushsubscriptionchange = handlePushSubscriptionChange;
function getJSON(data) { function getJSON(data) {
var result = { var result = {
@ -17,48 +25,105 @@ function getJSON(data) {
return result; return result;
} }
function handlePush(event) { function assert(value, message) {
if (!value) {
throw new Error(message);
}
}
event.waitUntil(self.clients.matchAll().then(function(result) { function broadcast(event, promise) {
if (event instanceof PushEvent) { event.waitUntil(Promise.resolve(promise).then(message => {
if (!('data' in event)) { return self.clients.matchAll().then(clients => {
result[0].postMessage({type: "finished", okay: "yes"}); clients.forEach(client => client.postMessage(message));
return; });
} }));
var message = { }
type: "finished",
okay: "yes", function reply(event, promise) {
}; event.waitUntil(Promise.resolve(promise).then(result => {
if (event.data) { event.ports[0].postMessage(result);
message.data = { }).catch(error => {
text: event.data.text(), event.ports[0].postMessage({
arrayBuffer: event.data.arrayBuffer(), error: String(error),
json: getJSON(event.data), });
blob: event.data.blob(), }));
}; }
}
result[0].postMessage(message); function handlePush(event) {
if (event instanceof PushEvent) {
if (!('data' in event)) {
broadcast(event, {type: "finished", okay: "yes"});
return; return;
} }
result[0].postMessage({type: "finished", okay: "no"}); var message = {
})); type: "finished",
okay: "yes",
};
if (event.data) {
message.data = {
text: event.data.text(),
arrayBuffer: event.data.arrayBuffer(),
json: getJSON(event.data),
blob: event.data.blob(),
};
}
broadcast(event, message);
return;
}
broadcast(event, {type: "finished", okay: "no"});
} }
function handleMessage(event) { function handleMessage(event) {
if (event.data.type == "publicKey") { if (event.data.type == "publicKey") {
event.waitUntil(self.registration.pushManager.getSubscription().then(subscription => { reply(event, self.registration.pushManager.getSubscription().then(
event.ports[0].postMessage({ subscription => ({
p256dh: subscription.getKey("p256dh"), p256dh: subscription.getKey("p256dh"),
auth: subscription.getKey("auth"), auth: subscription.getKey("auth"),
}); })
}).catch(error => { ));
event.ports[0].postMessage({ return;
error: String(error), }
}); if (event.data.type == "resubscribe") {
reply(event, self.registration.pushManager.getSubscription().then(
subscription => {
assert(subscription.endpoint == event.data.endpoint,
"Wrong push endpoint in worker");
return subscription.unsubscribe();
}
).then(result => {
assert(result, "Error unsubscribing in worker");
return self.registration.pushManager.getSubscription();
}).then(subscription => {
assert(!subscription, "Subscription not removed in worker");
return self.registration.pushManager.subscribe();
}).then(subscription => {
return {
endpoint: subscription.endpoint,
};
})); }));
return; return;
} }
event.ports[0].postMessage({ if (event.data.type == "denySubscribe") {
error: "Invalid message type: " + event.data.type, reply(event, self.registration.pushManager.getSubscription().then(
}); subscription => {
assert(!subscription,
"Should not return worker subscription with revoked permission");
return self.registration.pushManager.subscribe().then(_ => {
assert(false, "Expected error subscribing with revoked permission");
}, error => {
return {
isDOMException: error instanceof DOMException,
name: error.name,
};
});
}
));
return;
}
reply(event, Promise.reject(
"Invalid message type: " + event.data.type));
}
function handlePushSubscriptionChange(event) {
broadcast(event, {type: "changed", okay: "yes"});
} }

View File

@ -69,7 +69,7 @@ typedef any Transferable;
#ifdef HAVE_SIDEBAR #ifdef HAVE_SIDEBAR
[Replaceable, Throws] readonly attribute External external; [Replaceable, Throws] readonly attribute External external;
#endif #endif
[Throws] readonly attribute ApplicationCache applicationCache; [Throws, Pref="browser.cache.offline.enable"] readonly attribute ApplicationCache applicationCache;
// user prompts // user prompts
[Throws, UnsafeInPrerendering] void alert(); [Throws, UnsafeInPrerendering] void alert();

View File

@ -167,3 +167,5 @@ skip-if = toolkit == 'android'
[test_bug1181130-1.html] [test_bug1181130-1.html]
[test_bug1181130-2.html] [test_bug1181130-2.html]
[test_backspace_vs.html] [test_backspace_vs.html]
[test_css_chrome_load_access.html]
skip-if = toolkit == 'android' # chrome urls not available due to packaging

View File

@ -0,0 +1,67 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1245681
-->
<head>
<title>Test for Bug 1245681</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.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=1245681">Mozilla Bug 1245681</a>
<p id="display"></p>
<div id="content">
<iframe></iframe>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
const Ci = SpecialPowers.Ci;
var styleSheets = null;
function runTest() {
var editframe = window.frames[0];
var editdoc = editframe.document;
editdoc.designMode = 'on';
var editor = SpecialPowers.wrap(editframe)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIEditingSession)
.getEditorForWindow(editframe);
styleSheets = editor.QueryInterface(Ci.nsIEditorStyleSheets);
// test 1: try to access chrome:// url that is accessible to content
try
{
styleSheets.addOverrideStyleSheet("chrome://browser/content/pageinfo/pageInfo.css");
ok(true, "should be allowed to access chrome://*.css if contentaccessible");
}
catch (ex) {
ok(false, "should be allowed to access chrome://*.css if contentaccessible");
}
// test 2: try to access chrome:// url that is *not* accessible to content
// please note that addOverrideStyleSheet() is triggered by the system,
// so the load should also *always* succeed.
try
{
styleSheets.addOverrideStyleSheet("chrome://mozapps/skin/aboutNetworking.css");
ok(true, "should be allowed to access chrome://*.css even if *not* contentaccessible");
}
catch (ex) {
ok(false, "should be allowed to access chrome://*.css even if *not* contentaccessible");
}
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(runTest);
</script>
</pre>
</body>
</html>

View File

@ -178,12 +178,11 @@ function* runTest() {
// Scroll on inner3. inner3 isn't layerized, and this will cause it to // Scroll on inner3. inner3 isn't layerized, and this will cause it to
// get layerized, but it will also trigger displayport expiration for inner3 // get layerized, but it will also trigger displayport expiration for inner3
// which will eventually trigger displayport expiration on outer3. // which will eventually trigger displayport expiration on inner3 and outer3.
yield scrollWheelOver(document.getElementById('outer3').contentDocument.getElementById('inner3')); yield scrollWheelOver(document.getElementById('outer3').contentDocument.getElementById('inner3'));
yield waitForAllPaints(function() { yield waitForAllPaints(function() {
flushApzRepaints(driveTest); flushApzRepaints(driveTest);
}); });
ok(isLayerized('inner3'), "inner3 becomes layerized after scroll");
yield setTimeout(driveTest, DISPLAYPORT_EXPIRY); yield setTimeout(driveTest, DISPLAYPORT_EXPIRY);
yield waitForAllPaints(callDriveTestAsync); yield waitForAllPaints(callDriveTestAsync);
ok(!isLayerized('inner3'), "inner3 becomes unlayerized after expiry"); ok(!isLayerized('inner3'), "inner3 becomes unlayerized after expiry");

View File

@ -431,7 +431,11 @@ DataTextureSourceD3D9::Update(gfx::DataSourceSurface* aSurface,
mTexture->GetSurfaceLevel(0, getter_AddRefs(destSurface)); mTexture->GetSurfaceLevel(0, getter_AddRefs(destSurface));
D3DLOCKED_RECT rect; D3DLOCKED_RECT rect;
srcSurface->LockRect(&rect, nullptr, 0); HRESULT hr = srcSurface->LockRect(&rect, nullptr, 0);
if (FAILED(hr) || !rect.pBits) {
gfxCriticalError() << "Failed to lock rect initialize texture in D3D9 " << hexa(hr);
return nullptr;
}
for (auto iter = regionToUpdate.RectIter(); !iter.Done(); iter.Next()) { for (auto iter = regionToUpdate.RectIter(); !iter.Done(); iter.Next()) {
const nsIntRect& iterRect = iter.Get(); const nsIntRect& iterRect = iter.Get();

View File

@ -109,6 +109,18 @@ AtomDecls<ParseHandler>::addShadow(JSAtom* atom, typename ParseHandler::Definiti
return p.value().pushFront<ParseHandler>(cx, alloc, defn); return p.value().pushFront<ParseHandler>(cx, alloc, defn);
} }
template <typename ParseHandler>
bool
AtomDecls<ParseHandler>::addShadowedForAnnexB(JSAtom* atom,
typename ParseHandler::DefinitionNode defn)
{
AtomDefnListAddPtr p = map->lookupForAdd(atom);
if (!p)
return map->add(p, atom, DefinitionList(ParseHandler::definitionToBits(defn)));
return p.value().appendBack<ParseHandler>(cx, alloc, defn);
}
void void
frontend::InitAtomMap(frontend::AtomIndexMap* indices, HeapPtrAtom* atoms) frontend::InitAtomMap(frontend::AtomIndexMap* indices, HeapPtrAtom* atoms)
{ {

View File

@ -250,6 +250,14 @@ class DefinitionList
return (Node*) (u.bits & ~0x1); return (Node*) (u.bits & ~0x1);
} }
Node* lastNode() const {
MOZ_ASSERT(isMultiple());
Node* node = firstNode();
while (node->next)
node = node->next;
return node;
}
static Node* static Node*
allocNode(ExclusiveContext* cx, LifoAlloc& alloc, uintptr_t bits, Node* tail); allocNode(ExclusiveContext* cx, LifoAlloc& alloc, uintptr_t bits, Node* tail);
@ -361,6 +369,26 @@ class DefinitionList
return true; return true;
} }
template <typename ParseHandler>
bool appendBack(ExclusiveContext* cx, LifoAlloc& alloc,
typename ParseHandler::DefinitionNode defn)
{
Node* last = allocNode(cx, alloc, ParseHandler::definitionToBits(defn), nullptr);
if (!last)
return false;
if (isMultiple()) {
lastNode()->next = last;
} else {
Node* oldLast = allocNode(cx, alloc, u.bits, last);
if (!oldLast)
return false;
*this = DefinitionList(oldLast);
}
return true;
}
/* Overwrite the first Definition in the list. */ /* Overwrite the first Definition in the list. */
template <typename ParseHandler> template <typename ParseHandler>
void setFront(typename ParseHandler::DefinitionNode defn) { void setFront(typename ParseHandler::DefinitionNode defn) {
@ -473,6 +501,7 @@ class AtomDecls
} }
bool addShadow(JSAtom* atom, DefinitionNode defn); bool addShadow(JSAtom* atom, DefinitionNode defn);
bool addShadowedForAnnexB(JSAtom* atom, DefinitionNode defn);
/* Updating the definition for an entry that is known to exist is infallible. */ /* Updating the definition for an entry that is known to exist is infallible. */
void updateFirst(JSAtom* atom, DefinitionNode defn) { void updateFirst(JSAtom* atom, DefinitionNode defn) {

View File

@ -21,7 +21,10 @@ ParseContext<ParseHandler>::init(Parser<ParseHandler>& parser)
if (!parser.generateBlockId(sc->staticScope(), &this->bodyid)) if (!parser.generateBlockId(sc->staticScope(), &this->bodyid))
return false; return false;
if (!decls_.init() || !lexdeps.ensureMap(sc->context)) { if (!decls_.init() ||
!lexdeps.ensureMap(sc->context) ||
!bodyLevelLexicallyDeclaredNames_.init())
{
ReportOutOfMemory(sc->context); ReportOutOfMemory(sc->context);
return false; return false;
} }

View File

@ -216,17 +216,20 @@ SharedContext::markSuperScopeNeedsHomeObject()
// See comment on member function declaration. // See comment on member function declaration.
template <> template <>
bool bool
ParseContext<FullParseHandler>::define(TokenStream& ts, ParseContext<FullParseHandler>::define(TokenStream& ts, HandlePropertyName name, ParseNode* pn,
HandlePropertyName name, ParseNode* pn, Definition::Kind kind) Definition::Kind kind, bool declaringVarInCatchBody)
{ {
MOZ_ASSERT(!pn->isUsed()); MOZ_ASSERT(!pn->isUsed());
MOZ_ASSERT_IF(pn->isDefn(), pn->isPlaceholder()); MOZ_ASSERT_IF(pn->isDefn(), pn->isPlaceholder());
MOZ_ASSERT_IF(declaringVarInCatchBody, kind == Definition::VAR);
pn->setDefn(true); pn->setDefn(true);
Definition* prevDef = nullptr; Definition* prevDef = nullptr;
if (kind == Definition::LET || kind == Definition::CONSTANT) if (kind == Definition::LET || kind == Definition::CONSTANT)
prevDef = decls_.lookupFirst(name); prevDef = decls_.lookupFirst(name);
else if (declaringVarInCatchBody)
MOZ_ASSERT(decls_.lookupLast(name)->kind() != Definition::VAR);
else else
MOZ_ASSERT(!decls_.lookupFirst(name)); MOZ_ASSERT(!decls_.lookupFirst(name));
@ -306,8 +309,18 @@ ParseContext<FullParseHandler>::define(TokenStream& ts,
if (!checkLocalsOverflow(ts)) if (!checkLocalsOverflow(ts))
return false; return false;
} }
if (!decls_.addUnique(name, dn))
return false; // Set when declaring a 'var' binding despite a shadowing lexical binding
// of the same name already existing as a catch parameter. Covered by ES6
// Annex B.3.5. Also see note in Parser::bindVar
if (declaringVarInCatchBody) {
if (!decls_.addShadowedForAnnexB(name, dn))
return false;
} else {
if (!decls_.addUnique(name, dn))
return false;
}
break; break;
case Definition::LET: case Definition::LET:
@ -318,6 +331,8 @@ ParseContext<FullParseHandler>::define(TokenStream& ts,
if (atBodyLevel()) { if (atBodyLevel()) {
if (!bodyLevelLexicals_.append(dn)) if (!bodyLevelLexicals_.append(dn))
return false; return false;
if (!addBodyLevelLexicallyDeclaredName(ts, name))
return false;
if (!checkLocalsOverflow(ts)) if (!checkLocalsOverflow(ts))
return false; return false;
} }
@ -355,7 +370,7 @@ ParseContext<SyntaxParseHandler>::checkLocalsOverflow(TokenStream& ts)
template <> template <>
bool bool
ParseContext<SyntaxParseHandler>::define(TokenStream& ts, HandlePropertyName name, Node pn, ParseContext<SyntaxParseHandler>::define(TokenStream& ts, HandlePropertyName name, Node pn,
Definition::Kind kind) Definition::Kind kind, bool declaringVarInCatchBody)
{ {
MOZ_ASSERT(!decls_.lookupFirst(name)); MOZ_ASSERT(!decls_.lookupFirst(name));
@ -1520,7 +1535,10 @@ struct BindData
}; };
explicit BindData(ExclusiveContext* cx) explicit BindData(ExclusiveContext* cx)
: kind_(Uninitialized), nameNode_(ParseHandler::null()), letData_(cx) : kind_(Uninitialized),
nameNode_(ParseHandler::null()),
letData_(cx),
isForOf(false)
{} {}
void initLexical(VarContext varContext, JSOp op, StaticBlockScope* blockScope, void initLexical(VarContext varContext, JSOp op, StaticBlockScope* blockScope,
@ -1617,6 +1635,10 @@ struct BindData
bool isAnnexB_; // Whether this is a synthesized 'var' binding for Annex B.3. bool isAnnexB_; // Whether this is a synthesized 'var' binding for Annex B.3.
LetData letData_; LetData letData_;
public:
bool isForOf; // Whether this is binding a for-of head.
private:
bool isInitialized() { bool isInitialized() {
return kind_ != Uninitialized; return kind_ != Uninitialized;
} }
@ -3703,42 +3725,63 @@ Parser<FullParseHandler>::bindLexical(BindData<FullParseHandler>* data,
* define() right now. Otherwise, delay define until pushLetScope. * define() right now. Otherwise, delay define until pushLetScope.
*/ */
if (data->letData().varContext == HoistVars) { if (data->letData().varContext == HoistVars) {
// The reason we compare using >= instead of == on the block id is to if (dn) {
// detect redeclarations where a 'var' binding first appeared in a // The reason we compare using >= instead of == on the block id is to
// nested block: |{ var x; } let x;| // detect redeclarations where a 'var' binding first appeared in a
if (dn && dn->pn_blockid >= pc->blockid()) { // nested block: |{ var x; } let x;|
// XXXshu Used only for phasing in block-scope function early if (dn->pn_blockid >= pc->blockid()) {
// XXXshu errors. // XXXshu Used only for phasing in block-scope function early
// XXXshu // XXXshu errors.
// XXXshu Back out when major version >= 50. See [1]. // XXXshu
// XXXshu // XXXshu Back out when major version >= 50. See [1].
// XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10 // XXXshu
if (pn->isKind(PNK_FUNCTION) && dn->isKind(PNK_FUNCTION) && !pc->sc->strict()) { // XXXshu [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1235590#c10
if (!parser->makeDefIntoUse(dn, pn, name)) if (pn->isKind(PNK_FUNCTION) && dn->isKind(PNK_FUNCTION) && !pc->sc->strict()) {
return false; if (!parser->makeDefIntoUse(dn, pn, name))
return false;
MOZ_ASSERT(blockScope); MOZ_ASSERT(blockScope);
Shape* shape = blockScope->lastProperty()->search(cx, NameToId(name)); Shape* shape = blockScope->lastProperty()->search(cx, NameToId(name));
MOZ_ASSERT(shape); MOZ_ASSERT(shape);
uint32_t oldDefIndex = blockScope->shapeToIndex(*shape); uint32_t oldDefIndex = blockScope->shapeToIndex(*shape);
blockScope->updateDefinitionParseNode(oldDefIndex, dn, blockScope->updateDefinitionParseNode(oldDefIndex, dn,
reinterpret_cast<Definition*>(pn)); reinterpret_cast<Definition*>(pn));
parser->addTelemetry(JSCompartment::DeprecatedBlockScopeFunRedecl); parser->addTelemetry(JSCompartment::DeprecatedBlockScopeFunRedecl);
JSAutoByteString bytes; JSAutoByteString bytes;
if (!AtomToPrintableString(cx, name, &bytes)) if (!AtomToPrintableString(cx, name, &bytes))
return false; return false;
if (!parser->report(ParseWarning, false, null(), if (!parser->report(ParseWarning, false, null(),
JSMSG_DEPRECATED_BLOCK_SCOPE_FUN_REDECL, JSMSG_DEPRECATED_BLOCK_SCOPE_FUN_REDECL,
bytes.ptr())) bytes.ptr()))
{ {
return false; return false;
}
return true;
} }
return true; return parser->reportRedeclaration(pn, dn->kind(), name);
} }
return parser->reportRedeclaration(pn, dn->kind(), name); // Lexical declarations inside catch blocks need special
// attention. The catch parameter is in the enclosing block scope
// from the catch body, but lexical declarations inside the catch
// body block cannot shadow any catch parameter name.
//
// The reason the catch parameter is not in the same scope as the
// catch body is to satisfy Annex B.3.5, which allows 'var'
// redeclaration of the catch parameter but not of lexical
// bindings introduced in the catch body.
StmtInfoPC* enclosingStmt = pc->innermostScopeStmt()
? pc->innermostScopeStmt()->enclosing
: nullptr;
if (enclosingStmt && enclosingStmt->type == StmtType::CATCH &&
dn->pn_blockid == enclosingStmt->blockid)
{
MOZ_ASSERT(LexicalLookup(pc, name) == enclosingStmt);
return parser->reportRedeclaration(pn, dn->kind(), name);
}
} }
if (!pc->define(parser->tokenStream, name, pn, bindingKind)) if (!pc->define(parser->tokenStream, name, pn, bindingKind))
@ -3910,7 +3953,7 @@ Parser<ParseHandler>::AutoPushStmtInfoPC::makeInnermostLexicalScope(StaticBlockS
template <typename ParseHandler> template <typename ParseHandler>
static inline bool static inline bool
OuterLet(ParseContext<ParseHandler>* pc, StmtInfoPC* stmt, HandleAtom atom) HasOuterLexicalBinding(ParseContext<ParseHandler>* pc, StmtInfoPC* stmt, HandleAtom atom)
{ {
while (stmt->enclosingScope) { while (stmt->enclosingScope) {
stmt = LexicalLookup(pc, atom, stmt->enclosingScope); stmt = LexicalLookup(pc, atom, stmt->enclosingScope);
@ -3919,7 +3962,10 @@ OuterLet(ParseContext<ParseHandler>* pc, StmtInfoPC* stmt, HandleAtom atom)
if (stmt->type == StmtType::BLOCK) if (stmt->type == StmtType::BLOCK)
return true; return true;
} }
return false;
// Even if the binding doesn't appear in any blocks, it might still be a
// body-level lexical.
return pc->isBodyLevelLexicallyDeclaredName(atom);
} }
template <typename ParseHandler> template <typename ParseHandler>
@ -3937,6 +3983,27 @@ Parser<ParseHandler>::bindVar(BindData<ParseHandler>* data,
if (!parser->checkStrictBinding(name, pn)) if (!parser->checkStrictBinding(name, pn))
return false; return false;
// Special case var bindings in for-of heads for bailing out of syntax
// parsing to satisfy early errors per ES6 Annex B.3.5.
//
// 'var' bindings in for-of heads do not trigger Annex B.3.5 (i.e.,
// regular lexical redeclaration early errors apply). When syntax parsing
// we do not have enough binding information to detect early errors, so
// abort when binding vars in for-of head inside catch. We cannot use stmt
// as syntax parsing does not keep enough info to find the correct scope
// via LexicalLookup above.
if (data->isForOf) {
for (StmtInfoPC* scopeStmt = pc->innermostScopeStmt();
scopeStmt;
scopeStmt = scopeStmt->enclosingScope)
{
if (scopeStmt->type == StmtType::CATCH) {
if (!parser->abortIfSyntaxParser())
return false;
}
}
}
StmtInfoPC* stmt = LexicalLookup(pc, name); StmtInfoPC* stmt = LexicalLookup(pc, name);
if (stmt && stmt->type == StmtType::WITH) { if (stmt && stmt->type == StmtType::WITH) {
@ -3972,10 +4039,48 @@ Parser<ParseHandler>::bindVar(BindData<ParseHandler>* data,
DefinitionList::Range defs = pc->decls().lookupMulti(name); DefinitionList::Range defs = pc->decls().lookupMulti(name);
MOZ_ASSERT_IF(stmt, !defs.empty()); MOZ_ASSERT_IF(stmt, !defs.empty());
// TODOshu: ES6 Annex B.3.5 is not implemented.
if (defs.empty()) if (defs.empty())
return pc->define(parser->tokenStream, name, pn, Definition::VAR); return pc->define(parser->tokenStream, name, pn, Definition::VAR);
// ES6 Annex B.3.5 allows for var declarations inside catch blocks with
// the same name as the catch parameter.
bool nameIsCatchParam = stmt && stmt->type == StmtType::CATCH;
bool declaredVarInCatchBody = false;
if (nameIsCatchParam && !data->isForOf && !HasOuterLexicalBinding(pc, stmt, name)) {
declaredVarInCatchBody = true;
// Deoptimize the original name node, set the shadowing lexical
// name as aliased. Consider the following:
//
// try {} catch (e) { var e = 42; }
//
// While a new var 'e' is declared, the initializer '= 42' needs
// to be assigned to the lexically bound catch parameter
// 'e'. Deoptimizing the original parse node ensures that happen
// by emitting {BIND,SET}NAME ops.
//
// (Ideally, the 'e' in 'e = 42' can be linked up as a use to the
// def of the catch parameter. However, in practice this is messy
// because we then need to emit the synthesized var name node to
// ensure that functionless scopes get the proper DEFVAR emits.)
parser->handler.setFlag(pn, PND_DEOPTIMIZED);
// Synthesize a new 'var' binding if one does not exist.
DefinitionNode last = pc->decls().lookupLast(name);
if (last && parser->handler.getDefinitionKind(last) != Definition::VAR) {
parser->handler.setFlag(parser->handler.getDefinitionNode(last), PND_CLOSED);
Node synthesizedVarName = parser->newName(name);
if (!synthesizedVarName)
return false;
if (!pc->define(parser->tokenStream, name, synthesizedVarName, Definition::VAR,
/* declaringVarInCatchBody = */ true))
{
return false;
}
}
}
/* /*
* There was a previous declaration with the same name. The standard * There was a previous declaration with the same name. The standard
* disallows several forms of redeclaration. Critically, * disallows several forms of redeclaration. Critically,
@ -3992,11 +4097,9 @@ Parser<ParseHandler>::bindVar(BindData<ParseHandler>* data,
if (!parser->report(ParseExtraWarning, false, pn, JSMSG_VAR_HIDES_ARG, bytes.ptr())) if (!parser->report(ParseExtraWarning, false, pn, JSMSG_VAR_HIDES_ARG, bytes.ptr()))
return false; return false;
} else { } else {
bool inCatchBody = (stmt && stmt->type == StmtType::CATCH);
bool error = (dn_kind == Definition::IMPORT || bool error = (dn_kind == Definition::IMPORT ||
dn_kind == Definition::CONSTANT || dn_kind == Definition::CONSTANT ||
(dn_kind == Definition::LET && (dn_kind == Definition::LET && !declaredVarInCatchBody));
(!inCatchBody || OuterLet(pc, stmt, name))));
if (parser->options().extraWarningsOption if (parser->options().extraWarningsOption
? data->op() != JSOP_DEFVAR || dn_kind != Definition::VAR ? data->op() != JSOP_DEFVAR || dn_kind != Definition::VAR
@ -4007,7 +4110,7 @@ Parser<ParseHandler>::bindVar(BindData<ParseHandler>* data,
return false; return false;
ParseReportKind reporter = error ? ParseError : ParseExtraWarning; ParseReportKind reporter = error ? ParseError : ParseExtraWarning;
if (!(inCatchBody if (!(nameIsCatchParam && !declaredVarInCatchBody
? parser->report(reporter, false, pn, ? parser->report(reporter, false, pn,
JSMSG_REDECLARED_CATCH_IDENTIFIER, bytes.ptr()) JSMSG_REDECLARED_CATCH_IDENTIFIER, bytes.ptr())
: parser->report(reporter, false, pn, JSMSG_REDECLARED_VAR, : parser->report(reporter, false, pn, JSMSG_REDECLARED_VAR,
@ -4440,7 +4543,7 @@ Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockScope blockScope,
template <typename ParseHandler> template <typename ParseHandler>
typename ParseHandler::Node typename ParseHandler::Node
Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling) Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling, unsigned errorNumber)
{ {
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
@ -4452,7 +4555,7 @@ Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling)
if (!list) if (!list)
return null(); return null();
MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_IN_COMPOUND); MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, errorNumber);
return list; return list;
} }
@ -4522,12 +4625,14 @@ Parser<ParseHandler>::declarationPattern(Node decl, TokenKind tt, BindData<Parse
if (!matchInOrOf(&isForIn, &isForOf)) if (!matchInOrOf(&isForIn, &isForOf))
return null(); return null();
if (isForIn) if (isForIn) {
*forHeadKind = PNK_FORIN; *forHeadKind = PNK_FORIN;
else if (isForOf) } else if (isForOf) {
data->isForOf = true;
*forHeadKind = PNK_FOROF; *forHeadKind = PNK_FOROF;
else } else {
*forHeadKind = PNK_FORHEAD; *forHeadKind = PNK_FORHEAD;
}
if (*forHeadKind != PNK_FORHEAD) { if (*forHeadKind != PNK_FORHEAD) {
// |for (const ... in ...);| and |for (const ... of ...);| are // |for (const ... in ...);| and |for (const ... of ...);| are
@ -4727,12 +4832,15 @@ Parser<ParseHandler>::declarationName(Node decl, TokenKind tt, BindData<ParseHan
if (!matchInOrOf(&isForIn, &isForOf)) if (!matchInOrOf(&isForIn, &isForOf))
return null(); return null();
if (isForIn || isForOf) { if (isForIn) {
// XXX Uncomment this when fixing bug 449811. Until then, // XXX Uncomment this when fixing bug 449811. Until then,
// |for (const ... in/of ...)| remains an error. // |for (const ... in/of ...)| remains an error.
//constRequiringInitializer = false; //constRequiringInitializer = false;
*forHeadKind = isForIn ? PNK_FORIN : PNK_FOROF; *forHeadKind = PNK_FORIN;
} else if (isForOf) {
data->isForOf = true;
*forHeadKind = PNK_FOROF;
} else { } else {
*forHeadKind = PNK_FORHEAD; *forHeadKind = PNK_FORHEAD;
} }
@ -6780,10 +6888,9 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH); MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
Node catchBody = statements(yieldHandling); Node catchBody = blockStatement(yieldHandling, JSMSG_CURLY_AFTER_CATCH);
if (!catchBody) if (!catchBody)
return null(); return null();
MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_CATCH);
if (!catchGuard) if (!catchGuard)
hasUnconditionalCatch = true; hasUnconditionalCatch = true;

View File

@ -143,6 +143,9 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
DeclVector vars_; /* var/const definitions */ DeclVector vars_; /* var/const definitions */
DeclVector bodyLevelLexicals_; /* lexical definitions at body-level */ DeclVector bodyLevelLexicals_; /* lexical definitions at body-level */
typedef HashSet<JSAtom*, DefaultHasher<JSAtom*>, LifoAllocPolicy<Fallible>> DeclaredNameSet;
DeclaredNameSet bodyLevelLexicallyDeclaredNames_;
bool checkLocalsOverflow(TokenStream& ts); bool checkLocalsOverflow(TokenStream& ts);
public: public:
@ -181,7 +184,8 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
* 'pn' if they are in the scope of 'pn'. * 'pn' if they are in the scope of 'pn'.
* + Pre-existing placeholders in the scope of 'pn' have been removed. * + Pre-existing placeholders in the scope of 'pn' have been removed.
*/ */
bool define(TokenStream& ts, HandlePropertyName name, Node pn, Definition::Kind); bool define(TokenStream& ts, HandlePropertyName name, Node pn, Definition::Kind kind,
bool declaringVarInCatchBody = false);
/* /*
* Let definitions may shadow same-named definitions in enclosing scopes. * Let definitions may shadow same-named definitions in enclosing scopes.
@ -254,7 +258,7 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
// The comments atop checkDestructuring explain the distinction between // The comments atop checkDestructuring explain the distinction between
// assignment-like and declaration-like destructuring patterns, and why // assignment-like and declaration-like destructuring patterns, and why
// they need to be treated differently. // they need to be treated differently.
bool inDeclDestructuring:1; bool inDeclDestructuring:1;
ParseContext(Parser<ParseHandler>* prs, GenericParseContext* parent, ParseContext(Parser<ParseHandler>* prs, GenericParseContext* parent,
Node maybeFunction, SharedContext* sc, Directives* newDirectives) Node maybeFunction, SharedContext* sc, Directives* newDirectives)
@ -269,6 +273,7 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
args_(prs->context), args_(prs->context),
vars_(prs->context), vars_(prs->context),
bodyLevelLexicals_(prs->context), bodyLevelLexicals_(prs->context),
bodyLevelLexicallyDeclaredNames_(prs->alloc),
parserPC(&prs->pc), parserPC(&prs->pc),
oldpc(prs->pc), oldpc(prs->pc),
lexdeps(prs->context), lexdeps(prs->context),
@ -296,6 +301,16 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
return sc->staticScope(); return sc->staticScope();
} }
bool isBodyLevelLexicallyDeclaredName(HandleAtom name) {
return bodyLevelLexicallyDeclaredNames_.has(name);
}
bool addBodyLevelLexicallyDeclaredName(TokenStream& ts, HandleAtom name) {
if (!bodyLevelLexicallyDeclaredNames_.put(name))
return ts.reportError(JSMSG_OUT_OF_MEMORY);
return true;
}
// True if we are at the topmost level of a entire script or function body. // True if we are at the topmost level of a entire script or function body.
// For example, while parsing this code we would encounter f1 and f2 at // For example, while parsing this code we would encounter f1 and f2 at
// body level, but we would not encounter f3 or f4 at body level: // body level, but we would not encounter f3 or f4 at body level:
@ -658,7 +673,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
Node functionExpr(InvokedPrediction invoked = PredictUninvoked); Node functionExpr(InvokedPrediction invoked = PredictUninvoked);
Node statements(YieldHandling yieldHandling); Node statements(YieldHandling yieldHandling);
Node blockStatement(YieldHandling yieldHandling); Node blockStatement(YieldHandling yieldHandling,
unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND);
Node ifStatement(YieldHandling yieldHandling); Node ifStatement(YieldHandling yieldHandling);
Node doWhileStatement(YieldHandling yieldHandling); Node doWhileStatement(YieldHandling yieldHandling);
Node whileStatement(YieldHandling yieldHandling); Node whileStatement(YieldHandling yieldHandling);

View File

@ -0,0 +1,25 @@
// |jit-test| allow-oom
if (!('oomAfterAllocations' in this))
quit();
var g = newGlobal();
var dbg = new Debugger;
g.h = function h(d) {
if (d) {
dbg.addDebuggee(g);
var f = dbg.getNewestFrame().older;
f.st_p1((oomAfterAllocations(10)) + "foo = 'string of 42'");
}
}
g.eval("" + function f(d) {
g(d);
});
g.eval("" + function g(d) {
h(d);
});
g.eval("(" + function () {
for (i = 0; i < 5; i++)
f(false);
assertEq(f(true), "string of 42");
} + ")();");

View File

@ -1847,12 +1847,14 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfo)
if (act->hasRematerializedFrame(outerFp)) { if (act->hasRematerializedFrame(outerFp)) {
JitFrameIterator iter(cx); JitFrameIterator iter(cx);
size_t inlineDepth = numFrames; size_t inlineDepth = numFrames;
bool ok = true;
while (inlineDepth > 0) { while (inlineDepth > 0) {
if (iter.isBaselineJS() && if (iter.isBaselineJS()) {
!CopyFromRematerializedFrame(cx, act, outerFp, --inlineDepth, // We must attempt to copy all rematerialized frames over,
iter.baselineFrame())) // even if earlier ones failed, to invoke the proper frame
{ // cleanup in the Debugger.
return false; ok = CopyFromRematerializedFrame(cx, act, outerFp, --inlineDepth,
iter.baselineFrame());
} }
++iter; ++iter;
} }
@ -1860,6 +1862,9 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfo)
// After copying from all the rematerialized frames, remove them from // After copying from all the rematerialized frames, remove them from
// the table to keep the table up to date. // the table to keep the table up to date.
act->removeRematerializedFrame(outerFp); act->removeRematerializedFrame(outerFp);
if (!ok)
return false;
} }
JitSpew(JitSpew_BaselineBailouts, JitSpew(JitSpew_BaselineBailouts,

View File

@ -0,0 +1,105 @@
// Tests annex B.3.5.
assertThrowsInstanceOf(function () {
eval(`
function f() {
let x;
try {} catch (x) {
var x;
}
}
`);
}, SyntaxError);
assertThrowsInstanceOf(function () {
eval(`
function f() {
try {} catch (x) {
let y;
var y;
}
}
`);
}, SyntaxError);
assertThrowsInstanceOf(function () {
eval(`
function f() {
try {} catch (x) {
let x;
}
}
`);
}, SyntaxError);
assertThrowsInstanceOf(function () {
eval(`
function f() {
try {} catch (x) {
const x;
}
}
`);
}, SyntaxError);
var log = '';
var x = 'global-x';
function g() {
x = 'g';
try { throw 8; } catch (x) {
var x = 42;
log += x;
}
log += x;
}
g();
// Tests that var declaration is allowed in for-in head.
function h0() {
try {} catch (e) {
for (var e in {});
}
}
h0();
// Tests that var declaration is allowed in C-for head.
function h1() {
try {} catch (e) {
for (var e;;);
}
}
h1();
// Tests that redeclaring a var inside the catch is allowed.
function h3() {
var e;
try {} catch (e) {
var e;
}
}
h3();
// Tests that var declaration is not allowed in for-of head.
assertThrowsInstanceOf(function () {
eval(`
function h2() {
try {} catch (e) { for (var e of {}); }
}
log += 'unreached';
`);
}, SyntaxError);
if (typeof evaluate === "function") {
assertThrowsInstanceOf(function () {
evaluate(`
let y;
try {} catch (y) { var y; }
`);
}, SyntaxError);
}
assertEq(log, "42g");
if ("reportCompare" in this)
reportCompare(true, true)

View File

@ -2237,7 +2237,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
int index = 1; int index = 1;
while (nsDisplayItem* item = resultList.RemoveBottom()) { while (nsDisplayItem* item = resultList.RemoveBottom()) {
if (ItemParticipatesIn3DContext(this, item)) { if (ItemParticipatesIn3DContext(this, item) && !item->GetClip().HasClip()) {
// The frame of this item participates the same 3D context. // The frame of this item participates the same 3D context.
WrapSeparatorTransform(aBuilder, this, dirtyRect, WrapSeparatorTransform(aBuilder, this, dirtyRect,
&nonparticipants, &participants, index++); &nonparticipants, &participants, index++);

View File

@ -2152,7 +2152,7 @@ ScrollFrameHelper::ScrollToWithOrigin(nsPoint aScrollPosition,
mAsyncScroll = nullptr; mAsyncScroll = nullptr;
} }
if (nsLayoutUtils::AsyncPanZoomEnabled(mOuter)) { if (nsLayoutUtils::AsyncPanZoomEnabled(mOuter) && WantAsyncScroll()) {
if (mApzSmoothScrollDestination == Some(mDestination) && if (mApzSmoothScrollDestination == Some(mDestination) &&
mScrollGeneration == sScrollGenerationCounter) { mScrollGeneration == sScrollGenerationCounter) {
// If we already sent APZ a smooth-scroll request to this // If we already sent APZ a smooth-scroll request to this

View File

@ -1799,15 +1799,10 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext,
if (marginBStartIsAuto) { if (marginBStartIsAuto) {
if (marginBEndIsAuto) { if (marginBEndIsAuto) {
if (availMarginSpace < 0) { // Both 'margin-top' and 'margin-bottom' are 'auto', so they get
// FIXME: Note that the spec doesn't actually say we should do this! // equal values
margin.BEnd(cbwm) = availMarginSpace; margin.BStart(cbwm) = availMarginSpace / 2;
} else { margin.BEnd(cbwm) = availMarginSpace - margin.BStart(cbwm);
// Both margin-block-start and -end are 'auto', so they get
// equal values
margin.BStart(cbwm) = availMarginSpace / 2;
margin.BEnd(cbwm) = availMarginSpace - margin.BStart(cbwm);
}
} else { } else {
// Just margin-block-start is 'auto' // Just margin-block-start is 'auto'
margin.BStart(cbwm) = availMarginSpace; margin.BStart(cbwm) = availMarginSpace;

View File

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<title> absolutely positioned element should be vertically centered even if the height is bigger than that of the containing block (reference) - bug 812899</title>
<style>
body > div {
font-size: 16px;
position: relative;
border: red solid;
margin-top: 5em;
width: 5em;
height: 5em;
}
body > div > div {
position: absolute;
border: medium solid blue;
margin: -23px auto;
height: 150%;
width: 150%;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
}
</style>
<body>
<div>
<div></div>
</div>
</body>
</html>

View File

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<title> absolutely positioned element should be vertically centered even if the height is bigger than that of the containing block (reference) - bug 812899</title>
<style>
body > div {
font-size: 16px;
position: relative;
border: red solid;
margin-top: 5em;
width: 5em;
height: 5em;
}
body > div > div {
position: absolute;
border: medium solid blue;
margin: auto auto;
height: 150%;
width: 150%;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
}
</style>
<body>
<div>
<div></div>
</div>
</body>
</html>

View File

@ -19,6 +19,7 @@ skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.s
skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-7.html scroll-behavior-7.html?ref # see bug 1041833 skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-7.html scroll-behavior-7.html?ref # see bug 1041833
skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-8.html scroll-behavior-8.html?ref # see bug 1041833 skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-8.html scroll-behavior-8.html?ref # see bug 1041833
skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-9.html scroll-behavior-9.html?ref # see bug 1041833 skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-9.html scroll-behavior-9.html?ref # see bug 1041833
skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-10.html scroll-behavior-10.html?ref # see bug 1041833
skip-if((B2G&&browserIsRemote)||Mulet) HTTP == simple-1.html simple-1.html?ref # Initial mulet triage: parity with B2G/B2G Desktop skip-if((B2G&&browserIsRemote)||Mulet) HTTP == simple-1.html simple-1.html?ref # Initial mulet triage: parity with B2G/B2G Desktop
skip-if(B2G||Mulet) HTTP == subpixel-1.html#d subpixel-1-ref.html#d # Initial mulet triage: parity with B2G/B2G Desktop skip-if(B2G||Mulet) HTTP == subpixel-1.html#d subpixel-1-ref.html#d # Initial mulet triage: parity with B2G/B2G Desktop
fuzzy-if(Android,4,120) HTTP == text-1.html text-1.html?ref fuzzy-if(Android,4,120) HTTP == text-1.html text-1.html?ref

View File

@ -0,0 +1,64 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<head>
<meta charset="utf-8">
<title>Testcase for bug 1104356 smooth scrolling expected</title>
<style type="text/css">
html,body {
color: black;
background-color: white;
font-size: 16px;
padding: 0;
margin: 0;
}
#parent {
overflow: hidden;
width: 100px;
height: 100px;
}
#a_box {
position: relative;
left: 10px;
top: 10px;
width: 20px;
height: 20px;
background: blue;
}
#another_box {
position: relative;
left: 2000px;
top: 2000px;
width: 20px;
height: 20px;
background: green;
}
</style>
</head>
<body>
<div id="parent">
<div id="a_box"></div>
<div id="another_box"></div>
</div>
<script>
function doTest() {
if (document.location.search != '?ref') {
document.getElementById('parent').scrollTo({left: 10, top: 10, behavior: 'smooth'});
} else {
document.getElementById('parent').scrollLeft = 10;
document.getElementById('parent').scrollTop = 10;
}
// Allow smooth scrolling to complete before testing result
setTimeout(function() {
document.documentElement.removeAttribute("class");
}, 500);
}
window.addEventListener("MozReftestInvalidate", doTest, false);
</script>
</body>
</html>

View File

@ -9,7 +9,7 @@
*/ */
@media not print { @media not print {
.overflowing { .overflowingVertical, .overflowingHorizontalOnly {
cursor: zoom-out; cursor: zoom-out;
} }

View File

@ -1021,6 +1021,12 @@ Loader::CheckContentPolicy(nsIPrincipal* aSourcePrincipal,
nsISupports* aContext, nsISupports* aContext,
bool aIsPreload) bool aIsPreload)
{ {
// When performing a system load (e.g. aUseSystemPrincipal = true)
// then aSourcePrincipal == null; don't consult content policies.
if (!aSourcePrincipal) {
return NS_OK;
}
nsContentPolicyType contentPolicyType = nsContentPolicyType contentPolicyType =
aIsPreload ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD aIsPreload ? nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD
: nsIContentPolicy::TYPE_INTERNAL_STYLESHEET; : nsIContentPolicy::TYPE_INTERNAL_STYLESHEET;
@ -1412,11 +1418,6 @@ Loader::LoadSheet(SheetLoadData* aLoadData,
return NS_BINDING_ABORTED; return NS_BINDING_ABORTED;
} }
nsIPrincipal* triggeringPrincipal = aLoadData->mLoaderPrincipal;
if (!triggeringPrincipal) {
triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
}
SRIMetadata sriMetadata = aLoadData->mSheet->GetIntegrity(); SRIMetadata sriMetadata = aLoadData->mSheet->GetIntegrity();
if (aLoadData->mSyncLoad) { if (aLoadData->mSyncLoad) {
@ -1457,23 +1458,22 @@ Loader::LoadSheet(SheetLoadData* aLoadData,
// This is because of a case where the node is the document being styled and // This is because of a case where the node is the document being styled and
// the principal is the stylesheet (perhaps from a different origin) that is // the principal is the stylesheet (perhaps from a different origin) that is
// applying the styles. // applying the styles.
if (aLoadData->mRequestingNode) { if (aLoadData->mRequestingNode && aLoadData->mLoaderPrincipal) {
rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel), rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
aLoadData->mURI, aLoadData->mURI,
aLoadData->mRequestingNode, aLoadData->mRequestingNode,
triggeringPrincipal, aLoadData->mLoaderPrincipal,
securityFlags, securityFlags,
contentPolicyType); contentPolicyType);
} }
else { else {
// either we are loading something inside a document, in which case // either we are loading something inside a document, in which case
// we should always have a requestingNode, or we are loading something // we should always have a requestingNode, or we are loading something
// outside a document, in which case the triggeringPrincipal // outside a document, in which case the loadingPrincipal and the
// should always be the systemPrincipal. // triggeringPrincipal should always be the systemPrincipal.
MOZ_ASSERT(nsContentUtils::IsSystemPrincipal(triggeringPrincipal));
rv = NS_NewChannel(getter_AddRefs(channel), rv = NS_NewChannel(getter_AddRefs(channel),
aLoadData->mURI, aLoadData->mURI,
triggeringPrincipal, nsContentUtils::GetSystemPrincipal(),
securityFlags, securityFlags,
contentPolicyType); contentPolicyType);
} }
@ -1584,11 +1584,11 @@ Loader::LoadSheet(SheetLoadData* aLoadData,
// and a principal. This is because of a case where the node is the document // and a principal. This is because of a case where the node is the document
// being styled and the principal is the stylesheet (perhaps from a different // being styled and the principal is the stylesheet (perhaps from a different
// origin) that is applying the styles. // origin) that is applying the styles.
if (aLoadData->mRequestingNode) { if (aLoadData->mRequestingNode && aLoadData->mLoaderPrincipal) {
rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel), rv = NS_NewChannelWithTriggeringPrincipal(getter_AddRefs(channel),
aLoadData->mURI, aLoadData->mURI,
aLoadData->mRequestingNode, aLoadData->mRequestingNode,
triggeringPrincipal, aLoadData->mLoaderPrincipal,
securityFlags, securityFlags,
contentPolicyType, contentPolicyType,
loadGroup, loadGroup,
@ -1599,12 +1599,11 @@ Loader::LoadSheet(SheetLoadData* aLoadData,
else { else {
// either we are loading something inside a document, in which case // either we are loading something inside a document, in which case
// we should always have a requestingNode, or we are loading something // we should always have a requestingNode, or we are loading something
// outside a document, in which case the triggeringPrincipal // outside a document, in which case the loadingPrincipal and the
// should always be the systemPrincipal. // triggeringPrincipal should always be the systemPrincipal.
MOZ_ASSERT(nsContentUtils::IsSystemPrincipal(triggeringPrincipal));
rv = NS_NewChannel(getter_AddRefs(channel), rv = NS_NewChannel(getter_AddRefs(channel),
aLoadData->mURI, aLoadData->mURI,
triggeringPrincipal, nsContentUtils::GetSystemPrincipal(),
securityFlags, securityFlags,
contentPolicyType, contentPolicyType,
loadGroup, loadGroup,

View File

@ -23,6 +23,13 @@
left: 0; left: 0;
} }
img.overflowingVertical {
/* If we're overflowing vertically, we need to set margin-top to
0. Otherwise we'll end up trying to vertically center, and end
up cutting off the top part of the image. */
margin-top: 0;
}
.completeRotation { .completeRotation {
transition: transform 0.3s ease 0s; transition: transform 0.3s ease 0s;
} }

View File

@ -62,11 +62,11 @@ class JsepCodecDescription {
&& (mChannels == entry->channels)) { && (mChannels == entry->channels)) {
return ParametersMatch(fmt, remoteMsection); return ParametersMatch(fmt, remoteMsection);
} }
} else if (fmt.compare("9") && mName == "G722") { } else if (!fmt.compare("9") && mName == "G722") {
return true; return true;
} else if (fmt.compare("0") && mName == "PCMU") { } else if (!fmt.compare("0") && mName == "PCMU") {
return true; return true;
} else if (fmt.compare("8") && mName == "PCMA") { } else if (!fmt.compare("8") && mName == "PCMA") {
return true; return true;
} }
return false; return false;

View File

@ -2635,8 +2635,13 @@ pref("dom.ipc.plugins.unloadTimeoutSecs", 30);
// Asynchronous plugin initialization is on hold. // Asynchronous plugin initialization is on hold.
pref("dom.ipc.plugins.asyncInit.enabled", false); pref("dom.ipc.plugins.asyncInit.enabled", false);
#ifdef RELEASE_BUILD
// Allow the AsyncDrawing mode to be used for plugins.
pref("dom.ipc.plugins.asyncdrawing.enabled", false);
#else
// Allow the AsyncDrawing mode to be used for plugins. // Allow the AsyncDrawing mode to be used for plugins.
pref("dom.ipc.plugins.asyncdrawing.enabled", true); pref("dom.ipc.plugins.asyncdrawing.enabled", true);
#endif
pref("dom.ipc.processCount", 1); pref("dom.ipc.processCount", 1);

View File

@ -1270,5 +1270,7 @@ DigestOutputStream::IsNonBlocking(bool *retval)
return mOutputStream->IsNonBlocking(retval); return mOutputStream->IsNonBlocking(retval);
} }
#undef LOG_ENABLED
} // namespace net } // namespace net
} // namespace mozilla } // namespace mozilla

View File

@ -1,4 +1,5 @@
const GOOD_COOKIE = "GoodCookie=OMNOMNOM"; const GOOD_COOKIE = "GoodCookie=OMNOMNOM";
const SPACEY_COOKIE = "Spacey Cookie=Major Tom";
function run_test() { function run_test() {
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
@ -11,7 +12,8 @@ function run_test() {
cookieService.setCookieStringFromHttp(cookieURI, cookieURI, null, "BadCookie2=\v", null, null); cookieService.setCookieStringFromHttp(cookieURI, cookieURI, null, "BadCookie2=\v", null, null);
cookieService.setCookieStringFromHttp(cookieURI, cookieURI, null, "Bad\x07Name=illegal", null, null); cookieService.setCookieStringFromHttp(cookieURI, cookieURI, null, "Bad\x07Name=illegal", null, null);
cookieService.setCookieStringFromHttp(cookieURI, cookieURI, null, GOOD_COOKIE, null, null); cookieService.setCookieStringFromHttp(cookieURI, cookieURI, null, GOOD_COOKIE, null, null);
cookieService.setCookieStringFromHttp(cookieURI, cookieURI, null, SPACEY_COOKIE, null, null);
var storedCookie = cookieService.getCookieString(cookieURI, null); var storedCookie = cookieService.getCookieString(cookieURI, null);
do_check_eq(storedCookie, GOOD_COOKIE); do_check_eq(storedCookie, GOOD_COOKIE + "; " + SPACEY_COOKIE);
} }

View File

@ -46,11 +46,32 @@ class TryToolsMixin(TransferMixin):
harness_extra_args = None harness_extra_args = None
try_test_paths = {} try_test_paths = {}
known_try_arguments = { known_try_arguments = {
'--tag': { '--tag': ({
'action': 'append', 'action': 'append',
'dest': 'tags', 'dest': 'tags',
'default': None, 'default': None,
}, }, (
'browser-chrome',
'chrome',
'devtools-chrome',
'marionette',
'mochitest',
'web-plaftform-tests',
'xpcshell',
)),
'--setenv': ({
'action': 'append',
'dest': 'setenv',
'default': [],
'metavar': 'NAME=VALUE',
}, (
'browser-chrome',
'chrome',
'crashtest',
'devtools-chrome',
'mochitest',
'reftest',
)),
} }
def _extract_try_message(self): def _extract_try_message(self):
@ -124,7 +145,7 @@ class TryToolsMixin(TransferMixin):
return label_dict[val] return label_dict[val]
return '--%s' % val.replace('_', '-') return '--%s' % val.replace('_', '-')
for label, opts in self.known_try_arguments.iteritems(): for label, (opts, _) in self.known_try_arguments.iteritems():
if 'action' in opts and opts['action'] not in ('append', 'store', if 'action' in opts and opts['action'] not in ('append', 'store',
'store_true', 'store_false'): 'store_true', 'store_false'):
self.fatal('Try syntax does not support passing custom or store_const ' self.fatal('Try syntax does not support passing custom or store_const '
@ -139,22 +160,25 @@ class TryToolsMixin(TransferMixin):
self.try_test_paths = self._group_test_paths(args.try_test_paths) self.try_test_paths = self._group_test_paths(args.try_test_paths)
del args.try_test_paths del args.try_test_paths
out_args = [] out_args = defaultdict(list)
# This is a pretty hacky way to echo arguments down to the harness. # This is a pretty hacky way to echo arguments down to the harness.
# Hopefully this can be improved once we have a configuration system # Hopefully this can be improved once we have a configuration system
# in tree for harnesses that relies less on a command line. # in tree for harnesses that relies less on a command line.
for (arg, value) in vars(args).iteritems(): for arg, value in vars(args).iteritems():
if value: if value:
label = label_from_val(arg) label = label_from_val(arg)
if isinstance(value, bool): _, flavors = self.known_try_arguments[label]
# A store_true or store_false argument.
out_args.append(label)
elif isinstance(value, list):
out_args.extend(['%s=%s' % (label, el) for el in value])
else:
out_args.append('%s=%s' % (label, value))
self.harness_extra_args = out_args for f in flavors:
if isinstance(value, bool):
# A store_true or store_false argument.
out_args[f].append(label)
elif isinstance(value, list):
out_args[f].extend(['%s=%s' % (label, el) for el in value])
else:
out_args[f].append('%s=%s' % (label, value))
self.harness_extra_args = dict(out_args)
def _group_test_paths(self, args): def _group_test_paths(self, args):
rv = defaultdict(list) rv = defaultdict(list)
@ -169,8 +193,9 @@ class TryToolsMixin(TransferMixin):
def try_args(self, flavor): def try_args(self, flavor):
"""Get arguments, test_list derived from try syntax to apply to a command""" """Get arguments, test_list derived from try syntax to apply to a command"""
# TODO: Detect and reject incompatible arguments args = []
args = self.harness_extra_args[:] if self.harness_extra_args else [] if self.harness_extra_args:
args = self.harness_extra_args.get(flavor, [])[:]
if self.try_test_paths.get(flavor): if self.try_test_paths.get(flavor):
self.info('TinderboxPrint: Tests will be run from the following ' self.info('TinderboxPrint: Tests will be run from the following '

View File

@ -60,6 +60,7 @@ Cu.import("resource://gre/modules/ExtensionManagement.jsm");
ExtensionManagement.registerScript("chrome://extensions/content/ext-alarms.js"); ExtensionManagement.registerScript("chrome://extensions/content/ext-alarms.js");
ExtensionManagement.registerScript("chrome://extensions/content/ext-backgroundPage.js"); ExtensionManagement.registerScript("chrome://extensions/content/ext-backgroundPage.js");
ExtensionManagement.registerScript("chrome://extensions/content/ext-cookies.js"); ExtensionManagement.registerScript("chrome://extensions/content/ext-cookies.js");
ExtensionManagement.registerScript("chrome://extensions/content/ext-downloads.js");
ExtensionManagement.registerScript("chrome://extensions/content/ext-notifications.js"); ExtensionManagement.registerScript("chrome://extensions/content/ext-notifications.js");
ExtensionManagement.registerScript("chrome://extensions/content/ext-i18n.js"); ExtensionManagement.registerScript("chrome://extensions/content/ext-i18n.js");
ExtensionManagement.registerScript("chrome://extensions/content/ext-idle.js"); ExtensionManagement.registerScript("chrome://extensions/content/ext-idle.js");
@ -73,6 +74,7 @@ ExtensionManagement.registerScript("chrome://extensions/content/ext-test.js");
const BASE_SCHEMA = "chrome://extensions/content/schemas/manifest.json"; const BASE_SCHEMA = "chrome://extensions/content/schemas/manifest.json";
ExtensionManagement.registerSchema("chrome://extensions/content/schemas/cookies.json"); ExtensionManagement.registerSchema("chrome://extensions/content/schemas/cookies.json");
ExtensionManagement.registerSchema("chrome://extensions/content/schemas/downloads.json");
ExtensionManagement.registerSchema("chrome://extensions/content/schemas/extension.json"); ExtensionManagement.registerSchema("chrome://extensions/content/schemas/extension.json");
ExtensionManagement.registerSchema("chrome://extensions/content/schemas/extension_types.json"); ExtensionManagement.registerSchema("chrome://extensions/content/schemas/extension_types.json");
ExtensionManagement.registerSchema("chrome://extensions/content/schemas/i18n.json"); ExtensionManagement.registerSchema("chrome://extensions/content/schemas/i18n.json");

View File

@ -0,0 +1,29 @@
"use strict";
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
const {
ignoreEvent,
} = ExtensionUtils;
extensions.registerSchemaAPI("downloads", "downloads", (extension, context) => {
return {
downloads: {
// When we do open(), check for additional downloads.open permission.
// i.e.:
// open(downloadId) {
// if (!extension.hasPermission("downloads.open")) {
// throw new context.cloneScope.Error("Permission denied because 'downloads.open' permission is missing.");
// }
// ...
// }
// likewise for setShelfEnabled() and the "download.shelf" permission
onCreated: ignoreEvent(context, "downloads.onCreated"),
onErased: ignoreEvent(context, "downloads.onErased"),
onChanged: ignoreEvent(context, "downloads.onChanged"),
onDeterminingFilename: ignoreEvent(context, "downloads.onDeterminingFilename"),
},
};
});

View File

@ -7,6 +7,7 @@ toolkit.jar:
content/extensions/ext-alarms.js content/extensions/ext-alarms.js
content/extensions/ext-backgroundPage.js content/extensions/ext-backgroundPage.js
content/extensions/ext-cookies.js content/extensions/ext-cookies.js
content/extensions/ext-downloads.js
content/extensions/ext-notifications.js content/extensions/ext-notifications.js
content/extensions/ext-i18n.js content/extensions/ext-i18n.js
content/extensions/ext-idle.js content/extensions/ext-idle.js

View File

@ -0,0 +1,763 @@
[
{
"namespace": "manifest",
"types": [
{
"$extend": "Permission",
"choices": [{
"type": "string",
"enum": [
"downloads",
"downloads.open",
"downloads.shelf"
]
}]
}
]
},
{
"namespace": "downloads",
"types": [
{
"id": "FilenameConflictAction",
"type": "string",
"enum": [
"uniqify",
"overwrite",
"prompt"
]
},
{
"id": "InterruptReason",
"type": "string",
"enum": [
"FILE_FAILED",
"FILE_ACCESS_DENIED",
"FILE_NO_SPACE",
"FILE_NAME_TOO_LONG",
"FILE_TOO_LARGE",
"FILE_VIRUS_INFECTED",
"FILE_TRANSIENT_ERROR",
"FILE_BLOCKED",
"FILE_SECURITY_CHECK_FAILED",
"FILE_TOO_SHORT",
"NETWORK_FAILED",
"NETWORK_TIMEOUT",
"NETWORK_DISCONNECTED",
"NETWORK_SERVER_DOWN",
"NETWORK_INVALID_REQUEST",
"SERVER_FAILED",
"SERVER_NO_RANGE",
"SERVER_BAD_CONTENT",
"SERVER_UNAUTHORIZED",
"SERVER_CERT_PROBLEM",
"SERVER_FORBIDDEN",
"USER_CANCELED",
"USER_SHUTDOWN",
"CRASH"
]
},
{
"id": "DangerType",
"type": "string",
"enum": [
"file",
"url",
"content",
"uncommon",
"host",
"unwanted",
"safe",
"accepted"
],
"description": "<dl><dt>file</dt><dd>The download's filename is suspicious.</dd><dt>url</dt><dd>The download's URL is known to be malicious.</dd><dt>content</dt><dd>The downloaded file is known to be malicious.</dd><dt>uncommon</dt><dd>The download's URL is not commonly downloaded and could be dangerous.</dd><dt>safe</dt><dd>The download presents no known danger to the user's computer.</dd></dl>These string constants will never change, however the set of DangerTypes may change."
},
{
"id": "State",
"type": "string",
"enum": [
"in_progress",
"interrupted",
"complete"
],
"description": "<dl><dt>in_progress</dt><dd>The download is currently receiving data from the server.</dd><dt>interrupted</dt><dd>An error broke the connection with the file host.</dd><dt>complete</dt><dd>The download completed successfully.</dd></dl>These string constants will never change, however the set of States may change."
},
{
"id": "DownloadItem",
"type": "object",
"properties": {
"id": {
"description": "An identifier that is persistent across browser sessions.",
"type": "integer"
},
"url": {
"description": "Absolute URL.",
"type": "string"
},
"referrer": {
"type": "string"
},
"filename": {
"description": "Absolute local path.",
"type": "string"
},
"incognito": {
"description": "False if this download is recorded in the history, true if it is not recorded.",
"type": "boolean"
},
"danger": {
"$ref": "DangerType",
"description": "Indication of whether this download is thought to be safe or known to be suspicious."
},
"mime": {
"description": "The file's MIME type.",
"type": "string"
},
"startTime": {
"description": "Number of milliseconds between the unix epoch and when this download began.",
"type": "string"
},
"endTime": {
"description": "Number of milliseconds between the unix epoch and when this download ended.",
"optional": true,
"type": "string"
},
"estimatedEndTime": {
"type": "string",
"optional": true
},
"state": {
"$ref": "State",
"description": "Indicates whether the download is progressing, interrupted, or complete."
},
"paused": {
"description": "True if the download has stopped reading data from the host, but kept the connection open.",
"type": "boolean"
},
"canResume": {
"type": "boolean"
},
"error": {
"description": "Number indicating why a download was interrupted.",
"optional": true,
"$ref": "InterruptReason"
},
"bytesReceived": {
"description": "Number of bytes received so far from the host, without considering file compression.",
"type": "number"
},
"totalBytes": {
"description": "Number of bytes in the whole file, without considering file compression, or -1 if unknown.",
"type": "number"
},
"fileSize": {
"description": "Number of bytes in the whole file post-decompression, or -1 if unknown.",
"type": "number"
},
"exists": {
"type": "boolean"
},
"byExtensionId": {
"type": "string",
"optional": true
},
"byExtensionName": {
"type": "string",
"optional": true
}
}
},
{
"id": "StringDelta",
"type": "object",
"properties": {
"current": {
"optional": true,
"type": "string"
},
"previous": {
"optional": true,
"type": "string"
}
}
},
{
"id": "DoubleDelta",
"type": "object",
"properties": {
"current": {
"optional": true,
"type": "number"
},
"previous": {
"optional": true,
"type": "number"
}
}
},
{
"id": "BooleanDelta",
"type": "object",
"properties": {
"current": {
"optional": true,
"type": "boolean"
},
"previous": {
"optional": true,
"type": "boolean"
}
}
}
],
"functions": [
{
"name": "download",
"type": "function",
"unsupported": true,
"description": "Download a URL. If the URL uses the HTTP[S] protocol, then the request will include all cookies currently set for its hostname. If both <code>filename</code> and <code>saveAs</code> are specified, then the Save As dialog will be displayed, pre-populated with the specified <code>filename</code>. If the download started successfully, <code>callback</code> will be called with the new <a href='#type-DownloadItem'>DownloadItem</a>'s <code>downloadId</code>. If there was an error starting the download, then <code>callback</code> will be called with <code>downloadId=undefined</code> and <a href='extension.html#property-lastError'>chrome.extension.lastError</a> will contain a descriptive string. The error strings are not guaranteed to remain backwards compatible between releases. You must not parse it.",
"parameters": [
{
"description": "What to download and how.",
"name": "options",
"type": "object",
"properties": {
"url": {
"description": "The URL to download.",
"type": "string"
},
"filename": {
"description": "A file path relative to the Downloads directory to contain the downloaded file.",
"optional": true,
"type": "string"
},
"conflictAction": {
"$ref": "FilenameConflictAction",
"optional": true
},
"saveAs": {
"description": "Use a file-chooser to allow the user to select a filename.",
"optional": true,
"type": "boolean"
},
"method": {
"description": "The HTTP method to use if the URL uses the HTTP[S] protocol.",
"enum": [
"GET",
"POST"
],
"optional": true,
"type": "string"
},
"headers": {
"optional": true,
"type": "array",
"description": "Extra HTTP headers to send with the request if the URL uses the HTTP[s] protocol. Each header is represented as a dictionary containing the keys <code>name</code> and either <code>value</code> or <code>binaryValue</code>, restricted to those allowed by XMLHttpRequest.",
"items": {
"type": "object",
"properties": {
"name": {
"description": "Name of the HTTP header.",
"type": "string"
},
"value": {
"description": "Value of the HTTP header.",
"type": "string"
}
}
}
},
"body": {
"description": "Post body.",
"optional": true,
"type": "string"
}
}
},
{
"name": "callback",
"type": "function",
"optional": true,
"parameters": [
{
"name": "downloadId",
"type": "integer"
}
]
}
]
},
{
"name": "search",
"type": "function",
"unsupported": true,
"description": "Find <a href='#type-DownloadItem'>DownloadItems</a>. Set <code>query</code> to the empty object to get all <a href='#type-DownloadItem'>DownloadItems</a>. To get a specific <a href='#type-DownloadItem'>DownloadItem</a>, set only the <code>id</code> field.",
"parameters": [
{
"name": "query",
"type": "object",
"properties": {
"query": {
"description": "This array of search terms limits results to <a href='#type-DownloadItem'>DownloadItems</a> whose <code>filename</code> or <code>url</code> contain all of the search terms that do not begin with a dash '-' and none of the search terms that do begin with a dash.",
"optional": true,
"type": "array",
"items": { "type": "string" }
},
"startedBefore": {
"description": "Limits results to downloads that started before the given ms since the epoch.",
"optional": true,
"type": "string"
},
"startedAfter": {
"description": "Limits results to downloads that started after the given ms since the epoch.",
"optional": true,
"type": "string"
},
"endedBefore": {
"description": "Limits results to downloads that ended before the given ms since the epoch.",
"optional": true,
"type": "string"
},
"endedAfter": {
"description": "Limits results to downloads that ended after the given ms since the epoch.",
"optional": true,
"type": "string"
},
"totalBytesGreater": {
"description": "Limits results to downloads whose totalBytes is greater than the given integer.",
"optional": true,
"type": "number"
},
"totalBytesLess": {
"description": "Limits results to downloads whose totalBytes is less than the given integer.",
"optional": true,
"type": "number"
},
"filenameRegex": {
"description": "Limits results to <a href='#type-DownloadItem'>DownloadItems</a> whose <code>filename</code> matches the given regular expression.",
"optional": true,
"type": "string"
},
"urlRegex": {
"description": "Limits results to <a href='#type-DownloadItem'>DownloadItems</a> whose <code>url</code> matches the given regular expression.",
"optional": true,
"type": "string"
},
"limit": {
"description": "Setting this integer limits the number of results. Otherwise, all matching <a href='#type-DownloadItem'>DownloadItems</a> will be returned.",
"optional": true,
"type": "integer"
},
"orderBy": {
"description": "Setting elements of this array to <a href='#type-DownloadItem'>DownloadItem</a> properties in order to sort the search results. For example, setting <code>orderBy='startTime'</code> sorts the <a href='#type-DownloadItem'>DownloadItems</a> by their start time in ascending order. To specify descending order, prefix <code>orderBy</code> with a hyphen: '-startTime'.",
"optional": true,
"type": "array",
"items": { "type": "string" }
},
"id": {
"type": "integer",
"optional": true
},
"url": {
"description": "Absolute URL.",
"optional": true,
"type": "string"
},
"filename": {
"description": "Absolute local path.",
"optional": true,
"type": "string"
},
"danger": {
"$ref": "DangerType",
"description": "Indication of whether this download is thought to be safe or known to be suspicious.",
"optional": true
},
"mime": {
"description": "The file's MIME type.",
"optional": true,
"type": "string"
},
"startTime": {
"optional": true,
"type": "string"
},
"endTime": {
"optional": true,
"type": "string"
},
"state": {
"$ref": "State",
"description": "Indicates whether the download is progressing, interrupted, or complete.",
"optional": true
},
"paused": {
"description": "True if the download has stopped reading data from the host, but kept the connection open.",
"optional": true,
"type": "boolean"
},
"error": {
"description": "Why a download was interrupted.",
"optional": true,
"$ref": "InterruptReason"
},
"bytesReceived": {
"description": "Number of bytes received so far from the host, without considering file compression.",
"optional": true,
"type": "number"
},
"totalBytes": {
"description": "Number of bytes in the whole file, without considering file compression, or -1 if unknown.",
"optional": true,
"type": "number"
},
"fileSize": {
"description": "Number of bytes in the whole file post-decompression, or -1 if unknown.",
"optional": true,
"type": "number"
},
"exists": {
"type": "boolean",
"optional": true
}
}
},
{
"name": "callback",
"type": "function",
"parameters": [
{
"items": {
"$ref": "DownloadItem"
},
"name": "results",
"type": "array"
}
]
}
]
},
{
"name": "pause",
"type": "function",
"unsupported": true,
"description": "Pause the download. If the request was successful the download is in a paused state. Otherwise <a href='extension.html#property-lastError'>chrome.extension.lastError</a> contains an error message. The request will fail if the download is not active.",
"parameters": [
{
"description": "The id of the download to pause.",
"name": "downloadId",
"type": "integer"
},
{
"name": "callback",
"optional": true,
"parameters": [],
"type": "function"
}
]
},
{
"name": "resume",
"type": "function",
"unsupported": true,
"description": "Resume a paused download. If the request was successful the download is in progress and unpaused. Otherwise <a href='extension.html#property-lastError'>chrome.extension.lastError</a> contains an error message. The request will fail if the download is not active.",
"parameters": [
{
"description": "The id of the download to resume.",
"name": "downloadId",
"type": "integer"
},
{
"name": "callback",
"optional": true,
"parameters": [],
"type": "function"
}
]
},
{
"name": "cancel",
"type": "function",
"unsupported": true,
"description": "Cancel a download. When <code>callback</code> is run, the download is cancelled, completed, interrupted or doesn't exist anymore.",
"parameters": [
{
"description": "The id of the download to cancel.",
"name": "downloadId",
"type": "integer"
},
{
"name": "callback",
"optional": true,
"parameters": [],
"type": "function"
}
]
},
{
"name": "getFileIcon",
"type": "function",
"unsupported": true,
"description": "Retrieve an icon for the specified download. For new downloads, file icons are available after the <a href='#event-onCreated'>onCreated</a> event has been received. The image returned by this function while a download is in progress may be different from the image returned after the download is complete. Icon retrieval is done by querying the underlying operating system or toolkit depending on the platform. The icon that is returned will therefore depend on a number of factors including state of the download, platform, registered file types and visual theme. If a file icon cannot be determined, <a href='extension.html#property-lastError'>chrome.extension.lastError</a> will contain an error message.",
"parameters": [
{
"description": "The identifier for the download.",
"name": "downloadId",
"type": "integer"
},
{
"name": "options",
"optional": true,
"properties": {
"size": {
"description": "The size of the icon. The returned icon will be square with dimensions size * size pixels. The default size for the icon is 32x32 pixels.",
"optional": true,
"type": "integer"
}
},
"type": "object"
},
{
"name": "callback",
"parameters": [
{
"name": "iconURL",
"optional": true,
"type": "string"
}
],
"type": "function"
}
]
},
{
"name": "open",
"type": "function",
"unsupported": true,
"description": "Open the downloaded file.",
"parameters": [
{
"name": "downloadId",
"type": "integer"
}
]
},
{
"name": "show",
"type": "function",
"unsupported": true,
"description": "Show the downloaded file in its folder in a file manager.",
"parameters": [
{
"name": "downloadId",
"type": "integer"
}
]
},
{
"name": "showDefaultFolder",
"type": "function",
"unsupported": true,
"parameters": []
},
{
"name": "erase",
"type": "function",
"unsupported": true,
"description": "Erase matching <a href='#type-DownloadItem'>DownloadItems</a> from history",
"parameters": [
{
"name": "query",
"type": "object",
"properties": {
"TODO": {
"type": "string",
"description": "complete me..."
}
}
},
{
"name": "callback",
"type": "function",
"optional": true,
"parameters": [
{
"items": {
"type": "integer"
},
"name": "erasedIds",
"type": "array"
}
]
}
]
},
{
"name": "removeFile",
"type": "function",
"unsupported": true,
"parameters": [
{
"name": "downloadId",
"type": "integer"
},
{
"name": "callback",
"type": "function",
"optional": true,
"parameters": [ ]
}
]
},
{
"description": "Prompt the user to either accept or cancel a dangerous download. <code>acceptDanger()</code> does not automatically accept dangerous downloads.",
"name": "acceptDanger",
"unsupported": true,
"parameters": [
{
"name": "downloadId",
"type": "integer"
},
{
"name": "callback",
"type": "function",
"optional": true,
"parameters": [ ]
}
],
"type": "function"
},
{
"description": "Initiate dragging the file to another application.",
"name": "drag",
"unsupported": true,
"parameters": [
{
"name": "downloadId",
"type": "integer"
}
],
"type": "function"
},
{
"name": "setShelfEnabled",
"type": "function",
"unsupported": true,
"parameters": [
{
"name": "enabled",
"type": "boolean"
}
]
}
],
"events": [
{
"description": "This event fires with the <a href='#type-DownloadItem'>DownloadItem</a> object when a download begins.",
"name": "onCreated",
"unsupported": true,
"parameters": [
{
"$ref": "DownloadItem",
"name": "downloadItem"
}
],
"type": "function"
},
{
"description": "Fires with the <code>downloadId</code> when a download is erased from history.",
"name": "onErased",
"unsupported": true,
"parameters": [
{
"name": "downloadId",
"description": "The <code>id</code> of the <a href='#type-DownloadItem'>DownloadItem</a> that was erased.",
"type": "integer"
}
],
"type": "function"
},
{
"name": "onChanged",
"description": "When any of a <a href='#type-DownloadItem'>DownloadItem</a>'s properties except <code>bytesReceived</code> changes, this event fires with the <code>downloadId</code> and an object containing the properties that changed.",
"unsupported": true,
"parameters": [
{
"name": "downloadDelta",
"type": "object",
"properties": {
"id": {
"description": "The <code>id</code> of the <a href='#type-DownloadItem'>DownloadItem</a> that changed.",
"type": "integer"
},
"url": {
"description": "Describes a change in a <a href='#type-DownloadItem'>DownloadItem</a>'s <code>url</code>.",
"optional": true,
"$ref": "StringDelta"
},
"filename": {
"description": "Describes a change in a <a href='#type-DownloadItem'>DownloadItem</a>'s <code>filename</code>.",
"optional": true,
"$ref": "StringDelta"
},
"danger": {
"description": "Describes a change in a <a href='#type-DownloadItem'>DownloadItem</a>'s <code>danger</code>.",
"optional": true,
"$ref": "StringDelta"
},
"mime": {
"description": "Describes a change in a <a href='#type-DownloadItem'>DownloadItem</a>'s <code>mime</code>.",
"optional": true,
"$ref": "StringDelta"
},
"startTime": {
"description": "Describes a change in a <a href='#type-DownloadItem'>DownloadItem</a>'s <code>startTime</code>.",
"optional": true,
"$ref": "StringDelta"
},
"endTime": {
"description": "Describes a change in a <a href='#type-DownloadItem'>DownloadItem</a>'s <code>endTime</code>.",
"optional": true,
"$ref": "StringDelta"
},
"state": {
"description": "Describes a change in a <a href='#type-DownloadItem'>DownloadItem</a>'s <code>state</code>.",
"optional": true,
"$ref": "StringDelta"
},
"canResume": {
"optional": true,
"$ref": "BooleanDelta"
},
"paused": {
"description": "Describes a change in a <a href='#type-DownloadItem'>DownloadItem</a>'s <code>paused</code>.",
"optional": true,
"$ref": "BooleanDelta"
},
"error": {
"description": "Describes a change in a <a href='#type-DownloadItem'>DownloadItem</a>'s <code>error</code>.",
"optional": true,
"$ref": "StringDelta"
},
"totalBytes": {
"description": "Describes a change in a <a href='#type-DownloadItem'>DownloadItem</a>'s <code>totalBytes</code>.",
"optional": true,
"$ref": "DoubleDelta"
},
"fileSize": {
"description": "Describes a change in a <a href='#type-DownloadItem'>DownloadItem</a>'s <code>fileSize</code>.",
"optional": true,
"$ref": "DoubleDelta"
},
"exists": {
"optional": true,
"$ref": "BooleanDelta"
}
}
}
],
"type": "function"
}
]
}
]

View File

@ -5,6 +5,7 @@
toolkit.jar: toolkit.jar:
% content extensions %content/extensions/ % content extensions %content/extensions/
content/extensions/schemas/cookies.json content/extensions/schemas/cookies.json
content/extensions/schemas/downloads.json
content/extensions/schemas/extension.json content/extensions/schemas/extension.json
content/extensions/schemas/extension_types.json content/extensions/schemas/extension_types.json
content/extensions/schemas/i18n.json content/extensions/schemas/i18n.json

View File

@ -30,6 +30,7 @@ support-files =
skip-if = buildapp == 'b2g' # runat != document_idle is not supported. skip-if = buildapp == 'b2g' # runat != document_idle is not supported.
[test_ext_contentscript_create_iframe.html] [test_ext_contentscript_create_iframe.html]
[test_ext_contentscript_api_injection.html] [test_ext_contentscript_api_injection.html]
[test_ext_downloads.html]
[test_ext_i18n_css.html] [test_ext_i18n_css.html]
[test_ext_generate.html] [test_ext_generate.html]
[test_ext_idle.html] [test_ext_idle.html]

View File

@ -0,0 +1,48 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebExtension test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="text/javascript">
"use strict";
add_task(function* test_downloads_api_namespace_and_permissions() {
function backgroundScript() {
browser.test.assertTrue(!!chrome.downloads, "`downloads` API is present.");
browser.test.assertTrue(!!chrome.downloads.FilenameConflictAction,
"`downloads.FilenameConflictAction` enum is present.");
browser.test.assertTrue(!!chrome.downloads.InterruptReason,
"`downloads.InterruptReason` enum is present.");
browser.test.assertTrue(!!chrome.downloads.DangerType,
"`downloads.DangerType` enum is present.");
browser.test.assertTrue(!!chrome.downloads.State,
"`downloads.State` enum is present.");
browser.test.notifyPass("downloads tests");
}
let extensionData = {
background: "(" + backgroundScript.toString() + ")()",
manifest: {
permissions: ["downloads", "downloads.open", "downloads.shelf"],
},
};
let extension = ExtensionTestUtils.loadExtension(extensionData);
yield extension.startup();
info("extension loaded");
yield extension.awaitFinish("downloads tests");
yield extension.unload();
info("extension unloaded");
});
</script>
</body>
</html>

View File

@ -52,6 +52,7 @@ using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;
static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nullptr; static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nullptr;
static bool sAllowOfflineCache = true;
nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::mAllowedDomains = nullptr; nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::mAllowedDomains = nullptr;
@ -247,6 +248,10 @@ nsOfflineCacheUpdateService::nsOfflineCacheUpdateService()
, mUpdateRunning(false) , mUpdateRunning(false)
, mLowFreeSpace(false) , mLowFreeSpace(false)
{ {
MOZ_ASSERT(NS_IsMainThread());
Preferences::AddBoolVarCache(&sAllowOfflineCache,
"browser.cache.offline.enable",
true);
} }
nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService() nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService()
@ -605,6 +610,10 @@ OfflineAppPermForPrincipal(nsIPrincipal *aPrincipal,
{ {
*aAllowed = false; *aAllowed = false;
if (!sAllowOfflineCache) {
return NS_OK;
}
if (!aPrincipal) if (!aPrincipal)
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
@ -696,6 +705,10 @@ nsOfflineCacheUpdateService::AllowOfflineApp(nsIPrincipal *aPrincipal)
{ {
nsresult rv; nsresult rv;
if (!sAllowOfflineCache) {
return NS_ERROR_NOT_AVAILABLE;
}
if (GeckoProcessType_Default != XRE_GetProcessType()) { if (GeckoProcessType_Default != XRE_GetProcessType()) {
ContentChild* child = ContentChild::GetSingleton(); ContentChild* child = ContentChild::GetSingleton();

View File

@ -217,7 +217,7 @@ nsPrefetchNode::OnStopRequest(nsIRequest *aRequest,
{ {
LOG(("done prefetching [status=%x]\n", aStatus)); LOG(("done prefetching [status=%x]\n", aStatus));
if (mBytesRead == 0 && aStatus == NS_OK) { if (mBytesRead == 0 && aStatus == NS_OK && mChannel) {
// we didn't need to read (because LOAD_ONLY_IF_MODIFIED was // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
// specified), but the object should report loadedSize as if it // specified), but the object should report loadedSize as if it
// did. // did.