mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-23 10:54:33 +00:00
Merge the last PGO-green inbound changeset to m-c.
This commit is contained in:
commit
52ef3171c3
@ -59,9 +59,9 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(nsAccessiblePivot)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAccessiblePivot)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRoot, nsIAccessible)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mPosition, nsIAccessible)
|
||||
uint32_t i, length = tmp->mObservers.Length(); \
|
||||
uint32_t i, length = tmp->mObservers.Length();
|
||||
for (i = 0; i < length; ++i) {
|
||||
cb.NoteXPCOMChild(tmp->mObservers.ElementAt(i).get());
|
||||
cb.NoteXPCOMChild(tmp->mObservers[i]);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
|
@ -586,4 +586,4 @@ pref("network.activity.blipIntervalMilliseconds", 250);
|
||||
pref("general.useragent.override.facebook.com", "\(Mobile#(Android; Mobile");
|
||||
pref("general.useragent.override.youtube.com", "\(Mobile#(Android; Mobile");
|
||||
|
||||
pref("jsloader.reuseGlobal", false);
|
||||
pref("jsloader.reuseGlobal", true);
|
||||
|
@ -20,7 +20,6 @@ Browser context menu tests.
|
||||
|
||||
SpecialPowers.Cu.import("resource://gre/modules/InlineSpellChecker.jsm", window);
|
||||
|
||||
const Cc = SpecialPowers.Components.classes;
|
||||
const Ci = SpecialPowers.Ci;
|
||||
|
||||
function openContextMenuFor(element, shiftkey, shouldWaitForFocus) {
|
||||
|
@ -61,7 +61,7 @@ VariablesView.prototype = {
|
||||
*/
|
||||
empty: function VV_empty(aTimeout = LAZY_EMPTY_DELAY) {
|
||||
// If there are no items in this container, emptying is useless.
|
||||
if (!this._store.size()) {
|
||||
if (!this._store.size) {
|
||||
return;
|
||||
}
|
||||
// Check if this empty operation may be executed lazily.
|
||||
@ -110,7 +110,7 @@ VariablesView.prototype = {
|
||||
this._parent.removeChild(prevList);
|
||||
this._parent.appendChild(currList);
|
||||
|
||||
if (!this._store.size()) {
|
||||
if (!this._store.size) {
|
||||
this._appendEmptyNotice();
|
||||
}
|
||||
}.bind(this), aTimeout);
|
||||
|
@ -812,7 +812,7 @@ create({ constructor: GlobalSearchView, proto: MenuContainer.prototype }, {
|
||||
*/
|
||||
_fetchSources: function DVGS__fetchSources(aFetchCallback, aFetchedCallback, aLocations) {
|
||||
// If all the sources were already fetched, then don't do anything.
|
||||
if (this._cache.size() == aLocations.length) {
|
||||
if (this._cache.size == aLocations.length) {
|
||||
aFetchedCallback();
|
||||
return;
|
||||
}
|
||||
@ -840,7 +840,7 @@ create({ constructor: GlobalSearchView, proto: MenuContainer.prototype }, {
|
||||
this._cache.set(aLocation, aContents);
|
||||
|
||||
// Check if all sources were fetched and stored in the cache.
|
||||
if (this._cache.size() == this._sourcesCount) {
|
||||
if (this._cache.size == this._sourcesCount) {
|
||||
this._onFetchSourcesFinished();
|
||||
}
|
||||
},
|
||||
@ -1151,7 +1151,7 @@ GlobalResults.prototype = {
|
||||
/**
|
||||
* Gets the number of source results in this store.
|
||||
*/
|
||||
get itemCount() this._store.size(),
|
||||
get itemCount() this._store.size,
|
||||
|
||||
_store: null
|
||||
};
|
||||
|
@ -134,7 +134,7 @@ function testLocationChange()
|
||||
window.addEventListener("Debugger:GlobalSearch:CacheCleared", function _onCacheCleared(aEvent) {
|
||||
window.removeEventListener(aEvent.type, _onCacheCleared);
|
||||
|
||||
is(gSearchView._cache.size(), 0,
|
||||
is(gSearchView._cache.size, 0,
|
||||
"The scripts sources cache for global searching should be cleared after a page navigation.")
|
||||
|
||||
cacheCleared = true;
|
||||
|
@ -138,14 +138,14 @@ this.DOMApplicationRegistry = {
|
||||
},
|
||||
|
||||
// Registers all the activities and system messages.
|
||||
registerAppsHandlers: function registerAppsHandlers() {
|
||||
registerAppsHandlers: function registerAppsHandlers(aRunUpdate) {
|
||||
this.notifyAppsRegistryStart();
|
||||
let ids = [];
|
||||
for (let id in this.webapps) {
|
||||
ids.push({ id: id });
|
||||
}
|
||||
#ifdef MOZ_SYS_MSG
|
||||
this._processManifestForIds(ids);
|
||||
this._processManifestForIds(ids, aRunUpdate);
|
||||
#else
|
||||
// Read the CSPs. If MOZ_SYS_MSG is defined this is done on
|
||||
// _processManifestForIds so as to not reading the manifests
|
||||
@ -291,7 +291,7 @@ this.DOMApplicationRegistry = {
|
||||
this.updateOfflineCacheForApp(id);
|
||||
}
|
||||
}
|
||||
this.registerAppsHandlers();
|
||||
this.registerAppsHandlers(runUpdate);
|
||||
}).bind(this);
|
||||
|
||||
this.loadCurrentRegistry((function() {
|
||||
@ -351,7 +351,7 @@ this.DOMApplicationRegistry = {
|
||||
|
||||
// |aEntryPoint| is either the entry_point name or the null in which case we
|
||||
// use the root of the manifest.
|
||||
_createActivitiesToRegister: function(aManifest, aApp, aEntryPoint) {
|
||||
_createActivitiesToRegister: function(aManifest, aApp, aEntryPoint, aRunUpdate) {
|
||||
let activitiesToRegister = [];
|
||||
let root = aManifest;
|
||||
if (aEntryPoint && aManifest.entry_points[aEntryPoint]) {
|
||||
@ -369,14 +369,16 @@ this.DOMApplicationRegistry = {
|
||||
description.href = manifest.launch_path;
|
||||
}
|
||||
description.href = manifest.resolveFromOrigin(description.href);
|
||||
activitiesToRegister.push({ "manifest": aApp.manifestURL,
|
||||
"name": activity,
|
||||
"title": manifest.name,
|
||||
"icon": manifest.iconURLForSize(128),
|
||||
"description": description });
|
||||
|
||||
let launchPath =
|
||||
Services.io.newURI(manifest.resolveFromOrigin(description.href), null, null);
|
||||
if (aRunUpdate) {
|
||||
activitiesToRegister.push({ "manifest": aApp.manifestURL,
|
||||
"name": activity,
|
||||
"title": manifest.name,
|
||||
"icon": manifest.iconURLForSize(128),
|
||||
"description": description });
|
||||
}
|
||||
|
||||
let launchPath = Services.io.newURI(description.href, null, null);
|
||||
let manifestURL = Services.io.newURI(aApp.manifestURL, null, null);
|
||||
msgmgr.registerPage("activity", launchPath, manifestURL);
|
||||
}
|
||||
@ -385,14 +387,14 @@ this.DOMApplicationRegistry = {
|
||||
|
||||
// |aAppsToRegister| contains an array of apps to be registered, where
|
||||
// each element is an object in the format of {manifest: foo, app: bar}.
|
||||
_registerActivitiesForApps: function(aAppsToRegister) {
|
||||
_registerActivitiesForApps: function(aAppsToRegister, aRunUpdate) {
|
||||
// Collect the activities to be registered for root and entry_points.
|
||||
let activitiesToRegister = [];
|
||||
aAppsToRegister.forEach(function (aApp) {
|
||||
let manifest = aApp.manifest;
|
||||
let app = aApp.app;
|
||||
activitiesToRegister.push.apply(activitiesToRegister,
|
||||
this._createActivitiesToRegister(manifest, app, null));
|
||||
this._createActivitiesToRegister(manifest, app, null, aRunUpdate));
|
||||
|
||||
if (!manifest.entry_points) {
|
||||
return;
|
||||
@ -400,18 +402,23 @@ this.DOMApplicationRegistry = {
|
||||
|
||||
for (let entryPoint in manifest.entry_points) {
|
||||
activitiesToRegister.push.apply(activitiesToRegister,
|
||||
this._createActivitiesToRegister(manifest, app, entryPoint));
|
||||
this._createActivitiesToRegister(manifest, app, entryPoint, aRunUpdate));
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (!aRunUpdate || activitiesToRegister.length == 0) {
|
||||
this.notifyAppsRegistryReady();
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the array carrying all the activities to be registered.
|
||||
cpmm.sendAsyncMessage("Activities:Register", activitiesToRegister);
|
||||
},
|
||||
|
||||
// Better to directly use |_registerActivitiesForApps()| if we have
|
||||
// multiple apps to be registered for activities.
|
||||
_registerActivities: function(aManifest, aApp) {
|
||||
this._registerActivitiesForApps([{ manifest: aManifest, app: aApp }]);
|
||||
_registerActivities: function(aManifest, aApp, aRunUpdate) {
|
||||
this._registerActivitiesForApps([{ manifest: aManifest, app: aApp }], aRunUpdate);
|
||||
},
|
||||
|
||||
// |aEntryPoint| is either the entry_point name or the null in which case we
|
||||
@ -466,7 +473,7 @@ this.DOMApplicationRegistry = {
|
||||
this._unregisterActivitiesForApps([{ manifest: aManifest, app: aApp }]);
|
||||
},
|
||||
|
||||
_processManifestForIds: function(aIds) {
|
||||
_processManifestForIds: function(aIds, aRunUpdate) {
|
||||
this._readManifests(aIds, (function registerManifests(aResults) {
|
||||
let appsToRegister = [];
|
||||
aResults.forEach(function registerManifest(aResult) {
|
||||
@ -477,7 +484,7 @@ this.DOMApplicationRegistry = {
|
||||
this._registerSystemMessages(manifest, app);
|
||||
appsToRegister.push({ manifest: manifest, app: app });
|
||||
}, this);
|
||||
this._registerActivitiesForApps(appsToRegister);
|
||||
this._registerActivitiesForApps(appsToRegister, aRunUpdate);
|
||||
}).bind(this));
|
||||
},
|
||||
#endif
|
||||
@ -821,6 +828,11 @@ this.DOMApplicationRegistry = {
|
||||
|
||||
let id = this._appIdForManifestURL(app.manifestURL);
|
||||
|
||||
// Clean up the deprecated manifest cache if needed.
|
||||
if (id in this._manifestCache) {
|
||||
delete this._manifestCache[id];
|
||||
}
|
||||
|
||||
// Move the application.zip and manifest.webapp files out of TmpD
|
||||
let tmpDir = FileUtils.getDir("TmpD", ["webapps", id], true, true);
|
||||
let manFile = tmpDir.clone();
|
||||
@ -915,13 +927,17 @@ this.DOMApplicationRegistry = {
|
||||
debug("updateHostedApp");
|
||||
let id = this._appId(app.origin);
|
||||
|
||||
if (id in this._manifestCache) {
|
||||
delete this._manifestCache[id];
|
||||
}
|
||||
|
||||
// Update the web apps' registration.
|
||||
this.notifyAppsRegistryStart();
|
||||
#ifdef MOZ_SYS_MSG
|
||||
this._readManifests([{ id: id }], (function unregisterManifest(aResult) {
|
||||
this._unregisterActivities(aResult[0].manifest, app);
|
||||
this._registerSystemMessages(aManifest, app);
|
||||
this._registerActivities(aManifest, app);
|
||||
this._registerActivities(aManifest, app, true);
|
||||
}).bind(this));
|
||||
#else
|
||||
// Nothing else to do but notifying we're ready.
|
||||
@ -1142,7 +1158,7 @@ this.DOMApplicationRegistry = {
|
||||
this.notifyAppsRegistryStart();
|
||||
#ifdef MOZ_SYS_MSG
|
||||
this._registerSystemMessages(app.manifest, app);
|
||||
this._registerActivities(app.manifest, app);
|
||||
this._registerActivities(app.manifest, app, true);
|
||||
#else
|
||||
// Nothing else to do but notifying we're ready.
|
||||
this.notifyAppsRegistryReady();
|
||||
@ -1229,6 +1245,9 @@ this.DOMApplicationRegistry = {
|
||||
/**
|
||||
* Asynchronously reads a list of manifests
|
||||
*/
|
||||
|
||||
_manifestCache: {},
|
||||
|
||||
_readManifests: function(aData, aFinalCallback, aIndex) {
|
||||
if (!aData.length) {
|
||||
aFinalCallback(aData);
|
||||
@ -1238,6 +1257,16 @@ this.DOMApplicationRegistry = {
|
||||
let index = aIndex || 0;
|
||||
let id = aData[index].id;
|
||||
|
||||
// Use the cached manifest instead of reading the file again from disk.
|
||||
if (id in this._manifestCache) {
|
||||
aData[index].manifest = this._manifestCache[id];
|
||||
if (index == aData.length - 1)
|
||||
aFinalCallback(aData);
|
||||
else
|
||||
this._readManifests(aData, aFinalCallback, index + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// the manifest file used to be named manifest.json, so fallback on this.
|
||||
let baseDir = (this.webapps[id].removable ? DIRECTORY_NAME : "coreAppsDir");
|
||||
let file = FileUtils.getFile(baseDir, ["webapps", id, "manifest.webapp"], true);
|
||||
@ -1249,7 +1278,7 @@ this.DOMApplicationRegistry = {
|
||||
}
|
||||
|
||||
this._loadJSONAsync(file, (function(aJSON) {
|
||||
aData[index].manifest = aJSON;
|
||||
aData[index].manifest = this._manifestCache[id] = aJSON;
|
||||
if (index == aData.length - 1)
|
||||
aFinalCallback(aData);
|
||||
else
|
||||
@ -1406,6 +1435,11 @@ this.DOMApplicationRegistry = {
|
||||
if (!this.webapps[id].removable)
|
||||
return;
|
||||
|
||||
// Clean up the deprecated manifest cache if needed.
|
||||
if (id in this._manifestCache) {
|
||||
delete this._manifestCache[id];
|
||||
}
|
||||
|
||||
// Clear private data first.
|
||||
this._clearPrivateData(app.localId, false);
|
||||
|
||||
@ -1621,6 +1655,11 @@ this.DOMApplicationRegistry = {
|
||||
if (!this.webapps[record.id] || !this.webapps[record.id].removable)
|
||||
continue;
|
||||
|
||||
// Clean up the deprecated manifest cache if needed.
|
||||
if (record.id in this._manifestCache) {
|
||||
delete this._manifestCache[record.id];
|
||||
}
|
||||
|
||||
let origin = this.webapps[record.id].origin;
|
||||
delete this.webapps[record.id];
|
||||
let dir = this._getAppDir(record.id);
|
||||
@ -1667,6 +1706,10 @@ this.DOMApplicationRegistry = {
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the manifest cache.
|
||||
this._manifestCache = { };
|
||||
|
||||
this._saveApps(aCallback);
|
||||
},
|
||||
|
||||
|
@ -1755,6 +1755,8 @@ nsFocusManager::Focus(nsPIDOMWindow* aWindow,
|
||||
// if switching to a new document, first fire the focus event on the
|
||||
// document and then the window.
|
||||
if (aIsNewDocument) {
|
||||
nsIMEStateManager::OnChangeFocus(presShell->GetPresContext(), nullptr,
|
||||
GetFocusMoveActionCause(aFlags));
|
||||
nsIDocument* doc = aWindow->GetExtantDoc();
|
||||
if (doc)
|
||||
SendFocusOrBlurEvent(NS_FOCUS_CONTENT, presShell, doc,
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
#define TARGET_FOLDER "/sdcard/download/bluetooth/"
|
||||
#define TARGET_FOLDER "/sdcard/downloads/bluetooth/"
|
||||
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
using namespace mozilla;
|
||||
@ -44,7 +44,9 @@ public:
|
||||
bool Init()
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
|
||||
if (NS_FAILED(obs->AddObserver(this,
|
||||
NS_XPCOM_SHUTDOWN_OBSERVER_ID,
|
||||
false))) {
|
||||
NS_WARNING("Failed to add shutdown observer!");
|
||||
return false;
|
||||
}
|
||||
@ -162,6 +164,7 @@ BluetoothOppManager::BluetoothOppManager() : mConnected(false)
|
||||
, mWaitingForConfirmationFlag(false)
|
||||
{
|
||||
mConnectedDeviceAddress.AssignLiteral("00:00:00:00:00:00");
|
||||
mSocketStatus = GetConnectionStatus();
|
||||
}
|
||||
|
||||
BluetoothOppManager::~BluetoothOppManager()
|
||||
@ -194,7 +197,7 @@ BluetoothOppManager::Connect(const nsAString& aDeviceObjectPath,
|
||||
}
|
||||
|
||||
nsString serviceUuidStr =
|
||||
NS_ConvertUTF8toUTF16(mozilla::dom::bluetooth::BluetoothServiceUuidStr::ObjectPush);
|
||||
NS_ConvertUTF8toUTF16(BluetoothServiceUuidStr::ObjectPush);
|
||||
|
||||
nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
|
||||
|
||||
@ -244,12 +247,13 @@ BluetoothOppManager::Listen()
|
||||
true,
|
||||
true,
|
||||
this);
|
||||
mSocketStatus = GetConnectionStatus();
|
||||
|
||||
return NS_FAILED(rv) ? false : true;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothOppManager::SendFile(BlobParent* aActor,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
BluetoothOppManager::SendFile(BlobParent* aActor)
|
||||
{
|
||||
if (mBlob) {
|
||||
// Means there's a sending process. Reply error.
|
||||
@ -270,24 +274,19 @@ BluetoothOppManager::SendFile(BlobParent* aActor,
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothOppManager::StopSendingFile(BluetoothReplyRunnable* aRunnable)
|
||||
BluetoothOppManager::StopSendingFile()
|
||||
{
|
||||
if (!mBlob) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mAbortFlag = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::ConfirmReceivingFile(bool aConfirm,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
bool
|
||||
BluetoothOppManager::ConfirmReceivingFile(bool aConfirm)
|
||||
{
|
||||
if (!mWaitingForConfirmationFlag) {
|
||||
NS_WARNING("We are not waiting for a confirmation now.");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_ASSERTION(mPacketLeftLength == 0,
|
||||
@ -299,6 +298,8 @@ BluetoothOppManager::ConfirmReceivingFile(bool aConfirm,
|
||||
if (aConfirm) {
|
||||
StartFileTransfer(mConnectedDeviceAddress, true,
|
||||
sFileName, sFileLength, sContentType);
|
||||
} else {
|
||||
DeleteReceivedFile();
|
||||
}
|
||||
|
||||
if (mPutFinal || !aConfirm) {
|
||||
@ -306,6 +307,8 @@ BluetoothOppManager::ConfirmReceivingFile(bool aConfirm,
|
||||
FileTransferComplete(mConnectedDeviceAddress, aConfirm, true, sFileName,
|
||||
sSentFileLength, sContentType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
@ -347,6 +350,28 @@ BluetoothOppManager::AfterOppDisconnected()
|
||||
mConnectedDeviceAddress.AssignLiteral("00:00:00:00:00:00");
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::DeleteReceivedFile()
|
||||
{
|
||||
nsString path;
|
||||
path.AssignLiteral(TARGET_FOLDER);
|
||||
path += sFileName;
|
||||
|
||||
nsCOMPtr<nsIFile> f;
|
||||
nsresult rv = NS_NewLocalFile(path + sFileName, false, getter_AddRefs(f));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Couldn't find received file, nothing to delete.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mOutputStream) {
|
||||
mOutputStream->Close();
|
||||
mOutputStream = nullptr;
|
||||
}
|
||||
|
||||
f->Remove(false);
|
||||
}
|
||||
|
||||
// Virtual function of class SocketConsumer
|
||||
void
|
||||
BluetoothOppManager::ReceiveSocketData(UnixSocketRawData* aMessage)
|
||||
@ -520,10 +545,9 @@ BluetoothOppManager::ReceiveSocketData(UnixSocketRawData* aMessage)
|
||||
pktHeaders.GetContentType(sContentType);
|
||||
pktHeaders.GetLength(&sFileLength);
|
||||
|
||||
path += sFileName;
|
||||
|
||||
nsCOMPtr<nsIFile> f;
|
||||
nsresult rv = NS_NewLocalFile(path, false, getter_AddRefs(f));
|
||||
nsresult rv;
|
||||
rv = NS_NewLocalFile(path + sFileName, false, getter_AddRefs(f));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Couldn't new a local file");
|
||||
}
|
||||
@ -533,6 +557,13 @@ BluetoothOppManager::ReceiveSocketData(UnixSocketRawData* aMessage)
|
||||
NS_WARNING("Couldn't create the file");
|
||||
}
|
||||
|
||||
/*
|
||||
* The function CreateUnique() may create a file with a different file
|
||||
* name from the original sFileName. Therefore we have to retrieve
|
||||
* the file name again.
|
||||
*/
|
||||
f->GetLeafName(sFileName);
|
||||
|
||||
NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f);
|
||||
if (!mOutputStream) {
|
||||
NS_WARNING("Couldn't new an output stream");
|
||||
@ -581,7 +612,9 @@ BluetoothOppManager::ReceiveSocketData(UnixSocketRawData* aMessage)
|
||||
"Invalid packet length");
|
||||
mPacketLeftLength -= receivedLength;
|
||||
|
||||
mOutputStream->Write((char*)&aMessage->mData[0], receivedLength, &wrote);
|
||||
mOutputStream->Write((char*)&aMessage->mData[0],
|
||||
receivedLength,
|
||||
&wrote);
|
||||
NS_ASSERTION(receivedLength == wrote, "Writing to the file failed");
|
||||
}
|
||||
|
||||
@ -598,12 +631,17 @@ BluetoothOppManager::ReceiveSocketData(UnixSocketRawData* aMessage)
|
||||
ReceivingFileConfirmation(mConnectedDeviceAddress, sFileName,
|
||||
sFileLength, sContentType);
|
||||
} else {
|
||||
ReplyToPut(mPutFinal, true);
|
||||
ReplyToPut(mPutFinal, mAbortFlag ? false : true);
|
||||
|
||||
if (mPutFinal) {
|
||||
if (mAbortFlag) {
|
||||
mReceiving = false;
|
||||
FileTransferComplete(mConnectedDeviceAddress, true, true, sFileName,
|
||||
sSentFileLength, sContentType);
|
||||
FileTransferComplete(mConnectedDeviceAddress, false, true,
|
||||
sFileName, sSentFileLength, sContentType);
|
||||
DeleteReceivedFile();
|
||||
} else if (mPutFinal) {
|
||||
mReceiving = false;
|
||||
FileTransferComplete(mConnectedDeviceAddress, true, true,
|
||||
sFileName, sSentFileLength, sContentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -625,7 +663,7 @@ BluetoothOppManager::SendConnectRequest()
|
||||
req[3] = 0x10; // version=1.0
|
||||
req[4] = 0x00; // flag=0x00
|
||||
req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
|
||||
req[6] = BluetoothOppManager::MAX_PACKET_LENGTH;
|
||||
req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH;
|
||||
|
||||
index += AppendHeaderConnectionId(&req[index], mConnectionId);
|
||||
SetObexPacketInfo(req, ObexRequestCode::Connect, index);
|
||||
@ -642,9 +680,10 @@ BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName,
|
||||
{
|
||||
uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
|
||||
|
||||
const PRUnichar* fileNamePtr = aFileName.BeginReading();
|
||||
uint32_t len = aFileName.Length();
|
||||
int len = aFileName.Length();
|
||||
uint8_t* fileName = new uint8_t[(len + 1) * 2];
|
||||
const PRUnichar* fileNamePtr = aFileName.BeginReading();
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
fileName[i * 2] = (uint8_t)(fileNamePtr[i] >> 8);
|
||||
fileName[i * 2 + 1] = (uint8_t)fileNamePtr[i];
|
||||
@ -751,7 +790,7 @@ BluetoothOppManager::ReplyToConnect()
|
||||
req[3] = 0x10; // version=1.0
|
||||
req[4] = 0x00; // flag=0x00
|
||||
req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
|
||||
req[6] = BluetoothOppManager::MAX_PACKET_LENGTH;
|
||||
req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH;
|
||||
|
||||
SetObexPacketInfo(req, ObexResponseCode::Success, index);
|
||||
|
||||
@ -798,7 +837,9 @@ BluetoothOppManager::ReplyToPut(bool aFinal, bool aContinue)
|
||||
if (aFinal) {
|
||||
SetObexPacketInfo(req, ObexResponseCode::Unauthorized, index);
|
||||
} else {
|
||||
SetObexPacketInfo(req, ObexResponseCode::Unauthorized & (~FINAL_BIT), index);
|
||||
SetObexPacketInfo(req,
|
||||
ObexResponseCode::Unauthorized & (~FINAL_BIT),
|
||||
index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -949,7 +990,7 @@ BluetoothOppManager::ReceivingFileConfirmation(const nsString& aAddress,
|
||||
parameters.AppendElement(BluetoothNamedValue(name, v));
|
||||
|
||||
if (!BroadcastSystemMessage(type, parameters)) {
|
||||
NS_WARNING("Failed to broadcast [bluetooth-opp-receiving-file-confirmation]");
|
||||
NS_WARNING("Failed to send [bluetooth-opp-receiving-file-confirmation]");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -960,11 +1001,16 @@ BluetoothOppManager::OnConnectSuccess()
|
||||
// Cache device address since we can't get socket address when a remote
|
||||
// device disconnect with us.
|
||||
GetSocketAddr(mConnectedDeviceAddress);
|
||||
|
||||
mSocketStatus = GetConnectionStatus();
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::OnConnectError()
|
||||
{
|
||||
CloseSocket();
|
||||
mSocketStatus = GetConnectionStatus();
|
||||
Listen();
|
||||
}
|
||||
|
||||
void
|
||||
@ -974,4 +1020,8 @@ BluetoothOppManager::OnDisconnect()
|
||||
// closing socket without sending OBEX disconnect request first. So we
|
||||
// call AfterOppDisconnected here to ensure all variables will be cleaned.
|
||||
AfterOppDisconnected();
|
||||
|
||||
if (mSocketStatus == SocketConnectionStatus::SOCKET_CONNECTED) {
|
||||
Listen();
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "BluetoothCommon.h"
|
||||
#include "mozilla/dom/ipc/Blob.h"
|
||||
#include "mozilla/ipc/UnixSocket.h"
|
||||
#include "nsIDOMFile.h"
|
||||
|
||||
class nsIOutputStream;
|
||||
class nsIInputStream;
|
||||
@ -51,10 +50,9 @@ public:
|
||||
void Disconnect();
|
||||
bool Listen();
|
||||
|
||||
bool SendFile(BlobParent* aBlob,
|
||||
BluetoothReplyRunnable* aRunnable);
|
||||
bool StopSendingFile(BluetoothReplyRunnable* aRunnable);
|
||||
void ConfirmReceivingFile(bool aConfirm, BluetoothReplyRunnable* aRunnable);
|
||||
bool SendFile(BlobParent* aBlob);
|
||||
bool StopSendingFile();
|
||||
bool ConfirmReceivingFile(bool aConfirm);
|
||||
|
||||
void SendConnectRequest();
|
||||
void SendPutHeaderRequest(const nsAString& aFileName, int aFileSize);
|
||||
@ -85,6 +83,7 @@ private:
|
||||
const nsString& aFileName,
|
||||
uint32_t aFileLength,
|
||||
const nsString& aContentType);
|
||||
void DeleteReceivedFile();
|
||||
void ReplyToConnect();
|
||||
void ReplyToDisconnect();
|
||||
void ReplyToPut(bool aFinal, bool aContinue);
|
||||
@ -107,6 +106,7 @@ private:
|
||||
bool mPutFinal;
|
||||
bool mWaitingForConfirmationFlag;
|
||||
int mUpdateProgressCounter;
|
||||
enum mozilla::ipc::SocketConnectionStatus mSocketStatus;
|
||||
|
||||
nsCOMPtr<nsIDOMBlob> mBlob;
|
||||
nsCOMPtr<nsIThread> mReadFileThread;
|
||||
|
@ -273,13 +273,13 @@ public:
|
||||
virtual void
|
||||
Disconnect(uint16_t aProfileId, BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
SendFile(const nsAString& aDeviceAddress,
|
||||
BlobParent* aBlobParent,
|
||||
BlobChild* aBlobChild,
|
||||
BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
StopSendingFile(const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
|
@ -9,63 +9,63 @@
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
int
|
||||
AppendHeaderName(uint8_t* retBuf, const char* name, int length)
|
||||
AppendHeaderName(uint8_t* aRetBuf, const char* aName, int aLength)
|
||||
{
|
||||
int headerLength = length + 3;
|
||||
int headerLength = aLength + 3;
|
||||
|
||||
retBuf[0] = ObexHeaderId::Name;
|
||||
retBuf[1] = (headerLength & 0xFF00) >> 8;
|
||||
retBuf[2] = headerLength & 0x00FF;
|
||||
aRetBuf[0] = ObexHeaderId::Name;
|
||||
aRetBuf[1] = (headerLength & 0xFF00) >> 8;
|
||||
aRetBuf[2] = headerLength & 0x00FF;
|
||||
|
||||
memcpy(&retBuf[3], name, length);
|
||||
memcpy(&aRetBuf[3], aName, aLength);
|
||||
|
||||
return headerLength;
|
||||
}
|
||||
|
||||
int
|
||||
AppendHeaderBody(uint8_t* retBuf, uint8_t* data, int length)
|
||||
AppendHeaderBody(uint8_t* aRetBuf, uint8_t* aData, int aLength)
|
||||
{
|
||||
int headerLength = length + 3;
|
||||
int headerLength = aLength + 3;
|
||||
|
||||
retBuf[0] = ObexHeaderId::Body;
|
||||
retBuf[1] = (headerLength & 0xFF00) >> 8;
|
||||
retBuf[2] = headerLength & 0x00FF;
|
||||
aRetBuf[0] = ObexHeaderId::Body;
|
||||
aRetBuf[1] = (headerLength & 0xFF00) >> 8;
|
||||
aRetBuf[2] = headerLength & 0x00FF;
|
||||
|
||||
memcpy(&retBuf[3], data, length);
|
||||
memcpy(&aRetBuf[3], aData, aLength);
|
||||
|
||||
return headerLength;
|
||||
}
|
||||
|
||||
int
|
||||
AppendHeaderLength(uint8_t* retBuf, int objectLength)
|
||||
AppendHeaderLength(uint8_t* aRetBuf, int aObjectLength)
|
||||
{
|
||||
retBuf[0] = ObexHeaderId::Length;
|
||||
retBuf[1] = (objectLength & 0xFF000000) >> 24;
|
||||
retBuf[2] = (objectLength & 0x00FF0000) >> 16;
|
||||
retBuf[3] = (objectLength & 0x0000FF00) >> 8;
|
||||
retBuf[4] = objectLength & 0x000000FF;
|
||||
aRetBuf[0] = ObexHeaderId::Length;
|
||||
aRetBuf[1] = (aObjectLength & 0xFF000000) >> 24;
|
||||
aRetBuf[2] = (aObjectLength & 0x00FF0000) >> 16;
|
||||
aRetBuf[3] = (aObjectLength & 0x0000FF00) >> 8;
|
||||
aRetBuf[4] = aObjectLength & 0x000000FF;
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
int
|
||||
AppendHeaderConnectionId(uint8_t* retBuf, int connectionId)
|
||||
AppendHeaderConnectionId(uint8_t* aRetBuf, int aConnectionId)
|
||||
{
|
||||
retBuf[0] = ObexHeaderId::ConnectionId;
|
||||
retBuf[1] = (connectionId & 0xFF000000) >> 24;
|
||||
retBuf[2] = (connectionId & 0x00FF0000) >> 16;
|
||||
retBuf[3] = (connectionId & 0x0000FF00) >> 8;
|
||||
retBuf[4] = connectionId & 0x000000FF;
|
||||
aRetBuf[0] = ObexHeaderId::ConnectionId;
|
||||
aRetBuf[1] = (aConnectionId & 0xFF000000) >> 24;
|
||||
aRetBuf[2] = (aConnectionId & 0x00FF0000) >> 16;
|
||||
aRetBuf[3] = (aConnectionId & 0x0000FF00) >> 8;
|
||||
aRetBuf[4] = aConnectionId & 0x000000FF;
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
void
|
||||
SetObexPacketInfo(uint8_t* retBuf, uint8_t opcode, int packetLength)
|
||||
SetObexPacketInfo(uint8_t* aRetBuf, uint8_t aOpcode, int aPacketLength)
|
||||
{
|
||||
retBuf[0] = opcode;
|
||||
retBuf[1] = (packetLength & 0xFF00) >> 8;
|
||||
retBuf[2] = packetLength & 0x00FF;
|
||||
aRetBuf[0] = aOpcode;
|
||||
aRetBuf[1] = (aPacketLength & 0xFF00) >> 8;
|
||||
aRetBuf[2] = aPacketLength & 0x00FF;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -195,11 +195,11 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
int AppendHeaderName(uint8_t* retBuf, const char* name, int length);
|
||||
int AppendHeaderBody(uint8_t* retBuf, uint8_t* data, int length);
|
||||
int AppendHeaderLength(uint8_t* retBuf, int objectLength);
|
||||
int AppendHeaderConnectionId(uint8_t* retBuf, int connectionId);
|
||||
void SetObexPacketInfo(uint8_t* retBuf, uint8_t opcode, int packetLength);
|
||||
int AppendHeaderName(uint8_t* aRetBuf, const char* aName, int aLength);
|
||||
int AppendHeaderBody(uint8_t* aRetBuf, uint8_t* aData, int aLength);
|
||||
int AppendHeaderLength(uint8_t* aRetBuf, int aObjectLength);
|
||||
int AppendHeaderConnectionId(uint8_t* aRetBuf, int aConnectionId);
|
||||
void SetObexPacketInfo(uint8_t* aRetBuf, uint8_t aOpcode, int aPacketLength);
|
||||
int ParseHeadersAndFindBody(uint8_t* aHeaderStart,
|
||||
int aTotalLength,
|
||||
ObexHeaderSet* aRetHanderSet);
|
||||
|
@ -529,10 +529,12 @@ BluetoothRequestParent::DoRequest(const SendFileRequest& aRequest)
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TSendFileRequest);
|
||||
|
||||
return mService->SendFile(aRequest.devicePath(),
|
||||
(BlobParent*)aRequest.blobParent(),
|
||||
(BlobChild*)aRequest.blobChild(),
|
||||
mReplyRunnable.get());
|
||||
mService->SendFile(aRequest.devicePath(),
|
||||
(BlobParent*)aRequest.blobParent(),
|
||||
(BlobChild*)aRequest.blobChild(),
|
||||
mReplyRunnable.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -541,8 +543,10 @@ BluetoothRequestParent::DoRequest(const StopSendingFileRequest& aRequest)
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TStopSendingFileRequest);
|
||||
|
||||
return mService->StopSendingFile(aRequest.devicePath(),
|
||||
mReplyRunnable.get());
|
||||
mService->StopSendingFile(aRequest.devicePath(),
|
||||
mReplyRunnable.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -323,7 +323,7 @@ BluetoothServiceChildProcess::Disconnect(
|
||||
SendRequest(aRunnable, DisconnectRequest(aProfileId));
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
BluetoothServiceChildProcess::SendFile(
|
||||
const nsAString& aDeviceAddress,
|
||||
BlobParent* aBlobParent,
|
||||
@ -332,17 +332,15 @@ BluetoothServiceChildProcess::SendFile(
|
||||
{
|
||||
SendRequest(aRunnable,
|
||||
SendFileRequest(nsString(aDeviceAddress), nullptr, aBlobChild));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
BluetoothServiceChildProcess::StopSendingFile(
|
||||
const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
SendRequest(aRunnable,
|
||||
StopSendingFileRequest(nsString(aDeviceAddress)));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -139,13 +139,13 @@ public:
|
||||
Disconnect(const uint16_t aProfileId,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
SendFile(const nsAString& aDeviceAddress,
|
||||
BlobParent* aBlobParent,
|
||||
BlobChild* aBlobChild,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
StopSendingFile(const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
|
@ -2368,7 +2368,8 @@ void
|
||||
BluetoothDBusService::Disconnect(const uint16_t aProfileId,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
if (aProfileId == (uint16_t)(BluetoothServiceUuid::Handsfree >> 32)) {
|
||||
if (aProfileId == (uint16_t)(BluetoothServiceUuid::Handsfree >> 32) ||
|
||||
aProfileId == (uint16_t)(BluetoothServiceUuid::Headset >> 32)) {
|
||||
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
||||
hfp->Disconnect();
|
||||
} else if (aProfileId == (uint16_t)(BluetoothServiceUuid::ObjectPush >> 32)) {
|
||||
@ -2557,7 +2558,7 @@ BluetoothDBusService::GetScoSocket(const nsAString& aAddress,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
BluetoothDBusService::SendFile(const nsAString& aDeviceAddress,
|
||||
BlobParent* aBlobParent,
|
||||
BlobChild* aBlobChild,
|
||||
@ -2570,12 +2571,17 @@ BluetoothDBusService::SendFile(const nsAString& aDeviceAddress,
|
||||
// has been determined when calling 'Connect()'. Nevertheless, keep
|
||||
// it for future use.
|
||||
BluetoothOppManager* opp = BluetoothOppManager::Get();
|
||||
opp->SendFile(aBlobParent, aRunnable);
|
||||
BluetoothValue v = true;
|
||||
nsString errorStr;
|
||||
|
||||
return true;
|
||||
if (!opp->SendFile(aBlobParent)) {
|
||||
errorStr.AssignLiteral("Calling SendFile() failed");
|
||||
}
|
||||
|
||||
DispatchBluetoothReply(aRunnable, v, errorStr);
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
BluetoothDBusService::StopSendingFile(const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
@ -2586,9 +2592,14 @@ BluetoothDBusService::StopSendingFile(const nsAString& aDeviceAddress,
|
||||
// has been determined when calling 'Connect()'. Nevertheless, keep
|
||||
// it for future use.
|
||||
BluetoothOppManager* opp = BluetoothOppManager::Get();
|
||||
opp->StopSendingFile(aRunnable);
|
||||
BluetoothValue v = true;
|
||||
nsString errorStr;
|
||||
|
||||
return true;
|
||||
if (!opp->StopSendingFile()) {
|
||||
errorStr.AssignLiteral("Calling StopSendingFile() failed");
|
||||
}
|
||||
|
||||
DispatchBluetoothReply(aRunnable, v, errorStr);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2603,7 +2614,14 @@ BluetoothDBusService::ConfirmReceivingFile(const nsAString& aDeviceAddress,
|
||||
// has been determined when calling 'Connect()'. Nevertheless, keep
|
||||
// it for future use.
|
||||
BluetoothOppManager* opp = BluetoothOppManager::Get();
|
||||
opp->ConfirmReceivingFile(aConfirm, aRunnable);
|
||||
BluetoothValue v = true;
|
||||
nsString errorStr;
|
||||
|
||||
if (!opp->ConfirmReceivingFile(aConfirm)) {
|
||||
errorStr.AssignLiteral("Calling ConfirmReceivingFile() failed");
|
||||
}
|
||||
|
||||
DispatchBluetoothReply(aRunnable, v, errorStr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -142,13 +142,13 @@ public:
|
||||
virtual void
|
||||
Disconnect(const uint16_t aProfileId, BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
SendFile(const nsAString& aDeviceAddress,
|
||||
BlobParent* aBlobParent,
|
||||
BlobChild* aBlobChild,
|
||||
BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
virtual bool
|
||||
virtual void
|
||||
StopSendingFile(const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
|
@ -42,7 +42,6 @@ function checkStylesheets() {
|
||||
|
||||
function runTest() {
|
||||
const Ci = SpecialPowers.Ci;
|
||||
const Cc = SpecialPowers.Components.classes;
|
||||
|
||||
/** Found while fixing bug 440614 **/
|
||||
var editframe = window.frames[0];
|
||||
|
@ -111,6 +111,8 @@ class OrderedHashTable
|
||||
return false;
|
||||
}
|
||||
|
||||
// clear() requires that members are assigned only after all allocation
|
||||
// has succeeded, and that this->ranges is left untouched.
|
||||
hashTable = tableAlloc;
|
||||
data = dataAlloc;
|
||||
dataLength = 0;
|
||||
@ -218,6 +220,42 @@ class OrderedHashTable
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all entries.
|
||||
*
|
||||
* Returns false on OOM, leaving the OrderedHashTable and any live Ranges
|
||||
* in the old state.
|
||||
*
|
||||
* The effect on live Ranges is the same as removing all entries; in
|
||||
* particular, those Ranges are still live and will see any entries added
|
||||
* after a successful clear().
|
||||
*/
|
||||
bool clear() {
|
||||
if (dataLength != 0) {
|
||||
Data **oldHashTable = hashTable;
|
||||
Data *oldData = data;
|
||||
uint32_t oldDataLength = dataLength;
|
||||
|
||||
hashTable = NULL;
|
||||
if (!init()) {
|
||||
// init() only mutates members on success; see comment above.
|
||||
hashTable = oldHashTable;
|
||||
return false;
|
||||
}
|
||||
|
||||
alloc.free_(oldHashTable);
|
||||
freeData(oldData, oldDataLength);
|
||||
for (Range *r = ranges; r; r = r->next)
|
||||
r->onClear();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(hashTable);
|
||||
MOZ_ASSERT(data);
|
||||
MOZ_ASSERT(dataLength == 0);
|
||||
MOZ_ASSERT(liveCount == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ranges are used to iterate over OrderedHashTables.
|
||||
*
|
||||
@ -337,6 +375,12 @@ class OrderedHashTable
|
||||
i = count;
|
||||
}
|
||||
|
||||
/* The hash table calls this when cleared. */
|
||||
void onClear() {
|
||||
MOZ_ASSERT(valid());
|
||||
i = count = 0;
|
||||
}
|
||||
|
||||
bool valid() const {
|
||||
return next != this;
|
||||
}
|
||||
@ -476,9 +520,13 @@ class OrderedHashTable
|
||||
return 1 << (HashNumberSizeBits - hashShift);
|
||||
}
|
||||
|
||||
void freeData(Data *data, uint32_t length) {
|
||||
static void destroyData(Data *data, uint32_t length) {
|
||||
for (Data *p = data + length; p != data; )
|
||||
(--p)->~Data();
|
||||
}
|
||||
|
||||
void freeData(Data *data, uint32_t length) {
|
||||
destroyData(data, length);
|
||||
alloc.free_(data);
|
||||
}
|
||||
|
||||
@ -642,6 +690,7 @@ class OrderedHashMap
|
||||
Entry *get(const Key &key) { return impl.get(key); }
|
||||
bool put(const Key &key, const Value &value) { return impl.put(Entry(key, value)); }
|
||||
bool remove(const Key &key, bool *foundp) { return impl.remove(key, foundp); }
|
||||
bool clear() { return impl.clear(); }
|
||||
};
|
||||
|
||||
template <class T, class OrderedHashPolicy, class AllocPolicy>
|
||||
@ -668,6 +717,7 @@ class OrderedHashSet
|
||||
Range all() { return impl.all(); }
|
||||
bool put(const T &value) { return impl.put(value); }
|
||||
bool remove(const T &value, bool *foundp) { return impl.remove(value, foundp); }
|
||||
bool clear() { return impl.clear(); }
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
@ -897,19 +947,24 @@ Class MapObject::class_ = {
|
||||
mark
|
||||
};
|
||||
|
||||
JSPropertySpec MapObject::properties[] = {
|
||||
JS_PSG("size", size, 0),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
JSFunctionSpec MapObject::methods[] = {
|
||||
JS_FN("size", size, 0, 0),
|
||||
JS_FN("get", get, 1, 0),
|
||||
JS_FN("has", has, 1, 0),
|
||||
JS_FN("set", set, 2, 0),
|
||||
JS_FN("delete", delete_, 1, 0),
|
||||
JS_FN("iterator", iterator, 0, 0),
|
||||
JS_FN("clear", clear, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static JSObject *
|
||||
InitClass(JSContext *cx, Handle<GlobalObject*> global, Class *clasp, JSProtoKey key, Native construct,
|
||||
JSFunctionSpec *methods)
|
||||
JSPropertySpec *properties, JSFunctionSpec *methods)
|
||||
{
|
||||
Rooted<JSObject*> proto(cx, global->createBlankPrototype(cx, clasp));
|
||||
if (!proto)
|
||||
@ -919,7 +974,7 @@ InitClass(JSContext *cx, Handle<GlobalObject*> global, Class *clasp, JSProtoKey
|
||||
Rooted<JSFunction*> ctor(cx, global->createConstructor(cx, construct, ClassName(key, cx), 1));
|
||||
if (!ctor ||
|
||||
!LinkConstructorAndPrototype(cx, ctor, proto) ||
|
||||
!DefinePropertiesAndBrand(cx, proto, NULL, methods) ||
|
||||
!DefinePropertiesAndBrand(cx, proto, properties, methods) ||
|
||||
!DefineConstructorAndPrototype(cx, global, key, ctor, proto))
|
||||
{
|
||||
return NULL;
|
||||
@ -931,7 +986,7 @@ JSObject *
|
||||
MapObject::initClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
Rooted<GlobalObject*> global(cx, &obj->asGlobal());
|
||||
return InitClass(cx, global, &class_, JSProto_Map, construct, methods);
|
||||
return InitClass(cx, global, &class_, JSProto_Map, construct, properties, methods);
|
||||
}
|
||||
|
||||
template <class Range>
|
||||
@ -1147,8 +1202,10 @@ MapObject::delete_impl(JSContext *cx, CallArgs args)
|
||||
ValueMap &map = extract(args);
|
||||
ARG0_KEY(cx, args, key);
|
||||
bool found;
|
||||
if (!map.remove(key, &found))
|
||||
if (!map.remove(key, &found)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
args.rval().setBoolean(found);
|
||||
return true;
|
||||
}
|
||||
@ -1179,6 +1236,25 @@ MapObject::iterator(JSContext *cx, unsigned argc, Value *vp)
|
||||
return CallNonGenericMethod(cx, is, iterator_impl, args);
|
||||
}
|
||||
|
||||
bool
|
||||
MapObject::clear_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
Rooted<MapObject*> mapobj(cx, &args.thisv().toObject().asMap());
|
||||
if (!mapobj->getData()->clear()) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
MapObject::clear(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod(cx, is, clear_impl, args);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_InitMapClass(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
@ -1338,12 +1414,17 @@ Class SetObject::class_ = {
|
||||
mark
|
||||
};
|
||||
|
||||
JSPropertySpec SetObject::properties[] = {
|
||||
JS_PSG("size", size, 0),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
JSFunctionSpec SetObject::methods[] = {
|
||||
JS_FN("size", size, 0, 0),
|
||||
JS_FN("has", has, 1, 0),
|
||||
JS_FN("add", add, 1, 0),
|
||||
JS_FN("delete", delete_, 1, 0),
|
||||
JS_FN("iterator", iterator, 0, 0),
|
||||
JS_FN("clear", clear, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
@ -1351,7 +1432,7 @@ JSObject *
|
||||
SetObject::initClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
Rooted<GlobalObject*> global(cx, &obj->asGlobal());
|
||||
return InitClass(cx, global, &class_, JSProto_Set, construct, methods);
|
||||
return InitClass(cx, global, &class_, JSProto_Set, construct, properties, methods);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1488,8 +1569,10 @@ SetObject::delete_impl(JSContext *cx, CallArgs args)
|
||||
ValueSet &set = extract(args);
|
||||
ARG0_KEY(cx, args, key);
|
||||
bool found;
|
||||
if (!set.remove(key, &found))
|
||||
if (!set.remove(key, &found)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
args.rval().setBoolean(found);
|
||||
return true;
|
||||
}
|
||||
@ -1520,6 +1603,25 @@ SetObject::iterator(JSContext *cx, unsigned argc, Value *vp)
|
||||
return CallNonGenericMethod(cx, is, iterator_impl, args);
|
||||
}
|
||||
|
||||
bool
|
||||
SetObject::clear_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
Rooted<SetObject*> setobj(cx, &args.thisv().toObject().asSet());
|
||||
if (!setobj->getData()->clear()) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
JSBool
|
||||
SetObject::clear(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod(cx, is, clear_impl, args);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_InitSetClass(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
|
@ -84,6 +84,7 @@ class MapObject : public JSObject {
|
||||
static JSObject *initClass(JSContext *cx, JSObject *obj);
|
||||
static Class class_;
|
||||
private:
|
||||
static JSPropertySpec properties[];
|
||||
static JSFunctionSpec methods[];
|
||||
ValueMap *getData() { return static_cast<ValueMap *>(getPrivate()); }
|
||||
static ValueMap & extract(CallReceiver call);
|
||||
@ -105,6 +106,8 @@ class MapObject : public JSObject {
|
||||
static JSBool delete_(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool iterator_impl(JSContext *cx, CallArgs args);
|
||||
static JSBool iterator(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool clear_impl(JSContext *cx, CallArgs args);
|
||||
static JSBool clear(JSContext *cx, unsigned argc, Value *vp);
|
||||
};
|
||||
|
||||
class SetObject : public JSObject {
|
||||
@ -112,6 +115,7 @@ class SetObject : public JSObject {
|
||||
static JSObject *initClass(JSContext *cx, JSObject *obj);
|
||||
static Class class_;
|
||||
private:
|
||||
static JSPropertySpec properties[];
|
||||
static JSFunctionSpec methods[];
|
||||
ValueSet *getData() { return static_cast<ValueSet *>(getPrivate()); }
|
||||
static ValueSet & extract(CallReceiver call);
|
||||
@ -131,6 +135,8 @@ class SetObject : public JSObject {
|
||||
static JSBool delete_(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool iterator_impl(JSContext *cx, CallArgs args);
|
||||
static JSBool iterator(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool clear_impl(JSContext *cx, CallArgs args);
|
||||
static JSBool clear(JSContext *cx, unsigned argc, Value *vp);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
@ -631,6 +631,12 @@ class Rooted : public RootedBase<T>
|
||||
Rooted(const Rooted &) MOZ_DELETE;
|
||||
};
|
||||
|
||||
#if !(defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING))
|
||||
// Defined in vm/String.h.
|
||||
template <>
|
||||
class Rooted<JSStableString *>;
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
bool
|
||||
Return<T>::operator==(const Rooted<T> &other)
|
||||
|
8
js/src/jit-test/tests/collections/Map-clear-1.js
Normal file
8
js/src/jit-test/tests/collections/Map-clear-1.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Clearing an empty Map has no effect.
|
||||
|
||||
var m = Map();
|
||||
for (var i = 0; i < 2; i++) {
|
||||
m.clear();
|
||||
assertEq(m.size, 0);
|
||||
assertEq(m.has(undefined), false);
|
||||
}
|
17
js/src/jit-test/tests/collections/Map-clear-2.js
Normal file
17
js/src/jit-test/tests/collections/Map-clear-2.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Clearing a Map removes its entries; the Map remains usable afterwards.
|
||||
|
||||
var m = Map([["a", "b"], ["b", "c"]]);
|
||||
assertEq(m.size, 2);
|
||||
m.clear();
|
||||
assertEq(m.size, 0);
|
||||
assertEq(m.has("a"), false);
|
||||
assertEq(m.get("a"), undefined);
|
||||
assertEq(m.delete("a"), false);
|
||||
assertEq(m.has("b"), false);
|
||||
for (var pair of m)
|
||||
throw "FAIL"; // shouldn't be any pairs
|
||||
|
||||
m.set("c", "d");
|
||||
assertEq(m.size, 1);
|
||||
assertEq(m.has("a"), false);
|
||||
assertEq(m.has("b"), false);
|
10
js/src/jit-test/tests/collections/Map-clear-3.js
Normal file
10
js/src/jit-test/tests/collections/Map-clear-3.js
Normal file
@ -0,0 +1,10 @@
|
||||
// Clearing a Map with a nontrivial number of elements works.
|
||||
|
||||
var m = Map();
|
||||
for (var i = 0; i < 100; i++)
|
||||
m.set(i, i);
|
||||
assertEq(m.size, i);
|
||||
m.clear();
|
||||
assertEq(m.size, 0);
|
||||
m.set("a", 1);
|
||||
assertEq(m.get("a"), 1);
|
10
js/src/jit-test/tests/collections/Map-clear-4.js
Normal file
10
js/src/jit-test/tests/collections/Map-clear-4.js
Normal file
@ -0,0 +1,10 @@
|
||||
// Clearing a Map after deleting some entries works.
|
||||
|
||||
var m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
|
||||
for (var [k, v] of m)
|
||||
if (k !== "c")
|
||||
m.delete(k);
|
||||
m.clear();
|
||||
assertEq(m.size, 0);
|
||||
assertEq(m.has("c"), false);
|
||||
assertEq(m.has("d"), false);
|
14
js/src/jit-test/tests/collections/Map-clear-5.js
Normal file
14
js/src/jit-test/tests/collections/Map-clear-5.js
Normal file
@ -0,0 +1,14 @@
|
||||
// Map.clear is unaffected by deleting/monkeypatching Map.prototype.{delete,iterator}.
|
||||
|
||||
var data = [["a", 1], ["b", 2]];
|
||||
var m1 = Map(data), m2 = Map(data);
|
||||
|
||||
delete Map.prototype.delete;
|
||||
delete Map.prototype.iterator;
|
||||
m1.clear();
|
||||
assertEq(m1.size, 0);
|
||||
|
||||
Map.prototype.delete = function () { throw "FAIL"; };
|
||||
Map.prototype.iterator = function () { throw "FAIL"; };
|
||||
m2.clear();
|
||||
assertEq(m2.size, 0);
|
6
js/src/jit-test/tests/collections/Map-clear-6.js
Normal file
6
js/src/jit-test/tests/collections/Map-clear-6.js
Normal file
@ -0,0 +1,6 @@
|
||||
// Clearing a Map doesn't affect expando properties.
|
||||
|
||||
var m = Map();
|
||||
m.x = 3;
|
||||
m.clear();
|
||||
assertEq(m.x, 3);
|
14
js/src/jit-test/tests/collections/Map-clear-gc.js
Normal file
14
js/src/jit-test/tests/collections/Map-clear-gc.js
Normal file
@ -0,0 +1,14 @@
|
||||
// Clearing a Map removes any strong references to its keys and values.
|
||||
|
||||
load(libdir + "referencesVia.js");
|
||||
|
||||
var m = Map();
|
||||
var k = {}, v = {};
|
||||
m.set(k, v);
|
||||
assertEq(referencesVia(m, "key", k), true);
|
||||
assertEq(referencesVia(m, "value", v), true);
|
||||
m.clear();
|
||||
if (typeof findReferences == 'function') {
|
||||
assertEq(referencesVia(m, "key", k), false);
|
||||
assertEq(referencesVia(m, "value", v), false);
|
||||
}
|
23
js/src/jit-test/tests/collections/Map-clear-iterators-1.js
Normal file
23
js/src/jit-test/tests/collections/Map-clear-iterators-1.js
Normal file
@ -0,0 +1,23 @@
|
||||
// A Map iterator does not visit entries removed by clear().
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var m = Map();
|
||||
var it = m.iterator();
|
||||
m.clear();
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
||||
|
||||
m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
|
||||
it = m.iterator();
|
||||
assertEq(it.next()[0], "a");
|
||||
m.clear();
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
||||
|
||||
var log = "";
|
||||
m = Map([["a", 1], ["b", 2], ["c", 3], ["d", 4]]);
|
||||
for (var [k, v] of m) {
|
||||
log += k + v;
|
||||
if (k == "b")
|
||||
m.clear();
|
||||
}
|
||||
assertEq(log, "a1b2");
|
15
js/src/jit-test/tests/collections/Map-clear-iterators-2.js
Normal file
15
js/src/jit-test/tests/collections/Map-clear-iterators-2.js
Normal file
@ -0,0 +1,15 @@
|
||||
// A Map iterator continues to visit entries added after a clear().
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var m = Map([["a", 1]]);
|
||||
var it = m.iterator();
|
||||
assertEq(it.next()[0], "a");
|
||||
m.clear();
|
||||
m.set("b", 2);
|
||||
var pair = it.next()
|
||||
assertEq(pair[0], "b");
|
||||
assertEq(pair[1], 2);
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// The Map constructor creates an empty Map by default.
|
||||
|
||||
assertEq(Map().size(), 0);
|
||||
assertEq((new Map).size(), 0);
|
||||
assertEq(Map(undefined).size(), 0);
|
||||
assertEq(new Map(undefined).size(), 0);
|
||||
assertEq(Map().size, 0);
|
||||
assertEq((new Map).size, 0);
|
||||
assertEq(Map(undefined).size, 0);
|
||||
assertEq(new Map(undefined).size, 0);
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
var arr = [["a"], ["b"], ["c"]];
|
||||
var m = Map(arr);
|
||||
assertEq(m.size(), 3);
|
||||
assertEq(m.size, 3);
|
||||
for (var [k, _] of arr) {
|
||||
assertEq(m.has(k), true);
|
||||
assertEq(m.get(k), undefined);
|
||||
|
@ -5,4 +5,4 @@ var m = Map(arg);
|
||||
assertEq(m.get("zero"), 0);
|
||||
assertEq(m.get("one"), 1);
|
||||
assertEq(m.get("two"), 2);
|
||||
assertEq(m.size(), 3);
|
||||
assertEq(m.size, 3);
|
||||
|
@ -12,7 +12,7 @@ function data(n) {
|
||||
|
||||
var m = Map(data(50));
|
||||
assertEq(done, true); // the constructor consumes the argument
|
||||
assertEq(m.size(), 50);
|
||||
assertEq(m.size, 50);
|
||||
assertEq(m.get(""), 0);
|
||||
assertEq(m.get("....."), 5);
|
||||
assertEq(m.get(Array(49+1).join(".")), 49);
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
var arr = [1, 2, "green", "red"];
|
||||
var m = Map([v, v] for (v of arr));
|
||||
assertEq(m.size(), 4);
|
||||
assertEq(m.size, 4);
|
||||
|
||||
for (var i = 0; i < 4; i++)
|
||||
assertEq(m.get(arr[i]), arr[i]);
|
||||
|
@ -1,8 +1,8 @@
|
||||
// The argument to Map may be a generator-iterator that produces no values.
|
||||
|
||||
assertEq(Map(x for (x of [])).size(), 0);
|
||||
assertEq(Map(x for (x of [])).size, 0);
|
||||
|
||||
function none() {
|
||||
if (0) yield 0;
|
||||
}
|
||||
assertEq(Map(none()).size(), 0);
|
||||
assertEq(Map(none()).size, 0);
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
var m = Map();
|
||||
m.delete(3);
|
||||
assertEq(m.size(), 0);
|
||||
assertEq(m.size, 0);
|
||||
m.set({}, 'ok');
|
||||
m.set(Math, 'ok');
|
||||
assertEq(m.size(), 2);
|
||||
assertEq(m.size, 2);
|
||||
m.delete({});
|
||||
assertEq(m.size(), 2);
|
||||
assertEq(m.size, 2);
|
||||
m.delete(Math);
|
||||
assertEq(m.size(), 1);
|
||||
assertEq(m.size, 1);
|
||||
m.delete(Math);
|
||||
assertEq(m.size(), 1);
|
||||
assertEq(m.size, 1);
|
||||
|
@ -12,4 +12,4 @@ for (let [k, v] of map) {
|
||||
force(v);
|
||||
}
|
||||
assertEq(log, '5;4;3;2;1;0;');
|
||||
assertEq(map.size(), 6);
|
||||
assertEq(map.size, 6);
|
||||
|
@ -5,7 +5,7 @@ load(libdir + "eqArrayHelper.js");
|
||||
var map = Map();
|
||||
for (var i = 7; i !== 1; i = i * 7 % 1117)
|
||||
map.set("" + i, i);
|
||||
assertEq(map.size(), 557);
|
||||
assertEq(map.size, 557);
|
||||
|
||||
i = 7;
|
||||
for (var pair of map) {
|
||||
|
@ -9,4 +9,4 @@ assertEq(pair[0], 'b');
|
||||
assertEq(pair[1], 2);
|
||||
assertEq(map.get('a'), 1);
|
||||
assertEq(map.has('b'), false);
|
||||
assertEq(map.size(), 1);
|
||||
assertEq(map.size, 1);
|
||||
|
@ -18,5 +18,5 @@ for (var [k, v] of map) {
|
||||
}
|
||||
}
|
||||
assertEq(log, 'ABCDEFGHIJKLMNOPQRST');
|
||||
assertEq(map.size(), 1); // Only the last entry remains.
|
||||
assertEq(map.size, 1); // Only the last entry remains.
|
||||
assertEq(map.get('T'), 19);
|
||||
|
@ -11,7 +11,7 @@ var iter = map.iterator();
|
||||
assertEq(iter.next()[0], 0);
|
||||
for (var i = 0; i < 30; i++)
|
||||
map.delete(i);
|
||||
assertEq(map.size(), 2);
|
||||
assertEq(map.size, 2);
|
||||
for (var i = 32; i < 100; i++)
|
||||
map.set(i, i); // eventually triggers compaction
|
||||
|
||||
|
10
js/src/jit-test/tests/collections/Map-iterators-3.js
Normal file
10
js/src/jit-test/tests/collections/Map-iterators-3.js
Normal file
@ -0,0 +1,10 @@
|
||||
// A closed Map iterator does not visit new entries added after a clear().
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var m = Map();
|
||||
var it = m.iterator();
|
||||
assertThrowsValue(it.next.bind(it), StopIteration); // close the iterator
|
||||
m.clear();
|
||||
m.set("a", 1);
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
@ -2,13 +2,13 @@
|
||||
|
||||
var m = Map();
|
||||
m.set('a', 0);
|
||||
assertEq(m.size(), 1);
|
||||
assertEq(m.size, 1);
|
||||
m.set('a', 0);
|
||||
assertEq(m.size(), 1);
|
||||
assertEq(m.size, 1);
|
||||
m.set('a', undefined);
|
||||
assertEq(m.size(), 1);
|
||||
assertEq(m.size, 1);
|
||||
|
||||
m.set('b', 2);
|
||||
assertEq(m.size(), 2);
|
||||
assertEq(m.size, 2);
|
||||
m.set('a', 1);
|
||||
assertEq(m.size(), 2);
|
||||
assertEq(m.size, 2);
|
||||
|
@ -2,5 +2,5 @@
|
||||
|
||||
var m1 = Map(), m2 = Map();
|
||||
m1.set("x", 3);
|
||||
assertEq(m1.size(), 1);
|
||||
assertEq(m2.size(), 0);
|
||||
assertEq(m1.size, 1);
|
||||
assertEq(m2.size, 0);
|
||||
|
@ -27,8 +27,15 @@ function checkMethod(name, arity) {
|
||||
assertEq(desc.value.length, arity);
|
||||
}
|
||||
|
||||
checkMethod("size", 0);
|
||||
checkMethod("get", 1);
|
||||
checkMethod("has", 1);
|
||||
checkMethod("set", 2);
|
||||
checkMethod("delete", 1);
|
||||
|
||||
var desc = Object.getOwnPropertyDescriptor(Map.prototype, "size");
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.configurable, true);
|
||||
assertEq(typeof desc.get, 'function');
|
||||
assertEq(desc.get.length, 0);
|
||||
assertEq(desc.set, undefined);
|
||||
checkMethod("clear", 0);
|
||||
|
@ -8,12 +8,15 @@ function testcase(obj, fn) {
|
||||
assertThrowsInstanceOf(function () { fn.apply(obj, args); }, TypeError);
|
||||
}
|
||||
|
||||
var Map_size_getter = Object.getOwnPropertyDescriptor(Map.prototype, "size").get;
|
||||
|
||||
function test(obj) {
|
||||
testcase(obj, Map.prototype.size);
|
||||
testcase(obj, Map.prototype.get, "x");
|
||||
testcase(obj, Map.prototype.has, "x");
|
||||
testcase(obj, Map.prototype.set, "x", 1);
|
||||
testcase(obj, Map.prototype.delete, "x");
|
||||
testcase(obj, Map.prototype.clear);
|
||||
testcase(obj, Map_size_getter);
|
||||
}
|
||||
|
||||
test(Map.prototype);
|
||||
|
@ -1,11 +1,11 @@
|
||||
// set.add(v) increments set.size() iff the set did not already contain v.
|
||||
// set.add(v) increments set.size iff the set did not already contain v.
|
||||
|
||||
var s = Set();
|
||||
for (var i = 0; i < 10; i++) {
|
||||
assertEq(s.size(), i);
|
||||
assertEq(s.size, i);
|
||||
s.add(i);
|
||||
}
|
||||
for (var i = 0; i < 10; i++) {
|
||||
assertEq(s.size(), 10);
|
||||
assertEq(s.size, 10);
|
||||
s.add(i);
|
||||
}
|
||||
|
8
js/src/jit-test/tests/collections/Set-clear-1.js
Normal file
8
js/src/jit-test/tests/collections/Set-clear-1.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Clearing an empty Set has no effect.
|
||||
|
||||
var s = Set();
|
||||
for (var i = 0; i < 2; i++) {
|
||||
s.clear();
|
||||
assertEq(s.size, 0);
|
||||
assertEq(s.has(undefined), false);
|
||||
}
|
16
js/src/jit-test/tests/collections/Set-clear-2.js
Normal file
16
js/src/jit-test/tests/collections/Set-clear-2.js
Normal file
@ -0,0 +1,16 @@
|
||||
// Clearing a Set removes its elements; the Set remains usable afterwards.
|
||||
|
||||
var s = Set(["x", "y", "z", "z", "y"]);
|
||||
assertEq(s.size, 3);
|
||||
s.clear();
|
||||
assertEq(s.size, 0);
|
||||
assertEq(s.has("x"), false);
|
||||
assertEq(s.delete("x"), false);
|
||||
assertEq(s.has("z"), false);
|
||||
for (var v of s)
|
||||
throw "FAIL"; // shouldn't be any elements
|
||||
|
||||
s.add("y");
|
||||
assertEq(s.size, 1);
|
||||
assertEq(s.has("x"), false);
|
||||
assertEq(s.has("z"), false);
|
10
js/src/jit-test/tests/collections/Set-clear-3.js
Normal file
10
js/src/jit-test/tests/collections/Set-clear-3.js
Normal file
@ -0,0 +1,10 @@
|
||||
// Clearing a Set with a nontrivial number of elements works.
|
||||
|
||||
var s = Set();
|
||||
for (var i = 0; i < 100; i++)
|
||||
s.add(i);
|
||||
assertEq(s.size, i);
|
||||
s.clear();
|
||||
assertEq(s.size, 0);
|
||||
s.add(12);
|
||||
assertEq(s.has(12), true);
|
10
js/src/jit-test/tests/collections/Set-clear-4.js
Normal file
10
js/src/jit-test/tests/collections/Set-clear-4.js
Normal file
@ -0,0 +1,10 @@
|
||||
// Clearing a Set after deleting some entries works.
|
||||
|
||||
var s = Set(["a", "b", "c", "d"]);
|
||||
for (var v of s)
|
||||
if (v !== "c")
|
||||
s.delete(v);
|
||||
s.clear();
|
||||
assertEq(s.size, 0);
|
||||
assertEq(s.has("c"), false);
|
||||
assertEq(s.has("d"), false);
|
14
js/src/jit-test/tests/collections/Set-clear-5.js
Normal file
14
js/src/jit-test/tests/collections/Set-clear-5.js
Normal file
@ -0,0 +1,14 @@
|
||||
// Set.clear is unaffected by deleting/monkeypatching Set.prototype.{delete,iterator}.
|
||||
|
||||
var data = ["a", 1, {}];
|
||||
var s1 = Set(data), s2 = Set(data);
|
||||
|
||||
delete Set.prototype.delete;
|
||||
delete Set.prototype.iterator;
|
||||
s1.clear();
|
||||
assertEq(s1.size, 0);
|
||||
|
||||
Set.prototype.delete = function () { throw "FAIL"; };
|
||||
Set.prototype.iterator = function () { throw "FAIL"; };
|
||||
s2.clear();
|
||||
assertEq(s2.size, 0);
|
6
js/src/jit-test/tests/collections/Set-clear-6.js
Normal file
6
js/src/jit-test/tests/collections/Set-clear-6.js
Normal file
@ -0,0 +1,6 @@
|
||||
// Clearing a Set doesn't affect expando properties.
|
||||
|
||||
var s = Set();
|
||||
s.x = 3;
|
||||
s.clear();
|
||||
assertEq(s.x, 3);
|
11
js/src/jit-test/tests/collections/Set-clear-gc.js
Normal file
11
js/src/jit-test/tests/collections/Set-clear-gc.js
Normal file
@ -0,0 +1,11 @@
|
||||
// Clearing a Set removes any strong references to its elements.
|
||||
|
||||
load(libdir + "referencesVia.js");
|
||||
|
||||
var s = Set();
|
||||
var obj = {};
|
||||
s.add(obj);
|
||||
assertEq(referencesVia(s, "key", obj), true);
|
||||
s.clear();
|
||||
if (typeof findReferences == 'function')
|
||||
assertEq(referencesVia(s, "key", obj), false);
|
23
js/src/jit-test/tests/collections/Set-clear-iterators-1.js
Normal file
23
js/src/jit-test/tests/collections/Set-clear-iterators-1.js
Normal file
@ -0,0 +1,23 @@
|
||||
// A Set iterator does not visit entries removed by clear().
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var s = Set();
|
||||
var it = s.iterator();
|
||||
s.clear();
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
||||
|
||||
s = Set(["a", "b", "c", "d"]);
|
||||
it = s.iterator();
|
||||
assertEq(it.next()[0], "a");
|
||||
s.clear();
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
||||
|
||||
var log = "";
|
||||
s = Set(["a", "b", "c", "d"]);
|
||||
for (var v of s) {
|
||||
log += v;
|
||||
if (v == "b")
|
||||
s.clear();
|
||||
}
|
||||
assertEq(log, "ab");
|
11
js/src/jit-test/tests/collections/Set-clear-iterators-2.js
Normal file
11
js/src/jit-test/tests/collections/Set-clear-iterators-2.js
Normal file
@ -0,0 +1,11 @@
|
||||
// A Set iterator continues to visit entries added after a clear().
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var s = Set(["a"]);
|
||||
var it = s.iterator();
|
||||
assertEq(it.next(), "a");
|
||||
s.clear();
|
||||
s.add("b");
|
||||
assertEq(it.next(), "b");
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
10
js/src/jit-test/tests/collections/Set-clear-iterators-3.js
Normal file
10
js/src/jit-test/tests/collections/Set-clear-iterators-3.js
Normal file
@ -0,0 +1,10 @@
|
||||
// A closed Set iterator does not visit new entries added after a clear().
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var s = Set();
|
||||
var it = s.iterator();
|
||||
assertThrowsValue(it.next.bind(it), StopIteration); // close the iterator
|
||||
s.clear();
|
||||
s.add("a");
|
||||
assertThrowsValue(it.next.bind(it), StopIteration);
|
@ -1,6 +1,6 @@
|
||||
// The Set constructor creates an empty Set by default.
|
||||
|
||||
assertEq(Set().size(), 0);
|
||||
assertEq((new Set).size(), 0);
|
||||
assertEq(Set(undefined).size(), 0);
|
||||
assertEq(new Set(undefined).size(), 0);
|
||||
assertEq(Set().size, 0);
|
||||
assertEq((new Set).size, 0);
|
||||
assertEq(Set(undefined).size, 0);
|
||||
assertEq(new Set(undefined).size, 0);
|
||||
|
@ -1,17 +1,17 @@
|
||||
// The Set constructor can take an argument that is an array.
|
||||
|
||||
var s = Set([]);
|
||||
assertEq(s.size(), 0);
|
||||
assertEq(s.size, 0);
|
||||
assertEq(s.has(undefined), false);
|
||||
|
||||
s = Set(["one", "two", "three"]);
|
||||
assertEq(s.size(), 3);
|
||||
assertEq(s.size, 3);
|
||||
assertEq(s.has("one"), true);
|
||||
assertEq(s.has("eleventeen"), false);
|
||||
|
||||
var a = [{}, {}, {}];
|
||||
s = Set(a);
|
||||
assertEq(s.size(), 3);
|
||||
assertEq(s.size, 3);
|
||||
for (let obj of a)
|
||||
assertEq(s.has(obj), true);
|
||||
assertEq(s.has({}), false);
|
||||
|
@ -1,11 +1,11 @@
|
||||
// The argument to Set may contain a value multiple times. Duplicates are discarded.
|
||||
|
||||
assertEq(Set(["testing", "testing", 123]).size(), 2);
|
||||
assertEq(Set(["testing", "testing", 123]).size, 2);
|
||||
|
||||
var values = [undefined, null, false, NaN, 0, -0, 6.022e23, -Infinity, "", "xyzzy", {}, Math.sin];
|
||||
for (let v of values) {
|
||||
var a = [v, {}, {}, {}, v, {}, v, v];
|
||||
var s = Set(a);
|
||||
assertEq(s.size(), 5);
|
||||
assertEq(s.size, 5);
|
||||
assertEq(s.has(v), true);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ function hexData(n) {
|
||||
}
|
||||
|
||||
var s = Set(hexData(256));
|
||||
assertEq(s.size(), 256);
|
||||
assertEq(s.size, 256);
|
||||
assertEq(s.has("0"), true);
|
||||
assertEq(s.has(0), false);
|
||||
assertEq(s.has("ff"), true);
|
||||
|
@ -1,7 +1,7 @@
|
||||
// The argument to Set can be a generator-expression.
|
||||
|
||||
var s = Set(k * k for (k of [1, 2, 3, 4]));
|
||||
assertEq(s.size(), 4);
|
||||
assertEq(s.size, 4);
|
||||
assertEq(s.has(1), true);
|
||||
assertEq(s.has(4), true);
|
||||
assertEq(s.has(9), true);
|
||||
|
@ -1,15 +1,15 @@
|
||||
// set.delete(v) decrements set.size() iff the set contained v.
|
||||
// set.delete(v) decrements set.size iff the set contained v.
|
||||
|
||||
var s = Set();
|
||||
for (var i = 0; i < 10; i++)
|
||||
s.add(i);
|
||||
|
||||
for (var i = 10; i > 0; i--) {
|
||||
assertEq(s.size(), i);
|
||||
assertEq(s.size, i);
|
||||
assertEq(s.delete(i), false);
|
||||
assertEq(s.size(), i);
|
||||
assertEq(s.size, i);
|
||||
assertEq(s.delete(i - 1), true);
|
||||
assertEq(s.size(), i - 1);
|
||||
assertEq(s.size, i - 1);
|
||||
assertEq(s.delete(i - 1), false);
|
||||
assertEq(s.size(), i - 1);
|
||||
assertEq(s.size, i - 1);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
var arr = [{}, {}, {}, [], /xyz/, new Date];
|
||||
var set = Set(arr);
|
||||
assertEq(set.size(), arr.length);
|
||||
assertEq(set.size, arr.length);
|
||||
|
||||
var i = 0;
|
||||
for (var x of set)
|
||||
|
@ -8,4 +8,4 @@ for (let x of set) {
|
||||
set.add(x - 1);
|
||||
}
|
||||
assertEq(log, '5;4;3;2;1;0;');
|
||||
assertEq(set.size(), 6);
|
||||
assertEq(set.size, 6);
|
||||
|
@ -4,7 +4,7 @@ var set = Set();
|
||||
var i;
|
||||
for (i = 7; i !== 1; i = i * 7 % 1117)
|
||||
set.add(i);
|
||||
assertEq(set.size(), 557);
|
||||
assertEq(set.size, 557);
|
||||
|
||||
i = 7;
|
||||
for (var v of set) {
|
||||
|
@ -18,5 +18,5 @@ for (var x of set) {
|
||||
}
|
||||
}
|
||||
assertEq(log, str);
|
||||
assertEq(set.size(), 1); // Elements 0 to 24 are removed, leaving only 25 (Z).
|
||||
assertEq(set.size, 1); // Elements 0 to 24 are removed, leaving only 25 (Z).
|
||||
assertEq(set.has('Z'), true);
|
||||
|
@ -11,7 +11,7 @@ var iter = set.iterator();
|
||||
assertEq(iter.next(), 0);
|
||||
for (var i = 0; i < 30; i++)
|
||||
set.delete(i);
|
||||
assertEq(set.size(), 2);
|
||||
assertEq(set.size, 2);
|
||||
for (var i = 32; i < 100; i++)
|
||||
set.add(i); // eventually triggers compaction
|
||||
|
||||
|
@ -3,5 +3,5 @@
|
||||
var s1 = Set(), s2 = Set();
|
||||
for (var i = 0; i < 30; i++)
|
||||
s1.add(i);
|
||||
assertEq(s1.size(), 30);
|
||||
assertEq(s2.size(), 0);
|
||||
assertEq(s1.size, 30);
|
||||
assertEq(s2.size, 0);
|
||||
|
@ -30,3 +30,11 @@ function checkMethod(name, arity) {
|
||||
checkMethod("has", 1);
|
||||
checkMethod("add", 1);
|
||||
checkMethod("delete", 1);
|
||||
|
||||
var desc = Object.getOwnPropertyDescriptor(Set.prototype, "size");
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.configurable, true);
|
||||
assertEq(typeof desc.get, 'function');
|
||||
assertEq(desc.get.length, 0);
|
||||
assertEq(desc.set, undefined);
|
||||
checkMethod("clear", 0);
|
||||
|
@ -8,10 +8,14 @@ function testcase(obj, fn) {
|
||||
assertThrowsInstanceOf(function () { fn.apply(obj, args); }, TypeError);
|
||||
}
|
||||
|
||||
var Set_size_getter = Object.getOwnPropertyDescriptor(Set.prototype, "size").get;
|
||||
|
||||
function test(obj) {
|
||||
testcase(obj, Set.prototype.has, 12);
|
||||
testcase(obj, Set.prototype.add, 12);
|
||||
testcase(obj, Set.prototype.delete, 12);
|
||||
testcase(obj, Set.prototype.clear);
|
||||
testcase(obj, Set_size_getter);
|
||||
}
|
||||
|
||||
test(Set.prototype);
|
||||
|
@ -1,5 +1,7 @@
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
var g = newGlobal('new-compartment');
|
||||
assertThrowsInstanceOf(function () { Map.prototype.size.apply(g, []); }, g.TypeError);
|
||||
assertThrowsInstanceOf(function () { Set.prototype.size.apply(g, []); }, g.TypeError);
|
||||
for (var cls of [Map, Set]) {
|
||||
var getter = Object.getOwnPropertyDescriptor(cls.prototype, "size").get;
|
||||
assertThrowsInstanceOf(function () { getter.apply(g, []); }, g.TypeError);
|
||||
}
|
||||
|
5
js/src/jit-test/tests/gc/bug-787703.js
Normal file
5
js/src/jit-test/tests/gc/bug-787703.js
Normal file
@ -0,0 +1,5 @@
|
||||
eval(" function x() {}" + Array(241).join(" "));
|
||||
for (var i = 0; i < 100; i++) {
|
||||
gczeal(4, 2);
|
||||
String(x);
|
||||
}
|
@ -1197,6 +1197,10 @@ class AutoStringRooter : private AutoGCRooter {
|
||||
return &str;
|
||||
}
|
||||
|
||||
JSString * const * addr() const {
|
||||
return &str;
|
||||
}
|
||||
|
||||
friend void AutoGCRooter::trace(JSTracer *trc);
|
||||
|
||||
private:
|
||||
|
@ -201,6 +201,7 @@ typedef struct JSTracer JSTracer;
|
||||
|
||||
#ifdef __cplusplus
|
||||
class JSFlatString;
|
||||
class JSStableString; // long story
|
||||
class JSString;
|
||||
#else
|
||||
typedef struct JSFlatString JSFlatString;
|
||||
|
@ -43,12 +43,6 @@ class JS_FRIEND_API(Wrapper) : public DirectProxyHandler
|
||||
LAST_USED_FLAG = CROSS_COMPARTMENT
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
PermitObjectAccess,
|
||||
PermitPropertyAccess,
|
||||
DenyAccess
|
||||
} Permission;
|
||||
|
||||
static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto,
|
||||
JSObject *parent, Wrapper *handler);
|
||||
|
||||
|
@ -557,6 +557,64 @@ class JSStableString : public JSFlatString
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(JSStableString) == sizeof(JSString));
|
||||
|
||||
#if !(defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING))
|
||||
namespace js {
|
||||
/*
|
||||
* Specialization of Rooted<T> to explicitly root the string rather than
|
||||
* relying on conservative stack scanning.
|
||||
*
|
||||
* In exact-gc builds, Rooted<T> already keeps the T reachable, so this hack is
|
||||
* ifdef'd out. In non-exact-gc builds, conservative scanning would ordinarily
|
||||
* pick up the slack. However in the case where the Rooted pointer is no longer
|
||||
* used, but some subobject or malloc'd memory with the same lifetime may be
|
||||
* used, conservative scanning can fail. JSStableString's chars() method makes
|
||||
* it particularly attractive to use that way, so we explicitly keep the
|
||||
* JSString gc-reachable for the full lifetime of the Rooted<JSStableString *>.
|
||||
*
|
||||
* It would suffice simply to force the pointer to remain on the stack, a la
|
||||
* JS::Anchor<T>, but for some reason using that voodoo here seems to cause
|
||||
* some compilers (clang, VC++ with PGO) to generate incorrect code.
|
||||
*/
|
||||
template <>
|
||||
class Rooted<JSStableString *>
|
||||
{
|
||||
public:
|
||||
Rooted(JSContext *cx, JSStableString *initial = NULL
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: rooter(cx, initial)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
operator JSStableString *() const { return get(); }
|
||||
JSStableString * operator ->() const { return get(); }
|
||||
JSStableString ** address() { return reinterpret_cast<JSStableString **>(rooter.addr()); }
|
||||
JSStableString * const * address() const {
|
||||
return reinterpret_cast<JSStableString * const *>(rooter.addr());
|
||||
}
|
||||
JSStableString * get() const { return static_cast<JSStableString *>(rooter.string()); }
|
||||
|
||||
Rooted & operator =(JSStableString *value)
|
||||
{
|
||||
JS_ASSERT(!RootMethods<JSStableString *>::poisoned(value));
|
||||
rooter.setString(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rooted & operator =(const Rooted &value)
|
||||
{
|
||||
rooter.setString(value.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
JS::AutoStringRooter rooter;
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
Rooted(const Rooted &) MOZ_DELETE;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
class JSExtensibleString : public JSFlatString
|
||||
{
|
||||
/* Vacuous and therefore unimplemented. */
|
||||
|
@ -1,5 +1,6 @@
|
||||
const Cu = Components.utils;
|
||||
|
||||
function run_test() {
|
||||
var Cu = Components.utils;
|
||||
var sb1 = Cu.Sandbox("http://www.blah.com");
|
||||
var sb2 = Cu.Sandbox("http://www.blah.com");
|
||||
var sb3 = Cu.Sandbox(this);
|
||||
@ -13,25 +14,19 @@ function run_test() {
|
||||
|
||||
// non-chrome accessing chrome Components
|
||||
sb1.C = Components;
|
||||
rv = Cu.evalInSandbox("C.utils", sb1);
|
||||
do_check_eq(rv, undefined);
|
||||
rv = Cu.evalInSandbox("C.interfaces", sb1);
|
||||
do_check_neq(rv, undefined);
|
||||
checkThrows("C.utils", sb1);
|
||||
checkThrows("C.classes", sb1);
|
||||
|
||||
// non-chrome accessing own Components
|
||||
rv = Cu.evalInSandbox("Components.utils", sb1);
|
||||
do_check_eq(rv, undefined);
|
||||
rv = Cu.evalInSandbox("Components.interfaces", sb1);
|
||||
do_check_neq(rv, undefined);
|
||||
checkThrows("Components.utils", sb1);
|
||||
checkThrows("Components.classes", sb1);
|
||||
|
||||
// non-chrome same origin
|
||||
var C2 = Cu.evalInSandbox("Components", sb2);
|
||||
do_check_neq(rv, C2.utils);
|
||||
do_check_neq(rv, C2.utils);
|
||||
sb1.C2 = C2;
|
||||
rv = Cu.evalInSandbox("C2.utils", sb1);
|
||||
do_check_eq(rv, undefined);
|
||||
rv = Cu.evalInSandbox("C2.interfaces", sb1);
|
||||
do_check_neq(rv, undefined);
|
||||
checkThrows("C2.utils", sb1);
|
||||
checkThrows("C2.classes", sb1);
|
||||
|
||||
// chrome accessing chrome
|
||||
sb3.C = Components;
|
||||
@ -40,9 +35,11 @@ function run_test() {
|
||||
|
||||
// non-chrome cross origin
|
||||
sb4.C2 = C2;
|
||||
rv = Cu.evalInSandbox("C2.interfaces", sb1);
|
||||
do_check_neq(rv, undefined);
|
||||
rv = Cu.evalInSandbox("C2.utils", sb1);
|
||||
do_check_eq(rv, undefined);
|
||||
|
||||
checkThrows("C2.utils", sb1);
|
||||
checkThrows("C2.classes", sb1);
|
||||
}
|
||||
|
||||
function checkThrows(expression, sb) {
|
||||
var result = Cu.evalInSandbox('(function() { try { ' + expression + '; return "allowed"; } catch (e) { return e.toString(); }})();', sb);
|
||||
do_check_true(!!/denied/.exec(result));
|
||||
}
|
||||
|
@ -329,17 +329,6 @@ AccessCheck::deny(JSContext *cx, jsid id)
|
||||
|
||||
enum Access { READ = (1<<0), WRITE = (1<<1), NO_ACCESS = 0 };
|
||||
|
||||
static bool
|
||||
Deny(JSContext *cx, jsid id, Wrapper::Action act)
|
||||
{
|
||||
// Refuse to perform the action and just return the default value.
|
||||
if (act == Wrapper::GET)
|
||||
return true;
|
||||
// If its a set, deny it and throw an exception.
|
||||
AccessCheck::deny(cx, id);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsInSandbox(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
@ -349,19 +338,15 @@ IsInSandbox(JSContext *cx, JSObject *obj)
|
||||
}
|
||||
|
||||
bool
|
||||
ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act,
|
||||
Permission &perm)
|
||||
ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act)
|
||||
{
|
||||
JSObject *wrappedObject = Wrapper::wrappedObject(wrapper);
|
||||
|
||||
if (act == Wrapper::CALL) {
|
||||
perm = PermitObjectAccess;
|
||||
if (act == Wrapper::CALL)
|
||||
return true;
|
||||
}
|
||||
|
||||
perm = DenyAccess;
|
||||
if (act == Wrapper::PUNCTURE)
|
||||
return Deny(cx, id, act);
|
||||
return false;
|
||||
|
||||
jsid exposedPropsId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS);
|
||||
|
||||
@ -380,7 +365,6 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper:
|
||||
JS_IsTypedArrayObject(wrappedObject, cx)) &&
|
||||
((JSID_IS_INT(id) && JSID_TO_INT(id) >= 0) ||
|
||||
(JSID_IS_STRING(id) && JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), "length")))) {
|
||||
perm = PermitPropertyAccess;
|
||||
return true; // Allow
|
||||
}
|
||||
|
||||
@ -405,26 +389,20 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper:
|
||||
}
|
||||
}
|
||||
|
||||
perm = PermitPropertyAccess;
|
||||
return true;
|
||||
}
|
||||
return Deny(cx, id, act);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (id == JSID_VOID) {
|
||||
// This will force the caller to call us back for individual property accesses.
|
||||
perm = PermitPropertyAccess;
|
||||
if (id == JSID_VOID)
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::Value exposedProps;
|
||||
if (!JS_LookupPropertyById(cx, wrappedObject, exposedPropsId, &exposedProps))
|
||||
return false;
|
||||
|
||||
if (exposedProps.isNullOrUndefined()) {
|
||||
JSAutoCompartment wrapperAC(cx, wrapper);
|
||||
return Deny(cx, id, act);
|
||||
}
|
||||
if (exposedProps.isNullOrUndefined())
|
||||
return false;
|
||||
|
||||
if (!exposedProps.isObject()) {
|
||||
JS_ReportError(cx, "__exposedProps__ must be undefined, null, or an Object");
|
||||
@ -440,10 +418,8 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper:
|
||||
if (!JS_GetPropertyDescriptorById(cx, hallpass, id, JSRESOLVE_QUALIFIED, &desc)) {
|
||||
return false; // Error
|
||||
}
|
||||
if (desc.obj == NULL || !(desc.attrs & JSPROP_ENUMERATE)) {
|
||||
JSAutoCompartment wrapperAC(cx, wrapper);
|
||||
return Deny(cx, id, act);
|
||||
}
|
||||
if (!desc.obj || !(desc.attrs & JSPROP_ENUMERATE))
|
||||
return false;
|
||||
|
||||
if (!JSVAL_IS_STRING(desc.value)) {
|
||||
JS_ReportError(cx, "property must be a string");
|
||||
@ -487,19 +463,15 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper:
|
||||
|
||||
if ((act == Wrapper::SET && !(access & WRITE)) ||
|
||||
(act != Wrapper::SET && !(access & READ))) {
|
||||
JSAutoCompartment wrapperAC(cx, wrapper);
|
||||
return Deny(cx, id, act);
|
||||
return false;
|
||||
}
|
||||
|
||||
perm = PermitPropertyAccess;
|
||||
return true; // Allow
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act,
|
||||
Permission &perm)
|
||||
ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act)
|
||||
{
|
||||
perm = DenyAccess;
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
|
||||
if (JSID_IS_STRING(id) && act == Wrapper::GET) {
|
||||
@ -510,7 +482,6 @@ ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper
|
||||
JS_FlatStringEqualsAscii(flatId, "interfacesByID") ||
|
||||
JS_FlatStringEqualsAscii(flatId, "results"))
|
||||
{
|
||||
perm = PermitPropertyAccess;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -519,11 +490,10 @@ ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper
|
||||
// so we need this dynamic check. This can go away when we expose Components
|
||||
// as SpecialPowers.wrap(Components) during automation.
|
||||
if (xpc::IsUniversalXPConnectEnabled(cx)) {
|
||||
perm = PermitPropertyAccess;
|
||||
return true;
|
||||
}
|
||||
|
||||
return Deny(cx, id, act);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,33 +39,16 @@ class AccessCheck {
|
||||
};
|
||||
|
||||
struct Policy {
|
||||
typedef js::Wrapper::Permission Permission;
|
||||
|
||||
static const Permission PermitObjectAccess = js::Wrapper::PermitObjectAccess;
|
||||
static const Permission PermitPropertyAccess = js::Wrapper::PermitPropertyAccess;
|
||||
static const Permission DenyAccess = js::Wrapper::DenyAccess;
|
||||
};
|
||||
|
||||
// This policy permits access to all properties.
|
||||
struct Permissive : public Policy {
|
||||
static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act,
|
||||
Permission &perm) {
|
||||
perm = PermitObjectAccess;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// This policy only permits access to the object if the subject can touch
|
||||
// system objects.
|
||||
struct OnlyIfSubjectIsSystem : public Policy {
|
||||
static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act,
|
||||
Permission &perm) {
|
||||
if (AccessCheck::isSystemOnlyAccessPermitted(cx)) {
|
||||
perm = PermitObjectAccess;
|
||||
return true;
|
||||
}
|
||||
perm = DenyAccess;
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) {
|
||||
return AccessCheck::isSystemOnlyAccessPermitted(cx);
|
||||
}
|
||||
|
||||
static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) {
|
||||
AccessCheck::deny(cx, id);
|
||||
return false;
|
||||
}
|
||||
@ -74,17 +57,12 @@ struct OnlyIfSubjectIsSystem : public Policy {
|
||||
// This policy only permits access to properties that are safe to be used
|
||||
// across origins.
|
||||
struct CrossOriginAccessiblePropertiesOnly : public Policy {
|
||||
static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act,
|
||||
Permission &perm) {
|
||||
static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) {
|
||||
// Location objects should always use LocationPolicy.
|
||||
MOZ_ASSERT(!WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper)));
|
||||
|
||||
if (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act)) {
|
||||
perm = PermitPropertyAccess;
|
||||
return true;
|
||||
}
|
||||
perm = DenyAccess;
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
return AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act);
|
||||
}
|
||||
static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) {
|
||||
AccessCheck::deny(cx, id);
|
||||
return false;
|
||||
}
|
||||
@ -114,23 +92,19 @@ struct CrossOriginAccessiblePropertiesOnly : public Policy {
|
||||
// state of the outer window to determine whether we happen to be same-origin
|
||||
// at the moment.
|
||||
struct LocationPolicy : public Policy {
|
||||
static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act,
|
||||
Permission &perm) {
|
||||
static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act) {
|
||||
// We should only be dealing with Location objects here.
|
||||
MOZ_ASSERT(WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper)));
|
||||
|
||||
// Default to deny.
|
||||
perm = DenyAccess;
|
||||
|
||||
// Location object security is complicated enough. Don't allow punctures.
|
||||
if (act != js::Wrapper::PUNCTURE &&
|
||||
(AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act) ||
|
||||
AccessCheck::isLocationObjectSameOrigin(cx, wrapper))) {
|
||||
perm = PermitPropertyAccess;
|
||||
return true;
|
||||
}
|
||||
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
return false;
|
||||
}
|
||||
static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) {
|
||||
AccessCheck::deny(cx, id);
|
||||
return false;
|
||||
}
|
||||
@ -139,14 +113,26 @@ struct LocationPolicy : public Policy {
|
||||
// This policy only permits access to properties if they appear in the
|
||||
// objects exposed properties list.
|
||||
struct ExposedPropertiesOnly : public Policy {
|
||||
static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act,
|
||||
Permission &perm);
|
||||
static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act);
|
||||
|
||||
static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) {
|
||||
// For gets, silently fail.
|
||||
if (act == js::Wrapper::GET)
|
||||
return true;
|
||||
// For sets,throw an exception.
|
||||
AccessCheck::deny(cx, id);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Components specific policy
|
||||
struct ComponentsObjectPolicy : public Policy {
|
||||
static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act,
|
||||
Permission &perm);
|
||||
static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act);
|
||||
|
||||
static bool deny(JSContext *cx, jsid id, js::Wrapper::Action act) {
|
||||
AccessCheck::deny(cx, id);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -29,12 +29,6 @@ FilteringWrapper<Base, Policy>::~FilteringWrapper()
|
||||
{
|
||||
}
|
||||
|
||||
typedef Wrapper::Permission Permission;
|
||||
|
||||
static const Permission PermitObjectAccess = Wrapper::PermitObjectAccess;
|
||||
static const Permission PermitPropertyAccess = Wrapper::PermitPropertyAccess;
|
||||
static const Permission DenyAccess = Wrapper::DenyAccess;
|
||||
|
||||
template <typename Policy>
|
||||
static bool
|
||||
Filter(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
|
||||
@ -42,11 +36,10 @@ Filter(JSContext *cx, JSObject *wrapper, AutoIdVector &props)
|
||||
size_t w = 0;
|
||||
for (size_t n = 0; n < props.length(); ++n) {
|
||||
jsid id = props[n];
|
||||
Permission perm;
|
||||
if (!Policy::check(cx, wrapper, id, Wrapper::GET, perm))
|
||||
return false; // Error
|
||||
if (perm != DenyAccess)
|
||||
if (Policy::check(cx, wrapper, id, Wrapper::GET))
|
||||
props[w++] = id;
|
||||
else if (JS_IsExceptionPending(cx))
|
||||
return false;
|
||||
}
|
||||
props.resize(w);
|
||||
return true;
|
||||
@ -92,14 +85,16 @@ bool
|
||||
FilteringWrapper<Base, Policy>::enter(JSContext *cx, JSObject *wrapper, jsid id,
|
||||
Wrapper::Action act, bool *bp)
|
||||
{
|
||||
Permission perm;
|
||||
if (!Policy::check(cx, wrapper, id, act, perm)) {
|
||||
*bp = false;
|
||||
if (!Policy::check(cx, wrapper, id, act)) {
|
||||
if (JS_IsExceptionPending(cx)) {
|
||||
*bp = false;
|
||||
return false;
|
||||
}
|
||||
JSAutoCompartment ac(cx, wrapper);
|
||||
*bp = Policy::deny(cx, id, act);
|
||||
return false;
|
||||
}
|
||||
*bp = true;
|
||||
if (perm == DenyAccess)
|
||||
return false;
|
||||
return Base::enter(cx, wrapper, id, act, bp);
|
||||
}
|
||||
|
||||
|
19
layout/svg/crashtests/612736-1.svg
Normal file
19
layout/svg/crashtests/612736-1.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
class="reftest-wait">
|
||||
<path id="path"/>
|
||||
<text>
|
||||
<textPath xlink:href="#path">f</textPath>
|
||||
<textPath xlink:href="#path">f</textPath>
|
||||
</text>
|
||||
|
||||
<script>
|
||||
function boom()
|
||||
{
|
||||
var path = document.getElementById("path");
|
||||
path.parentNode.removeChild(path);
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
window.addEventListener("load", boom, false);
|
||||
</script>
|
||||
</svg>
|
After Width: | Height: | Size: 493 B |
8
layout/svg/crashtests/612736-2.svg
Normal file
8
layout/svg/crashtests/612736-2.svg
Normal file
@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<path id="path" d="M0 100 h50" stroke="black"/>
|
||||
<text>
|
||||
<textPath xlink:href="#path">abc</textPath>
|
||||
<textPath xlink:href="#path">def</textPath>
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 261 B |
16
layout/svg/crashtests/784061-1.svg
Normal file
16
layout/svg/crashtests/784061-1.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
class="reftest-wait">
|
||||
|
||||
<defs><path id="x"/></defs>
|
||||
|
||||
<script>
|
||||
function boom()
|
||||
{
|
||||
document.getElementById("x").style.transform = "translate(0px)";
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
|
||||
window.addEventListener("load", boom, false);
|
||||
</script>
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 315 B |
@ -106,6 +106,8 @@ load 610594-1.html
|
||||
load 610954-1.html
|
||||
load 612662-1.svg
|
||||
load 612662-2.svg
|
||||
load 612736-1.svg
|
||||
load 612736-2.svg
|
||||
load 614367-1.svg
|
||||
load 620034-1.html
|
||||
load 621598-1.svg
|
||||
@ -134,6 +136,6 @@ load 767056-1.svg
|
||||
load 768351.svg
|
||||
load 780963-1.html
|
||||
load 757751-1.svg
|
||||
load 784061-1.svg
|
||||
load 790072.svg
|
||||
load 791826-1.svg
|
||||
|
||||
|
@ -223,6 +223,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
registerEventListener("Feedback:OpenPlayStore");
|
||||
registerEventListener("Feedback:MaybeLater");
|
||||
registerEventListener("Dex:Load");
|
||||
registerEventListener("Telemetry:Gather");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -237,6 +238,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
unregisterEventListener("Feedback:OpenPlayStore");
|
||||
unregisterEventListener("Feedback:MaybeLater");
|
||||
unregisterEventListener("Dex:Load");
|
||||
unregisterEventListener("Telemetry:Gather");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -428,6 +430,11 @@ abstract public class BrowserApp extends GeckoApp
|
||||
menu.findItem(R.id.settings).setEnabled(true);
|
||||
}
|
||||
});
|
||||
} else if (event.equals("Telemetry:Gather")) {
|
||||
Telemetry.HistogramAdd("PLACES_PAGES_COUNT", BrowserDB.getCount(getContentResolver(), "history"));
|
||||
Telemetry.HistogramAdd("PLACES_BOOKMARKS_COUNT", BrowserDB.getCount(getContentResolver(), "bookmarks"));
|
||||
Telemetry.HistogramAdd("FENNEC_FAVICONS_COUNT", BrowserDB.getCount(getContentResolver(), "favicons"));
|
||||
Telemetry.HistogramAdd("FENNEC_THUMBNAILS_COUNT", BrowserDB.getCount(getContentResolver(), "thumbnails"));
|
||||
} else if (event.equals("Dex:Load")) {
|
||||
String zipFile = message.getString("zipfile");
|
||||
String implClass = message.getString("impl");
|
||||
|
@ -90,6 +90,8 @@ public class BrowserDB {
|
||||
public void registerBookmarkObserver(ContentResolver cr, ContentObserver observer);
|
||||
|
||||
public void registerHistoryObserver(ContentResolver cr, ContentObserver observer);
|
||||
|
||||
public int getCount(ContentResolver cr, String database);
|
||||
}
|
||||
|
||||
static {
|
||||
@ -227,4 +229,8 @@ public class BrowserDB {
|
||||
public static void unregisterContentObserver(ContentResolver cr, ContentObserver observer) {
|
||||
cr.unregisterContentObserver(observer);
|
||||
}
|
||||
|
||||
public static int getCount(ContentResolver cr, String database) {
|
||||
return sDb.getCount(cr, database);
|
||||
}
|
||||
}
|
||||
|
@ -177,6 +177,38 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
return new LocalDBCursor(c);
|
||||
}
|
||||
|
||||
public int getCount(ContentResolver cr, String database) {
|
||||
Cursor cursor = null;
|
||||
int count = 0;
|
||||
String constraint = null;
|
||||
try {
|
||||
Uri uri = null;
|
||||
if ("history".equals(database)) {
|
||||
uri = mHistoryUriWithProfile;
|
||||
constraint = Combined.VISITS + " > 0";
|
||||
} else if ("bookmarks".equals(database)) {
|
||||
uri = mBookmarksUriWithProfile;
|
||||
// ignore folders, tags, keywords, separators, etc.
|
||||
constraint = Bookmarks.TYPE + " = " + Bookmarks.TYPE_BOOKMARK;
|
||||
} else if ("thumbnails".equals(database)) {
|
||||
uri = mImagesUriWithProfile;
|
||||
constraint = Combined.THUMBNAIL + " IS NOT NULL";
|
||||
} else if ("favicons".equals(database)) {
|
||||
uri = mImagesUriWithProfile;
|
||||
constraint = Combined.FAVICON + " IS NOT NULL";
|
||||
}
|
||||
if (uri != null) {
|
||||
cursor = cr.query(uri, null, constraint, null, null);
|
||||
count = cursor.getCount();
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
debug("Got count " + count + " for " + database);
|
||||
return count;
|
||||
}
|
||||
|
||||
public Cursor filter(ContentResolver cr, CharSequence constraint, int limit) {
|
||||
return filterAllSites(cr,
|
||||
new String[] { Combined._ID,
|
||||
|
@ -184,6 +184,7 @@ var BrowserApp = {
|
||||
Services.obs.addObserver(this, "Passwords:Init", false);
|
||||
Services.obs.addObserver(this, "FormHistory:Init", false);
|
||||
Services.obs.addObserver(this, "ToggleProfiling", false);
|
||||
Services.obs.addObserver(this, "gather-telemetry", false);
|
||||
|
||||
Services.obs.addObserver(this, "sessionstore-state-purge-complete", false);
|
||||
|
||||
@ -1145,6 +1146,8 @@ var BrowserApp = {
|
||||
} else {
|
||||
profiler.StartProfiler(100000, 25, ["stackwalk"], 1);
|
||||
}
|
||||
} else if (aTopic == "gather-telemetry") {
|
||||
sendMessageToJava({ gecko: { type: "Telemetry:Gather" }});
|
||||
} else if (aTopic == "Session:Restore") {
|
||||
let data = JSON.parse(aData);
|
||||
this.restoreSession(data.restoringOOM, data.sessionString);
|
||||
|
@ -158,7 +158,7 @@ add_test(function test_invalid_request_method() {
|
||||
allowed.add(method);
|
||||
}
|
||||
|
||||
do_check_eq(allowed.size(), 3);
|
||||
do_check_eq(allowed.size, 3);
|
||||
for (let method of ["GET", "PUT", "DELETE"]) {
|
||||
do_check_true(allowed.has(method));
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
* 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/. */
|
||||
|
||||
var EXPORTED_SYMBOLS = ["MockPermissionPrompt"];
|
||||
this.EXPORTED_SYMBOLS = ["MockPermissionPrompt"];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
@ -131,7 +131,7 @@ var inMemoryPrefsProto = {
|
||||
|
||||
if (this._prefCache[aGroup].has(aName)) {
|
||||
this._prefCache[aGroup].delete(aName);
|
||||
if (this._prefCache[aGroup].size() == 0) {
|
||||
if (this._prefCache[aGroup].size == 0) {
|
||||
// remove empty group
|
||||
delete this._prefCache[aGroup];
|
||||
}
|
||||
|
@ -1754,6 +1754,20 @@
|
||||
"n_buckets": 10,
|
||||
"description": "PLACES: Number of keywords"
|
||||
},
|
||||
"FENNEC_FAVICONS_COUNT": {
|
||||
"kind": "exponential",
|
||||
"high": "2000",
|
||||
"n_buckets": 10,
|
||||
"cpp_guard": "ANDROID",
|
||||
"description": "FENNEC: (Places) Number of favicons stored"
|
||||
},
|
||||
"FENNEC_THUMBNAILS_COUNT": {
|
||||
"kind": "exponential",
|
||||
"high": "2000",
|
||||
"n_buckets": 10,
|
||||
"cpp_guard": "ANDROID",
|
||||
"description": "FENNEC: (Places) Number of thumbnails stored"
|
||||
},
|
||||
"PLACES_SORTED_BOOKMARKS_PERC": {
|
||||
"kind": "linear",
|
||||
"high": "100",
|
||||
|
@ -36,7 +36,7 @@ function dumpn(str)
|
||||
|
||||
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader);
|
||||
loader.loadSubScript("chrome://global/content/devtools/dbg-transport.js");
|
||||
loader.loadSubScript("chrome://global/content/devtools/dbg-transport.js", this);
|
||||
|
||||
/**
|
||||
* Add simple event notification to a prototype object. Any object that has
|
||||
|
@ -5,7 +5,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
/**
|
||||
* An adapter that handles data transfers between the debugger client and
|
||||
|
@ -18,6 +18,15 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
function CcDenied() {
|
||||
try {
|
||||
Components.classes;
|
||||
return false;
|
||||
} catch (e) {
|
||||
return !!/denied/.exec(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Build an object with test results (true = pass)
|
||||
let results = {
|
||||
windowTop: window.top == window,
|
||||
@ -28,7 +37,7 @@
|
||||
.docCharsetIsForced;
|
||||
}),
|
||||
|
||||
ccAccess: SpecialPowers.Components.classes == null,
|
||||
ccAccess: !!CcDenied(),
|
||||
};
|
||||
|
||||
let resultsJSON = JSON.stringify(results);
|
||||
|
@ -78,9 +78,9 @@ nsPluginInstallerWizard.prototype.pluginInfoReceived = function (aPluginRequestI
|
||||
progressMeter.setAttribute("mode", "determined");
|
||||
|
||||
progressMeter.setAttribute("value",
|
||||
((this.WSPluginCounter / this.mPluginRequests.size()) * 100) + "%");
|
||||
((this.WSPluginCounter / this.mPluginRequests.size) * 100) + "%");
|
||||
|
||||
if (this.WSPluginCounter == this.mPluginRequests.size()) {
|
||||
if (this.WSPluginCounter == this.mPluginRequests.size) {
|
||||
// check if no plugins were found
|
||||
if (this.mPluginInfoArrayLength == 0) {
|
||||
this.advancePage("lastpage");
|
||||
|
@ -134,7 +134,7 @@ function runBasicTest(aIsEditable, aInDesignMode, aDescription)
|
||||
{
|
||||
function test(aTest)
|
||||
{
|
||||
function moveFocus(aTest)
|
||||
function moveFocus(aTest, aFocusEventHandler)
|
||||
{
|
||||
if (aInDesignMode) {
|
||||
if (document.activeElement) {
|
||||
@ -149,10 +149,14 @@ function runBasicTest(aIsEditable, aInDesignMode, aDescription)
|
||||
}
|
||||
var previousFocusedElement = gFM.focusedElement;
|
||||
var element = document.getElementById(aTest.id);
|
||||
var focusEventTarget = element;
|
||||
if (element.contentDocument) {
|
||||
focusEventTarget = element.contentDocument;
|
||||
element = element.contentDocument.documentElement;
|
||||
}
|
||||
focusEventTarget.addEventListener("focus", aFocusEventHandler, true);
|
||||
element.focus();
|
||||
focusEventTarget.removeEventListener("focus", aFocusEventHandler, true);
|
||||
var focusedElement = gFM.focusedElement;
|
||||
if (focusedElement) {
|
||||
var bindingParent = document.getBindingParent(focusedElement);
|
||||
@ -186,9 +190,28 @@ function runBasicTest(aIsEditable, aInDesignMode, aDescription)
|
||||
// IME Enabled state testing
|
||||
var enabled = gUtils.IME_STATUS_ENABLED;
|
||||
if (kIMEEnabledSupported) {
|
||||
if (!moveFocus(aTest)) {
|
||||
var focusEventCount = 0;
|
||||
function onFocus(aEvent)
|
||||
{
|
||||
focusEventCount++;
|
||||
is(gUtils.IMEStatus, aTest.expectedEnabled,
|
||||
aDescription + ": " + aTest.description + ", wrong enabled state at focus event");
|
||||
}
|
||||
|
||||
if (!moveFocus(aTest, onFocus)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aTest.focusable) {
|
||||
if (aTest.focusEventNotFired) {
|
||||
todo(focusEventCount > 0,
|
||||
aDescription + ": " + aTest.description + ", focus event is never fired");
|
||||
} else {
|
||||
ok(focusEventCount > 0,
|
||||
aDescription + ": " + aTest.description + ", focus event is never fired");
|
||||
}
|
||||
}
|
||||
|
||||
enabled = gUtils.IMEStatus;
|
||||
inputtype = gUtils.focusedInputType;
|
||||
is(enabled, aTest.expectedEnabled,
|
||||
@ -254,10 +277,12 @@ function runBasicTest(aIsEditable, aInDesignMode, aDescription)
|
||||
{ id: "checkbox",
|
||||
description: "input[type=checkbox]",
|
||||
focusable: !aInDesignMode,
|
||||
focusEventNotFired: aIsEditable && !aInDesignMode,
|
||||
expectedEnabled: kEnabledStateOnNonEditableElement },
|
||||
{ id: "radio",
|
||||
description: "input[type=radio]",
|
||||
focusable: !aInDesignMode,
|
||||
focusEventNotFired: aIsEditable && !aInDesignMode,
|
||||
expectedEnabled: kEnabledStateOnNonEditableElement },
|
||||
{ id: "submit",
|
||||
description: "input[type=submit]",
|
||||
@ -270,6 +295,7 @@ function runBasicTest(aIsEditable, aInDesignMode, aDescription)
|
||||
{ id: "file",
|
||||
description: "input[type=file]",
|
||||
focusable: !aInDesignMode,
|
||||
focusEventNotFired: aIsEditable && !aInDesignMode,
|
||||
expectedEnabled: kEnabledStateOnNonEditableElement },
|
||||
{ id: "button",
|
||||
description: "input[type=button]",
|
||||
@ -321,10 +347,12 @@ function runBasicTest(aIsEditable, aInDesignMode, aDescription)
|
||||
{ id: "select",
|
||||
description: "select (dropdown list)",
|
||||
focusable: !aInDesignMode,
|
||||
focusEventNotFired: aIsEditable && !aInDesignMode,
|
||||
expectedEnabled: kEnabledStateOnNonEditableElement },
|
||||
{ id: "select_multiple",
|
||||
description: "select (list box)",
|
||||
focusable: !aInDesignMode,
|
||||
focusEventNotFired: aIsEditable && !aInDesignMode,
|
||||
expectedEnabled: kEnabledStateOnNonEditableElement },
|
||||
|
||||
// a element
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user