Merge inbound to m-c.

This commit is contained in:
Ryan VanderMeulen 2013-09-13 16:07:02 -04:00
commit f16ab57207
117 changed files with 2877 additions and 2252 deletions

View File

@ -341,6 +341,15 @@ function setupSearchEngine()
}
/**
* Inform the test harness that we're done loading the page.
*/
function loadSucceeded()
{
var event = new CustomEvent("AboutHomeLoadSnippetsSucceeded", {bubbles:true});
document.dispatchEvent(event);
}
/**
* Update the local snippets from the remote storage, then show them through
* showSnippets.
@ -350,6 +359,10 @@ function loadSnippets()
if (!gSnippetsMap)
throw new Error("Snippets map has not properly been initialized");
// Allow tests to modify the snippets map before using it.
var event = new CustomEvent("AboutHomeLoadSnippets", {bubbles:true});
document.dispatchEvent(event);
// Check cached snippets version.
let cachedVersion = gSnippetsMap.get("snippets-cached-version") || 0;
let currentVersion = document.documentElement.getAttribute("snippetsVersion");
@ -370,6 +383,7 @@ function loadSnippets()
xhr.open("GET", updateURL, true);
} catch (ex) {
showSnippets();
loadSucceeded();
return;
}
// Even if fetching should fail we don't want to spam the server, thus
@ -385,10 +399,12 @@ function loadSnippets()
gSnippetsMap.set("snippets-cached-version", currentVersion);
}
showSnippets();
loadSucceeded();
};
xhr.send(null);
} else {
showSnippets();
loadSucceeded();
}
}

View File

@ -2753,6 +2753,10 @@
onget="return this.mCurrentBrowser.currentURI;"
readonly="true"/>
<property name="finder"
onget="return this.mCurrentBrowser.finder"
readonly="true"/>
<property name="docShell"
onget="return this.mCurrentBrowser.docShell"
readonly="true"/>

View File

@ -17,6 +17,7 @@ registerCleanupFunction(function() {
Services.prefs.clearUserPref("network.cookie.lifetimePolicy");
Services.prefs.clearUserPref("browser.rights.override");
Services.prefs.clearUserPref("browser.rights." + gRightsVersion + ".shown");
Services.prefs.clearUserPref("browser.aboutHomeSnippets.updateUrl");
});
let gTests = [
@ -338,20 +339,25 @@ function test()
for (let test of gTests) {
info(test.desc);
// Make sure we don't try to load snippets from the network.
Services.prefs.setCharPref("browser.aboutHomeSnippets.updateUrl", "nonexistent://test");
if (test.beforeRun)
yield test.beforeRun();
let tab = yield promiseNewTabLoadEvent("about:home", "DOMContentLoaded");
// Create a tab to run the test.
let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
// Add an event handler to modify the snippets map once it's ready.
let snippetsPromise = promiseSetupSnippetsMap(tab, test.setup);
// Start loading about:home and wait for it to complete.
yield promiseTabLoadEvent(tab, "about:home", "AboutHomeLoadSnippetsSucceeded");
// This promise should already be resolved since the page is done,
// but we still want to get the snippets map out of it.
let snippetsMap = yield snippetsPromise;
// Must wait for both the snippets map and the browser attributes, since
// can't guess the order they will happen.
// So, start listening now, but verify the promise is fulfilled only
// after the snippets map setup.
let promise = promiseBrowserAttributes(tab);
// Prepare the snippets map with default values, then run the test setup.
let snippetsMap = yield promiseSetupSnippetsMap(tab, test.setup);
// Ensure browser has set attributes already, or wait for them.
yield promise;
info("Running test");
yield test.run(snippetsMap);
info("Cleanup");
@ -364,29 +370,31 @@ function test()
}
/**
* Creates a new tab and waits for a load event.
* Starts a load in an existing tab and waits for it to finish (via some event).
*
* @param aTab
* The tab to load into.
* @param aUrl
* The url to load in a new tab.
* The url to load.
* @param aEvent
* The load event type to wait for. Defaults to "load".
* @return {Promise} resolved when the event is handled. Gets the new tab.
* @return {Promise} resolved when the event is handled.
*/
function promiseNewTabLoadEvent(aUrl, aEventType="load")
function promiseTabLoadEvent(aTab, aURL, aEventType="load")
{
let deferred = Promise.defer();
let tab = gBrowser.selectedTab = gBrowser.addTab(aUrl);
info("Wait tab event: " + aEventType);
tab.linkedBrowser.addEventListener(aEventType, function load(event) {
if (event.originalTarget != tab.linkedBrowser.contentDocument ||
aTab.linkedBrowser.addEventListener(aEventType, function load(event) {
if (event.originalTarget != aTab.linkedBrowser.contentDocument ||
event.target.location.href == "about:blank") {
info("skipping spurious load event");
return;
}
tab.linkedBrowser.removeEventListener(aEventType, load, true);
aTab.linkedBrowser.removeEventListener(aEventType, load, true);
info("Tab event received: " + aEventType);
deferred.resolve(tab);
}, true);
deferred.resolve();
}, true, true);
aTab.linkedBrowser.loadURI(aURL);
return deferred.promise;
}
@ -403,28 +411,32 @@ function promiseNewTabLoadEvent(aUrl, aEventType="load")
function promiseSetupSnippetsMap(aTab, aSetupFn)
{
let deferred = Promise.defer();
let cw = aTab.linkedBrowser.contentWindow.wrappedJSObject;
info("Waiting for snippets map");
cw.ensureSnippetsMapThen(function (aSnippetsMap) {
info("Got snippets map: " +
"{ last-update: " + aSnippetsMap.get("snippets-last-update") +
", cached-version: " + aSnippetsMap.get("snippets-cached-version") +
" }");
// Don't try to update.
aSnippetsMap.set("snippets-last-update", Date.now());
aSnippetsMap.set("snippets-cached-version", AboutHomeUtils.snippetsVersion);
// Clear snippets.
aSnippetsMap.delete("snippets");
aSetupFn(aSnippetsMap);
// Must be sure to continue after the page snippets map setup.
executeSoon(function() deferred.resolve(aSnippetsMap));
});
aTab.linkedBrowser.addEventListener("AboutHomeLoadSnippets", function load(event) {
aTab.linkedBrowser.removeEventListener("AboutHomeLoadSnippets", load, true);
let cw = aTab.linkedBrowser.contentWindow.wrappedJSObject;
// The snippets should already be ready by this point. Here we're
// just obtaining a reference to the snippets map.
cw.ensureSnippetsMapThen(function (aSnippetsMap) {
info("Got snippets map: " +
"{ last-update: " + aSnippetsMap.get("snippets-last-update") +
", cached-version: " + aSnippetsMap.get("snippets-cached-version") +
" }");
// Don't try to update.
aSnippetsMap.set("snippets-last-update", Date.now());
aSnippetsMap.set("snippets-cached-version", AboutHomeUtils.snippetsVersion);
// Clear snippets.
aSnippetsMap.delete("snippets");
aSetupFn(aSnippetsMap);
deferred.resolve(aSnippetsMap);
});
}, true, true);
return deferred.promise;
}
/**
* Waits for the attributes being set by browser.js and overwrites snippetsURL
* to ensure we won't try to hit the network and we can force xhr to throw.
* Waits for the attributes being set by browser.js.
*
* @param aTab
* The tab containing about:home.
@ -435,16 +447,10 @@ function promiseBrowserAttributes(aTab)
let deferred = Promise.defer();
let docElt = aTab.linkedBrowser.contentDocument.documentElement;
//docElt.setAttribute("snippetsURL", "nonexistent://test");
let observer = new MutationObserver(function (mutations) {
for (let mutation of mutations) {
info("Got attribute mutation: " + mutation.attributeName +
" from " + mutation.oldValue);
if (mutation.attributeName == "snippetsURL" &&
docElt.getAttribute("snippetsURL") != "nonexistent://test") {
docElt.setAttribute("snippetsURL", "nonexistent://test");
}
// Now we just have to wait for the last attribute.
if (mutation.attributeName == "searchEngineName") {
info("Remove attributes observer");

View File

@ -41,7 +41,11 @@ function nextTest() {
testFindDisabled(url, nextTest);
} else {
// Make sure the find bar is re-enabled after disabled page is closed.
testFindEnabled("about:blank", finish);
testFindEnabled("about:blank", function () {
EventUtils.synthesizeKey("VK_ESCAPE", { });
ok(gFindBar.hidden, "Find bar should now be hidden");
finish();
});
}
}

View File

@ -178,26 +178,32 @@ let AboutHome = {
// Send all the chrome-privileged data needed by about:home. This
// gets re-sent when the search engine changes.
sendAboutHomeData: function(target) {
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
let data = {
showRestoreLastSession: ss.canRestoreLastSession,
snippetsURL: AboutHomeUtils.snippetsURL,
showKnowYourRights: AboutHomeUtils.showKnowYourRights,
snippetsVersion: AboutHomeUtils.snippetsVersion
};
let wrapper = {};
Components.utils.import("resource:///modules/sessionstore/SessionStore.jsm",
wrapper);
let ss = wrapper.SessionStore;
ss.promiseInitialized.then(function() {
let data = {
showRestoreLastSession: ss.canRestoreLastSession,
snippetsURL: AboutHomeUtils.snippetsURL,
showKnowYourRights: AboutHomeUtils.showKnowYourRights,
snippetsVersion: AboutHomeUtils.snippetsVersion
};
if (AboutHomeUtils.showKnowYourRights) {
// Set pref to indicate we've shown the notification.
let currentVersion = Services.prefs.getIntPref("browser.rights.version");
Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
}
if (AboutHomeUtils.showKnowYourRights) {
// Set pref to indicate we've shown the notification.
let currentVersion = Services.prefs.getIntPref("browser.rights.version");
Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
}
if (target) {
target.messageManager.sendAsyncMessage("AboutHome:Update", data);
} else {
let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
mm.broadcastAsyncMessage("AboutHome:Update", data);
}
if (target) {
target.messageManager.sendAsyncMessage("AboutHome:Update", data);
} else {
let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
mm.broadcastAsyncMessage("AboutHome:Update", data);
}
}).then(null, function onError(x) {
Cu.reportError("Error in AboutHome.sendAboutHomeData " + x);
});
},
};

View File

@ -48,6 +48,29 @@ public interface Actions {
*/
void sendGeckoEvent(String geckoEvent, String data);
/**
* Sends a preferences get event to Gecko.
*
* @param requestId The id of this request.
* @param prefNames The preferences being requested.
*/
void sendPreferencesGetEvent(int requestId, String[] prefNames);
/**
* Sends a preferences observe event to Gecko.
*
* @param requestId The id of this request.
* @param prefNames The preferences being requested.
*/
void sendPreferencesObserveEvent(int requestId, String[] prefNames);
/**
* Sends a preferences remove observers event to Gecko.
*
* @param requestId The id of this request.
*/
void sendPreferencesRemoveObserversEvent(int requestid);
/**
* Listens for a gecko event to be sent from the Gecko instance.
* The returned object can be used to test if the event has been

View File

@ -44,6 +44,9 @@ public class FennecNativeActions implements Actions {
private Method mRegisterEventListener;
private Method mUnregisterEventListener;
private Method mBroadcastEvent;
private Method mPreferencesGetEvent;
private Method mPreferencesObserveEvent;
private Method mPreferencesRemoveObserversEvent;
private Method mSetDrawListener;
private Method mQuerySql;
private Object mRobocopApi;
@ -66,6 +69,9 @@ public class FennecNativeActions implements Actions {
mRegisterEventListener = mApiClass.getMethod("registerEventListener", String.class, mEventListenerClass);
mUnregisterEventListener = mApiClass.getMethod("unregisterEventListener", String.class, mEventListenerClass);
mBroadcastEvent = mApiClass.getMethod("broadcastEvent", String.class, String.class);
mPreferencesGetEvent = mApiClass.getMethod("preferencesGetEvent", Integer.TYPE, String[].class);
mPreferencesObserveEvent = mApiClass.getMethod("preferencesObserveEvent", Integer.TYPE, String[].class);
mPreferencesRemoveObserversEvent = mApiClass.getMethod("preferencesRemoveObserversEvent", Integer.TYPE);
mSetDrawListener = mApiClass.getMethod("setDrawListener", mDrawListenerClass);
mQuerySql = mApiClass.getMethod("querySql", String.class, String.class);
@ -265,6 +271,34 @@ public class FennecNativeActions implements Actions {
}
}
private void sendPreferencesEvent(Method method, int requestId, String[] prefNames) {
try {
method.invoke(mRobocopApi, requestId, prefNames);
} catch (IllegalAccessException e) {
FennecNativeDriver.log(LogLevel.ERROR, e);
} catch (InvocationTargetException e) {
FennecNativeDriver.log(LogLevel.ERROR, e);
}
}
public void sendPreferencesGetEvent(int requestId, String[] prefNames) {
sendPreferencesEvent(mPreferencesGetEvent, requestId, prefNames);
}
public void sendPreferencesObserveEvent(int requestId, String[] prefNames) {
sendPreferencesEvent(mPreferencesObserveEvent, requestId, prefNames);
}
public void sendPreferencesRemoveObserversEvent(int requestId) {
try {
mPreferencesRemoveObserversEvent.invoke(mRobocopApi, requestId);
} catch (IllegalAccessException e) {
FennecNativeDriver.log(LogLevel.ERROR, e);
} catch (InvocationTargetException e) {
FennecNativeDriver.log(LogLevel.ERROR, e);
}
}
class DrawListenerProxy implements InvocationHandler {
private final PaintExpecter mPaintExpecter;

View File

@ -217,7 +217,7 @@ class FileKind(object):
def get_all_filenames():
'''Get a list of all the files in the (Mercurial or Git) repository.'''
cmds = [['hg', 'manifest', '-q'], ['git', 'ls-files']]
cmds = [['hg', 'manifest', '-q'], ['git', 'ls-files', '--full-name', '../..']]
for cmd in cmds:
try:
all_filenames = subprocess.check_output(cmd, universal_newlines=True,

View File

@ -14,8 +14,8 @@
#include "mozilla/RefPtr.h"
#define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
{ 0x9a6a5bdf, 0x1261, 0x4057, \
{ 0x85, 0xcc, 0xaf, 0x97, 0x6c, 0x36, 0x99, 0xa9 } }
{ 0x8b8da863, 0xd151, 0x4014, \
{ 0x8b, 0xdc, 0x62, 0xb5, 0x0d, 0xc0, 0x2b, 0x62 } }
class gfxContext;
class gfxASurface;
@ -65,9 +65,6 @@ public:
gfxPattern::GraphicsFilter aFilter,
uint32_t aFlags = RenderFlagPremultAlpha) = 0;
// Creates an image buffer. Returns null on failure.
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0;
// Gives you a stream containing the image represented by this context.
// The format is given in aMimeTime, for example "image/png".
//

View File

@ -41,7 +41,7 @@
#include "nsTArray.h"
#include "ImageEncoder.h"
#include "imgIEncoder.h"
#include "gfxContext.h"
#include "gfxASurface.h"
@ -1036,71 +1036,71 @@ CanvasRenderingContext2D::Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFi
return rv;
}
void
CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
int32_t* aFormat)
NS_IMETHODIMP
CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
const PRUnichar *aEncoderOptions,
nsIInputStream **aStream)
{
*aImageBuffer = nullptr;
*aFormat = 0;
nsRefPtr<gfxASurface> surface;
nsresult rv = GetThebesSurface(getter_AddRefs(surface));
if (NS_FAILED(rv)) {
return;
EnsureTarget();
if (!IsTargetValid()) {
return NS_ERROR_FAILURE;
}
nsRefPtr<gfxASurface> surface;
if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
return NS_ERROR_FAILURE;
}
nsresult rv;
const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
static const fallible_t fallible = fallible_t();
uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
nsAutoArrayPtr<char> conid(new (fallible) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
if (!conid) {
return NS_ERROR_OUT_OF_MEMORY;
}
strcpy(conid, encoderPrefix);
strcat(conid, aMimeType);
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
if (!encoder) {
return NS_ERROR_FAILURE;
}
nsAutoArrayPtr<uint8_t> imageBuffer(new (fallible) uint8_t[mWidth * mHeight * 4]);
if (!imageBuffer) {
return;
return NS_ERROR_OUT_OF_MEMORY;
}
nsRefPtr<gfxImageSurface> imgsurf =
new gfxImageSurface(imageBuffer,
new gfxImageSurface(imageBuffer.get(),
gfxIntSize(mWidth, mHeight),
mWidth * 4,
gfxASurface::ImageFormatARGB32);
if (!imgsurf || imgsurf->CairoStatus()) {
delete[] imageBuffer;
return;
return NS_ERROR_FAILURE;
}
nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
if (!ctx || ctx->HasError()) {
delete[] imageBuffer;
return;
return NS_ERROR_FAILURE;
}
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
ctx->SetSource(surface, gfxPoint(0, 0));
ctx->Paint();
*aImageBuffer = imageBuffer;
*aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
}
rv = encoder->InitFromData(imageBuffer.get(),
mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
imgIEncoder::INPUT_FORMAT_HOSTARGB,
nsDependentString(aEncoderOptions));
NS_ENSURE_SUCCESS(rv, rv);
NS_IMETHODIMP
CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
const PRUnichar *aEncoderOptions,
nsIInputStream **aStream)
{
uint8_t* imageBuffer = nullptr;
int32_t format = 0;
GetImageBuffer(&imageBuffer, &format);
if (!imageBuffer) {
return NS_ERROR_FAILURE;
}
nsCString enccid("@mozilla.org/image/encoder;2?type=");
enccid += aMimeType;
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
if (!encoder) {
return NS_ERROR_FAILURE;
}
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
encoder, aEncoderOptions, aStream);
return CallQueryInterface(encoder, aStream);
}
SurfaceFormat
@ -3739,9 +3739,6 @@ NS_IMETHODIMP
CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
{
EnsureTarget();
if (!IsTargetValid()) {
return NS_ERROR_FAILURE;
}
nsRefPtr<gfxASurface> thebesSurface =
gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);

View File

@ -20,7 +20,6 @@
#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
#include "mozilla/dom/CanvasPattern.h"
#include "mozilla/gfx/Rect.h"
#include "imgIEncoder.h"
class nsXULElement;
@ -449,8 +448,6 @@ public:
friend class CanvasRenderingContext2DUserData;
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
protected:
nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
uint32_t aWidth, uint32_t aHeight,

View File

@ -1,299 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ImageEncoder.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
namespace mozilla {
namespace dom {
class EncodingCompleteEvent : public nsRunnable
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
EncodingCompleteEvent(nsIScriptContext* aScriptContext,
nsIThread* aEncoderThread,
FileCallback& aCallback)
: mImgSize(0)
, mType()
, mImgData(nullptr)
, mScriptContext(aScriptContext)
, mEncoderThread(aEncoderThread)
, mCallback(&aCallback)
{}
virtual ~EncodingCompleteEvent() {}
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<nsDOMMemoryFile> blob =
new nsDOMMemoryFile(mImgData, mImgSize, mType);
if (mScriptContext) {
JSContext* jsContext = mScriptContext->GetNativeContext();
if (jsContext) {
JS_updateMallocCounter(jsContext, mImgSize);
}
}
mozilla::ErrorResult rv;
mCallback->Call(blob, rv);
NS_ENSURE_SUCCESS(rv.ErrorCode(), rv.ErrorCode());
mEncoderThread->Shutdown();
return rv.ErrorCode();
}
void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType)
{
mImgData = aImgData;
mImgSize = aImgSize;
mType = aType;
}
private:
uint64_t mImgSize;
nsAutoString mType;
void* mImgData;
nsCOMPtr<nsIScriptContext> mScriptContext;
nsCOMPtr<nsIThread> mEncoderThread;
nsRefPtr<FileCallback> mCallback;
};
NS_IMPL_ISUPPORTS1(EncodingCompleteEvent, nsIRunnable);
class EncodingRunnable : public nsRunnable
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
EncodingRunnable(const nsAString& aType,
const nsAString& aOptions,
uint8_t* aImageBuffer,
imgIEncoder* aEncoder,
EncodingCompleteEvent* aEncodingCompleteEvent,
int32_t aFormat,
const nsIntSize aSize,
bool aUsingCustomOptions)
: mType(aType)
, mOptions(aOptions)
, mImageBuffer(aImageBuffer)
, mEncoder(aEncoder)
, mEncodingCompleteEvent(aEncodingCompleteEvent)
, mFormat(aFormat)
, mSize(aSize)
, mUsingCustomOptions(aUsingCustomOptions)
{}
virtual ~EncodingRunnable() {}
NS_IMETHOD Run()
{
nsCOMPtr<nsIInputStream> stream;
nsresult rv = ImageEncoder::ExtractDataInternal(mType,
mOptions,
mImageBuffer,
mFormat,
mSize,
nullptr,
getter_AddRefs(stream),
mEncoder);
// If there are unrecognized custom parse options, we should fall back to
// the default values for the encoder without any options at all.
if (rv == NS_ERROR_INVALID_ARG && mUsingCustomOptions) {
rv = ImageEncoder::ExtractDataInternal(mType,
EmptyString(),
mImageBuffer,
mFormat,
mSize,
nullptr,
getter_AddRefs(stream),
mEncoder);
}
NS_ENSURE_SUCCESS(rv, rv);
uint64_t imgSize;
rv = stream->Available(&imgSize);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
void* imgData = nullptr;
rv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
NS_ENSURE_SUCCESS(rv, rv);
mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
rv = NS_DispatchToMainThread(mEncodingCompleteEvent, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return rv;
}
private:
nsAutoString mType;
nsAutoString mOptions;
nsAutoArrayPtr<uint8_t> mImageBuffer;
nsCOMPtr<imgIEncoder> mEncoder;
nsRefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
int32_t mFormat;
const nsIntSize mSize;
bool mUsingCustomOptions;
};
NS_IMPL_ISUPPORTS1(EncodingRunnable, nsIRunnable)
/* static */
nsresult
ImageEncoder::ExtractData(nsAString& aType,
const nsAString& aOptions,
const nsIntSize aSize,
nsICanvasRenderingContextInternal* aContext,
nsIInputStream** aStream)
{
nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
if (!encoder) {
return NS_IMAGELIB_ERROR_NO_ENCODER;
}
return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize, aContext,
aStream, encoder);
}
/* static */
nsresult
ImageEncoder::ExtractDataAsync(nsAString& aType,
const nsAString& aOptions,
bool aUsingCustomOptions,
uint8_t* aImageBuffer,
int32_t aFormat,
const nsIntSize aSize,
nsICanvasRenderingContextInternal* aContext,
nsIScriptContext* aScriptContext,
FileCallback& aCallback)
{
nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
if (!encoder) {
return NS_IMAGELIB_ERROR_NO_ENCODER;
}
nsCOMPtr<nsIThread> encoderThread;
nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<EncodingCompleteEvent> completeEvent =
new EncodingCompleteEvent(aScriptContext, encoderThread, aCallback);
nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
aOptions,
aImageBuffer,
encoder,
completeEvent,
aFormat,
aSize,
aUsingCustomOptions);
return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
}
/*static*/ nsresult
ImageEncoder::GetInputStream(int32_t aWidth,
int32_t aHeight,
uint8_t* aImageBuffer,
int32_t aFormat,
imgIEncoder* aEncoder,
const PRUnichar* aEncoderOptions,
nsIInputStream** aStream)
{
nsresult rv =
aEncoder->InitFromData(aImageBuffer,
aWidth * aHeight * 4, aWidth, aHeight, aWidth * 4,
aFormat,
nsDependentString(aEncoderOptions));
NS_ENSURE_SUCCESS(rv, rv);
return CallQueryInterface(aEncoder, aStream);
}
/* static */
nsresult
ImageEncoder::ExtractDataInternal(const nsAString& aType,
const nsAString& aOptions,
uint8_t* aImageBuffer,
int32_t aFormat,
const nsIntSize aSize,
nsICanvasRenderingContextInternal* aContext,
nsIInputStream** aStream,
imgIEncoder* aEncoder)
{
nsCOMPtr<nsIInputStream> imgStream;
// get image bytes
nsresult rv;
if (aImageBuffer) {
rv = ImageEncoder::GetInputStream(
aSize.width,
aSize.height,
aImageBuffer,
aFormat,
aEncoder,
nsPromiseFlatString(aOptions).get(),
getter_AddRefs(imgStream));
} else if (aContext) {
NS_ConvertUTF16toUTF8 encoderType(aType);
rv = aContext->GetInputStream(encoderType.get(),
nsPromiseFlatString(aOptions).get(),
getter_AddRefs(imgStream));
} else {
// no context, so we have to encode an empty image
// note that if we didn't have a current context, the spec says we're
// supposed to just return transparent black pixels of the canvas
// dimensions.
nsRefPtr<gfxImageSurface> emptyCanvas =
new gfxImageSurface(gfxIntSize(aSize.width, aSize.height),
gfxASurface::ImageFormatARGB32);
if (emptyCanvas->CairoStatus()) {
return NS_ERROR_INVALID_ARG;
}
rv = aEncoder->InitFromData(emptyCanvas->Data(),
aSize.width * aSize.height * 4,
aSize.width,
aSize.height,
aSize.width * 4,
imgIEncoder::INPUT_FORMAT_HOSTARGB,
aOptions);
if (NS_SUCCEEDED(rv)) {
imgStream = do_QueryInterface(aEncoder);
}
}
NS_ENSURE_SUCCESS(rv, rv);
imgStream.forget(aStream);
return rv;
}
/* static */
already_AddRefed<imgIEncoder>
ImageEncoder::GetImageEncoder(nsAString& aType)
{
// Get an image encoder for the media type.
nsCString encoderCID("@mozilla.org/image/encoder;2?type=");
NS_ConvertUTF16toUTF8 encoderType(aType);
encoderCID += encoderType;
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
if (!encoder && aType != NS_LITERAL_STRING("image/png")) {
// Unable to create an encoder instance of the specified type. Falling back
// to PNG.
aType.AssignLiteral("image/png");
nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
encoder = do_CreateInstance(PNGEncoderCID.get());
}
return encoder.forget();
}
} // namespace dom
} // namespace mozilla

View File

@ -1,92 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ImageEncoder_h
#define ImageEncoder_h
#include "imgIEncoder.h"
#include "nsDOMFile.h"
#include "nsError.h"
#include "mozilla/dom/HTMLCanvasElementBinding.h"
#include "nsLayoutUtils.h"
#include "nsNetUtil.h"
#include "nsSize.h"
class nsICanvasRenderingContextInternal;
namespace mozilla {
namespace dom {
class EncodingRunnable;
class ImageEncoder
{
public:
// Extracts data synchronously and gives you a stream containing the image
// represented by aContext. aType may change to "image/png" if we had to fall
// back to a PNG encoder. A return value of NS_OK implies successful data
// extraction. If there are any unrecognized custom parse options in
// aOptions, NS_ERROR_INVALID_ARG will be returned. When encountering this
// error it is usual to call this function again without any options at all.
static nsresult ExtractData(nsAString& aType,
const nsAString& aOptions,
const nsIntSize aSize,
nsICanvasRenderingContextInternal* aContext,
nsIInputStream** aStream);
// Extracts data asynchronously. aType may change to "image/png" if we had to
// fall back to a PNG encoder. aOptions are the options to be passed to the
// encoder and aUsingCustomOptions specifies whether custom parse options were
// used (i.e. by using -moz-parse-options). If there are any unrecognized
// custom parse options, we fall back to the default values for the encoder
// without any options at all. A return value of NS_OK only implies
// successful dispatching of the extraction step to the encoding thread.
static nsresult ExtractDataAsync(nsAString& aType,
const nsAString& aOptions,
bool aUsingCustomOptions,
uint8_t* aImageBuffer,
int32_t aFormat,
const nsIntSize aSize,
nsICanvasRenderingContextInternal* aContext,
nsIScriptContext* aScriptContext,
FileCallback& aCallback);
// Gives you a stream containing the image represented by aImageBuffer.
// The format is given in aFormat, for example
// imgIEncoder::INPUT_FORMAT_HOSTARGB.
static nsresult GetInputStream(int32_t aWidth,
int32_t aHeight,
uint8_t* aImageBuffer,
int32_t aFormat,
imgIEncoder* aEncoder,
const PRUnichar* aEncoderOptions,
nsIInputStream** aStream);
private:
// When called asynchronously, aContext is null.
static nsresult
ExtractDataInternal(const nsAString& aType,
const nsAString& aOptions,
uint8_t* aImageBuffer,
int32_t aFormat,
const nsIntSize aSize,
nsICanvasRenderingContextInternal* aContext,
nsIInputStream** aStream,
imgIEncoder* aEncoder);
// Creates and returns an encoder instance of the type specified in aType.
// aType may change to "image/png" if no instance of the original type could
// be created and we had to fall back to a PNG encoder. A return value of
// NULL should be interpreted as NS_IMAGELIB_ERROR_NO_ENCODER and aType is
// undefined in this case.
static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
friend class EncodingRunnable;
};
} // namespace dom
} // namespace mozilla
#endif // ImageEncoder_h

View File

@ -22,6 +22,5 @@ INCLUDES += \
-I$(srcdir)/../../html/content/src \
-I$(srcdir)/../../../js/xpconnect/src \
-I$(srcdir)/../../../dom/base \
-I$(srcdir)/../../../image/src \
-I$(topsrcdir)/content/xul/content/src \
$(NULL)

View File

@ -27,7 +27,7 @@
#include "nsIVariant.h"
#include "ImageEncoder.h"
#include "imgIEncoder.h"
#include "gfxContext.h"
#include "gfxPattern.h"
@ -721,54 +721,6 @@ void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
}
}
void
WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
{
*aImageBuffer = nullptr;
*aFormat = 0;
nsRefPtr<gfxImageSurface> imgsurf =
new gfxImageSurface(gfxIntSize(mWidth, mHeight),
gfxASurface::ImageFormatARGB32);
if (!imgsurf || imgsurf->CairoStatus()) {
return;
}
nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
if (!ctx || ctx->HasError()) {
return;
}
// Use Render() to make sure that appropriate y-flip gets applied
uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
nsresult rv = Render(ctx, gfxPattern::FILTER_NEAREST, flags);
if (NS_FAILED(rv)) {
return;
}
int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
if (!mOptions.premultipliedAlpha) {
// We need to convert to INPUT_FORMAT_RGBA, otherwise
// we are automatically considered premult, and unpremult'd.
// Yes, it is THAT silly.
// Except for different lossy conversions by color,
// we could probably just change the label, and not change the data.
gfxUtils::ConvertBGRAtoRGBA(imgsurf);
format = imgIEncoder::INPUT_FORMAT_RGBA;
}
static const fallible_t fallible = fallible_t();
uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
if (!imageBuffer) {
return;
}
memcpy(imageBuffer, imgsurf->Data(), mWidth * mHeight * 4);
*aImageBuffer = imageBuffer;
*aFormat = format;
}
NS_IMETHODIMP
WebGLContext::GetInputStream(const char* aMimeType,
const PRUnichar* aEncoderOptions,
@ -778,22 +730,48 @@ WebGLContext::GetInputStream(const char* aMimeType,
if (!gl)
return NS_ERROR_FAILURE;
uint8_t* imageBuffer = nullptr;
int32_t format = 0;
GetImageBuffer(&imageBuffer, &format);
if (!imageBuffer) {
nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
gfxASurface::ImageFormatARGB32);
if (surf->CairoStatus() != 0)
return NS_ERROR_FAILURE;
nsRefPtr<gfxContext> tmpcx = new gfxContext(surf);
// Use Render() to make sure that appropriate y-flip gets applied
uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
nsresult rv = Render(tmpcx, gfxPattern::FILTER_NEAREST, flags);
if (NS_FAILED(rv))
return rv;
const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
nsAutoArrayPtr<char> conid(new char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
strcpy(conid, encoderPrefix);
strcat(conid, aMimeType);
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
if (!encoder)
return NS_ERROR_FAILURE;
int format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
if (!mOptions.premultipliedAlpha) {
// We need to convert to INPUT_FORMAT_RGBA, otherwise
// we are automatically considered premult, and unpremult'd.
// Yes, it is THAT silly.
// Except for different lossy conversions by color,
// we could probably just change the label, and not change the data.
gfxUtils::ConvertBGRAtoRGBA(surf);
format = imgIEncoder::INPUT_FORMAT_RGBA;
}
nsCString enccid("@mozilla.org/image/encoder;2?type=");
enccid += aMimeType;
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
if (!encoder) {
return NS_ERROR_FAILURE;
}
rv = encoder->InitFromData(surf->Data(),
mWidth * mHeight * 4,
mWidth, mHeight,
surf->Stride(),
format,
nsDependentString(aEncoderOptions));
NS_ENSURE_SUCCESS(rv, rv);
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
encoder, aEncoderOptions, aStream);
return CallQueryInterface(encoder, aStream);
}
NS_IMETHODIMP

View File

@ -160,7 +160,6 @@ public:
NS_IMETHOD Render(gfxContext *ctx,
gfxPattern::GraphicsFilter f,
uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
NS_IMETHOD GetInputStream(const char* aMimeType,
const PRUnichar* aEncoderOptions,
nsIInputStream **aStream) MOZ_OVERRIDE;

View File

@ -22,7 +22,6 @@ CPP_SOURCES += [
'DocumentRendererChild.cpp',
'DocumentRendererParent.cpp',
'ImageData.cpp',
'ImageEncoder.cpp',
]
if CONFIG['MOZ_WEBGL']:

View File

@ -7,43 +7,42 @@
<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<script>
function compareAsync(file, canvas, type, callback)
var gCompares = 0;
function compareAsync(file, canvas, type)
{
++gCompares;
var reader = new FileReader();
reader.onload =
function(e) {
is(e.target.result, canvas.toDataURL(type),
"<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
callback(canvas);
if (--gCompares == 0) {
SimpleTest.finish();
}
};
reader.readAsDataURL(file);
}
function test1(canvas)
{
var pngfile = canvas.mozGetAsFile("foo.png");
is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
compareAsync(pngfile, canvas, "image/png", test2);
is(pngfile.name, "foo.png", "File name should be what we passed in");
}
function test2(canvas)
{
var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
is(jpegfile.type, "image/jpeg",
"When a valid type is specified that should be returned");
compareAsync(jpegfile, canvas, "image/jpeg", SimpleTest.finish);
is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(function () {
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
test1(canvas);
var pngfile = canvas.mozGetAsFile("foo.png");
is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
compareAsync(pngfile, canvas, "image/png");
is(pngfile.name, "foo.png", "File name should be what we passed in");
var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
is(jpegfile.type, "image/jpeg",
"When a valid type is specified that should be returned");
compareAsync(jpegfile, canvas, "image/jpeg");
is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
});
</script>

View File

@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<title>Canvas test: toBlob</title>
<title>Canvas test: mozGetAsFile</title>
<script src="/MochiKit/MochiKit.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
@ -7,40 +7,34 @@
<canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<script>
function BlobListener(type, canvas, callback, file)
var gCompares = 2;
function BlobListener(type, canvas, file)
{
is(file.type, type,
"When a valid type is specified that should be returned");
var reader = new FileReader();
reader.onload =
reader.onload =
function(e) {
is(e.target.result, canvas.toDataURL(type),
"<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
callback(canvas);
"<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
if (--gCompares == 0) {
SimpleTest.finish();
}
};
reader.readAsDataURL(file);
}
function test1(canvas)
{
canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas, test2));
}
function test2(canvas)
{
canvas.toBlob(
BlobListener.bind(undefined, "image/jpeg", canvas, SimpleTest.finish),
"image/jpeg");
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(function () {
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
test1(canvas);
canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas));
canvas.toBlob(BlobListener.bind(undefined, "image/jpeg", canvas), "image/jpeg");
});
</script>

View File

@ -223,9 +223,10 @@ protected:
const JS::Value& aEncoderOptions,
nsAString& aParams,
bool* usingCustomParseOptions);
nsresult ExtractData(nsAString& aType,
nsresult ExtractData(const nsAString& aType,
const nsAString& aOptions,
nsIInputStream** aStream);
nsIInputStream** aStream,
bool& aFellBackToPNG);
nsresult ToDataURLImpl(JSContext* aCx,
const nsAString& aMimeType,
const JS::Value& aEncoderOptions,

View File

@ -5,10 +5,10 @@
#include "mozilla/dom/HTMLCanvasElement.h"
#include "ImageEncoder.h"
#include "Layers.h"
#include "imgIEncoder.h"
#include "jsapi.h"
#include "jsfriendapi.h"
#include "Layers.h"
#include "mozilla/Base64.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
@ -22,7 +22,6 @@
#include "nsContentUtils.h"
#include "nsDisplayList.h"
#include "nsDOMFile.h"
#include "nsDOMJSUtils.h"
#include "nsFrameManager.h"
#include "nsIScriptSecurityManager.h"
#include "nsITimer.h"
@ -46,6 +45,29 @@ namespace {
typedef mozilla::dom::HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement
HTMLImageOrCanvasOrVideoElement;
class ToBlobRunnable : public nsRunnable
{
public:
ToBlobRunnable(mozilla::dom::FileCallback& aCallback,
nsIDOMBlob* aBlob)
: mCallback(&aCallback),
mBlob(aBlob)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
NS_IMETHOD Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mozilla::ErrorResult rv;
mCallback->Call(mBlob, rv);
return rv.ErrorCode();
}
private:
nsRefPtr<mozilla::dom::FileCallback> mCallback;
nsCOMPtr<nsIDOMBlob> mBlob;
};
} // anonymous namespace
namespace mozilla {
@ -330,10 +352,10 @@ HTMLCanvasElement::MozFetchAsStream(nsIInputStreamCallback *aCallback,
return NS_ERROR_FAILURE;
nsresult rv;
bool fellBackToPNG = false;
nsCOMPtr<nsIInputStream> inputData;
nsAutoString type(aType);
rv = ExtractData(type, EmptyString(), getter_AddRefs(inputData));
rv = ExtractData(aType, EmptyString(), getter_AddRefs(inputData), fellBackToPNG);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAsyncInputStream> asyncData = do_QueryInterface(inputData, &rv);
@ -365,15 +387,68 @@ HTMLCanvasElement::GetMozPrintCallback() const
}
nsresult
HTMLCanvasElement::ExtractData(nsAString& aType,
HTMLCanvasElement::ExtractData(const nsAString& aType,
const nsAString& aOptions,
nsIInputStream** aStream)
nsIInputStream** aStream,
bool& aFellBackToPNG)
{
return ImageEncoder::ExtractData(aType,
aOptions,
GetSize(),
mCurrentContext,
aStream);
// note that if we don't have a current context, the spec says we're
// supposed to just return transparent black pixels of the canvas
// dimensions.
nsRefPtr<gfxImageSurface> emptyCanvas;
nsIntSize size = GetWidthHeight();
if (!mCurrentContext) {
emptyCanvas = new gfxImageSurface(gfxIntSize(size.width, size.height), gfxASurface::ImageFormatARGB32);
if (emptyCanvas->CairoStatus()) {
return NS_ERROR_INVALID_ARG;
}
}
nsresult rv;
// get image bytes
nsCOMPtr<nsIInputStream> imgStream;
NS_ConvertUTF16toUTF8 encoderType(aType);
try_again:
if (mCurrentContext) {
rv = mCurrentContext->GetInputStream(encoderType.get(),
nsPromiseFlatString(aOptions).get(),
getter_AddRefs(imgStream));
} else {
// no context, so we have to encode the empty image we created above
nsCString enccid("@mozilla.org/image/encoder;2?type=");
enccid += encoderType;
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get(), &rv);
if (NS_SUCCEEDED(rv) && encoder) {
rv = encoder->InitFromData(emptyCanvas->Data(),
size.width * size.height * 4,
size.width,
size.height,
size.width * 4,
imgIEncoder::INPUT_FORMAT_HOSTARGB,
aOptions);
if (NS_SUCCEEDED(rv)) {
imgStream = do_QueryInterface(encoder);
}
} else {
rv = NS_ERROR_FAILURE;
}
}
if (NS_FAILED(rv) && !aFellBackToPNG) {
// Try image/png instead.
// XXX ERRMSG we need to report an error to developers here! (bug 329026)
aFellBackToPNG = true;
encoderType.AssignLiteral("image/png");
goto try_again;
}
NS_ENSURE_SUCCESS(rv, rv);
imgStream.forget(aStream);
return NS_OK;
}
nsresult
@ -424,6 +499,8 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
const JS::Value& aEncoderOptions,
nsAString& aDataURL)
{
bool fallbackToPNG = false;
nsIntSize size = GetWidthHeight();
if (size.height == 0 || size.width == 0) {
aDataURL = NS_LITERAL_STRING("data:,");
@ -444,18 +521,23 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
}
nsCOMPtr<nsIInputStream> stream;
rv = ExtractData(type, params, getter_AddRefs(stream));
rv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
// If there are unrecognized custom parse options, we should fall back to
// the default values for the encoder without any options at all.
if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
fallbackToPNG = false;
rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
}
NS_ENSURE_SUCCESS(rv, rv);
// build data URL string
aDataURL = NS_LITERAL_STRING("data:") + type + NS_LITERAL_STRING(";base64,");
if (fallbackToPNG)
aDataURL = NS_LITERAL_STRING("data:image/png;base64,");
else
aDataURL = NS_LITERAL_STRING("data:") + type +
NS_LITERAL_STRING(";base64,");
uint64_t count;
rv = stream->Available(&count);
@ -465,6 +547,7 @@ HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count, aDataURL.Length());
}
// XXXkhuey the encoding should be off the main thread, but we're lazy.
void
HTMLCanvasElement::ToBlob(JSContext* aCx,
FileCallback& aCallback,
@ -495,24 +578,52 @@ HTMLCanvasElement::ToBlob(JSContext* aCx,
return;
}
nsCOMPtr<nsIScriptContext> scriptContext =
GetScriptContextFromJSContext(nsContentUtils::GetCurrentJSContext());
bool fallbackToPNG = false;
uint8_t* imageBuffer = nullptr;
int32_t format = 0;
if (mCurrentContext) {
mCurrentContext->GetImageBuffer(&imageBuffer, &format);
nsCOMPtr<nsIInputStream> stream;
aRv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
// If there are unrecognized custom parse options, we should fall back to
// the default values for the encoder without any options at all.
if (aRv.ErrorCode() == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
fallbackToPNG = false;
aRv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
}
aRv = ImageEncoder::ExtractDataAsync(type,
params,
usingCustomParseOptions,
imageBuffer,
format,
GetSize(),
mCurrentContext,
scriptContext,
aCallback);
if (aRv.Failed()) {
return;
}
if (fallbackToPNG) {
type.AssignLiteral("image/png");
}
uint64_t imgSize;
aRv = stream->Available(&imgSize);
if (aRv.Failed()) {
return;
}
if (imgSize > UINT32_MAX) {
aRv.Throw(NS_ERROR_FILE_TOO_BIG);
return;
}
void* imgData = nullptr;
aRv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
if (aRv.Failed()) {
return;
}
// The DOMFile takes ownership of the buffer
nsRefPtr<nsDOMMemoryFile> blob =
new nsDOMMemoryFile(imgData, imgSize, type);
JSContext* cx = nsContentUtils::GetCurrentJSContext();
if (cx) {
JS_updateMallocCounter(cx, imgSize);
}
nsRefPtr<ToBlobRunnable> runnable = new ToBlobRunnable(aCallback, blob);
aRv = NS_DispatchToCurrentThread(runnable);
}
already_AddRefed<nsIDOMFile>
@ -546,11 +657,18 @@ HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
const nsAString& aType,
nsIDOMFile** aResult)
{
bool fallbackToPNG = false;
nsCOMPtr<nsIInputStream> stream;
nsAutoString type(aType);
nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
nsresult rv = ExtractData(aType, EmptyString(), getter_AddRefs(stream),
fallbackToPNG);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString type(aType);
if (fallbackToPNG) {
type.AssignLiteral("image/png");
}
uint64_t imgSize;
rv = stream->Available(&imgSize);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -22,7 +22,6 @@ INCLUDES += \
-I$(srcdir)/../../../../editor/libeditor/text \
-I$(srcdir)/../../../../editor/txmgr/src \
-I$(srcdir)/../../../../netwerk/base/src \
-I$(srcdir)/../../../../content/canvas/src \
-I$(srcdir) \
-I$(topsrcdir)/xpcom/ds \
-I$(topsrcdir)/content/media/ \

View File

@ -302,6 +302,10 @@ private:
NS_IMETHODIMP DispatchAsyncScrollEventRunnable::Run()
{
nsCOMPtr<Element> frameElement = mTabParent->GetOwnerElement();
nsIDocument *doc = frameElement->OwnerDoc();
nsCOMPtr<nsIGlobalObject> globalObject = doc->GetScopeObject();
NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
// Create the event's detail object.
AsyncScrollEventDetailInitializer detail;
detail.mLeft = mContentRect.x;
@ -310,7 +314,12 @@ NS_IMETHODIMP DispatchAsyncScrollEventRunnable::Run()
detail.mHeight = mContentRect.height;
detail.mScrollWidth = mContentRect.width;
detail.mScrollHeight = mContentRect.height;
AutoSafeJSContext cx;
JS::Rooted<JSObject*> globalJSObject(cx, globalObject->GetGlobalJSObject());
NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
JSAutoCompartment ac(cx, globalJSObject);
JS::Rooted<JS::Value> val(cx);
// We can get away with a null global here because

View File

@ -19,13 +19,11 @@
#include "nsContentUtils.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "IndexedDatabaseManager.h"
#define PERMISSION_INDEXEDDB "indexedDB"
#define PREF_INDEXEDDB_ENABLED "dom.indexedDB.enabled"
#define TOPIC_PERMISSIONS_PROMPT "indexedDB-permissions-prompt"
#define TOPIC_PERMISSIONS_RESPONSE "indexedDB-permissions-response"
@ -40,7 +38,6 @@
USING_INDEXEDDB_NAMESPACE
using namespace mozilla::services;
using mozilla::dom::quota::CheckQuotaHelper;
using mozilla::Preferences;
namespace {
@ -50,21 +47,13 @@ GetIndexedDBPermissions(nsIDOMWindow* aWindow)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!Preferences::GetBool(PREF_INDEXEDDB_ENABLED)) {
return PERMISSION_DENIED;
}
// No window here means chrome access.
if (!aWindow) {
return PERMISSION_ALLOWED;
}
NS_ASSERTION(aWindow, "Chrome shouldn't check the permission!");
nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(aWindow));
NS_ENSURE_TRUE(sop, nsIPermissionManager::DENY_ACTION);
if (nsContentUtils::IsSystemPrincipal(sop->GetPrincipal())) {
return PERMISSION_ALLOWED;
}
NS_ASSERTION(!nsContentUtils::IsSystemPrincipal(sop->GetPrincipal()),
"Chrome windows shouldn't check the permission!");
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);

View File

@ -21,6 +21,7 @@
#include "mozilla/dom/quota/OriginOrPatternString.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/Preferences.h"
#include "mozilla/storage.h"
#include "nsComponentManagerUtils.h"
#include "nsCharSeparatedTokenizer.h"
@ -46,6 +47,8 @@
#include "ipc/IndexedDBChild.h"
#define PREF_INDEXEDDB_ENABLED "dom.indexedDB.enabled"
USING_INDEXEDDB_NAMESPACE
USING_QUOTA_NAMESPACE
@ -56,6 +59,7 @@ using mozilla::dom::NonNull;
using mozilla::dom::Optional;
using mozilla::dom::TabChild;
using mozilla::ErrorResult;
using mozilla::Preferences;
namespace {
@ -602,22 +606,33 @@ IDBFactory::OpenInternal(const nsAString& aName,
rv = openHelper->Init();
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
nsRefPtr<CheckPermissionsHelper> permissionHelper =
new CheckPermissionsHelper(openHelper, window);
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "This should never be null!");
rv = quotaManager->
WaitForOpenAllowed(OriginOrPatternString::FromOrigin(aASCIIOrigin),
Nullable<PersistenceType>(aPersistenceType),
openHelper->Id(), permissionHelper);
if (!Preferences::GetBool(PREF_INDEXEDDB_ENABLED)) {
openHelper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
rv = openHelper->WaitForOpenAllowed();
}
else {
NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?");
StoragePrivilege openerPrivilege;
rv = QuotaManager::GetInfoFromWindow(window, nullptr, nullptr,
&openerPrivilege, nullptr);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = openHelper->WaitForOpenAllowed();
if (openerPrivilege != Chrome &&
aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
nsRefPtr<CheckPermissionsHelper> permissionHelper =
new CheckPermissionsHelper(openHelper, window);
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "This should never be null!");
rv = quotaManager->
WaitForOpenAllowed(OriginOrPatternString::FromOrigin(aASCIIOrigin),
Nullable<PersistenceType>(aPersistenceType),
openHelper->Id(), permissionHelper);
}
else {
// Chrome and temporary storage doesn't need to check the permission.
rv = openHelper->WaitForOpenAllowed();
}
}
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}

View File

@ -1737,7 +1737,8 @@ OpenDatabaseHelper::DispatchToIOThread()
nsresult
OpenDatabaseHelper::RunImmediately()
{
NS_ASSERTION(mState == eCreated, "We've already been dispatched?");
NS_ASSERTION(mState == eCreated || mState == eOpenPending,
"We've already been dispatched?");
NS_ASSERTION(NS_FAILED(mResultCode),
"Should only be short-circuiting if we failed!");
NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!");
@ -2168,6 +2169,10 @@ OpenDatabaseHelper::Run()
PROFILER_MAIN_THREAD_LABEL("IndexedDB", "OpenDatabaseHelper::Run");
if (mState == eOpenPending) {
if (NS_FAILED(mResultCode)) {
return RunImmediately();
}
return DispatchToIOThread();
}

View File

@ -124,6 +124,12 @@ struct RectTyped :
RectTyped<UnknownUnits> ToUnknownRect() const {
return RectTyped<UnknownUnits>(this->x, this->y, this->width, this->height);
}
// This is here only to keep IPDL-generated code happy. DO NOT USE.
bool operator==(const RectTyped<units>& aRect) const
{
return RectTyped<units>::IsEqualEdges(aRect);
}
};
typedef RectTyped<UnknownUnits> Rect;

View File

@ -186,6 +186,7 @@ Layer::Layer(LayerManager* aManager, void* aImplData) :
mUseTileSourceRect(false),
mIsFixedPosition(false),
mMargins(0, 0, 0, 0),
mStickyPositionData(nullptr),
mDebugColorIndex(0),
mAnimationGeneration(0)
{}
@ -1280,6 +1281,14 @@ Layer::PrintInfo(nsACString& aTo, const char* aPrefix)
if (GetIsFixedPosition()) {
aTo.AppendPrintf(" [isFixedPosition anchor=%f,%f]", mAnchor.x, mAnchor.y);
}
if (GetIsStickyPosition()) {
aTo.AppendPrintf(" [isStickyPosition scrollId=%d outer=%f,%f %fx%f "
"inner=%f,%f %fx%f]", mStickyPositionData->mScrollId,
mStickyPositionData->mOuter.x, mStickyPositionData->mOuter.y,
mStickyPositionData->mOuter.width, mStickyPositionData->mOuter.height,
mStickyPositionData->mInner.x, mStickyPositionData->mInner.y,
mStickyPositionData->mInner.width, mStickyPositionData->mInner.height);
}
return aTo;
}

View File

@ -909,6 +909,32 @@ public:
}
}
/**
* CONSTRUCTION PHASE ONLY
* If a layer is "sticky position", |aScrollId| holds the scroll identifier
* of the scrollable content that contains it. The difference between the two
* rectangles |aOuter| and |aInner| is treated as two intervals in each
* dimension, with the current scroll position at the origin. For each
* dimension, while that component of the scroll position lies within either
* interval, the layer should not move relative to its scrolling container.
*/
void SetStickyPositionData(FrameMetrics::ViewID aScrollId, LayerRect aOuter,
LayerRect aInner)
{
if (!mStickyPositionData ||
!mStickyPositionData->mOuter.IsEqualEdges(aOuter) ||
!mStickyPositionData->mInner.IsEqualEdges(aInner)) {
MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) StickyPositionData", this));
if (!mStickyPositionData) {
mStickyPositionData = new StickyPositionData;
}
mStickyPositionData->mScrollId = aScrollId;
mStickyPositionData->mOuter = aOuter;
mStickyPositionData->mInner = aInner;
Mutated();
}
}
// These getters can be used anytime.
float GetOpacity() { return mOpacity; }
const nsIntRect* GetClipRect() { return mUseClipRect ? &mClipRect : nullptr; }
@ -926,8 +952,12 @@ public:
float GetPostXScale() const { return mPostXScale; }
float GetPostYScale() const { return mPostYScale; }
bool GetIsFixedPosition() { return mIsFixedPosition; }
bool GetIsStickyPosition() { return mStickyPositionData; }
LayerPoint GetFixedPositionAnchor() { return mAnchor; }
const LayerMargin& GetFixedPositionMargins() { return mMargins; }
FrameMetrics::ViewID GetStickyScrollContainerId() { return mStickyPositionData->mScrollId; }
const LayerRect& GetStickyScrollRangeOuter() { return mStickyPositionData->mOuter; }
const LayerRect& GetStickyScrollRangeInner() { return mStickyPositionData->mInner; }
Layer* GetMaskLayer() const { return mMaskLayer; }
// Note that all lengths in animation data are either in CSS pixels or app
@ -1283,6 +1313,12 @@ protected:
bool mIsFixedPosition;
LayerPoint mAnchor;
LayerMargin mMargins;
struct StickyPositionData {
FrameMetrics::ViewID mScrollId;
LayerRect mOuter;
LayerRect mInner;
};
nsAutoPtr<StickyPositionData> mStickyPositionData;
DebugOnly<uint32_t> mDebugColorIndex;
// If this layer is used for OMTA, then this counter is used to ensure we
// stay in sync with the animation manager

View File

@ -214,14 +214,31 @@ GetLayerFixedMarginsOffset(Layer* aLayer,
return translation;
}
void
AsyncCompositionManager::AlignFixedLayersForAnchorPoint(Layer* aLayer,
Layer* aTransformedSubtreeRoot,
const gfx3DMatrix& aPreviousTransformForRoot,
const LayerMargin& aFixedLayerMargins)
static gfxFloat
IntervalOverlap(gfxFloat aTranslation, gfxFloat aMin, gfxFloat aMax)
{
if (aLayer != aTransformedSubtreeRoot && aLayer->GetIsFixedPosition() &&
!aLayer->GetParent()->GetIsFixedPosition()) {
// Determine the amount of overlap between the 1D vector |aTranslation|
// and the interval [aMin, aMax].
if (aTranslation > 0) {
return std::max(0.0, std::min(aMax, aTranslation) - std::max(aMin, 0.0));
} else {
return std::min(0.0, std::max(aMin, aTranslation) - std::min(aMax, 0.0));
}
}
void
AsyncCompositionManager::AlignFixedAndStickyLayers(Layer* aLayer,
Layer* aTransformedSubtreeRoot,
const gfx3DMatrix& aPreviousTransformForRoot,
const LayerMargin& aFixedLayerMargins)
{
bool isRootFixed = aLayer->GetIsFixedPosition() &&
!aLayer->GetParent()->GetIsFixedPosition();
bool isStickyForSubtree = aLayer->GetIsStickyPosition() &&
aTransformedSubtreeRoot->AsContainerLayer() &&
aLayer->GetStickyScrollContainerId() ==
aTransformedSubtreeRoot->AsContainerLayer()->GetFrameMetrics().mScrollId;
if (aLayer != aTransformedSubtreeRoot && (isRootFixed || isStickyForSubtree)) {
// Insert a translation so that the position of the anchor point is the same
// before and after the change to the transform of aTransformedSubtreeRoot.
// This currently only works for fixed layers with 2D transforms.
@ -286,6 +303,21 @@ AsyncCompositionManager::AlignFixedLayersForAnchorPoint(Layer* aLayer,
oldCumulativeTransform.Transform(locallyTransformedOffsetAnchor));
gfxPoint translation = oldAnchorPositionInNewSpace - locallyTransformedAnchor;
if (aLayer->GetIsStickyPosition()) {
// For sticky positioned layers, the difference between the two rectangles
// defines a pair of translation intervals in each dimension through which
// the layer should not move relative to the scroll container. To
// accomplish this, we limit each dimension of the |translation| to that
// part of it which overlaps those intervals.
const LayerRect& stickyOuter = aLayer->GetStickyScrollRangeOuter();
const LayerRect& stickyInner = aLayer->GetStickyScrollRangeInner();
translation.y = IntervalOverlap(translation.y, stickyOuter.y, stickyOuter.YMost()) -
IntervalOverlap(translation.y, stickyInner.y, stickyInner.YMost());
translation.x = IntervalOverlap(translation.x, stickyOuter.x, stickyOuter.XMost()) -
IntervalOverlap(translation.x, stickyInner.x, stickyInner.XMost());
}
// Finally, apply the 2D translation to the layer transform.
TranslateShadowLayer2D(aLayer, translation);
@ -296,8 +328,8 @@ AsyncCompositionManager::AlignFixedLayersForAnchorPoint(Layer* aLayer,
for (Layer* child = aLayer->GetFirstChild();
child; child = child->GetNextSibling()) {
AlignFixedLayersForAnchorPoint(child, aTransformedSubtreeRoot,
aPreviousTransformForRoot, aFixedLayerMargins);
AlignFixedAndStickyLayers(child, aTransformedSubtreeRoot,
aPreviousTransformForRoot, aFixedLayerMargins);
}
}
@ -501,7 +533,7 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFram
#endif
oldTransform.Scale(resolution.scale, resolution.scale, 1);
AlignFixedLayersForAnchorPoint(aLayer, aLayer, oldTransform, fixedLayerMargins);
AlignFixedAndStickyLayers(aLayer, aLayer, oldTransform, fixedLayerMargins);
appliedTransform = true;
}
@ -607,7 +639,7 @@ AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer, const LayoutDev
// transform so that fixed position content moves and scales accordingly.
// These calculations will effectively scale and offset fixed position layers
// in screen space when the compensatory transform is performed in
// AlignFixedLayersForAnchorPoint.
// AlignFixedAndStickyLayers.
ScreenRect contentScreenRect = mContentRect * userZoom;
gfxPoint3D overscrollTranslation;
if (userScroll.x < contentScreenRect.x) {
@ -637,7 +669,7 @@ AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer, const LayoutDev
// Make sure fixed position layers don't move away from their anchor points
// when we're asynchronously panning or zooming
AlignFixedLayersForAnchorPoint(aLayer, aLayer, oldTransform, fixedLayerMargins);
AlignFixedAndStickyLayers(aLayer, aLayer, oldTransform, fixedLayerMargins);
}
bool

View File

@ -146,19 +146,20 @@ private:
ScreenPoint& aOffset);
/**
* Adds a translation to the transform of any fixed-pos layer descendant of
* aTransformedSubtreeRoot whose parent layer is not fixed. The translation is
* chosen so that the layer's anchor point relative to aTransformedSubtreeRoot's
* parent layer is the same as it was when aTransformedSubtreeRoot's
* GetLocalTransform() was aPreviousTransformForRoot.
* Adds a translation to the transform of any fixed position (whose parent
* layer is not fixed) or sticky position layer descendant of
* aTransformedSubtreeRoot. The translation is chosen so that the layer's
* anchor point relative to aTransformedSubtreeRoot's parent layer is the same
* as it was when aTransformedSubtreeRoot's GetLocalTransform() was
* aPreviousTransformForRoot. For sticky position layers, the translation is
* further intersected with the layer's sticky scroll ranges.
* This function will also adjust layers so that the given content document
* fixed position margins will be respected during asynchronous panning and
* zooming.
*/
void AlignFixedLayersForAnchorPoint(Layer* aLayer,
Layer* aTransformedSubtreeRoot,
const gfx3DMatrix& aPreviousTransformForRoot,
const LayerMargin& aFixedLayerMargins);
void AlignFixedAndStickyLayers(Layer* aLayer, Layer* aTransformedSubtreeRoot,
const gfx3DMatrix& aPreviousTransformForRoot,
const LayerMargin& aFixedLayerMargins);
/**
* DRAWING PHASE ONLY

View File

@ -131,7 +131,7 @@ ThebesLayerComposite::RenderLayer(const nsIntPoint& aOffset,
tiledLayerProps.mDisplayPort = GetDisplayPort();
tiledLayerProps.mEffectiveResolution = GetEffectiveResolution();
tiledLayerProps.mCompositionBounds = GetCompositionBounds();
tiledLayerProps.mRetainTiles = !mIsFixedPosition;
tiledLayerProps.mRetainTiles = !(mIsFixedPosition || mStickyPositionData);
tiledLayerProps.mValidRegion = mValidRegion;
}

View File

@ -272,6 +272,11 @@ LayerTransactionParent::RecvUpdate(const InfallibleTArray<Edit>& cset,
layer->SetIsFixedPosition(common.isFixedPosition());
layer->SetFixedPositionAnchor(common.fixedPositionAnchor());
layer->SetFixedPositionMargins(common.fixedPositionMargin());
if (common.isStickyPosition()) {
layer->SetStickyPositionData(common.stickyScrollContainerId(),
common.stickyScrollRangeOuter(),
common.stickyScrollRangeInner());
}
if (PLayerParent* maskLayer = common.maskLayerParent()) {
layer->SetMaskLayer(cast(maskLayer)->AsLayer());
} else {

View File

@ -33,6 +33,7 @@ using mozilla::dom::ScreenOrientation;
using mozilla::layers::TextureInfo;
using mozilla::LayerMargin;
using mozilla::LayerPoint;
using mozilla::LayerRect;
using mozilla::layers::ScaleMode;
using mozilla::layers::DiagnosticTypes;
@ -192,6 +193,10 @@ struct CommonLayerAttributes {
bool isFixedPosition;
LayerPoint fixedPositionAnchor;
LayerMargin fixedPositionMargin;
bool isStickyPosition;
uint64_t stickyScrollContainerId;
LayerRect stickyScrollRangeOuter;
LayerRect stickyScrollRangeInner;
nullable PLayer maskLayer;
// Animated colors will only honored for ColorLayers.
Animation[] animations;

View File

@ -496,6 +496,12 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies)
common.isFixedPosition() = mutant->GetIsFixedPosition();
common.fixedPositionAnchor() = mutant->GetFixedPositionAnchor();
common.fixedPositionMargin() = mutant->GetFixedPositionMargins();
common.isStickyPosition() = mutant->GetIsStickyPosition();
if (mutant->GetIsStickyPosition()) {
common.stickyScrollContainerId() = mutant->GetStickyScrollContainerId();
common.stickyScrollRangeOuter() = mutant->GetStickyScrollRangeOuter();
common.stickyScrollRangeInner() = mutant->GetStickyScrollRangeInner();
}
if (Layer* maskLayer = mutant->GetMaskLayer()) {
common.maskLayerChild() = Shadow(maskLayer->AsShadowableLayer());
} else {

View File

@ -89,6 +89,61 @@ gfxCharacterMap::NotifyReleased()
delete this;
}
gfxFontEntry::gfxFontEntry() :
mItalic(false), mFixedPitch(false),
mIsProxy(false), mIsValid(true),
mIsBadUnderlineFont(false),
mIsUserFont(false),
mIsLocalUserFont(false),
mStandardFace(false),
mSymbolFont(false),
mIgnoreGDEF(false),
mIgnoreGSUB(false),
mSVGInitialized(false),
mHasSpaceFeaturesInitialized(false),
mHasSpaceFeatures(false),
mHasSpaceFeaturesKerning(false),
mHasSpaceFeaturesNonKerning(false),
mHasSpaceFeaturesSubDefault(false),
mCheckedForGraphiteTables(false),
mHasCmapTable(false),
mGrFaceInitialized(false),
mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
mUVSOffset(0), mUVSData(nullptr),
mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
mHBFace(nullptr),
mGrFace(nullptr),
mGrFaceRefCnt(0)
{
}
gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) :
mName(aName), mItalic(false), mFixedPitch(false),
mIsProxy(false), mIsValid(true),
mIsBadUnderlineFont(false), mIsUserFont(false),
mIsLocalUserFont(false), mStandardFace(aIsStandardFace),
mSymbolFont(false),
mIgnoreGDEF(false),
mIgnoreGSUB(false),
mSVGInitialized(false),
mHasSpaceFeaturesInitialized(false),
mHasSpaceFeatures(false),
mHasSpaceFeaturesKerning(false),
mHasSpaceFeaturesNonKerning(false),
mHasSpaceFeaturesSubDefault(false),
mCheckedForGraphiteTables(false),
mHasCmapTable(false),
mGrFaceInitialized(false),
mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
mUVSOffset(0), mUVSData(nullptr),
mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
mHBFace(nullptr),
mGrFace(nullptr),
mGrFaceRefCnt(0)
{
memset(&mHasSpaceFeaturesSub, 0, sizeof(mHasSpaceFeaturesSub));
}
gfxFontEntry::~gfxFontEntry()
{
// For downloaded fonts, we need to tell the user font cache that this
@ -102,11 +157,6 @@ gfxFontEntry::~gfxFontEntry()
// face objects should have been released.
MOZ_ASSERT(!mHBFace);
MOZ_ASSERT(!mGrFaceInitialized);
if (mSVGGlyphs) {
delete mSVGGlyphs;
}
delete mUserFontData;
}
bool gfxFontEntry::IsSymbolFont()
@ -248,7 +298,7 @@ gfxFontEntry::RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId,
}
bool
gfxFontEntry::TryGetSVGData()
gfxFontEntry::TryGetSVGData(gfxFont* aFont)
{
if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) {
return false;
@ -266,12 +316,31 @@ gfxFontEntry::TryGetSVGData()
// gfxSVGGlyphs will hb_blob_destroy() the table when it is finished
// with it.
mSVGGlyphs = new gfxSVGGlyphs(svgTable);
mSVGGlyphs = new gfxSVGGlyphs(svgTable, this);
}
if (!mFontsUsingSVGGlyphs.Contains(aFont)) {
mFontsUsingSVGGlyphs.AppendElement(aFont);
}
return !!mSVGGlyphs;
}
void
gfxFontEntry::NotifyFontDestroyed(gfxFont* aFont)
{
mFontsUsingSVGGlyphs.RemoveElement(aFont);
}
void
gfxFontEntry::NotifyGlyphsChanged()
{
for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) {
gfxFont* font = mFontsUsingSVGGlyphs[i];
font->NotifyGlyphsChanged();
}
}
/**
* FontTableBlobData
*
@ -1704,6 +1773,14 @@ gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
mKerningSet = HasFeatureSet(HB_TAG('k','e','r','n'), mKerningEnabled);
}
static PLDHashOperator
NotifyFontDestroyed(nsPtrHashKey<gfxFont::GlyphChangeObserver>* aKey,
void* aClosure)
{
aKey->GetKey()->ForgetFont();
return PL_DHASH_NEXT;
}
gfxFont::~gfxFont()
{
uint32_t i, count = mGlyphExtentsArray.Length();
@ -1713,6 +1790,12 @@ gfxFont::~gfxFont()
for (i = 0; i < count; ++i) {
delete mGlyphExtentsArray[i];
}
mFontEntry->NotifyFontDestroyed(this);
if (mGlyphChangeObservers) {
mGlyphChangeObservers->EnumerateEntries(NotifyFontDestroyed, nullptr);
}
}
/*static*/
@ -2295,14 +2378,14 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
double direction = aTextRun->GetDirection();
gfxMatrix globalMatrix = aContext->CurrentMatrix();
bool haveSVGGlyphs = GetFontEntry()->TryGetSVGData();
bool haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
nsAutoPtr<gfxTextContextPaint> contextPaint;
if (haveSVGGlyphs && !aContextPaint) {
// If no pattern is specified for fill, use the current pattern
NS_ASSERTION((aDrawMode & GLYPH_STROKE) == 0, "no pattern supplied for stroking text");
nsRefPtr<gfxPattern> fillPattern = aContext->GetPattern();
contextPaint = new SimpleTextContextPaint(fillPattern, nullptr,
aContext->CurrentMatrix());
aContext->CurrentMatrix());
aContextPaint = contextPaint;
}
@ -2935,6 +3018,28 @@ gfxFont::Measure(gfxTextRun *aTextRun,
return metrics;
}
static PLDHashOperator
NotifyGlyphChangeObservers(nsPtrHashKey<gfxFont::GlyphChangeObserver>* aKey,
void* aClosure)
{
aKey->GetKey()->NotifyGlyphsChanged();
return PL_DHASH_NEXT;
}
void
gfxFont::NotifyGlyphsChanged()
{
uint32_t i, count = mGlyphExtentsArray.Length();
for (i = 0; i < count; ++i) {
// Flush cached extents array
mGlyphExtentsArray[i]->NotifyGlyphsChanged();
}
if (mGlyphChangeObservers) {
mGlyphChangeObservers->EnumerateEntries(NotifyGlyphChangeObservers, nullptr);
}
}
static bool
IsBoundarySpace(PRUnichar aChar, PRUnichar aNextChar)
{
@ -3389,7 +3494,7 @@ gfxFont::SetupGlyphExtents(gfxContext *aContext, uint32_t aGlyphID, bool aNeedTi
aContext->IdentityMatrix();
gfxRect svgBounds;
if (mFontEntry->TryGetSVGData() && mFontEntry->HasSVGGlyph(aGlyphID) &&
if (mFontEntry->TryGetSVGData(this) && mFontEntry->HasSVGGlyph(aGlyphID) &&
mFontEntry->GetSVGGlyphExtents(aContext, aGlyphID, &svgBounds)) {
gfxFloat d2a = aExtents->GetAppUnitsPerDevUnit();
aExtents->SetTightGlyphExtents(aGlyphID,
@ -3725,6 +3830,23 @@ gfxFont::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
SizeOfExcludingThis(aMallocSizeOf, aSizes);
}
void
gfxFont::AddGlyphChangeObserver(GlyphChangeObserver *aObserver)
{
if (!mGlyphChangeObservers) {
mGlyphChangeObservers = new nsTHashtable<nsPtrHashKey<GlyphChangeObserver> >;
}
mGlyphChangeObservers->PutEntry(aObserver);
}
void
gfxFont::RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver)
{
NS_ASSERTION(mGlyphChangeObservers, "No observers registered");
NS_ASSERTION(mGlyphChangeObservers->Contains(aObserver), "Observer not registered");
mGlyphChangeObservers->RemoveEntry(aObserver);
}
gfxGlyphExtents::~gfxGlyphExtents()
{
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS

View File

@ -228,35 +228,7 @@ class gfxFontEntry {
public:
NS_INLINE_DECL_REFCOUNTING(gfxFontEntry)
gfxFontEntry(const nsAString& aName, bool aIsStandardFace = false) :
mName(aName), mItalic(false), mFixedPitch(false),
mIsProxy(false), mIsValid(true),
mIsBadUnderlineFont(false), mIsUserFont(false),
mIsLocalUserFont(false), mStandardFace(aIsStandardFace),
mSymbolFont(false),
mIgnoreGDEF(false),
mIgnoreGSUB(false),
mSVGInitialized(false),
mHasSpaceFeaturesInitialized(false),
mHasSpaceFeatures(false),
mHasSpaceFeaturesKerning(false),
mHasSpaceFeaturesNonKerning(false),
mHasSpaceFeaturesSubDefault(false),
mCheckedForGraphiteTables(false),
mHasCmapTable(false),
mGrFaceInitialized(false),
mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
mUVSOffset(0), mUVSData(nullptr),
mUserFontData(nullptr),
mSVGGlyphs(nullptr),
mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
mHBFace(nullptr),
mGrFace(nullptr),
mGrFaceRefCnt(0)
{
memset(&mHasSpaceFeaturesSub, 0, sizeof(mHasSpaceFeaturesSub));
}
gfxFontEntry(const nsAString& aName, bool aIsStandardFace = false);
virtual ~gfxFontEntry();
// unique name for the face, *not* the family; not necessarily the
@ -324,12 +296,15 @@ public:
// can be safely dereferenced.
virtual nsresult ReadCMAP();
bool TryGetSVGData();
bool TryGetSVGData(gfxFont* aFont);
bool HasSVGGlyph(uint32_t aGlyphId);
bool GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId,
gfxRect *aResult);
bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, int aDrawMode,
gfxTextContextPaint *aContextPaint);
// Call this when glyph geometry or rendering has changed
// (e.g. animated SVG glyphs)
void NotifyGlyphsChanged();
virtual bool MatchesGenericFamily(const nsACString& aGeneric) const {
return true;
@ -414,6 +389,10 @@ public:
// Caller must call gfxFontEntry::ReleaseGrFace when finished with it.
gr_face* GetGrFace();
virtual void ReleaseGrFace(gr_face* aFace);
// Called to notify that aFont is being destroyed. Needed when we're tracking
// the fonts belonging to this font entry.
void NotifyFontDestroyed(gfxFont* aFont);
// For memory reporting
virtual void SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
@ -455,9 +434,10 @@ public:
nsRefPtr<gfxCharacterMap> mCharacterMap;
uint32_t mUVSOffset;
nsAutoArrayPtr<uint8_t> mUVSData;
gfxUserFontData* mUserFontData;
gfxSVGGlyphs *mSVGGlyphs;
nsAutoPtr<gfxUserFontData> mUserFontData;
nsAutoPtr<gfxSVGGlyphs> mSVGGlyphs;
// list of gfxFonts that are using SVG glyphs
nsTArray<gfxFont*> mFontsUsingSVGGlyphs;
nsTArray<gfxFontFeature> mFeatureSettings;
uint32_t mLanguageOverride;
@ -468,34 +448,7 @@ protected:
friend class gfxFontFamily;
friend class gfxSingleFaceMacFontFamily;
gfxFontEntry() :
mItalic(false), mFixedPitch(false),
mIsProxy(false), mIsValid(true),
mIsBadUnderlineFont(false),
mIsUserFont(false),
mIsLocalUserFont(false),
mStandardFace(false),
mSymbolFont(false),
mIgnoreGDEF(false),
mIgnoreGSUB(false),
mSVGInitialized(false),
mHasSpaceFeaturesInitialized(false),
mHasSpaceFeatures(false),
mHasSpaceFeaturesKerning(false),
mHasSpaceFeaturesNonKerning(false),
mHasSpaceFeaturesSubDefault(false),
mCheckedForGraphiteTables(false),
mHasCmapTable(false),
mGrFaceInitialized(false),
mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
mUVSOffset(0), mUVSData(nullptr),
mUserFontData(nullptr),
mSVGGlyphs(nullptr),
mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
mHBFace(nullptr),
mGrFace(nullptr),
mGrFaceRefCnt(0)
{ }
gfxFontEntry();
virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) {
NS_NOTREACHED("oops, somebody didn't override CreateFontInstance");
@ -1160,6 +1113,10 @@ public:
enum { INVALID_WIDTH = 0xFFFF };
void NotifyGlyphsChanged() {
mTightGlyphExtents.Clear();
}
// returns INVALID_WIDTH => not a contained glyph
// Otherwise the glyph has no before-bearing or vertical bearings,
// and the result is its width measured from the baseline origin, in
@ -1695,6 +1652,9 @@ public:
}
}
// Glyph rendering/geometry has changed, so invalidate data as necessary.
void NotifyGlyphsChanged();
virtual void SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontCacheSizes* aSizes) const;
virtual void SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
@ -1718,7 +1678,39 @@ public:
return mKerningSet && !mKerningEnabled;
}
/**
* Subclass this object to be notified of glyph changes. Delete the object
* when no longer needed.
*/
class GlyphChangeObserver {
public:
virtual ~GlyphChangeObserver()
{
if (mFont) {
mFont->RemoveGlyphChangeObserver(this);
}
}
// This gets called when the gfxFont dies.
void ForgetFont() { mFont = nullptr; }
virtual void NotifyGlyphsChanged() = 0;
protected:
GlyphChangeObserver(gfxFont *aFont) : mFont(aFont)
{
mFont->AddGlyphChangeObserver(this);
}
gfxFont* mFont;
};
friend class GlyphChangeObserver;
bool GlyphsMayChange()
{
// Currently only fonts with SVG glyphs can have animated glyphs
return mFontEntry->TryGetSVGData(this);
}
protected:
void AddGlyphChangeObserver(GlyphChangeObserver *aObserver);
void RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver);
bool HasSubstitutionRulesWithSpaceLookups(int32_t aRunScript) {
NS_ASSERTION(GetFontEntry()->mHasSpaceFeaturesInitialized,
@ -1925,6 +1917,7 @@ protected:
nsExpirationState mExpirationState;
gfxFontStyle mStyle;
nsAutoTArray<gfxGlyphExtents*,1> mGlyphExtentsArray;
nsAutoPtr<nsTHashtable<nsPtrHashKey<GlyphChangeObserver> > > mGlyphChangeObservers;
gfxFloat mAdjustedSize;

View File

@ -31,11 +31,15 @@
#include "nsIScriptSecurityManager.h"
#include "nsHostObjectProtocolHandler.h"
#include "nsContentUtils.h"
#include "gfxFont.h"
#include "nsSMILAnimationController.h"
#include "harfbuzz/hb.h"
#define SVG_CONTENT_TYPE NS_LITERAL_CSTRING("image/svg+xml")
#define UTF8_CHARSET NS_LITERAL_CSTRING("utf-8")
using namespace mozilla;
typedef mozilla::dom::Element Element;
mozilla::gfx::UserDataKey gfxTextContextPaint::sUserDataKey;
@ -44,10 +48,10 @@ const float gfxSVGGlyphs::SVG_UNITS_PER_EM = 1000.0f;
const gfxRGBA SimpleTextContextPaint::sZero = gfxRGBA(0.0f, 0.0f, 0.0f, 0.0f);
gfxSVGGlyphs::gfxSVGGlyphs(hb_blob_t *aSVGTable)
gfxSVGGlyphs::gfxSVGGlyphs(hb_blob_t *aSVGTable, gfxFontEntry *aFontEntry)
: mSVGData(aSVGTable)
, mFontEntry(aFontEntry)
{
mSVGData = aSVGTable;
unsigned int length;
const char* svgData = hb_blob_get_data(mSVGData, &length);
mHeader = reinterpret_cast<const Header*>(svgData);
@ -70,6 +74,12 @@ gfxSVGGlyphs::~gfxSVGGlyphs()
hb_blob_destroy(mSVGData);
}
void
gfxSVGGlyphs::DidRefresh()
{
mFontEntry->NotifyGlyphsChanged();
}
/*
* Comparison operator for finding a range containing a given glyph ID. Simply
* checks whether |key| is less (greater) than every element of |range|, in
@ -120,7 +130,7 @@ gfxSVGGlyphs::FindOrCreateGlyphsDocument(uint32_t aGlyphId)
if (entry->mDocOffset > 0 &&
uint64_t(mHeader->mDocIndexOffset) + entry->mDocOffset + entry->mDocLength <= length) {
result = new gfxSVGGlyphsDocument(data + mHeader->mDocIndexOffset + entry->mDocOffset,
entry->mDocLength);
entry->mDocLength, this);
mGlyphDocs.Put(entry->mDocOffset, result);
}
}
@ -131,8 +141,6 @@ gfxSVGGlyphs::FindOrCreateGlyphsDocument(uint32_t aGlyphId)
nsresult
gfxSVGGlyphsDocument::SetupPresentation()
{
mDocument->SetIsBeingUsedAsImage();
nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
nsXPIDLCString contractId;
nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "image/svg+xml", getter_Copies(contractId));
@ -154,22 +162,36 @@ gfxSVGGlyphsDocument::SetupPresentation()
nsCOMPtr<nsIPresShell> presShell;
rv = viewer->GetPresShell(getter_AddRefs(presShell));
NS_ENSURE_SUCCESS(rv, rv);
presShell->GetPresContext()->SetIsGlyph(true);
nsPresContext* presContext = presShell->GetPresContext();
presContext->SetIsGlyph(true);
if (!presShell->DidInitialize()) {
nsRect rect = presShell->GetPresContext()->GetVisibleArea();
nsRect rect = presContext->GetVisibleArea();
rv = presShell->Initialize(rect.width, rect.height);
NS_ENSURE_SUCCESS(rv, rv);
}
mDocument->FlushPendingNotifications(Flush_Layout);
nsSMILAnimationController* controller = mDocument->GetAnimationController();
if (controller) {
controller->Resume(nsSMILTimeContainer::PAUSE_IMAGE);
}
mDocument->SetImagesNeedAnimating(true);
mViewer = viewer;
mPresShell = presShell;
mPresShell->AddPostRefreshObserver(this);
return NS_OK;
}
void
gfxSVGGlyphsDocument::DidRefresh()
{
mOwner->DidRefresh();
}
/**
* Walk the DOM tree to find all glyph elements and insert them into the lookup
* table
@ -251,7 +273,10 @@ gfxSVGGlyphsDocument::GetGlyphElement(uint32_t aGlyphId)
return mGlyphIdMap.Get(aGlyphId);
}
gfxSVGGlyphsDocument::gfxSVGGlyphsDocument(const uint8_t *aBuffer, uint32_t aBufLen)
gfxSVGGlyphsDocument::gfxSVGGlyphsDocument(const uint8_t *aBuffer,
uint32_t aBufLen,
gfxSVGGlyphs *aSVGGlyphs)
: mOwner(aSVGGlyphs)
{
ParseDocument(aBuffer, aBufLen);
if (!mDocument) {
@ -274,6 +299,22 @@ gfxSVGGlyphsDocument::gfxSVGGlyphsDocument(const uint8_t *aBuffer, uint32_t aBuf
FindGlyphElements(root);
}
gfxSVGGlyphsDocument::~gfxSVGGlyphsDocument()
{
if (mDocument) {
nsSMILAnimationController* controller = mDocument->GetAnimationController();
if (controller) {
controller->Pause(nsSMILTimeContainer::PAUSE_PAGEHIDE);
}
}
if (mPresShell) {
mPresShell->RemovePostRefreshObserver(this);
}
if (mViewer) {
mViewer->Destroy();
}
}
static nsresult
CreateBufferedStream(const uint8_t *aBuffer, uint32_t aBufLen,
nsCOMPtr<nsIInputStream> &aResult)
@ -339,6 +380,8 @@ gfxSVGGlyphsDocument::ParseDocument(const uint8_t *aBuffer, uint32_t aBufLen)
channel->SetOwner(principal);
// Set this early because various decisions during page-load depend on it.
document->SetIsBeingUsedAsImage();
document->SetReadyStateInternal(nsIDocument::READYSTATE_UNINITIALIZED);
nsCOMPtr<nsIStreamListener> listener;

View File

@ -17,6 +17,9 @@
#include "gfxPattern.h"
#include "gfxFont.h"
#include "mozilla/gfx/UserData.h"
#include "nsRefreshDriver.h"
class gfxSVGGlyphs;
/**
@ -27,21 +30,20 @@
* Finds and looks up elements contained in the SVG document which have glyph
* mappings to be drawn by gfxSVGGlyphs
*/
class gfxSVGGlyphsDocument
class gfxSVGGlyphsDocument MOZ_FINAL : public nsAPostRefreshObserver
{
typedef mozilla::dom::Element Element;
typedef gfxFont::DrawMode DrawMode;
public:
gfxSVGGlyphsDocument(const uint8_t *aBuffer, uint32_t aBufLen);
gfxSVGGlyphsDocument(const uint8_t *aBuffer, uint32_t aBufLen,
gfxSVGGlyphs *aSVGGlyphs);
Element *GetGlyphElement(uint32_t aGlyphId);
~gfxSVGGlyphsDocument() {
if (mViewer) {
mViewer->Destroy();
}
}
~gfxSVGGlyphsDocument();
virtual void DidRefresh() MOZ_OVERRIDE;
private:
nsresult ParseDocument(const uint8_t *aBuffer, uint32_t aBufLen);
@ -52,6 +54,8 @@ private:
void InsertGlyphId(Element *aGlyphElement);
// Weak so as not to create a cycle. mOwner owns us so this can't dangle.
gfxSVGGlyphs* mOwner;
nsCOMPtr<nsIDocument> mDocument;
nsCOMPtr<nsIContentViewer> mViewer;
nsCOMPtr<nsIPresShell> mPresShell;
@ -83,13 +87,18 @@ public:
* that are passed in, and will hb_blob_destroy() them when finished;
* the caller should -not- destroy these references.
*/
gfxSVGGlyphs(hb_blob_t *aSVGTable);
gfxSVGGlyphs(hb_blob_t *aSVGTable, gfxFontEntry *aFontEntry);
/**
* Releases our references to the SVG table.
* Releases our references to the SVG table and cleans up everything else.
*/
~gfxSVGGlyphs();
/**
* This is called when the refresh driver has ticked.
*/
void DidRefresh();
/**
* Find the |gfxSVGGlyphsDocument| containing an SVG glyph for |aGlyphId|.
* If |aGlyphId| does not map to an SVG document, return null.
@ -126,6 +135,7 @@ private:
nsBaseHashtable<nsUint32HashKey, Element*, Element*> mGlyphIdMap;
hb_blob_t *mSVGData;
gfxFontEntry *mFontEntry;
const struct Header {
mozilla::AutoSwap_PRUint16 mVersion;

View File

@ -246,7 +246,8 @@ public:
kWindowsServer2003 = 0x50002,
kWindowsVista = 0x60000,
kWindows7 = 0x60001,
kWindows8 = 0x60002
kWindows8 = 0x60002,
kWindows8_1 = 0x60003
};
static int32_t WindowsOSVersion(int32_t *aBuildNum = nullptr);

View File

@ -54,7 +54,7 @@ function takeReferenceSnapshot() {
"reference div should disappear when it becomes display:none");
}
function myOnStopFrame() {
function myOnStopFrame(aRequest) {
gOnStopFrameCounter++;
ok(true, "myOnStopFrame called");
let currentSnapshot = snapshotWindow(window, false);
@ -64,8 +64,7 @@ function myOnStopFrame() {
"at call #" + gOnStopFrameCounter + " to onStopFrame");
cleanUpAndFinish();
}
else
setTimeout(myOnStopFrame, 1);
setTimeout(function() { myOnStopFrame(0, 0); }, 1000);
}
function failTest() {
@ -81,6 +80,8 @@ function cleanUpAndFinish() {
if (gIsTestFinished) {
return;
}
let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
imgLoadingContent.removeObserver(gMyDecoderObserver);
SimpleTest.finish();
gIsTestFinished = true;
}
@ -88,12 +89,19 @@ function cleanUpAndFinish() {
function main() {
takeReferenceSnapshot();
// Create, customize & attach decoder observer
observer = new ImageDecoderObserverStub();
observer.frameComplete = myOnStopFrame;
gMyDecoderObserver =
Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
.createScriptedObserver(observer);
let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
imgLoadingContent.addObserver(gMyDecoderObserver);
// We want to test the cold loading behavior, so clear cache in case an
// earlier test got our image in there already.
clearImageCache();
setTimeout(myOnStopFrame, 1);
// kick off image-loading! myOnStopFrame handles the rest.
gImg.setAttribute("src", "lime-anim-100x100.svg");

View File

@ -287,7 +287,6 @@ class Message : public Pickle {
COMPRESS_BIT = 0x0200
};
#pragma pack(push, 2)
struct Header : Pickle::Header {
int32_t routing; // ID of the view that this message is destined for
msgid_t type; // specifies the user-defined message type
@ -305,7 +304,6 @@ class Message : public Pickle {
// Sequence number
int32_t seqno;
};
#pragma pack(pop)
Header* header() {
return headerT<Header>();

View File

@ -400,37 +400,10 @@ js::DirectEvalFromIon(JSContext *cx,
NullFramePtr() /* evalInFrame */, vp.address());
}
// We once supported a second argument to eval to use as the scope chain
// when evaluating the code string. Warn when such uses are seen so that
// authors will know that support for eval(s, o) has been removed.
static inline bool
WarnOnTooManyArgs(JSContext *cx, const CallArgs &args)
{
if (args.length() > 1) {
Rooted<JSScript*> script(cx, cx->currentScript());
if (script && !script->warnedAboutTwoArgumentEval) {
static const char TWO_ARGUMENT_WARNING[] =
"Support for eval(code, scopeObject) has been removed. "
"Use |with (scopeObject) eval(code);| instead.";
if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
return false;
script->warnedAboutTwoArgumentEval = true;
} else {
// In the case of an indirect call without a caller frame, avoid a
// potential warning-flood by doing nothing.
}
}
return true;
}
bool
js::IndirectEval(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (!WarnOnTooManyArgs(cx, args))
return false;
Rooted<GlobalObject*> global(cx, &args.callee().global());
return EvalKernel(cx, args, INDIRECT_EVAL, NullFramePtr(), global, NULL);
}
@ -447,9 +420,6 @@ js::DirectEval(JSContext *cx, const CallArgs &args)
JS_ASSERT_IF(caller.isFunctionFrame(),
caller.compartment() == caller.callee()->compartment());
if (!WarnOnTooManyArgs(cx, args))
return false;
RootedObject scopeChain(cx, caller.scopeChain());
return EvalKernel(cx, args, DIRECT_EVAL, caller, scopeChain, iter.pc());
}

View File

@ -217,7 +217,7 @@ class FileKind(object):
def get_all_filenames():
'''Get a list of all the files in the (Mercurial or Git) repository.'''
cmds = [['hg', 'manifest', '-q'], ['git', 'ls-files']]
cmds = [['hg', 'manifest', '-q'], ['git', 'ls-files', '--full-name', '../..']]
for cmd in cmds:
try:
all_filenames = subprocess.check_output(cmd, universal_newlines=True,

View File

@ -7,10 +7,10 @@ const ASM_TYPE_FAIL_STRING = "asm.js type error:";
const ASM_DIRECTIVE_FAIL_STRING = "\"use asm\" is only meaningful in the Directive Prologue of a function body";
const USE_ASM = "'use asm';";
const HEAP_IMPORTS = "var i8=new glob.Int8Array(b);var u8=new glob.Uint8Array(b);"+
"var i16=new glob.Int16Array(b);var u16=new glob.Uint16Array(b);"+
"var i32=new glob.Int32Array(b);var u32=new glob.Uint32Array(b);"+
"var f32=new glob.Float32Array(b);var f64=new glob.Float64Array(b);";
const HEAP_IMPORTS = "const i8=new glob.Int8Array(b);var u8=new glob.Uint8Array(b);"+
"const i16=new glob.Int16Array(b);var u16=new glob.Uint16Array(b);"+
"const i32=new glob.Int32Array(b);var u32=new glob.Uint32Array(b);"+
"const f32=new glob.Float32Array(b);var f64=new glob.Float64Array(b);";
const BUF_64KB = new ArrayBuffer(64 * 1024);
function asmCompile()

View File

@ -48,8 +48,11 @@ assertEq(f(), 2);
assertEq(counter, 3);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var add1=imp.add1; function g(i) { i=i|0; return add1(i|0)|0 } return g'), null, imp)(9), 10);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'const add1=imp.add1; function g(i) { i=i|0; return add1(i|0)|0 } return g'), null, imp)(9), 10);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var add3=imp.add3; function g() { var i=1,j=3,k=9; return add3(i|0,j|0,k|0)|0 } return g'), null, imp)(), 13);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'const add3=imp.add3; function g() { var i=1,j=3,k=9; return add3(i|0,j|0,k|0)|0 } return g'), null, imp)(), 13);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var add3=imp.add3; function g() { var i=1.4,j=2.3,k=32.1; return +add3(i,j,k) } return g'), null, imp)(), 1.4+2.3+32.1);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'const add3=imp.add3; function g() { var i=1.4,j=2.3,k=32.1; return +add3(i,j,k) } return g'), null, imp)(), 1.4+2.3+32.1);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var add3=imp.add3; function f(i,j,k) { i=i|0;j=+j;k=k|0; return add3(i|0,j,k|0)|0 } return f'), null, imp)(1, 2.5, 3), 6);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var addN=imp.addN; function f() { return +addN(1,2,3,4.1,5,6.1,7,8.1,9.1,10,11.1,12,13,14.1,15.1,16.1,17.1,18.1) } return f'), null, imp)(), 1+2+3+4.1+5+6.1+7+8.1+9.1+10+11.1+12+13+14.1+15.1+16.1+17.1+18.1);

View File

@ -27,18 +27,42 @@ assertAsmTypeFail(USE_ASM + "function f(i) {i=i|0} function g(i) { i=i|0; return
assertAsmTypeFail(USE_ASM + "function f(i) {i=i|0} function g(i) { i=i|0; return tbl[i&1](3.0)|0 } var tbl=[f,f]; return g");
assertAsmTypeFail(USE_ASM + "function f(d) {d=+d} function g(i) { i=i|0; return tbl[i&1](3)|0 } var tbl=[f,f]; return g");
assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g(i) { i=i|0; return tbl[i&1]()|0 } var tbl=[f,f]; return g"))(0), 42);
assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g(i) { i=i|0; return tbl[i&1]()|0 } const tbl=[f,f]; return g"))(0), 42);
assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() {return 13} function h(i) { i=i|0; return tbl[i&1]()|0 } var tbl=[f,g]; return h"))(1), 13);
assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() {return 13} function h(i) { i=i|0; return tbl[i&1]()|0 } const tbl=[f,g]; return h"))(1), 13);
assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() {return 13} function h(i) { i=i|0; return tbl2[i&1]()|0 } var tbl1=[f,g]; var tbl2=[g,f]; return h"))(1), 42);
assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() {return 13} function h(i) { i=i|0; return tbl2[i&1]()|0 } const tbl1=[f,g]; const tbl2=[g,f]; return h"))(1), 42);
assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() {return 13} function h(i) { i=i|0; return tbl2[i&3]()|0 } var tbl1=[f,g]; var tbl2=[g,g,g,f]; return h"))(3), 42);
assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() {return 13} function h(i) { i=i|0; return tbl2[i&3]()|0 } const tbl1=[f,g]; const tbl2=[g,g,g,f]; return h"))(3), 42);
assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() {return 13} function h(i) { i=i|0; return tbl1[i&1]()|0 } var tbl1=[f,g]; var tbl2=[g,g,g,f]; return h"))(1), 13);
assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() {return 13} function h(i) { i=i|0; return tbl1[i&1]()|0 } const tbl1=[f,g]; const tbl2=[g,g,g,f]; return h"))(1), 13);
assertEq(asmLink(asmCompile(USE_ASM + "var i=0,j=0; function f() {return i|0} function g() {return j|0} function h(x) { x=x|0; i=5;j=10; return tbl2[x&3]()|0 } var tbl1=[f,g]; var tbl2=[g,g,g,f]; return h"))(3), 5);
assertEq(asmLink(asmCompile(USE_ASM + "var i=0,j=0; function f() {return i|0} function g() {return j|0} function h(x) { x=x|0; i=5;j=10; return tbl2[x&3]()|0 } const tbl1=[f,g]; const tbl2=[g,g,g,f]; return h"))(3), 5);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + "var ffi=imp.ffi; function f() {return ffi()|0} function g() {return 13} function h(x) { x=x|0; return tbl2[x&3]()|0 } var tbl2=[g,g,g,f]; return h"), null, {ffi:function(){return 20}})(3), 20);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + "var ffi=imp.ffi; var i=0; function f() {return ((ffi()|0)+i)|0} function g() {return 13} function h(x) { x=x|0; i=2; return tbl2[x&3]()|0 } var tbl2=[g,g,g,f]; return h"), null, {ffi:function(){return 20}})(3), 22);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + "const ffi=imp.ffi; function f() {return ffi()|0} function g() {return 13} function h(x) { x=x|0; return tbl2[x&3]()|0 } const tbl2=[g,g,g,f]; return h"), null, {ffi:function(){return 20}})(3), 20);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + "var ffi=imp.ffi; var i=0; function f() {return ((ffi()|0)+i)|0} function g() {return 13} function h(x) { x=x|0; i=2; return tbl2[x&3]()|0 } var tbl2=[g,g,g,f]; return h"), null, {ffi:function(){return 20}})(3), 22)
;assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + "const ffi=imp.ffi; var i=0; function f() {return ((ffi()|0)+i)|0} function g() {return 13} function h(x) { x=x|0; i=2; return tbl2[x&3]()|0 } const tbl2=[g,g,g,f]; return h"), null, {ffi:function(){return 20}})(3), 22);
assertEq(asmLink(asmCompile(USE_ASM + "function f(i) {i=i|0; return +((i+1)|0)} function g(d) { d=+d; return +(d+2.5) } function h(i,j) { i=i|0;j=j|0; return +tbl2[i&1](+tbl1[i&1](j)) } var tbl1=[f,f]; var tbl2=[g,g]; return h"))(0,10), 11+2.5);
assertEq(asmLink(asmCompile(USE_ASM + "function f(i) {i=i|0; return +((i+1)|0)} function g(d) { d=+d; return +(d+2.5) } function h(i,j) { i=i|0;j=j|0; return +tbl2[i&1](+tbl1[i&1](j)) } const tbl1=[f,f]; const tbl2=[g,g]; return h"))(0,10), 11+2.5);
assertAsmTypeFail(USE_ASM + "function f() {return 42} function g() { return tbl[0]()|0 } var tbl=[f]; return g");
assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() { return tbl[0&0]()|0 } var tbl=[f]; return g"))(), 42);
assertEq(asmLink(asmCompile(USE_ASM + "function f() {return 42} function g() { return tbl[0&0]()|0 } const tbl=[f]; return g"))(), 42);
assertEq(asmLink(asmCompile(USE_ASM + "function f1() {return 42} function f2() {return 13} function g() { return tbl[1&1]()|0 } var tbl=[f1,f2]; return g"))(), 13);
assertEq(asmLink(asmCompile(USE_ASM + "function f1() {return 42} function f2() {return 13} function g() { return tbl[1&1]()|0 } const tbl=[f1,f2]; return g"))(), 13);
// Test some literal constant paths.
assertAsmTypeFail(USE_ASM + "function f() {return 42} function g() { return tbl[0&4294967295]()|0 } var tbl=[f]; return g");
assertAsmTypeFail(USE_ASM + "const i=4294967295; function f() {return 42} function g() { return tbl[0&i]()|0 } var tbl=[f]; return g");
assertAsmTypeFail(USE_ASM + "function f() {return 42} function g() { return tbl[0&-1]()|0 } var tbl=[f]; return g");
assertAsmTypeFail(USE_ASM + "const i=-1; function f() {return 42} function g() { return tbl[0&i]()|0 } var tbl=[f]; return g");
assertAsmTypeFail(USE_ASM + "function f() {return 42} function g() { return tbl[0&0x80000000]()|0 } var tbl=[f]; return g");
assertAsmTypeFail(USE_ASM + "const i=0x80000000; function f() {return 42} function g() { return tbl[0&i]()|0 } var tbl=[f]; return g");
assertAsmTypeFail(USE_ASM + "function f() {return 42} function g() { return tbl[0&-2147483648]()|0 } var tbl=[f]; return g");
assertAsmTypeFail(USE_ASM + "const i=-2147483648; function f() {return 42} function g() { return tbl[0&i]()|0 } var tbl=[f]; return g");
assertAsmTypeFail(USE_ASM + "const i=0; function f() {return 42} function g() { return tbl[0&i]()|0 } var tbl=[f]; return g");
// Limited by the inability to test really large tables.
assertAsmTypeFail(USE_ASM + "function f() {return 42} function g() { return tbl[0&0x7fffffff]()|0 } var tbl=[f]; return g");
var f = asmLink(asmCompile(USE_ASM + "function f1(d) {d=+d; return +(d/2.0)} function f2(d) {d=+d; return +(d+10.0)} function g(i,j) { i=i|0;j=+j; return +tbl[i&1](+tbl[(i+1)&1](j)) } var tbl=[f1,f2]; return g"));
assertEq(f(0,10.2), (10.2+10)/2);

View File

@ -1,15 +1,29 @@
load(libdir + "asm.js");
assertAsmTypeFail(USE_ASM + "var i; function f(){} return f");
assertAsmTypeFail(USE_ASM + "const i; function f(){} return f");
assertEq(asmLink(asmCompile(USE_ASM + "var i=0; function f(){} return f"))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + "const i=0; function f(){} return f"))(), undefined);
assertEq(asmLink(asmCompile(USE_ASM + "var i=42; function f(){ return i|0 } return f"))(), 42);
assertEq(asmLink(asmCompile(USE_ASM + "const i=42; function f(){ return i|0 } return f"))(), 42);
assertEq(asmLink(asmCompile(USE_ASM + "var i=4.2; function f(){ return +i } return f"))(), 4.2);
assertEq(asmLink(asmCompile(USE_ASM + "const i=4.2; function f(){ return +i } return f"))(), 4.2);
assertAsmTypeFail(USE_ASM + "var i=42; function f(){ return +(i+.1) } return f");
assertAsmTypeFail(USE_ASM + "const i=42; function f(){ return +(i+.1) } return f");
assertAsmTypeFail(USE_ASM + "var i=1.2; function f(){ return i|0 } return f");
assertAsmTypeFail(USE_ASM + "const i=1.2; function f(){ return i|0 } return f");
assertAsmTypeFail(USE_ASM + "var i=0; function f(e){ e=+e; i=e } return f");
assertAsmTypeFail(USE_ASM + "const i=0; function f(e){ e=+e; i=e } return f");
assertAsmTypeFail(USE_ASM + "var d=0.1; function f(i){ i=i|0; d=i } return f");
assertAsmTypeFail(USE_ASM + "const d=0.1; function f(i){ i=i|0; d=i } return f");
assertEq(asmLink(asmCompile(USE_ASM + "var i=13; function f(j) { j=j|0; i=j; return i|0 } return f"))(42), 42);
assertAsmTypeFail(USE_ASM + "const i=13; function f(j) { j=j|0; i=j; return i|0 } return f");
assertAsmTypeFail(USE_ASM + "const c=0,i=13; function f(j) { j=j|0; i=j; return i|0 } return f");
assertEq(asmLink(asmCompile(USE_ASM + "var d=.1; function f(e) { e=+e; d=e; return +e } return f"))(42.1), 42.1);
assertAsmTypeFail(USE_ASM + "const d=.1; function f(e) { e=+e; d=e; return +e } return f");
assertAsmTypeFail(USE_ASM + "const c=0, d=.1; function f(e) { e=+e; d=e; return +e } return f");
assertEq(asmLink(asmCompile(USE_ASM + "var i=13; function f(i, j) { i=i|0; j=j|0; i=j; return i|0 } return f"))(42,43), 43);
assertEq(asmLink(asmCompile(USE_ASM + "var i=13; function f(j) { j=j|0; var i=0; i=j; return i|0 } return f"))(42), 42);
var f = asmLink(asmCompile(USE_ASM + "var i=13; function f(j) { j=j|0; if ((j|0) != -1) { i=j } else { return i|0 } return 0 } return f"));
assertEq(f(-1), 13);
@ -17,19 +31,27 @@ assertEq(f(42), 0);
assertEq(f(-1), 42);
assertAsmTypeFail('global', USE_ASM + "var i=global; function f() { return i|0 } return f");
assertAsmTypeFail('global', USE_ASM + "const i=global; function f() { return i|0 } return f");
assertAsmTypeFail('global', USE_ASM + "var i=global|0; function f() { return i|0 } return f");
assertAsmTypeFail('global', USE_ASM + "const i=global|0; function f() { return i|0 } return f");
assertAsmTypeFail('global', USE_ASM + "var j=0;var i=j.i|0; function f() { return i|0 } return f");
assertAsmTypeFail('global', USE_ASM + "var i=global.i|0; function f() { return i|0 } return f");
assertAsmTypeFail('global', USE_ASM + "const i=global.i|0; function f() { return i|0 } return f");
assertAsmTypeFail('global', USE_ASM + "var i=global.i|0; function f() { return i|0 } return f");
assertAsmTypeFail('global', USE_ASM + 'var i=global.Infinity; function f() { i = 0.0 } return f');
assertAsmTypeFail('global', USE_ASM + 'const i=global.Infinity; function f() { i = 0.0 } return f');
assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), undefined);
assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'const i=global.Infinity; function f() { return +i } return f'), undefined);
assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), null);
assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'const i=global.Infinity; function f() { return +i } return f'), null);
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), 3);
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {});
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {Infinity:NaN});
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {Infinity:-Infinity});
assertEq(asmLink(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {Infinity:Infinity})(), Infinity);
assertEq(asmLink(asmCompile('global', USE_ASM + 'const i=global.Infinity; function f() { return +i } return f'), {Infinity:Infinity})(), Infinity);
assertEq(asmLink(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), this)(), Infinity);
assertEq(asmLink(asmCompile('global', USE_ASM + 'const i=global.Infinity; function f() { return +i } return f'), this)(), Infinity);
assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), undefined);
assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), null);
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), 3);
@ -37,10 +59,14 @@ assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f()
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), {Infinity:Infinity});
assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), {Infinity:-Infinity});
assertEq(asmLink(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), {NaN:NaN})(), NaN);
assertEq(asmLink(asmCompile('global', USE_ASM + 'const i=global.NaN; function f() { return +i } return f'), {NaN:NaN})(), NaN);
assertEq(asmLink(asmCompile('global', USE_ASM + 'var i=global.NaN; function f() { return +i } return f'), this)(), NaN);
assertEq(asmLink(asmCompile('global', USE_ASM + 'const i=global.NaN; function f() { return +i } return f'), this)(), NaN);
assertAsmLinkFail(asmCompile('glob','foreign','buf', USE_ASM + 'var t = new glob.Int32Array(buf); function f() {} return f'), {get Int32Array(){return Int32Array}}, null, new ArrayBuffer(4096))
assertAsmLinkFail(asmCompile('glob','foreign','buf', USE_ASM + 'const t = new glob.Int32Array(buf); function f() {} return f'), {get Int32Array(){return Int32Array}}, null, new ArrayBuffer(4096))
assertAsmLinkFail(asmCompile('glob','foreign','buf', USE_ASM + 'var t = new glob.Int32Array(buf); function f() {} return f'), new Proxy({}, {get:function() {return Int32Array}}), null, new ArrayBuffer(4096))
assertAsmLinkFail(asmCompile('glob','foreign','buf', USE_ASM + 'const t = new glob.Int32Array(buf); function f() {} return f'), new Proxy({}, {get:function() {return Int32Array}}), null, new ArrayBuffer(4096))
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var t = glob.Math.sin; function f() {} return f'), {get Math(){return Math}});
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var t = glob.Math.sin; function f() {} return f'), new Proxy({}, {get:function(){return Math}}));
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var t = glob.Math.sin; function f() {} return f'), {Math:{get sin(){return Math.sin}}});
@ -61,9 +87,13 @@ assertAsmLinkAlwaysFail(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; fu
assertAsmLinkAlwaysFail(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f"), this, null);
assertAsmLinkFail(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f"), this, 42);
assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f")(null, {i:42})), 42);
assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "const i=imp.i|0; function f() { return i|0 } return f")(null, {i:42})), 42);
assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f")(null, {i:1.4})), 1);
assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "const i=imp.i|0; function f() { return i|0 } return f")(null, {i:1.4})), 1);
assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=+imp.i; function f() { return +i } return f")(null, {i:42})), 42);
assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "const i=+imp.i; function f() { return +i } return f")(null, {i:42})), 42);
assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=+imp.i; function f() { return +i } return f")(this, {i:1.4})), 1.4);
assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "const i=+imp.i; function f() { return +i } return f")(this, {i:1.4})), 1.4);
assertEq(asmLink(asmCompile(USE_ASM + "var g=0; function f() { var i=42; while (1) { break; } g = i; return g|0 } return f"))(), 42);
var f1 = asmCompile('global', 'foreign', 'heap', USE_ASM + 'var i32 = new global.Int32Array(heap); function g() { return i32[4]|0 } return g');

View File

@ -453,3 +453,81 @@ assertEq(new Int32Array(buf)[0], 1);
// Bug 882012
assertEq(asmLink(asmCompile('stdlib', 'foreign', 'heap', USE_ASM + "var id=foreign.id;var doubles=new stdlib.Float64Array(heap);function g(){doubles[0]=+id(2.0);return +doubles[0];}return g"), this, {id: function(x){return x;}}, BUF_64KB)(), 2.0);
// Some literal constant paths.
var buf = new ArrayBuffer(8192);
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[0>>4294967295]|0; } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[0>>-1]|0; } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[0>>0x80000000]|0; } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[0>>-2147483648]|0; } return f');
new Uint32Array(buf)[0] = 0xAA;
new Uint32Array(buf)[0x5A>>2] = 0xA5;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(0x5A&4294967295)>>2]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=4294967295; function f() { return u32[(0x5A&i)>>2]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(0x5A&-1)>>2]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-1; function f() { return u32[(0x5A&i)>>2]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(0x5A&0x80000000)>>2]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=0x80000000; function f() { return u32[(0x5A&i)>>2]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(0x5A&-2147483648)>>2]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-2147483648; function f() { return u32[(0x5A&i)>>2]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(4294967295&0x5A)>>2]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=4294967295; function f() { return u32[(i&0x5A)>>2]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(-1&0x5A)>>2]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-1; function f() { return u32[(i&0x5A)>>2]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(0x80000000&0x5A)>>2]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=0x80000000; function f() { return u32[(i&0x5A)>>2]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[(-2147483648&0x5A)>>2]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-2147483648; function f() { return u32[(-2147483648&0x5A)>>2]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[4294967295>>2]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=4294967295; function f() { return u32[i>>2]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[-1>>2]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-1; function f() { return u32[i>>2]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[0x80000000>>2]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=0x80000000; function f() { return u32[i>>2]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u32[-2147483648>>2]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-2147483648; function f() { return u32[-2147483648>>2]|0; } return f'), this, null, buf)(),0);
var buf = new ArrayBuffer(8192);
new Uint8Array(buf)[0] = 0xAA;
new Uint8Array(buf)[0x5A] = 0xA5;
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[0x5A&4294967295]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=4294967295; function f() { return u8[0x5A&i]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[0x5A&-1]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-1; function f() { return u8[0x5A&i]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[0x5A&0x80000000]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=0x80000000; function f() { return u8[0x5A&i]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[0x5A&-2147483648]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-2147483648; function f() { return u8[0x5A&i]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[4294967295&0x5A]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=4294967295; function f() { return u8[i&0x5A]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[-1&0x5A]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-1; function f() { return u8[i&0x5A]|0; } return f'), this, null, buf)(),0xA5);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[0x80000000&0x5A]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=0x80000000; function f() { return u8[i&0x5A]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[-2147483648&0x5A]|0; } return f'), this, null, buf)(),0xAA);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-2147483648; function f() { return u8[i&0x5A]|0; } return f'), this, null, buf)(),0xAA);
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[4294967295]|0; } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=4294967295; function f() { return u8[i]|0; } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[-1]|0; } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-1; function f() { return u8[i]|0; } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[0x80000000]|0; } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=0x80000000; function f() { return u8[i]|0; } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[-2147483648]|0; } return f');
assertAsmTypeFail('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-2147483648; function f() { return u8[i]|0; } return f');
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[4294967295>>0]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=4294967295; function f() { return u8[i>>0]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[-1>>0]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-1; function f() { return u8[i>>0]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[0x80000000>>0]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=0x80000000; function f() { return u8[i>>0]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f() { return u8[-2147483648>>0]|0; } return f'), this, null, buf)(),0);
assertEq(asmLink(asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'const i=-2147483648; function f() { return u8[i>>0]|0; } return f'), this, null, buf)(),0);

View File

@ -41,6 +41,10 @@ assertEq(asmLink(asmCompile(USE_ASM + 'function f(i) { i=i|0; return (i+-2147483
assertEq(asmLink(asmCompile(USE_ASM + 'function f(i) { i=i|0; return (i+4294967295)|0 } return f'))(0), 4294967295|0);
assertAsmTypeFail(USE_ASM + 'var i=-2147483649; function f() { return i|0 } return f');
assertAsmTypeFail(USE_ASM + 'const i=-2147483649; function f() { return i|0 } return f');
assertAsmTypeFail(USE_ASM + 'var i=4294967296; function f() { return i|0 } return f');
assertAsmTypeFail(USE_ASM + 'const i=4294967296; function f() { return i|0 } return f');
assertEq(asmLink(asmCompile(USE_ASM + 'var i=-2147483648; function f() { return i|0 } return f'))(), -2147483648);
assertEq(asmLink(asmCompile(USE_ASM + 'const i=-2147483648; function f() { return i|0 } return f'))(), -2147483648);
assertEq(asmLink(asmCompile(USE_ASM + 'var i=4294967295; function f() { return i|0 } return f'))(), 4294967295|0);
assertEq(asmLink(asmCompile(USE_ASM + 'const i=4294967295; function f() { return i|0 } return f'))(), 4294967295|0);

View File

@ -9,6 +9,7 @@ function testUnary(f, g) {
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sin; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sin:Math.sqrt}});
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sin; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sin:null}});
testUnary(asmLink(asmCompile('glob', USE_ASM + 'var sq=glob.Math.sin; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sin:Math.sin}}), Math.sin);
testUnary(asmLink(asmCompile('glob', USE_ASM + 'const sq=glob.Math.sin; function f(d) { d=+d; return +sq(d) } return f'), {Math:{sin:Math.sin}}), Math.sin);
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var co=glob.Math.cos; function f(d) { d=+d; return +co(d) } return f'), {Math:{cos:Math.sqrt}});
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var co=glob.Math.cos; function f(d) { d=+d; return +co(d) } return f'), {Math:{cos:null}});

View File

@ -0,0 +1,4 @@
(function() {
"use strict";
assertEq(eval("this"), undefined);
})();

View File

@ -0,0 +1,3 @@
var f = (function () {}).bind({});
var p = new Proxy(f, {});
Object.defineProperty(p, "caller", {get: function(){}});

View File

@ -146,7 +146,7 @@ CallArgList(ParseNode *pn)
static inline ParseNode *
VarListHead(ParseNode *pn)
{
JS_ASSERT(pn->isKind(PNK_VAR));
JS_ASSERT(pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST));
return ListHead(pn);
}
@ -370,9 +370,10 @@ PeekToken(AsmJSParser &parser)
}
static bool
ParseVarStatement(AsmJSParser &parser, ParseNode **var)
ParseVarOrConstStatement(AsmJSParser &parser, ParseNode **var)
{
if (PeekToken(parser) != TOK_VAR) {
TokenKind tk = PeekToken(parser);
if (tk != TOK_VAR && tk != TOK_CONST) {
*var = NULL;
return true;
}
@ -381,7 +382,7 @@ ParseVarStatement(AsmJSParser &parser, ParseNode **var)
if (!*var)
return false;
JS_ASSERT((*var)->isKind(PNK_VAR));
JS_ASSERT((*var)->isKind(PNK_VAR) || (*var)->isKind(PNK_CONST));
return true;
}
@ -823,7 +824,7 @@ ExtractNumericLiteral(ParseNode *pn)
}
static inline bool
IsLiteralUint32(ParseNode *pn, uint32_t *u32)
IsLiteralInt(ParseNode *pn, uint32_t *u32)
{
if (!IsNumericLiteral(pn))
return false;
@ -832,9 +833,9 @@ IsLiteralUint32(ParseNode *pn, uint32_t *u32)
switch (literal.which()) {
case NumLit::Fixnum:
case NumLit::BigUnsigned:
case NumLit::NegativeInt:
*u32 = uint32_t(literal.toInt32());
return true;
case NumLit::NegativeInt:
case NumLit::Double:
case NumLit::OutOfRangeInt:
return false;
@ -1019,6 +1020,9 @@ class MOZ_STACK_CLASS ModuleCompiler
struct {
uint32_t index_;
VarType::Which type_;
bool isConst_;
bool isLitConst_;
Value litConstValue_;
} var;
uint32_t funcIndex_;
uint32_t funcPtrTableIndex_;
@ -1045,6 +1049,19 @@ class MOZ_STACK_CLASS ModuleCompiler
JS_ASSERT(which_ == Variable);
return u.var.index_;
}
bool varIsConstant() const {
JS_ASSERT(which_ == Variable);
return u.var.isConst_;
}
bool varIsLitConstant() const {
JS_ASSERT(which_ == Variable);
return u.var.isLitConst_;
}
const Value &litConstValue() const {
JS_ASSERT(which_ == Variable);
JS_ASSERT(u.var.isLitConst_);
return u.var.litConstValue_;
}
uint32_t funcIndex() const {
JS_ASSERT(which_ == Function);
return u.funcIndex_;
@ -1361,7 +1378,8 @@ class MOZ_STACK_CLASS ModuleCompiler
void initImportArgumentName(PropertyName *n) { module_->initImportArgumentName(n); }
void initBufferArgumentName(PropertyName *n) { module_->initBufferArgumentName(n); }
bool addGlobalVarInitConstant(PropertyName *varName, VarType type, const Value &v) {
bool addGlobalVarInitConstant(PropertyName *varName, VarType type, const Value &v,
bool isConst) {
uint32_t index;
if (!module_->addGlobalVarInitConstant(v, &index))
return false;
@ -1370,9 +1388,14 @@ class MOZ_STACK_CLASS ModuleCompiler
return false;
global->u.var.index_ = index;
global->u.var.type_ = type.which();
global->u.var.isConst_ = isConst;
global->u.var.isLitConst_ = isConst;
if (isConst)
global->u.var.litConstValue_ = v;
return globals_.putNew(varName, global);
}
bool addGlobalVarImport(PropertyName *varName, PropertyName *fieldName, AsmJSCoercion coercion) {
bool addGlobalVarImport(PropertyName *varName, PropertyName *fieldName, AsmJSCoercion coercion,
bool isConst) {
uint32_t index;
if (!module_->addGlobalVarImport(fieldName, coercion, &index))
return false;
@ -1381,6 +1404,8 @@ class MOZ_STACK_CLASS ModuleCompiler
return false;
global->u.var.index_ = index;
global->u.var.type_ = VarType(coercion).which();
global->u.var.isConst_ = isConst;
global->u.var.isLitConst_ = false;
return globals_.putNew(varName, global);
}
bool addFunction(PropertyName *name, MoveRef<Signature> sig, Func **func) {
@ -1990,9 +2015,16 @@ class FunctionCompiler
{
if (!curBlock_)
return NULL;
if (global.varIsLitConstant()) {
JS_ASSERT(global.litConstValue().isNumber());
MConstant *constant = MConstant::New(global.litConstValue());
curBlock_->add(constant);
return constant;
}
MIRType type = global.varType().toMIRType();
unsigned globalDataOffset = module().globalVarIndexToGlobalDataOffset(global.varIndex());
MAsmJSLoadGlobalVar *load = MAsmJSLoadGlobalVar::New(type, globalDataOffset);
MAsmJSLoadGlobalVar *load = MAsmJSLoadGlobalVar::New(type, globalDataOffset,
global.varIsConstant());
curBlock_->add(load);
return load;
}
@ -2692,7 +2724,8 @@ CheckPrecedingStatements(ModuleCompiler &m, ParseNode *stmtList)
}
static bool
CheckGlobalVariableInitConstant(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode)
CheckGlobalVariableInitConstant(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode,
bool isConst)
{
NumLit literal = ExtractNumericLiteral(initNode);
VarType type;
@ -2708,7 +2741,7 @@ CheckGlobalVariableInitConstant(ModuleCompiler &m, PropertyName *varName, ParseN
case NumLit::OutOfRangeInt:
return m.fail(initNode, "global initializer is out of representable integer range");
}
return m.addGlobalVarInitConstant(varName, type, literal.value());
return m.addGlobalVarInitConstant(varName, type, literal.value(), isConst);
}
static bool
@ -2744,7 +2777,8 @@ CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *c
}
static bool
CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode)
CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNode,
bool isConst)
{
AsmJSCoercion coercion;
ParseNode *coercedExpr;
@ -2763,7 +2797,7 @@ CheckGlobalVariableInitImport(ModuleCompiler &m, PropertyName *varName, ParseNod
if (!IsUseOfName(base, importName))
return m.failName(coercedExpr, "base of import expression must be '%s'", importName);
return m.addGlobalVarImport(varName, field, coercion);
return m.addGlobalVarImport(varName, field, coercion, isConst);
}
static bool
@ -2850,7 +2884,7 @@ CheckGlobalDotImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNo
}
static bool
CheckModuleGlobal(ModuleCompiler &m, ParseNode *var)
CheckModuleGlobal(ModuleCompiler &m, ParseNode *var, bool isConst)
{
if (!IsDefinition(var))
return m.fail(var, "import variable names must be unique");
@ -2863,10 +2897,10 @@ CheckModuleGlobal(ModuleCompiler &m, ParseNode *var)
return m.fail(var, "module import needs initializer");
if (IsNumericLiteral(initNode))
return CheckGlobalVariableInitConstant(m, var->name(), initNode);
return CheckGlobalVariableInitConstant(m, var->name(), initNode, isConst);
if (initNode->isKind(PNK_BITOR) || initNode->isKind(PNK_POS))
return CheckGlobalVariableInitImport(m, var->name(), initNode);
return CheckGlobalVariableInitImport(m, var->name(), initNode, isConst);
if (initNode->isKind(PNK_NEW))
return CheckNewArrayView(m, var->name(), initNode);
@ -2882,12 +2916,12 @@ CheckModuleGlobals(ModuleCompiler &m)
{
while (true) {
ParseNode *varStmt;
if (!ParseVarStatement(m.parser(), &varStmt))
if (!ParseVarOrConstStatement(m.parser(), &varStmt))
return false;
if (!varStmt)
break;
for (ParseNode *var = VarListHead(varStmt); var; var = NextNode(var)) {
if (!CheckModuleGlobal(m, var))
if (!CheckModuleGlobal(m, var, varStmt->isKind(PNK_CONST)))
return false;
}
}
@ -3106,6 +3140,30 @@ CheckVarRef(FunctionCompiler &f, ParseNode *varRef, MDefinition **def, Type *typ
return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
}
static inline bool
IsLiteralOrConstInt(FunctionCompiler &f, ParseNode *pn, uint32_t *u32)
{
if (IsLiteralInt(pn, u32))
return true;
if (pn->getKind() != PNK_NAME)
return false;
PropertyName *name = pn->name();
const ModuleCompiler::Global *global = f.lookupGlobal(name);
if (!global || global->which() != ModuleCompiler::Global::Variable ||
!global->varIsLitConstant()) {
return false;
}
const Value &v = global->litConstValue();
if (!v.isInt32())
return false;
*u32 = (uint32_t) v.toInt32();
return true;
}
static bool
FoldMaskedArrayIndex(FunctionCompiler &f, ParseNode **indexExpr, int32_t *mask,
NeedsBoundsCheck *needsBoundsCheck)
@ -3114,7 +3172,7 @@ FoldMaskedArrayIndex(FunctionCompiler &f, ParseNode **indexExpr, int32_t *mask,
ParseNode *maskNode = BinaryRight(*indexExpr);
uint32_t mask2;
if (IsLiteralUint32(maskNode, &mask2)) {
if (IsLiteralOrConstInt(f, maskNode, &mask2)) {
// Flag the access to skip the bounds check if the mask ensures that an 'out of
// bounds' access can not occur based on the current heap length constraint.
if (mask2 == 0 ||
@ -3147,7 +3205,7 @@ CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType
*viewType = global->viewType();
uint32_t pointer;
if (IsLiteralUint32(indexExpr, &pointer)) {
if (IsLiteralOrConstInt(f, indexExpr, &pointer)) {
if (pointer > (uint32_t(INT32_MAX) >> TypedArrayShift(*viewType)))
return f.fail(indexExpr, "constant index out of range");
pointer <<= TypedArrayShift(*viewType);
@ -3171,7 +3229,7 @@ CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType
ParseNode *pointerNode = BinaryLeft(indexExpr);
uint32_t shift;
if (!IsLiteralUint32(shiftNode, &shift))
if (!IsLiteralInt(shiftNode, &shift))
return f.failf(shiftNode, "shift amount must be constant");
unsigned requiredShift = TypedArrayShift(*viewType);
@ -3184,7 +3242,7 @@ CheckArrayAccess(FunctionCompiler &f, ParseNode *elem, ArrayBufferView::ViewType
// Fold a 'literal constant right shifted' now, and skip the bounds check if
// currently possible. This handles the optimization of many of these uses without
// the need for range analysis, and saves the generation of a MBitAnd op.
if (IsLiteralUint32(pointerNode, &pointer) && pointer <= uint32_t(INT32_MAX)) {
if (IsLiteralOrConstInt(f, pointerNode, &pointer) && pointer <= uint32_t(INT32_MAX)) {
// Cases: b[c>>n], and b[(c&m)>>n]
pointer &= mask;
if (pointer < f.m().minHeapLength())
@ -3297,6 +3355,8 @@ CheckAssignName(FunctionCompiler &f, ParseNode *lhs, ParseNode *rhs, MDefinition
} else if (const ModuleCompiler::Global *global = f.lookupGlobal(name)) {
if (global->which() != ModuleCompiler::Global::Variable)
return f.failName(lhs, "'%s' is not a mutable variable", name);
if (global->varIsConstant())
return f.failName(lhs, "'%s' is a constant variable and not mutable", name);
if (!(rhsType <= global->varType())) {
return f.failf(lhs, "%s is not a subtype of %s",
rhsType.toChars(), global->varType().toType().toChars());
@ -3560,7 +3620,7 @@ CheckFuncPtrCall(FunctionCompiler &f, ParseNode *callNode, RetType retType, MDef
ParseNode *maskNode = BinaryRight(indexExpr);
uint32_t mask;
if (!IsLiteralUint32(maskNode, &mask) || mask == UINT32_MAX || !IsPowerOfTwo(mask + 1))
if (!IsLiteralInt(maskNode, &mask) || mask == UINT32_MAX || !IsPowerOfTwo(mask + 1))
return f.fail(maskNode, "function-pointer table index mask value must be a power of two");
MDefinition *indexDef;
@ -5187,7 +5247,7 @@ CheckFuncPtrTables(ModuleCompiler &m)
{
while (true) {
ParseNode *varStmt;
if (!ParseVarStatement(m.parser(), &varStmt))
if (!ParseVarOrConstStatement(m.parser(), &varStmt))
return false;
if (!varStmt)
break;

View File

@ -69,6 +69,9 @@ BaselineCompiler::compile()
IonSpew(IonSpew_BaselineScripts, "Baseline compiling script %s:%d (%p)",
script->filename(), script->lineno, script.get());
IonSpew(IonSpew_Codegen, "# Emitting baseline code for script %s:%d",
script->filename(), script->lineno);
if (cx->typeInferenceEnabled() && !script->ensureHasBytecodeTypeMap(cx))
return Method_Error;
@ -968,8 +971,8 @@ BaselineCompiler::emit_JSOP_THIS()
// Keep this value in R0
frame.pushThis();
// In strict mode function or self-hosted function, |this| is left alone.
if (function() && (function()->strict() || function()->isSelfHostedBuiltin()))
// In strict mode code or self-hosted functions, |this| is left alone.
if (script->strict || (function() && function()->isSelfHostedBuiltin()))
return true;
Label skipIC;

View File

@ -5512,6 +5512,8 @@ CodeGenerator::visitRestPar(LRestPar *lir)
bool
CodeGenerator::generateAsmJS()
{
IonSpew(IonSpew_Codegen, "# Emitting asm.js code");
// The caller (either another asm.js function or the external-entry
// trampoline) has placed all arguments in registers and on the stack
// according to the system ABI. The MAsmJSParameters which represent these
@ -5552,6 +5554,10 @@ CodeGenerator::generateAsmJS()
bool
CodeGenerator::generate()
{
IonSpew(IonSpew_Codegen, "# Emitting code for script %s:%d",
gen->info().script()->filename(),
gen->info().script()->lineno);
if (!safepoints_.init(graph.totalSlotCount()))
return false;

View File

@ -227,15 +227,19 @@ IonRuntime::initialize(JSContext *cx)
if (!functionWrappers_ || !functionWrappers_->init())
return false;
IonSpew(IonSpew_Codegen, "# Emitting exception tail stub");
exceptionTail_ = generateExceptionTailStub(cx);
if (!exceptionTail_)
return false;
IonSpew(IonSpew_Codegen, "# Emitting bailout tail stub");
bailoutTail_ = generateBailoutTailStub(cx);
if (!bailoutTail_)
return false;
if (cx->runtime()->jitSupportsFloatingPoint) {
IonSpew(IonSpew_Codegen, "# Emitting bailout tables");
// Initialize some Ion-only stubs that require floating-point support.
if (!bailoutTables_.reserve(FrameSizeClass::ClassLimit().classId()))
return false;
@ -250,41 +254,50 @@ IonRuntime::initialize(JSContext *cx)
return false;
}
IonSpew(IonSpew_Codegen, "# Emitting bailout handler");
bailoutHandler_ = generateBailoutHandler(cx);
if (!bailoutHandler_)
return false;
IonSpew(IonSpew_Codegen, "# Emitting invalidator");
invalidator_ = generateInvalidator(cx);
if (!invalidator_)
return false;
}
IonSpew(IonSpew_Codegen, "# Emitting sequential arguments rectifier");
argumentsRectifier_ = generateArgumentsRectifier(cx, SequentialExecution, &argumentsRectifierReturnAddr_);
if (!argumentsRectifier_)
return false;
#ifdef JS_THREADSAFE
IonSpew(IonSpew_Codegen, "# Emitting parallel arguments rectifier");
parallelArgumentsRectifier_ = generateArgumentsRectifier(cx, ParallelExecution, NULL);
if (!parallelArgumentsRectifier_)
return false;
#endif
IonSpew(IonSpew_Codegen, "# Emitting EnterJIT sequence");
enterJIT_ = generateEnterJIT(cx, EnterJitOptimized);
if (!enterJIT_)
return false;
IonSpew(IonSpew_Codegen, "# Emitting EnterBaselineJIT sequence");
enterBaselineJIT_ = generateEnterJIT(cx, EnterJitBaseline);
if (!enterBaselineJIT_)
return false;
IonSpew(IonSpew_Codegen, "# Emitting Pre Barrier for Value");
valuePreBarrier_ = generatePreBarrier(cx, MIRType_Value);
if (!valuePreBarrier_)
return false;
IonSpew(IonSpew_Codegen, "# Emitting Pre Barrier for Shape");
shapePreBarrier_ = generatePreBarrier(cx, MIRType_Shape);
if (!shapePreBarrier_)
return false;
IonSpew(IonSpew_Codegen, "# Emitting VM function wrappers");
for (VMFunction *fun = VMFunction::functions; fun; fun = fun->next) {
if (!generateVMWrapper(cx, *fun))
return false;

View File

@ -3748,8 +3748,6 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target)
MTypeBarrier *barrier = MTypeBarrier::New(callInfo.thisArg(), cloneTypeSet(types), Bailout_Normal);
current->add(barrier);
callInfo.setThis(barrier);
// object or missing
JS_ASSERT(barrier->type() == MIRType_Object || barrier->type() == MIRType_Value);
}
}

View File

@ -8434,23 +8434,33 @@ class MAsmJSStoreHeap : public MBinaryInstruction, public MAsmJSHeapAccess
class MAsmJSLoadGlobalVar : public MNullaryInstruction
{
MAsmJSLoadGlobalVar(MIRType type, unsigned globalDataOffset)
: globalDataOffset_(globalDataOffset)
MAsmJSLoadGlobalVar(MIRType type, unsigned globalDataOffset, bool isConstant)
: globalDataOffset_(globalDataOffset), isConstant_(isConstant)
{
JS_ASSERT(type == MIRType_Int32 || type == MIRType_Double);
setResultType(type);
if (isConstant)
setMovable();
}
unsigned globalDataOffset_;
bool isConstant_;
public:
INSTRUCTION_HEADER(AsmJSLoadGlobalVar);
static MAsmJSLoadGlobalVar *New(MIRType type, unsigned globalDataOffset) {
return new MAsmJSLoadGlobalVar(type, globalDataOffset);
static MAsmJSLoadGlobalVar *New(MIRType type, unsigned globalDataOffset, bool isConstant) {
return new MAsmJSLoadGlobalVar(type, globalDataOffset, isConstant);
}
unsigned globalDataOffset() const { return globalDataOffset_; }
AliasSet getAliasSet() const {
if (isConstant_)
return AliasSet::None();
else
return AliasSet::Store(AliasSet::Any);
}
};
class MAsmJSStoreGlobalVar : public MUnaryInstruction

View File

@ -307,7 +307,7 @@ js::fun_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
PropertyOp getter;
StrictPropertyOp setter;
unsigned attrs = JSPROP_PERMANENT;
unsigned attrs = JSPROP_PERMANENT | JSPROP_SHARED;
if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
return false;
if (fun->isInterpreted() ? fun->strict() : fun->isBoundFunction()) {

View File

@ -569,9 +569,6 @@ class JSScript : public js::gc::Cell
bool funHasExtensibleScope:1; /* see FunctionContextFlags */
bool funNeedsDeclEnvObject:1; /* see FunctionContextFlags */
bool funHasAnyAliasedFormal:1; /* true if any formalIsAliased(i) */
bool warnedAboutTwoArgumentEval:1; /* have warned about use of
obsolete eval(s, o) in
this script */
bool warnedAboutUndefinedProp:1; /* have warned about uses of
undefined properties in this
script */

View File

@ -262,20 +262,22 @@ var cosh_data = [
[1875817529344, 28.953212876533797]
];
var sloppy_tolerance = 1000; // FIXME
for (var [x, y] of cosh_data)
assertNear(Math.acosh(x), y);
assertNear(Math.acosh(x), y, sloppy_tolerance);
assertNear(Math.acosh(1e300), 691.4686750787737, sloppy_tolerance);
assertNear(Math.acosh(1.0000000001), 0.000014142136208675862, sloppy_tolerance);
for (var i = 0; i <= 100; i++) {
var x = (i - 50) / 5;
var y = Math.cosh(x);
var z = Math.acosh(y);
assertNear(z, Math.abs(x));
assertNear(z, Math.abs(x), sloppy_tolerance);
}
for (var i = 1; i < 20; i++)
assertNear(Math.acosh(Math.cosh(i)), i);
assertNear(Math.acosh(1e300), 691.4686750787737);
assertNear(Math.acosh(1.0000000001), 0.000014142136208675862);
assertNear(Math.acosh(Math.cosh(i)), i, sloppy_tolerance);
reportCompare(0, 0, "ok");

View File

@ -282,24 +282,24 @@ var sinh_data = [
[1581915832320, 28.78280496108106]
];
var sloppy_tolerance = 1000; // FIXME
for (var [x, y] of sinh_data)
assertNear(Math.asinh(x), y);
assertNear(Math.asinh(x), y, sloppy_tolerance);
assertNear(Math.asinh(1e300), 691.4686750787737, sloppy_tolerance);
assertNear(Math.asinh(1e-300), 1e-300, sloppy_tolerance);
assertNear(Math.asinh(1e-5), 0.000009999999999833334, sloppy_tolerance);
assertNear(Math.asinh(0.3), 0.29567304756342244, sloppy_tolerance);
assertNear(Math.asinh(1), 0.881373587019543, sloppy_tolerance);
for (var i = 0; i <= 80; i++) {
var x = (i - 40) / 4;
var y = Math.sinh(x);
var z = Math.asinh(y);
assertNear(z, x);
assertNear(Math.asinh(Math.sinh(x)), x, sloppy_tolerance);
}
for (var i = -20; i < 20; i++)
assertNear(Math.asinh(Math.sinh(i)), i);
assertNear(Math.asinh(1e300), 691.4686750787737);
assertNear(Math.asinh(1e-300), 1e-300);
assertNear(Math.asinh(1e-5), 0.000009999999999833334);
assertNear(Math.asinh(0.3), 0.29567304756342244);
assertNear(Math.asinh(1), 0.881373587019543);
assertNear(Math.asinh(Math.sinh(i)), i, sloppy_tolerance);
reportCompare(0, 0, "ok");

View File

@ -1,6 +1,3 @@
for (var i = -1; i < 1; i += 0.05)
assertNear(Math.atanh(Math.tanh(i)), i);
var tanh_data = [
[-0.9999983310699463, -6.998237084679027],
[-0.9999978542327881, -6.87257975132917],
@ -269,10 +266,15 @@ var tanh_data = [
[1e-10, 1e-10],
];
for (var [x, y] of tanh_data)
assertNear(Math.atanh(x), y);
var sloppy_tolerance = 10; // FIXME
assertNear(Math.atanh(+3 / 5), +Math.log(2));
assertNear(Math.atanh(-3 / 5), -Math.log(2));
for (var [x, y] of tanh_data)
assertNear(Math.atanh(x), y, sloppy_tolerance);
assertNear(Math.atanh(+3 / 5), +Math.log(2), sloppy_tolerance);
assertNear(Math.atanh(-3 / 5), -Math.log(2), sloppy_tolerance);
for (var i = -1; i < 1; i += 0.05)
assertNear(Math.atanh(Math.tanh(i)), i, sloppy_tolerance);
reportCompare(0, 0, "ok");

View File

@ -1,8 +1,10 @@
assertEq(Math.cbrt(1), 1);
assertEq(Math.cbrt(-1), -1);
assertNear(Math.cbrt(1e-300), 1e-100);
assertNear(Math.cbrt(-1e-300), -1e-100);
var sloppy_tolerance = 200; // FIXME
assertNear(Math.cbrt(1e-300), 1e-100, sloppy_tolerance);
assertNear(Math.cbrt(-1e-300), -1e-100, sloppy_tolerance);
var cbrt_data = [
[ Math.E, 1.3956124250860895 ],
@ -12,7 +14,6 @@ var cbrt_data = [
];
for (var [x, y] of cbrt_data)
assertNear(Math.cbrt(x), y);
assertNear(Math.cbrt(x), y, sloppy_tolerance);
reportCompare(0, 0, "ok");

View File

@ -1,9 +1,9 @@
for (var i = -20; i < 20; i++)
assertNear(Math.cosh(i), (Math.exp(i) + Math.exp(-i)) / 2);
var sloppy_tolerance = 100;
assertNear(Math.cosh(1e5), Infinity);
assertNear(Math.cosh(1e-30), 1);
assertNear(Math.cosh(1e-10), 1);
assertEq(Math.cosh(1000), Infinity);
assertEq(Math.cosh(Number.MAX_VALUE), Infinity);
assertNear(Math.cosh(1e-30), 1, sloppy_tolerance);
assertNear(Math.cosh(1e-10), 1, sloppy_tolerance);
var cosh_data = [
[0.0016914556651292944, 1.0000014305114746],
@ -270,6 +270,9 @@ var cosh_data = [
];
for (var [x, y] of cosh_data)
assertNear(Math.cosh(x), y);
assertNear(Math.cosh(x), y, sloppy_tolerance);
for (var i = -20; i < 20; i++)
assertNear(Math.cosh(i), (Math.exp(i) + Math.exp(-i)) / 2, sloppy_tolerance);
reportCompare(0, 0, "ok");

View File

@ -1,17 +1,16 @@
// |reftest| skip
// Math.hypot is disabled pending the resolution of spec issues (bug 896264).
for (var i = -20; i < 20; i++)
for (var i = -20; i < 20; i++) {
assertEq(Math.hypot(+0, i), Math.abs(i));
for (var i = -20; i < 20; i++)
assertEq(Math.hypot(-0, i), Math.abs(i));
for (var i = 1, j = 1; i < 2; i += 0.05, j += 0.05)
assertNear(Math.hypot(i, j), Math.sqrt(i * i + j * j));
}
assertNear(Math.hypot(1e300, 1e300), 1.4142135623730952e+300);
assertNear(Math.hypot(1e-300, 1e-300), 1.414213562373095e-300);
assertNear(Math.hypot(1e3, 1e-3), 1000.0000000005);
for (var i = 1, j = 1; i < 2; i += 0.05, j += 0.05)
assertNear(Math.hypot(i, j), Math.sqrt(i * i + j * j));
reportCompare(0, 0, "ok");

View File

@ -1,9 +1,9 @@
for (var i = -10; i < 10; i++)
assertNear(Math.log10(Math.pow(10, i)), i);
assertNear(Math.log10(2), 0.3010299956639812);
assertNear(Math.log10(7), 0.8450980400142568);
assertNear(Math.log10(Math.E), Math.LOG10E);
for (var i = -10; i < 10; i++)
assertNear(Math.log10(Math.pow(10, i)), i);
reportCompare(0, 0, 'ok');

View File

@ -2,10 +2,73 @@
const ONE_PLUS_EPSILON = 1 + Math.pow(2, -52); // 0.9999999999999999
const ONE_MINUS_EPSILON = 1 - Math.pow(2, -53); // 1.0000000000000002
function assertNear(actual, expected) {
var error = Math.abs(actual - expected);
{
var fail = function (msg) {
var exc = new Error(msg);
try {
// Try to improve on exc.fileName and .lineNumber; leave exc.stack
// alone. We skip two frames: fail() and its caller, an assertX()
// function.
var frames = exc.stack.trim().split("\n");
if (frames.length > 2) {
var m = /@([^@:]*):([0-9]+)$/.exec(frames[2]);
if (m) {
exc.fileName = m[1];
exc.lineNumber = +m[2];
}
}
} catch (ignore) { throw ignore;}
throw exc;
};
if (error > 1e-300 && error > Math.abs(actual) * 1e-12)
throw 'Assertion failed: got "' + actual + '", expected "' + expected + '" (rel error = ' + (error / Math.abs(actual)) + ')';
var ENDIAN; // 0 for little-endian, 1 for big-endian.
// Return the difference between the IEEE 754 bit-patterns for a and b.
//
// This is meaningful when a and b are both finite and have the same
// sign. Then the following hold:
//
// * If a === b, then diff(a, b) === 0.
//
// * If a !== b, then diff(a, b) === 1 + the number of representable values
// between a and b.
//
var f = new Float64Array([0, 0]);
var u = new Uint32Array(f.buffer);
var diff = function (a, b) {
f[0] = a;
f[1] = b;
//print(u[1].toString(16) + u[0].toString(16) + " " + u[3].toString(16) + u[2].toString(16));
return Math.abs((u[3-ENDIAN] - u[1-ENDIAN]) * 0x100000000 + u[2+ENDIAN] - u[0+ENDIAN]);
};
// Set ENDIAN to the platform's endianness.
ENDIAN = 0; // try little-endian first
if (diff(2, 4) === 0x100000) // exact wrong answer we'll get on a big-endian platform
ENDIAN = 1;
assertEq(diff(2,4), 0x10000000000000);
assertEq(diff(0, Number.MIN_VALUE), 1);
assertEq(diff(1, ONE_PLUS_EPSILON), 1);
assertEq(diff(1, ONE_MINUS_EPSILON), 1);
var assertNear = function assertNear(a, b, tolerance=1) {
if (!Number.isFinite(b)) {
fail("second argument to assertNear (expected value) must be a finite number");
} else if (Number.isNaN(a)) {
fail("got NaN, expected a number near " + b);
} else if (!Number.isFinite(a)) {
if (b * Math.sign(a) < Number.MAX_VALUE)
fail("got " + a + ", expected a number near " + b);
} else {
// When the two arguments do not have the same sign bit, diff()
// returns some huge number. So if b is positive or negative 0,
// make target the zero that has the same sign bit as a.
var target = b === 0 ? a * 0 : b;
var err = diff(a, target);
if (err > tolerance) {
fail("got " + a + ", expected a number near " + b +
" (relative error: " + err + ")");
}
}
};
}

View File

@ -1,9 +1,11 @@
var sloppy_tolerance = 100;
for (var i = -20; i < 20; i++)
assertNear(Math.sinh(i), (Math.exp(i) - Math.exp(-i)) / 2);
assertNear(Math.sinh(i), (Math.exp(i) - Math.exp(-i)) / 2, sloppy_tolerance);
assertNear(Math.sinh(1e5), Infinity);
assertNear(Math.sinh(1e-30), 1e-30);
assertNear(Math.sinh(1e-10), 1e-10);
assertEq(Math.sinh(1000), Infinity);
assertEq(Math.sinh(Number.MAX_VALUE), Infinity);
assertNear(Math.sinh(1e-30), 1e-30, sloppy_tolerance);
assertNear(Math.sinh(1e-10), 1e-10, sloppy_tolerance);
var sinh_data = [
[-6.902103625349695, -497.1816406250001],
@ -290,7 +292,7 @@ var sinh_data = [
];
for (var [x, y] of sinh_data)
assertNear(Math.sinh(x), y);
assertNear(Math.sinh(x), y, sloppy_tolerance);
reportCompare(0, 0, "ok");

View File

@ -1,5 +1,10 @@
for (var i = -20; i < 20; i++)
assertNear(Math.tanh(i), (Math.exp(i) - Math.exp(-i)) / (Math.exp(i) + Math.exp(-i)));
var sloppy_tolerance = 4;
for (var i = -20; i < 20; i++) {
assertNear(Math.tanh(i),
(Math.exp(i) - Math.exp(-i)) / (Math.exp(i) + Math.exp(-i)),
sloppy_tolerance);
}
assertEq(Math.tanh(1e300), 1);
@ -272,6 +277,6 @@ var tanh_data = [
];
for (var [x, y] of tanh_data)
assertNear(Math.tanh(y), x);
assertNear(Math.tanh(y), x, sloppy_tolerance);
reportCompare(0, 0, "ok");

View File

@ -44,6 +44,7 @@
#include "ImageLayers.h"
#include "ImageContainer.h"
#include "nsCanvasFrame.h"
#include "StickyScrollContainer.h"
#include "mozilla/LookAndFeel.h"
#include <stdint.h>
@ -3133,42 +3134,29 @@ nsDisplayFixedPosition::~nsDisplayFixedPosition() {
}
#endif
already_AddRefed<Layer>
nsDisplayFixedPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerParameters& aContainerParameters) {
nsRefPtr<Layer> layer =
nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, aContainerParameters);
void nsDisplayFixedPosition::SetFixedPositionLayerData(Layer* const aLayer,
nsIFrame* aViewportFrame,
nsSize aViewportSize,
nsPresContext* aPresContext,
const ContainerParameters& aContainerParameters) {
// Find out the rect of the viewport frame relative to the reference frame.
// This, in conjunction with the container scale, will correspond to the
// coordinate-space of the built layer.
float factor = aPresContext->AppUnitsPerDevPixel();
nsPoint origin = aViewportFrame->GetOffsetToCrossDoc(ReferenceFrame());
LayerRect anchorRect(NSAppUnitsToFloatPixels(origin.x, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(origin.y, factor) *
aContainerParameters.mYScale,
NSAppUnitsToFloatPixels(aViewportSize.width, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(aViewportSize.height, factor) *
aContainerParameters.mYScale);
// Work out the anchor point for this fixed position layer. We assume that
// any positioning set (left/top/right/bottom) indicates that the
// corresponding side of its container should be the anchor point,
// defaulting to top-left.
nsIFrame* viewportFrame = mFixedPosFrame->GetParent();
nsPresContext *presContext = viewportFrame->PresContext();
// Fixed position frames are reflowed into the scroll-port size if one has
// been set.
nsSize containingBlockSize = viewportFrame->GetSize();
if (presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
containingBlockSize = presContext->PresShell()->
GetScrollPositionClampingScrollPortSize();
}
// Find out the rect of the viewport frame relative to the reference frame.
// This, in conjunction with the container scale, will correspond to the
// coordinate-space of the built layer.
float factor = presContext->AppUnitsPerDevPixel();
nsPoint origin = viewportFrame->GetOffsetToCrossDoc(ReferenceFrame());
LayerRect anchorRect(NSAppUnitsToFloatPixels(origin.x, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(origin.y, factor) *
aContainerParameters.mYScale,
NSAppUnitsToFloatPixels(containingBlockSize.width, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(containingBlockSize.height, factor) *
aContainerParameters.mYScale);
LayerPoint anchor = anchorRect.TopLeft();
const nsStylePosition* position = mFixedPosFrame->StylePosition();
@ -3177,11 +3165,11 @@ nsDisplayFixedPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
if (position->mOffset.GetBottomUnit() != eStyleUnit_Auto)
anchor.y = anchorRect.YMost();
layer->SetFixedPositionAnchor(anchor);
aLayer->SetFixedPositionAnchor(anchor);
// Also make sure the layer is aware of any fixed position margins that have
// been set.
nsMargin fixedMargins = presContext->PresShell()->GetContentDocumentFixedPositionMargins();
nsMargin fixedMargins = aPresContext->PresShell()->GetContentDocumentFixedPositionMargins();
LayerMargin fixedLayerMargins(NSAppUnitsToFloatPixels(fixedMargins.top, factor) *
aContainerParameters.mYScale,
NSAppUnitsToFloatPixels(fixedMargins.right, factor) *
@ -3203,7 +3191,29 @@ nsDisplayFixedPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
fixedLayerMargins.top = -1;
}
layer->SetFixedPositionMargins(fixedLayerMargins);
aLayer->SetFixedPositionMargins(fixedLayerMargins);
}
already_AddRefed<Layer>
nsDisplayFixedPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerParameters& aContainerParameters) {
nsRefPtr<Layer> layer =
nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, aContainerParameters);
nsIFrame* viewportFrame = mFixedPosFrame->GetParent();
nsPresContext *presContext = viewportFrame->PresContext();
// Fixed position frames are reflowed into the scroll-port size if one has
// been set.
nsSize viewportSize = viewportFrame->GetSize();
if (presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
viewportSize = presContext->PresShell()->
GetScrollPositionClampingScrollPortSize();
}
SetFixedPositionLayerData(layer, viewportFrame, viewportSize, presContext,
aContainerParameters);
return layer.forget();
}
@ -3221,6 +3231,76 @@ bool nsDisplayFixedPosition::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayI
return true;
}
nsDisplayStickyPosition::nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame,
nsIFrame* aStickyPosFrame,
nsDisplayList* aList)
: nsDisplayFixedPosition(aBuilder, aFrame, aStickyPosFrame, aList) {
MOZ_COUNT_CTOR(nsDisplayStickyPosition);
}
#ifdef NS_BUILD_REFCNT_LOGGING
nsDisplayStickyPosition::~nsDisplayStickyPosition() {
MOZ_COUNT_DTOR(nsDisplayStickyPosition);
}
#endif
already_AddRefed<Layer>
nsDisplayStickyPosition::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerParameters& aContainerParameters) {
nsRefPtr<Layer> layer =
nsDisplayOwnLayer::BuildLayer(aBuilder, aManager, aContainerParameters);
StickyScrollContainer* stickyScrollContainer = StickyScrollContainer::
GetStickyScrollContainerForFrame(mFrame);
if (!stickyScrollContainer) {
return layer.forget();
}
nsIFrame* scrollFrame = do_QueryFrame(stickyScrollContainer->ScrollFrame());
nsPresContext* presContext = scrollFrame->PresContext();
// Sticky position frames whose scroll frame is the root scroll frame are
// reflowed into the scroll-port size if one has been set.
nsSize scrollFrameSize = scrollFrame->GetSize();
if (scrollFrame == presContext->PresShell()->GetRootScrollFrame() &&
presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) {
scrollFrameSize = presContext->PresShell()->
GetScrollPositionClampingScrollPortSize();
}
SetFixedPositionLayerData(layer, scrollFrame, scrollFrameSize, presContext,
aContainerParameters);
ViewID scrollId = nsLayoutUtils::FindOrCreateIDFor(
stickyScrollContainer->ScrollFrame()->GetScrolledFrame()->GetContent());
float factor = presContext->AppUnitsPerDevPixel();
nsRect outer;
nsRect inner;
stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
LayerRect stickyOuter(NSAppUnitsToFloatPixels(outer.x, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(outer.y, factor) *
aContainerParameters.mYScale,
NSAppUnitsToFloatPixels(outer.width, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(outer.height, factor) *
aContainerParameters.mYScale);
LayerRect stickyInner(NSAppUnitsToFloatPixels(inner.x, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(inner.y, factor) *
aContainerParameters.mYScale,
NSAppUnitsToFloatPixels(inner.width, factor) *
aContainerParameters.mXScale,
NSAppUnitsToFloatPixels(inner.height, factor) *
aContainerParameters.mYScale);
layer->SetStickyPositionData(scrollId, stickyOuter, stickyInner);
return layer.forget();
}
nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder,
nsDisplayList* aList,
nsIFrame* aForFrame,

View File

@ -2569,9 +2569,30 @@ public:
NS_DISPLAY_DECL_NAME("FixedPosition", TYPE_FIXED_POSITION)
protected:
void SetFixedPositionLayerData(Layer* const aLayer, nsIFrame* aViewportFrame,
nsSize aViewportSize, nsPresContext* aPresContext,
const ContainerParameters& aContainerParameters);
nsIFrame* mFixedPosFrame;
};
/**
* A display item used to represent sticky position elements. The contents
* gets its own layer and creates a stacking context, and the layer will have
* position-related metadata set on it.
*/
class nsDisplayStickyPosition : public nsDisplayFixedPosition {
public:
nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsIFrame* aStickyPosFrame, nsDisplayList* aList);
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayStickyPosition();
#endif
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerParameters& aContainerParameters) MOZ_OVERRIDE;
};
/**
* This potentially creates a layer for the given list of items, whose
* visibility is determined by the displayport for the given frame instead of

View File

@ -77,6 +77,7 @@ struct nsRect;
class nsRegion;
class nsRefreshDriver;
class nsARefreshObserver;
class nsAPostRefreshObserver;
#ifdef ACCESSIBILITY
class nsAccessibilityService;
namespace mozilla {
@ -1363,13 +1364,13 @@ public:
*/
protected:
virtual bool AddRefreshObserverExternal(nsARefreshObserver* aObserver,
mozFlushType aFlushType);
mozFlushType aFlushType);
bool AddRefreshObserverInternal(nsARefreshObserver* aObserver,
mozFlushType aFlushType);
mozFlushType aFlushType);
virtual bool RemoveRefreshObserverExternal(nsARefreshObserver* aObserver,
mozFlushType aFlushType);
mozFlushType aFlushType);
bool RemoveRefreshObserverInternal(nsARefreshObserver* aObserver,
mozFlushType aFlushType);
mozFlushType aFlushType);
/**
* Do computations necessary to determine if font size inflation is enabled.
@ -1380,7 +1381,7 @@ protected:
public:
bool AddRefreshObserver(nsARefreshObserver* aObserver,
mozFlushType aFlushType) {
mozFlushType aFlushType) {
#ifdef MOZILLA_INTERNAL_API
return AddRefreshObserverInternal(aObserver, aFlushType);
#else
@ -1389,7 +1390,7 @@ public:
}
bool RemoveRefreshObserver(nsARefreshObserver* aObserver,
mozFlushType aFlushType) {
mozFlushType aFlushType) {
#ifdef MOZILLA_INTERNAL_API
return RemoveRefreshObserverInternal(aObserver, aFlushType);
#else
@ -1397,6 +1398,9 @@ public:
#endif
}
virtual bool AddPostRefreshObserver(nsAPostRefreshObserver* aObserver);
virtual bool RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver);
/**
* Initialize and shut down static variables.
*/

View File

@ -8221,8 +8221,8 @@ nsIPresShell::AddRefreshObserverInternal(nsARefreshObserver* aObserver,
mozFlushType aFlushType)
{
nsPresContext* presContext = GetPresContext();
return presContext ? presContext->RefreshDriver()->
AddRefreshObserver(aObserver, aFlushType) : false;
return presContext &&
presContext->RefreshDriver()->AddRefreshObserver(aObserver, aFlushType);
}
/* virtual */ bool
@ -8237,8 +8237,8 @@ nsIPresShell::RemoveRefreshObserverInternal(nsARefreshObserver* aObserver,
mozFlushType aFlushType)
{
nsPresContext* presContext = GetPresContext();
return presContext ? presContext->RefreshDriver()->
RemoveRefreshObserver(aObserver, aFlushType) : false;
return presContext &&
presContext->RefreshDriver()->RemoveRefreshObserver(aObserver, aFlushType);
}
/* virtual */ bool
@ -8248,6 +8248,28 @@ nsIPresShell::RemoveRefreshObserverExternal(nsARefreshObserver* aObserver,
return RemoveRefreshObserverInternal(aObserver, aFlushType);
}
/* virtual */ bool
nsIPresShell::AddPostRefreshObserver(nsAPostRefreshObserver* aObserver)
{
nsPresContext* presContext = GetPresContext();
if (!presContext) {
return false;
}
presContext->RefreshDriver()->AddPostRefreshObserver(aObserver);
return true;
}
/* virtual */ bool
nsIPresShell::RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver)
{
nsPresContext* presContext = GetPresContext();
if (!presContext) {
return false;
}
presContext->RefreshDriver()->RemovePostRefreshObserver(aObserver);
return true;
}
//------------------------------------------------------
// End of protected and private methods on the PresShell
//------------------------------------------------------

View File

@ -323,7 +323,6 @@ public:
virtual void AddInvalidateHiddenPresShellObserver(nsRefreshDriver *aDriver) MOZ_OVERRIDE;
// This data is stored as a content property (nsGkAtoms::scrolling) on
// mContentToScrollTo when we have a pending ScrollIntoView.
struct ScrollIntoViewData {

View File

@ -753,9 +753,7 @@ nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
{
ObserverArray& array = ArrayFor(aFlushType);
bool success = array.AppendElement(aObserver) != nullptr;
EnsureTimerStarted(false);
return success;
}
@ -767,6 +765,18 @@ nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
return array.RemoveElement(aObserver);
}
void
nsRefreshDriver::AddPostRefreshObserver(nsAPostRefreshObserver* aObserver)
{
mPostRefreshObservers.AppendElement(aObserver);
}
void
nsRefreshDriver::RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver)
{
mPostRefreshObservers.RemoveElement(aObserver);
}
bool
nsRefreshDriver::AddImageRequest(imgIRequest* aRequest)
{
@ -1198,6 +1208,10 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
}
#endif
}
for (uint32_t i = 0; i < mPostRefreshObservers.Length(); ++i) {
mPostRefreshObservers[i]->DidRefresh();
}
}
/* static */ PLDHashOperator

View File

@ -26,16 +26,17 @@ class nsPresContext;
class nsIPresShell;
class nsIDocument;
class imgIRequest;
class nsIRunnable;
namespace mozilla {
class RefreshDriverTimer;
}
/**
* An abstract base class to be implemented by callers wanting to be
* notified at refresh times. When nothing needs to be painted, callers
* may not be notified.
*/
namespace mozilla {
class RefreshDriverTimer;
}
class nsARefreshObserver {
public:
// AddRef and Release signatures that match nsISupports. Implementors
@ -50,6 +51,16 @@ public:
virtual void WillRefresh(mozilla::TimeStamp aTime) = 0;
};
/**
* An abstract base class to be implemented by callers wanting to be notified
* that a refresh has occurred. Callers must ensure an observer is removed
* before it is destroyed.
*/
class nsAPostRefreshObserver {
public:
virtual void DidRefresh() = 0;
};
class nsRefreshDriver MOZ_FINAL : public nsISupports {
public:
nsRefreshDriver(nsPresContext *aPresContext);
@ -101,11 +112,21 @@ public:
*
* The refresh driver does NOT own a reference to these observers;
* they must remove themselves before they are destroyed.
*
* The observer will be called even if there is no other activity.
*/
bool AddRefreshObserver(nsARefreshObserver *aObserver,
mozFlushType aFlushType);
mozFlushType aFlushType);
bool RemoveRefreshObserver(nsARefreshObserver *aObserver,
mozFlushType aFlushType);
mozFlushType aFlushType);
/**
* Add an observer that will be called after each refresh. The caller
* must remove the observer before it is deleted. This does not trigger
* refresh driver ticks.
*/
void AddPostRefreshObserver(nsAPostRefreshObserver *aObserver);
void RemovePostRefreshObserver(nsAPostRefreshObserver *aObserver);
/**
* Add/Remove imgIRequest versions of observers.
@ -295,6 +316,7 @@ private:
nsAutoTArray<nsIPresShell*, 16> mPresShellsToInvalidateIfHidden;
// nsTArray on purpose, because we want to be able to swap.
nsTArray<nsIDocument*> mFrameRequestCallbackDocs;
nsTArray<nsAPostRefreshObserver*> mPostRefreshObservers;
// Helper struct for processing image requests
struct ImageRequestParameters {

View File

@ -219,12 +219,53 @@ StickyScrollContainer::ComputePosition(nsIFrame* aFrame) const
return position;
}
void
StickyScrollContainer::GetScrollRanges(nsIFrame* aFrame, nsRect* aOuter,
nsRect* aInner) const
{
nsRect stick;
nsRect contain;
ComputeStickyLimits(aFrame, &stick, &contain);
aOuter->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX);
aInner->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX);
const nsPoint normalPosition = aFrame->GetNormalPosition();
// Bottom and top
if (stick.YMost() != nscoord_MAX/2) {
aOuter->SetTopEdge(contain.y - stick.YMost());
aInner->SetTopEdge(normalPosition.y - stick.YMost());
}
if (stick.y != nscoord_MIN/2) {
aInner->SetBottomEdge(normalPosition.y - stick.y);
aOuter->SetBottomEdge(contain.YMost() - stick.y);
}
// Right and left
if (stick.XMost() != nscoord_MAX/2) {
aOuter->SetLeftEdge(contain.x - stick.XMost());
aInner->SetLeftEdge(normalPosition.x - stick.XMost());
}
if (stick.x != nscoord_MIN/2) {
aInner->SetRightEdge(normalPosition.x - stick.x);
aOuter->SetRightEdge(contain.XMost() - stick.x);
}
}
void
StickyScrollContainer::UpdatePositions(nsPoint aScrollPosition,
nsIFrame* aSubtreeRoot)
{
NS_ASSERTION(!aSubtreeRoot || aSubtreeRoot == do_QueryFrame(mScrollFrame),
"If reflowing, should be reflowing the scroll frame");
#ifdef DEBUG
{
nsIFrame* scrollFrameAsFrame = do_QueryFrame(mScrollFrame);
NS_ASSERTION(!aSubtreeRoot || aSubtreeRoot == scrollFrameAsFrame,
"If reflowing, should be reflowing the scroll frame");
}
#endif
mScrollPosition = aScrollPosition;
OverflowChangedTracker oct;

View File

@ -44,6 +44,10 @@ public:
mFrames.RemoveElement(aFrame);
}
nsIScrollableFrame* ScrollFrame() const {
return mScrollFrame;
}
// Compute the offsets for a sticky position element
static void ComputeStickyOffsets(nsIFrame* aFrame);
@ -53,6 +57,12 @@ public:
*/
nsPoint ComputePosition(nsIFrame* aFrame) const;
/**
* Compute where a frame should not scroll with the page, represented by the
* difference of two rectangles.
*/
void GetScrollRanges(nsIFrame* aFrame, nsRect* aOuter, nsRect* aInner) const;
/**
* Compute and set the position of all sticky frames, given the current
* scroll position of the scroll frame. If not in reflow, aSubtreeRoot should

View File

@ -1736,6 +1736,12 @@ WrapPreserve3DListInternal(nsIFrame* aFrame, nsDisplayListBuilder *aBuilder, nsD
return NS_OK;
}
static bool
IsScrollFrameActive(nsIScrollableFrame* aScrollableFrame)
{
return aScrollableFrame && aScrollableFrame->IsScrollingActive();
}
static nsresult
WrapPreserve3DList(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder, nsDisplayList *aList)
{
@ -1813,10 +1819,14 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
bool useOpacity = HasOpacity() && !nsSVGUtils::CanOptimizeOpacity(this);
bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this);
bool useStickyPosition = disp->mPosition == NS_STYLE_POSITION_STICKY &&
IsScrollFrameActive(nsLayoutUtils::GetNearestScrollableFrame(GetParent(),
nsLayoutUtils::SCROLLABLE_SAME_DOC |
nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN));
DisplayListClipState::AutoSaveRestore clipState(aBuilder);
if (isTransformed || useOpacity || usingSVGEffects) {
if (isTransformed || useOpacity || usingSVGEffects || useStickyPosition) {
// We don't need to pass ancestor clipping down to our children;
// everything goes inside a display item's child list, and the display
// item itself will be clipped.
@ -1935,6 +1945,13 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
resultList.AppendNewToTop(
new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList));
}
/* If we have sticky positioning, wrap it in a sticky position item.
*/
if (useStickyPosition) {
resultList.AppendNewToTop(
new (aBuilder) nsDisplayStickyPosition(aBuilder, this, this,
&resultList));
}
/* If we're going to apply a transformation and don't have preserve-3d set, wrap
* everything in an nsDisplayTransform. If there's nothing in the list, don't add
@ -1962,13 +1979,6 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
aList->AppendToTop(&resultList);
}
static bool
IsRootScrollFrameActive(nsIPresShell* aPresShell)
{
nsIScrollableFrame* sf = aPresShell->GetRootScrollFrameAsScrollable();
return sf && sf->IsScrollingActive();
}
static nsDisplayItem*
WrapInWrapList(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, nsDisplayList* aList)
@ -2127,7 +2137,8 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
// active, that's pointless and the extra layer(s) created may be wasteful.
bool buildFixedPositionItem = disp->mPosition == NS_STYLE_POSITION_FIXED &&
!child->GetParent()->GetParent() && !aBuilder->IsInFixedPosition() &&
IsRootScrollFrameActive(PresContext()->PresShell()) && !isSVG;
IsScrollFrameActive(PresContext()->PresShell()->GetRootScrollFrameAsScrollable()) &&
!isSVG;
nsDisplayListBuilder::AutoBuildingDisplayList
buildingForChild(aBuilder, child, pseudoStackingContext, buildFixedPositionItem);

View File

@ -148,6 +148,28 @@ NS_DECLARE_FRAME_PROPERTY(UninflatedTextRunProperty, nullptr)
NS_DECLARE_FRAME_PROPERTY(FontSizeInflationProperty, nullptr)
class GlyphObserver : public gfxFont::GlyphChangeObserver {
public:
GlyphObserver(gfxFont* aFont, nsTextFrame* aFrame)
: gfxFont::GlyphChangeObserver(aFont), mFrame(aFrame) {}
virtual void NotifyGlyphsChanged() MOZ_OVERRIDE;
private:
nsTextFrame* mFrame;
};
static void DestroyGlyphObserverList(void* aPropertyValue)
{
delete static_cast<nsTArray<nsAutoPtr<GlyphObserver> >*>(aPropertyValue);
}
/**
* This property is set on text frames with TEXT_IN_TEXTRUN_USER_DATA set that
* have potentially-animated glyphs.
* The only reason this list is in a property is to automatically destroy the
* list when the frame is deleted, unregistering the observers.
*/
NS_DECLARE_FRAME_PROPERTY(TextFrameGlyphObservers, DestroyGlyphObserverList);
// The following flags are set during reflow
// This bit is set on the first frame in a continuation indicating
@ -506,6 +528,26 @@ UnhookTextRunFromFrames(gfxTextRun* aTextRun, nsTextFrame* aStartContinuation)
}
}
void
GlyphObserver::NotifyGlyphsChanged()
{
nsIPresShell* shell = mFrame->PresContext()->PresShell();
for (nsIFrame* f = mFrame; f;
f = nsLayoutUtils::GetNextContinuationOrSpecialSibling(f)) {
if (f != mFrame && f->HasAnyStateBits(TEXT_IN_TEXTRUN_USER_DATA)) {
// f will have its own GlyphObserver (if needed) so we can stop here.
break;
}
f->InvalidateFrame();
// Theoretically we could just update overflow areas, perhaps using
// OverflowChangedTracker, but that would do a bunch of work eagerly that
// we should probably do lazily here since there could be a lot
// of text frames affected and we'd like to coalesce the work. So that's
// not easy to do well.
shell->FrameNeedsReflow(f, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
}
}
class FrameTextRunCache;
static FrameTextRunCache *gTextRuns = nullptr;
@ -772,6 +814,63 @@ IsAllNewlines(const nsTextFragment* aFrag)
return true;
}
static void
CreateObserverForAnimatedGlyphs(nsTextFrame* aFrame, const nsTArray<gfxFont*>& aFonts)
{
if (!(aFrame->GetStateBits() & TEXT_IN_TEXTRUN_USER_DATA)) {
// Maybe the textrun was created for uninflated text.
return;
}
nsTArray<nsAutoPtr<GlyphObserver> >* observers =
new nsTArray<nsAutoPtr<GlyphObserver> >();
for (uint32_t i = 0, count = aFonts.Length(); i < count; ++i) {
observers->AppendElement(new GlyphObserver(aFonts[i], aFrame));
}
aFrame->Properties().Set(TextFrameGlyphObservers(), observers);
// We are lazy and don't try to remove a property value that might be
// obsolete due to style changes or font selection changes. That is
// likely to be rarely needed, and we don't want to eat the overhead of
// doing it for the overwhelmingly common case of no property existing.
// (And we're out of state bits to conveniently use for a fast property
// existence check.) The only downside is that in some rare cases we might
// keep fonts alive for longer than necessary, or unnecessarily invalidate
// frames.
}
static void
CreateObserversForAnimatedGlyphs(gfxTextRun* aTextRun)
{
if (!aTextRun->GetUserData()) {
return;
}
nsTArray<gfxFont*> fontsWithAnimatedGlyphs;
uint32_t numGlyphRuns;
const gfxTextRun::GlyphRun* glyphRuns =
aTextRun->GetGlyphRuns(&numGlyphRuns);
for (uint32_t i = 0; i < numGlyphRuns; ++i) {
gfxFont* font = glyphRuns[i].mFont;
if (font->GlyphsMayChange() && !fontsWithAnimatedGlyphs.Contains(font)) {
fontsWithAnimatedGlyphs.AppendElement(font);
}
}
if (fontsWithAnimatedGlyphs.IsEmpty()) {
return;
}
if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
CreateObserverForAnimatedGlyphs(static_cast<nsTextFrame*>(
static_cast<nsIFrame*>(aTextRun->GetUserData())), fontsWithAnimatedGlyphs);
} else {
TextRunUserData* userData =
static_cast<TextRunUserData*>(aTextRun->GetUserData());
for (uint32_t i = 0; i < userData->mMappedFlowCount; ++i) {
CreateObserverForAnimatedGlyphs(userData->mMappedFlows[i].mStartFrame,
fontsWithAnimatedGlyphs);
}
}
}
/**
* This class accumulates state as we scan a paragraph of text. It detects
* textrun boundaries (changes from text to non-text, hard
@ -927,6 +1026,10 @@ public:
static_cast<nsTransformedTextRun*>(mTextRun);
transformedTextRun->FinishSettingProperties(mContext);
}
// The way nsTransformedTextRun is implemented, its glyph runs aren't
// available until after nsTransformedTextRun::FinishSettingProperties()
// is called. So that's why we defer checking for animated glyphs to here.
CreateObserversForAnimatedGlyphs(mTextRun);
}
gfxTextRun* mTextRun;

View File

@ -30,14 +30,14 @@ fuzzy-if(Android,2,4) == right-3.html right-3-ref.html
== scrollframe-reflow-1.html scrollframe-reflow-1-ref.html
== scrollframe-reflow-2.html scrollframe-reflow-2-ref.html
== scrollframe-auto-1.html scrollframe-auto-1-ref.html
== stacking-context-1.html stacking-context-1-ref.html
fuzzy-if(Android,2,3) == stacking-context-1.html stacking-context-1-ref.html
== top-bottom-1.html top-bottom-1-ref.html
== top-bottom-2.html top-bottom-2-ref.html
== top-bottom-3.html top-bottom-3-ref.html
== left-right-1.html left-right-1-ref.html
== left-right-2.html left-right-2-ref.html
== left-right-3.html left-right-3-ref.html
== containing-block-1.html containing-block-1-ref.html
fuzzy-if(Android,4,1) == containing-block-1.html containing-block-1-ref.html
== overconstrained-1.html overconstrained-1-ref.html
== overconstrained-2.html overconstrained-2-ref.html
== overconstrained-3.html overconstrained-3-ref.html

View File

@ -28,6 +28,7 @@ function testDisabled() {
is(document.body.style[p], "", p + " not settable to " + props[p]);
document.body.style[p] = "";
}
SimpleTest.finish();
}
function testEnabled() {
@ -43,6 +44,8 @@ function testEnabled() {
);
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv(
{'set': [['gfx.font_rendering.opentype_svg.enabled', true]]},
testEnabled

View File

@ -113,6 +113,43 @@
#define ARCH_CPU_MIPSEL 1
#define ARCH_CPU_32_BITS 1
#define ARCH_CPU_LITTLE_ENDIAN 1
#elif defined(__powerpc64__)
#define ARCH_CPU_PPC_FAMILY 1
#define ARCH_CPU_PPC64 1
#define ARCH_CPU_64_BITS 1
#define ARCH_CPU_BIG_ENDIAN 1
#elif defined(__ppc__) || defined(__powerpc__)
#define ARCH_CPU_PPC_FAMILY 1
#define ARCH_CPU_PPC 1
#define ARCH_CPU_32_BITS 1
#define ARCH_CPU_BIG_ENDIAN 1
#elif defined(__sparc64__)
#define ARCH_CPU_SPARC_FAMILY 1
#define ARCH_CPU_SPARC 1
#define ARCH_CPU_64_BITS 1
#elif defined(__sparc__)
#define ARCH_CPU_SPARC_FAMILY 1
#define ARCH_CPU_SPARC 1
#define ARCH_CPU_32_BITS 1
#elif defined(__mips__)
#define ARCH_CPU_MIPS_FAMILY 1
#define ARCH_CPU_MIPS 1
#define ARCH_CPU_32_BITS 1
#elif defined(__hppa__)
#define ARCH_CPU_HPPA 1
#define ARCH_CPU_32_BITS 1
#elif defined(__ia64__)
#define ARCH_CPU_IA64 1
#define ARCH_CPU_64_BITS 1
#elif defined(__s390x__)
#define ARCH_CPU_S390X 1
#define ARCH_CPU_64_BITS 1
#elif defined(__s390__)
#define ARCH_CPU_S390 1
#define ARCH_CPU_32_BITS 1
#elif defined(__alpha__)
#define ARCH_CPU_ALPHA 1
#define ARCH_CPU_64_BITS 1
#else
#error Please add support for your architecture in build/build_config.h
#endif

View File

@ -67,7 +67,10 @@ public class GeckoEvent {
REMOVE_OBSERVER(34),
LOW_MEMORY(35),
NETWORK_LINK_CHANGE(36),
TELEMETRY_HISTOGRAM_ADD(37);
TELEMETRY_HISTOGRAM_ADD(37),
PREFERENCES_OBSERVE(39),
PREFERENCES_GET(40),
PREFERENCES_REMOVE_OBSERVERS(41);
public final int value;
@ -186,6 +189,8 @@ public class GeckoEvent {
private int mWidth;
private int mHeight;
private String[] mPrefNames;
private GeckoEvent(NativeGeckoEvent event) {
mType = event.value;
}
@ -689,6 +694,26 @@ public class GeckoEvent {
return event;
}
public static GeckoEvent createPreferencesObserveEvent(int requestId, String[] prefNames) {
GeckoEvent event = new GeckoEvent(NativeGeckoEvent.PREFERENCES_OBSERVE);
event.mCount = requestId;
event.mPrefNames = prefNames;
return event;
}
public static GeckoEvent createPreferencesGetEvent(int requestId, String[] prefNames) {
GeckoEvent event = new GeckoEvent(NativeGeckoEvent.PREFERENCES_GET);
event.mCount = requestId;
event.mPrefNames = prefNames;
return event;
}
public static GeckoEvent createPreferencesRemoveObserversEvent(int requestId) {
GeckoEvent event = new GeckoEvent(NativeGeckoEvent.PREFERENCES_REMOVE_OBSERVERS);
event.mCount = requestId;
return event;
}
public static GeckoEvent createLowMemoryEvent(int level) {
GeckoEvent event = new GeckoEvent(NativeGeckoEvent.LOW_MEMORY);
event.mMetaState = level;

View File

@ -676,9 +676,7 @@ public class GeckoPreferences
// Initialize preferences by requesting the preference values from Gecko
private int getGeckoPreferences(final PreferenceGroup screen, ArrayList<String> prefs) {
JSONArray jsonPrefs = new JSONArray(prefs);
return PrefsHelper.getPrefs(jsonPrefs, new PrefsHelper.PrefHandlerBase() {
return PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() {
private Preference getField(String prefName) {
return screen.findPreference(prefName);
}

View File

@ -13,6 +13,7 @@ import org.json.JSONObject;
import android.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@ -27,20 +28,18 @@ public final class PrefsHelper {
private static int sUniqueRequestId = 1;
public static int getPref(String prefName, PrefHandler callback) {
JSONArray prefs = new JSONArray();
prefs.put(prefName);
return getPrefs(prefs, callback);
return getPrefsInternal(new String[] { prefName }, callback);
}
public static int getPrefs(String[] prefNames, PrefHandler callback) {
JSONArray prefs = new JSONArray();
for (String p : prefNames) {
prefs.put(p);
}
return getPrefs(prefs, callback);
return getPrefsInternal(prefNames, callback);
}
public static int getPrefs(JSONArray prefNames, PrefHandler callback) {
public static int getPrefs(ArrayList<String> prefNames, PrefHandler callback) {
return getPrefsInternal(prefNames.toArray(new String[prefNames.size()]), callback);
}
private static int getPrefsInternal(String[] prefNames, PrefHandler callback) {
int requestId;
synchronized (PrefsHelper.class) {
ensureRegistered();
@ -50,25 +49,12 @@ public final class PrefsHelper {
}
GeckoEvent event;
try {
JSONObject message = new JSONObject();
message.put("requestId", Integer.toString(requestId));
message.put("preferences", prefNames);
event = GeckoEvent.createBroadcastEvent(callback.isObserver() ?
"Preferences:Observe" : "Preferences:Get", message.toString());
GeckoAppShell.sendEventToGecko(event);
} catch (Exception e) {
Log.e(LOGTAG, "Error while composing Preferences:" +
(callback.isObserver() ? "Observe" : "Get") + " message", e);
// if we failed to send the message, drop our reference to the callback because
// otherwise it will leak since we will never get the response
synchronized (PrefsHelper.class) {
sCallbacks.remove(requestId);
}
return -1;
if (callback.isObserver()) {
event = GeckoEvent.createPreferencesObserveEvent(requestId, prefNames);
} else {
event = GeckoEvent.createPreferencesGetEvent(requestId, prefNames);
}
GeckoAppShell.sendEventToGecko(event);
return requestId;
}

View File

@ -35,6 +35,18 @@ public class RobocopAPI {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(subject, data));
}
public void preferencesGetEvent(int requestId, String[] prefNames) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesGetEvent(requestId, prefNames));
}
public void preferencesObserveEvent(int requestId, String[] prefNames) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesObserveEvent(requestId, prefNames));
}
public void preferencesRemoveObserversEvent(int requestId) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createPreferencesRemoveObserversEvent(requestId));
}
public void setDrawListener(GeckoLayerClient.DrawListener listener) {
GeckoAppShell.getLayerView().getLayerClient().setDrawListener(listener);
}

View File

@ -64,13 +64,12 @@ abstract class Axis {
}
static void initPrefs() {
JSONArray prefs = new JSONArray();
prefs.put(PREF_SCROLLING_FRICTION_FAST);
prefs.put(PREF_SCROLLING_FRICTION_SLOW);
prefs.put(PREF_SCROLLING_MAX_EVENT_ACCELERATION);
prefs.put(PREF_SCROLLING_OVERSCROLL_DECEL_RATE);
prefs.put(PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT);
prefs.put(PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE);
final String[] prefs = { PREF_SCROLLING_FRICTION_FAST,
PREF_SCROLLING_FRICTION_SLOW,
PREF_SCROLLING_MAX_EVENT_ACCELERATION,
PREF_SCROLLING_OVERSCROLL_DECEL_RATE,
PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT,
PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE };
PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() {
Map<String, Integer> mPrefs = new HashMap<String, Integer>();

View File

@ -61,19 +61,18 @@ final class DisplayPortCalculator {
}
static void initPrefs() {
JSONArray prefs = new JSONArray();
prefs.put(PREF_DISPLAYPORT_STRATEGY);
prefs.put(PREF_DISPLAYPORT_FM_MULTIPLIER);
prefs.put(PREF_DISPLAYPORT_FM_DANGER_X);
prefs.put(PREF_DISPLAYPORT_FM_DANGER_Y);
prefs.put(PREF_DISPLAYPORT_VB_MULTIPLIER);
prefs.put(PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD);
prefs.put(PREF_DISPLAYPORT_VB_REVERSE_BUFFER);
prefs.put(PREF_DISPLAYPORT_VB_DANGER_X_BASE);
prefs.put(PREF_DISPLAYPORT_VB_DANGER_Y_BASE);
prefs.put(PREF_DISPLAYPORT_VB_DANGER_X_INCR);
prefs.put(PREF_DISPLAYPORT_VB_DANGER_Y_INCR);
prefs.put(PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD);
final String[] prefs = { PREF_DISPLAYPORT_STRATEGY,
PREF_DISPLAYPORT_FM_MULTIPLIER,
PREF_DISPLAYPORT_FM_DANGER_X,
PREF_DISPLAYPORT_FM_DANGER_Y,
PREF_DISPLAYPORT_VB_MULTIPLIER,
PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD,
PREF_DISPLAYPORT_VB_REVERSE_BUFFER,
PREF_DISPLAYPORT_VB_DANGER_X_BASE,
PREF_DISPLAYPORT_VB_DANGER_Y_BASE,
PREF_DISPLAYPORT_VB_DANGER_X_INCR,
PREF_DISPLAYPORT_VB_DANGER_Y_INCR,
PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD };
PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() {
private Map<String, Integer> mValues = new HashMap<String, Integer>();

View File

@ -59,21 +59,18 @@ public class testAddonManager extends PixelTest {
mActions.sendGeckoEvent("Preferences:Set", jsonPref.toString());
// Wait for confirmation of the pref change before proceeding with the test.
JSONArray getPrefData = new JSONArray();
getPrefData.put("extensions.getAddons.browseAddons");
JSONObject message = new JSONObject();
message.put("requestId", "testAddonManager");
message.put("preferences", getPrefData);
final String[] prefNames = { "extensions.getAddons.browseAddons" };
final int ourRequestId = 0x7357;
Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data");
mActions.sendGeckoEvent("Preferences:Get", message.toString());
mActions.sendPreferencesGetEvent(ourRequestId, prefNames);
JSONObject data = null;
String requestId = "";
int requestId = -1;
// Wait until we get the correct "Preferences:Data" event
while (!requestId.equals("testAddonManager")) {
while (requestId != ourRequestId) {
data = new JSONObject(eventExpecter.blockForEventData());
requestId = data.getString("requestId");
requestId = data.getInt("requestId");
}
eventExpecter.unregisterListener();

View File

@ -29,7 +29,7 @@ import org.json.JSONObject;
*/
public class testDistribution extends ContentProviderTest {
private static final String MOCK_PACKAGE = "mock-package.zip";
private static final String PREF_REQUEST_ID = "testDistribution";
private static final int PREF_REQUEST_ID = 0x7357;
private Activity mActivity;
@ -86,28 +86,23 @@ public class testDistribution extends ContentProviderTest {
String prefTestInt = "distribution.test.int";
try {
JSONArray getPrefData = new JSONArray();
getPrefData.put(prefID);
getPrefData.put(prefAbout);
getPrefData.put(prefVersion);
getPrefData.put(prefTestBoolean);
getPrefData.put(prefTestString);
getPrefData.put(prefTestInt);
JSONObject message = new JSONObject();
message.put("requestId", PREF_REQUEST_ID);
message.put("preferences", getPrefData);
final String[] prefNames = { prefID,
prefAbout,
prefVersion,
prefTestBoolean,
prefTestString,
prefTestInt };
Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data");
mActions.sendGeckoEvent("Preferences:Get", message.toString());
mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames);
JSONObject data = null;
String requestId = "";
int requestId = -1;
// Wait until we get the correct "Preferences:Data" event
while (!requestId.equals(PREF_REQUEST_ID)) {
while (requestId != PREF_REQUEST_ID) {
data = new JSONObject(eventExpecter.blockForEventData());
requestId = data.getString("requestId");
requestId = data.getInt("requestId");
}
eventExpecter.unregisterListener();
@ -172,23 +167,18 @@ public class testDistribution extends ContentProviderTest {
mActions.sendGeckoEvent("Preferences:Set", jsonPref.toString());
// Wait for confirmation of the pref change.
JSONArray getPrefData = new JSONArray();
getPrefData.put(prefUseragentLocale);
JSONObject message = new JSONObject();
message.put("requestId", PREF_REQUEST_ID);
message.put("preferences", getPrefData);
final String[] prefNames = { prefUseragentLocale };
Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data");
mActions.sendGeckoEvent("Preferences:Get", message.toString());
mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames);
JSONObject data = null;
String requestId = "";
int requestId = -1;
// Wait until we get the correct "Preferences:Data" event
while (!requestId.equals(PREF_REQUEST_ID)) {
while (requestId != PREF_REQUEST_ID) {
data = new JSONObject(eventExpecter.blockForEventData());
requestId = data.getString("requestId");
requestId = data.getInt("requestId");
}
eventExpecter.unregisterListener();
@ -204,25 +194,18 @@ public class testDistribution extends ContentProviderTest {
String prefLocalizeableOverride = "distribution.test.localizeable-override";
try {
JSONArray getPrefData = new JSONArray();
getPrefData.put(prefAbout);
getPrefData.put(prefLocalizeable);
getPrefData.put(prefLocalizeableOverride);
JSONObject message = new JSONObject();
message.put("requestId", PREF_REQUEST_ID);
message.put("preferences", getPrefData);
final String[] prefNames = { prefAbout, prefLocalizeable, prefLocalizeableOverride };
Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data");
mActions.sendGeckoEvent("Preferences:Get", message.toString());
mActions.sendPreferencesGetEvent(PREF_REQUEST_ID, prefNames);
JSONObject data = null;
String requestId = "";
int requestId = -1;
// Wait until we get the correct "Preferences:Data" event
while (!requestId.equals(PREF_REQUEST_ID)) {
while (requestId != PREF_REQUEST_ID) {
data = new JSONObject(eventExpecter.blockForEventData());
requestId = data.getString("requestId");
requestId = data.getInt("requestId");
}
eventExpecter.unregisterListener();

View File

@ -81,22 +81,19 @@ public class testDoorHanger extends BaseTest {
boolean offlineAllowedByDefault = true;
try {
// Save offline-allow-by-default preferences first
JSONArray getPrefData = new JSONArray();
getPrefData.put("offline-apps.allow_by_default");
JSONObject message = new JSONObject();
message.put("requestId", "testDoorHanger");
message.put("preferences", getPrefData);
final String[] prefNames = { "offline-apps.allow_by_default" };
final int ourRequestId = 0x7357;
Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data");
mActions.sendGeckoEvent("Preferences:Get", message.toString());
mActions.sendPreferencesGetEvent(ourRequestId, prefNames);
JSONObject data = null;
String requestId = "";
int requestId = -1;
// Wait until we get the correct "Preferences:Data" event
while (!requestId.equals("testDoorHanger")) {
while (requestId != ourRequestId) {
data = new JSONObject(eventExpecter.blockForEventData());
requestId = data.getString("requestId");
requestId = data.getInt("requestId");
}
eventExpecter.unregisterListener();

View File

@ -126,21 +126,18 @@ public class testPasswordEncrypt extends BaseTest {
mActions.sendGeckoEvent("Preferences:Set", jsonPref.toString());
// Wait for confirmation of the pref change before proceeding with the test.
JSONArray getPrefData = new JSONArray();
getPrefData.put("privacy.masterpassword.enabled");
JSONObject message = new JSONObject();
message.put("requestId", "testPasswordEncrypt");
message.put("preferences", getPrefData);
final String[] prefNames = { "privacy.masterpassword.enabled" };
final int ourRequestId = 0x73577;
Actions.RepeatedEventExpecter eventExpecter = mActions.expectGeckoEvent("Preferences:Data");
mActions.sendGeckoEvent("Preferences:Get", message.toString());
mActions.sendPreferencesGetEvent(ourRequestId, prefNames);
JSONObject data = null;
String requestId = "";
int requestId = -1;
// Wait until we get the correct "Preferences:Data" event
while (!requestId.equals("testPasswordEncrypt")) {
while (requestId != ourRequestId) {
data = new JSONObject(eventExpecter.blockForEventData());
requestId = data.getString("requestId");
requestId = data.getInt("requestId");
}
} catch (Exception ex) {
mAsserter.ok(false, "exception in toggleMasterPassword", ex.toString());

View File

@ -16,7 +16,7 @@ import org.json.JSONObject;
*/
public class testPrefsObserver extends BaseTest {
private static final String PREF_TEST_PREF = "robocop.tests.dummy";
private static final String PREF_REQUEST_ID = "testPrefsObserver";
private static final int PREF_OBSERVE_REQUEST_ID = 0x7357;
private static final long PREF_TIMEOUT = 10000;
private Actions.RepeatedEventExpecter mExpecter;
@ -40,15 +40,15 @@ public class testPrefsObserver extends BaseTest {
mAsserter.dumpLog("Waiting to check pref");
JSONObject data = null;
String requestId = "";
int requestId = -1;
while (!requestId.equals(PREF_REQUEST_ID)) {
while (requestId != PREF_OBSERVE_REQUEST_ID) {
data = new JSONObject(mExpecter.blockForEventData());
if (!mExpecter.eventReceived()) {
mAsserter.ok(false, "Checking pref is correct value", "Didn't receive pref");
return;
}
requestId = data.getString("requestId");
requestId = data.getInt("requestId");
}
JSONObject pref = data.getJSONArray("preferences").getJSONObject(0);
@ -61,16 +61,16 @@ public class testPrefsObserver extends BaseTest {
mAsserter.dumpLog("Checking pref observer is removed");
JSONObject pref = null;
String requestId = "";
int requestId = -1;
while (!requestId.equals(PREF_REQUEST_ID)) {
while (requestId != PREF_OBSERVE_REQUEST_ID) {
String data = mExpecter.blockForEventDataWithTimeout(PREF_TIMEOUT);
if (data == null) {
mAsserter.ok(true, "Verifying pref is unobserved", "Didn't get unobserved pref");
return;
}
pref = new JSONObject(data);
requestId = pref.getString("requestId");
requestId = pref.getInt("requestId");
}
mAsserter.ok(false, "Received unobserved pref change", "");
@ -80,19 +80,14 @@ public class testPrefsObserver extends BaseTest {
mAsserter.dumpLog("Setting up pref observer");
// Setup the pref observer
JSONArray getPrefData = new JSONArray();
getPrefData.put(PREF_TEST_PREF);
JSONObject message = new JSONObject();
message.put("requestId", PREF_REQUEST_ID);
message.put("preferences", getPrefData);
mExpecter = mActions.expectGeckoEvent("Preferences:Data");
mActions.sendGeckoEvent("Preferences:Observe", message.toString());
mActions.sendPreferencesObserveEvent(PREF_OBSERVE_REQUEST_ID, new String[] { PREF_TEST_PREF });
}
public void removePrefObserver() {
mAsserter.dumpLog("Removing pref observer");
mActions.sendGeckoEvent("Preferences:RemoveObservers", PREF_REQUEST_ID);
mActions.sendPreferencesRemoveObserversEvent(PREF_OBSERVE_REQUEST_ID);
}
public void testPrefsObserver() {

View File

@ -279,10 +279,7 @@ var BrowserApp = {
Services.obs.addObserver(this, "Session:Stop", false);
Services.obs.addObserver(this, "SaveAs:PDF", false);
Services.obs.addObserver(this, "Browser:Quit", false);
Services.obs.addObserver(this, "Preferences:Get", false);
Services.obs.addObserver(this, "Preferences:Set", false);
Services.obs.addObserver(this, "Preferences:Observe", false);
Services.obs.addObserver(this, "Preferences:RemoveObservers", false);
Services.obs.addObserver(this, "ScrollTo:FocusedInput", false);
Services.obs.addObserver(this, "Sanitize:ClearData", false);
Services.obs.addObserver(this, "FullScreen:Exit", false);
@ -993,16 +990,17 @@ var BrowserApp = {
notifyPrefObservers: function(aPref) {
this._prefObservers[aPref].forEach(function(aRequestId) {
let request = { requestId : aRequestId,
preferences : [aPref] };
this.getPreferences(request);
this.getPreferences(aRequestId, [aPref], 1);
}, this);
},
getPreferences: function getPreferences(aPrefsRequest, aListen) {
handlePreferencesRequest: function handlePreferencesRequest(aRequestId,
aPrefNames,
aListen) {
let prefs = [];
for (let prefName of aPrefsRequest.preferences) {
for (let prefName of aPrefNames) {
let pref = {
name: prefName,
type: "",
@ -1011,9 +1009,9 @@ var BrowserApp = {
if (aListen) {
if (this._prefObservers[prefName])
this._prefObservers[prefName].push(aPrefsRequest.requestId);
this._prefObservers[prefName].push(aRequestId);
else
this._prefObservers[prefName] = [ aPrefsRequest.requestId ];
this._prefObservers[prefName] = [ aRequestId ];
Services.prefs.addObserver(prefName, this, false);
}
@ -1120,31 +1118,11 @@ var BrowserApp = {
sendMessageToJava({
type: "Preferences:Data",
requestId: aPrefsRequest.requestId, // opaque request identifier, can be any string/int/whatever
requestId: aRequestId, // opaque request identifier, can be any string/int/whatever
preferences: prefs
});
},
removePreferenceObservers: function removePreferenceObservers(aRequestId) {
let newPrefObservers = [];
for (let prefName in this._prefObservers) {
let requestIds = this._prefObservers[prefName];
// Remove the requestID from the preference handlers
let i = requestIds.indexOf(aRequestId);
if (i >= 0) {
requestIds.splice(i, 1);
}
// If there are no more request IDs, remove the observer
if (requestIds.length == 0) {
Services.prefs.removeObserver(prefName, this);
} else {
newPrefObservers[prefName] = requestIds;
}
}
this._prefObservers = newPrefObservers;
},
setPreferences: function setPreferences(aPref) {
let json = JSON.parse(aPref);
@ -1443,22 +1421,10 @@ var BrowserApp = {
this.saveAsPDF(browser);
break;
case "Preferences:Get":
this.getPreferences(JSON.parse(aData));
break;
case "Preferences:Set":
this.setPreferences(aData);
break;
case "Preferences:Observe":
this.getPreferences(JSON.parse(aData), true);
break;
case "Preferences:RemoveObservers":
this.removePreferenceObservers(aData);
break;
case "ScrollTo:FocusedInput":
// these messages come from a change in the viewable area and not user interaction
// we allow scrolling to the selected input, but not zooming the page
@ -1532,6 +1498,34 @@ var BrowserApp = {
return this.getTabForId(tabId);
},
getPreferences: function getPreferences(requestId, prefNames, count) {
this.handlePreferencesRequest(requestId, prefNames, false);
},
observePreferences: function observePreferences(requestId, prefNames, count) {
this.handlePreferencesRequest(requestId, prefNames, true);
},
removePreferenceObservers: function removePreferenceObservers(aRequestId) {
let newPrefObservers = [];
for (let prefName in this._prefObservers) {
let requestIds = this._prefObservers[prefName];
// Remove the requestID from the preference handlers
let i = requestIds.indexOf(aRequestId);
if (i >= 0) {
requestIds.splice(i, 1);
}
// If there are no more request IDs, remove the observer
if (requestIds.length == 0) {
Services.prefs.removeObserver(prefName, this);
} else {
newPrefObservers[prefName] = requestIds;
}
}
this._prefObservers = newPrefObservers;
},
// This method will print a list from fromIndex to toIndex, optionally
// selecting selIndex(if fromIndex<=selIndex<=toIndex)
showHistory: function(fromIndex, toIndex, selIndex) {

View File

@ -168,29 +168,19 @@ class TreeMetadataEmitter(LoggingMixin):
if program:
yield Program(sandbox, program, sandbox['CONFIG']['BIN_SUFFIX'])
for manifest in sandbox.get('XPCSHELL_TESTS_MANIFESTS', []):
yield XpcshellManifests(sandbox, manifest)
for ipdl in sandbox.get('IPDL_SOURCES', []):
yield IPDLFile(sandbox, ipdl)
for local_include in sandbox.get('LOCAL_INCLUDES', []):
yield LocalInclude(sandbox, local_include)
for webidl in sandbox.get('WEBIDL_FILES', []):
yield WebIDLFile(sandbox, webidl)
for webidl in sandbox.get('GENERATED_EVENTS_WEBIDL_FILES', []):
yield GeneratedEventWebIDLFile(sandbox, webidl)
for webidl in sandbox.get('TEST_WEBIDL_FILES', []):
yield TestWebIDLFile(sandbox, webidl)
for webidl in sandbox.get('PREPROCESSED_WEBIDL_FILES', []):
yield PreprocessedWebIDLFile(sandbox, webidl)
for webidl in sandbox.get('GENERATED_WEBIDL_FILES', []):
yield GeneratedWebIDLFile(sandbox, webidl)
simple_lists = [
('GENERATED_EVENTS_WEBIDL_FILES', GeneratedEventWebIDLFile),
('GENERATED_WEBIDL_FILES', GeneratedWebIDLFile),
('IPDL_SOURCES', IPDLFile),
('LOCAL_INCLUDES', LocalInclude),
('PREPROCESSED_WEBIDL_FILES', PreprocessedWebIDLFile),
('TEST_WEBIDL_FILES', TestWebIDLFile),
('WEBIDL_FILES', WebIDLFile),
('XPCSHELL_TESTS_MANIFESTS', XpcshellManifests),
]
for sandbox_var, klass in simple_lists:
for name in sandbox.get(sandbox_var, []):
yield klass(sandbox, name)
def _emit_directory_traversal_from_sandbox(self, sandbox):
o = DirectoryTraversal(sandbox)

View File

@ -16,7 +16,7 @@
#include "nsComponentManagerUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsICertOverrideService.h"
#include "nsIPrefService.h"
#include "mozilla/Preferences.h"
#ifndef MOZ_DISABLE_CRYPTOLEGACY
#include "nsIDOMNode.h"
@ -871,9 +871,9 @@ static CipherPref CipherPrefs[] = {
};
static void
setNonPkixOcspEnabled(int32_t ocspEnabled, nsIPrefBranch * pref)
setNonPkixOcspEnabled(int32_t ocspEnabled)
{
// Note: this preference is numeric vs bolean because previously we
// Note: this preference is numeric vs boolean because previously we
// supported more than two options.
if (!ocspEnabled) {
CERT_DisableOCSPChecking(CERT_GetDefaultCertDB());
@ -886,7 +886,7 @@ setNonPkixOcspEnabled(int32_t ocspEnabled, nsIPrefBranch * pref)
#define CRL_DOWNLOAD_DEFAULT false
#define OCSP_ENABLED_DEFAULT 1
#define OCSP_REQUIRED_DEFAULT 0
#define OCSP_REQUIRED_DEFAULT false
#define FRESH_REVOCATION_REQUIRED_DEFAULT false
#define MISSING_CERT_DOWNLOAD_DEFAULT false
#define FIRST_REVO_METHOD_DEFAULT "ocsp"
@ -894,56 +894,39 @@ setNonPkixOcspEnabled(int32_t ocspEnabled, nsIPrefBranch * pref)
#define OCSP_STAPLING_ENABLED_DEFAULT true
// Caller must hold a lock on nsNSSComponent::mutex when calling this function
void nsNSSComponent::setValidationOptions(nsIPrefBranch * pref)
void nsNSSComponent::setValidationOptions()
{
nsNSSShutDownPreventionLock locker;
nsresult rv;
bool crlDownloading;
rv = pref->GetBoolPref("security.CRL_download.enabled", &crlDownloading);
if (NS_FAILED(rv))
crlDownloading = CRL_DOWNLOAD_DEFAULT;
int32_t ocspEnabled;
rv = pref->GetIntPref("security.OCSP.enabled", &ocspEnabled);
// 0 = disabled, 1 = enabled,
// 2 = enabled with given default responder
if (NS_FAILED(rv))
ocspEnabled = OCSP_ENABLED_DEFAULT;
bool crlDownloading = Preferences::GetBool("security.CRL_download.enabled",
CRL_DOWNLOAD_DEFAULT);
// 0 = disabled, 1 = enabled
int32_t ocspEnabled = Preferences::GetInt("security.OCSP.enabled",
OCSP_ENABLED_DEFAULT);
bool ocspRequired;
rv = pref->GetBoolPref("security.OCSP.require", &ocspRequired);
if (NS_FAILED(rv))
ocspRequired = OCSP_REQUIRED_DEFAULT;
bool ocspRequired = Preferences::GetBool("security.OCSP.require",
OCSP_REQUIRED_DEFAULT);
bool anyFreshRequired = Preferences::GetBool("security.fresh_revocation_info.require",
FRESH_REVOCATION_REQUIRED_DEFAULT);
bool aiaDownloadEnabled = Preferences::GetBool("security.missing_cert_download.enabled",
MISSING_CERT_DOWNLOAD_DEFAULT);
bool anyFreshRequired;
rv = pref->GetBoolPref("security.fresh_revocation_info.require", &anyFreshRequired);
if (NS_FAILED(rv))
anyFreshRequired = FRESH_REVOCATION_REQUIRED_DEFAULT;
bool aiaDownloadEnabled;
rv = pref->GetBoolPref("security.missing_cert_download.enabled", &aiaDownloadEnabled);
if (NS_FAILED(rv))
aiaDownloadEnabled = MISSING_CERT_DOWNLOAD_DEFAULT;
nsCString firstNetworkRevo;
rv = pref->GetCharPref("security.first_network_revocation_method", getter_Copies(firstNetworkRevo));
if (NS_FAILED(rv))
nsCString firstNetworkRevo =
Preferences::GetCString("security.first_network_revocation_method");
if (firstNetworkRevo.IsEmpty()) {
firstNetworkRevo = FIRST_REVO_METHOD_DEFAULT;
bool ocspStaplingEnabled;
rv = pref->GetBoolPref("security.ssl.enable_ocsp_stapling", &ocspStaplingEnabled);
if (NS_FAILED(rv)) {
ocspStaplingEnabled = OCSP_STAPLING_ENABLED_DEFAULT;
}
bool ocspStaplingEnabled = Preferences::GetBool("security.ssl.enable_ocsp_stapling",
OCSP_STAPLING_ENABLED_DEFAULT);
if (!ocspEnabled) {
ocspStaplingEnabled = false;
}
PublicSSLState()->SetOCSPStaplingEnabled(ocspStaplingEnabled);
PrivateSSLState()->SetOCSPStaplingEnabled(ocspStaplingEnabled);
setNonPkixOcspEnabled(ocspEnabled, pref);
setNonPkixOcspEnabled(ocspEnabled);
CERT_SetOCSPFailureMode( ocspRequired ?
ocspMode_FailureIsVerificationFailure
: ocspMode_FailureIsNotAVerificationFailure);
@ -971,16 +954,16 @@ void nsNSSComponent::setValidationOptions(nsIPrefBranch * pref)
// Enable the TLS versions given in the prefs, defaulting to SSL 3.0 and
// TLS 1.0 when the prefs aren't set or when they are set to invalid values.
nsresult
nsNSSComponent::setEnabledTLSVersions(nsIPrefBranch * prefBranch)
nsNSSComponent::setEnabledTLSVersions()
{
// keep these values in sync with security-prefs.js and firefox.js
static const int32_t PSM_DEFAULT_MIN_TLS_VERSION = 0;
static const int32_t PSM_DEFAULT_MAX_TLS_VERSION = 1;
int32_t minVersion = PSM_DEFAULT_MIN_TLS_VERSION;
int32_t maxVersion = PSM_DEFAULT_MAX_TLS_VERSION;
mPrefBranch->GetIntPref("security.tls.version.min", &minVersion);
mPrefBranch->GetIntPref("security.tls.version.max", &maxVersion);
int32_t minVersion = Preferences::GetInt("security.tls.version.min",
PSM_DEFAULT_MIN_TLS_VERSION);
int32_t maxVersion = Preferences::GetInt("security.tls.version.max",
PSM_DEFAULT_MAX_TLS_VERSION);
// 0 means SSL 3.0, 1 means TLS 1.0, 2 means TLS 1.1, etc.
minVersion += SSL_LIBRARY_VERSION_3_0;
@ -1016,13 +999,11 @@ NS_IMETHODIMP
nsNSSComponent::SkipOcspOff()
{
nsNSSShutDownPreventionLock locker;
int32_t ocspEnabled;
if (NS_FAILED(mPrefBranch->GetIntPref("security.OCSP.enabled", &ocspEnabled)))
ocspEnabled = OCSP_ENABLED_DEFAULT;
// 0 = disabled, 1 = enabled,
// 2 = enabled with given default responder
setNonPkixOcspEnabled(ocspEnabled, mPrefBranch);
// 0 = disabled, 1 = enabled
int32_t ocspEnabled = Preferences::GetInt("security.OCSP.enabled",
OCSP_ENABLED_DEFAULT);
setNonPkixOcspEnabled(ocspEnabled);
if (ocspEnabled)
SSL_ClearSessionCache();
@ -1050,6 +1031,14 @@ static void configureMD5(bool enabled)
}
}
static const bool SUPPRESS_WARNING_PREF_DEFAULT = false;
static const bool MD5_ENABLED_DEFAULT = false;
static const bool TLS_SESSION_TICKETS_ENABLED_DEFAULT = true;
static const bool REQUIRE_SAFE_NEGOTIATION_DEFAULT = false;
static const bool ALLOW_UNRESTRICTED_RENEGO_DEFAULT = false;
static const bool FALSE_START_ENABLED_DEFAULT = true;
static const bool CIPHER_ENABLED_DEFAULT = false;
nsresult
nsNSSComponent::InitializeNSS(bool showWarningBox)
{
@ -1119,17 +1108,13 @@ nsNSSComponent::InitializeNSS(bool showWarningBox)
}
#ifndef NSS_NO_LIBPKIX
rv = mPrefBranch->GetBoolPref("security.use_libpkix_verification", &globalConstFlagUsePKIXVerification);
if (NS_FAILED(rv))
globalConstFlagUsePKIXVerification = USE_NSS_LIBPKIX_DEFAULT;
globalConstFlagUsePKIXVerification =
Preferences::GetBool("security.use_libpkix_verification", USE_NSS_LIBPKIX_DEFAULT);
#endif
bool supress_warning_preference = false;
rv = mPrefBranch->GetBoolPref("security.suppress_nss_rw_impossible_warning", &supress_warning_preference);
if (NS_FAILED(rv)) {
supress_warning_preference = false;
}
bool suppressWarningPref =
Preferences::GetBool("security.suppress_nss_rw_impossible_warning",
SUPPRESS_WARNING_PREF_DEFAULT);
// init phase 2, init calls to NSS library
@ -1155,7 +1140,7 @@ nsNSSComponent::InitializeNSS(bool showWarningBox)
if (init_rv != SECSuccess) {
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("can not init NSS r/w in %s\n", profileStr.get()));
if (supress_warning_preference) {
if (suppressWarningPref) {
which_nss_problem = problem_none;
}
else {
@ -1193,38 +1178,44 @@ nsNSSComponent::InitializeNSS(bool showWarningBox)
SharedSSLState::GlobalInit();
// Register an observer so we can inform NSS when these prefs change
mPrefBranch->AddObserver("security.", this, false);
Preferences::AddStrongObserver(this, "security.");
SSL_OptionSetDefault(SSL_ENABLE_SSL2, false);
SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, false);
rv = setEnabledTLSVersions(mPrefBranch);
rv = setEnabledTLSVersions();
if (NS_FAILED(rv)) {
nsPSMInitPanic::SetPanic();
return NS_ERROR_UNEXPECTED;
}
bool enabled = true; // XXX: see bug 733644
mPrefBranch->GetBoolPref("security.enable_md5_signatures", &enabled);
configureMD5(enabled);
bool md5Enabled = Preferences::GetBool("security.enable_md5_signatures",
MD5_ENABLED_DEFAULT);
configureMD5(md5Enabled);
// Configure TLS session tickets
mPrefBranch->GetBoolPref("security.enable_tls_session_tickets", &enabled);
SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, enabled);
bool tlsSessionTicketsEnabled =
Preferences::GetBool("security.enable_tls_session_tickets",
TLS_SESSION_TICKETS_ENABLED_DEFAULT);
SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, tlsSessionTicketsEnabled);
mPrefBranch->GetBoolPref("security.ssl.require_safe_negotiation", &enabled);
SSL_OptionSetDefault(SSL_REQUIRE_SAFE_NEGOTIATION, enabled);
bool requireSafeNegotiation =
Preferences::GetBool("security.ssl.require_safe_negotiation",
REQUIRE_SAFE_NEGOTIATION_DEFAULT);
SSL_OptionSetDefault(SSL_REQUIRE_SAFE_NEGOTIATION, requireSafeNegotiation);
mPrefBranch->GetBoolPref(
"security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref",
&enabled);
SSL_OptionSetDefault(SSL_ENABLE_RENEGOTIATION,
enabled ? SSL_RENEGOTIATE_UNRESTRICTED : SSL_RENEGOTIATE_REQUIRES_XTN);
bool allowUnrestrictedRenego =
Preferences::GetBool("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref",
ALLOW_UNRESTRICTED_RENEGO_DEFAULT);
SSL_OptionSetDefault(SSL_ENABLE_RENEGOTIATION,
allowUnrestrictedRenego ?
SSL_RENEGOTIATE_UNRESTRICTED :
SSL_RENEGOTIATE_REQUIRES_XTN);
#ifdef SSL_ENABLE_FALSE_START // Requires NSS 3.12.8
mPrefBranch->GetBoolPref("security.ssl.enable_false_start", &enabled);
SSL_OptionSetDefault(SSL_ENABLE_FALSE_START, enabled);
bool falseStartEnabled = Preferences::GetBool("security.ssl.enable_false_start",
FALSE_START_ENABLED_DEFAULT);
SSL_OptionSetDefault(SSL_ENABLE_FALSE_START, falseStartEnabled);
#endif
// Disable any ciphers that NSS might have enabled by default
@ -1234,13 +1225,11 @@ nsNSSComponent::InitializeNSS(bool showWarningBox)
SSL_CipherPrefSetDefault(cipher_id, false);
}
bool cipherEnabled;
// Now only set SSL/TLS ciphers we knew about at compile time
for (CipherPref* cp = CipherPrefs; cp->pref; ++cp) {
rv = mPrefBranch->GetBoolPref(cp->pref, &enabled);
if (NS_FAILED(rv))
enabled = false;
SSL_CipherPrefSetDefault(cp->id, enabled);
cipherEnabled = Preferences::GetBool(cp->pref, CIPHER_ENABLED_DEFAULT);
SSL_CipherPrefSetDefault(cp->id, cipherEnabled);
}
// Enable ciphers for PKCS#12
@ -1254,7 +1243,7 @@ nsNSSComponent::InitializeNSS(bool showWarningBox)
PORT_SetUCS2_ASCIIConversionFunction(pip_ucs2_ascii_conversion_fn);
// dynamic options from prefs
setValidationOptions(mPrefBranch);
setValidationOptions();
mHttpForNSS.initTable();
mHttpForNSS.registerHttpClient();
@ -1300,9 +1289,7 @@ nsNSSComponent::ShutdownNSS()
PK11_SetPasswordFunc((PK11PasswordFunc)nullptr);
mHttpForNSS.unregisterHttpClient();
if (mPrefBranch) {
mPrefBranch->RemoveObserver("security.", this);
}
Preferences::RemoveObserver(this, "security.");
#ifndef MOZ_DISABLE_CRYPTOLEGACY
ShutdownSmartCardThreads();
@ -1323,7 +1310,9 @@ nsNSSComponent::ShutdownNSS()
}
}
}
static const bool SEND_LM_DEFAULT = false;
NS_IMETHODIMP
nsNSSComponent::Init()
{
@ -1359,13 +1348,8 @@ nsNSSComponent::Init()
getter_Copies(result));
}
if (!mPrefBranch) {
mPrefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
NS_ASSERTION(mPrefBranch, "Unable to get pref service");
}
bool sendLM = false;
mPrefBranch->GetBoolPref("network.ntlm.send-lm-response", &sendLM);
bool sendLM = Preferences::GetBool("network.ntlm.send-lm-response",
SEND_LM_DEFAULT);
nsNTLMAuthModule::SetSendLM(sendLM);
// Do that before NSS init, to make sure we won't get unloaded.
@ -1635,31 +1619,40 @@ nsNSSComponent::Observe(nsISupports *aSubject, const char *aTopic,
else if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
nsNSSShutDownPreventionLock locker;
bool clearSessionCache = false;
bool enabled;
NS_ConvertUTF16toUTF8 prefName(someData);
if (prefName.Equals("security.tls.version.min") ||
prefName.Equals("security.tls.version.max")) {
(void) setEnabledTLSVersions(mPrefBranch);
(void) setEnabledTLSVersions();
clearSessionCache = true;
} else if (prefName.Equals("security.enable_md5_signatures")) {
mPrefBranch->GetBoolPref("security.enable_md5_signatures", &enabled);
configureMD5(enabled);
bool md5Enabled = Preferences::GetBool("security.enable_md5_signatures",
MD5_ENABLED_DEFAULT);
configureMD5(md5Enabled);
clearSessionCache = true;
} else if (prefName.Equals("security.enable_tls_session_tickets")) {
mPrefBranch->GetBoolPref("security.enable_tls_session_tickets", &enabled);
SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, enabled);
bool tlsSessionTicketsEnabled =
Preferences::GetBool("security.enable_tls_session_tickets",
TLS_SESSION_TICKETS_ENABLED_DEFAULT);
SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, tlsSessionTicketsEnabled);
} else if (prefName.Equals("security.ssl.require_safe_negotiation")) {
mPrefBranch->GetBoolPref("security.ssl.require_safe_negotiation", &enabled);
SSL_OptionSetDefault(SSL_REQUIRE_SAFE_NEGOTIATION, enabled);
bool requireSafeNegotiation =
Preferences::GetBool("security.ssl.require_safe_negotiation",
REQUIRE_SAFE_NEGOTIATION_DEFAULT);
SSL_OptionSetDefault(SSL_REQUIRE_SAFE_NEGOTIATION, requireSafeNegotiation);
} else if (prefName.Equals("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref")) {
mPrefBranch->GetBoolPref("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref", &enabled);
SSL_OptionSetDefault(SSL_ENABLE_RENEGOTIATION,
enabled ? SSL_RENEGOTIATE_UNRESTRICTED : SSL_RENEGOTIATE_REQUIRES_XTN);
bool allowUnrestrictedRenego =
Preferences::GetBool("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref",
ALLOW_UNRESTRICTED_RENEGO_DEFAULT);
SSL_OptionSetDefault(SSL_ENABLE_RENEGOTIATION,
allowUnrestrictedRenego ?
SSL_RENEGOTIATE_UNRESTRICTED :
SSL_RENEGOTIATE_REQUIRES_XTN);
#ifdef SSL_ENABLE_FALSE_START // Requires NSS 3.12.8
} else if (prefName.Equals("security.ssl.enable_false_start")) {
mPrefBranch->GetBoolPref("security.ssl.enable_false_start", &enabled);
SSL_OptionSetDefault(SSL_ENABLE_FALSE_START, enabled);
bool falseStartEnabled = Preferences::GetBool("security.ssl.enable_false_start",
FALSE_START_ENABLED_DEFAULT);
SSL_OptionSetDefault(SSL_ENABLE_FALSE_START, falseStartEnabled);
#endif
} else if (prefName.Equals("security.OCSP.enabled")
|| prefName.Equals("security.CRL_download.enabled")
@ -1669,17 +1662,18 @@ nsNSSComponent::Observe(nsISupports *aSubject, const char *aTopic,
|| prefName.Equals("security.OCSP.require")
|| prefName.Equals("security.ssl.enable_ocsp_stapling")) {
MutexAutoLock lock(mutex);
setValidationOptions(mPrefBranch);
setValidationOptions();
} else if (prefName.Equals("network.ntlm.send-lm-response")) {
bool sendLM = false;
mPrefBranch->GetBoolPref("network.ntlm.send-lm-response", &sendLM);
bool sendLM = Preferences::GetBool("network.ntlm.send-lm-response",
SEND_LM_DEFAULT);
nsNTLMAuthModule::SetSendLM(sendLM);
} else {
/* Look through the cipher table and set according to pref setting */
bool cipherEnabled;
for (CipherPref* cp = CipherPrefs; cp->pref; ++cp) {
if (prefName.Equals(cp->pref)) {
mPrefBranch->GetBoolPref(cp->pref, &enabled);
SSL_CipherPrefSetDefault(cp->id, enabled);
cipherEnabled = Preferences::GetBool(cp->pref, CIPHER_ENABLED_DEFAULT);
SSL_CipherPrefSetDefault(cp->id, cipherEnabled);
clearSessionCache = true;
break;
}

View File

@ -13,7 +13,6 @@
#include "nsISignatureVerifier.h"
#include "nsIEntropyCollector.h"
#include "nsIStringBundle.h"
#include "nsIPrefBranch.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#ifndef MOZ_DISABLE_CRYPTOLEGACY
@ -185,8 +184,8 @@ private:
void InstallLoadableRoots();
void UnloadLoadableRoots();
void CleanupIdentityInfo();
void setValidationOptions(nsIPrefBranch * pref);
nsresult setEnabledTLSVersions(nsIPrefBranch * pref);
void setValidationOptions();
nsresult setEnabledTLSVersions();
nsresult InitializePIPNSSBundle();
nsresult ConfigureInternalPKCS11Token();
nsresult RegisterObservers();
@ -203,7 +202,6 @@ private:
nsCOMPtr<nsIStringBundle> mPIPNSSBundle;
nsCOMPtr<nsIStringBundle> mNSSErrorsBundle;
nsCOMPtr<nsIPrefBranch> mPrefBranch;
bool mNSSInitialized;
bool mObserversRegistered;
static int mInstanceCount;

Some files were not shown because too many files have changed in this diff Show More