Merge m-c to b2g-inbound a=merge

This commit is contained in:
Wes Kocher 2014-10-20 18:59:53 -07:00
commit 371e52348d
123 changed files with 2238 additions and 2106 deletions

View File

@ -7,15 +7,15 @@
/******************************************************************************
All gestures have the following pathways when being resolved(v)/rejected(x):
Tap -> DoubleTap (v)
Tap -> DoubleTap (x)
-> Dwell (x)
-> Swipe (x)
AndroidTap -> TripleTap (v)
AndroidTap -> TripleTap (x)
-> TapHold (x)
-> Swipe (x)
DoubleTap -> TripleTap (v)
DoubleTap -> TripleTap (x)
-> TapHold (x)
-> Explore (x)
@ -360,6 +360,7 @@ Gesture.prototype = {
* Clear the existing timer.
*/
clearTimer: function Gesture_clearTimer() {
Logger.gesture('clearTimeout', this.type);
clearTimeout(this._timer);
delete this._timer;
},
@ -370,9 +371,11 @@ Gesture.prototype = {
* started the gesture resolution sequence.
*/
startTimer: function Gesture_startTimer(aTimeStamp) {
Logger.gesture('startTimer', this.type);
this.clearTimer();
let delay = this._getDelay(aTimeStamp);
let handler = () => {
Logger.gesture('timer handler');
delete this._timer;
if (!this._inProgress) {
this._deferred.reject();
@ -672,14 +675,17 @@ DoubleTapHoldEnd.prototype.type = 'doubletapholdend';
* the gesture resolution sequence.
* @param {Object} aPoints An existing set of points (from previous events).
* @param {?String} aLastEvent Last pointer event type.
* @param {Function} aRejectTo A constructor for the next gesture to reject to
* in case no pointermove or pointerup happens within the
* @param {Function} aRejectToOnWait A constructor for the next gesture to
* reject to in case no pointermove or pointerup happens within the
* GestureSettings.dwellThreshold.
* @param {Function} aRejectToOnPointerDown A constructor for the gesture to
* reject to if a finger comes down immediately after the tap.
* @param {Function} aTravelTo An optional constuctor for the next gesture to
* reject to in case the the TravelGesture test fails.
*/
function TapGesture(aTimeStamp, aPoints, aLastEvent, aRejectTo, aTravelTo) {
this._rejectToOnWait = aRejectTo;
function TapGesture(aTimeStamp, aPoints, aLastEvent, aRejectToOnWait, aTravelTo, aRejectToOnPointerDown) {
this._rejectToOnWait = aRejectToOnWait;
this._rejectToOnPointerDown = aRejectToOnPointerDown;
// If the pointer travels, reject to aTravelTo.
TravelGesture.call(this, aTimeStamp, aPoints, aLastEvent, aTravelTo,
TAP_MAX_RADIUS);
@ -693,6 +699,35 @@ TapGesture.prototype._getDelay = function TapGesture__getDelay() {
return GestureSettings.dwellThreshold;
};
TapGesture.prototype.pointerup = function TapGesture_pointerup(aPoints) {
if (this._rejectToOnPointerDown) {
let complete = this._update(aPoints, 'pointerup', false, true);
if (complete) {
this.clearTimer();
if (GestureSettings.maxConsecutiveGestureDelay) {
this._pointerUpTimer = setTimeout(() => {
delete this._pointerUpTimer;
this._deferred.resolve();
}, GestureSettings.maxConsecutiveGestureDelay);
} else {
this._deferred.resolve();
}
}
} else {
TravelGesture.prototype.pointerup.call(this, aPoints);
}
};
TapGesture.prototype.pointerdown = function TapGesture_pointerdown(aPoints, aTimeStamp) {
TravelGesture.prototype.pointerdown.call(this, aPoints, aTimeStamp);
if (this._pointerUpTimer) {
clearTimeout(this._pointerUpTimer);
delete this._pointerUpTimer;
this._deferred.reject(this._rejectToOnPointerDown);
}
};
/**
* Tap gesture.
* @param {Number} aTimeStamp An original pointer event's timeStamp that started
@ -702,12 +737,11 @@ TapGesture.prototype._getDelay = function TapGesture__getDelay() {
*/
function Tap(aTimeStamp, aPoints, aLastEvent) {
// If the pointer travels, reject to Swipe.
TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, Dwell, Swipe);
TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, Dwell, Swipe, DoubleTap);
}
Tap.prototype = Object.create(TapGesture.prototype);
Tap.prototype.type = 'tap';
Tap.prototype.resolveTo = DoubleTap;
/**
* Tap (multi) gesture on Android.
@ -719,12 +753,11 @@ Tap.prototype.resolveTo = DoubleTap;
function AndroidTap(aTimeStamp, aPoints, aLastEvent) {
// If the pointer travels, reject to Swipe. On dwell threshold reject to
// TapHold.
TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, TapHold, Swipe);
TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, TapHold, Swipe, TripleTap);
}
AndroidTap.prototype = Object.create(TapGesture.prototype);
// Android double taps are translated to single taps.
AndroidTap.prototype.type = 'doubletap';
AndroidTap.prototype.resolveTo = TripleTap;
/**
* Clear the pointerup handler timer in case of the 3 pointer swipe.
@ -767,12 +800,12 @@ AndroidTap.prototype.pointerup = function AndroidTap_pointerup(aPoints) {
* @param {?String} aLastEvent Last pointer event type.
*/
function DoubleTap(aTimeStamp, aPoints, aLastEvent) {
TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, TapHold);
this._inProgress = true;
TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, TapHold, null, TripleTap);
}
DoubleTap.prototype = Object.create(TapGesture.prototype);
DoubleTap.prototype.type = 'doubletap';
DoubleTap.prototype.resolveTo = TripleTap;
/**
* Triple Tap gesture.
@ -782,6 +815,7 @@ DoubleTap.prototype.resolveTo = TripleTap;
* @param {?String} aLastEvent Last pointer event type.
*/
function TripleTap(aTimeStamp, aPoints, aLastEvent) {
this._inProgress = true;
TapGesture.call(this, aTimeStamp, aPoints, aLastEvent, DoubleTapHold);
}

View File

@ -102,6 +102,8 @@ var eventMap = {
var originalDwellThreshold = GestureSettings.dwellThreshold;
var originalSwipeMaxDuration = GestureSettings.swipeMaxDuration;
var originalConsecutiveGestureDelay =
GestureSettings.maxConsecutiveGestureDelay;
/**
* Attach a listener for the mozAccessFuGesture event that tests its
@ -113,11 +115,13 @@ function testMozAccessFuGesture(aExpectedGestures) {
var types = aExpectedGestures;
function handleGesture(aEvent) {
if (aEvent.detail.type !== types[0].type) {
info('Got ' + aEvent.detail.type + ' waiting for ' + types[0].type);
// The is not the event of interest.
return;
}
is(!!aEvent.detail.edge, !!types[0].edge);
ok(true, 'Received correct mozAccessFuGesture: ' + types.shift() + '.');
ok(true, 'Received correct mozAccessFuGesture: ' +
JSON.stringify(types.shift()) + '.');
if (types.length === 0) {
win.removeEventListener('mozAccessFuGesture', handleGesture);
if (AccessFuTest.sequenceCleanup) {
@ -173,6 +177,9 @@ AccessFuTest.addSequence = function AccessFuTest_addSequence(aSequence) {
};
var timeStamp = Date.now();
resetTimers();
GestureSettings.maxConsecutiveGestureDelay =
aEvent.removeConsecutiveGestureDelay ?
0 : originalConsecutiveGestureDelay;
GestureTracker.handle(event, timeStamp);
setTimers(timeStamp, aEvent.removeDwellThreshold,
aEvent.removeSwipeMaxDuration);

View File

@ -2,7 +2,8 @@
{
"events": [
{"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
{"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
{"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}],
"removeConsecutiveGestureDelay": true }
],
"expectedGestures": [{ "type": "tap" }]
},
@ -11,7 +12,8 @@
{"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
{"type": "pointermove",
"points": [{"x": 1.03, "y": 1.03, "identifier": 1}]},
{"type": "pointerup", "points": [{"x": 1.03, "y": 1.03, "identifier": 1}]}
{"type": "pointerup", "points": [{"x": 1.03, "y": 1.03, "identifier": 1}],
"removeConsecutiveGestureDelay": true }
],
"expectedGestures": [{ "type": "tap" }]
},
@ -34,9 +36,10 @@
{"type": "pointermove",
"points": [{"x": 0.97, "y": 1.01, "identifier": 1}]},
{"type": "pointerup",
"points": [{"x": 0.97, "y": 1.01, "identifier": 1}]}
"points": [{"x": 0.97, "y": 1.01, "identifier": 1}],
"removeConsecutiveGestureDelay": true }
],
"expectedGestures": [{ "type": "tap" }, { "type": "doubletap" }]
"expectedGestures": [{ "type": "doubletap" }]
},
{
"events": [
@ -45,9 +48,10 @@
{"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
{"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
{"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
{"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
{"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}],
"removeConsecutiveGestureDelay": true }
],
"expectedGestures": [{ "type": "tap" }, { "type": "doubletap" }, { "type": "tripletap" }]
"expectedGestures": [{ "type": "tripletap" }]
},
{
"events": [
@ -59,8 +63,8 @@
"removeDwellThreshold": true},
{"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
],
"expectedGestures": [{ "type": "tap" }, { "type": "doubletap" },
{ "type": "doubletaphold" }, { "type": "doubletapholdend" }]
"expectedGestures": [{ "type": "doubletaphold" },
{ "type": "doubletapholdend" }]
},
{
"events": [
@ -70,8 +74,7 @@
"removeDwellThreshold": true},
{"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
],
"expectedGestures": [{ "type": "tap" }, { "type": "taphold" },
{ "type": "tapholdend" }]
"expectedGestures": [{ "type": "taphold" }, { "type": "tapholdend" }]
},
{
"events": [

View File

@ -31,7 +31,7 @@
gestures.forEach(AccessFuTest.addSequence);
AccessFuTest.addFunc(stopGestureTracker);
AccessFuTest.waitForExplicitFinish();
Logger.logLevel = Logger.DEBUG;
Logger.logLevel = Logger.GESTURE;
AccessFuTest.runTests();
});
}

View File

@ -565,7 +565,11 @@ InitXPCOMGlue(const char *argv0, nsIFile **xreDirectory)
return rv;
}
#ifndef MOZ_METRO
// This will set this thread as the main thread, which in metro land is
// wrong. We initialize this later from the right thread in nsAppRunner.
NS_LogInit();
#endif
// chop XPCOM_DLL off exePath
*lastSlash = '\0';

View File

@ -2,7 +2,12 @@
# 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 MOZ_WINCONSOLE
MOZ_WINCONSOLE = 0
endif
include $(topsrcdir)/config/config.mk
include $(topsrcdir)/config/rules.mk
DIST_PROGRAM = CommandExecuteHandler$(BIN_SUFFIX)

View File

@ -23,6 +23,7 @@ class nsIPrincipal;
namespace mozilla {
class DOMMediaStream;
namespace dom {
class FileImpl;
class MediaSource;
}
}
@ -118,6 +119,9 @@ inline bool IsFontTableURI(nsIURI* aUri)
return NS_SUCCEEDED(aUri->SchemeIs(FONTTABLEURI_SCHEME, &isFont)) && isFont;
}
extern nsresult
NS_GetBlobForBlobURI(nsIURI* aURI, mozilla::dom::FileImpl** aBlob);
extern nsresult
NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream);

View File

@ -588,19 +588,32 @@ nsFontTableProtocolHandler::GetScheme(nsACString &result)
}
nsresult
NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream)
NS_GetBlobForBlobURI(nsIURI* aURI, FileImpl** aBlob)
{
NS_ASSERTION(IsBlobURI(aURI), "Only call this with blob URIs");
*aStream = nullptr;
*aBlob = nullptr;
nsCOMPtr<PIFileImpl> blobImpl = do_QueryInterface(GetDataObject(aURI));
if (!blobImpl) {
return NS_ERROR_DOM_BAD_URI;
}
FileImpl* blob = static_cast<FileImpl*>(blobImpl.get());
return blob->GetInternalStream(aStream);
nsRefPtr<FileImpl> blob = static_cast<FileImpl*>(blobImpl.get());
blob.forget(aBlob);
return NS_OK;
}
nsresult
NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream)
{
nsRefPtr<FileImpl> blobImpl;
nsresult rv = NS_GetBlobForBlobURI(aURI, getter_AddRefs(blobImpl));
if (NS_FAILED(rv)) {
return rv;
}
return blobImpl->GetInternalStream(aStream);
}
nsresult

View File

@ -206,6 +206,7 @@
#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/BrowserElementDictionariesBinding.h"
#include "mozilla/dom/Console.h"
#include "mozilla/dom/Fetch.h"
#include "mozilla/dom/FunctionBinding.h"
#include "mozilla/dom/HashChangeEvent.h"
#include "mozilla/dom/MozSelfSupportBinding.h"
@ -6368,8 +6369,7 @@ already_AddRefed<Promise>
nsGlobalWindow::Fetch(const RequestOrScalarValueString& aInput,
const RequestInit& aInit, ErrorResult& aRv)
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return nullptr;
return FetchRequest(this, aInput, aInit, aRv);
}
void

View File

@ -173,6 +173,7 @@ static uint32_t sPendingLoadCount;
static bool sLoadingInProgress;
static uint32_t sCCollectedWaitingForGC;
static uint32_t sCCollectedZonesWaitingForGC;
static uint32_t sLikelyShortLivingObjectsNeedingGC;
static bool sPostGCEventsToConsole;
static bool sPostGCEventsToObserver;
@ -246,6 +247,7 @@ static bool
NeedsGCAfterCC()
{
return sCCollectedWaitingForGC > 250 ||
sCCollectedZonesWaitingForGC > 0 ||
sLikelyShortLivingObjectsNeedingGC > 2500 ||
sNeedsGCAfterCC;
}
@ -1806,7 +1808,8 @@ nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
// CCs, this won't happen.
gCCStats.FinishCycleCollectionSlice();
sCCollectedWaitingForGC += aResults.mFreedRefCounted + aResults.mFreedGCed;
sCCollectedWaitingForGC += aResults.mFreedGCed;
sCCollectedZonesWaitingForGC += aResults.mFreedJSZones;
TimeStamp endCCTimeStamp = TimeStamp::Now();
uint32_t ccNowDuration = TimeBetween(gCCStats.mBeginTime, endCCTimeStamp);
@ -1855,7 +1858,7 @@ nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
}
NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
MOZ_UTF16("CC(T+%.1f) max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu waiting for GC)%s\n")
MOZ_UTF16("CC(T+%.1f) max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n")
MOZ_UTF16("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu"));
nsString msg;
msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
@ -1863,7 +1866,7 @@ nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
aResults.mNumSlices, gCCStats.mSuspected,
aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
aResults.mFreedRefCounted, aResults.mFreedGCed,
sCCollectedWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
sCCollectedWaitingForGC, sCCollectedZonesWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
gcMsg.get(),
sForgetSkippableBeforeCC,
minForgetSkippableTime / PR_USEC_PER_MSEC,
@ -1895,6 +1898,7 @@ nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
MOZ_UTF16("\"RCed\": %lu, ")
MOZ_UTF16("\"GCed\": %lu }, ")
MOZ_UTF16("\"waiting_for_gc\": %lu, ")
MOZ_UTF16("\"zones_waiting_for_gc\": %lu, ")
MOZ_UTF16("\"short_living_objects_waiting_for_gc\": %lu, ")
MOZ_UTF16("\"forced_gc\": %d, ")
MOZ_UTF16("\"forget_skippable\": { ")
@ -1915,6 +1919,7 @@ nsJSContext::EndCycleCollectionCallback(CycleCollectorResults &aResults)
aResults.mVisitedRefCounted, aResults.mVisitedGCed,
aResults.mFreedRefCounted, aResults.mFreedGCed,
sCCollectedWaitingForGC,
sCCollectedZonesWaitingForGC,
sLikelyShortLivingObjectsNeedingGC,
aResults.mForcedGC,
sForgetSkippableBeforeCC,
@ -2370,6 +2375,7 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::GCProgress aProgress, const JS::GCDescrip
nsJSContext::KillInterSliceGCTimer();
sCCollectedWaitingForGC = 0;
sCCollectedZonesWaitingForGC = 0;
sLikelyShortLivingObjectsNeedingGC = 0;
sCleanupsSinceLastGC = 0;
sNeedsFullCC = true;
@ -2452,6 +2458,7 @@ mozilla::dom::StartupJSEnvironment()
sPendingLoadCount = 0;
sLoadingInProgress = false;
sCCollectedWaitingForGC = 0;
sCCollectedZonesWaitingForGC = 0;
sLikelyShortLivingObjectsNeedingGC = 0;
sPostGCEventsToConsole = false;
sNeedsFullCC = false;

View File

@ -92,33 +92,6 @@ function runEmulatorCmdSafe(aCommand) {
return deferred.promise;
}
/**
* Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject.
*
* Fulfill params: A DOMEvent.
* Reject params: A DOMEvent.
*
* @param aRequest
* A DOMRequest instance.
*
* @return A deferred promise.
*/
function wrapDomRequestAsPromise(aRequest) {
let deferred = Promise.defer();
ok(aRequest instanceof DOMRequest,
"aRequest is instanceof " + aRequest.constructor);
aRequest.onsuccess = function(aEvent) {
deferred.resolve(aEvent);
};
aRequest.onerror = function(aEvent) {
deferred.reject(aEvent);
};
return deferred.promise;
}
/**
* Add a Bluetooth remote device to scatternet and set its properties.
*
@ -246,16 +219,15 @@ function getEmulatorDeviceProperty(aAddress, aPropertyName) {
function startDiscovery(aAdapter) {
let request = aAdapter.startDiscovery();
return wrapDomRequestAsPromise(request)
.then(function resolve() {
return request.then(function resolve() {
// TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
// Currently, discovering state wouldn't change immediately here.
// We would turn on this check when the redesigned API are landed.
// is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
log(" Start discovery - Success");
}, function reject(aEvent) {
}, function reject(aError) {
ok(false, "Start discovery - Fail");
throw aEvent.target.error;
throw aError;
});
}
@ -275,16 +247,15 @@ function startDiscovery(aAdapter) {
function stopDiscovery(aAdapter) {
let request = aAdapter.stopDiscovery();
return wrapDomRequestAsPromise(request)
.then(function resolve() {
return request.then(function resolve() {
// TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
// Currently, discovering state wouldn't change immediately here.
// We would turn on this check when the redesigned API are landed.
// is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
log(" Stop discovery - Success");
}, function reject(aEvent) {
}, function reject(aError) {
ok(false, "Stop discovery - Fail");
throw aEvent.target.error;
throw aError;
});
}
@ -363,12 +334,11 @@ function startDiscoveryAndWaitDevicesFound(aAdapter, aRemoteAddresses) {
function pair(aAdapter, aDeviceAddress) {
let request = aAdapter.pair(aDeviceAddress);
return wrapDomRequestAsPromise(request)
.then(function resolve() {
return request.then(function resolve() {
log(" Pair - Success");
}, function reject(aEvent) {
}, function reject(aError) {
ok(false, "Pair - Fail");
throw aEvent.target.error;
throw aError;
});
}
@ -390,12 +360,11 @@ function pair(aAdapter, aDeviceAddress) {
function unpair(aAdapter, aDeviceAddress) {
let request = aAdapter.unpair(aDeviceAddress);
return wrapDomRequestAsPromise(request)
.then(function resolve() {
return request.then(function resolve() {
log(" Unpair - Success");
}, function reject(aEvent) {
}, function reject(aError) {
ok(false, "Unpair - Fail");
throw aEvent.target.error;
throw aError;
});
}
@ -440,14 +409,13 @@ function pairDeviceAndWait(aAdapter, aDeviceAddress) {
function getPairedDevices(aAdapter) {
let request = aAdapter.getPairedDevices();
return wrapDomRequestAsPromise(request)
.then(function resolve() {
return request.then(function resolve() {
log(" getPairedDevices - Success");
let pairedDevices = request.result.slice();
return pairedDevices;
}, function reject(aEvent) {
}, function reject(aError) {
ok(false, "getPairedDevices - Fail");
throw aEvent.target.error;
throw aError;
});
}
@ -469,13 +437,12 @@ function getPairedDevices(aAdapter) {
function getSettings(aKey) {
let request = navigator.mozSettings.createLock().get(aKey);
return wrapDomRequestAsPromise(request)
.then(function resolve(aEvent) {
return request.then(function resolve(aValue) {
ok(true, "getSettings(" + aKey + ")");
return aEvent.target.result[aKey];
}, function reject(aEvent) {
return aValue[aKey];
}, function reject(aError) {
ok(false, "getSettings(" + aKey + ")");
throw aEvent.target.error;
throw aError;
});
}

View File

@ -105,20 +105,8 @@ this.DataStoreChangeNotifier = {
delete this.sysMsgOnChangeLongTimers[storeKey];
}
// Get all the manifest URLs of the apps which can access the datastore.
var manifestURLs = dataStoreService.getAppManifestURLsForDataStore(aStore);
var enumerate = manifestURLs.enumerate();
while (enumerate.hasMoreElements()) {
var manifestURL = enumerate.getNext().QueryInterface(Ci.nsISupportsString);
debug("Notify app " + manifestURL + " of datastore updates");
// Send the system message 'datastore-update-{store name}' to all the
// pages for these apps. With the manifest URL of the owner in the message
// payload, it notifies the consumer a sync operation should be performed.
systemMessenger.sendMessage("datastore-update-" + aStore,
{ owner: aOwner },
null,
Services.io.newURI(manifestURL, null, null));
}
systemMessenger.broadcastMessage("datastore-update-" + aStore,
{ owner: aOwner });
},
// Use the following logic to broadcast system messages in a moderate pattern.

View File

@ -5,6 +5,8 @@
#include "Fetch.h"
#include "nsIDocument.h"
#include "nsIGlobalObject.h"
#include "nsIStringStream.h"
#include "nsIUnicodeDecoder.h"
#include "nsIUnicodeEncoder.h"
@ -16,16 +18,264 @@
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/EncodingUtils.h"
#include "mozilla/dom/FetchDriver.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/Headers.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseWorkerProxy.h"
#include "mozilla/dom/Request.h"
#include "mozilla/dom/Response.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/URLSearchParams.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/workers/Workers.h"
#include "InternalResponse.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
namespace mozilla {
namespace dom {
using namespace workers;
class WorkerFetchResolver MOZ_FINAL : public FetchDriverObserver
{
friend class WorkerFetchResponseRunnable;
friend class ResolveFetchWithBodyRunnable;
// This promise proxy is for the Promise returned by a call to fetch() that
// is resolved with a Response instance.
nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
// Passed from main thread to worker thread after being initialized (except
// for the body.
nsRefPtr<InternalResponse> mInternalResponse;
public:
WorkerFetchResolver(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise);
void
OnResponseAvailable(InternalResponse* aResponse) MOZ_OVERRIDE;
workers::WorkerPrivate*
GetWorkerPrivate() { return mPromiseProxy->GetWorkerPrivate(); }
private:
~WorkerFetchResolver();
};
class MainThreadFetchResolver MOZ_FINAL : public FetchDriverObserver
{
nsRefPtr<Promise> mPromise;
nsRefPtr<InternalResponse> mInternalResponse;
NS_DECL_OWNINGTHREAD
public:
MainThreadFetchResolver(Promise* aPromise);
void
OnResponseAvailable(InternalResponse* aResponse) MOZ_OVERRIDE;
private:
~MainThreadFetchResolver();
};
class MainThreadFetchRunnable : public nsRunnable
{
nsRefPtr<WorkerFetchResolver> mResolver;
nsRefPtr<InternalRequest> mRequest;
public:
MainThreadFetchRunnable(WorkerPrivate* aWorkerPrivate,
Promise* aPromise,
InternalRequest* aRequest)
: mResolver(new WorkerFetchResolver(aWorkerPrivate, aPromise))
, mRequest(aRequest)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
}
NS_IMETHODIMP
Run()
{
AssertIsOnMainThread();
nsRefPtr<FetchDriver> fetch = new FetchDriver(mRequest);
nsresult rv = fetch->Fetch(mResolver);
// Right now we only support async fetch, which should never directly fail.
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
};
already_AddRefed<Promise>
FetchRequest(nsIGlobalObject* aGlobal, const RequestOrScalarValueString& aInput,
const RequestInit& aInit, ErrorResult& aRv)
{
nsRefPtr<Promise> p = Promise::Create(aGlobal, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
AutoJSAPI jsapi;
jsapi.Init(aGlobal);
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> jsGlobal(cx, aGlobal->GetGlobalJSObject());
GlobalObject global(cx, jsGlobal);
nsRefPtr<Request> request = Request::Constructor(global, aInput, aInit, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
nsRefPtr<InternalRequest> r = request->GetInternalRequest();
if (!r->ReferrerIsNone()) {
nsAutoCString ref;
aRv = GetRequestReferrer(aGlobal, r, ref);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
r->SetReferrer(ref);
}
if (NS_IsMainThread()) {
nsRefPtr<MainThreadFetchResolver> resolver = new MainThreadFetchResolver(p);
nsRefPtr<FetchDriver> fetch = new FetchDriver(r);
aRv = fetch->Fetch(resolver);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
} else {
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
nsRefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable(worker, p, r);
if (NS_FAILED(NS_DispatchToMainThread(run))) {
NS_WARNING("MainThreadFetchRunnable dispatch failed!");
}
}
return p.forget();
}
MainThreadFetchResolver::MainThreadFetchResolver(Promise* aPromise)
: mPromise(aPromise)
{
}
void
MainThreadFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
{
NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
AssertIsOnMainThread();
mInternalResponse = aResponse;
nsCOMPtr<nsIGlobalObject> go = mPromise->GetParentObject();
nsRefPtr<Response> response = new Response(go, aResponse);
mPromise->MaybeResolve(response);
}
MainThreadFetchResolver::~MainThreadFetchResolver()
{
NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
}
class WorkerFetchResponseRunnable : public WorkerRunnable
{
nsRefPtr<WorkerFetchResolver> mResolver;
public:
WorkerFetchResponseRunnable(WorkerFetchResolver* aResolver)
: WorkerRunnable(aResolver->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
, mResolver(aResolver)
{
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(aWorkerPrivate == mResolver->GetWorkerPrivate());
nsRefPtr<nsIGlobalObject> global = aWorkerPrivate->GlobalScope();
nsRefPtr<Response> response = new Response(global, mResolver->mInternalResponse);
nsRefPtr<Promise> promise = mResolver->mPromiseProxy->GetWorkerPromise();
MOZ_ASSERT(promise);
promise->MaybeResolve(response);
mResolver->mPromiseProxy->CleanUp(aCx);
return true;
}
};
WorkerFetchResolver::WorkerFetchResolver(WorkerPrivate* aWorkerPrivate, Promise* aPromise)
: mPromiseProxy(new PromiseWorkerProxy(aWorkerPrivate, aPromise))
{
}
WorkerFetchResolver::~WorkerFetchResolver()
{
}
void
WorkerFetchResolver::OnResponseAvailable(InternalResponse* aResponse)
{
AssertIsOnMainThread();
mInternalResponse = aResponse;
nsRefPtr<WorkerFetchResponseRunnable> r =
new WorkerFetchResponseRunnable(this);
AutoSafeJSContext cx;
if (!r->Dispatch(cx)) {
NS_WARNING("Could not dispatch fetch resolve");
}
}
// Empty string for no-referrer. FIXME(nsm): Does returning empty string
// actually lead to no-referrer in the base channel?
// The actual referrer policy and stripping is dealt with by HttpBaseChannel,
// this always returns the full API referrer URL of the relevant global.
nsresult
GetRequestReferrer(nsIGlobalObject* aGlobal, const InternalRequest* aRequest, nsCString& aReferrer)
{
if (aRequest->ReferrerIsURL()) {
aReferrer = aRequest->ReferrerAsURL();
return NS_OK;
}
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
if (window) {
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
if (doc) {
nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
nsAutoCString origin;
nsresult rv = nsContentUtils::GetASCIIOrigin(docURI, origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoString referrer;
doc->GetReferrer(referrer);
aReferrer = NS_ConvertUTF16toUTF8(referrer);
}
} else {
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
worker->AssertIsOnWorkerThread();
aReferrer = worker->GetLocationInfo().mHref;
// XXX(nsm): Algorithm says "If source is not a URL..." but when is it
// not a URL?
}
return NS_OK;
}
namespace {
nsresult
ExtractFromArrayBuffer(const ArrayBuffer& aBuffer,

View File

@ -10,15 +10,30 @@
#include "nsError.h"
#include "nsString.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/RequestBinding.h"
class nsIInputStream;
class nsIGlobalObject;
namespace mozilla {
namespace dom {
class ArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams;
class InternalRequest;
class OwningArrayBufferOrArrayBufferViewOrBlobOrScalarValueStringOrURLSearchParams;
class Promise;
class RequestOrScalarValueString;
namespace workers {
class WorkerPrivate;
} // namespace workers
already_AddRefed<Promise>
FetchRequest(nsIGlobalObject* aGlobal, const RequestOrScalarValueString& aInput,
const RequestInit& aInit, ErrorResult& aRv);
nsresult
GetRequestReferrer(nsIGlobalObject* aGlobal, const InternalRequest* aRequest, nsCString& aReferrer);
/*
* Creates an nsIInputStream based on the fetch specifications 'extract a byte
@ -105,6 +120,7 @@ private:
bool mBodyUsed;
nsCString mMimeType;
};
} // namespace dom
} // namespace mozilla

301
dom/fetch/FetchDriver.cpp Normal file
View File

@ -0,0 +1,301 @@
/* -*- 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 "mozilla/dom/FetchDriver.h"
#include "nsIScriptSecurityManager.h"
#include "nsContentPolicyUtils.h"
#include "nsDataHandler.h"
#include "nsHostObjectProtocolHandler.h"
#include "nsNetUtil.h"
#include "nsStringStream.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/workers/Workers.h"
#include "Fetch.h"
#include "InternalRequest.h"
#include "InternalResponse.h"
namespace mozilla {
namespace dom {
FetchDriver::FetchDriver(InternalRequest* aRequest)
: mRequest(aRequest)
, mFetchRecursionCount(0)
{
}
FetchDriver::~FetchDriver()
{
}
nsresult
FetchDriver::Fetch(FetchDriverObserver* aObserver)
{
workers::AssertIsOnMainThread();
mObserver = aObserver;
return Fetch(false /* CORS flag */);
}
nsresult
FetchDriver::Fetch(bool aCORSFlag)
{
// We do not currently implement parts of the spec that lead to recursion.
MOZ_ASSERT(mFetchRecursionCount == 0);
mFetchRecursionCount++;
// FIXME(nsm): Deal with HSTS.
if (!mRequest->IsSynchronous() && mFetchRecursionCount <= 1) {
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableMethodWithArg<bool>(this, &FetchDriver::ContinueFetch, aCORSFlag);
return NS_DispatchToCurrentThread(r);
}
MOZ_CRASH("Synchronous fetch not supported");
}
nsresult
FetchDriver::ContinueFetch(bool aCORSFlag)
{
workers::AssertIsOnMainThread();
nsAutoCString url;
mRequest->GetURL(url);
nsCOMPtr<nsIURI> requestURI;
// FIXME(nsm): Deal with relative URLs.
nsresult rv = NS_NewURI(getter_AddRefs(requestURI), url,
nullptr, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return FailWithNetworkError();
}
// FIXME(nsm): Bug 1039846: Add CSP checks
nsAutoCString scheme;
rv = requestURI->GetScheme(scheme);
if (NS_WARN_IF(NS_FAILED(rv))) {
return FailWithNetworkError();
}
nsAutoCString originURL;
mRequest->GetOrigin(originURL);
nsCOMPtr<nsIURI> originURI;
rv = NS_NewURI(getter_AddRefs(originURI), originURL, nullptr, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return FailWithNetworkError();
}
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
rv = ssm->CheckSameOriginURI(requestURI, originURI, false);
if ((!aCORSFlag && NS_SUCCEEDED(rv)) ||
(scheme.EqualsLiteral("data") && mRequest->SameOriginDataURL()) ||
scheme.EqualsLiteral("about")) {
return BasicFetch();
}
if (mRequest->Mode() == RequestMode::Same_origin) {
return FailWithNetworkError();
}
if (mRequest->Mode() == RequestMode::No_cors) {
mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_OPAQUE);
return BasicFetch();
}
if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
return FailWithNetworkError();
}
if (mRequest->Mode() == RequestMode::Cors_with_forced_preflight ||
(mRequest->UnsafeRequest() && (mRequest->HasSimpleMethod() || !mRequest->Headers()->HasOnlySimpleHeaders()))) {
// FIXME(nsm): Set corsPreflight;
}
mRequest->SetResponseTainting(InternalRequest::RESPONSETAINT_CORS);
// FIXME(nsm): HttpFetch.
return FailWithNetworkError();
}
nsresult
FetchDriver::BasicFetch()
{
nsAutoCString url;
mRequest->GetURL(url);
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri),
url,
nullptr,
nullptr);
NS_ENSURE_SUCCESS(rv, rv);
nsCString scheme;
rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
if (scheme.LowerCaseEqualsLiteral("about")) {
if (url.EqualsLiteral("about:blank")) {
nsRefPtr<InternalResponse> response =
new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
ErrorResult result;
response->Headers()->Append(NS_LITERAL_CSTRING("content-type"),
NS_LITERAL_CSTRING("text/html;charset=utf-8"),
result);
MOZ_ASSERT(!result.Failed());
nsCOMPtr<nsIInputStream> body;
rv = NS_NewCStringInputStream(getter_AddRefs(body), EmptyCString());
if (NS_WARN_IF(NS_FAILED(rv))) {
return FailWithNetworkError();
}
response->SetBody(body);
BeginResponse(response);
return SucceedWithResponse();
}
return FailWithNetworkError();
}
if (scheme.LowerCaseEqualsLiteral("blob")) {
nsRefPtr<FileImpl> blobImpl;
rv = NS_GetBlobForBlobURI(uri, getter_AddRefs(blobImpl));
FileImpl* blob = static_cast<FileImpl*>(blobImpl.get());
if (NS_WARN_IF(NS_FAILED(rv))) {
return FailWithNetworkError();
}
nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
{
ErrorResult result;
uint64_t size = blob->GetSize(result);
if (NS_WARN_IF(result.Failed())) {
return FailWithNetworkError();
}
nsAutoString sizeStr;
sizeStr.AppendInt(size);
response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"), NS_ConvertUTF16toUTF8(sizeStr), result);
if (NS_WARN_IF(result.Failed())) {
return FailWithNetworkError();
}
nsAutoString type;
blob->GetType(type);
response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), NS_ConvertUTF16toUTF8(type), result);
if (NS_WARN_IF(result.Failed())) {
return FailWithNetworkError();
}
}
nsCOMPtr<nsIInputStream> stream;
rv = blob->GetInternalStream(getter_AddRefs(stream));
if (NS_WARN_IF(NS_FAILED(rv))) {
return FailWithNetworkError();
}
response->SetBody(stream);
BeginResponse(response);
return SucceedWithResponse();
}
if (scheme.LowerCaseEqualsLiteral("data")) {
nsAutoCString method;
mRequest->GetMethod(method);
if (method.LowerCaseEqualsASCII("get")) {
// Use nsDataHandler directly so that we can extract the content type.
// XXX(nsm): Is there a way to acquire the charset without such tight
// coupling with the DataHandler? nsIProtocolHandler does not provide
// anything similar.
nsAutoCString contentType, contentCharset, dataBuffer, hashRef;
bool isBase64;
rv = nsDataHandler::ParseURI(url,
contentType,
contentCharset,
isBase64,
dataBuffer,
hashRef);
if (NS_SUCCEEDED(rv)) {
ErrorResult result;
nsRefPtr<InternalResponse> response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
if (!contentCharset.IsEmpty()) {
contentType.Append(";charset=");
contentType.Append(contentCharset);
}
response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, result);
if (!result.Failed()) {
nsCOMPtr<nsIInputStream> stream;
rv = NS_NewCStringInputStream(getter_AddRefs(stream), dataBuffer);
if (NS_SUCCEEDED(rv)) {
response->SetBody(stream);
BeginResponse(response);
return SucceedWithResponse();
}
}
}
}
return FailWithNetworkError();
}
if (scheme.LowerCaseEqualsLiteral("file")) {
} else if (scheme.LowerCaseEqualsLiteral("http") ||
scheme.LowerCaseEqualsLiteral("https")) {
// FIXME(nsm): HttpFetch.
return FailWithNetworkError();
}
return FailWithNetworkError();
}
nsresult
FetchDriver::BeginResponse(InternalResponse* aResponse)
{
MOZ_ASSERT(aResponse);
nsAutoCString reqURL;
mRequest->GetURL(reqURL);
aResponse->SetUrl(reqURL);
// FIXME(nsm): Handle mixed content check, step 7 of fetch.
nsRefPtr<InternalResponse> filteredResponse;
switch (mRequest->GetResponseTainting()) {
case InternalRequest::RESPONSETAINT_BASIC:
filteredResponse = InternalResponse::BasicResponse(aResponse);
break;
case InternalRequest::RESPONSETAINT_CORS:
filteredResponse = InternalResponse::CORSResponse(aResponse);
break;
case InternalRequest::RESPONSETAINT_OPAQUE:
filteredResponse = InternalResponse::OpaqueResponse();
break;
default:
MOZ_CRASH("Unexpected case");
}
MOZ_ASSERT(filteredResponse);
mObserver->OnResponseAvailable(filteredResponse);
return NS_OK;
}
nsresult
FetchDriver::SucceedWithResponse()
{
return NS_OK;
}
nsresult
FetchDriver::FailWithNetworkError()
{
nsRefPtr<InternalResponse> error = InternalResponse::NetworkError();
mObserver->OnResponseAvailable(error);
// FIXME(nsm): Some sort of shutdown?
return NS_OK;
}
} // namespace dom
} // namespace mozilla

59
dom/fetch/FetchDriver.h Normal file
View File

@ -0,0 +1,59 @@
/* -*- 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 mozilla_dom_FetchDriver_h
#define mozilla_dom_FetchDriver_h
#include "nsIStreamListener.h"
#include "nsRefPtr.h"
class nsPIDOMWindow;
namespace mozilla {
namespace dom {
class InternalRequest;
class InternalResponse;
class FetchDriverObserver
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FetchDriverObserver);
virtual void OnResponseAvailable(InternalResponse* aResponse) = 0;
protected:
virtual ~FetchDriverObserver()
{ };
};
class FetchDriver MOZ_FINAL
{
NS_INLINE_DECL_REFCOUNTING(FetchDriver)
public:
explicit FetchDriver(InternalRequest* aRequest);
NS_IMETHOD Fetch(FetchDriverObserver* aObserver);
private:
nsRefPtr<InternalRequest> mRequest;
nsRefPtr<FetchDriverObserver> mObserver;
uint32_t mFetchRecursionCount;
FetchDriver() MOZ_DELETE;
FetchDriver(const FetchDriver&) MOZ_DELETE;
FetchDriver& operator=(const FetchDriver&) MOZ_DELETE;
~FetchDriver();
nsresult Fetch(bool aCORSFlag);
nsresult ContinueFetch(bool aCORSFlag);
nsresult BasicFetch();
nsresult FailWithNetworkError();
nsresult BeginResponse(InternalResponse* aResponse);
nsresult SucceedWithResponse();
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_FetchDriver_h

View File

@ -268,5 +268,61 @@ InternalHeaders::Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv)
Append(NS_ConvertUTF16toUTF8(keys[i]), aInit.Get(keys[i]), aRv);
}
}
bool
InternalHeaders::HasOnlySimpleHeaders() const
{
for (uint32_t i = 0; i < mList.Length(); ++i) {
if (!IsSimpleHeader(mList[i].mName, mList[i].mValue)) {
return false;
}
}
return true;
}
// static
already_AddRefed<InternalHeaders>
InternalHeaders::BasicHeaders(InternalHeaders* aHeaders)
{
nsRefPtr<InternalHeaders> basic = new InternalHeaders(*aHeaders);
ErrorResult result;
// The Set-Cookie headers cannot be invalid mutable headers, so the Delete
// must succeed.
basic->Delete(NS_LITERAL_CSTRING("Set-Cookie"), result);
MOZ_ASSERT(!result.Failed());
basic->Delete(NS_LITERAL_CSTRING("Set-Cookie2"), result);
MOZ_ASSERT(!result.Failed());
return basic.forget();
}
// static
already_AddRefed<InternalHeaders>
InternalHeaders::CORSHeaders(InternalHeaders* aHeaders)
{
nsRefPtr<InternalHeaders> cors = new InternalHeaders(aHeaders->mGuard);
ErrorResult result;
nsAutoTArray<nsCString, 1> acExposedNames;
aHeaders->GetAll(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"), acExposedNames, result);
MOZ_ASSERT(!result.Failed());
nsCaseInsensitiveCStringArrayComparator comp;
for (uint32_t i = 0; i < aHeaders->mList.Length(); ++i) {
const Entry& entry = aHeaders->mList[i];
if (entry.mName.EqualsASCII("cache-control") ||
entry.mName.EqualsASCII("content-language") ||
entry.mName.EqualsASCII("content-type") ||
entry.mName.EqualsASCII("expires") ||
entry.mName.EqualsASCII("last-modified") ||
entry.mName.EqualsASCII("pragma") ||
acExposedNames.Contains(entry.mName, comp)) {
cors->Append(entry.mName, entry.mValue, result);
MOZ_ASSERT(!result.Failed());
}
}
return cors.forget();
}
} // namespace dom
} // namespace mozilla

View File

@ -77,6 +77,14 @@ public:
void Fill(const InternalHeaders& aInit, ErrorResult& aRv);
void Fill(const Sequence<Sequence<nsCString>>& aInit, ErrorResult& aRv);
void Fill(const MozMap<nsCString>& aInit, ErrorResult& aRv);
bool HasOnlySimpleHeaders() const;
static already_AddRefed<InternalHeaders>
BasicHeaders(InternalHeaders* aHeaders);
static already_AddRefed<InternalHeaders>
CORSHeaders(InternalHeaders* aHeaders);
private:
virtual ~InternalHeaders();

View File

@ -66,7 +66,11 @@ public:
, mForceOriginHeader(false)
, mManualRedirect(false)
, mPreserveContentCodings(false)
, mSameOriginDataURL(false)
// FIXME(nsm): This should be false by default, but will lead to the
// algorithm never loading data: URLs right now. See Bug 1018872 about
// how certain contexts will override it to set it to true. Fetch
// specification does not handle this yet.
, mSameOriginDataURL(true)
, mSkipServiceWorker(false)
, mSynchronous(false)
, mUnsafeRequest(false)
@ -113,6 +117,14 @@ public:
mMethod.Assign(aMethod);
}
bool
HasSimpleMethod() const
{
return mMethod.LowerCaseEqualsASCII("get") ||
mMethod.LowerCaseEqualsASCII("post") ||
mMethod.LowerCaseEqualsASCII("head");
}
void
GetURL(nsCString& aURL) const
{
@ -177,12 +189,30 @@ public:
mCredentialsMode = aCredentialsMode;
}
ResponseTainting
GetResponseTainting() const
{
return mResponseTainting;
}
void
SetResponseTainting(ResponseTainting aTainting)
{
mResponseTainting = aTainting;
}
nsContentPolicyType
GetContext() const
{
return mContext;
}
bool
UnsafeRequest() const
{
return mUnsafeRequest;
}
InternalHeaders*
Headers()
{
@ -201,6 +231,12 @@ public:
aOrigin.Assign(mOrigin);
}
bool
SameOriginDataURL() const
{
return mSameOriginDataURL;
}
void
SetBody(nsIInputStream* aStream)
{

View File

@ -20,5 +20,40 @@ InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusTe
{
}
// Headers are not copied since BasicResponse and CORSResponse both need custom
// header handling.
InternalResponse::InternalResponse(const InternalResponse& aOther)
: mType(aOther.mType)
, mTerminationReason(aOther.mTerminationReason)
, mURL(aOther.mURL)
, mStatus(aOther.mStatus)
, mStatusText(aOther.mStatusText)
, mBody(aOther.mBody)
, mContentType(aOther.mContentType)
{
}
// static
already_AddRefed<InternalResponse>
InternalResponse::BasicResponse(InternalResponse* aInner)
{
MOZ_ASSERT(aInner);
nsRefPtr<InternalResponse> basic = new InternalResponse(*aInner);
basic->mType = ResponseType::Basic;
basic->mHeaders = InternalHeaders::BasicHeaders(aInner->mHeaders);
return basic.forget();
}
// static
already_AddRefed<InternalResponse>
InternalResponse::CORSResponse(InternalResponse* aInner)
{
MOZ_ASSERT(aInner);
nsRefPtr<InternalResponse> cors = new InternalResponse(*aInner);
cors->mType = ResponseType::Cors;
cors->mHeaders = InternalHeaders::CORSHeaders(aInner->mHeaders);
return cors.forget();
}
} // namespace dom
} // namespace mozilla

View File

@ -25,8 +25,6 @@ public:
InternalResponse(uint16_t aStatus, const nsACString& aStatusText);
explicit InternalResponse(const InternalResponse& aOther) MOZ_DELETE;
static already_AddRefed<InternalResponse>
NetworkError()
{
@ -35,6 +33,24 @@ public:
return response.forget();
}
static already_AddRefed<InternalResponse>
OpaqueResponse()
{
nsRefPtr<InternalResponse> response = new InternalResponse(0, EmptyCString());
response->mType = ResponseType::Opaque;
return response.forget();
}
// DO NOT use the inner response after filtering it since the filtered
// response will adopt the inner response's body.
static already_AddRefed<InternalResponse>
BasicResponse(InternalResponse* aInner);
// DO NOT use the inner response after filtering it since the filtered
// response will adopt the inner response's body.
static already_AddRefed<InternalResponse>
CORSResponse(InternalResponse* aInner);
ResponseType
Type() const
{
@ -48,10 +64,16 @@ public:
}
// FIXME(nsm): Return with exclude fragment.
nsCString&
GetUrl()
void
GetUrl(nsCString& aURL) const
{
return mURL;
aURL.Assign(mURL);
}
void
SetUrl(const nsACString& aURL)
{
mURL.Assign(aURL);
}
uint16_t
@ -89,6 +111,10 @@ private:
~InternalResponse()
{ }
// Used to create filtered responses.
// Does not copy headers.
explicit InternalResponse(const InternalResponse& aOther);
ResponseType mType;
nsCString mTerminationReason;
nsCString mURL;

View File

@ -171,12 +171,9 @@ Request::Constructor(const GlobalObject& aGlobal,
requestHeaders->Clear();
if (request->Mode() == RequestMode::No_cors) {
nsCString method;
request->GetMethod(method);
ToLowerCase(method);
if (!method.EqualsASCII("get") &&
!method.EqualsASCII("head") &&
!method.EqualsASCII("post")) {
if (!request->HasSimpleMethod()) {
nsAutoCString method;
request->GetMethod(method);
NS_ConvertUTF8toUTF16 label(method);
aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
return nullptr;

View File

@ -51,7 +51,9 @@ public:
void
GetUrl(DOMString& aUrl) const
{
aUrl.AsAString() = NS_ConvertUTF8toUTF16(mInternalResponse->GetUrl());
nsCString url;
mInternalResponse->GetUrl(url);
aUrl.AsAString() = NS_ConvertUTF8toUTF16(url);
}
uint16_t

View File

@ -6,6 +6,7 @@
EXPORTS.mozilla.dom += [
'Fetch.h',
'FetchDriver.h',
'Headers.h',
'InternalHeaders.h',
'InternalRequest.h',
@ -16,6 +17,7 @@ EXPORTS.mozilla.dom += [
UNIFIED_SOURCES += [
'Fetch.cpp',
'FetchDriver.cpp',
'Headers.cpp',
'InternalHeaders.cpp',
'InternalRequest.cpp',
@ -26,6 +28,8 @@ UNIFIED_SOURCES += [
LOCAL_INCLUDES += [
'../workers',
# For nsDataHandler.h
'/netwerk/protocol/data',
]
FAIL_ON_WARNINGS = True

View File

@ -14,9 +14,12 @@ Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
Cu.import("resource://gre/modules/PermissionsTable.jsm");
Cu.import("resource://gre/modules/PermissionSettings.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "dataStoreService",
"@mozilla.org/datastore-service;1",
"nsIDataStoreService");
this.EXPORTED_SYMBOLS = ["SystemMessagePermissionsChecker",
"SystemMessagePermissionsTable",
"SystemMessagePrefixPermissionsTable"];
"SystemMessagePermissionsTable"];
function debug(aStr) {
// dump("SystemMessagePermissionsChecker.jsm: " + aStr + "\n");
@ -129,17 +132,6 @@ this.SystemMessagePermissionsTable = {
}
};
// This table maps system message prefix to permission(s), indicating only
// the system messages with specified prefixes granted by the page's permissions
// are allowed to be registered or sent to that page. Note the empty permission
// set means this type of system message is always permitted.
//
// Note that this table is only used when the permission checker can't find a
// match in SystemMessagePermissionsTable listed above.
this.SystemMessagePrefixPermissionsTable = {
"datastore-update-": { },
};
this.SystemMessagePermissionsChecker = {
/**
@ -159,19 +151,9 @@ this.SystemMessagePermissionsChecker = {
let permNames = SystemMessagePermissionsTable[aSysMsgName];
if (permNames === undefined) {
// Try to look up in the prefix table.
for (let sysMsgPrefix in SystemMessagePrefixPermissionsTable) {
if (aSysMsgName.indexOf(sysMsgPrefix) === 0) {
permNames = SystemMessagePrefixPermissionsTable[sysMsgPrefix];
break;
}
}
if (permNames === undefined) {
debug("'" + aSysMsgName + "' is not associated with permissions. " +
"Please add them to the SystemMessage[Prefix]PermissionsTable.");
return null;
}
debug("'" + aSysMsgName + "' is not associated with permissions. " +
"Please add them to the SystemMessage[Prefix]PermissionsTable.");
return null;
}
let object = { };
@ -194,6 +176,34 @@ this.SystemMessagePermissionsChecker = {
return object
},
/**
* Check if the message is a datastore-update message
* @param string aSysMsgName
* The system messsage name.
*/
isDataStoreSystemMessage: function(aSysMsgName) {
return aSysMsgName.indexOf('datastore-update-') === 0;
},
/**
* Check if this manifest can deliver this particular datastore message.
*/
canDeliverDataStoreSystemMessage: function(aSysMsgName, aManifestURL) {
let store = aSysMsgName.substr('datastore-update-'.length);
// Get all the manifest URLs of the apps which can access the datastore.
let manifestURLs = dataStoreService.getAppManifestURLsForDataStore(store);
let enumerate = manifestURLs.enumerate();
while (enumerate.hasMoreElements()) {
let manifestURL = enumerate.getNext().QueryInterface(Ci.nsISupportsString);
if (manifestURL == aManifestURL) {
return true;
}
}
return false;
},
/**
* Check if the system message is permitted to be registered for the given
* app at start-up based on the permissions claimed in the app's manifest.
@ -215,6 +225,11 @@ this.SystemMessagePermissionsChecker = {
"aManifestURL: " + aManifestURL + ", " +
"aManifest: " + JSON.stringify(aManifest));
if (this.isDataStoreSystemMessage(aSysMsgName) &&
this.canDeliverDataStoreSystemMessage(aSysMsgName, aManifestURL)) {
return true;
}
let permNames = this.getSystemMessagePermissions(aSysMsgName);
if (permNames === null) {
return false;
@ -302,6 +317,11 @@ this.SystemMessagePermissionsChecker = {
"aPageURL: " + aPageURL + ", " +
"aManifestURL: " + aManifestURL);
if (this.isDataStoreSystemMessage(aSysMsgName) &&
this.canDeliverDataStoreSystemMessage(aSysMsgName, aManifestURL)) {
return true;
}
let permNames = this.getSystemMessagePermissions(aSysMsgName);
if (permNames === null) {
return false;

View File

@ -126,7 +126,7 @@ IntToNPIdentifier(int i)
JSContext* GetJSContext(NPP npp);
inline bool
NPStringIdentifierIsPermanent(NPP npp, NPIdentifier id)
NPStringIdentifierIsPermanent(NPIdentifier id)
{
AutoSafeJSContext cx;
return JS_StringHasBeenInterned(cx, NPIdentifierToString(id));

View File

@ -1,30 +0,0 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* 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 protocol PPluginModule;
namespace mozilla {
namespace plugins {
/**
* Represents an NPIdentifier that wraps either a string or an integer.
*/
async protocol PPluginIdentifier
{
manager PPluginModule;
parent:
/**
* If this is a temporary identifier, inform the parent that the plugin
* has made the identifier permanent by calling NPN_GetStringIdentifier.
*/
async Retain();
child:
async __delete__();
};
} // namespace plugins
} // namespace mozilla

View File

@ -3,12 +3,10 @@
* 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 protocol PPluginIdentifier;
include protocol PPluginInstance;
include protocol PPluginScriptableObject;
include protocol PCrashReporter;
using NPError from "npapi.h";
using NPNVariable from "npapi.h";
using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
@ -21,25 +19,9 @@ namespace plugins {
intr protocol PPluginModule
{
manages PPluginInstance;
manages PPluginIdentifier;
manages PCrashReporter;
both:
/**
* Sending a void string to this constructor creates an int identifier whereas
* sending a non-void string will create a string identifier. This constructor
* may be called by either child or parent. If a race occurs by calling the
* constructor with the same string or int argument then we create two actors
* and detect the second instance in the child. We prevent the parent's actor
* from leaking out to plugin code and only allow the child's to be used.
*
* When calling into the plugin, the parent may create a "temporary"
* identifier which is only valid for the lifetime of the current inerrupt frame.
*/
async PPluginIdentifier(nsCString aString,
int32_t aInt,
bool temporary);
// Window-specific message which instructs the interrupt mechanism to enter
// a nested event loop for the current interrupt call.
async ProcessNativeEventsInInterruptCall();

View File

@ -4,7 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PPluginInstance;
include protocol PPluginIdentifier;
include PluginTypes;
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
@ -39,10 +39,10 @@ child:
both:
// NPClass methods
intr HasMethod(PPluginIdentifier aId)
intr HasMethod(PluginIdentifier aId)
returns (bool aHasMethod);
intr Invoke(PPluginIdentifier aId,
intr Invoke(PluginIdentifier aId,
Variant[] aArgs)
returns (Variant aResult,
bool aSuccess);
@ -51,18 +51,18 @@ both:
returns (Variant aResult,
bool aSuccess);
intr HasProperty(PPluginIdentifier aId)
intr HasProperty(PluginIdentifier aId)
returns (bool aHasProperty);
intr SetProperty(PPluginIdentifier aId,
intr SetProperty(PluginIdentifier aId,
Variant aValue)
returns (bool aSuccess);
intr RemoveProperty(PPluginIdentifier aId)
intr RemoveProperty(PluginIdentifier aId)
returns (bool aSuccess);
intr Enumerate()
returns (PPluginIdentifier[] aProperties,
returns (PluginIdentifier[] aProperties,
bool aSuccess);
intr Construct(Variant[] aArgs)
@ -86,12 +86,12 @@ both:
* several checks at once and return all the results simultaneously.
*/
parent:
intr GetParentProperty(PPluginIdentifier aId)
intr GetParentProperty(PluginIdentifier aId)
returns (Variant aResult,
bool aSuccess);
child:
intr GetChildProperty(PPluginIdentifier aId)
intr GetChildProperty(PluginIdentifier aId)
returns (bool aHasProperty,
bool aHasMethod,
Variant aResult,

View File

@ -1,141 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 et :
* 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 "PluginIdentifierChild.h"
#include "PluginModuleChild.h"
namespace mozilla {
namespace plugins {
void
PluginIdentifierChild::MakePermanent()
{
if (mCanonicalIdentifier) {
NS_ASSERTION(mCanonicalIdentifier->mHashed && mCanonicalIdentifier->mTemporaryRefs == 0,
"Canonical identifiers should always be permanent.");
return; // nothing to do
}
if (!mHashed) {
NS_ASSERTION(mTemporaryRefs == 0, "Not hashed, but temporary refs?");
PluginIdentifierChild* c = GetCanonical();
if (c) {
NS_ASSERTION(c != this, "How did I get in the hash?");
mCanonicalIdentifier = c;
NS_ASSERTION(mCanonicalIdentifier->mHashed && mCanonicalIdentifier->mTemporaryRefs == 0,
"Canonical identifiers should always be permanent.");
return;
}
Hash();
mHashed = true;
return;
}
if (mTemporaryRefs) {
SendRetain();
mTemporaryRefs = 0;
}
}
void
PluginIdentifierChild::StartTemporary()
{
if (mCanonicalIdentifier) {
NS_ASSERTION(mCanonicalIdentifier->mHashed && mCanonicalIdentifier->mTemporaryRefs == 0,
"Canonical identifiers should always be permanent.");
return; // nothing to do
}
if (!mHashed) {
NS_ASSERTION(mTemporaryRefs == 0, "Not hashed, but temporary refs?");
PluginIdentifierChild* c = GetCanonical();
if (c) {
NS_ASSERTION(c != this, "How did I get in the hash?");
mCanonicalIdentifier = c;
NS_ASSERTION(mCanonicalIdentifier->mHashed && mCanonicalIdentifier->mTemporaryRefs == 0,
"Canonical identifiers should always be permanent.");
return;
}
Hash();
mHashed = true;
mTemporaryRefs = 1;
return;
}
if (mTemporaryRefs)
++mTemporaryRefs;
}
void
PluginIdentifierChild::FinishTemporary()
{
if (mCanonicalIdentifier)
return;
NS_ASSERTION(mHashed, "Finishing unhashed identifier?");
if (!mTemporaryRefs)
return;
--mTemporaryRefs;
if (mTemporaryRefs)
return;
Unhash();
mHashed = false;
}
PluginIdentifierChild*
PluginIdentifierChildString::GetCanonical()
{
PluginModuleChild* module = static_cast<PluginModuleChild*>(Manager());
return module->mStringIdentifiers.Get(mString);
}
void
PluginIdentifierChildString::Hash()
{
PluginModuleChild* module = static_cast<PluginModuleChild*>(Manager());
NS_ASSERTION(module->mStringIdentifiers.Get(mString) == nullptr, "Replacing Hash?");
module->mStringIdentifiers.Put(mString, this);
}
void
PluginIdentifierChildString::Unhash()
{
PluginModuleChild* module = static_cast<PluginModuleChild*>(Manager());
NS_ASSERTION(module->mStringIdentifiers.Get(mString) == this, "Incorrect identifier hash?");
module->mStringIdentifiers.Remove(mString);
}
PluginIdentifierChild*
PluginIdentifierChildInt::GetCanonical()
{
PluginModuleChild* module = static_cast<PluginModuleChild*>(Manager());
return module->mIntIdentifiers.Get(mInt);
}
void
PluginIdentifierChildInt::Hash()
{
PluginModuleChild* module = static_cast<PluginModuleChild*>(Manager());
NS_ASSERTION(module->mIntIdentifiers.Get(mInt) == nullptr, "Replacing Hash?");
module->mIntIdentifiers.Put(mInt, this);
}
void
PluginIdentifierChildInt::Unhash()
{
PluginModuleChild* module = static_cast<PluginModuleChild*>(Manager());
NS_ASSERTION(module->mIntIdentifiers.Get(mInt) == this, "Incorrect identifier hash?");
module->mIntIdentifiers.Remove(mInt);
}
} // namespace mozilla::plugins
} // namespace mozilla

View File

@ -1,164 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 et :
* 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 dom_plugins_PluginIdentifierChild_h
#define dom_plugins_PluginIdentifierChild_h
#include "mozilla/plugins/PPluginIdentifierChild.h"
#include "npapi.h"
#include "npruntime.h"
#include "nsString.h"
namespace mozilla {
namespace plugins {
class PluginModuleChild;
/**
* Plugin identifiers may be "temporary", see the comment on the
* PPluginIdentifier constructor for details. This means that any IPDL method
* which receives a PPluginIdentifierChild* parameter must use StackIdentifier
* to track it.
*/
class PluginIdentifierChild : public PPluginIdentifierChild
{
friend class PluginModuleChild;
public:
bool IsString()
{
return mIsString;
}
NPIdentifier ToNPIdentifier()
{
if (mCanonicalIdentifier) {
return mCanonicalIdentifier;
}
NS_ASSERTION(mHashed, "Handing out an unhashed identifier?");
return this;
}
void MakePermanent();
class MOZ_STACK_CLASS StackIdentifier
{
public:
explicit StackIdentifier(PPluginIdentifierChild* actor)
: mIdentifier(static_cast<PluginIdentifierChild*>(actor))
{
if (mIdentifier)
mIdentifier->StartTemporary();
}
~StackIdentifier() {
if (mIdentifier)
mIdentifier->FinishTemporary();
}
PluginIdentifierChild* operator->() { return mIdentifier; }
private:
PluginIdentifierChild* mIdentifier;
};
protected:
explicit PluginIdentifierChild(bool aIsString)
: mCanonicalIdentifier(nullptr)
, mHashed(false)
, mTemporaryRefs(0)
, mIsString(aIsString)
{
MOZ_COUNT_CTOR(PluginIdentifierChild);
}
virtual ~PluginIdentifierChild()
{
MOZ_COUNT_DTOR(PluginIdentifierChild);
}
// The following functions are implemented by the subclasses for their
// identifier maps.
virtual PluginIdentifierChild* GetCanonical() = 0;
virtual void Hash() = 0;
virtual void Unhash() = 0;
private:
void StartTemporary();
void FinishTemporary();
// There's a possibility that we already have an actor that wraps the same
// string or int because we do all this identifier construction
// asynchronously. In this case we need to hand out the canonical version
// created by the child side.
//
// In order to deal with temporary identifiers which appear on the stack,
// identifiers use the following state invariants:
//
// * mCanonicalIdentifier is non-nullptr: this is a duplicate identifier, no
// further information is necessary.
// * mHashed is false: this identifier is a newborn, non-permanent identifier
// * mHashed is true, mTemporaryRefs is 0: this identifier is permanent
// * mHashed is true, mTemporaryRefs is non-0: this identifier is temporary;
// if NPN_GetFooIdentifier is called for it, we need to retain it. If
// all stack references are lost, unhash it because it will soon be
// deleted.
PluginIdentifierChild* mCanonicalIdentifier;
bool mHashed;
unsigned int mTemporaryRefs;
bool mIsString;
};
class PluginIdentifierChildString : public PluginIdentifierChild
{
friend class PluginModuleChild;
public:
NPUTF8* ToString()
{
return ToNewCString(mString);
}
protected:
explicit PluginIdentifierChildString(const nsCString& aString)
: PluginIdentifierChild(true),
mString(aString)
{ }
virtual PluginIdentifierChild* GetCanonical();
virtual void Hash();
virtual void Unhash();
nsCString mString;
};
class PluginIdentifierChildInt : public PluginIdentifierChild
{
friend class PluginModuleChild;
public:
int32_t ToInt()
{
return mInt;
}
protected:
explicit PluginIdentifierChildInt(int32_t aInt)
: PluginIdentifierChild(false),
mInt(aInt)
{ }
virtual PluginIdentifierChild* GetCanonical();
virtual void Hash();
virtual void Unhash();
int32_t mInt;
};
} // namespace plugins
} // namespace mozilla
#endif // dom_plugins_PluginIdentifierChild_h

View File

@ -1,80 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 et :
* 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 "PluginIdentifierParent.h"
#include "nsNPAPIPlugin.h"
#include "nsServiceManagerUtils.h"
#include "PluginScriptableObjectUtils.h"
#include "mozilla/unused.h"
using namespace mozilla::plugins::parent;
namespace mozilla {
namespace plugins {
void
PluginIdentifierParent::ActorDestroy(ActorDestroyReason aWhy)
{
// Implement me! Bug 1005161
}
bool
PluginIdentifierParent::RecvRetain()
{
mTemporaryRefs = 0;
// Intern the jsid if necessary.
AutoSafeJSContext cx;
JS::Rooted<jsid> id(cx, NPIdentifierToJSId(mIdentifier));
if (!JSID_IS_STRING(id)) {
return true;
}
// The following is what nsNPAPIPlugin.cpp does. Gross, but the API doesn't
// give you a NPP to play with.
JS::Rooted<JSString*> str(cx, JSID_TO_STRING(id));
JSString* str2 = JS_InternJSString(cx, str);
if (!str2) {
return false;
}
NS_ASSERTION(str == str2, "Interning a string in a JSID should always return the same string.");
return true;
}
PluginIdentifierParent::StackIdentifier::StackIdentifier
(PluginInstanceParent* inst, NPIdentifier aIdentifier)
: mIdentifier(inst->Module()->GetIdentifierForNPIdentifier(inst->GetNPP(), aIdentifier))
{
}
PluginIdentifierParent::StackIdentifier::StackIdentifier
(NPObject* aObject, NPIdentifier aIdentifier)
: mIdentifier(nullptr)
{
PluginInstanceParent* inst = GetInstance(aObject);
mIdentifier = inst->Module()->GetIdentifierForNPIdentifier(inst->GetNPP(), aIdentifier);
}
PluginIdentifierParent::StackIdentifier::~StackIdentifier()
{
if (!mIdentifier) {
return;
}
if (!mIdentifier->IsTemporary()) {
return;
}
if (mIdentifier->RemoveTemporaryRef()) {
unused << PPluginIdentifierParent::Send__delete__(mIdentifier);
}
}
} // namespace mozilla::plugins
} // namespace mozilla

View File

@ -1,91 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=2 et :
* 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 dom_plugins_PluginIdentifierParent_h
#define dom_plugins_PluginIdentifierParent_h
#include "mozilla/plugins/PPluginIdentifierParent.h"
#include "npapi.h"
#include "npruntime.h"
namespace mozilla {
namespace plugins {
class PluginInstanceParent;
class PluginIdentifierParent : public PPluginIdentifierParent
{
friend class PluginModuleParent;
public:
NPIdentifier ToNPIdentifier()
{
return mIdentifier;
}
bool IsTemporary() {
return !!mTemporaryRefs;
}
/**
* Holds a perhaps-temporary identifier for the current stack frame.
*/
class MOZ_STACK_CLASS StackIdentifier
{
public:
StackIdentifier(PluginInstanceParent* inst, NPIdentifier aIdentifier);
StackIdentifier(NPObject* aObject, NPIdentifier aIdentifier);
~StackIdentifier();
operator PluginIdentifierParent*() {
return mIdentifier;
}
private:
DISALLOW_COPY_AND_ASSIGN(StackIdentifier);
PluginIdentifierParent* mIdentifier;
};
protected:
PluginIdentifierParent(NPIdentifier aIdentifier, bool aTemporary)
: mIdentifier(aIdentifier)
, mTemporaryRefs(aTemporary ? 1 : 0)
{
MOZ_COUNT_CTOR(PluginIdentifierParent);
}
virtual ~PluginIdentifierParent()
{
MOZ_COUNT_DTOR(PluginIdentifierParent);
}
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
virtual bool RecvRetain() MOZ_OVERRIDE;
void AddTemporaryRef() {
mTemporaryRefs++;
}
/**
* @returns true if the last temporary reference was removed.
*/
bool RemoveTemporaryRef() {
--mTemporaryRefs;
return !mTemporaryRefs;
}
private:
NPIdentifier mIdentifier;
unsigned int mTemporaryRefs;
};
} // namespace plugins
} // namespace mozilla
#endif // dom_plugins_PluginIdentifierParent_h

View File

@ -37,7 +37,6 @@
#include "mozilla/plugins/StreamNotifyChild.h"
#include "mozilla/plugins/BrowserStreamChild.h"
#include "mozilla/plugins/PluginStreamChild.h"
#include "PluginIdentifierChild.h"
#include "mozilla/dom/CrashReporterChild.h"
#include "nsNPAPIPlugin.h"
@ -121,6 +120,7 @@ PluginModuleChild::~PluginModuleChild()
// other similar hooks.
DeinitGraphics();
PluginScriptableObjectChild::ClearIdentifiers();
gInstance = nullptr;
}
@ -1844,38 +1844,6 @@ PluginModuleChild::AnswerNP_Initialize(NPError* _retval)
#endif
}
PPluginIdentifierChild*
PluginModuleChild::AllocPPluginIdentifierChild(const nsCString& aString,
const int32_t& aInt,
const bool& aTemporary)
{
// We cannot call SetPermanent within this function because Manager() isn't
// set up yet.
if (aString.IsVoid()) {
return new PluginIdentifierChildInt(aInt);
}
return new PluginIdentifierChildString(aString);
}
bool
PluginModuleChild::RecvPPluginIdentifierConstructor(PPluginIdentifierChild* actor,
const nsCString& aString,
const int32_t& aInt,
const bool& aTemporary)
{
if (!aTemporary) {
static_cast<PluginIdentifierChild*>(actor)->MakePermanent();
}
return true;
}
bool
PluginModuleChild::DeallocPPluginIdentifierChild(PPluginIdentifierChild* aActor)
{
delete aActor;
return true;
}
#if defined(XP_WIN)
BOOL WINAPI
PMCGetWindowInfoHook(HWND hWnd, PWINDOWINFO pwi)
@ -2180,18 +2148,11 @@ PluginModuleChild::NPN_GetStringIdentifier(const NPUTF8* aName)
if (!aName)
return 0;
PluginModuleChild* self = PluginModuleChild::current();
nsDependentCString name(aName);
PluginIdentifierChildString* ident = self->mStringIdentifiers.Get(name);
if (!ident) {
nsCString nameCopy(name);
ident = new PluginIdentifierChildString(nameCopy);
self->SendPPluginIdentifierConstructor(ident, nameCopy, -1, false);
}
ident->MakePermanent();
return ident;
PluginIdentifier ident(name);
PluginScriptableObjectChild::StackIdentifier stackID(ident);
stackID.MakePermanent();
return stackID.ToNPIdentifier();
}
void
@ -2206,23 +2167,16 @@ PluginModuleChild::NPN_GetStringIdentifiers(const NPUTF8** aNames,
NS_RUNTIMEABORT("Bad input! Headed for a crash!");
}
PluginModuleChild* self = PluginModuleChild::current();
for (int32_t index = 0; index < aNameCount; ++index) {
if (!aNames[index]) {
aIdentifiers[index] = 0;
continue;
}
nsDependentCString name(aNames[index]);
PluginIdentifierChildString* ident = self->mStringIdentifiers.Get(name);
if (!ident) {
nsCString nameCopy(name);
ident = new PluginIdentifierChildString(nameCopy);
self->SendPPluginIdentifierConstructor(ident, nameCopy, -1, false);
}
ident->MakePermanent();
aIdentifiers[index] = ident;
PluginIdentifier ident(name);
PluginScriptableObjectChild::StackIdentifier stackID(ident);
stackID.MakePermanent();
aIdentifiers[index] = stackID.ToNPIdentifier();
}
}
@ -2231,9 +2185,8 @@ PluginModuleChild::NPN_IdentifierIsString(NPIdentifier aIdentifier)
{
PLUGIN_LOG_DEBUG_FUNCTION;
PluginIdentifierChild* ident =
static_cast<PluginIdentifierChild*>(aIdentifier);
return ident->IsString();
PluginScriptableObjectChild::StackIdentifier stack(aIdentifier);
return stack.IsString();
}
NPIdentifier
@ -2242,18 +2195,10 @@ PluginModuleChild::NPN_GetIntIdentifier(int32_t aIntId)
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
PluginModuleChild* self = PluginModuleChild::current();
PluginIdentifierChildInt* ident = self->mIntIdentifiers.Get(aIntId);
if (!ident) {
nsCString voidString;
voidString.SetIsVoid(true);
ident = new PluginIdentifierChildInt(aIntId);
self->SendPPluginIdentifierConstructor(ident, voidString, aIntId, false);
}
ident->MakePermanent();
return ident;
PluginIdentifier ident(aIntId);
PluginScriptableObjectChild::StackIdentifier stackID(ident);
stackID.MakePermanent();
return stackID.ToNPIdentifier();
}
NPUTF8*
@ -2261,8 +2206,9 @@ PluginModuleChild::NPN_UTF8FromIdentifier(NPIdentifier aIdentifier)
{
PLUGIN_LOG_DEBUG_FUNCTION;
if (static_cast<PluginIdentifierChild*>(aIdentifier)->IsString()) {
return static_cast<PluginIdentifierChildString*>(aIdentifier)->ToString();
PluginScriptableObjectChild::StackIdentifier stackID(aIdentifier);
if (stackID.IsString()) {
return ToNewCString(stackID.GetString());
}
return nullptr;
}
@ -2272,8 +2218,9 @@ PluginModuleChild::NPN_IntFromIdentifier(NPIdentifier aIdentifier)
{
PLUGIN_LOG_DEBUG_FUNCTION;
if (!static_cast<PluginIdentifierChild*>(aIdentifier)->IsString()) {
return static_cast<PluginIdentifierChildInt*>(aIdentifier)->ToInt();
PluginScriptableObjectChild::StackIdentifier stackID(aIdentifier);
if (!stackID.IsString()) {
return stackID.GetInt();
}
return INT32_MIN;
}

View File

@ -30,7 +30,6 @@
#include "mozilla/plugins/PPluginModuleChild.h"
#include "mozilla/plugins/PluginInstanceChild.h"
#include "mozilla/plugins/PluginIdentifierChild.h"
#include "mozilla/plugins/PluginMessageUtils.h"
// NOTE: stolen from nsNPAPIPlugin.h
@ -77,20 +76,6 @@ protected:
virtual bool AnswerNP_GetEntryPoints(NPError* rv) MOZ_OVERRIDE;
virtual bool AnswerNP_Initialize(NPError* rv) MOZ_OVERRIDE;
virtual PPluginIdentifierChild*
AllocPPluginIdentifierChild(const nsCString& aString,
const int32_t& aInt,
const bool& aTemporary) MOZ_OVERRIDE;
virtual bool
RecvPPluginIdentifierConstructor(PPluginIdentifierChild* actor,
const nsCString& aString,
const int32_t& aInt,
const bool& aTemporary) MOZ_OVERRIDE;
virtual bool
DeallocPPluginIdentifierChild(PPluginIdentifierChild* aActor) MOZ_OVERRIDE;
virtual PPluginInstanceChild*
AllocPPluginInstanceChild(const nsCString& aMimeType,
const uint16_t& aMode,
@ -388,12 +373,6 @@ private:
*/
nsTHashtable<NPObjectData> mObjectMap;
friend class PluginIdentifierChild;
friend class PluginIdentifierChildString;
friend class PluginIdentifierChildInt;
nsDataHashtable<nsCStringHashKey, PluginIdentifierChildString*> mStringIdentifiers;
nsDataHashtable<nsUint32HashKey, PluginIdentifierChildInt*> mIntIdentifiers;
public: // called by PluginInstanceChild
/**
* Dealloc an NPObject after last-release or when the associated instance

View File

@ -28,7 +28,6 @@
#include "nsIObserverService.h"
#include "nsNPAPIPlugin.h"
#include "nsPrintfCString.h"
#include "PluginIdentifierParent.h"
#include "prsystem.h"
#include "GeckoProfiler.h"
@ -779,37 +778,6 @@ PluginModuleParent::NotifyPluginCrashed()
mPlugin->PluginCrashed(mPluginDumpID, mBrowserDumpID);
}
PPluginIdentifierParent*
PluginModuleParent::AllocPPluginIdentifierParent(const nsCString& aString,
const int32_t& aInt,
const bool& aTemporary)
{
if (aTemporary) {
NS_ERROR("Plugins don't create temporary identifiers.");
return nullptr; // should abort the plugin
}
NPIdentifier npident = aString.IsVoid() ?
mozilla::plugins::parent::_getintidentifier(aInt) :
mozilla::plugins::parent::_getstringidentifier(aString.get());
if (!npident) {
NS_WARNING("Failed to get identifier!");
return nullptr;
}
PluginIdentifierParent* ident = new PluginIdentifierParent(npident, false);
mIdentifiers.Put(npident, ident);
return ident;
}
bool
PluginModuleParent::DeallocPPluginIdentifierParent(PPluginIdentifierParent* aActor)
{
delete aActor;
return true;
}
PPluginInstanceParent*
PluginModuleParent::AllocPPluginInstanceParent(const nsCString& aMimeType,
const uint16_t& aMode,
@ -1051,44 +1019,6 @@ PluginModuleParent::AnswerNPN_UserAgent(nsCString* userAgent)
return true;
}
PluginIdentifierParent*
PluginModuleParent::GetIdentifierForNPIdentifier(NPP npp, NPIdentifier aIdentifier)
{
PluginIdentifierParent* ident;
if (mIdentifiers.Get(aIdentifier, &ident)) {
if (ident->IsTemporary()) {
ident->AddTemporaryRef();
}
return ident;
}
nsCString string;
int32_t intval = -1;
bool temporary = false;
if (mozilla::plugins::parent::_identifierisstring(aIdentifier)) {
NPUTF8* chars =
mozilla::plugins::parent::_utf8fromidentifier(aIdentifier);
if (!chars) {
return nullptr;
}
string.Adopt(chars);
temporary = !NPStringIdentifierIsPermanent(npp, aIdentifier);
}
else {
intval = mozilla::plugins::parent::_intfromidentifier(aIdentifier);
string.SetIsVoid(true);
}
ident = new PluginIdentifierParent(aIdentifier, temporary);
if (!SendPPluginIdentifierConstructor(ident, string, intval, temporary))
return nullptr;
if (!temporary) {
mIdentifiers.Put(aIdentifier, ident);
}
return ident;
}
PluginInstanceParent*
PluginModuleParent::InstCast(NPP instance)
{

View File

@ -14,6 +14,7 @@
#include "mozilla/plugins/PluginProcessParent.h"
#include "mozilla/plugins/PPluginModuleParent.h"
#include "mozilla/plugins/PluginMessageUtils.h"
#include "mozilla/plugins/PluginTypes.h"
#include "npapi.h"
#include "npfunctions.h"
#include "nsAutoPtr.h"
@ -35,7 +36,6 @@ namespace plugins {
//-----------------------------------------------------------------------------
class BrowserStreamParent;
class PluginIdentifierParent;
class PluginInstanceParent;
#ifdef XP_WIN
@ -67,14 +67,6 @@ private:
protected:
virtual PPluginIdentifierParent*
AllocPPluginIdentifierParent(const nsCString& aString,
const int32_t& aInt,
const bool& aTemporary) MOZ_OVERRIDE;
virtual bool
DeallocPPluginIdentifierParent(PPluginIdentifierParent* aActor) MOZ_OVERRIDE;
PPluginInstanceParent*
AllocPPluginInstanceParent(const nsCString& aMimeType,
const uint16_t& aMode,
@ -116,15 +108,6 @@ public:
return !IsOnCxxStack();
}
/**
* Get an identifier actor for this NPIdentifier. If this is a temporary
* identifier, the temporary refcount is increased by one. This method
* is intended only for use by StackIdentifier and the scriptable
* Enumerate hook.
*/
PluginIdentifierParent*
GetIdentifierForNPIdentifier(NPP npp, NPIdentifier aIdentifier);
void ProcessRemoteNativeEventsInInterruptCall();
void TerminateChildProcess(MessageLoop* aMsgLoop);
@ -298,7 +281,6 @@ private:
bool mClearSiteDataSupported;
bool mGetSitesWithDataSupported;
const NPNetscapeFuncs* mNPNIface;
nsDataHashtable<nsPtrHashKey<void>, PluginIdentifierParent*> mIdentifiers;
nsNPAPIPlugin* mPlugin;
ScopedMethodFactory<PluginModuleParent> mTaskFactory;
nsString mPluginDumpID;

View File

@ -6,10 +6,125 @@
#include "PluginScriptableObjectChild.h"
#include "PluginScriptableObjectUtils.h"
#include "PluginIdentifierChild.h"
#include "mozilla/plugins/PluginTypes.h"
using namespace mozilla::plugins;
/**
* NPIdentifiers in the plugin process use a tagged representation. The low bit
* stores the tag. If it's zero, the identifier is a string, and the value is a
* pointer to a StoredIdentifier. If the tag bit is 1, then the rest of the
* NPIdentifier value is the integer itself. Like the JSAPI, we require that all
* integers stored in NPIdentifier be non-negative.
*
* String identifiers are stored in the sIdentifiers hashtable to ensure
* uniqueness. The lifetime of these identifiers is only as long as the incoming
* IPC call from the chrome process. If the plugin wants to retain an
* identifier, it needs to call NPN_GetStringIdentifier, which causes the
* mPermanent flag to be set on the identifier. When this flag is set, the
* identifier is saved until the plugin process exits.
*
* The StackIdentifier RAII class is used to manage ownership of
* identifiers. Any identifier obtained from this class should not be used
* outside its scope, except when the MakePermanent() method has been called on
* it.
*
* The lifetime of an NPIdentifier in the plugin process is totally divorced
* from the lifetime of an NPIdentifier in the chrome process (where an
* NPIdentifier is stored as a jsid). The JS GC in the chrome process is able to
* trace through the entire heap, unlike in the plugin process, so there is no
* reason to retain identifiers there.
*/
PluginScriptableObjectChild::IdentifierTable PluginScriptableObjectChild::sIdentifiers;
/* static */ PluginScriptableObjectChild::StoredIdentifier*
PluginScriptableObjectChild::HashIdentifier(const nsCString& aIdentifier)
{
StoredIdentifier* stored = sIdentifiers.Get(aIdentifier);
if (stored) {
return stored;
}
stored = new StoredIdentifier(aIdentifier);
sIdentifiers.Put(aIdentifier, stored);
return stored;
}
/* static */ void
PluginScriptableObjectChild::UnhashIdentifier(StoredIdentifier* aStored)
{
MOZ_ASSERT(sIdentifiers.Get(aStored->mIdentifier));
sIdentifiers.Remove(aStored->mIdentifier);
}
/* static */ void
PluginScriptableObjectChild::ClearIdentifiers()
{
sIdentifiers.Clear();
}
PluginScriptableObjectChild::StackIdentifier::StackIdentifier(const PluginIdentifier& aIdentifier)
: mIdentifier(aIdentifier),
mStored(nullptr)
{
if (aIdentifier.type() == PluginIdentifier::TnsCString) {
mStored = PluginScriptableObjectChild::HashIdentifier(mIdentifier.get_nsCString());
}
}
PluginScriptableObjectChild::StackIdentifier::StackIdentifier(NPIdentifier aIdentifier)
: mStored(nullptr)
{
uintptr_t bits = reinterpret_cast<uintptr_t>(aIdentifier);
if (bits & 1) {
int32_t num = int32_t(bits >> 1);
mIdentifier = PluginIdentifier(num);
} else {
mStored = static_cast<StoredIdentifier*>(aIdentifier);
mIdentifier = mStored->mIdentifier;
}
}
PluginScriptableObjectChild::StackIdentifier::~StackIdentifier()
{
if (!mStored) {
return;
}
// Each StackIdentifier owns one reference to its StoredIdentifier. In
// addition, the sIdentifiers table owns a reference. If mPermanent is false
// and sIdentifiers has the last reference, then we want to remove the
// StoredIdentifier from the table (and destroy it).
StoredIdentifier *stored = mStored;
mStored = nullptr;
if (stored->mRefCnt == 1 && !stored->mPermanent) {
PluginScriptableObjectChild::UnhashIdentifier(stored);
}
}
NPIdentifier
PluginScriptableObjectChild::StackIdentifier::ToNPIdentifier() const
{
if (mStored) {
MOZ_ASSERT(mIdentifier.type() == PluginIdentifier::TnsCString);
MOZ_ASSERT((reinterpret_cast<uintptr_t>(mStored.get()) & 1) == 0);
return mStored;
}
int32_t num = mIdentifier.get_int32_t();
// The JS engine imposes this condition on int32s in jsids, so we assume it.
MOZ_ASSERT(num >= 0);
return reinterpret_cast<NPIdentifier>((num << 1) | 1);
}
static PluginIdentifier
FromNPIdentifier(NPIdentifier aIdentifier)
{
PluginScriptableObjectChild::StackIdentifier stack(aIdentifier);
return stack.GetIdentifier();
}
// static
NPObject*
PluginScriptableObjectChild::ScriptableAllocate(NPP aInstance,
@ -85,7 +200,7 @@ PluginScriptableObjectChild::ScriptableHasMethod(NPObject* aObject,
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
bool result;
actor->CallHasMethod(static_cast<PPluginIdentifierChild*>(aName), &result);
actor->CallHasMethod(FromNPIdentifier(aName), &result);
return result;
}
@ -122,7 +237,7 @@ PluginScriptableObjectChild::ScriptableInvoke(NPObject* aObject,
Variant remoteResult;
bool success;
actor->CallInvoke(static_cast<PPluginIdentifierChild*>(aName), args,
actor->CallInvoke(FromNPIdentifier(aName), args,
&remoteResult, &success);
if (!success) {
@ -196,7 +311,7 @@ PluginScriptableObjectChild::ScriptableHasProperty(NPObject* aObject,
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
bool result;
actor->CallHasProperty(static_cast<PPluginIdentifierChild*>(aName), &result);
actor->CallHasProperty(FromNPIdentifier(aName), &result);
return result;
}
@ -225,7 +340,7 @@ PluginScriptableObjectChild::ScriptableGetProperty(NPObject* aObject,
Variant result;
bool success;
actor->CallGetParentProperty(static_cast<PPluginIdentifierChild*>(aName),
actor->CallGetParentProperty(FromNPIdentifier(aName),
&result, &success);
if (!success) {
@ -265,7 +380,7 @@ PluginScriptableObjectChild::ScriptableSetProperty(NPObject* aObject,
}
bool success;
actor->CallSetProperty(static_cast<PPluginIdentifierChild*>(aName), value,
actor->CallSetProperty(FromNPIdentifier(aName), value,
&success);
return success;
@ -293,7 +408,7 @@ PluginScriptableObjectChild::ScriptableRemoveProperty(NPObject* aObject,
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
bool success;
actor->CallRemoveProperty(static_cast<PPluginIdentifierChild*>(aName),
actor->CallRemoveProperty(FromNPIdentifier(aName),
&success);
return success;
@ -321,7 +436,7 @@ PluginScriptableObjectChild::ScriptableEnumerate(NPObject* aObject,
NS_ASSERTION(actor, "This shouldn't ever be null!");
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
AutoInfallibleTArray<PPluginIdentifierChild*, 10> identifiers;
AutoInfallibleTArray<PluginIdentifier, 10> identifiers;
bool success;
actor->CallEnumerate(&identifiers, &success);
@ -343,8 +458,10 @@ PluginScriptableObjectChild::ScriptableEnumerate(NPObject* aObject,
}
for (uint32_t index = 0; index < *aCount; index++) {
(*aIdentifiers)[index] =
static_cast<PPluginIdentifierChild*>(identifiers[index]);
StackIdentifier id(identifiers[index]);
// Make the id permanent in case the plugin retains it.
id.MakePermanent();
(*aIdentifiers)[index] = id.ToNPIdentifier();
}
return true;
}
@ -610,7 +727,7 @@ PluginScriptableObjectChild::AnswerInvalidate()
}
bool
PluginScriptableObjectChild::AnswerHasMethod(PPluginIdentifierChild* aId,
PluginScriptableObjectChild::AnswerHasMethod(const PluginIdentifier& aId,
bool* aHasMethod)
{
AssertPluginThread();
@ -629,13 +746,13 @@ PluginScriptableObjectChild::AnswerHasMethod(PPluginIdentifierChild* aId,
return true;
}
PluginIdentifierChild::StackIdentifier id(aId);
*aHasMethod = mObject->_class->hasMethod(mObject, id->ToNPIdentifier());
StackIdentifier id(aId);
*aHasMethod = mObject->_class->hasMethod(mObject, id.ToNPIdentifier());
return true;
}
bool
PluginScriptableObjectChild::AnswerInvoke(PPluginIdentifierChild* aId,
PluginScriptableObjectChild::AnswerInvoke(const PluginIdentifier& aId,
const InfallibleTArray<Variant>& aArgs,
Variant* aResult,
bool* aSuccess)
@ -673,8 +790,8 @@ PluginScriptableObjectChild::AnswerInvoke(PPluginIdentifierChild* aId,
NPVariant result;
VOID_TO_NPVARIANT(result);
PluginIdentifierChild::StackIdentifier id(aId);
bool success = mObject->_class->invoke(mObject, id->ToNPIdentifier(),
StackIdentifier id(aId);
bool success = mObject->_class->invoke(mObject, id.ToNPIdentifier(),
convertedArgs.Elements(), argCount,
&result);
@ -775,7 +892,7 @@ PluginScriptableObjectChild::AnswerInvokeDefault(const InfallibleTArray<Variant>
}
bool
PluginScriptableObjectChild::AnswerHasProperty(PPluginIdentifierChild* aId,
PluginScriptableObjectChild::AnswerHasProperty(const PluginIdentifier& aId,
bool* aHasProperty)
{
AssertPluginThread();
@ -794,13 +911,13 @@ PluginScriptableObjectChild::AnswerHasProperty(PPluginIdentifierChild* aId,
return true;
}
PluginIdentifierChild::StackIdentifier id(aId);
*aHasProperty = mObject->_class->hasProperty(mObject, id->ToNPIdentifier());
StackIdentifier id(aId);
*aHasProperty = mObject->_class->hasProperty(mObject, id.ToNPIdentifier());
return true;
}
bool
PluginScriptableObjectChild::AnswerGetChildProperty(PPluginIdentifierChild* aId,
PluginScriptableObjectChild::AnswerGetChildProperty(const PluginIdentifier& aId,
bool* aHasProperty,
bool* aHasMethod,
Variant* aResult,
@ -824,8 +941,8 @@ PluginScriptableObjectChild::AnswerGetChildProperty(PPluginIdentifierChild* aId,
return true;
}
PluginIdentifierChild::StackIdentifier stackID(aId);
NPIdentifier id = stackID->ToNPIdentifier();
StackIdentifier stackID(aId);
NPIdentifier id = stackID.ToNPIdentifier();
*aHasProperty = mObject->_class->hasProperty(mObject, id);
*aHasMethod = mObject->_class->hasMethod(mObject, id);
@ -850,7 +967,7 @@ PluginScriptableObjectChild::AnswerGetChildProperty(PPluginIdentifierChild* aId,
}
bool
PluginScriptableObjectChild::AnswerSetProperty(PPluginIdentifierChild* aId,
PluginScriptableObjectChild::AnswerSetProperty(const PluginIdentifier& aId,
const Variant& aValue,
bool* aSuccess)
{
@ -871,8 +988,8 @@ PluginScriptableObjectChild::AnswerSetProperty(PPluginIdentifierChild* aId,
return true;
}
PluginIdentifierChild::StackIdentifier stackID(aId);
NPIdentifier id = stackID->ToNPIdentifier();
StackIdentifier stackID(aId);
NPIdentifier id = stackID.ToNPIdentifier();
if (!mObject->_class->hasProperty(mObject, id)) {
*aSuccess = false;
@ -889,7 +1006,7 @@ PluginScriptableObjectChild::AnswerSetProperty(PPluginIdentifierChild* aId,
}
bool
PluginScriptableObjectChild::AnswerRemoveProperty(PPluginIdentifierChild* aId,
PluginScriptableObjectChild::AnswerRemoveProperty(const PluginIdentifier& aId,
bool* aSuccess)
{
AssertPluginThread();
@ -909,8 +1026,8 @@ PluginScriptableObjectChild::AnswerRemoveProperty(PPluginIdentifierChild* aId,
return true;
}
PluginIdentifierChild::StackIdentifier stackID(aId);
NPIdentifier id = stackID->ToNPIdentifier();
StackIdentifier stackID(aId);
NPIdentifier id = stackID.ToNPIdentifier();
*aSuccess = mObject->_class->hasProperty(mObject, id) ?
mObject->_class->removeProperty(mObject, id) :
true;
@ -919,7 +1036,7 @@ PluginScriptableObjectChild::AnswerRemoveProperty(PPluginIdentifierChild* aId,
}
bool
PluginScriptableObjectChild::AnswerEnumerate(InfallibleTArray<PPluginIdentifierChild*>* aProperties,
PluginScriptableObjectChild::AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
bool* aSuccess)
{
AssertPluginThread();
@ -948,8 +1065,7 @@ PluginScriptableObjectChild::AnswerEnumerate(InfallibleTArray<PPluginIdentifierC
aProperties->SetCapacity(idCount);
for (uint32_t index = 0; index < idCount; index++) {
PluginIdentifierChild* id = static_cast<PluginIdentifierChild*>(ids[index]);
aProperties->AppendElement(id);
aProperties->AppendElement(FromNPIdentifier(ids[index]));
}
PluginModuleChild::sBrowserFuncs.memfree(ids);

View File

@ -9,15 +9,16 @@
#include "mozilla/plugins/PPluginScriptableObjectChild.h"
#include "mozilla/plugins/PluginMessageUtils.h"
#include "mozilla/plugins/PluginTypes.h"
#include "npruntime.h"
#include "nsDataHashtable.h"
namespace mozilla {
namespace plugins {
class PluginInstanceChild;
class PluginScriptableObjectChild;
class PPluginIdentifierChild;
struct ChildNPObject : NPObject
{
@ -57,11 +58,11 @@ public:
AnswerInvalidate() MOZ_OVERRIDE;
virtual bool
AnswerHasMethod(PPluginIdentifierChild* aId,
AnswerHasMethod(const PluginIdentifier& aId,
bool* aHasMethod) MOZ_OVERRIDE;
virtual bool
AnswerInvoke(PPluginIdentifierChild* aId,
AnswerInvoke(const PluginIdentifier& aId,
const InfallibleTArray<Variant>& aArgs,
Variant* aResult,
bool* aSuccess) MOZ_OVERRIDE;
@ -72,27 +73,27 @@ public:
bool* aSuccess) MOZ_OVERRIDE;
virtual bool
AnswerHasProperty(PPluginIdentifierChild* aId,
AnswerHasProperty(const PluginIdentifier& aId,
bool* aHasProperty) MOZ_OVERRIDE;
virtual bool
AnswerGetChildProperty(PPluginIdentifierChild* aId,
AnswerGetChildProperty(const PluginIdentifier& aId,
bool* aHasProperty,
bool* aHasMethod,
Variant* aResult,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool
AnswerSetProperty(PPluginIdentifierChild* aId,
AnswerSetProperty(const PluginIdentifier& aId,
const Variant& aValue,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool
AnswerRemoveProperty(PPluginIdentifierChild* aId,
AnswerRemoveProperty(const PluginIdentifier& aId,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool
AnswerEnumerate(InfallibleTArray<PPluginIdentifierChild*>* aProperties,
AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool
@ -156,6 +157,66 @@ public:
return mType;
}
private:
struct StoredIdentifier
{
nsCString mIdentifier;
nsAutoRefCnt mRefCnt;
bool mPermanent;
nsrefcnt AddRef() {
++mRefCnt;
return mRefCnt;
}
nsrefcnt Release() {
--mRefCnt;
if (mRefCnt == 0) {
delete this;
return 0;
}
return mRefCnt;
}
explicit StoredIdentifier(const nsCString& aIdentifier)
: mIdentifier(aIdentifier), mRefCnt(), mPermanent(false)
{ MOZ_COUNT_CTOR(StoredIdentifier); }
~StoredIdentifier() { MOZ_COUNT_DTOR(StoredIdentifier); }
};
public:
class MOZ_STACK_CLASS StackIdentifier
{
public:
StackIdentifier(const PluginIdentifier& aIdentifier);
StackIdentifier(NPIdentifier aIdentifier);
~StackIdentifier();
void MakePermanent()
{
if (mStored) {
mStored->mPermanent = true;
}
}
NPIdentifier ToNPIdentifier() const;
bool IsString() const { return mIdentifier.type() == PluginIdentifier::TnsCString; }
const nsCString& GetString() const { return mIdentifier.get_nsCString(); }
int32_t GetInt() const { return mIdentifier.get_int32_t(); }
PluginIdentifier GetIdentifier() const { return mIdentifier; }
private:
DISALLOW_COPY_AND_ASSIGN(StackIdentifier);
PluginIdentifier mIdentifier;
nsRefPtr<StoredIdentifier> mStored;
};
static void ClearIdentifiers();
private:
static NPObject*
ScriptableAllocate(NPP aInstance,
@ -230,6 +291,12 @@ private:
ScriptableObjectType mType;
static const NPClass sNPClass;
static StoredIdentifier* HashIdentifier(const nsCString& aIdentifier);
static void UnhashIdentifier(StoredIdentifier* aIdentifier);
typedef nsDataHashtable<nsCStringHashKey, nsRefPtr<StoredIdentifier>> IdentifierTable;
static IdentifierTable sIdentifiers;
};
} /* namespace plugins */

View File

@ -6,15 +6,103 @@
#include "PluginScriptableObjectParent.h"
#include "jsapi.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/plugins/PluginIdentifierParent.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/plugins/PluginTypes.h"
#include "mozilla/unused.h"
#include "nsNPAPIPlugin.h"
#include "PluginScriptableObjectUtils.h"
using namespace mozilla;
using namespace mozilla::plugins;
using namespace mozilla::plugins::parent;
/**
* NPIdentifiers in the chrome process are stored as jsids. The difficulty is in
* ensuring that string identifiers are rooted without interning them all. We
* assume that all NPIdentifiers passed into nsJSNPRuntime will not be used
* outside the scope of the NPAPI call (i.e., they won't be stored in the
* heap). Rooting is done using the StackIdentifier class, which roots the
* identifier via RootedId.
*
* This system does not allow jsids to be moved, as would be needed for
* generational or compacting GC. When Firefox implements a moving GC for
* strings, we will need to ensure that no movement happens while NPAPI code is
* on the stack: although StackIdentifier roots all identifiers used, the GC has
* no way to no that a jsid cast to an NPIdentifier needs to be fixed up if it
* is moved.
*/
class MOZ_STACK_CLASS StackIdentifier
{
public:
StackIdentifier(const PluginIdentifier& aIdentifier, bool aIntern = false);
bool Failed() const { return mFailed; }
NPIdentifier ToNPIdentifier() const { return mIdentifier; }
private:
bool mFailed;
NPIdentifier mIdentifier;
AutoSafeJSContext mCx;
JS::RootedId mId;
};
StackIdentifier::StackIdentifier(const PluginIdentifier& aIdentifier, bool aIntern)
: mFailed(false),
mId(mCx)
{
if (aIdentifier.type() == PluginIdentifier::TnsCString) {
// We don't call _getstringidentifier because we may not want to intern the string.
NS_ConvertUTF8toUTF16 utf16name(aIdentifier.get_nsCString());
JS::RootedString str(mCx, JS_NewUCStringCopyN(mCx, utf16name.get(), utf16name.Length()));
if (!str) {
NS_ERROR("Id can't be allocated");
mFailed = true;
return;
}
if (aIntern) {
str = JS_InternJSString(mCx, str);
if (!str) {
NS_ERROR("Id can't be allocated");
mFailed = true;
return;
}
}
if (!JS_StringToId(mCx, str, &mId)) {
NS_ERROR("Id can't be allocated");
mFailed = true;
return;
}
mIdentifier = JSIdToNPIdentifier(mId);
return;
}
mIdentifier = mozilla::plugins::parent::_getintidentifier(aIdentifier.get_int32_t());
}
static bool
FromNPIdentifier(NPIdentifier aIdentifier, PluginIdentifier* aResult)
{
if (mozilla::plugins::parent::_identifierisstring(aIdentifier)) {
nsCString string;
NPUTF8* chars =
mozilla::plugins::parent::_utf8fromidentifier(aIdentifier);
if (!chars) {
return false;
}
string.Adopt(chars);
*aResult = PluginIdentifier(string);
return true;
}
else {
int32_t intval = mozilla::plugins::parent::_intfromidentifier(aIdentifier);
*aResult = PluginIdentifier(intval);
return true;
}
}
namespace {
inline void
@ -105,8 +193,8 @@ PluginScriptableObjectParent::ScriptableHasMethod(NPObject* aObject,
return false;
}
PluginIdentifierParent::StackIdentifier identifier(aObject, aName);
if (!identifier) {
PluginIdentifier identifier;
if (!FromNPIdentifier(aName, &identifier)) {
return false;
}
@ -145,8 +233,8 @@ PluginScriptableObjectParent::ScriptableInvoke(NPObject* aObject,
return false;
}
PluginIdentifierParent::StackIdentifier identifier(aObject, aName);
if (!identifier) {
PluginIdentifier identifier;
if (!FromNPIdentifier(aName, &identifier)) {
return false;
}
@ -247,8 +335,8 @@ PluginScriptableObjectParent::ScriptableHasProperty(NPObject* aObject,
return false;
}
PluginIdentifierParent::StackIdentifier identifier(aObject, aName);
if (!identifier) {
PluginIdentifier identifier;
if (!FromNPIdentifier(aName, &identifier)) {
return false;
}
@ -296,8 +384,8 @@ PluginScriptableObjectParent::ScriptableSetProperty(NPObject* aObject,
return false;
}
PluginIdentifierParent::StackIdentifier identifier(aObject, aName);
if (!identifier) {
PluginIdentifier identifier;
if (!FromNPIdentifier(aName, &identifier)) {
return false;
}
@ -339,8 +427,8 @@ PluginScriptableObjectParent::ScriptableRemoveProperty(NPObject* aObject,
return false;
}
PluginIdentifierParent::StackIdentifier identifier(aObject, aName);
if (!identifier) {
PluginIdentifier identifier;
if (!FromNPIdentifier(aName, &identifier)) {
return false;
}
@ -385,7 +473,7 @@ PluginScriptableObjectParent::ScriptableEnumerate(NPObject* aObject,
return false;
}
AutoInfallibleTArray<PPluginIdentifierParent*, 10> identifiers;
AutoInfallibleTArray<PluginIdentifier, 10> identifiers;
bool success;
if (!actor->CallEnumerate(&identifiers, &success)) {
NS_WARNING("Failed to send message!");
@ -409,9 +497,13 @@ PluginScriptableObjectParent::ScriptableEnumerate(NPObject* aObject,
}
for (uint32_t index = 0; index < *aCount; index++) {
PluginIdentifierParent* id =
static_cast<PluginIdentifierParent*>(identifiers[index]);
(*aIdentifiers)[index] = id->ToNPIdentifier();
// We intern the ID to avoid a GC hazard here. This could probably be fixed
// if the interface with nsJSNPRuntime were smarter.
StackIdentifier stackID(identifiers[index], true /* aIntern */);
if (stackID.Failed()) {
return false;
}
(*aIdentifiers)[index] = stackID.ToNPIdentifier();
}
return true;
}
@ -649,7 +741,7 @@ PluginScriptableObjectParent::ActorDestroy(ActorDestroyReason aWhy)
}
bool
PluginScriptableObjectParent::AnswerHasMethod(PPluginIdentifierParent* aId,
PluginScriptableObjectParent::AnswerHasMethod(const PluginIdentifier& aId,
bool* aHasMethod)
{
if (!mObject) {
@ -675,13 +767,17 @@ PluginScriptableObjectParent::AnswerHasMethod(PPluginIdentifierParent* aId,
return true;
}
PluginIdentifierParent* id = static_cast<PluginIdentifierParent*>(aId);
*aHasMethod = npn->hasmethod(instance->GetNPP(), mObject, id->ToNPIdentifier());
StackIdentifier stackID(aId);
if (stackID.Failed()) {
*aHasMethod = false;
return true;
}
*aHasMethod = npn->hasmethod(instance->GetNPP(), mObject, stackID.ToNPIdentifier());
return true;
}
bool
PluginScriptableObjectParent::AnswerInvoke(PPluginIdentifierParent* aId,
PluginScriptableObjectParent::AnswerInvoke(const PluginIdentifier& aId,
const InfallibleTArray<Variant>& aArgs,
Variant* aResult,
bool* aSuccess)
@ -712,6 +808,13 @@ PluginScriptableObjectParent::AnswerInvoke(PPluginIdentifierParent* aId,
return true;
}
StackIdentifier stackID(aId);
if (stackID.Failed()) {
*aResult = void_t();
*aSuccess = false;
return true;
}
AutoFallibleTArray<NPVariant, 10> convertedArgs;
uint32_t argCount = aArgs.Length();
@ -733,9 +836,8 @@ PluginScriptableObjectParent::AnswerInvoke(PPluginIdentifierParent* aId,
}
}
PluginIdentifierParent* id = static_cast<PluginIdentifierParent*>(aId);
NPVariant result;
bool success = npn->invoke(instance->GetNPP(), mObject, id->ToNPIdentifier(),
bool success = npn->invoke(instance->GetNPP(), mObject, stackID.ToNPIdentifier(),
convertedArgs.Elements(), argCount, &result);
for (uint32_t index = 0; index < argCount; index++) {
@ -848,7 +950,7 @@ PluginScriptableObjectParent::AnswerInvokeDefault(const InfallibleTArray<Variant
}
bool
PluginScriptableObjectParent::AnswerHasProperty(PPluginIdentifierParent* aId,
PluginScriptableObjectParent::AnswerHasProperty(const PluginIdentifier& aId,
bool* aHasProperty)
{
if (!mObject) {
@ -874,15 +976,20 @@ PluginScriptableObjectParent::AnswerHasProperty(PPluginIdentifierParent* aId,
return true;
}
PluginIdentifierParent* id = static_cast<PluginIdentifierParent*>(aId);
StackIdentifier stackID(aId);
if (stackID.Failed()) {
*aHasProperty = false;
return true;
}
*aHasProperty = npn->hasproperty(instance->GetNPP(), mObject,
id->ToNPIdentifier());
stackID.ToNPIdentifier());
return true;
}
bool
PluginScriptableObjectParent::AnswerGetParentProperty(
PPluginIdentifierParent* aId,
const PluginIdentifier& aId,
Variant* aResult,
bool* aSuccess)
{
@ -912,9 +1019,15 @@ PluginScriptableObjectParent::AnswerGetParentProperty(
return true;
}
PluginIdentifierParent* id = static_cast<PluginIdentifierParent*>(aId);
StackIdentifier stackID(aId);
if (stackID.Failed()) {
*aResult = void_t();
*aSuccess = false;
return true;
}
NPVariant result;
if (!npn->getproperty(instance->GetNPP(), mObject, id->ToNPIdentifier(),
if (!npn->getproperty(instance->GetNPP(), mObject, stackID.ToNPIdentifier(),
&result)) {
*aResult = void_t();
*aSuccess = false;
@ -934,7 +1047,7 @@ PluginScriptableObjectParent::AnswerGetParentProperty(
}
bool
PluginScriptableObjectParent::AnswerSetProperty(PPluginIdentifierParent* aId,
PluginScriptableObjectParent::AnswerSetProperty(const PluginIdentifier& aId,
const Variant& aValue,
bool* aSuccess)
{
@ -967,16 +1080,21 @@ PluginScriptableObjectParent::AnswerSetProperty(PPluginIdentifierParent* aId,
return true;
}
PluginIdentifierParent* id = static_cast<PluginIdentifierParent*>(aId);
StackIdentifier stackID(aId);
if (stackID.Failed()) {
*aSuccess = false;
return true;
}
if ((*aSuccess = npn->setproperty(instance->GetNPP(), mObject,
id->ToNPIdentifier(), &converted))) {
stackID.ToNPIdentifier(), &converted))) {
ReleaseVariant(converted, instance);
}
return true;
}
bool
PluginScriptableObjectParent::AnswerRemoveProperty(PPluginIdentifierParent* aId,
PluginScriptableObjectParent::AnswerRemoveProperty(const PluginIdentifier& aId,
bool* aSuccess)
{
if (!mObject) {
@ -1002,14 +1120,19 @@ PluginScriptableObjectParent::AnswerRemoveProperty(PPluginIdentifierParent* aId,
return true;
}
PluginIdentifierParent* id = static_cast<PluginIdentifierParent*>(aId);
StackIdentifier stackID(aId);
if (stackID.Failed()) {
*aSuccess = false;
return true;
}
*aSuccess = npn->removeproperty(instance->GetNPP(), mObject,
id->ToNPIdentifier());
stackID.ToNPIdentifier());
return true;
}
bool
PluginScriptableObjectParent::AnswerEnumerate(InfallibleTArray<PPluginIdentifierParent*>* aProperties,
PluginScriptableObjectParent::AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
bool* aSuccess)
{
if (!mObject) {
@ -1044,21 +1167,12 @@ PluginScriptableObjectParent::AnswerEnumerate(InfallibleTArray<PPluginIdentifier
aProperties->SetCapacity(idCount);
mozilla::AutoSafeJSContext cx;
for (uint32_t index = 0; index < idCount; index++) {
// Because of GC hazards, all identifiers returned from enumerate
// must be made permanent.
if (_identifierisstring(ids[index])) {
JS::Rooted<JSString*> str(cx, NPIdentifierToString(ids[index]));
if (!JS_StringHasBeenInterned(cx, str)) {
DebugOnly<JSString*> str2 = JS_InternJSString(cx, str);
NS_ASSERTION(str2 == str, "Interning a JS string which is currently an ID should return itself.");
}
PluginIdentifier id;
if (!FromNPIdentifier(ids[index], &id)) {
return false;
}
PluginIdentifierParent* id =
instance->Module()->GetIdentifierForNPIdentifier(instance->GetNPP(), ids[index]);
aProperties->AppendElement(id);
NS_ASSERTION(!id->IsTemporary(), "Should only have permanent identifiers!");
}
npn->memfree(ids);
@ -1229,8 +1343,8 @@ PluginScriptableObjectParent::GetPropertyHelper(NPIdentifier aName,
return false;
}
PluginIdentifierParent::StackIdentifier identifier(GetInstance(), aName);
if (!identifier) {
PluginIdentifier identifier;
if (!FromNPIdentifier(aName, &identifier)) {
return false;
}

View File

@ -18,7 +18,6 @@ namespace plugins {
class PluginInstanceParent;
class PluginScriptableObjectParent;
class PPluginIdentifierParent;
struct ParentNPObject : NPObject
{
@ -49,11 +48,11 @@ public:
ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
virtual bool
AnswerHasMethod(PPluginIdentifierParent* aId,
AnswerHasMethod(const PluginIdentifier& aId,
bool* aHasMethod) MOZ_OVERRIDE;
virtual bool
AnswerInvoke(PPluginIdentifierParent* aId,
AnswerInvoke(const PluginIdentifier& aId,
const InfallibleTArray<Variant>& aArgs,
Variant* aResult,
bool* aSuccess) MOZ_OVERRIDE;
@ -64,25 +63,25 @@ public:
bool* aSuccess) MOZ_OVERRIDE;
virtual bool
AnswerHasProperty(PPluginIdentifierParent* aId,
AnswerHasProperty(const PluginIdentifier& aId,
bool* aHasProperty) MOZ_OVERRIDE;
virtual bool
AnswerGetParentProperty(PPluginIdentifierParent* aId,
AnswerGetParentProperty(const PluginIdentifier& aId,
Variant* aResult,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool
AnswerSetProperty(PPluginIdentifierParent* aId,
AnswerSetProperty(const PluginIdentifier& aId,
const Variant& aValue,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool
AnswerRemoveProperty(PPluginIdentifierParent* aId,
AnswerRemoveProperty(const PluginIdentifier& aId,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool
AnswerEnumerate(InfallibleTArray<PPluginIdentifierParent*>* aProperties,
AnswerEnumerate(InfallibleTArray<PluginIdentifier>* aProperties,
bool* aSuccess) MOZ_OVERRIDE;
virtual bool

View File

@ -0,0 +1,16 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* 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/. */
namespace mozilla {
namespace plugins {
union PluginIdentifier
{
nsCString;
int32_t;
};
} // namespace plugins
} // namespace mozilla

View File

@ -21,8 +21,6 @@ EXPORTS.mozilla.plugins += [
'NPEventOSX.h',
'NPEventUnix.h',
'NPEventWindows.h',
'PluginIdentifierChild.h',
'PluginIdentifierParent.h',
'PluginInstanceChild.h',
'PluginInstanceParent.h',
'PluginMessageUtils.h',
@ -79,8 +77,6 @@ UNIFIED_SOURCES += [
'ChildAsyncCall.cpp',
'ChildTimer.cpp',
'PluginBackgroundDestroyer.cpp',
'PluginIdentifierChild.cpp',
'PluginIdentifierParent.cpp',
'PluginInstanceParent.cpp',
'PluginMessageUtils.cpp',
'PluginModuleParent.cpp',
@ -105,8 +101,8 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
IPDL_SOURCES += [
'PBrowserStream.ipdl',
'PluginTypes.ipdlh',
'PPluginBackgroundDestroyer.ipdl',
'PPluginIdentifier.ipdl',
'PPluginInstance.ipdl',
'PPluginModule.ipdl',
'PPluginScriptableObject.ipdl',

View File

@ -313,6 +313,8 @@ Promise::Promise(nsIGlobalObject* aGlobal)
MOZ_ASSERT(mGlobal);
mozilla::HoldJSObjects(this);
mCreationTimestamp = TimeStamp::Now();
}
Promise::~Promise()
@ -1172,6 +1174,7 @@ Promise::RunResolveTask(JS::Handle<JS::Value> aValue,
SetResult(aValue);
SetState(aState);
mSettlementTimestamp = TimeStamp::Now();
// If the Promise was rejected, and there is no reject handler already setup,
// watch for thread shutdown.

View File

@ -9,6 +9,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/TypeTraits.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "nsCycleCollectionParticipant.h"
@ -322,6 +323,12 @@ private:
// console before the worker's context is deleted. This feature is used for
// that purpose.
nsAutoPtr<PromiseReportRejectFeature> mFeature;
// The time when this promise was created.
TimeStamp mCreationTimestamp;
// The time when this promise transitioned out of the pending state.
TimeStamp mSettlementTimestamp;
};
} // namespace dom

View File

@ -8,6 +8,7 @@
#include "js/Value.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseDebuggingBinding.h"
@ -64,5 +65,23 @@ PromiseDebugging::GetDependentPromises(GlobalObject&, Promise& aPromise,
aPromise.GetDependentPromises(aPromises);
}
/* static */ double
PromiseDebugging::GetPromiseLifetime(GlobalObject&, Promise& aPromise)
{
return (TimeStamp::Now() - aPromise.mCreationTimestamp).ToMilliseconds();
}
/* static */ double
PromiseDebugging::GetTimeToSettle(GlobalObject&, Promise& aPromise,
ErrorResult& aRv)
{
if (aPromise.mState == Promise::Pending) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return 0;
}
return (aPromise.mSettlementTimestamp -
aPromise.mCreationTimestamp).ToMilliseconds();
}
} // namespace dom
} // namespace mozilla

View File

@ -12,6 +12,9 @@
#include "nsRefPtr.h"
namespace mozilla {
class ErrorResult;
namespace dom {
class Promise;
@ -32,6 +35,9 @@ public:
JS::MutableHandle<JSObject*> aStack);
static void GetDependentPromises(GlobalObject&, Promise& aPromise,
nsTArray<nsRefPtr<Promise>>& aPromises);
static double GetPromiseLifetime(GlobalObject&, Promise& aPromise);
static double GetTimeToSettle(GlobalObject&, Promise& aPromise,
ErrorResult& aRv);
};
} // namespace dom

View File

@ -5,6 +5,8 @@
#ifndef mozilla_dom_PromiseWorkerProxy_h
#define mozilla_dom_PromiseWorkerProxy_h
// Required for Promise::PromiseTaskSync.
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/workers/bindings/WorkerFeature.h"
#include "nsProxyRelease.h"
@ -43,6 +45,16 @@ class WorkerPrivate;
// 4. Then the Promise results returned by ResolvedCallback/RejectedCallback
// will be dispatched as a WorkerRunnable to the worker thread to
// resolve/reject the Promise created at #1.
//
// PromiseWorkerProxy can also be used in situations where there is no main
// thread Promise, or where special handling is required on the worker thread
// for promise resolution. Create a PromiseWorkerProxy as in steps 1 and
// 2 above. When the main thread is ready to resolve the worker thread promise,
// dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the
// worker. This might be null! In the WorkerRunnable's WorkerRun() use
// GetWorkerPromise() to access the Promise and resolve/reject it. Then call
// CleanUp() on the worker
// thread.
class PromiseWorkerProxy : public PromiseNativeHandler,
public workers::WorkerFeature

View File

@ -31,33 +31,6 @@ function waitForManagerEvent(aEventName) {
return deferred.promise;
}
/**
* Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject.
*
* Fulfill params: A DOMEvent.
* Reject params: A DOMEvent.
*
* @param aRequest
* A DOMRequest instance.
*
* @return A deferred promise.
*/
function wrapDomRequestAsPromise(aRequest) {
let deferred = Promise.defer();
ok(aRequest instanceof DOMRequest,
"aRequest is instanceof " + aRequest.constructor);
aRequest.addEventListener("success", function(aEvent) {
deferred.resolve(aEvent);
});
aRequest.addEventListener("error", function(aEvent) {
deferred.reject(aEvent);
});
return deferred.promise;
}
/**
* Configures call forward options.
*
@ -73,8 +46,7 @@ function wrapDomRequestAsPromise(aRequest) {
*/
function setCallForwardingOption(aOptions) {
let request = connection.setCallForwardingOption(aOptions);
return wrapDomRequestAsPromise(request)
.then(null, () => { throw request.error; });
return request.then(null, () => { throw request.error; });
}
const TEST_DATA = [

View File

@ -64,16 +64,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=667388
true,
"Chrome should be able to clone content object");
// COWs without __exposedProps__ silently deny property gets. So Content
// should be able to clone a COW, but the result of the clone should be empty.
var p = contentWin.wrappedJSObject.tryToClone(window.testObject, true,
"Content should be able to clone COW");
return new Promise(function(resolve) {
p.then(function(cloneResult) {
is(Cu.waiveXrays(cloneResult).toSource(), "({})", "Empty COW clone");
resolve();
});
});
return Promise.resolve();
}
// Test cloning between content and content.

View File

@ -2,6 +2,8 @@
support-files =
test_headers_common.js
test_headers_mainthread.js
worker_test_fetch_basic.js
worker_wrapper.js
[test_headers.html]
[test_fetch_basic.html]

View File

@ -0,0 +1,57 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1039846 - Test fetch() function in worker</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script type="text/javascript" src="worker_test_fetch_basic.js"> </script>
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
function testOnWorker(done) {
ok(true, "=== Start Worker Tests ===");
var worker = new Worker("worker_test_fetch_basic.js");
worker.onmessage = function(event) {
if (event.data.type == "finish") {
ok(true, "=== Finish Worker Tests ===");
done();
} else if (event.data.type == "status") {
ok(event.data.status, event.data.msg);
}
}
worker.onerror = function(event) {
ok(false, "Worker had an error: " + event.data);
ok(true, "=== Finish Worker Tests ===");
done();
};
worker.postMessage("start");
}
//
// Driver
//
SpecialPowers.pushPrefEnv({"set": [
["dom.fetch.enabled", true]
]}, function() {
testOnWorker(function() {
SimpleTest.finish();
});
});
</script>
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,94 @@
if (typeof ok !== "function") {
function ok(a, msg) {
dump("OK: " + !!a + " => " + a + " " + msg + "\n");
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
}
}
if (typeof is !== "function") {
function is(a, b, msg) {
dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
}
}
function testAboutURL() {
var p1 = fetch('about:blank').then(function(res) {
is(res.status, 200, "about:blank should load a valid Response");
is(res.headers.get('content-type'), 'text/html;charset=utf-8',
"about:blank content-type should be text/html;charset=utf-8");
return res.text().then(function(v) {
is(v, "", "about:blank body should be empty");
});
});
var p2 = fetch('about:config').then(function(res) {
is(res.type, "error", "about:config should fail");
});
return Promise.all([p1, p2]);
}
function testDataURL() {
return fetch("data:text/plain;charset=UTF-8,Hello").then(function(res) {
ok(true, "Data URL fetch should resolve");
if (res.type == "error") {
ok(false, "Data URL fetch should not fail.");
return Promise.reject();
}
ok(res instanceof Response, "Fetch should resolve to a Response");
is(res.status, 200, "Data URL status should be 200");
is(res.statusText, "OK", "Data URL statusText should be OK");
ok(res.headers.has("content-type"), "Headers must have Content-Type header");
is(res.headers.get("content-type"), "text/plain;charset=UTF-8", "Content-Type header should match specified value");
return res.text().then(function(body) {
is(body, "Hello", "Data URL Body should match");
});
});
}
function testSameOriginBlobURL() {
var blob = new Blob(["english ", "sentence"], { type: "text/plain" });
var url = URL.createObjectURL(blob);
return fetch(url).then(function(res) {
ok(true, "Blob URL fetch should resolve");
if (res.type == "error") {
ok(false, "Blob URL fetch should not fail.");
return Promise.reject();
}
ok(res instanceof Response, "Fetch should resolve to a Response");
is(res.status, 200, "Blob fetch status should be 200");
is(res.statusText, "OK", "Blob fetch statusText should be OK");
ok(res.headers.has("content-type"), "Headers must have Content-Type header");
is(res.headers.get("content-type"), blob.type, "Content-Type header should match specified value");
ok(res.headers.has("content-length"), "Headers must have Content-Length header");
is(parseInt(res.headers.get("content-length")), 16, "Content-Length should match Blob's size");
return res.text().then(function(body) {
is(body, "english sentence", "Blob fetch body should match");
});
});
}
function runTest() {
var done = function() {
if (typeof SimpleTest === "object") {
SimpleTest.finish();
} else {
postMessage({ type: 'finish' });
}
}
Promise.resolve()
.then(testAboutURL)
.then(testDataURL)
.then(testSameOriginBlobURL)
//.then(testAboutURL)
// Put more promise based tests here.
.then(done)
.catch(function(e) {
ok(false, "Some Response tests failed " + e);
done();
})
}
onmessage = runTest;

View File

@ -55,4 +55,17 @@ interface PromiseDebugging {
* depend on p.
*/
static sequence<Promise<any>> getDependentPromises(Promise<any> p);
/**
* Get the number of milliseconds elapsed since the given promise was created.
*/
static DOMHighResTimeStamp getPromiseLifetime(Promise<any> p);
/*
* Get the number of milliseconds elapsed between the promise being created
* and being settled. Throws NS_ERROR_UNEXPECTED if the promise has not
* settled.
*/
[Throws]
static DOMHighResTimeStamp getTimeToSettle(Promise<any> p);
};

View File

@ -1,4 +1,4 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* -*- Mode: IDL; tab-width: 1; 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/.
@ -34,5 +34,5 @@ dictionary RequestInit {
RequestCredentials credentials;
};
enum RequestMode { "same-origin", "no-cors", "cors" };
enum RequestMode { "same-origin", "no-cors", "cors", "cors-with-forced-preflight" };
enum RequestCredentials { "omit", "same-origin", "include" };

View File

@ -13,15 +13,16 @@
* http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
*/
[Constructor(optional DOMString init = ""),
[Constructor(optional ScalarValueString init = ""),
Constructor(URLSearchParams init),
Exposed=(Window,Worker)]
interface URLSearchParams {
void append(DOMString name, DOMString value);
void delete(DOMString name);
DOMString? get(DOMString name);
sequence<DOMString> getAll(DOMString name);
boolean has(DOMString name);
void set(DOMString name, DOMString value);
void append(ScalarValueString name, ScalarValueString value);
void delete(ScalarValueString name);
ScalarValueString? get(ScalarValueString name);
sequence<ScalarValueString> getAll(ScalarValueString name);
boolean has(ScalarValueString name);
void set(ScalarValueString name, ScalarValueString value);
// iterable<ScalarValueString, ScalarValueString>; - Bug 1085284
stringifier;
};

View File

@ -17,33 +17,33 @@
Exposed=(Window, Worker)]
interface URLUtils {
// Bug 824857: no support for stringifier attributes yet.
// stringifier attribute DOMString href;
// stringifier attribute ScalarValueString href;
[Throws, CrossOriginWritable=Location]
attribute DOMString href;
attribute ScalarValueString href;
[Throws]
readonly attribute DOMString origin;
readonly attribute ScalarValueString origin;
[Throws]
attribute DOMString protocol;
attribute ScalarValueString protocol;
[Throws]
attribute DOMString username;
attribute ScalarValueString username;
[Throws]
attribute DOMString password;
attribute ScalarValueString password;
[Throws]
attribute DOMString host;
attribute ScalarValueString host;
[Throws]
attribute DOMString hostname;
attribute ScalarValueString hostname;
[Throws]
attribute DOMString port;
attribute ScalarValueString port;
[Throws]
attribute DOMString pathname;
attribute ScalarValueString pathname;
[Throws]
attribute DOMString search;
attribute ScalarValueString search;
attribute URLSearchParams searchParams;
[Throws]
attribute DOMString hash;
attribute ScalarValueString hash;
// Bug 824857 should remove this.
[Throws]

View File

@ -17,14 +17,14 @@
Exposed=(Window, Worker)]
interface URLUtilsReadOnly {
stringifier;
readonly attribute DOMString href;
readonly attribute ScalarValueString href;
readonly attribute DOMString protocol;
readonly attribute DOMString host;
readonly attribute DOMString hostname;
readonly attribute DOMString port;
readonly attribute DOMString pathname;
readonly attribute DOMString search;
readonly attribute DOMString hash;
readonly attribute DOMString origin;
readonly attribute ScalarValueString protocol;
readonly attribute ScalarValueString host;
readonly attribute ScalarValueString hostname;
readonly attribute ScalarValueString port;
readonly attribute ScalarValueString pathname;
readonly attribute ScalarValueString search;
readonly attribute ScalarValueString hash;
readonly attribute ScalarValueString origin;
};

View File

@ -10,6 +10,7 @@
#include "mozilla/EventListenerManager.h"
#include "mozilla/dom/Console.h"
#include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h"
#include "mozilla/dom/Fetch.h"
#include "mozilla/dom/FunctionBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
@ -305,8 +306,7 @@ already_AddRefed<Promise>
WorkerGlobalScope::Fetch(const RequestOrScalarValueString& aInput,
const RequestInit& aInit, ErrorResult& aRv)
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return nullptr;
return FetchRequest(this, aInput, aInit, aRv);
}
DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate)

View File

@ -305,7 +305,7 @@ XMLDocument::Load(const nsAString& aUrl, ErrorResult& aRv)
nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
// The callingDoc's Principal and doc's Principal should be the same
if (callingDoc->NodePrincipal() != principal) {
if (callingDoc && (callingDoc->NodePrincipal() != principal)) {
nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
NS_LITERAL_CSTRING("DOM"),
callingDoc,

View File

@ -191,8 +191,9 @@ ImageClientSingle::UpdateImageInternal(ImageContainer* aContainer,
if (image->AsSharedImage() && image->AsSharedImage()->GetTextureClient(this)) {
// fast path: no need to allocate and/or copy image data
RefPtr<TextureClient> texture = image->AsSharedImage()->GetTextureClient(this);
autoRemoveTexture.mTexture = mFrontBuffer;
if (texture != mFrontBuffer) {
autoRemoveTexture.mTexture = mFrontBuffer;
}
mFrontBuffer = texture;
if (!AddTextureClient(texture)) {
mFrontBuffer = nullptr;

View File

@ -38,6 +38,9 @@ SurfaceFormatForAndroidPixelFormat(android::PixelFormat aFormat,
case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
case HAL_PIXEL_FORMAT_YV12:
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
#endif
return gfx::SurfaceFormat::R8G8B8A8; // yup, use SurfaceFormat::R8G8B8A8 even though it's a YUV texture. This is an external texture.
default:
if (aFormat >= 0x100 && aFormat <= 0x1FF) {
@ -66,6 +69,9 @@ TextureTargetForAndroidPixelFormat(android::PixelFormat aFormat)
case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
case HAL_PIXEL_FORMAT_YV12:
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
#endif
return LOCAL_GL_TEXTURE_EXTERNAL;
case android::PIXEL_FORMAT_BGRA_8888:
case android::PIXEL_FORMAT_RGBA_8888:

View File

@ -537,8 +537,7 @@ JavaScriptShared::fromDescriptor(JSContext *cx, Handle<JSPropertyDescriptor> des
if (!toVariant(cx, desc.value(), &out->value()))
return false;
MOZ_ASSERT(desc.object());
if (!toObjectVariant(cx, desc.object(), &out->obj()))
if (!toObjectOrNullVariant(cx, desc.object(), &out->obj()))
return false;
if (!desc.getter()) {
@ -595,11 +594,7 @@ JavaScriptShared::toDescriptor(JSContext *cx, const PPropertyDescriptor &in,
out.setAttributes(in.attrs());
if (!fromVariant(cx, in.value(), out.value()))
return false;
Rooted<JSObject*> obj(cx);
obj = fromObjectVariant(cx, in.obj());
if (!obj)
return false;
out.object().set(obj);
out.object().set(fromObjectOrNullVariant(cx, in.obj()));
if (in.getter().type() == GetterSetter::Tuint64_t && !in.getter().get_uint64_t()) {
out.setGetter(nullptr);
@ -634,6 +629,31 @@ JavaScriptShared::toDescriptor(JSContext *cx, const PPropertyDescriptor &in,
return true;
}
bool
JavaScriptShared::toObjectOrNullVariant(JSContext *cx, JSObject *obj, ObjectOrNullVariant *objVarp)
{
if (!obj) {
*objVarp = NullVariant();
return true;
}
ObjectVariant objVar;
if (!toObjectVariant(cx, obj, &objVar))
return false;
*objVarp = objVar;
return true;
}
JSObject *
JavaScriptShared::fromObjectOrNullVariant(JSContext *cx, ObjectOrNullVariant objVar)
{
if (objVar.type() == ObjectOrNullVariant::TNullVariant)
return nullptr;
return fromObjectVariant(cx, objVar.get_ObjectVariant());
}
CpowIdHolder::CpowIdHolder(dom::CPOWManagerGetter *managerGetter, const InfallibleTArray<CpowEntry> &cpows)
: js_(nullptr),
cpows_(cpows)

View File

@ -169,6 +169,9 @@ class JavaScriptShared
bool toDescriptor(JSContext *cx, const PPropertyDescriptor &in,
JS::MutableHandle<JSPropertyDescriptor> out);
bool toObjectOrNullVariant(JSContext *cx, JSObject *obj, ObjectOrNullVariant *objVarp);
JSObject *fromObjectOrNullVariant(JSContext *cx, ObjectOrNullVariant objVar);
bool convertIdToGeckoString(JSContext *cx, JS::HandleId id, nsString *to);
bool convertGeckoStringToId(JSContext *cx, const nsString &from, JS::MutableHandleId id);

View File

@ -62,6 +62,12 @@ union SymbolVariant
struct UndefinedVariant {};
struct NullVariant {};
union ObjectOrNullVariant
{
ObjectVariant;
NullVariant;
};
union JSVariant
{
UndefinedVariant;
@ -115,7 +121,7 @@ union GetterSetter
struct PPropertyDescriptor
{
ObjectVariant obj;
ObjectOrNullVariant obj;
uint32_t attrs;
JSVariant value;

View File

@ -111,9 +111,6 @@ WrapperAnswer::RecvGetPropertyDescriptor(const ObjectId &objId, const JSIDVarian
if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc))
return fail(cx, rs);
if (!desc.object())
return ok(rs);
if (!fromDescriptor(cx, desc, out))
return fail(cx, rs);
@ -142,12 +139,9 @@ WrapperAnswer::RecvGetOwnPropertyDescriptor(const ObjectId &objId, const JSIDVar
return fail(cx, rs);
Rooted<JSPropertyDescriptor> desc(cx);
if (!JS_GetPropertyDescriptorById(cx, obj, id, &desc))
if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc))
return fail(cx, rs);
if (desc.object() != obj)
return ok(rs);
if (!fromDescriptor(cx, desc, out))
return fail(cx, rs);

View File

@ -173,11 +173,7 @@ struct CompileError {
JSErrorReport report;
char *message;
ErrorArgumentsType argumentsType;
CompileError()
: message(nullptr), argumentsType(ArgumentsAreUnicode)
{
mozilla::PodZero(&report);
}
CompileError() : message(nullptr), argumentsType(ArgumentsAreUnicode) {}
~CompileError();
void throwError(JSContext *cx);

View File

@ -0,0 +1,5 @@
// |jit-test| error: TypeError
var r = Proxy.revocable({}, {});
var p = r.proxy;
r.revoke();
p instanceof Object;

View File

@ -4675,10 +4675,19 @@ JS_ReportOutOfMemory(JSContext *cx);
extern JS_PUBLIC_API(void)
JS_ReportAllocationOverflow(JSContext *cx);
struct JSErrorReport {
class JSErrorReport
{
public:
JSErrorReport()
: filename(nullptr), lineno(0), column(0), isMuted(false), linebuf(nullptr),
tokenptr(nullptr), uclinebuf(nullptr), uctokenptr(nullptr), flags(0), errorNumber(0),
ucmessage(nullptr), messageArgs(nullptr), exnType(0)
{}
const char *filename; /* source file name, URL, etc., or null */
bool isMuted; /* See the comment in ReadOnlyCompileOptions. */
unsigned lineno; /* source line number */
unsigned column; /* zero-based column index in line */
bool isMuted; /* See the comment in ReadOnlyCompileOptions. */
const char *linebuf; /* offending source line without final \n */
const char *tokenptr; /* pointer to error token in linebuf */
const char16_t *uclinebuf; /* unicode (original) line buffer */
@ -4688,7 +4697,6 @@ struct JSErrorReport {
const char16_t *ucmessage; /* the (default) error message */
const char16_t **messageArgs; /* arguments for the error message */
int16_t exnType; /* One of the JSExnType constants */
unsigned column; /* zero-based column index in line */
};
/*

View File

@ -55,7 +55,6 @@ using namespace js::gc;
using mozilla::DebugOnly;
using mozilla::PodArrayZero;
using mozilla::PodZero;
using mozilla::PointerRangeSize;
bool
@ -381,7 +380,6 @@ js_ReportOutOfMemory(ThreadSafeContext *cxArg)
/* Fill out the report, but don't do anything that requires allocation. */
JSErrorReport report;
PodZero(&report);
report.flags = JSREPORT_ERROR;
report.errorNumber = JSMSG_OUT_OF_MEMORY;
PopulateReportBlame(cx, &report);
@ -503,7 +501,6 @@ js_ReportErrorVA(JSContext *cx, unsigned flags, const char *format, va_list ap)
return false;
messagelen = strlen(message);
PodZero(&report);
report.flags = flags;
report.errorNumber = JSMSG_USER_DEFINED_ERROR;
report.ucmessage = ucmessage = InflateString(cx, message, &messagelen);
@ -808,7 +805,6 @@ js_ReportErrorNumberVA(JSContext *cx, unsigned flags, JSErrorCallback callback,
return true;
warning = JSREPORT_IS_WARNING(flags);
PodZero(&report);
report.flags = flags;
report.errorNumber = errorNumber;
PopulateReportBlame(cx, &report);
@ -848,7 +844,6 @@ js_ReportErrorNumberUCArray(JSContext *cx, unsigned flags, JSErrorCallback callb
bool warning = JSREPORT_IS_WARNING(flags);
JSErrorReport report;
PodZero(&report);
report.flags = flags;
report.errorNumber = errorNumber;
PopulateReportBlame(cx, &report);

View File

@ -40,7 +40,6 @@ using namespace js::types;
using mozilla::ArrayLength;
using mozilla::PodArrayZero;
using mozilla::PodZero;
static void
exn_finalize(FreeOp *fop, JSObject *obj);
@ -813,7 +812,7 @@ ErrorReport::init(JSContext *cx, HandleValue exn)
}
reportp = &ownedReport;
PodZero(&ownedReport);
new (reportp) JSErrorReport();
ownedReport.filename = filename.ptr();
ownedReport.lineno = lineno;
ownedReport.exnType = int16_t(JSEXN_NONE);
@ -865,7 +864,7 @@ ErrorReport::populateUncaughtExceptionReport(JSContext *cx, ...)
void
ErrorReport::populateUncaughtExceptionReportVA(JSContext *cx, va_list ap)
{
PodZero(&ownedReport);
new (&ownedReport) JSErrorReport();
ownedReport.flags = JSREPORT_ERROR;
ownedReport.errorNumber = JSMSG_UNCAUGHT_EXCEPTION;
// XXXbz this assumes the stack we have right now is still

View File

@ -45,7 +45,7 @@ class JSAtom;
struct JSErrorFormatString;
class JSLinearString;
struct JSJitInfo;
struct JSErrorReport;
class JSErrorReport;
namespace JS {
template <class T>

View File

@ -125,7 +125,7 @@ enum JSGCTraceKind {
struct JSClass;
struct JSCompartment;
struct JSCrossCompartmentCall;
struct JSErrorReport;
class JSErrorReport;
struct JSExceptionState;
struct JSFunctionSpec;
struct JSIdArray;

View File

@ -1090,6 +1090,35 @@ ScriptedDirectProxyHandler::isCallable(JSObject *obj) const
return obj->as<ProxyObject>().extra(IS_CALLABLE_EXTRA).toBoolean();
}
// ES6 implements both getPrototypeOf and setPrototypeOf traps. We don't have them yet (see bug
// 888969). For now, use these, to account for proxy revocation.
bool
ScriptedDirectProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy,
MutableHandleObject protop) const
{
RootedObject target(cx, proxy->as<ProxyObject>().target());
// Though handler is used elsewhere, spec mandates that both get set to null.
if (!target) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
return false;
}
return DirectProxyHandler::getPrototypeOf(cx, proxy, protop);
}
bool
ScriptedDirectProxyHandler::setPrototypeOf(JSContext *cx, HandleObject proxy,
HandleObject proto, bool *bp) const
{
RootedObject target(cx, proxy->as<ProxyObject>().target());
if (!target) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
return false;
}
return DirectProxyHandler::setPrototypeOf(cx, proxy, proto, bp);
}
const char ScriptedDirectProxyHandler::family = 0;
const ScriptedDirectProxyHandler ScriptedDirectProxyHandler::singleton;

View File

@ -36,6 +36,11 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler {
bool strict, MutableHandleValue vp) const MOZ_OVERRIDE;
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
// These are standard internal methods, but are not implemented to spec yet.
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop)
const MOZ_OVERRIDE;
virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp)
const MOZ_OVERRIDE;
/* SpiderMonkey extensions. */
virtual bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,

View File

@ -17,7 +17,6 @@
#include "vm/Shape-inl.h"
using namespace js;
using mozilla::PodZero;
/* static */ Shape *
js::ErrorObject::assignInitialShape(ExclusiveContext *cx, Handle<ErrorObject*> obj)
@ -116,7 +115,6 @@ js::ErrorObject::getOrCreateErrorReport(JSContext *cx)
// We build an error report on the stack and then use CopyErrorReport to do
// the nitty-gritty malloc stuff.
JSErrorReport report;
PodZero(&report);
// Type.
JSExnType type_ = type();

View File

@ -619,39 +619,26 @@ WrapCallable(JSContext *cx, HandleObject callable, HandleObject sandboxProtoProx
}
template<typename Op>
bool BindPropertyOp(JSContext *cx, Op &op, JSPropertyDescriptor *desc, HandleId id,
unsigned attrFlag, HandleObject sandboxProtoProxy)
bool WrapAccessorFunction(JSContext *cx, Op &op, JSPropertyDescriptor *desc,
unsigned attrFlag, HandleObject sandboxProtoProxy)
{
if (!op) {
return true;
}
RootedObject func(cx);
if (desc->attrs & attrFlag) {
// Already an object
func = JS_FUNC_TO_DATA_PTR(JSObject *, op);
} else {
// We have an actual property op. For getters, we use 0
// args, for setters we use 1 arg.
uint32_t args = (attrFlag == JSPROP_GETTER) ? 0 : 1;
RootedObject obj(cx, desc->obj);
func = GeneratePropertyOp(cx, obj, id, args, op);
if (!func)
return false;
if (!(desc->attrs & attrFlag)) {
XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
return false;
}
RootedObject func(cx, JS_FUNC_TO_DATA_PTR(JSObject *, op));
func = WrapCallable(cx, func, sandboxProtoProxy);
if (!func)
return false;
op = JS_DATA_TO_FUNC_PTR(Op, func.get());
desc->attrs |= attrFlag;
return true;
}
extern bool
XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp);
extern bool
XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp);
bool
xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext *cx,
JS::Handle<JSObject*> proxy,
@ -668,14 +655,11 @@ xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext *cx,
return true; // No property, nothing to do
// Now fix up the getter/setter/value as needed to be bound to desc->obj.
//
// Don't mess with XPC_WN_Helper_GetProperty and XPC_WN_Helper_SetProperty,
// because that could confuse our access to expandos.
if (desc.getter() != XPC_WN_Helper_GetProperty &&
!BindPropertyOp(cx, desc.getter(), desc.address(), id, JSPROP_GETTER, proxy))
if (!WrapAccessorFunction(cx, desc.getter(), desc.address(),
JSPROP_GETTER, proxy))
return false;
if (desc.setter() != XPC_WN_Helper_SetProperty &&
!BindPropertyOp(cx, desc.setter(), desc.address(), id, JSPROP_SETTER, proxy))
if (!WrapAccessorFunction(cx, desc.setter(), desc.address(),
JSPROP_SETTER, proxy))
return false;
if (desc.value().isObject()) {
RootedObject val (cx, &desc.value().toObject());

View File

@ -79,20 +79,6 @@ HasBitInInterfacesBitmap(JSObject *obj, uint32_t interfaceBit)
return (clasp->interfacesBitmap & (1 << interfaceBit)) != 0;
}
static void
PointerFinalize(JSFreeOp *fop, JSObject *obj)
{
JSPropertyOp *popp = static_cast<JSPropertyOp *>(JS_GetPrivate(obj));
delete popp;
}
const JSClass
PointerHolderClass = {
"Pointer", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, PointerFinalize
};
bool
xpc_qsDefineQuickStubs(JSContext *cx, JSObject *protoArg, unsigned flags,
uint32_t ifacec, const nsIID **interfaces,

View File

@ -529,87 +529,4 @@ xpc_qsSameResult(int32_t result1, int32_t result2)
#define XPC_QS_ASSERT_CONTEXT_OK(cx) ((void) 0)
#endif
// Apply |op| to |obj|, |id|, and |vp|. If |op| is a setter, treat the assignment as lenient.
template<typename Op>
inline bool ApplyPropertyOp(JSContext *cx, Op op, JS::HandleObject obj, JS::HandleId id,
JS::MutableHandleValue vp);
template<>
inline bool
ApplyPropertyOp<JSPropertyOp>(JSContext *cx, JSPropertyOp op, JS::HandleObject obj, JS::HandleId id,
JS::MutableHandleValue vp)
{
return op(cx, obj, id, vp);
}
template<>
inline bool
ApplyPropertyOp<JSStrictPropertyOp>(JSContext *cx, JSStrictPropertyOp op, JS::HandleObject obj,
JS::HandleId id, JS::MutableHandleValue vp)
{
return op(cx, obj, id, true, vp);
}
template<typename Op>
bool
PropertyOpForwarder(JSContext *cx, unsigned argc, jsval *vp)
{
// Layout:
// this = our this
// property op to call = callee reserved slot 0
// name of the property = callee reserved slot 1
JS::CallArgs args = CallArgsFromVp(argc, vp);
JS::RootedObject callee(cx, &args.callee());
JS::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
if (!obj)
return false;
JS::RootedValue v(cx, js::GetFunctionNativeReserved(callee, 0));
JSObject *ptrobj = v.toObjectOrNull();
Op *popp = static_cast<Op *>(JS_GetPrivate(ptrobj));
v = js::GetFunctionNativeReserved(callee, 1);
JS::RootedValue argval(cx, args.get(0));
JS::RootedId id(cx);
if (!JS_ValueToId(cx, v, &id))
return false;
args.rval().set(argval);
return ApplyPropertyOp<Op>(cx, *popp, obj, id, args.rval());
}
extern const JSClass PointerHolderClass;
template<typename Op>
JSObject *
GeneratePropertyOp(JSContext *cx, JS::HandleObject obj, JS::HandleId id, unsigned argc, Op pop)
{
// The JS engine provides two reserved slots on function objects for
// XPConnect to use. Use them to stick the necessary info here.
JSFunction *fun =
js::NewFunctionByIdWithReserved(cx, PropertyOpForwarder<Op>, argc, 0, obj, id);
if (!fun)
return nullptr;
JS::RootedObject funobj(cx, JS_GetFunctionObject(fun));
// Unfortunately, we cannot guarantee that Op is aligned. Use a
// second object to work around this.
JSObject *ptrobj = JS_NewObject(cx, &PointerHolderClass, JS::NullPtr(), funobj);
if (!ptrobj)
return nullptr;
Op *popp = new Op;
if (!popp)
return nullptr;
*popp = pop;
JS_SetPrivate(ptrobj, popp);
js::SetFunctionNativeReserved(funobj, 0, OBJECT_TO_JSVAL(ptrobj));
js::SetFunctionNativeReserved(funobj, 1, js::IdToValue(id));
return funobj;
}
#endif /* xpcquickstubs_h___ */

View File

@ -41,8 +41,6 @@ skip-if = buildapp == 'mulet'
[test_bug743843.xul]
[test_bug760076.xul]
skip-if = buildapp == 'mulet'
[test_bug760109.xul]
skip-if = buildapp == 'mulet'
[test_bug760131.html]
[test_bug763343.xul]
[test_bug771429.xul]

View File

@ -1,61 +0,0 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=760109
-->
<window title="Mozilla Bug 760109"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=760109"
target="_blank">Mozilla Bug 760109</a>
</body>
<!-- test code goes here -->
<script type="application/javascript">
<![CDATA[
/** Test for COW prototype remapping.**/
// This gets decompiled and run inside the sandbox.
function sandboxCode() {
// Check that COWs for objects with standard prototypes use the standard
// prototype in the home compartment.
var protoProto = Object.getPrototypeOf(Object.getPrototypeOf(chromeObject));
ok(protoProto === Object.prototype,
"Object prototype remapped properly");
// Check |constructor|.
// Note that the 'constructor' property of the underlying chrome object
// will be resolved on SomeConstructor.prototype, which has an empty
// __exposedProps__. This means that we shouldn't remap the property, even
// though we'd also be able to find it on Object.prototype. Some recent
// refactoring has made it possible to do the right thing here.
is(typeof chromeObject.constructor, "undefined", "Object constructor does what we expect");
}
// We use a constructor to create the test object so that there's an
// intermediate object on the prototype chain between the instance and the
// standard prototype.
function SomeConstructor() {
this.foo = 2;
this.bar = 3;
this.baz = 4;
this.__exposedProps__ = {foo: 'r', baz: 'rw'};
}
SomeConstructor.prototype.__exposedProps__ = {};
const Cu = Components.utils;
var sb = new Cu.Sandbox('http://www.example.org');
sb.chromeObject = new SomeConstructor();
sb.ok = ok;
sb.is = is;
Cu.evalInSandbox('(' + sandboxCode.toSource() + ')();', sb);
]]>
</script>
</window>

View File

@ -32,16 +32,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=866823
Cu.evalInSandbox('iwin.wrappedJSObject.waiver = iwin.wrappedJSObject', sb);
ok(iwin.wrappedJSObject.eval('waiver == window'), "Waivers disappear same-compartment");
// Make sure that COW prototype always happens.
// Make sure that standard prototypes don't get COWs.
sb.objProto = Object.prototype;
ok(Cu.evalInSandbox('objProto == Object.prototype', sb),
"Prototype remapping should happen even when the object isn't used as a prototype");
// Make sure that prototype remapping happens even for nsEP.
var sbEP = new Cu.Sandbox(['http://example.org']);
sb.epObjProto = sbEP.Object.prototype;
ok(Cu.evalInSandbox('epObjProto == Object.prototype', sb),
"Prototype remapping should happen for all non-subsuming access");
try {
sb.eval('objProto.toString;', sb);
ok(false, "Should have thrown");
} catch (e) {
ok(/denied|insecure/, "No silent failure when accessing properties on forbidden prototype");
}
SimpleTest.finish();
}

View File

@ -4,7 +4,7 @@ function run_test() {
sb.obj = {};
sb.arr = [];
sb.fun = function() {};
do_check_true(sb.eval('Object.getPrototypeOf(obj) == Object.prototype'));
do_check_true(sb.eval('Object.getPrototypeOf(obj) == null'));
do_check_true(sb.eval('Object.getPrototypeOf(arr) == null'));
do_check_true(sb.eval('Object.getPrototypeOf(fun) == null'));
}

View File

@ -8,6 +8,7 @@
#include "nsJSPrincipals.h"
#include "nsGlobalWindow.h"
#include "JavaScriptParent.h"
#include "XPCWrapper.h"
#include "XrayWrapper.h"
@ -213,6 +214,63 @@ AccessCheck::isCrossOriginAccessPermitted(JSContext *cx, HandleObject wrapper, H
return false;
}
bool
AccessCheck::checkPassToPrivilegedCode(JSContext *cx, HandleObject wrapper, HandleValue v)
{
// Primitives are fine.
if (!v.isObject())
return true;
RootedObject obj(cx, &v.toObject());
// Non-wrappers are fine.
if (!js::IsWrapper(obj))
return true;
// CPOWs use COWs (in the unprivileged junk scope) for all child->parent
// references. Without this test, the child process wouldn't be able to
// pass any objects at all to CPOWs.
if (mozilla::jsipc::IsWrappedCPOW(obj) &&
js::GetObjectCompartment(wrapper) == js::GetObjectCompartment(xpc::UnprivilegedJunkScope()) &&
XRE_GetProcessType() == GeckoProcessType_Default)
{
return true;
}
// COWs are fine to pass to chrome if and only if they have __exposedProps__,
// since presumably content should never have a reason to pass an opaque
// object back to chrome.
if (AccessCheck::isChrome(js::UncheckedUnwrap(wrapper)) && WrapperFactory::IsCOW(obj)) {
RootedObject target(cx, js::UncheckedUnwrap(obj));
JSAutoCompartment ac(cx, target);
RootedId id(cx, GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS));
bool found = false;
if (!JS_HasPropertyById(cx, target, id, &found))
return false;
if (found)
return true;
}
// Same-origin wrappers are fine.
if (AccessCheck::wrapperSubsumes(obj))
return true;
// Badness.
JS_ReportError(cx, "Permission denied to pass object to privileged code");
return false;
}
bool
AccessCheck::checkPassToPrivilegedCode(JSContext *cx, HandleObject wrapper, const CallArgs &args)
{
if (!checkPassToPrivilegedCode(cx, wrapper, args.thisv()))
return false;
for (size_t i = 0; i < args.length(); ++i) {
if (!checkPassToPrivilegedCode(cx, wrapper, args[i]))
return false;
}
return true;
}
enum Access { READ = (1<<0), WRITE = (1<<1), NO_ACCESS = 0 };
static void

View File

@ -39,8 +39,6 @@ enum CrossOriginObjectType {
CrossOriginObjectType IdentifyCrossOriginObject(JSObject *obj);
struct Policy {
static const bool AllowGetPrototypeOf = false;
static bool checkCall(JSContext *cx, JS::HandleObject wrapper, const JS::CallArgs &args) {
MOZ_CRASH("As a rule, filtering wrappers are non-callable");
}
@ -95,12 +93,6 @@ struct CrossOriginAccessiblePropertiesOnly : public Policy {
// This policy only permits access to properties if they appear in the
// objects exposed properties list.
struct ExposedPropertiesOnly : public Policy {
// COWs are the only type of filtering wrapper that allow access to the
// prototype, because the standard prototypes are remapped into the
// wrapper's compartment.
static const bool AllowGetPrototypeOf = true;
static bool check(JSContext *cx, JS::HandleObject wrapper, JS::HandleId id, js::Wrapper::Action act);
static bool deny(js::Wrapper::Action act, JS::HandleId id);

View File

@ -7,7 +7,6 @@
#include "ChromeObjectWrapper.h"
#include "WrapperFactory.h"
#include "AccessCheck.h"
#include "JavaScriptParent.h"
#include "xpcprivate.h"
#include "jsapi.h"
#include "jswrapper.h"
@ -17,152 +16,8 @@ using namespace JS;
namespace xpc {
// When creating wrappers for chrome objects in content, we detect if the
// prototype of the wrapped chrome object is a prototype for a standard class
// (like Array.prototype). If it is, we use the corresponding standard prototype
// from the wrapper's scope, rather than the wrapped standard prototype
// from the wrappee's scope.
//
// One of the reasons for doing this is to allow standard operations like
// chromeArray.forEach(..) to Just Work without explicitly listing them in
// __exposedProps__. Since proxies don't automatically inherit behavior from
// their prototype, we have to instrument the traps to do this manually.
const ChromeObjectWrapper ChromeObjectWrapper::singleton;
using js::assertEnteredPolicy;
static bool
AllowedByBase(JSContext *cx, HandleObject wrapper, HandleId id,
js::Wrapper::Action act)
{
MOZ_ASSERT(js::Wrapper::wrapperHandler(wrapper) ==
&ChromeObjectWrapper::singleton);
bool bp;
const ChromeObjectWrapper *handler = &ChromeObjectWrapper::singleton;
return handler->ChromeObjectWrapperBase::enter(cx, wrapper, id, act, &bp);
}
static bool
PropIsFromStandardPrototype(JSContext *cx, JS::MutableHandle<JSPropertyDescriptor> desc)
{
MOZ_ASSERT(desc.object());
RootedObject unwrapped(cx, js::UncheckedUnwrap(desc.object()));
JSAutoCompartment ac(cx, unwrapped);
return IdentifyStandardPrototype(unwrapped) != JSProto_Null;
}
// Note that we're past the policy enforcement stage, here, so we can query
// CrossCompartmentSecurityWrapper (our grand-parent wrapper) and get an
// unfiltered view of the underlying object. This lets us determine whether
// the property we would have found (given a transparent wrapper) would
// have come off a standard prototype.
static bool
PropIsFromStandardPrototype(JSContext *cx, HandleObject wrapper,
HandleId id)
{
MOZ_ASSERT(js::Wrapper::wrapperHandler(wrapper) ==
&ChromeObjectWrapper::singleton);
Rooted<JSPropertyDescriptor> desc(cx);
const ChromeObjectWrapper *handler = &ChromeObjectWrapper::singleton;
if (!handler->js::CrossCompartmentSecurityWrapper::getPropertyDescriptor(cx, wrapper, id,
&desc) ||
!desc.object())
{
return false;
}
return PropIsFromStandardPrototype(cx, &desc);
}
bool
ChromeObjectWrapper::getPropertyDescriptor(JSContext *cx,
HandleObject wrapper,
HandleId id,
JS::MutableHandle<JSPropertyDescriptor> desc) const
{
assertEnteredPolicy(cx, wrapper, id, GET | SET | GET_PROPERTY_DESCRIPTOR);
// First, try a lookup on the base wrapper if permitted.
desc.object().set(nullptr);
if (AllowedByBase(cx, wrapper, id, Wrapper::GET) &&
!ChromeObjectWrapperBase::getPropertyDescriptor(cx, wrapper, id,
desc)) {
return false;
}
// If the property is something that can be found on a standard prototype,
// prefer the one we'll get via the prototype chain in the content
// compartment.
if (desc.object() && PropIsFromStandardPrototype(cx, desc))
desc.object().set(nullptr);
// If we found something or have no proto, we're done.
RootedObject wrapperProto(cx);
if (!JS_GetPrototype(cx, wrapper, &wrapperProto))
return false;
if (desc.object() || !wrapperProto)
return true;
// If not, try doing the lookup on the prototype.
MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
return JS_GetPropertyDescriptorById(cx, wrapperProto, id, desc);
}
bool
AccessCheck::checkPassToPrivilegedCode(JSContext *cx, HandleObject wrapper, HandleValue v)
{
// Primitives are fine.
if (!v.isObject())
return true;
RootedObject obj(cx, &v.toObject());
// Non-wrappers are fine.
if (!js::IsWrapper(obj))
return true;
// CPOWs use COWs (in the unprivileged junk scope) for all child->parent
// references. Without this test, the child process wouldn't be able to
// pass any objects at all to CPOWs.
if (mozilla::jsipc::IsWrappedCPOW(obj) &&
js::GetObjectCompartment(wrapper) == js::GetObjectCompartment(xpc::UnprivilegedJunkScope()) &&
XRE_GetProcessType() == GeckoProcessType_Default)
{
return true;
}
// COWs are fine to pass to chrome if and only if they have __exposedProps__,
// since presumably content should never have a reason to pass an opaque
// object back to chrome.
if (AccessCheck::isChrome(js::UncheckedUnwrap(wrapper)) && WrapperFactory::IsCOW(obj)) {
RootedObject target(cx, js::UncheckedUnwrap(obj));
JSAutoCompartment ac(cx, target);
RootedId id(cx, GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS));
bool found = false;
if (!JS_HasPropertyById(cx, target, id, &found))
return false;
if (found)
return true;
}
// Same-origin wrappers are fine.
if (AccessCheck::wrapperSubsumes(obj))
return true;
// Badness.
JS_ReportError(cx, "Permission denied to pass object to privileged code");
return false;
}
bool
AccessCheck::checkPassToPrivilegedCode(JSContext *cx, HandleObject wrapper, const CallArgs &args)
{
if (!checkPassToPrivilegedCode(cx, wrapper, args.thisv()))
return false;
for (size_t i = 0; i < args.length(); ++i) {
if (!checkPassToPrivilegedCode(cx, wrapper, args[i]))
return false;
}
return true;
}
bool
ChromeObjectWrapper::defineProperty(JSContext *cx, HandleObject wrapper,
HandleId id,
@ -183,100 +38,4 @@ ChromeObjectWrapper::set(JSContext *cx, HandleObject wrapper,
return ChromeObjectWrapperBase::set(cx, wrapper, receiver, id, strict, vp);
}
bool
ChromeObjectWrapper::has(JSContext *cx, HandleObject wrapper,
HandleId id, bool *bp) const
{
assertEnteredPolicy(cx, wrapper, id, GET);
// Try the lookup on the base wrapper if permitted.
if (AllowedByBase(cx, wrapper, id, js::Wrapper::GET) &&
!ChromeObjectWrapperBase::has(cx, wrapper, id, bp))
{
return false;
}
// If we found something or have no prototype, we're done.
RootedObject wrapperProto(cx);
if (!JS_GetPrototype(cx, wrapper, &wrapperProto))
return false;
if (*bp || !wrapperProto)
return true;
// Try the prototype if that failed.
MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
Rooted<JSPropertyDescriptor> desc(cx);
if (!JS_GetPropertyDescriptorById(cx, wrapperProto, id, &desc))
return false;
*bp = !!desc.object();
return true;
}
bool
ChromeObjectWrapper::get(JSContext *cx, HandleObject wrapper,
HandleObject receiver, HandleId id,
MutableHandleValue vp) const
{
assertEnteredPolicy(cx, wrapper, id, GET);
vp.setUndefined();
// Only call through to the get trap on the underlying object if we're
// allowed to see the property, and if what we'll find is not on a standard
// prototype.
if (AllowedByBase(cx, wrapper, id, js::Wrapper::GET) &&
!PropIsFromStandardPrototype(cx, wrapper, id))
{
// Call the get trap.
if (!ChromeObjectWrapperBase::get(cx, wrapper, receiver, id, vp))
return false;
// If we found something, we're done.
if (!vp.isUndefined())
return true;
}
// If we have no proto, we're done.
RootedObject wrapperProto(cx);
if (!JS_GetPrototype(cx, wrapper, &wrapperProto))
return false;
if (!wrapperProto)
return true;
// Try the prototype.
MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
return js::GetGeneric(cx, wrapperProto, receiver, id, vp.address());
}
// SecurityWrapper categorically returns false for objectClassIs, but the
// contacts API depends on Array.isArray returning true for COW-implemented
// contacts. This isn't really ideal, but make it work for now.
bool
ChromeObjectWrapper::objectClassIs(HandleObject obj, js::ESClassValue classValue,
JSContext *cx) const
{
return CrossCompartmentWrapper::objectClassIs(obj, classValue, cx);
}
// This mechanism isn't ideal because we end up calling enter() on the base class
// twice (once during enter() here and once during the trap itself), and policy
// enforcement or COWs isn't cheap. But it results in the cleanest code, and this
// whole proto remapping thing for COWs is going to be phased out anyway.
bool
ChromeObjectWrapper::enter(JSContext *cx, HandleObject wrapper,
HandleId id, js::Wrapper::Action act, bool *bp) const
{
if (AllowedByBase(cx, wrapper, id, act))
return true;
// COWs fail silently for GETs, and that also happens to be the only case
// where we might want to redirect the lookup to the home prototype chain.
*bp = (act == Wrapper::GET || act == Wrapper::ENUMERATE ||
act == Wrapper::GET_PROPERTY_DESCRIPTOR) && !JS_IsExceptionPending(cx);
if (!*bp || id == JSID_VOID)
return false;
// Note that PropIsFromStandardPrototype needs to invoke getPropertyDescriptor
// before we've fully entered the policy. Waive our policy.
js::AutoWaivePolicy policy(cx, wrapper, id, act);
return PropIsFromStandardPrototype(cx, wrapper, id);
}
}

View File

@ -15,11 +15,10 @@ namespace xpc {
struct ExposedPropertiesOnly;
// When chrome JS objects are exposed to content, they get a ChromeObjectWrapper.
//
// The base filtering wrapper here does most of the work for us. We define a
// custom class here to introduce custom behavior with respect to the prototype
// chain.
// When a vanilla chrome JS object is exposed to content, we use a wrapper that
// supports __exposedProps__ for legacy reasons. For extra security, we override
// the traps that allow content to pass an object to chrome, and perform extra
// security checks on them.
#define ChromeObjectWrapperBase \
FilteringWrapper<js::CrossCompartmentSecurityWrapper, ExposedPropertiesOnly>
@ -28,35 +27,13 @@ class ChromeObjectWrapper : public ChromeObjectWrapperBase
public:
MOZ_CONSTEXPR ChromeObjectWrapper() : ChromeObjectWrapperBase(0) {}
virtual bool enter(JSContext *cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
js::Wrapper::Action act, bool *bp) const MOZ_OVERRIDE;
virtual bool defineProperty(JSContext *cx, JS::Handle<JSObject*> wrapper,
JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
virtual bool has(JSContext *cx, JS::Handle<JSObject*> wrapper,
JS::Handle<jsid> id, bool *bp) const MOZ_OVERRIDE;
virtual bool get(JSContext *cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> receiver,
JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
virtual bool set(JSContext *cx, JS::Handle<JSObject*> wrapper,
JS::Handle<JSObject*> receiver, JS::Handle<jsid> id,
bool strict, JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle<JSObject*> wrapper,
JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc) const MOZ_OVERRIDE;
virtual bool objectClassIs(JS::Handle<JSObject*> obj, js::ESClassValue classValue,
JSContext *cx) const MOZ_OVERRIDE;
// NB: One might think we'd need to implement enumerate(),
// getOwnEnumerablePropertyKeys(), iterate(), and ownPropertyKeys()
// here. However, ES5 built-in properties aren't enumerable (and
// SpiderMonkey's implementation seems to match the spec modulo
// Error.prototype.fileName and Error.prototype.lineNumber). Since we're
// only remapping the prototypes of standard objects, there would never be
// anything more to enumerate up the prototype chain. So we can actually
// skip these.
static const ChromeObjectWrapper singleton;
};

View File

@ -177,11 +177,7 @@ bool
FilteringWrapper<Base, Policy>::getPrototypeOf(JSContext *cx, JS::HandleObject wrapper,
JS::MutableHandleObject protop) const
{
// If the policy explicitly allows access to the prototype, bounce to the base.
if (Policy::AllowGetPrototypeOf)
return Base::getPrototypeOf(cx, wrapper, protop);
// In general, filtering wrappers do not allow access to the prototype.
// Filtering wrappers do not allow access to the prototype.
protop.set(nullptr);
return true;
}

View File

@ -107,15 +107,6 @@ WrapperFactory::WaiveXray(JSContext *cx, JSObject *objArg)
return CreateXrayWaiver(cx, obj);
}
// In general, we're trying to deprecate COWs incrementally as we introduce
// Xrays to the corresponding object types. But switching off COWs for certain
// things would be too tumultuous at present, so we punt on them for later.
static bool
ForceCOWBehavior(JSObject *obj)
{
return IdentifyStandardInstanceOrPrototype(obj) == JSProto_Object;
}
inline bool
ShouldWaiveXray(JSContext *cx, JSObject *originalObj)
{
@ -171,45 +162,6 @@ WrapperFactory::PrepareForWrapping(JSContext *cx, HandleObject scope,
// We should never get a proxy here (the JS engine unwraps those for us).
MOZ_ASSERT(!IsWrapper(obj));
// If the object being wrapped is a prototype for a standard class and the
// wrapper does not subsumes the wrappee, use the one from the content
// compartment. This is generally safer all-around, and in the COW case this
// lets us safely take advantage of things like .forEach() via the
// ChromeObjectWrapper machinery.
//
// If the prototype chain of chrome object |obj| looks like this:
//
// obj => foo => bar => chromeWin.StandardClass.prototype
//
// The prototype chain of COW(obj) looks lke this:
//
// COW(obj) => COW(foo) => COW(bar) => contentWin.StandardClass.prototype
//
// NB: We now remap all non-subsuming access of standard prototypes.
//
// NB: We need to ignore domain here so that the security relationship we
// compute here can't change over time. See the comment above the other
// subsumes call below.
bool subsumes = AccessCheck::subsumes(js::GetContextCompartment(cx),
js::GetObjectCompartment(obj));
XrayType xrayType = GetXrayType(obj);
if (!subsumes && (xrayType == NotXray || ForceCOWBehavior(obj))) {
JSProtoKey key = JSProto_Null;
{
JSAutoCompartment ac(cx, obj);
key = IdentifyStandardPrototype(obj);
}
if (key != JSProto_Null) {
RootedObject homeProto(cx);
if (!JS_GetClassPrototype(cx, key, &homeProto))
return nullptr;
MOZ_ASSERT(homeProto);
// No need to double-wrap here. We should never have waivers to
// COWs.
return homeProto;
}
}
// Now, our object is ready to be wrapped, but several objects (notably
// nsJSIIDs) have a wrapper per scope. If we are about to wrap one of
// those objects in a security wrapper, then we need to hand back the
@ -478,13 +430,11 @@ WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj,
wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall>::singleton;
}
// If this is a chrome object being exposed to content without Xrays, use
// a COW.
//
// We make an exception for Object instances, because we still rely on COWs
// for those in a lot of places in the tree.
// For Vanilla JSObjects exposed from chrome to content, we use a wrapper
// that supports __exposedProps__. We'd like to get rid of these eventually,
// but in their current form they don't cause much trouble.
else if (originIsChrome && !targetIsChrome &&
(xrayType == NotXray || ForceCOWBehavior(obj)))
IdentifyStandardInstance(obj) == JSProto_Object)
{
wrapper = &ChromeObjectWrapper::singleton;
}

View File

@ -34,6 +34,9 @@ public:
nsResetStyleData_id,
nsFrameList_id,
CustomCounterStyle_id,
DependentBuiltinCounterStyle_id,
First_nsStyleStruct_id,
DummyBeforeStyleStructs_id = First_nsStyleStruct_id - 1,

View File

@ -968,11 +968,6 @@ nsPresContext::Init(nsDeviceContext* aDeviceContext)
mAnimationManager = new nsAnimationManager(this);
// Since CounterStyleManager is also the name of a method of
// nsPresContext, it is necessary to prefix the class with the mozilla
// namespace here.
mCounterStyleManager = new mozilla::CounterStyleManager(this);
if (mDocument->GetDisplayDocument()) {
NS_ASSERTION(mDocument->GetDisplayDocument()->GetShell() &&
mDocument->GetDisplayDocument()->GetShell()->GetPresContext(),
@ -1100,6 +1095,10 @@ nsPresContext::SetShell(nsIPresShell* aShell)
mFontFaceSet->DestroyUserFontSet();
mFontFaceSet = nullptr;
}
if (mCounterStyleManager) {
mCounterStyleManager->Disconnect();
mCounterStyleManager = nullptr;
}
if (mShell) {
// Remove ourselves as the charset observer from the shell's doc, because
@ -1113,6 +1112,11 @@ nsPresContext::SetShell(nsIPresShell* aShell)
mShell = aShell;
if (mShell) {
// Since CounterStyleManager is also the name of a method of
// nsPresContext, it is necessary to prefix the class with the mozilla
// namespace here.
mCounterStyleManager = new mozilla::CounterStyleManager(this);
nsIDocument *doc = mShell->GetDocument();
NS_ASSERTION(doc, "expect document here");
if (doc) {
@ -1156,10 +1160,6 @@ nsPresContext::SetShell(nsIPresShell* aShell)
mRestyleManager->Disconnect();
mRestyleManager = nullptr;
}
if (mCounterStyleManager) {
mCounterStyleManager->Disconnect();
mCounterStyleManager = nullptr;
}
if (IsRoot()) {
// Have to cancel our plugin geometry timer, because the

View File

@ -0,0 +1,41 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<style>
.parent {
position: relative;
width: 100px;
height: 100px;
}
.child {
filter: blur(3px);
background: #0f0;
width: 100px;
height: 100px;
}
.black-rect {
position: absolute;
top: 50px;
width: 100px;
height: 50px;
background: #000;
}
</style>
</head>
<body>
<!--
You should see a blurred green square, with its bottom half mostly
covered by a black rectangle.
-->
<div class="parent">
<div class="child"></div>
<div class="black-rect"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="blur">
<feGaussianBlur stdDeviation="3"/>
</filter>
</defs>
<rect x="0" y="0" width="100" height="50" fill="#fff"/>
<rect x="0" y="0" width="100" height="100" fill="#0f0" filter="url(#blur)"/>
<rect x="0" y="50" width="100" height="50" fill="#000"/>
</svg>

After

Width:  |  Height:  |  Size: 459 B

View File

@ -0,0 +1,37 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<style>
.parent {
/*
With the darken blend mode, green will overwrite white parts of this
background, while black parts of this background will show through.
*/
background: linear-gradient(#fff 0%, #fff 50%, #000 50%, #000 100%);
width: 100px;
height: 100px;
}
.child {
filter: blur(3px);
mix-blend-mode: darken;
background: #0f0;
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<!--
You should see a blurred green square, with its bottom half mostly
covered by a black rectangle.
-->
<div class="parent">
<div class="child"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,21 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="blur">
<feGaussianBlur stdDeviation="3"/>
</filter>
</defs>
<!-- Background consists of a white rect and a black rect. -->
<rect x="0" y="0" width="100" height="50" fill="#fff"/>
<rect x="0" y="50" width="100" height="50" fill="#000"/>
<!--
Foreground consists of a blurred green square, blended with the background
using the darken blend mode. Green should overwrite the white rect, while
the black rect should show through.
-->
<rect x="0" y="0" width="100" height="100" fill="#0f0" filter="url(#blur)"
style="mix-blend-mode: darken;"/>
</svg>

After

Width:  |  Height:  |  Size: 774 B

View File

@ -40,6 +40,9 @@ fuzzy-if(azureQuartz,2,40000) fuzzy-if(azureSkia||d2d||gtk2Widget,1,40000) pref(
#fuzzy due to inconsistencies in rounded rect cliping between parent and child; may be related to antialiasing. Between platforms, the max difference is the same, and the number of different pixels is either 36 or 37. (Win, Mac and Lin)
fuzzy(64,37) pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-952051.html mix-blend-mode-952051-ref.html
pref(layout.css.mix-blend-mode.enabled,true) pref(layout.css.filters.enabled,true) == mix-blend-mode-and-filter.html mix-blend-mode-and-filter-ref.html
pref(layout.css.mix-blend-mode.enabled,true) pref(layout.css.filters.enabled,true) == mix-blend-mode-and-filter.svg mix-blend-mode-and-filter-ref.svg
pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-child-of-blended-has-opacity.html mix-blend-mode-child-of-blended-has-opacity-ref.html
pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-nested-976533.html mix-blend-mode-nested-976533-ref.html

View File

@ -1,13 +1,14 @@
fails-if(Android||B2G) HTTP(..) == aspect-ratio-1a.xhtml aspect-ratio-1-ref.html # bug 773482
fails-if(Android||B2G) HTTP(..) == aspect-ratio-1b.xhtml aspect-ratio-1-ref.html # bug 773482
fails-if(Android||B2G) skip-if(gtk2Widget) HTTP(..) == aspect-ratio-2a.xhtml aspect-ratio-2-ref.html # bug 773482
fails-if(Android||B2G) skip-if(gtk2Widget) HTTP(..) == aspect-ratio-2b.xhtml aspect-ratio-2-ref.html # bug 773482
# NOTE: bug 1084564 covers "fails"/"skip" annotations for b2g/android below:
fails-if(Android||B2G) HTTP(..) == aspect-ratio-1a.xhtml aspect-ratio-1-ref.html
fails-if(Android||B2G) HTTP(..) == aspect-ratio-1b.xhtml aspect-ratio-1-ref.html
fails-if(Android||B2G) skip-if(gtk2Widget) HTTP(..) == aspect-ratio-2a.xhtml aspect-ratio-2-ref.html
fails-if(Android||B2G) skip-if(gtk2Widget) HTTP(..) == aspect-ratio-2b.xhtml aspect-ratio-2-ref.html
HTTP(..) == aspect-ratio-3a.xhtml aspect-ratio-3-ref.xhtml
HTTP(..) == aspect-ratio-3b.xhtml aspect-ratio-3-ref.xhtml
fails-if(Android||B2G) random-if(layersGPUAccelerated) fails-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == encoded-aspect-ratio-1.html encoded-aspect-ratio-1-ref.html # bug 623460 for WinXP # bug 773482
fails-if(Android||B2G) HTTP(..) == basic-1.xhtml basic-1-ref.html # bug 773482
skip-if(Android||B2G) HTTP(..) == canvas-1a.xhtml basic-1-ref.html # bug 773482
fails-if(Android||B2G) HTTP(..) == canvas-1b.xhtml basic-1-ref.html # bug 773482
fails-if(Android||B2G) random-if(layersGPUAccelerated) fails-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == encoded-aspect-ratio-1.html encoded-aspect-ratio-1-ref.html # bug 623460 for WinXP
fails-if(Android||B2G) HTTP(..) == basic-1.xhtml basic-1-ref.html
skip-if(Android||B2G) HTTP(..) == canvas-1a.xhtml basic-1-ref.html
fails-if(Android||B2G) HTTP(..) == canvas-1b.xhtml basic-1-ref.html
== clipping-1a.html clipping-1-ref.html
== empty-1a.html empty-1-ref.html
== empty-1b.html empty-1-ref.html

View File

@ -1,3 +1,4 @@
# NOTE: bug 1084564 covers "fails"/"skip" annotations for b2g/android below:
fails-if(Android||B2G) HTTP(..) == aspect-ratio-1a.xhtml aspect-ratio-1-ref.html
fails-if(Android||B2G) HTTP(..) == aspect-ratio-1b.xhtml aspect-ratio-1-ref.html
fails-if(Android||B2G) skip-if(gtk2Widget) HTTP(..) == aspect-ratio-2a.xhtml aspect-ratio-2-ref.html

View File

@ -966,12 +966,32 @@ public:
// DependentBuiltinCounterStyle is managed in the same way as
// CustomCounterStyle.
NS_INLINE_DECL_REFCOUNTING(DependentBuiltinCounterStyle)
NS_IMETHOD_(MozExternalRefCountType) AddRef() MOZ_OVERRIDE;
NS_IMETHOD_(MozExternalRefCountType) Release() MOZ_OVERRIDE;
void* operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
{
return aPresContext->PresShell()->AllocateByObjectID(
nsPresArena::DependentBuiltinCounterStyle_id, sz);
}
private:
void Destroy()
{
nsIPresShell* shell = mManager->PresContext()->PresShell();
this->~DependentBuiltinCounterStyle();
shell->FreeByObjectID(nsPresArena::DependentBuiltinCounterStyle_id, this);
}
CounterStyleManager* mManager;
nsAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
};
NS_IMPL_ADDREF(DependentBuiltinCounterStyle)
NS_IMPL_RELEASE_WITH_DESTROY(DependentBuiltinCounterStyle, Destroy())
/* virtual */ CounterStyle*
DependentBuiltinCounterStyle::GetFallback()
{
@ -1064,9 +1084,23 @@ public:
// CustomCounterStyle should be reference-counted because it may be
// dereferenced from the manager but still referenced by nodes and
// frames before the style change is propagated.
NS_INLINE_DECL_REFCOUNTING(CustomCounterStyle)
NS_IMETHOD_(MozExternalRefCountType) AddRef() MOZ_OVERRIDE;
NS_IMETHOD_(MozExternalRefCountType) Release() MOZ_OVERRIDE;
void* operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
{
return aPresContext->PresShell()->AllocateByObjectID(
nsPresArena::CustomCounterStyle_id, sz);
}
private:
void Destroy()
{
nsIPresShell* shell = mManager->PresContext()->PresShell();
this->~CustomCounterStyle();
shell->FreeByObjectID(nsPresArena::CustomCounterStyle_id, this);
}
const nsTArray<nsString>& GetSymbols();
const nsTArray<AdditiveSymbol>& GetAdditiveSymbols();
@ -1139,8 +1173,14 @@ private:
// counter must be either a builtin style or a style whose system is
// not 'extends'.
CounterStyle* mExtendsRoot;
nsAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
};
NS_IMPL_ADDREF(CustomCounterStyle)
NS_IMPL_RELEASE_WITH_DESTROY(CustomCounterStyle, Destroy())
void
CustomCounterStyle::ResetCachedData()
{
@ -1968,13 +2008,13 @@ CounterStyleManager::BuildCounterStyle(const nsSubstring& aName)
nsCSSCounterStyleRule* rule =
mPresContext->StyleSet()->CounterStyleRuleForName(mPresContext, aName);
if (rule) {
data = new CustomCounterStyle(this, rule);
data = new (mPresContext) CustomCounterStyle(this, rule);
} else {
int32_t type;
nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(aName);
if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kListStyleKTable, type)) {
if (gBuiltinStyleTable[type].IsDependentStyle()) {
data = new DependentBuiltinCounterStyle(type, this);
data = new (mPresContext) DependentBuiltinCounterStyle(type, this);
} else {
data = GetBuiltinStyle(type);
}

View File

@ -176,6 +176,8 @@ public:
// be called when any counter style may be affected.
bool NotifyRuleChanged();
nsPresContext* PresContext() const { return mPresContext; }
NS_INLINE_DECL_REFCOUNTING(CounterStyleManager)
private:

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