Merge a PGO-green inbound changeset to m-c.

This commit is contained in:
Philipp von Weitershausen 2012-08-31 10:14:33 -07:00
commit 6017a8e2ad
70 changed files with 1222 additions and 876 deletions

View File

@ -189,6 +189,10 @@ var shell = {
window.removeEventListener('sizemodechange', this);
this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
ppmm.removeMessageListener("content-handler", this);
if (this.timer) {
this.timer.cancel();
this.timer = null;
}
#ifndef MOZ_WIDGET_GONK
delete Services.audioManager;
@ -254,8 +258,11 @@ var shell = {
this.sendChromeEvent({type: type});
}
},
lastHardwareButtonEventType: null, // property for the hack above
needBufferSysMsgs: true,
bufferedSysMsgs: [],
timer: null,
handleEvent: function shell_handleEvent(evt) {
let content = this.contentBrowser.contentWindow;
@ -340,6 +347,18 @@ var shell = {
ObjectWrapper.wrap(details, getContentWindow()));
},
sendSystemMessage: function shell_sendSystemMessage(msg) {
let origin = Services.io.newURI(msg.manifest, null, null).prePath;
this.sendChromeEvent({
type: 'open-app',
url: msg.uri,
origin: origin,
manifest: msg.manifest,
isActivity: (msg.type == 'activity'),
target: msg.target
});
},
receiveMessage: function shell_receiveMessage(message) {
if (message.name != 'content-handler') {
return;
@ -389,15 +408,13 @@ nsBrowserAccess.prototype = {
// Listen for system messages and relay them to Gaia.
Services.obs.addObserver(function onSystemMessage(subject, topic, data) {
let msg = JSON.parse(data);
let origin = Services.io.newURI(msg.manifest, null, null).prePath;
shell.sendChromeEvent({
type: 'open-app',
url: msg.uri,
origin: origin,
manifest: msg.manifest,
isActivity: (msg.type == 'activity'),
target: msg.target
});
// Buffer non-activity messages until content starts to load for 10 seconds.
// We'll revisit this later if new kind of messages don't need to be cached.
if (shell.needBufferSysMsgs && msg.type !== 'activity') {
shell.bufferedSysMsgs.push(msg);
return;
}
shell.sendSystemMessage(msg);
}, 'system-messages-open-app', false);
Services.obs.addObserver(function(aSubject, aTopic, aData) {
@ -461,6 +478,18 @@ var CustomEventManager = {
window.addEventListener("ContentStart", (function(evt) {
let content = shell.contentBrowser.contentWindow;
content.addEventListener("mozContentEvent", this, false, true);
// After content starts to load for 10 seconds, send and
// clean up the buffered system messages if there is any.
shell.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
shell.timer.initWithCallback(function timerCallback() {
shell.bufferedSysMsgs.forEach(function sendSysMsg(msg) {
shell.sendSystemMessage(msg);
});
shell.bufferedSysMsgs.length = 0;
shell.needBufferSysMsgs = false;
shell.timer = null;
}, 10000, Ci.nsITimer.TYPE_ONE_SHOT);
}).bind(this), false);
},

View File

@ -274,6 +274,7 @@
<panel id="social-flyout-panel"
onpopupshown="SocialFlyout.onShown()"
onpopuphidden="SocialFlyout.onHidden()"
side="right"
type="arrow"
hidden="true"
noautofocus="true"

View File

@ -1066,8 +1066,8 @@ nsEventListenerManager::HasUnloadListeners()
}
nsresult
nsEventListenerManager::SetEventHandlerToJsval(nsIAtom *aEventName,
JSContext *cx,
nsEventListenerManager::SetEventHandlerToJsval(nsIAtom* aEventName,
JSContext* cx,
JSObject* aScope,
const jsval& v,
bool aExpectScriptContext)

View File

@ -272,8 +272,8 @@ public:
* be in the same compartment. If aExpectScriptContext is false,
* not finding an nsIScriptContext does not cause failure.
*/
nsresult SetEventHandlerToJsval(nsIAtom *aEventName, JSContext *cx,
JSObject *aScope, const jsval &v,
nsresult SetEventHandlerToJsval(nsIAtom* aEventName, JSContext* cx,
JSObject* aScope, const jsval& v,
bool aExpectScriptContext);
/**
* Get the value of the "inline" event listener for aEventName.

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<script>
function boom()
{
var mo = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mo");
var text = document.createTextNode(" ");
mo.appendChild(text);
document.body.appendChild(mo);
window.getSelection().removeAllRanges();
var r = document.createRange();
r.setStart(mo.firstChild, 0);
r.setEnd(mo.firstChild, 1);
window.getSelection().addRange(r);
window.getSelection().toString();
}
</script>
</head>
<body onload="boom();"></body>
</html>

View File

@ -1 +1,2 @@
load 462929-1.html
load 770710-1.html

View File

@ -989,9 +989,23 @@ DOMApplicationManifest.prototype = {
},
fullLaunchPath: function(aStartPoint) {
let startPoint = aStartPoint || "";
let launchPath = this._localeProp("launch_path") || "";
return this._origin.resolve(launchPath + startPoint);
// If no start point is specified, we use the root launch path.
// In all error cases, we just return null.
if ((aStartPoint || "") === "") {
return this._origin.resolve(this._localeProp("launch_path") || "");
}
// Search for the l10n entry_points property.
let entryPoints = this._localeProp("entry_points");
if (!entryPoints) {
return null;
}
if (entryPoints[aStartPoint]) {
return this._origin.resolve(entryPoints[aStartPoint].launch_path || "");
}
return null;
},
resolveFromOrigin: function(aURI) {

View File

@ -28,7 +28,7 @@ public:
nsresult Init(nsPIDOMWindow* aWindow, const nsAString &aType);
void SetRootFileForType(const nsAString& aType);
void SetRootDirectoryForType(const nsAString& aType);
static void CreateDeviceStoragesFor(nsPIDOMWindow* aWin,
const nsAString &aType,
@ -50,15 +50,15 @@ private:
bool aEditable,
nsIDOMDeviceStorageCursor** aRetval);
int32_t mStorageType;
nsCOMPtr<nsIFile> mFile;
nsString mStorageType;
nsCOMPtr<nsIFile> mRootDirectory;
nsCOMPtr<nsIPrincipal> mPrincipal;
bool mIsWatchingFile;
bool mAllowedToWatchFile;
nsresult Notify(const char* aReason, nsIFile* aFile);
nsresult Notify(const char* aReason, class DeviceStorageFile* aFile);
friend class WatchFileEvent;
friend class DeviceStorageRequest;

View File

@ -83,7 +83,7 @@ DeviceStorageRequestChild::Recv__delete__(const DeviceStorageResponseValue& aVal
continue;
}
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(r.paths()[i].type(), f);
dsf->SetPath(r.paths()[i].name());
cursor->mFiles.AppendElement(dsf);
}

View File

@ -30,7 +30,7 @@ DeviceStorageRequestParent::DeviceStorageRequestParent(const DeviceStorageParams
nsCOMPtr<nsIFile> f;
NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
BlobParent* bp = static_cast<BlobParent*>(p.blobParent());
nsCOMPtr<nsIDOMBlob> blob = bp->GetBlob();
@ -53,7 +53,7 @@ DeviceStorageRequestParent::DeviceStorageRequestParent(const DeviceStorageParams
nsCOMPtr<nsIFile> f;
NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
dsf->SetPath(p.name());
nsRefPtr<CancelableRunnable> r = new ReadFileEvent(this, dsf);
@ -70,7 +70,7 @@ DeviceStorageRequestParent::DeviceStorageRequestParent(const DeviceStorageParams
nsCOMPtr<nsIFile> f;
NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
nsRefPtr<CancelableRunnable> r = new DeleteFileEvent(this, dsf);
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
@ -86,7 +86,7 @@ DeviceStorageRequestParent::DeviceStorageRequestParent(const DeviceStorageParams
nsCOMPtr<nsIFile> f;
NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
nsRefPtr<StatFileEvent> r = new StatFileEvent(this, dsf);
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
@ -102,7 +102,7 @@ DeviceStorageRequestParent::DeviceStorageRequestParent(const DeviceStorageParams
nsCOMPtr<nsIFile> f;
NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
nsRefPtr<CancelableRunnable> r = new EnumerateFileEvent(this, dsf, p.since());
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
@ -312,14 +312,15 @@ DeviceStorageRequestParent::StatFileEvent::CancelableRun()
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIRunnable> r;
uint64_t diskUsage = DeviceStorageFile::DirectoryDiskUsage(mFile->mFile);
int64_t freeSpace;
uint64_t diskUsage = 0;
DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &diskUsage);
int64_t freeSpace = 0;
nsresult rv = mFile->mFile->GetDiskSpaceAvailable(&freeSpace);
if (NS_FAILED(rv)) {
freeSpace = 0;
}
r = new PostStatResultEvent(mParent, diskUsage, freeSpace);
r = new PostStatResultEvent(mParent, freeSpace, diskUsage);
NS_DispatchToMainThread(r);
return NS_OK;
}
@ -406,7 +407,7 @@ DeviceStorageRequestParent::EnumerateFileEvent::CancelableRun()
for (uint32_t i = 0; i < count; i++) {
nsString fullpath;
files[i]->mFile->GetPath(fullpath);
DeviceStorageFileValue dsvf(files[i]->mPath, fullpath);
DeviceStorageFileValue dsvf(mFile->mStorageType, files[i]->mPath, fullpath);
values.AppendElement(dsvf);
}

View File

@ -27,6 +27,7 @@ struct BlobResponse
struct DeviceStorageFileValue
{
nsString type;
nsString name;
nsString fullpath;
};

View File

@ -36,6 +36,8 @@
#include "nsIObserverService.h"
#include "GeneratedEvents.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "nsIMIMEService.h"
#include "nsCExternalHandlerService.h"
// Microsoft's API Name hackery sucks
#undef CreateEvent
@ -45,6 +47,12 @@
#include "nsIVolumeService.h"
#endif
#define DEBUG_ISTYPE 1
#ifdef DEBUG_ISTYPE
#include "nsIConsoleService.h"
#endif
using namespace mozilla::dom;
using namespace mozilla::dom::devicestorage;
@ -53,7 +61,7 @@ using namespace mozilla::dom::devicestorage;
class IOEventComplete : public nsRunnable
{
public:
IOEventComplete(nsIFile *aFile, const char *aType)
IOEventComplete(DeviceStorageFile *aFile, const char *aType)
: mFile(aFile)
, mType(aType)
{
@ -67,17 +75,18 @@ public:
nsString data;
CopyASCIItoUTF16(mType, data);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->NotifyObservers(mFile, "file-watcher-update", data.get());
obs->NotifyObservers(mFile, "file-watcher-update", data.get());
return NS_OK;
}
private:
nsCOMPtr<nsIFile> mFile;
nsRefPtr<DeviceStorageFile> mFile;
nsCString mType;
};
DeviceStorageFile::DeviceStorageFile(nsIFile* aFile, const nsAString& aPath)
DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile, const nsAString& aPath)
: mPath(aPath)
, mStorageType(aStorageType)
, mEditable(false)
{
NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile");
@ -89,8 +98,9 @@ DeviceStorageFile::DeviceStorageFile(nsIFile* aFile, const nsAString& aPath)
NormalizeFilePath();
}
DeviceStorageFile::DeviceStorageFile(nsIFile* aFile)
: mEditable(false)
DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile)
: mStorageType(aStorageType)
, mEditable(false)
{
NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile");
// always take a clone
@ -140,6 +150,75 @@ DeviceStorageFile::IsSafePath()
return true;
}
bool
DeviceStorageFile::IsType(nsAString& aType)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// in testing, we ignore filtering for the testing types
if (mozilla::Preferences::GetBool("device.storage.testing", false) &&
(aType.Equals(NS_LITERAL_STRING("testing")) ||
aType.Equals(NS_LITERAL_STRING("testing-other")))) {
return true;
}
#ifdef DEBUG_ISTYPE
nsCOMPtr<nsIConsoleService> svc = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
char buffer[1024];
nsCString path;
mFile->GetNativePath(path);
PRIntervalTime iStart = PR_IntervalNow();
#endif
nsCAutoString mimeType;
nsCOMPtr<nsIMIMEService> mimeService = do_GetService(NS_MIMESERVICE_CONTRACTID);
if (!mimeService) {
return false;
}
nsresult rv = mimeService->GetTypeFromFile(mFile, mimeType);
if (NS_FAILED(rv)) {
#ifdef DEBUG_ISTYPE
sprintf(buffer, "GetTypeFromFile failed for %s (took: %dms)\n",
path.get(),
PR_IntervalToMilliseconds(PR_IntervalNow() - iStart));
nsString data;
CopyASCIItoUTF16(buffer, data);
svc->LogStringMessage(data.get());
printf("%s\n", buffer);
#endif
return false;
}
#ifdef DEBUG_ISTYPE
sprintf(buffer, "IsType of %s is %s (took: %dms)\n",
path.get(),
mimeType.get(),
PR_IntervalToMilliseconds(PR_IntervalNow() - iStart));
nsString data;
CopyASCIItoUTF16(buffer, data);
svc->LogStringMessage(data.get());
printf("%s\n", buffer);
#endif
if (aType.Equals(NS_LITERAL_STRING("pictures"))) {
return StringBeginsWith(mimeType, NS_LITERAL_CSTRING("image/"));
}
if (aType.Equals(NS_LITERAL_STRING("videos"))) {
return StringBeginsWith(mimeType, NS_LITERAL_CSTRING("video/"));
}
if (aType.Equals(NS_LITERAL_STRING("music"))) {
return StringBeginsWith(mimeType, NS_LITERAL_CSTRING("audio/"));
}
return false;
}
void
DeviceStorageFile::NormalizeFilePath() {
#if defined(XP_WIN)
@ -185,8 +264,8 @@ DeviceStorageFile::Write(nsIInputStream* aInputStream)
return rv;
}
nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(mFile, "created");
NS_DispatchToMainThread(iocomplete);
nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(this, "created");
NS_DispatchToMainThread(iocomplete);
uint64_t bufSize = 0;
aInputStream->Available(&bufSize);
@ -219,7 +298,7 @@ DeviceStorageFile::Write(nsIInputStream* aInputStream)
bufSize -= wrote;
}
iocomplete = new IOEventComplete(mFile, "modified");
iocomplete = new IOEventComplete(this, "modified");
NS_DispatchToMainThread(iocomplete);
bufferedOutputStream->Close();
@ -238,7 +317,7 @@ DeviceStorageFile::Write(InfallibleTArray<uint8_t>& aBits) {
return rv;
}
nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(mFile, "created");
nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(this, "created");
NS_DispatchToMainThread(iocomplete);
nsCOMPtr<nsIOutputStream> outputStream;
@ -252,7 +331,7 @@ DeviceStorageFile::Write(InfallibleTArray<uint8_t>& aBits) {
outputStream->Write((char*) aBits.Elements(), aBits.Length(), &wrote);
outputStream->Close();
iocomplete = new IOEventComplete(mFile, "modified");
iocomplete = new IOEventComplete(this, "modified");
NS_DispatchToMainThread(iocomplete);
if (aBits.Length() != wrote) {
@ -281,7 +360,7 @@ DeviceStorageFile::Remove()
return rv;
}
nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(mFile, "deleted");
nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(this, "deleted");
NS_DispatchToMainThread(iocomplete);
return NS_OK;
}
@ -344,22 +423,22 @@ DeviceStorageFile::collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> > &
nsDependentSubstring newPath = Substring(fullpath, len);
if (isDir) {
DeviceStorageFile dsf(f);
DeviceStorageFile dsf(mStorageType, f);
dsf.SetPath(newPath);
dsf.collectFilesInternal(aFiles, aSince, aRootPath);
} else if (isFile) {
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(f);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, f);
dsf->SetPath(newPath);
aFiles.AppendElement(dsf);
}
}
}
uint64_t
DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, uint64_t aSoFar)
void
DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar)
{
if (!aFile) {
return aSoFar;
return;
}
nsresult rv;
@ -367,7 +446,7 @@ DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, uint64_t aSoFar)
rv = aFile->GetDirectoryEntries(getter_AddRefs(e));
if (NS_FAILED(rv) || !e) {
return aSoFar;
return;
}
nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
@ -392,21 +471,19 @@ DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, uint64_t aSoFar)
if (NS_FAILED(rv)) {
continue;
}
if (isLink) {
// for now, lets just totally ignore symlinks.
NS_WARNING("DirectoryDiskUsage ignores symlinks");
} else if (isDir) {
aSoFar += DirectoryDiskUsage(f, aSoFar);
DirectoryDiskUsage(f, aSoFar);
} else if (isFile) {
int64_t size;
rv = f->GetFileSize(&size);
if (NS_SUCCEEDED(rv)) {
aSoFar += size;
*aSoFar += size;
}
}
}
return aSoFar;
}
NS_IMPL_THREADSAFE_ISUPPORTS0(DeviceStorageFile)
@ -456,7 +533,7 @@ UnregisterForSDCardChanges(nsIObserver* aObserver)
#endif
void
nsDOMDeviceStorage::SetRootFileForType(const nsAString& aType)
nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aType)
{
nsCOMPtr<nsIFile> f;
nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
@ -465,7 +542,7 @@ nsDOMDeviceStorage::SetRootFileForType(const nsAString& aType)
// Picture directory
if (aType.Equals(NS_LITERAL_STRING("pictures"))) {
#ifdef MOZ_WIDGET_GONK
NS_NewLocalFile(NS_LITERAL_STRING("/sdcard/DCIM"), false, getter_AddRefs(f));
NS_NewLocalFile(NS_LITERAL_STRING("/sdcard"), false, getter_AddRefs(f));
#elif defined (MOZ_WIDGET_COCOA)
dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
#elif defined (XP_UNIX)
@ -476,7 +553,7 @@ nsDOMDeviceStorage::SetRootFileForType(const nsAString& aType)
// Video directory
else if (aType.Equals(NS_LITERAL_STRING("videos"))) {
#ifdef MOZ_WIDGET_GONK
NS_NewLocalFile(NS_LITERAL_STRING("/sdcard/Movies"), false, getter_AddRefs(f));
NS_NewLocalFile(NS_LITERAL_STRING("/sdcard"), false, getter_AddRefs(f));
#elif defined (MOZ_WIDGET_COCOA)
dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
#elif defined (XP_UNIX)
@ -487,7 +564,7 @@ nsDOMDeviceStorage::SetRootFileForType(const nsAString& aType)
// Music directory
else if (aType.Equals(NS_LITERAL_STRING("music"))) {
#ifdef MOZ_WIDGET_GONK
NS_NewLocalFile(NS_LITERAL_STRING("/sdcard/Music"), false, getter_AddRefs(f));
NS_NewLocalFile(NS_LITERAL_STRING("/sdcard"), false, getter_AddRefs(f));
#elif defined (MOZ_WIDGET_COCOA)
dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
#elif defined (XP_UNIX)
@ -499,7 +576,8 @@ nsDOMDeviceStorage::SetRootFileForType(const nsAString& aType)
if (mozilla::Preferences::GetBool("device.storage.testing", false)) {
// testing directory
if (aType.Equals(NS_LITERAL_STRING("testing"))) {
if (aType.Equals(NS_LITERAL_STRING("testing")) ||
aType.Equals(NS_LITERAL_STRING("testing-other"))) {
dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
if (f) {
f->AppendRelativeNativePath(NS_LITERAL_CSTRING("device-storage-testing"));
@ -507,16 +585,7 @@ nsDOMDeviceStorage::SetRootFileForType(const nsAString& aType)
f->Normalize();
}
}
if (aType.Equals(NS_LITERAL_STRING("testing-other"))) {
dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
if (f) {
f->AppendRelativeNativePath(NS_LITERAL_CSTRING("device-storage-testing-other"));
f->Create(nsIFile::DIRECTORY_TYPE, 0777);
f->Normalize();
}
}
}
}
#ifdef MOZ_WIDGET_GONK
RegisterForSDCardChanges(this);
@ -524,7 +593,8 @@ nsDOMDeviceStorage::SetRootFileForType(const nsAString& aType)
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->AddObserver(this, "file-watcher-update", false);
mFile = f;
mRootDirectory = f;
mStorageType = aType;
}
jsval InterfaceToJsval(nsPIDOMWindow* aWindow, nsISupports* aObject, const nsIID* aIID)
@ -729,18 +799,21 @@ NS_IMETHODIMP
ContinueCursorEvent::Run() {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
jsval val;
jsval val = JSVAL_NULL;
nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
if (cursor->mFiles.Length() == 0) {
val = JSVAL_NULL;
}
else {
nsString cursorStorageType;
cursor->GetStorageType(cursorStorageType);
while (cursor->mFiles.Length() > 0) {
nsRefPtr<DeviceStorageFile> file = cursor->mFiles[0];
cursor->mFiles.RemoveElementAt(0);
// todo, this blob needs to be opened in the parent. This will be signifincally easier when bent lands
if (!file->IsType(cursorStorageType)) {
continue;
}
val = nsIFileToJsval(cursor->GetOwner(), file);
cursor->mOkToCallContinue = true;
break;
}
mRequest->FireSuccess(val);
@ -816,6 +889,12 @@ nsDOMDeviceStorageCursor::~nsDOMDeviceStorageCursor()
{
}
void
nsDOMDeviceStorageCursor::GetStorageType(nsAString & aType)
{
aType = mFile->mStorageType;
}
NS_IMETHODIMP
nsDOMDeviceStorageCursor::GetType(nsACString & aType)
{
@ -876,7 +955,7 @@ nsDOMDeviceStorageCursor::Allow()
}
PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(this, mFile);
DeviceStorageEnumerationParams params(fullpath, mSince);
DeviceStorageEnumerationParams params(mFile->mStorageType, fullpath, mSince);
ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
return NS_OK;
}
@ -933,7 +1012,7 @@ nsDOMDeviceStorageCursor::IPDLRelease()
class PostStatResultEvent : public nsRunnable
{
public:
PostStatResultEvent(nsRefPtr<DOMRequest>& aRequest, int64_t aFreeBytes, int64_t aTotalBytes)
PostStatResultEvent(nsRefPtr<DOMRequest>& aRequest, uint64_t aFreeBytes, uint64_t aTotalBytes)
: mFreeBytes(aFreeBytes)
, mTotalBytes(aTotalBytes)
{
@ -951,9 +1030,7 @@ public:
#ifdef MOZ_WIDGET_GONK
nsresult rv = GetSDCardStatus(state);
if (NS_FAILED(rv)) {
mRequest->FireError(NS_ERROR_FAILURE);
mRequest = nullptr;
return NS_OK;
state.Assign(NS_LITERAL_STRING("unavailable"));
}
#endif
@ -969,7 +1046,7 @@ public:
}
private:
int64_t mFreeBytes, mTotalBytes;
uint64_t mFreeBytes, mTotalBytes;
nsString mState;
nsRefPtr<DOMRequest> mRequest;
};
@ -1144,14 +1221,15 @@ public:
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIRunnable> r;
uint64_t diskUsage = DeviceStorageFile::DirectoryDiskUsage(mFile->mFile);
int64_t freeSpace;
uint64_t diskUsage = 0;
DeviceStorageFile::DirectoryDiskUsage(mFile->mFile, &diskUsage);
int64_t freeSpace = 0;
nsresult rv = mFile->mFile->GetDiskSpaceAvailable(&freeSpace);
if (NS_FAILED(rv)) {
freeSpace = 0;
}
r = new PostStatResultEvent(mRequest, diskUsage, freeSpace);
r = new PostStatResultEvent(mRequest, freeSpace, diskUsage);
NS_DispatchToMainThread(r);
return NS_OK;
}
@ -1306,6 +1384,7 @@ public:
DeviceStorageAddParams params;
params.blobChild() = actor;
params.type() = mFile->mStorageType;
params.name() = mFile->mPath;
params.fullpath() = fullpath;
@ -1321,7 +1400,7 @@ public:
{
if (XRE_GetProcessType() != GeckoProcessType_Default) {
PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
DeviceStorageGetParams params(mFile->mPath, fullpath);
DeviceStorageGetParams params(mFile->mStorageType, mFile->mPath, fullpath);
ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
return NS_OK;
}
@ -1334,7 +1413,7 @@ public:
{
if (XRE_GetProcessType() != GeckoProcessType_Default) {
PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
DeviceStorageDeleteParams params(fullpath);
DeviceStorageDeleteParams params(mFile->mStorageType, fullpath);
ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
return NS_OK;
}
@ -1346,7 +1425,7 @@ public:
{
if (XRE_GetProcessType() != GeckoProcessType_Default) {
PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
DeviceStorageStatParams params(fullpath);
DeviceStorageStatParams params(mFile->mStorageType, fullpath);
ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
return NS_OK;
}
@ -1452,8 +1531,8 @@ nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType)
{
NS_ASSERTION(aWindow, "Must have a content dom");
SetRootFileForType(aType);
if (!mFile) {
SetRootDirectoryForType(aType);
if (!mRootDirectory) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -1530,7 +1609,7 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob *aBlob,
nsCOMPtr<nsIRunnable> r;
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile, aPath);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, aPath);
if (!dsf->IsSafePath()) {
r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_FILE_NAME, dsf);
@ -1578,7 +1657,7 @@ nsDOMDeviceStorage::GetInternal(const JS::Value & aPath,
JSString* jsstr = JS_ValueToString(aCx, aPath);
nsDependentJSString path;
if (!path.init(aCx, jsstr)) {
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
r = new PostErrorEvent(request,
POST_ERROR_EVENT_NON_STRING_TYPE_UNSUPPORTED,
dsf);
@ -1586,7 +1665,7 @@ nsDOMDeviceStorage::GetInternal(const JS::Value & aPath,
return NS_OK;
}
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile, path);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path);
dsf->SetEditable(aEditable);
if (!dsf->IsSafePath()) {
@ -1615,13 +1694,13 @@ nsDOMDeviceStorage::Delete(const JS::Value & aPath, JSContext* aCx, nsIDOMDOMReq
JSString* jsstr = JS_ValueToString(aCx, aPath);
nsDependentJSString path;
if (!path.init(aCx, jsstr)) {
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
r = new PostErrorEvent(request, POST_ERROR_EVENT_NON_STRING_TYPE_UNSUPPORTED, dsf);
NS_DispatchToMainThread(r);
return NS_OK;
}
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile, path);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path);
if (!dsf->IsSafePath()) {
r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_FILE_NAME, dsf);
@ -1645,7 +1724,7 @@ nsDOMDeviceStorage::Stat(nsIDOMDOMRequest** aRetval)
nsRefPtr<DOMRequest> request = new DOMRequest(win);
NS_ADDREF(*aRetval = request);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_STAT,
win,
mPrincipal,
@ -1729,7 +1808,7 @@ nsDOMDeviceStorage::EnumerateInternal(const JS::Value & aName,
since = ExtractDateFromOptions(aCx, aOptions);
}
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile, path);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path);
dsf->SetEditable(aEditable);
nsRefPtr<nsDOMDeviceStorageCursor> cursor = new nsDOMDeviceStorageCursor(win, mPrincipal,
@ -1780,7 +1859,7 @@ nsDOMDeviceStorage::DispatchMountChangeEvent(nsAString& aType)
nsCOMPtr<nsIDOMDeviceStorageChangeEvent> ce = do_QueryInterface(event);
nsresult rv = ce->InitDeviceStorageChangeEvent(NS_LITERAL_STRING("change"),
true, false,
NS_LITERAL_STRING(""),
NS_LITERAL_STRING(""),
aType);
if (NS_FAILED(rv)) {
return;
@ -1839,10 +1918,7 @@ nsDOMDeviceStorage::Observe(nsISupports *aSubject, const char *aTopic, const PRU
{
if (!strcmp(aTopic, "file-watcher-update")) {
nsCOMPtr<nsIFile> file = do_QueryInterface(aSubject);
if (!file) {
return NS_OK;
}
DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
Notify(NS_ConvertUTF16toUTF8(aData).get(), file);
return NS_OK;
}
@ -1884,34 +1960,34 @@ nsDOMDeviceStorage::Observe(nsISupports *aSubject, const char *aTopic, const PRU
}
nsresult
nsDOMDeviceStorage::Notify(const char* aReason, nsIFile* aFile)
nsDOMDeviceStorage::Notify(const char* aReason, DeviceStorageFile* aFile)
{
if (!mAllowedToWatchFile) {
return NS_OK;
}
if (!mFile) {
if (!mStorageType.Equals(aFile->mStorageType)) {
// Ignore this
return NS_OK;
}
if (!mRootDirectory) {
return NS_ERROR_FAILURE;
}
nsString rootpath;
nsresult rv = mFile->GetPath(rootpath);
nsresult rv = mRootDirectory->GetPath(rootpath);
if (NS_FAILED(rv)) {
return NS_OK;
}
nsString fullpath;
rv = aFile->GetPath(fullpath);
rv = aFile->mFile->GetPath(fullpath);
if (NS_FAILED(rv)) {
return NS_OK;
}
NS_ASSERTION(fullpath.Length() >= rootpath.Length(), "Root path longer than full path!");
if (!StringBeginsWith(fullpath, rootpath)) {
NS_WARNING("Observing a path outside of our root!");
return NS_OK;
}
nsAString::size_type len = rootpath.Length() + 1; // +1 for the trailing /
nsDependentSubstring newPath (fullpath, len, fullpath.Length() - len);
@ -1944,7 +2020,7 @@ nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
}
nsRefPtr<DOMRequest> request = new DOMRequest(win);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mFile);
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DeviceStorageRequest::DEVICE_STORAGE_REQUEST_WATCH,
win, mPrincipal, dsf, request, this, aListener);
NS_DispatchToMainThread(r);

View File

@ -44,10 +44,11 @@ class DeviceStorageFile MOZ_FINAL
public:
nsCOMPtr<nsIFile> mFile;
nsString mPath;
nsString mStorageType;
bool mEditable;
DeviceStorageFile(nsIFile* aFile, const nsAString& aPath);
DeviceStorageFile(nsIFile* aFile);
DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile, const nsAString& aPath);
DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile);
void SetPath(const nsAString& aPath);
void SetEditable(bool aEditable);
@ -56,6 +57,7 @@ public:
// we want to make sure that the names of file can't reach
// outside of the type of storage the user asked for.
bool IsSafePath();
bool IsType(nsAString& aType);
nsresult Remove();
nsresult Write(nsIInputStream* aInputStream);
@ -63,7 +65,7 @@ public:
void CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, uint64_t aSince = 0);
void collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, uint64_t aSince, nsAString& aRootPath);
static uint64_t DirectoryDiskUsage(nsIFile* aFile, uint64_t aSoFar = 0);
static void DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar);
private:
void NormalizeFilePath();
@ -105,6 +107,8 @@ public:
virtual bool Recv__delete__(const bool& allow);
virtual void IPDLRelease();
void GetStorageType(nsAString & aType);
private:
~nsDOMDeviceStorageCursor();

View File

@ -974,15 +974,17 @@ ContentChild::RecvLastPrivateDocShellDestroyed()
}
bool
ContentChild::RecvFilePathUpdate(const nsString& path, const nsCString& aReason)
ContentChild::RecvFilePathUpdate(const nsString& type, const nsString& path, const nsCString& aReason)
{
nsCOMPtr<nsIFile> file;
NS_NewLocalFile(path, false, getter_AddRefs(file));
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(type, file);
nsString reason;
CopyASCIItoUTF16(aReason, reason);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->NotifyObservers(file, "file-watcher-update", reason.get());
obs->NotifyObservers(dsf, "file-watcher-update", reason.get());
return true;
}

View File

@ -173,7 +173,7 @@ public:
virtual bool RecvLastPrivateDocShellDestroyed();
virtual bool RecvFilePathUpdate(const nsString& path, const nsCString& reason);
virtual bool RecvFilePathUpdate(const nsString& type, const nsString& path, const nsCString& reason);
virtual bool RecvFileSystemUpdate(const nsString& aFsName, const nsString& aName, const int32_t& aState);
#ifdef ANDROID

View File

@ -1036,14 +1036,12 @@ ContentParent::Observe(nsISupports* aSubject,
else if (!strcmp(aTopic, "file-watcher-update")) {
nsCString creason;
CopyUTF16toUTF8(aData, creason);
nsCOMPtr<nsIFile> file = do_QueryInterface(aSubject);
if (!file) {
return NS_OK;
}
DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
nsString path;
file->GetPath(path);
unused << SendFilePathUpdate(path, creason);
file->mFile->GetPath(path);
unused << SendFilePathUpdate(file->mStorageType, path, creason);
}
#ifdef MOZ_WIDGET_GONK
else if(!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {

View File

@ -72,11 +72,13 @@ struct FontListEntry {
struct DeviceStorageStatParams
{
nsString type;
nsString fullpath;
};
struct DeviceStorageAddParams
{
nsString type;
PBlob blob;
nsString name;
nsString fullpath;
@ -84,17 +86,20 @@ struct DeviceStorageAddParams
struct DeviceStorageGetParams
{
nsString type;
nsString name;
nsString fullpath;
};
struct DeviceStorageDeleteParams
{
nsString type;
nsString fullpath;
};
struct DeviceStorageEnumerationParams
{
nsString type;
nsString fullpath;
uint64_t since;
};
@ -237,7 +242,7 @@ child:
// Notify child that last-pb-context-exited notification was observed
LastPrivateDocShellDestroyed();
FilePathUpdate(nsString filepath, nsCString reasons);
FilePathUpdate(nsString type, nsString filepath, nsCString reasons);
FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState);

View File

@ -397,18 +397,49 @@ FunctionBox::FunctionBox(ObjectBox* traceListHead, JSObject *obj, ParseContext *
bufEnd(0),
ndefaults(0),
strictModeState(sms),
inWith(outerpc->parsingWith),
inWith(false), // initialized below
inGenexpLambda(false),
cxFlags(outerpc->sc->context) // the cxFlags are set in LeaveFunction
cxFlags(outerpc->sc->context) // the cxFlags are set in LeaveFunction
{
isFunctionBox = true;
if (!outerpc->sc->inFunction()) {
if (outerpc->parsingWith) {
// This covers cases that don't involve eval(). For example:
//
// with (o) { (function() { g(); })(); }
//
// In this case, |outerpc| corresponds to global code, and
// outerpc->parsingWith is true.
inWith = true;
} else if (!outerpc->sc->inFunction()) {
// This covers the case where a function is nested within an eval()
// within a |with| statement.
//
// with (o) { eval("(function() { g(); })();"); }
//
// In this case, |outerpc| corresponds to the eval(),
// outerpc->parsingWith is false because the eval() breaks the
// ParseContext chain, and |parent| is NULL (again because of the
// eval(), so we have to look at |outerpc|'s scopeChain.
//
JSObject *scope = outerpc->sc->scopeChain();
while (scope) {
if (scope->isWith())
inWith = true;
scope = scope->enclosingScope();
}
} else {
// This is like the above case, but for more deeply nested functions.
// For example:
//
// with (o) { eval("(function() { (function() { g(); })(); })();"); } }
//
// In this case, the inner anonymous function needs to inherit the
// setting of |inWith| from the outer one.
FunctionBox *parent = outerpc->sc->funbox();
if (parent && parent->inWith)
inWith = true;
}
}

View File

@ -189,7 +189,8 @@ struct ParseContext /* tree context for semantic checks */
exclude 'in' */
bool parsingWith:1; /* true while we are within a
with-statement or E4X filter-expression
in the current ParseContext chain */
in the current ParseContext chain
(which stops at the top-level or an eval() */
// Set when parsing a declaration-like destructuring pattern. This flag
// causes PrimaryExpr to create PN_NAME parse nodes for variable references

View File

@ -12,8 +12,6 @@
#include "mozilla/TypeTraits.h"
#include "jsapi.h"
#include "js/TemplateLib.h"
#include "js/Utility.h"
@ -236,6 +234,9 @@ typedef MutableHandle<Value> MutableHandleValue;
* rooted.
*/
typedef JSObject * RawObject;
typedef JSString * RawString;
extern mozilla::ThreadLocal<JSRuntime *> TlsRuntime;
/*
* By default, pointers should use the inheritance hierarchy to find their
@ -267,10 +268,10 @@ class RootedBase {};
template <typename T>
class Rooted : public RootedBase<T>
{
void init(JSContext *cx_)
void init(JSContext *cxArg)
{
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
ContextFriendFields *cx = ContextFriendFields::get(cx_);
ContextFriendFields *cx = ContextFriendFields::get(cxArg);
ThingRootKind kind = RootMethods<T>::kind();
this->stack = reinterpret_cast<Rooted<T>**>(&cx->thingGCRooters[kind]);
@ -281,7 +282,27 @@ class Rooted : public RootedBase<T>
#endif
}
void init(JSRuntime *rtArg)
{
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
RuntimeFriendFields *rt = const_cast<RuntimeFriendFields *>(RuntimeFriendFields::get(rtArg));
ThingRootKind kind = RootMethods<T>::kind();
this->stack = reinterpret_cast<Rooted<T>**>(&rt->thingGCRooters[kind]);
this->prev = *stack;
*stack = this;
JS_ASSERT(!RootMethods<T>::poisoned(ptr));
#endif
}
public:
Rooted() : ptr(RootMethods<T>::initial()) { init(JS::TlsRuntime); }
Rooted(const T &initial) : ptr(initial) { init(JS::TlsRuntime); }
Rooted(JSRuntime *rt) : ptr(RootMethods<T>::initial()) { init(rt); }
Rooted(JSRuntime *rt, T initial) : ptr(initial) { init(rt); }
Rooted(JSContext *cx) : ptr(RootMethods<T>::initial()) { init(cx); }
Rooted(JSContext *cx, T initial) : ptr(initial) { init(cx); }
@ -318,13 +339,11 @@ class Rooted : public RootedBase<T>
}
private:
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
Rooted<T> **stack, *prev;
#endif
T ptr;
Rooted() MOZ_DELETE;
Rooted(const Rooted &) MOZ_DELETE;
};

View File

@ -0,0 +1,32 @@
let expected = 'o!o!o!';
let actual = '';
// g is a function that needs an implicit |this| if called within a |with|
// statement. If we fail to provide that implicit |this|, it will append
// "[object global]" instead of "o!".
let o = {
g: function() { actual += this.toString(); },
toString: function() { return "o!"; }
}
// g's presence within the |with| is detected by simple tracking of |with|s
// during parsing.
with (o) {
(function() { g(); })();
}
// The eval() defeats the tracking of |with| during parsing. Instead, g's
// presence within the |with| is detected by looking at the scopeChain of the
// ParseContext.
with (o) {
eval("(function() { g(); })()");
}
// This is like the above case, but the knowledge of the |with| presence must
// be inherited by the inner function. This is the case that was missed in bug
// 786114.
with (o) {
eval("(function() { (function() { g(); })(); })()");
}
assertEq(actual, expected);

View File

@ -10,6 +10,7 @@
*/
#include "mozilla/FloatingPoint.h"
#include "mozilla/ThreadLocal.h"
#include <ctype.h>
#include <stdarg.h>
@ -719,6 +720,13 @@ JS_IsBuiltinFunctionConstructor(JSFunction *fun)
*/
static JSBool js_NewRuntimeWasCalled = JS_FALSE;
/*
* Thread Local Storage slot for storing the runtime for a thread.
*/
namespace JS {
mozilla::ThreadLocal<JSRuntime *> TlsRuntime;
}
static const JSSecurityCallbacks NullSecurityCallbacks = { };
JSRuntime::JSRuntime()
@ -877,6 +885,8 @@ JSRuntime::init(uint32_t maxbytes)
ownerThread_ = PR_GetCurrentThread();
#endif
JS::TlsRuntime.set(this);
#ifdef JS_METHODJIT_SPEW
JMCheckLogging();
#endif
@ -938,7 +948,9 @@ JSRuntime::init(uint32_t maxbytes)
JSRuntime::~JSRuntime()
{
JS_ASSERT(onOwnerThread());
#ifdef JS_THREADSAFE
clearOwnerThread();
#endif
delete_(debugScopes);
@ -994,7 +1006,10 @@ JSRuntime::setOwnerThread()
{
JS_ASSERT(ownerThread_ == (void *)0xc1ea12); /* "clear" */
JS_ASSERT(requestDepth == 0);
JS_ASSERT(js_NewRuntimeWasCalled);
JS_ASSERT(JS::TlsRuntime.get() == NULL);
ownerThread_ = PR_GetCurrentThread();
JS::TlsRuntime.set(this);
nativeStackBase = GetNativeStackBase();
if (nativeStackQuota)
JS_SetNativeStackQuota(this, nativeStackQuota);
@ -1003,9 +1018,11 @@ JSRuntime::setOwnerThread()
void
JSRuntime::clearOwnerThread()
{
JS_ASSERT(onOwnerThread());
assertValidThread();
JS_ASSERT(requestDepth == 0);
JS_ASSERT(js_NewRuntimeWasCalled);
ownerThread_ = (void *)0xc1ea12; /* "clear" */
JS::TlsRuntime.set(NULL);
nativeStackBase = 0;
#if JS_STACK_GROWTH_DIRECTION > 0
nativeStackLimit = UINTPTR_MAX;
@ -1014,10 +1031,20 @@ JSRuntime::clearOwnerThread()
#endif
}
JS_FRIEND_API(bool)
JSRuntime::onOwnerThread() const
JS_FRIEND_API(void)
JSRuntime::abortIfWrongThread() const
{
return ownerThread_ == PR_GetCurrentThread();
if (ownerThread_ != PR_GetCurrentThread())
MOZ_CRASH();
if (this != JS::TlsRuntime.get())
MOZ_CRASH();
}
JS_FRIEND_API(void)
JSRuntime::assertValidThread() const
{
JS_ASSERT(ownerThread_ == PR_GetCurrentThread());
JS_ASSERT(this == JS::TlsRuntime.get());
}
#endif /* JS_THREADSAFE */
@ -1054,6 +1081,9 @@ JS_NewRuntime(uint32_t maxbytes)
InitMemorySubsystem();
if (!JS::TlsRuntime.init())
return false;
js_NewRuntimeWasCalled = JS_TRUE;
}
@ -1101,7 +1131,7 @@ static void
StartRequest(JSContext *cx)
{
JSRuntime *rt = cx->runtime;
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
if (rt->requestDepth) {
rt->requestDepth++;
@ -1118,7 +1148,7 @@ static void
StopRequest(JSContext *cx)
{
JSRuntime *rt = cx->runtime;
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
JS_ASSERT(rt->requestDepth != 0);
if (rt->requestDepth != 1) {
rt->requestDepth--;
@ -1166,7 +1196,7 @@ JS_SuspendRequest(JSContext *cx)
{
#ifdef JS_THREADSAFE
JSRuntime *rt = cx->runtime;
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
unsigned saveDepth = rt->requestDepth;
if (!saveDepth)
@ -1186,7 +1216,7 @@ JS_ResumeRequest(JSContext *cx, unsigned saveDepth)
{
#ifdef JS_THREADSAFE
JSRuntime *rt = cx->runtime;
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
if (saveDepth == 0)
return;
JS_ASSERT(saveDepth >= 1);
@ -1202,7 +1232,7 @@ JS_PUBLIC_API(JSBool)
JS_IsInRequest(JSRuntime *rt)
{
#ifdef JS_THREADSAFE
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
return rt->requestDepth != 0;
#else
return false;
@ -1213,7 +1243,7 @@ JS_PUBLIC_API(JSBool)
JS_IsInSuspendedRequest(JSRuntime *rt)
{
#ifdef JS_THREADSAFE
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
return rt->suspendCount != 0;
#else
return false;
@ -7025,10 +7055,7 @@ JS_SetRuntimeThread(JSRuntime *rt)
extern JS_NEVER_INLINE JS_PUBLIC_API(void)
JS_AbortIfWrongThread(JSRuntime *rt)
{
#ifdef JS_THREADSAFE
if (!rt->onOwnerThread())
MOZ_CRASH();
#endif
rt->abortIfWrongThread();
}
#ifdef JS_GC_ZEAL

View File

@ -14,6 +14,9 @@
#include "mozilla/Attributes.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/StandardInteger.h"
#ifdef __cplusplus
# include "mozilla/ThreadLocal.h"
#endif
#include <stddef.h>
#include <stdio.h>
@ -3018,6 +3021,8 @@ JS_END_EXTERN_C
namespace JS {
extern mozilla::ThreadLocal<JSRuntime *> TlsRuntime;
inline bool
IsPoisonedId(jsid iden)
{

View File

@ -1364,39 +1364,6 @@ JSObject::makeDenseArraySlow(JSContext *cx, HandleObject obj)
}
#if JS_HAS_TOSOURCE
class ArraySharpDetector
{
JSContext *cx;
bool success;
bool alreadySeen;
bool sharp;
public:
ArraySharpDetector(JSContext *cx)
: cx(cx),
success(false),
alreadySeen(false),
sharp(false)
{}
bool init(HandleObject obj) {
success = js_EnterSharpObject(cx, obj, NULL, &alreadySeen, &sharp);
if (!success)
return false;
return true;
}
bool initiallySharp() const {
JS_ASSERT_IF(sharp, alreadySeen);
return sharp;
}
~ArraySharpDetector() {
if (success && !sharp)
js_LeaveSharpObject(cx, NULL);
}
};
JS_ALWAYS_INLINE bool
IsArray(const Value &v)
{
@ -1411,13 +1378,13 @@ array_toSource_impl(JSContext *cx, CallArgs args)
Rooted<JSObject*> obj(cx, &args.thisv().toObject());
RootedValue elt(cx);
ArraySharpDetector detector(cx);
if (!detector.init(obj))
AutoCycleDetector detector(cx, obj);
if (!detector.init())
return false;
StringBuffer sb(cx);
if (detector.initiallySharp()) {
if (detector.foundCycle()) {
if (!sb.append("[]"))
return false;
goto make_string;
@ -1481,52 +1448,6 @@ array_toSource(JSContext *cx, unsigned argc, Value *vp)
}
#endif
class AutoArrayCycleDetector
{
JSContext *cx;
JSObject *obj;
uint32_t genBefore;
BusyArraysSet::AddPtr hashPointer;
bool cycle;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
AutoArrayCycleDetector(JSContext *cx, JSObject *obj JS_GUARD_OBJECT_NOTIFIER_PARAM)
: cx(cx),
obj(obj),
cycle(true)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
}
bool init()
{
BusyArraysSet &set = cx->busyArrays;
hashPointer = set.lookupForAdd(obj);
if (!hashPointer) {
if (!set.add(hashPointer, obj))
return false;
cycle = false;
genBefore = set.generation();
}
return true;
}
~AutoArrayCycleDetector()
{
if (!cycle) {
if (genBefore == cx->busyArrays.generation())
cx->busyArrays.remove(hashPointer);
else
cx->busyArrays.remove(obj);
}
}
bool foundCycle() { return cycle; }
protected:
};
static bool
array_join_sub(JSContext *cx, CallArgs &args, bool locale)
{
@ -1539,7 +1460,7 @@ array_join_sub(JSContext *cx, CallArgs &args, bool locale)
if (!obj)
return false;
AutoArrayCycleDetector detector(cx, obj);
AutoCycleDetector detector(cx, obj);
if (!detector.init())
return false;
@ -1553,7 +1474,6 @@ array_join_sub(JSContext *cx, CallArgs &args, bool locale)
if (!GetLengthProperty(cx, obj, &length))
return false;
// Steps 4 and 5
RootedString sepstr(cx, NULL);
if (!locale && args.hasDefined(0)) {

View File

@ -63,6 +63,41 @@
using namespace js;
using namespace js::gc;
bool
js::AutoCycleDetector::init()
{
ObjectSet &set = cx->cycleDetectorSet;
hashsetAddPointer = set.lookupForAdd(obj);
if (!hashsetAddPointer) {
if (!set.add(hashsetAddPointer, obj))
return false;
cyclic = false;
hashsetGenerationAtInit = set.generation();
}
return true;
}
js::AutoCycleDetector::~AutoCycleDetector()
{
if (!cyclic) {
if (hashsetGenerationAtInit == cx->cycleDetectorSet.generation())
cx->cycleDetectorSet.remove(hashsetAddPointer);
else
cx->cycleDetectorSet.remove(obj);
}
}
void
js::TraceCycleDetectionSet(JSTracer *trc, js::ObjectSet &set)
{
for (js::ObjectSet::Enum e(set); !e.empty(); e.popFront()) {
JSObject *prior = e.front();
MarkObjectRoot(trc, const_cast<JSObject **>(&e.front()), "cycle detector table entry");
if (prior != e.front())
e.rekeyFront(e.front());
}
}
struct CallbackData
{
CallbackData(JSMallocSizeOfFun f) : mallocSizeOf(f), n(0) {}
@ -296,7 +331,7 @@ js::NewContext(JSRuntime *rt, size_t stackChunkSize)
JS_ASSERT(cx->findVersion() == JSVERSION_DEFAULT);
if (!cx->busyArrays.init()) {
if (!cx->cycleDetectorSet.init()) {
Foreground::delete_(cx);
return NULL;
}
@ -1069,7 +1104,7 @@ JSContext::JSContext(JSRuntime *rt)
defaultCompartmentObject_(NULL),
stack(thisDuringConstruction()),
parseMapPool_(NULL),
sharpObjectMap(thisDuringConstruction()),
cycleDetectorSet(thisDuringConstruction()),
argumentFormatMap(NULL),
lastMessage(NULL),
errorReporter(NULL),
@ -1376,7 +1411,7 @@ JSContext::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const
* ones have been found by DMD to be worth measuring. More stuff may be
* added later.
*/
return mallocSizeOf(this) + busyArrays.sizeOfExcludingThis(mallocSizeOf);
return mallocSizeOf(this) + cycleDetectorSet.sizeOfExcludingThis(mallocSizeOf);
}
void
@ -1390,8 +1425,7 @@ JSContext::mark(JSTracer *trc)
if (isExceptionPending())
MarkValueRoot(trc, &exception, "exception");
if (sharpObjectMap.depth > 0)
js_TraceSharpMap(trc, &sharpObjectMap);
TraceCycleDetectionSet(trc, cycleDetectorSet);
MarkValueRoot(trc, &iterValue, "iterValue");
}
@ -1404,7 +1438,7 @@ AutoCheckRequestDepth::AutoCheckRequestDepth(JSContext *cx)
: cx(cx)
{
JS_ASSERT(cx->runtime->requestDepth || cx->runtime->isHeapBusy());
JS_ASSERT(cx->runtime->onOwnerThread());
cx->runtime->assertValidThread();
cx->runtime->checkRequestDepth++;
}

View File

@ -43,27 +43,39 @@ JS_BEGIN_EXTERN_C
struct DtoaState;
JS_END_EXTERN_C
struct JSSharpInfo {
bool hasGen;
bool isSharp;
JSSharpInfo() : hasGen(false), isSharp(false) {}
};
typedef js::HashMap<JSObject *, JSSharpInfo> JSSharpTable;
struct JSSharpObjectMap {
unsigned depth;
uint32_t sharpgen;
JSSharpTable table;
JSSharpObjectMap(JSContext *cx) : depth(0), sharpgen(0), table(js::TempAllocPolicy(cx)) {
table.init();
}
};
namespace js {
typedef HashSet<JSObject *> ObjectSet;
/* Detects cycles when traversing an object graph. */
class AutoCycleDetector
{
JSContext *cx;
JSObject *obj;
bool cyclic;
uint32_t hashsetGenerationAtInit;
ObjectSet::AddPtr hashsetAddPointer;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
AutoCycleDetector(JSContext *cx, JSObject *obj
JS_GUARD_OBJECT_NOTIFIER_PARAM)
: cx(cx), obj(obj), cyclic(true)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoCycleDetector();
bool init();
bool foundCycle() { return cyclic; }
};
/* Updates references in the cycle detection set if the GC moves them. */
extern void
TraceCycleDetectionSet(JSTracer *trc, ObjectSet &set);
namespace mjit {
class JaegerRuntime;
}
@ -362,13 +374,15 @@ struct JSRuntime : js::RuntimeFriendFields
void *ownerThread() const { return ownerThread_; }
void clearOwnerThread();
void setOwnerThread();
JS_FRIEND_API(bool) onOwnerThread() const;
JS_FRIEND_API(void) abortIfWrongThread() const;
JS_FRIEND_API(void) assertValidThread() const;
private:
void *ownerThread_;
public:
#else
public:
bool onOwnerThread() const { return true; }
void abortIfWrongThread() const {}
void assertValidThread() const {}
#endif
/* Keeper of the contiguous stack used by all contexts in this thread. */
@ -1100,10 +1114,6 @@ VersionIsKnown(JSVersion version)
return VersionNumber(version) != JSVERSION_UNKNOWN;
}
typedef HashSet<JSObject *,
DefaultHasher<JSObject *>,
SystemAllocPolicy> BusyArraysSet;
inline void
FreeOp::free_(void* p) {
if (shouldFreeLater()) {
@ -1227,8 +1237,7 @@ struct JSContext : js::ContextFriendFields
public:
/* State for object and array toSource conversion. */
JSSharpObjectMap sharpObjectMap;
js::BusyArraysSet busyArrays;
js::ObjectSet cycleDetectorSet;
/* Argument formatter support for JS_{Convert,Push}Arguments{,VA}. */
JSArgumentFormatMap *argumentFormatMap;

View File

@ -183,25 +183,6 @@ JS_SetSourceHook(JSRuntime *rt, JS_SourceHook hook);
namespace js {
struct RuntimeFriendFields {
/*
* If non-zero, we were been asked to call the operation callback as soon
* as possible.
*/
volatile int32_t interrupt;
/* Limit pointer for checking native stack consumption. */
uintptr_t nativeStackLimit;
RuntimeFriendFields()
: interrupt(0),
nativeStackLimit(0) { }
static const RuntimeFriendFields *get(const JSRuntime *rt) {
return reinterpret_cast<const RuntimeFriendFields *>(rt);
}
};
inline JSRuntime *
GetRuntime(const JSContext *cx)
{

View File

@ -2599,7 +2599,7 @@ TriggerOperationCallback(JSRuntime *rt, gcreason::Reason reason)
void
TriggerGC(JSRuntime *rt, gcreason::Reason reason)
{
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
if (rt->isHeapBusy())
return;
@ -2612,7 +2612,7 @@ void
TriggerCompartmentGC(JSCompartment *comp, gcreason::Reason reason)
{
JSRuntime *rt = comp->rt;
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
if (rt->isHeapBusy())
return;
@ -2636,7 +2636,7 @@ void
MaybeGC(JSContext *cx)
{
JSRuntime *rt = cx->runtime;
JS_ASSERT(rt->onOwnerThread());
rt->assertValidThread();
if (rt->gcZeal() == ZealAllocValue || rt->gcZeal() == ZealPokeValue) {
PrepareForFullGC(rt);
@ -4824,6 +4824,16 @@ SetValidateGC(JSContext *cx, bool enabled)
#if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
JS_ALWAYS_INLINE void
CheckStackRootThings(uintptr_t *w, Rooted<void*> *rooter, bool *matched)
{
while (rooter) {
if (rooter->address() == static_cast<void*>(w))
*matched = true;
rooter = rooter->previous();
}
}
static void
CheckStackRoot(JSTracer *trc, uintptr_t *w)
{
@ -4837,21 +4847,17 @@ CheckStackRoot(JSTracer *trc, uintptr_t *w)
if (test == CGCT_VALID) {
bool matched = false;
JSRuntime *rt = trc->runtime;
for (ContextIter cx(rt); !cx.done(); cx.next()) {
for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
Rooted<void*> *rooter = cx->thingGCRooters[i];
while (rooter) {
if (rooter->address() == static_cast<void*>(w))
for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
CheckStackRootThings(w, rt->thingGCRooters[i], &matched);
for (ContextIter cx(rt); !cx.done(); cx.next()) {
CheckStackRootThings(w, cx->thingGCRooters[i], &matched);
SkipRoot *skip = cx->skipGCRooters;
while (skip) {
if (skip->contains(reinterpret_cast<uint8_t*>(w), sizeof(w)))
matched = true;
rooter = rooter->previous();
skip = skip->previous();
}
}
SkipRoot *skip = cx->skipGCRooters;
while (skip) {
if (skip->contains(reinterpret_cast<uint8_t*>(w), sizeof(w)))
matched = true;
skip = skip->previous();
}
}
if (!matched) {
/*

View File

@ -102,266 +102,24 @@ JS_ObjectToOuterObject(JSContext *cx, JSObject *obj_)
return GetOuterObject(cx, obj);
}
static bool
MarkSharpObjects(JSContext *cx, HandleObject obj, JSIdArray **idap, JSSharpInfo *value)
{
JS_CHECK_RECURSION(cx, return false);
JSIdArray *ida;
JSSharpObjectMap *map = &cx->sharpObjectMap;
JS_ASSERT(map->depth >= 1);
JSSharpInfo sharpid;
JSSharpTable::Ptr p = map->table.lookup(obj);
if (!p) {
if (!map->table.put(obj.get(), sharpid))
return false;
ida = JS_Enumerate(cx, obj);
if (!ida)
return false;
bool ok = true;
RootedId id(cx);
for (int i = 0, length = ida->length; i < length; i++) {
id = ida->vector[i];
RootedObject obj2(cx);
RootedShape prop(cx);
ok = JSObject::lookupGeneric(cx, obj, id, &obj2, &prop);
if (!ok)
break;
if (!prop)
continue;
bool hasGetter, hasSetter;
RootedValue value(cx), setter(cx);
if (obj2->isNative()) {
Shape *shape = (Shape *) prop;
hasGetter = shape->hasGetterValue();
hasSetter = shape->hasSetterValue();
if (hasGetter)
value = shape->getterValue();
if (hasSetter)
setter = shape->setterValue();
} else {
hasGetter = hasSetter = false;
}
if (hasSetter) {
/* Mark the getter, then set val to setter. */
if (hasGetter && value.isObject()) {
Rooted<JSObject*> vobj(cx, &value.toObject());
ok = MarkSharpObjects(cx, vobj, NULL, NULL);
if (!ok)
break;
}
value = setter;
} else if (!hasGetter) {
ok = JSObject::getGeneric(cx, obj, obj, id, &value);
if (!ok)
break;
}
if (value.isObject()) {
Rooted<JSObject*> vobj(cx, &value.toObject());
if (!MarkSharpObjects(cx, vobj, NULL, NULL)) {
ok = false;
break;
}
}
}
if (!ok || !idap)
JS_DestroyIdArray(cx, ida);
if (!ok)
return false;
} else {
if (!p->value.hasGen && !p->value.isSharp) {
p->value.hasGen = true;
}
sharpid = p->value;
ida = NULL;
}
if (idap)
*idap = ida;
if (value)
*value = sharpid;
return true;
}
bool
js_EnterSharpObject(JSContext *cx, HandleObject obj, JSIdArray **idap, bool *alreadySeen, bool *isSharp)
{
if (!JS_CHECK_OPERATION_LIMIT(cx))
return false;
*alreadySeen = false;
JSSharpObjectMap *map = &cx->sharpObjectMap;
JS_ASSERT_IF(map->depth == 0, map->table.count() == 0);
JS_ASSERT_IF(map->table.count() == 0, map->depth == 0);
JSSharpTable::Ptr p;
JSSharpInfo sharpid;
JSIdArray *ida = NULL;
/* From this point the control must flow either through out: or bad:. */
if (map->depth == 0) {
JS_KEEP_ATOMS(cx->runtime);
/*
* Although MarkSharpObjects tries to avoid invoking getters,
* it ends up doing so anyway under some circumstances; for
* example, if a wrapped object has getters, the wrapper will
* prevent MarkSharpObjects from recognizing them as such.
* This could lead to js_LeaveSharpObject being called while
* MarkSharpObjects is still working.
*
* Increment map->depth while we call MarkSharpObjects, to
* ensure that such a call doesn't free the hash table we're
* still using.
*/
map->depth = 1;
bool success = MarkSharpObjects(cx, obj, &ida, &sharpid);
JS_ASSERT(map->depth == 1);
map->depth = 0;
if (!success)
goto bad;
JS_ASSERT(!sharpid.isSharp);
if (!idap) {
JS_DestroyIdArray(cx, ida);
ida = NULL;
}
} else {
/*
* It's possible that the value of a property has changed from the
* first time the object's properties are traversed (when the property
* ids are entered into the hash table) to the second (when they are
* converted to strings), i.e., the JSObject::getProperty() call is not
* idempotent.
*/
p = map->table.lookup(obj);
if (!p) {
if (!map->table.put(obj.get(), sharpid))
goto bad;
goto out;
}
sharpid = p->value;
}
if (sharpid.isSharp || sharpid.hasGen)
*alreadySeen = true;
out:
if (!sharpid.isSharp) {
if (idap && !ida) {
ida = JS_Enumerate(cx, obj);
if (!ida)
goto bad;
}
map->depth++;
}
if (idap)
*idap = ida;
*isSharp = sharpid.isSharp;
return true;
bad:
/* Clean up the sharpObjectMap table on outermost error. */
if (map->depth == 0) {
JS_UNKEEP_ATOMS(cx->runtime);
map->sharpgen = 0;
map->table.clear();
}
return false;
}
void
js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
{
JSSharpObjectMap *map = &cx->sharpObjectMap;
JS_ASSERT(map->depth > 0);
if (--map->depth == 0) {
JS_UNKEEP_ATOMS(cx->runtime);
map->sharpgen = 0;
map->table.clear();
}
if (idap) {
if (JSIdArray *ida = *idap) {
JS_DestroyIdArray(cx, ida);
*idap = NULL;
}
}
}
void
js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
{
JS_ASSERT(map->depth > 0);
/*
* During recursive calls to MarkSharpObjects a non-native object or
* object with a custom getProperty method can potentially return an
* unrooted value or even cut from the object graph an argument of one of
* MarkSharpObjects recursive invocations. So we must protect map->table
* entries against GC.
*
* We can not simply use JSTempValueRooter to mark the obj argument of
* MarkSharpObjects during recursion as we have to protect *all* entries
* in JSSharpObjectMap including those that contains otherwise unreachable
* objects just allocated through custom getProperty. Otherwise newer
* allocations can re-use the address of an object stored in the hashtable
* confusing js_EnterSharpObject. So to address the problem we simply
* mark all objects from map->table.
*
* An alternative "proper" solution is to use JSTempValueRooter in
* MarkSharpObjects with code to remove during finalization entries
* with otherwise unreachable objects. But this is way too complex
* to justify spending efforts.
*/
for (JSSharpTable::Range r = map->table.all(); !r.empty(); r.popFront()) {
JSObject *tmp = r.front().key;
MarkObjectRoot(trc, &tmp, "sharp table entry");
JS_ASSERT(tmp == r.front().key);
}
}
#if JS_HAS_TOSOURCE
static JSBool
obj_toSource(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
bool comma = false;
const jschar *vchars;
size_t vlength;
Value *val;
JSString *gsop[2];
SkipRoot skipGsop(cx, &gsop, 2);
JS_CHECK_RECURSION(cx, return JS_FALSE);
Value localroot[4];
PodArrayZero(localroot);
AutoArrayRooter tvr(cx, ArrayLength(localroot), localroot);
/* If outermost, we need parentheses to be an expression, not a block. */
bool outermost = (cx->sharpObjectMap.depth == 0);
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
JSIdArray *ida;
bool alreadySeen = false;
bool isSharp = false;
if (!js_EnterSharpObject(cx, obj, &ida, &alreadySeen, &isSharp))
return false;
/* If outermost, we need parentheses to be an expression, not a block. */
bool outermost = (cx->cycleDetectorSet.count() == 0);
if (!ida) {
/*
* We've already seen obj, so don't serialize it again (particularly as
* we might recur in the process): just serialize an empty object.
*/
JS_ASSERT(alreadySeen);
AutoCycleDetector detector(cx, obj);
if (!detector.init())
return false;
if (detector.foundCycle()) {
JSString *str = js_NewStringCopyZ(cx, "{}");
if (!str)
return false;
@ -369,62 +127,36 @@ obj_toSource(JSContext *cx, unsigned argc, Value *vp)
return true;
}
JS_ASSERT(!isSharp);
if (alreadySeen) {
JSSharpTable::Ptr p = cx->sharpObjectMap.table.lookup(obj);
JS_ASSERT(p);
JS_ASSERT(!p->value.isSharp);
p->value.isSharp = true;
}
/* Automatically call js_LeaveSharpObject when we leave this frame. */
class AutoLeaveSharpObject {
JSContext *cx;
JSIdArray *ida;
public:
AutoLeaveSharpObject(JSContext *cx, JSIdArray *ida) : cx(cx), ida(ida) {}
~AutoLeaveSharpObject() {
js_LeaveSharpObject(cx, &ida);
}
} autoLeaveSharpObject(cx, ida);
StringBuffer buf(cx);
if (outermost && !buf.append('('))
return false;
if (!buf.append('{'))
return false;
/*
* We have four local roots for cooked and raw value GC safety. Hoist the
* "localroot + 2" out of the loop using the val local, which refers to
* the raw (unconverted, "uncooked") values.
*/
val = localroot + 2;
Value val[2];
PodArrayZero(val);
AutoArrayRooter tvr2(cx, ArrayLength(val), val);
RootedId id(cx);
for (int i = 0; i < ida->length; i++) {
/* Get strings for id and value and GC-root them via vp. */
id = ida->vector[i];
Rooted<JSLinearString*> idstr(cx);
JSString *gsop[2];
SkipRoot skipGsop(cx, &gsop, 2);
AutoIdVector idv(cx);
if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &idv))
return false;
bool comma = false;
for (size_t i = 0; i < idv.length(); ++i) {
RootedId id(cx, idv[i]);
RootedObject obj2(cx);
RootedShape prop(cx);
if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &prop))
return false;
/*
* Convert id to a value and then to a string. Decide early whether we
* prefer get/set or old getter/setter syntax.
*/
JSString *s = ToString(cx, IdToValue(id));
if (!s || !(idstr = s->ensureLinear(cx)))
RootedShape shape(cx);
if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &shape))
return false;
/* Decide early whether we prefer get/set or old getter/setter syntax. */
int valcnt = 0;
if (prop) {
if (shape) {
bool doGet = true;
if (obj2->isNative()) {
Shape *shape = (Shape *) prop;
unsigned attrs = shape->attributes();
if (attrs & JSPROP_GETTER) {
doGet = false;
@ -448,13 +180,22 @@ obj_toSource(JSContext *cx, unsigned argc, Value *vp)
}
}
/* Convert id to a linear string. */
RawString s = ToString(cx, IdToValue(id));
if (!s)
return false;
Rooted<JSLinearString*> idstr(cx, s->ensureLinear(cx));
if (!idstr)
return false;
/*
* If id is a string that's not an identifier, or if it's a negative
* integer, then it must be quoted.
*/
if (JSID_IS_ATOM(id)
? !IsIdentifier(idstr)
: (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
: (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0))
{
s = js_QuoteString(cx, idstr, jschar('\''));
if (!s || !(idstr = s->ensureLinear(cx)))
return false;
@ -469,14 +210,13 @@ obj_toSource(JSContext *cx, unsigned argc, Value *vp)
continue;
/* Convert val[j] to its canonical source form. */
JSString *valstr = js_ValueToSource(cx, val[j]);
RootedString valstr(cx, js_ValueToSource(cx, val[j]));
if (!valstr)
return false;
localroot[j].setString(valstr); /* local root */
vchars = valstr->getChars(cx);
const jschar *vchars = valstr->getChars(cx);
if (!vchars)
return false;
vlength = valstr->length();
size_t vlength = valstr->length();
/*
* Remove '(function ' from the beginning of valstr and ')' from the
@ -536,7 +276,7 @@ obj_toSource(JSContext *cx, unsigned argc, Value *vp)
if (outermost && !buf.append(')'))
return false;
JSString *str = buf.finishString();
RawString str = buf.finishString();
if (!str)
return false;
args.rval().setString(str);

View File

@ -1080,20 +1080,6 @@ class ValueArray {
ValueArray(js::Value *v, size_t c) : array(v), length(c) {}
};
/* For manipulating JSContext::sharpObjectMap. */
extern bool
js_EnterSharpObject(JSContext *cx, js::HandleObject obj, JSIdArray **idap, bool *alreadySeen, bool *isSharp);
extern void
js_LeaveSharpObject(JSContext *cx, JSIdArray **idap);
/*
* Mark objects stored in map if GC happens between js_EnterSharpObject
* and js_LeaveSharpObject. GC calls this when map->depth > 0.
*/
extern void
js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map);
extern JSBool
js_HasOwnPropertyHelper(JSContext *cx, js::LookupGenericOp lookup, js::HandleObject obj,
js::HandleId id, js::MutableHandleValue rval);

View File

@ -51,7 +51,6 @@ typedef struct JSArgumentFormatMap JSArgumentFormatMap;
typedef struct JSGCThing JSGCThing;
typedef struct JSGenerator JSGenerator;
typedef struct JSNativeEnumerator JSNativeEnumerator;
typedef struct JSSharpObjectMap JSSharpObjectMap;
typedef struct JSTryNote JSTryNote;
/* Friend "Advanced API" typedefs. */

View File

@ -290,6 +290,33 @@ struct ContextFriendFields {
#endif
};
struct RuntimeFriendFields {
/*
* If non-zero, we were been asked to call the operation callback as soon
* as possible.
*/
volatile int32_t interrupt;
/* Limit pointer for checking native stack consumption. */
uintptr_t nativeStackLimit;
#if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
/*
* Stack allocated GC roots for stack GC heap pointers, which may be
* overwritten if moved during a GC.
*/
Rooted<void*> *thingGCRooters[THING_ROOT_LIMIT];
#endif
RuntimeFriendFields()
: interrupt(0),
nativeStackLimit(0) { }
static const RuntimeFriendFields *get(const JSRuntime *rt) {
return reinterpret_cast<const RuntimeFriendFields *>(rt);
}
};
} /* namespace JS */
#endif /* __cplusplus */

View File

@ -1456,7 +1456,8 @@ mjit::Compiler::finishThisUp()
}
JSScript *script =
(from.inlineIndex == UINT32_MAX) ? outerScript : inlineFrames[from.inlineIndex]->script;
(from.inlineIndex == UINT32_MAX) ? outerScript.get()
: inlineFrames[from.inlineIndex]->script;
uint32_t codeOffset = from.ool
? masm.size() + from.returnOffset
: from.returnOffset;

View File

@ -23,15 +23,7 @@ function test()
q = [];
q.__defineGetter__("0", q.toString);
q[2] = q;
try
{
q.toSource();
throw new Error("didn't throw");
}
catch (e)
{
assertEq(e instanceof InternalError, true, "bad error: " + e);
}
assertEq(q.toSource(), "[\"\", , []]", "wrong string");
reportCompare(expect, actual, summary);

View File

@ -244,6 +244,10 @@ typedef uint64_t nsFrameState;
// placeholder. This can happen for two reasons: (1) the float was
// split, and this piece is the continuation, or (2) the entire float
// didn't fit on the page.
// Note that this bit is also shared by text frames for
// TEXT_FORCE_TRIM_WHITESPACE. That's OK because we only check the
// NS_FRAME_IS_PUSHED_FLOAT bit on frames which we already know are
// out-of-flow.
#define NS_FRAME_IS_PUSHED_FLOAT NS_FRAME_STATE_BIT(32)
// This bit acts as a loop flag for recursive paint server drawing.

View File

@ -21,6 +21,10 @@ class PropertyProvider;
// reflow
#define TEXT_HAS_NONCOLLAPSED_CHARACTERS NS_FRAME_STATE_BIT(31)
// This state bit is set on frames which are forced to trim their leading and
// trailing whitespaces
#define TEXT_FORCE_TRIM_WHITESPACE NS_FRAME_STATE_BIT(32)
#define TEXT_HAS_FONT_INFLATION NS_FRAME_STATE_BIT(61)
typedef nsFrame nsTextFrameBase;

View File

@ -196,6 +196,9 @@ NS_DECLARE_FRAME_PROPERTY(FontSizeInflationProperty, nullptr)
// nsTextFrame.h has
// #define TEXT_HAS_NONCOLLAPSED_CHARACTERS NS_FRAME_STATE_BIT(31)
// nsTextFrame.h has
// #define TEXT_FORCE_TRIM_WHITESPACE NS_FRAME_STATE_BIT(32)
// Set when this text frame is mentioned in the userdata for the
// uninflated textrun property
#define TEXT_IN_UNINFLATED_TEXTRUN_USER_DATA NS_FRAME_STATE_BIT(60)
@ -7612,7 +7615,8 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
length = newLineOffset + 1 - offset;
}
}
if (atStartOfLine && !textStyle->WhiteSpaceIsSignificant()) {
if ((atStartOfLine && !textStyle->WhiteSpaceIsSignificant()) ||
(GetStateBits() & TEXT_FORCE_TRIM_WHITESPACE)) {
// Skip leading whitespace. Make sure we don't skip a 'pre-line'
// newline if there is one.
int32_t skipLength = newLineOffset >= 0 ? length - 1 : length;
@ -7770,7 +7774,8 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
bool usedHyphenation;
gfxFloat trimmedWidth = 0;
gfxFloat availWidth = aAvailableWidth;
bool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant();
bool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant() ||
(GetStateBits() & TEXT_FORCE_TRIM_WHITESPACE);
int32_t unusedOffset;
gfxBreakPriority breakPriority;
aLineLayout.GetLastOptionalBreakPosition(&unusedOffset, &breakPriority);
@ -7839,11 +7844,12 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
// the line. (If we actually do end up at the end of the line, we'll have
// to trim it off again in TrimTrailingWhiteSpace, and we'd like to avoid
// having to re-do it.)
if (brokeText) {
if (brokeText ||
(GetStateBits() & TEXT_FORCE_TRIM_WHITESPACE)) {
// We're definitely going to break so our trailing whitespace should
// definitely be timmed. Record that we've already done it.
// definitely be trimmed. Record that we've already done it.
AddStateBits(TEXT_TRIMMED_TRAILING_WHITESPACE);
} else {
} else if (!(GetStateBits() & TEXT_FORCE_TRIM_WHITESPACE)) {
// We might not be at the end of the line. (Note that even if this frame
// ends in breakable whitespace, it might not be at the end of the line
// because it might be followed by breakable, but preformatted, whitespace.)

View File

@ -11,6 +11,7 @@
#include "nsContentUtils.h"
#include "nsCSSFrameConstructor.h"
#include "nsMathMLTokenFrame.h"
#include "nsTextFrame.h"
nsIFrame*
NS_NewMathMLTokenFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
@ -68,6 +69,7 @@ nsMathMLTokenFrame::GetMathMLFrameType()
else if(style.EqualsLiteral("invariant")) {
nsAutoString data;
nsContentUtils::GetNodeTextContent(mContent, false, data);
data.CompressWhitespace();
eMATHVARIANT variant = nsMathMLOperators::LookupInvariantChar(data);
switch (variant) {
@ -85,35 +87,19 @@ nsMathMLTokenFrame::GetMathMLFrameType()
return eMathMLFrameType_UprightIdentifier;
}
static void
CompressWhitespace(nsIContent* aContent)
void
nsMathMLTokenFrame::ForceTrimChildTextFrames()
{
uint32_t numKids = aContent->GetChildCount();
for (uint32_t kid = 0; kid < numKids; kid++) {
nsIContent* cont = aContent->GetChildAt(kid);
if (cont && cont->IsNodeOfType(nsINode::eTEXT)) {
nsAutoString text;
cont->AppendTextTo(text);
text.CompressWhitespace();
cont->SetText(text, false); // not meant to be used if notify is needed
// Set flags on child text frames to force them to trim their leading and
// trailing whitespaces.
for (nsIFrame* childFrame = GetFirstPrincipalChild(); childFrame;
childFrame = childFrame->GetNextSibling()) {
if (childFrame->GetType() == nsGkAtoms::textFrame) {
childFrame->AddStateBits(TEXT_FORCE_TRIM_WHITESPACE);
}
}
}
NS_IMETHODIMP
nsMathMLTokenFrame::Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow)
{
// leading and trailing whitespace doesn't count -- bug 15402
// brute force removal for people who do <mi> a </mi> instead of <mi>a</mi>
// XXX the best fix is to skip these in nsTextFrame
CompressWhitespace(aContent);
// let the base class do its Init()
return nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
}
NS_IMETHODIMP
nsMathMLTokenFrame::SetInitialChildList(ChildListID aListID,
nsFrameList& aChildList)
@ -123,11 +109,41 @@ nsMathMLTokenFrame::SetInitialChildList(ChildListID aListID,
if (NS_FAILED(rv))
return rv;
ForceTrimChildTextFrames();
SetQuotes(false);
ProcessTextData();
return rv;
}
NS_IMETHODIMP
nsMathMLTokenFrame::AppendFrames(ChildListID aListID,
nsFrameList& aChildList)
{
nsresult rv = nsMathMLContainerFrame::AppendFrames(aListID, aChildList);
if (NS_FAILED(rv))
return rv;
ForceTrimChildTextFrames();
return rv;
}
NS_IMETHODIMP
nsMathMLTokenFrame::InsertFrames(ChildListID aListID,
nsIFrame* aPrevFrame,
nsFrameList& aChildList)
{
nsresult rv = nsMathMLContainerFrame::InsertFrames(aListID, aPrevFrame,
aChildList);
if (NS_FAILED(rv))
return rv;
ForceTrimChildTextFrames();
return rv;
}
nsresult
nsMathMLTokenFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
@ -297,6 +313,7 @@ nsMathMLTokenFrame::SetTextStyle()
// Get the text content that we enclose and its length
nsAutoString data;
nsContentUtils::GetNodeTextContent(mContent, false, data);
data.CompressWhitespace();
int32_t length = data.Length();
if (!length)
return false;

View File

@ -34,15 +34,19 @@ public:
virtual eMathMLFrameType GetMathMLFrameType();
NS_IMETHOD
Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow);
NS_IMETHOD
SetInitialChildList(ChildListID aListID,
nsFrameList& aChildList);
NS_IMETHOD
AppendFrames(ChildListID aListID,
nsFrameList& aChildList);
NS_IMETHOD
InsertFrames(ChildListID aListID,
nsIFrame* aPrevFrame,
nsFrameList& aChildList);
NS_IMETHOD
Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
@ -83,6 +87,8 @@ protected:
// helper to set the quotes of <ms>
void SetQuotes(bool aNotify);
void ForceTrimChildTextFrames();
};
#endif /* nsMathMLTokentFrame_h___ */

View File

@ -122,6 +122,7 @@ nsMathMLmoFrame::ProcessTextData()
nsAutoString data;
nsContentUtils::GetNodeTextContent(mContent, false, data);
data.CompressWhitespace();
int32_t length = data.Length();
PRUnichar ch = (length == 0) ? kNullCh : data[0];

View File

@ -104,3 +104,8 @@ fails == mstyle-5.xhtml mstyle-5-ref.xhtml # See bug 569125#c29
== maction-dynamic-2.html maction-dynamic-2-ref.html
== mo-lspace-rspace.html mo-lspace-rspace-ref.html
== maction-dynamic-3.html maction-dynamic-3-ref.html
== whitespace-trim-1.html whitespace-trim-1-ref.html
== whitespace-trim-2.html whitespace-trim-2-ref.html
== whitespace-trim-3.html whitespace-trim-3-ref.html
fails == whitespace-trim-4.html whitespace-trim-4-ref.html # Bug 560100
== whitespace-trim-5.html whitespace-trim-5-ref.html

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
<math><mo minsize="10em">(</mo></math>

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
<math><mo minsize="10em"> ( </mo></math>

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
<math><mi>(</mi></math>

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
<math><mi> ( </mi></math>

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
<math><mi mathvariant="italic">&#x210e;</mi></math>

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
<math><mi mathvariant="italic"> &#x210e; </mi></math>

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
<math><ms>x</ms></math>

View File

@ -0,0 +1,2 @@
<!DOCTYPE html>
<math><ms> x </ms></math>

View File

@ -0,0 +1,5 @@
<!DOCTYPE html>
<math><mtext>|</mtext><mi>x</mi><mtext>|</mtext></math>
<math><mtext>|</mtext><mn>x</mn><mtext>|</mtext></math>
<math><mtext>|</mtext><mo>x</mo><mtext>|</mtext></math>
<math><mtext>|</mtext><mtext>x</mtext><mtext>|</mtext></math>

View File

@ -0,0 +1,5 @@
<!DOCTYPE html>
<math><mtext>|</mtext><mi> x </mi><mtext>|</mtext></math>
<math><mtext>|</mtext><mn> x </mn><mtext>|</mtext></math>
<math><mtext>|</mtext><mo> x </mo><mtext>|</mtext></math>
<math><mtext>|</mtext><mtext> x </mtext><mtext>|</mtext></math>

View File

@ -14,7 +14,6 @@ import org.mozilla.gecko.sync.ThreadPool;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.config.AccountPickler;
import org.mozilla.gecko.sync.config.ClientRecordTerminator;
import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters;
import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity;
import android.accounts.AbstractAccountAuthenticator;
@ -222,7 +221,7 @@ public class SyncAuthenticatorService extends Service {
* Account disappeared.
*/
@Override
public Bundle getAccountRemovalAllowed(final AccountAuthenticatorResponse response, final Account account)
public Bundle getAccountRemovalAllowed(final AccountAuthenticatorResponse response, Account account)
throws NetworkErrorException {
Bundle result = super.getAccountRemovalAllowed(response, account);
@ -237,11 +236,13 @@ public class SyncAuthenticatorService extends Service {
return result;
}
final String accountName = account.name;
// Delete the Account pickle in the background.
ThreadPool.run(new Runnable() {
@Override
public void run() {
Logger.info(LOG_TAG, "Account named " + account.name + " being removed; " +
Logger.info(LOG_TAG, "Account named " + accountName + " being removed; " +
"deleting saved pickle file '" + Constants.ACCOUNT_PICKLE_FILENAME + "'.");
try {
AccountPickler.deletePickle(mContext, Constants.ACCOUNT_PICKLE_FILENAME);
@ -252,26 +253,34 @@ public class SyncAuthenticatorService extends Service {
}
});
// Bug 770785: delete the Account's client record in the background. We want
// to get the Account's data synchronously, though, since it is possible the
// Account object will be invalid by the time the Runnable executes. We
// don't need to worry about accessing prefs too early since deleting the
// Account doesn't remove them -- at least, not yet.
SyncAccountParameters tempParams = null;
try {
tempParams = SyncAccounts.blockingFromAndroidAccountV0(mContext, AccountManager.get(mContext), account);
} catch (Exception e) {
// Do nothing. Null parameters are handled in the Runnable.
}
final SyncAccountParameters params = tempParams;
// Bug 770785: delete the Account's client record in the background. We
// want to get the Account's data synchronously, though, since it is
// possible the Account object will be invalid by the time the Runnable
// executes. We don't need to worry about accessing prefs too early since
// deleting the Account doesn't remove them -- at least, not yet. We would
// prefer to use SyncAccounts.blockingFromAndroidAccountV0, but that
// hangs, possibly because the Account Manager doesn't appreciate giving
// out an auth token while deleting the account.
final AccountManager accountManager = AccountManager.get(mContext);
final String password = accountManager.getPassword(account);
final String serverURL = accountManager.getUserData(account, Constants.OPTION_SERVER);
ThreadPool.run(new Runnable() {
@Override
public void run() {
Logger.info(LOG_TAG, "Account named " + account.name + " being removed; " +
Logger.info(LOG_TAG, "Account named " + accountName + " being removed; " +
"deleting client record from server.");
if (params == null || params.username == null || params.password == null) {
String encodedUsername;
try {
encodedUsername = Utils.usernameFromAccount(accountName);
} catch (Exception e) {
Logger.warn(LOG_TAG, "Got exception deleting client record from server; ignoring.", e);
return;
}
if (accountName == null || encodedUsername == null || password == null || serverURL == null) {
Logger.warn(LOG_TAG, "Account parameters were null; not deleting client record from server.");
return;
}
@ -287,7 +296,7 @@ public class SyncAuthenticatorService extends Service {
SharedPreferences prefs;
try {
prefs = Utils.getSharedPreferences(mContext, product, params.username, params.serverURL, profile, version);
prefs = Utils.getSharedPreferences(mContext, product, encodedUsername, serverURL, profile, version);
} catch (Exception e) {
Logger.warn(LOG_TAG, "Caught exception fetching preferences; not deleting client record from server.", e);
return;
@ -307,7 +316,7 @@ public class SyncAuthenticatorService extends Service {
}
try {
ClientRecordTerminator.deleteClientRecord(params.username, params.password, clusterURL, clientGuid);
ClientRecordTerminator.deleteClientRecord(encodedUsername, password, clusterURL, clientGuid);
} catch (Exception e) {
Logger.warn(LOG_TAG, "Got exception deleting client record from server; ignoring.", e);
}

View File

@ -20,7 +20,7 @@
// 2) #include IPDL boilerplate, and then undef LOG so our LOG wins.
// 3) nsNetModule.cpp does its own crazy stuff with #including prlog.h
// multiple times; allow it to define ALLOW_LATE_NSHTTP_H_INCLUDE to bypass
// check.
// check.
#if defined(PR_LOG) && !defined(ALLOW_LATE_NSHTTP_H_INCLUDE)
#error "If nsHttp.h #included it must come before any IPDL-generated files or other files that #include prlog.h"
#endif
@ -181,7 +181,7 @@ struct nsHttp
}
// Declare all atoms
//
//
// The atom names and values are stored in nsHttpAtomList.h and are brought
// to you by the magic of C preprocessing. Add new atoms to nsHttpAtomList
// and all support logic will be auto-generated.
@ -203,8 +203,8 @@ PRTimeToSeconds(PRTime t_usec)
#define NowInSeconds() PRTimeToSeconds(PR_Now())
// round q-value to one decimal place; return most significant digit as uint.
#define QVAL_TO_UINT(q) ((unsigned int) ((q + 0.05) * 10.0))
// Round q-value to 2 decimal places; return 2 most significant digits as uint.
#define QVAL_TO_UINT(q) ((unsigned int) ((q + 0.005) * 100.0))
#define HTTP_LWS " \t"
#define HTTP_HEADER_VALUE_SEPS HTTP_LWS ","

View File

@ -234,7 +234,7 @@ nsHttpHandler::Init()
if (prefBranch) {
prefBranch->AddObserver(HTTP_PREF_PREFIX, this, true);
prefBranch->AddObserver(UA_PREF_PREFIX, this, true);
prefBranch->AddObserver(INTL_ACCEPT_LANGUAGES, this, true);
prefBranch->AddObserver(INTL_ACCEPT_LANGUAGES, this, true);
prefBranch->AddObserver(NETWORK_ENABLEIDN, this, true);
prefBranch->AddObserver(BROWSER_PREF("disk_cache_ssl"), this, true);
prefBranch->AddObserver(DONOTTRACK_HEADER_ENABLED, this, true);
@ -292,8 +292,8 @@ nsHttpHandler::Init()
// Bring alive the objects in the http-protocol-startup category
NS_CreateServicesFromCategory(NS_HTTP_STARTUP_CATEGORY,
static_cast<nsISupports*>(static_cast<void*>(this)),
NS_HTTP_STARTUP_TOPIC);
NS_HTTP_STARTUP_TOPIC);
mObserverService = mozilla::services::GetObserverService();
if (mObserverService) {
mObserverService->AddObserver(this, "profile-change-net-teardown", true);
@ -303,7 +303,7 @@ nsHttpHandler::Init()
mObserverService->AddObserver(this, "net:prune-dead-connections", true);
mObserverService->AddObserver(this, "net:failed-to-process-uri-content", true);
}
return NS_OK;
}
@ -363,10 +363,10 @@ nsHttpHandler::AddStandardRequestHeaders(nsHttpHeaderArray *request,
// transparent proxies) can result.
//
// However, we need to send something so that we can use keepalive
// with HTTP/1.0 servers/proxies. We use "Proxy-Connection:" when
// with HTTP/1.0 servers/proxies. We use "Proxy-Connection:" when
// we're talking to an http proxy, and "Connection:" otherwise.
// We no longer send the Keep-Alive request header.
NS_NAMED_LITERAL_CSTRING(close, "close");
NS_NAMED_LITERAL_CSTRING(keepAlive, "keep-alive");
@ -435,7 +435,7 @@ nsHttpHandler::GetCookieService()
return mCookieService;
}
nsresult
nsresult
nsHttpHandler::GetIOService(nsIIOService** result)
{
NS_ADDREF(*result = mIOService);
@ -452,7 +452,7 @@ nsHttpHandler::Get32BitsOfPseudoRandom()
// 15 or 31 bits are common amounts.
PR_STATIC_ASSERT(RAND_MAX >= 0xfff);
#if RAND_MAX < 0xffffU
return ((uint16_t) rand() << 20) |
(((uint16_t) rand() & 0xfff) << 8) |
@ -521,10 +521,10 @@ nsHttpHandler::BuildUserAgent()
// preallocate to worst-case size, which should always be better
// than if we didn't preallocate at all.
mUserAgent.SetCapacity(mLegacyAppName.Length() +
mLegacyAppVersion.Length() +
mUserAgent.SetCapacity(mLegacyAppName.Length() +
mLegacyAppVersion.Length() +
#ifndef UA_SPARE_PLATFORM
mPlatform.Length() +
mPlatform.Length() +
#endif
mOscpu.Length() +
mMisc.Length() +
@ -674,7 +674,7 @@ nsHttpHandler::InitUserAgentComponents()
}
#elif defined (XP_UNIX)
struct utsname name;
int ret = uname(&name);
if (ret >= 0) {
nsCAutoString buf;
@ -981,7 +981,7 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
if (NS_SUCCEEDED(rv))
SetAccept(accept);
}
if (PREF_CHANGED(HTTP_PREF("accept-encoding"))) {
nsXPIDLCString acceptEncodings;
rv = prefs->GetCharPref(HTTP_PREF("accept-encoding"),
@ -1034,7 +1034,7 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
mEnforceAssocReq = cVar;
}
// enable Persistent caching for HTTPS - bug#205921
// enable Persistent caching for HTTPS - bug#205921
if (PREF_CHANGED(BROWSER_PREF("disk_cache_ssl"))) {
cVar = false;
rv = prefs->GetBoolPref(BROWSER_PREF("disk_cache_ssl"), &cVar);
@ -1141,7 +1141,7 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
pls->ToString(getter_Copies(uval));
if (uval)
SetAcceptLanguages(NS_ConvertUTF16toUTF8(uval).get());
}
}
}
//
@ -1221,7 +1221,7 @@ PrepareAcceptLanguages(const char *i_AcceptLanguages, nsACString &o_AcceptLangua
if (!i_AcceptLanguages)
return NS_OK;
uint32_t n, size, wrote;
uint32_t n, count_n, size, wrote;
double q, dec;
char *p, *p2, *token, *q_Accept, *o_Accept;
const char *comma;
@ -1244,7 +1244,7 @@ PrepareAcceptLanguages(const char *i_AcceptLanguages, nsACString &o_AcceptLangua
*q_Accept = '\0';
q = 1.0;
dec = q / (double) n;
n = 0;
count_n = 0;
p2 = q_Accept;
for (token = nsCRT::strtok(o_Accept, ",", &p);
token != (char *) 0;
@ -1257,12 +1257,28 @@ PrepareAcceptLanguages(const char *i_AcceptLanguages, nsACString &o_AcceptLangua
*trim = '\0';
if (*token != '\0') {
comma = n++ != 0 ? "," : ""; // delimiter if not first item
comma = count_n++ != 0 ? "," : ""; // delimiter if not first item
uint32_t u = QVAL_TO_UINT(q);
if (u < 10)
wrote = PR_snprintf(p2, available, "%s%s;q=0.%u", comma, token, u);
else
// Only display q-value if less than 1.00.
if (u < 100) {
const char *qval_str;
// With a small number of languages, one decimal place is enough to prevent duplicate q-values.
// Also, trailing zeroes do not add any information, so they can be removed.
if ((n < 10) || ((u % 10) == 0)) {
u = (u + 5) / 10;
qval_str = "%s%s;q=0.%u";
} else {
// Values below 10 require zero padding.
qval_str = "%s%s;q=0.%02u";
}
wrote = PR_snprintf(p2, available, qval_str, comma, token, u);
} else {
wrote = PR_snprintf(p2, available, "%s%s", comma, token);
}
q -= dec;
p2 += wrote;
available -= wrote;
@ -1278,7 +1294,7 @@ PrepareAcceptLanguages(const char *i_AcceptLanguages, nsACString &o_AcceptLangua
}
nsresult
nsHttpHandler::SetAcceptLanguages(const char *aAcceptLanguages)
nsHttpHandler::SetAcceptLanguages(const char *aAcceptLanguages)
{
nsCAutoString buf;
nsresult rv = PrepareAcceptLanguages(aAcceptLanguages, buf);
@ -1288,14 +1304,14 @@ nsHttpHandler::SetAcceptLanguages(const char *aAcceptLanguages)
}
nsresult
nsHttpHandler::SetAccept(const char *aAccept)
nsHttpHandler::SetAccept(const char *aAccept)
{
mAccept = aAccept;
return NS_OK;
}
nsresult
nsHttpHandler::SetAcceptEncodings(const char *aAcceptEncodings)
nsHttpHandler::SetAcceptEncodings(const char *aAcceptEncodings)
{
mAcceptEncodings = aAcceptEncodings;
return NS_OK;
@ -1373,10 +1389,10 @@ nsHttpHandler::NewChannel(nsIURI *uri, nsIChannel **result)
return NewProxiedChannel(uri, nullptr, result);
}
NS_IMETHODIMP
NS_IMETHODIMP
nsHttpHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
{
// don't override anything.
// don't override anything.
*_retval = false;
return NS_OK;
}
@ -1394,7 +1410,7 @@ nsHttpHandler::NewProxiedChannel(nsIURI *uri,
LOG(("nsHttpHandler::NewProxiedChannel [proxyInfo=%p]\n",
givenProxyInfo));
nsCOMPtr<nsProxyInfo> proxyInfo;
if (givenProxyInfo) {
proxyInfo = do_QueryInterface(givenProxyInfo);
@ -1532,7 +1548,7 @@ nsHttpHandler::Observe(nsISupports *subject,
if (uri && mConnMgr)
mConnMgr->ReportFailedToProcess(uri);
}
return NS_OK;
}
@ -1547,7 +1563,7 @@ nsHttpHandler::SpeculativeConnect(nsIURI *aURI,
bool isStsHost = false;
if (!stss)
return NS_OK;
nsCOMPtr<nsIURI> clone;
if (NS_SUCCEEDED(stss->IsStsURI(aURI, &isStsHost)) && isStsHost) {
if (NS_SUCCEEDED(aURI->Clone(getter_AddRefs(clone)))) {
@ -1657,7 +1673,7 @@ nsHttpsHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval)
NS_IMETHODIMP
nsHttpsHandler::AllowPort(int32_t aPort, const char *aScheme, bool *_retval)
{
// don't override anything.
// don't override anything.
*_retval = false;
return NS_OK;
}

View File

@ -0,0 +1,88 @@
//
// HTTP Accept-Language header test
//
const Cc = Components.classes;
const Ci = Components.interfaces;
var testpath = "/bug672448";
function run_test() {
let intlPrefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch("intl.");
// Save old value of preference for later.
let oldPref = intlPrefs.getCharPref("accept_languages");
// Test different numbers of languages, to test different fractions.
let acceptLangTests = [
"qaa", // 1
"qaa,qab", // 2
"qaa,qab,qac,qad", // 4
"qaa,qab,qac,qad,qae,qaf,qag,qah", // 8
"qaa,qab,qac,qad,qae,qaf,qag,qah,qai,qaj", // 10
"qaa,qab,qac,qad,qae,qaf,qag,qah,qai,qaj,qak", // 11
"qaa,qab,qac,qad,qae,qaf,qag,qah,qai,qaj,qak,qal,qam,qan,qao,qap,qaq,qar,qas,qat,qau", // 21
oldPref, // Restore old value of preference (and test it).
];
let acceptLangTestsNum = acceptLangTests.length;
for (let i = 0; i < acceptLangTestsNum; i++) {
// Set preference to test value.
intlPrefs.setCharPref("accept_languages", acceptLangTests[i]);
// Test value.
test_accepted_languages();
}
}
function test_accepted_languages() {
let channel = setupChannel(testpath);
let AcceptLanguage = channel.getRequestHeader("Accept-Language");
let acceptedLanguages = AcceptLanguage.split(",");
let acceptedLanguagesLength = acceptedLanguages.length;
for (let i = 0; i < acceptedLanguagesLength; i++) {
let acceptedLanguage, qualityValue;
try {
// The q-value must conform to the definition in HTTP/1.1 Section 3.9.
[_, acceptedLanguage, qualityValue] = acceptedLanguages[i].trim().match(/^([a-z0-9_-]*?)(?:;q=(1(?:\.0{0,3})?|0(?:\.[0-9]{0,3})))?$/i);
} catch(e) {
do_throw("Invalid language tag or quality value: " + e);
}
if (i == 0) {
// The first language shouldn't have a quality value.
do_check_eq(qualityValue, undefined);
} else {
let decimalPlaces;
// When the number of languages is small, we keep the quality value to only one decimal place.
// Otherwise, it can be up to two decimal places.
if (acceptedLanguagesLength < 10) {
do_check_true(qualityValue.length == 3);
decimalPlaces = 1;
} else {
do_check_true(qualityValue.length >= 3);
do_check_true(qualityValue.length <= 4);
decimalPlaces = 2;
}
// All the other languages should have an evenly-spaced quality value.
do_check_eq(parseFloat(qualityValue).toFixed(decimalPlaces), (1.0 - ((1 / acceptedLanguagesLength) * i)).toFixed(decimalPlaces));
}
}
}
function setupChannel(path) {
let ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
let chan = ios.newChannel("http://localhost:4444" + path, "", null);
chan.QueryInterface(Ci.nsIHttpChannel);
return chan;
}

View File

@ -42,7 +42,7 @@ skip-if = os == "android"
[test_bug455311.js]
[test_bug455598.js]
[test_bug468426.js]
# Bug 675039: test hangs consistently on Android
# Bug 675039: test hangs consistently on Android
skip-if = os == "android"
[test_bug468594.js]
[test_bug470716.js]
@ -53,7 +53,7 @@ skip-if = os == "android"
[test_bug490095.js]
[test_bug504014.js]
[test_bug510359.js]
# Bug 675039: test hangs consistently on Android
# Bug 675039: test hangs consistently on Android
skip-if = os == "android"
[test_bug515583.js]
[test_bug528292.js]
@ -71,16 +71,16 @@ skip-if = os == "android"
[test_bug652761.js]
[test_bug651100.js]
# Bug 675044: test fails consistently on Android
fail-if = os == "android"
fail-if = os == "android"
[test_bug654926.js]
# Bug 675049: test fails consistently on Android
fail-if = os == "android"
fail-if = os == "android"
[test_bug654926_doom_and_read.js]
# Bug 675049: test fails consistently on Android
fail-if = os == "android"
fail-if = os == "android"
[test_bug654926_test_seek.js]
# Bug 675049: test fails consistently on Android
fail-if = os == "android"
fail-if = os == "android"
[test_bug659569.js]
[test_bug660066.js]
[test_bug667907.js]
@ -103,28 +103,28 @@ fail-if = os == "android"
[test_extract_charset_from_content_type.js]
[test_force_sniffing.js]
[test_fallback_no-cache-entry_canceled.js]
# Bug 675039: test hangs consistently on Android
# Bug 675039: test hangs consistently on Android
skip-if = os == "android"
[test_fallback_no-cache-entry_passing.js]
# Bug 675039: test hangs consistently on Android
# Bug 675039: test hangs consistently on Android
skip-if = os == "android"
[test_fallback_redirect-to-different-origin_canceled.js]
# Bug 675039: test hangs consistently on Android
# Bug 675039: test hangs consistently on Android
skip-if = os == "android"
[test_fallback_redirect-to-different-origin_passing.js]
# Bug 675039: test hangs consistently on Android
# Bug 675039: test hangs consistently on Android
skip-if = os == "android"
[test_fallback_request-error_canceled.js]
# Bug 675039: test hangs consistently on Android
# Bug 675039: test hangs consistently on Android
skip-if = os == "android"
[test_fallback_request-error_passing.js]
# Bug 675039: test hangs consistently on Android
# Bug 675039: test hangs consistently on Android
skip-if = os == "android"
[test_fallback_response-error_canceled.js]
# Bug 675039: test hangs consistently on Android
# Bug 675039: test hangs consistently on Android
skip-if = os == "android"
[test_fallback_response-error_passing.js]
# Bug 675039: test hangs consistently on Android
# Bug 675039: test hangs consistently on Android
skip-if = os == "android"
[test_file_partial_inputstream.js]
[test_file_protocol.js]
@ -132,6 +132,7 @@ skip-if = os == "android"
[test_gre_resources.js]
[test_gzipped_206.js]
[test_head.js]
[test_header_Accept-Language.js]
[test_headers.js]
[test_http_headers.js]
[test_httpcancel.js]

View File

@ -37,7 +37,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=366936
"webkit01.dat",
"webkit02.dat"];
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
if (!isOSXLion) {
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
if (!isOSXLion && !isOSXMtnLion) {
parserDatFiles.push("tests19.dat");
parserDatFiles.push("tests7.dat");
}

View File

@ -50,7 +50,7 @@ AbstractFile.prototype = {
* bytes read and the number of bytes read. Note that |buffer| may be
* larger than the number of bytes actually read.
*/
readAll: function readAll(bytes) {
read: function read(bytes) {
if (bytes == null) {
bytes = this.stat().size;
}
@ -88,7 +88,7 @@ AbstractFile.prototype = {
options.offset);
let pos = 0;
while (pos < bytes) {
let chunkSize = this.read(pointer, bytes - pos, options);
let chunkSize = this._read(pointer, bytes - pos, options);
if (chunkSize == 0) {
break;
}
@ -117,7 +117,7 @@ AbstractFile.prototype = {
* @return {number} The number of bytes actually written, which may be
* less than |bytes| if |options.once| was set.
*/
writeFrom: function writeFrom(buffer, bytes, options) {
write: function write(buffer, bytes, options) {
options = options || noOptions;
let pointer = AbstractFile.normalizeToPointer(buffer, bytes,
@ -125,7 +125,7 @@ AbstractFile.prototype = {
let pos = 0;
while (pos < bytes) {
let chunkSize = this.write(pointer, bytes - pos, options);
let chunkSize = this._write(pointer, bytes - pos, options);
pos += chunkSize;
pointer = exports.OS.Shared.offsetBy(pointer, chunkSize);
}

View File

@ -92,7 +92,7 @@
* the end of the file has been reached.
* @throws {OS.File.Error} In case of I/O error.
*/
File.prototype.read = function read(buffer, nbytes, options) {
File.prototype._read = function _read(buffer, nbytes, options) {
return throw_on_negative("read",
UnixFile.read(this.fd, buffer, nbytes)
);
@ -111,7 +111,7 @@
* @return {number} The number of bytes effectively written.
* @throws {OS.File.Error} In case of I/O error.
*/
File.prototype.write = function write(buffer, nbytes, options) {
File.prototype._write = function _write(buffer, nbytes, options) {
return throw_on_negative("write",
UnixFile.write(this.fd, buffer, nbytes)
);
@ -159,6 +159,16 @@
return new File.Info(gStatData);
};
/**
* Flushes the file's buffers and causes all buffered data
* to be written.
*
* @throws {OS.File.Error} In case of I/O error.
*/
File.prototype.flush = function flush() {
throw_on_negative("flush", UnixFile.fsync(this.fd));
};
// Constant used to normalize options.
const noOptions = {};
@ -401,8 +411,8 @@
if (!pump_buffer || pump_buffer.length < bufSize) {
pump_buffer = new (ctypes.ArrayType(ctypes.char))(bufSize);
}
let read = source.read.bind(source);
let write = dest.write.bind(dest);
let read = source._read.bind(source);
let write = dest._write.bind(dest);
// Perform actual copy
let total_read = 0;
while (true) {

View File

@ -320,6 +320,11 @@
/*nbytes_wr*/Types.DWORD.out_ptr,
/*overlapped*/Types.void_t.inout_ptr // FIXME: Implement?
);
WinFile.FlushFileBuffers =
declareFFI("FlushFileBuffers", ctypes.winapi_abi,
/*return*/ Types.zero_or_nothing,
/*file*/ Types.HANDLE);
};
exports.OS.Win.File._init = init;
})(this);

View File

@ -111,7 +111,7 @@
* the end of the file has been reached.
* @throws {OS.File.Error} In case of I/O error.
*/
File.prototype.read = function read(buffer, nbytes, options) {
File.prototype._read = function _read(buffer, nbytes, options) {
// |gBytesReadPtr| is a pointer to |gBytesRead|.
throw_on_zero("read",
WinFile.ReadFile(this.fd, buffer, nbytes, gBytesReadPtr, null)
@ -132,7 +132,7 @@
* @return {number} The number of bytes effectively written.
* @throws {OS.File.Error} In case of I/O error.
*/
File.prototype.write = function write(buffer, nbytes, options) {
File.prototype._write = function _write(buffer, nbytes, options) {
// |gBytesWrittenPtr| is a pointer to |gBytesWritten|.
throw_on_zero("write",
WinFile.WriteFile(this.fd, buffer, nbytes, gBytesWrittenPtr, null)
@ -182,6 +182,16 @@
return new File.Info(gFileInfo);
};
/**
* Flushes the file's buffers and causes all buffered data
* to be written.
*
* @throws {OS.File.Error} In case of I/O error.
*/
File.prototype.flush = function flush() {
throw_on_zero("flush", WinFile.FlushFileBuffers(this.fd));
};
// Constant used to normalize options.
const noOptions = {};

View File

@ -19,8 +19,8 @@ self.onmessage = function onmessage_start(msg) {
test_offsetby();
test_open_existing_file();
test_open_non_existing_file();
test_flush_open_file();
test_copy_existing_file();
test_read_write_file();
test_readall_writeall_file();
test_position();
test_move_file();
@ -160,6 +160,20 @@ function test_open_non_existing_file()
ok(exn.becauseNoSuchFile, "test_open_non_existing_file: Exception confirms that the file does not exist");
}
/**
* Test that to ensure that |foo.flush()| does not
* cause an error, where |foo| is an open file.
*/
function test_flush_open_file()
{
ok(true, "Starting test_flush_open_file");
let tmp = "test_flush.tmp";
let file = OS.File.open(tmp, {create: true, write: true});
file.flush();
file.close();
OS.File.remove(tmp);
}
/**
* Utility function for comparing two files (or a prefix of two files).
*
@ -178,90 +192,38 @@ function compare_files(test, sourcePath, destPath, prefix)
let source = OS.File.open(sourcePath);
let dest = OS.File.open(destPath);
ok(true, "Files are open");
let array1 = new (ctypes.ArrayType(ctypes.char, 4096))();
let array2 = new (ctypes.ArrayType(ctypes.char, 4096))();
ok(true, "Arrays are created");
let pos = 0;
while (true) {
ok(true, "Position: "+pos);
let chunkSize;
let sourceResult, destResult;
try {
if (prefix != undefined) {
chunkSize = Math.min(4096, prefix - pos);
sourceResult = source.read(prefix);
destResult = dest.read(prefix);
} else {
chunkSize = 4096;
sourceResult = source.read();
destResult = dest.read();
}
let bytes_read1 = source.read(array1, chunkSize);
let bytes_read2 = dest.read(array2, chunkSize);
is (bytes_read1 > 0, bytes_read2 > 0,
test + ": Both files contain data or neither does " +
bytes_read1 + ", " + bytes_read2);
if ((bytes_read1 > 0) != (bytes_read2 > 0)) {
break;
}
if (bytes_read1 == 0) {
break;
}
let bytes;
if (bytes_read1 != bytes_read2) {
// This would be surprising, but theoretically possible with a
// remote file system, I believe.
bytes = Math.min(bytes_read1, bytes_read2);
pos += bytes;
source.setPosition(pos, OS.File.POS_START);
dest.setPosition(pos, OS.File.POS_START);
} else {
bytes = bytes_read1;
pos += bytes;
}
for (let i = 0; i < bytes; ++i) {
if (array1[i] != array2[i]) {
ok(false, test + ": Files do not match at position " + i
+ " (" + array1[i] + "/ " + array2[i] + ")");
is(sourceResult.bytes, destResult.bytes, test + ": Both files have the same size");
let sourceView = new Uint8Array(sourceResult.buffer);
let destView = new Uint8Array(destResult.buffer);
for (let i = 0; i < sourceResult.bytes; ++i) {
if (sourceView[i] != destView[i]) {
is(sourceView[i] != destView[i], test + ": Comparing char " + i);
break;
}
}
} finally {
source.close();
dest.close();
}
source.close();
dest.close();
ok(true, test + ": Comparison complete");
}
/**
* Test basic read/write through an ArrayBuffer
*/
function test_read_write_file()
{
let src_file_name = "chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js";
let tmp_file_name = "test_osfile_front.tmp";
ok(true, "Starting test_read_write_file");
let source = OS.File.open(src_file_name);
let dest = OS.File.open(tmp_file_name, {write: true, trunc:true});
let buf = new ArrayBuffer(4096);
for (let bytesAvailable = source.read(buf, 4096);
bytesAvailable != 0;
bytesAvailable = source.read(buf, 4096)) {
let bytesWritten = dest.write(buf, bytesAvailable);
if (bytesWritten != bytesAvailable) {
is(bytesWritten, bytesAvailable, "test_read_write_file: writing all bytes");
}
}
ok(true, "test_read_write_file: copy complete");
source.close();
dest.close();
compare_files("test_read_write_file", src_file_name, tmp_file_name);
OS.File.remove(tmp_file_name);
}
function test_readall_writeall_file()
{
let src_file_name = "chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js";
let tmp_file_name = "test_osfile_front.tmp";
ok(true, "Starting test_readall_writeall_file");
// readTo, ArrayBuffer
// read, ArrayBuffer
let source = OS.File.open(src_file_name);
let dest = OS.File.open(tmp_file_name, {write: true, trunc:true});
@ -271,7 +233,7 @@ function test_readall_writeall_file()
let readResult = source.readTo(buf, size);
is(readResult, size, "test_readall_writeall_file: read the right number of bytes");
dest.writeFrom(buf, size);
dest.write(buf, size);
ok(true, "test_readall_writeall_file: copy complete (manual allocation)");
source.close();
@ -280,7 +242,7 @@ function test_readall_writeall_file()
compare_files("test_readall_writeall_file (manual allocation)", src_file_name, tmp_file_name);
OS.File.remove(tmp_file_name);
// readTo, C buffer
// read, C buffer
source = OS.File.open(src_file_name);
dest = OS.File.open(tmp_file_name, {write: true, trunc:true});
@ -289,7 +251,7 @@ function test_readall_writeall_file()
readResult = source.readTo(ptr, size);
is(readResult, size, "test_readall_writeall_file: read the right number of bytes (C buffer)");
dest.writeFrom(ptr, readResult, {bytes: size});
dest.write(ptr, readResult, {bytes: size});
ok(true, "test_readall_writeall_file: copy complete (C buffer)");
source.close();
@ -308,7 +270,7 @@ function test_readall_writeall_file()
readResult = source.readTo(buf, LEFT, {offset: OFFSET});
is(readResult, LEFT, "test_readall_writeall_file: read the right number of bytes (with offset)");
dest.writeFrom(buf, LEFT, {offset: OFFSET});
dest.write(buf, LEFT, {offset: OFFSET});
is(dest.stat().size, LEFT, "test_readall_writeall_file: wrote the right number of bytes (with offset)");
ok(true, "test_readall_writeall_file: copy complete (with offset)");
@ -328,7 +290,7 @@ function test_readall_writeall_file()
readResult = source.readTo(ptr, LEFT, {offset: OFFSET});
is(readResult, LEFT, "test_readall_writeall_file: read the right number of bytes (with offset)");
dest.writeFrom(ptr, LEFT, {offset: OFFSET});
dest.write(ptr, LEFT, {offset: OFFSET});
is(dest.stat().size, LEFT, "test_readall_writeall_file: wrote the right number of bytes (with offset)");
ok(true, "test_readall_writeall_file: copy complete (with offset)");
@ -338,12 +300,12 @@ function test_readall_writeall_file()
compare_files("test_readall_writeall_file (with offset)", src_file_name, tmp_file_name, LEFT);
OS.File.remove(tmp_file_name);
// readAll
// read
buf = new ArrayBuffer(size);
source = OS.File.open(src_file_name);
dest = OS.File.open(tmp_file_name, {write: true, trunc:true});
readResult = source.readAll();
readResult = source.read();
is(readResult.bytes, size, "test_readall_writeall_file: read the right number of bytes (auto allocation)");
dest.write(readResult.buffer, readResult.bytes);
@ -527,7 +489,7 @@ function test_info() {
let file = OS.File.open(filename, {trunc: true});
let buf = new ArrayBuffer(size);
file.write(buf, size);
file._write(buf, size);
file.close();
// Test OS.File.stat on new file

View File

@ -9,6 +9,9 @@ const Cr = Components.results;
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
"resource://gre/modules/DeferredTask.jsm");
const PERMS_FILE = 0644;
const PERMS_DIRECTORY = 0755;
@ -279,64 +282,6 @@ function ENSURE_WARN(assertion, message, resultCode) {
throw Components.Exception(message, resultCode);
}
/**
* A delayed treatment that may be delayed even further.
*
* Use this for instance if you write data to a file and you expect
* that you may have to rewrite data very soon afterwards. With
* |Lazy|, the treatment is delayed by a few milliseconds and,
* should a new change to the data occur during this period,
* 1/ only the final version of the data is actually written;
* 2/ a further grace delay is added to take into account other
* changes.
*
* @constructor
* @param {Function} code The code to execute after the delay.
* @param {number=} delay An optional delay, in milliseconds.
*/
function Lazy(code, delay) {
LOG("Lazy: Creating a Lazy");
this._callback =
(function(){
code();
this._timer = null;
}).bind(this);
this._delay = delay || LAZY_SERIALIZE_DELAY;
this._timer = null;
}
Lazy.prototype = {
/**
* Start (or postpone) treatment.
*/
go: function Lazy_go() {
LOG("Lazy_go: starting");
if (this._timer) {
LOG("Lazy_go: reusing active timer");
this._timer.delay = this._delay;
} else {
LOG("Lazy_go: creating timer");
this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._timer.
initWithCallback(this._callback,
this._delay,
Ci.nsITimer.TYPE_ONE_SHOT);
}
},
/**
* Perform any postponed treatment immediately.
*/
flush: function Lazy_flush() {
LOG("Lazy_flush: starting");
if (!this._timer) {
return;
}
this._timer.cancel();
this._timer = null;
this._callback();
}
};
function loadListener(aChannel, aEngine, aCallback) {
this._channel = aChannel;
this._bytes = [];
@ -3825,10 +3770,10 @@ var engineMetadataService = {
let istream = converter.convertToInputStream(JSON.stringify(store));
NetUtil.asyncCopy(istream, ostream, callback);
}
this._lazyWriter = new Lazy(writeCommit);
this._lazyWriter = new DeferredTask(writeCommit, LAZY_SERIALIZE_DELAY);
}
LOG("epsCommit: (re)setting timer");
this._lazyWriter.go();
this._lazyWriter.start();
},
_lazyWriter: null,
};

View File

@ -0,0 +1,86 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const EXPORTED_SYMBOLS = ["DeferredTask"];
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
/**
* A task that will run after a delay. Multiple attempts to run the same task
* before the delay will be coalesced
*
* Use this for instance if you write data to a file and you expect that you
* may have to rewrite data very soon afterwards. With |DeferredTask|, the
* task is delayed by a few milliseconds and, should a new change to the data
* occur during this period,
* 1/ only the final version of the data is actually written;
* 2/ a further grace delay is added to take into account other changes.
*
* Constructor
* @param aDelay The delay time in milliseconds.
* @param aCallback The code to execute after the delay.
*/
function DeferredTask(aCallback, aDelay) {
this._callback = function onCallback() {
this._timer = null;
try {
aCallback();
} catch(e) {
Cu.reportError(e);
}
}.bind(this);
this._delay = aDelay;
}
DeferredTask.prototype = {
/* Callback */
_callback: null,
/* Delay */
_delay: null,
/* Timer */
_timer: null,
/**
* Check the current task state.
* @returns true if pending, false otherwise.
*/
get isPending() {
return (this._timer != null);
},
/**
* Start (or postpone) task.
*/
start: function DeferredTask_start() {
if (this._timer) {
this._timer.delay = this._delay;
} else {
this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._timer.initWithCallback(
this._callback, this._delay, Ci.nsITimer.TYPE_ONE_SHOT);
}
},
/**
* Perform any postponed task immediately.
*/
flush: function DeferredTask_flush() {
if (this._timer) {
this.cancel();
this._callback();
}
},
/**
* Cancel any pending task.
*/
cancel: function DeferredTask_cancel() {
if (this._timer) {
this._timer.cancel();
this._timer = null;
}
}
};

View File

@ -58,13 +58,14 @@ TEST_DIRS += tests
EXTRA_JS_MODULES = \
debug.js \
DeferredTask.jsm \
Dict.jsm \
Geometry.jsm \
InlineSpellChecker.jsm \
PopupNotifications.jsm \
Dict.jsm \
PageMenu.jsm \
PropertyListUtils.jsm \
PopupNotifications.jsm \
PrivateBrowsingUtils.jsm \
PropertyListUtils.jsm \
Task.jsm \
$(NULL)

View File

@ -10,6 +10,7 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width; user-scalable=false;">
<title>about:buildconfig</title>
<link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css">
<style type="text/css">

View File

@ -26,6 +26,7 @@ MOCHITEST_BROWSER_FILES = \
browser_save_resend_postdata.js \
browser_browserDrop.js \
browser_Services.js \
browser_DeferredTask.js \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,166 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let DeferredTask =
Components.utils.import("resource://gre/modules/DeferredTask.jsm", {}).DeferredTask;
function test() {
waitForExplicitFinish();
nextTest();
}
function nextTest() {
let test = tests.shift();
if (test) {
info("Starting " + test.name);
executeSoon(test.bind(null, nextTest));
} else {
executeSoon(finish);
}
}
let tests = [
function checkRanOnce(next) {
// Test that a deferred task runs only once.
let deferredTaskCounter = 0;
let deferredTask = new DeferredTask(function checkDeferred() {
is(++deferredTaskCounter, 1, "Deferred task ran once");
if (deferredTaskCounter > 1)
return; // shouldn't happen!
next();
}, 100);
deferredTask.start();
deferredTask.start();
deferredTask.start();
},
function testOrdering(next) {
// Test the deferred task running order.
let ARan, BRan;
let deferredTaskA = new DeferredTask(function () {
info("Deferred task A running");
ARan = true;
ok(!BRan, "Deferred task B shouldn't have run yet");
}, 10);
let deferredTaskB = new DeferredTask(function () {
info("Deferred task B running");
BRan = true;
ok(ARan, "Deferred task A should have run already");
executeSoon(next);
}, 100);
deferredTaskA.start();
deferredTaskB.start();
},
function testFlush(next) {
// Test the flush method.
let deferedTaskCounter = 0;
let deferredTask = new DeferredTask(function () {
is(++deferedTaskCounter, 1, "Deferred task should only run once");
}, 10);
is(deferedTaskCounter, 0, "Deferred task shouldn't run right away");
deferredTask.flush();
is(deferedTaskCounter, 0, "Deferred task shouldn't run after flush() if it wasn't started");
deferredTask.start();
is(deferedTaskCounter, 0, "Deferred task shouldn't run immediately after start");
deferredTask.flush();
is(deferedTaskCounter, 1, "Deferred task should run after flush() after being started");
next();
},
function testCancel(next) {
// Test the cancel method.
let deferredTask1, deferredTask2, deferredTask3;
let deferredTask2Ran = false;
deferredTask1 = new DeferredTask(function () {
info("Deferred task 1 running");
deferredTask2.cancel();
info("Deferred task 2 canceled");
}, 10);
deferredTask2 = new DeferredTask(function () {
info("Deferred task 2 running");
deferredTask2Ran = true;
}, 100);
deferredTask3 = new DeferredTask(function () {
info("Deferred task 3 running");
is(deferredTask2Ran, false, "Deferred task 2 shouldn't run");
next();
}, 200);
deferredTask1.start();
deferredTask2.start();
deferredTask3.start();
},
function testIsPending(next) {
// Test the pending property.
let deferredTask1, deferredTask2;
let deferredTask1 = new DeferredTask(function () {
info("Deferred task 1 running");
is(deferredTask1.isPending, false, "Deferred task 1 shouldn't be pending");
is(deferredTask2.isPending, true, "Deferred task 2 should be pending");
}, 100);
let deferredTask2 = new DeferredTask(function () {
info("Deferred task 2 running");
is(deferredTask1.isPending, false, "Deferred task 1 shouldn't be pending");
is(deferredTask2.isPending, false, "Deferred task 2 shouldn't be pending");
next();
}, 200);
is(deferredTask1.isPending, false, "Deferred task 1 shouldn't be pending");
is(deferredTask2.isPending, false, "Deferred task 2 shouldn't be pending");
deferredTask1.start();
deferredTask2.start();
is(deferredTask1.isPending, true, "Deferred task 1 should be pending");
is(deferredTask2.isPending, true, "Deferred task 2 should be pending");
},
function testRestartingTask(next) {
// Test restarting a task from other task.
let deferredTask1, deferredTask2, deferredTask3;
let deferredTask1 = new DeferredTask(function () {
info("Deferred task 1 running");
is(deferredTask1.isPending, false, "Deferred task 1 shouldn't be pending");
is(deferredTask2.isPending, false, "Deferred task 2 shouldn't be pending");
is(deferredTask3.isPending, false, "Deferred task 3 shouldn't be pending");
next();
}, 100);
let deferredTask2 = new DeferredTask(function () {
info("Deferred task 2 running");
deferredTask1.start();
deferredTask3.start();
}, 50);
let deferredTask3 = new DeferredTask(function () {
info("Deferred task 3 running");
is(deferredTask1.isPending, true, "Deferred task 1 should be pending");
}, 75);
deferredTask1.start();
deferredTask2.start();
},
function testRestartItselfTask(next) {
// Test restarting a task from the same task callback.
let deferredTask;
let taskReRun = false;
let deferredTask = new DeferredTask(function () {
info("Deferred task running");
is(deferredTask.isPending, false, "Deferred task shouldn't be pending");
if (!taskReRun) {
taskReRun = true;
info("Deferred task restart");
deferredTask.start();
is(deferredTask.isPending, true, "Deferred task should be pending");
} else {
next();
}
}, 100);
deferredTask.start();
}
];

View File

@ -3,3 +3,5 @@ head =
tail =
[test_content_annotation.js]
# Bug 706751: test fails on 10.7 and 10.8
skip-if = os == "mac"

View File

@ -712,6 +712,12 @@ TISInputSourceWrapper::InitKeyEvent(NSEvent *aNativeKeyEvent,
UInt32 nativeKeyCode = [aNativeKeyEvent keyCode];
bool isPrintableKey = !TextInputHandler::IsSpecialGeckoKey(nativeKeyCode);
if (isPrintableKey &&
[aNativeKeyEvent type] != NSKeyDown &&
[aNativeKeyEvent type] != NSKeyUp) {
NS_WARNING("Why the printable key doesn't cause NSKeyDown or NSKeyUp?");
isPrintableKey = false;
}
// Decide what string will be input.
nsAutoString insertString;