Merge remote-tracking branch 'origin/master' into media-works

This commit is contained in:
2021-08-20 10:48:29 +08:00
106 changed files with 3298 additions and 1246 deletions
+1 -1
View File
@@ -273,7 +273,7 @@ var FullScreen = {
},
_cancelAnimation: function() {
window.mozCancelAnimationFrame(this._animationHandle);
window.cancelAnimationFrame(this._animationHandle);
this._animationHandle = 0;
clearTimeout(this._animationTimeout);
this._isAnimating = false;
+12 -43
View File
@@ -41,8 +41,6 @@
#include "mozilla/dom/WorkerPrivate.h"
#include "nsThreadUtils.h"
#include "mozilla/dom/FileListBinding.h"
namespace mozilla {
namespace dom {
@@ -538,13 +536,13 @@ File::GetLastModified(ErrorResult& aRv)
}
void
File::GetMozFullPath(nsAString& aFilename, ErrorResult& aRv)
File::GetMozFullPath(nsAString& aFilename, ErrorResult& aRv) const
{
mImpl->GetMozFullPath(aFilename, aRv);
}
void
File::GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv)
File::GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const
{
mImpl->GetMozFullPathInternal(aFileName, aRv);
}
@@ -742,7 +740,7 @@ BlobImplBase::GetPath(nsAString& aPath, ErrorResult& aRv)
}
void
BlobImplBase::GetMozFullPath(nsAString& aFileName, ErrorResult& aRv)
BlobImplBase::GetMozFullPath(nsAString& aFileName, ErrorResult& aRv) const
{
NS_ASSERTION(mIsFile, "Should only be called on files");
@@ -766,7 +764,7 @@ BlobImplBase::GetMozFullPath(nsAString& aFileName, ErrorResult& aRv)
}
void
BlobImplBase::GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv)
BlobImplBase::GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const
{
if (!mIsFile) {
aRv.Throw(NS_ERROR_FAILURE);
@@ -936,6 +934,13 @@ BlobImplBase::SetMutable(bool aMutable)
return rv;
}
/* static */ uint64_t
BlobImplBase::NextSerialNumber()
{
static Atomic<uint64_t> nextSerialNumber;
return nextSerialNumber++;
}
////////////////////////////////////////////////////////////////////////////
// BlobImplFile implementation
@@ -950,7 +955,7 @@ BlobImplFile::CreateSlice(uint64_t aStart, uint64_t aLength,
}
void
BlobImplFile::GetMozFullPathInternal(nsAString& aFilename, ErrorResult& aRv)
BlobImplFile::GetMozFullPathInternal(nsAString& aFilename, ErrorResult& aRv) const
{
NS_ASSERTION(mIsFile, "Should only be called on files");
aRv = mFile->GetPath(aFilename);
@@ -1225,42 +1230,6 @@ BlobImplTemporaryBlob::GetInternalStream(nsIInputStream** aStream,
stream.forget(aStream);
}
////////////////////////////////////////////////////////////////////////////
// FileList implementation
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFiles)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileList)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFileList)
NS_INTERFACE_MAP_ENTRY(nsIDOMFileList)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(FileList)
NS_IMPL_CYCLE_COLLECTING_RELEASE(FileList)
JSObject*
FileList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
{
return mozilla::dom::FileListBinding::Wrap(cx, this, aGivenProto);
}
NS_IMETHODIMP
FileList::GetLength(uint32_t* aLength)
{
*aLength = Length();
return NS_OK;
}
NS_IMETHODIMP
FileList::Item(uint32_t aIndex, nsISupports** aFile)
{
nsCOMPtr<nsIDOMBlob> file = Item(aIndex);
file.forget(aFile);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////
// BlobSet implementation
+44 -89
View File
@@ -21,7 +21,6 @@
#include "nsCycleCollectionParticipant.h"
#include "nsCOMPtr.h"
#include "nsIDOMBlob.h"
#include "nsIDOMFileList.h"
#include "nsIFile.h"
#include "nsIMutable.h"
#include "nsIXMLHttpRequest.h"
@@ -33,7 +32,7 @@
class nsIFile;
class nsIInputStream;
#define FILEIMPL_IID \
#define BLOBIMPL_IID \
{ 0xbccb3275, 0x6778, 0x4ac5, \
{ 0xaf, 0x03, 0x90, 0xed, 0x37, 0xad, 0xdf, 0x5d } }
@@ -282,9 +281,9 @@ public:
void GetPath(nsAString& aName, ErrorResult& aRv);
void GetMozFullPath(nsAString& aFilename, ErrorResult& aRv);
void GetMozFullPath(nsAString& aFilename, ErrorResult& aRv) const;
void GetMozFullPathInternal(nsAString& aName, ErrorResult& aRv);
void GetMozFullPathInternal(nsAString& aName, ErrorResult& aRv) const;
protected:
virtual bool HasFileInterface() const override { return true; }
@@ -301,7 +300,7 @@ private:
class BlobImpl : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(FILEIMPL_IID)
NS_DECLARE_STATIC_IID_ACCESSOR(BLOBIMPL_IID)
NS_DECL_THREADSAFE_ISUPPORTS
BlobImpl() {}
@@ -314,14 +313,22 @@ public:
virtual void SetLastModified(int64_t aLastModified) = 0;
virtual void GetMozFullPath(nsAString& aName, ErrorResult& aRv) = 0;
virtual void GetMozFullPath(nsAString& aName, ErrorResult& aRv) const = 0;
virtual void GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) = 0;
virtual void GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const = 0;
virtual uint64_t GetSize(ErrorResult& aRv) = 0;
virtual void GetType(nsAString& aType) = 0;
/**
* An effectively-unique serial number identifying this instance of FileImpl.
*
* Implementations should obtain a serial number from
* FileImplBase::NextSerialNumber().
*/
virtual uint64_t GetSerialNumber() const = 0;
already_AddRefed<BlobImpl>
Slice(const Optional<int64_t>& aStart, const Optional<int64_t>& aEnd,
const nsAString& aContentType, ErrorResult& aRv);
@@ -379,6 +386,7 @@ public:
* calling IsDirectory will MOZ_ASSERT.
*/
virtual void LookupAndCacheIsDirectory() = 0;
virtual void SetIsDirectory(bool aIsDir) = 0;
virtual bool IsDirectory() const = 0;
/**
@@ -397,7 +405,7 @@ protected:
virtual ~BlobImpl() {}
};
NS_DEFINE_STATIC_IID_ACCESSOR(BlobImpl, FILEIMPL_IID)
NS_DEFINE_STATIC_IID_ACCESSOR(BlobImpl, BLOBIMPL_IID)
class BlobImplBase : public BlobImpl
{
@@ -413,6 +421,7 @@ public:
, mStart(0)
, mLength(aLength)
, mLastModificationDate(aLastModifiedDate)
, mSerialNumber(NextSerialNumber())
{
// Ensure non-null mContentType by default
mContentType.SetIsVoid(false);
@@ -428,6 +437,7 @@ public:
, mStart(0)
, mLength(aLength)
, mLastModificationDate(INT64_MAX)
, mSerialNumber(NextSerialNumber())
{
// Ensure non-null mContentType by default
mContentType.SetIsVoid(false);
@@ -441,6 +451,7 @@ public:
, mStart(0)
, mLength(aLength)
, mLastModificationDate(INT64_MAX)
, mSerialNumber(NextSerialNumber())
{
// Ensure non-null mContentType by default
mContentType.SetIsVoid(false);
@@ -455,6 +466,7 @@ public:
, mStart(aStart)
, mLength(aLength)
, mLastModificationDate(INT64_MAX)
, mSerialNumber(NextSerialNumber())
{
NS_ASSERTION(aLength != UINT64_MAX,
"Must know length when creating slice");
@@ -470,10 +482,10 @@ public:
virtual void SetLastModified(int64_t aLastModified) override;
virtual void GetMozFullPath(nsAString& aName, ErrorResult& aRv) override;
virtual void GetMozFullPath(nsAString& aName, ErrorResult& aRv) const override;
virtual void GetMozFullPathInternal(nsAString& aFileName,
ErrorResult& aRv) override;
ErrorResult& aRv) const override;
virtual uint64_t GetSize(ErrorResult& aRv) override
{
@@ -482,6 +494,8 @@ public:
virtual void GetType(nsAString& aType) override;
virtual uint64_t GetSerialNumber() const override { return mSerialNumber; }
virtual already_AddRefed<BlobImpl>
CreateSlice(uint64_t aStart, uint64_t aLength,
const nsAString& aContentType, ErrorResult& aRv) override
@@ -551,6 +565,14 @@ public:
MOZ_ASSERT(false, "Why is this being called on a non-BlobImplFile?");
}
virtual void SetIsDirectory(bool aIsDir) override
{
MOZ_ASSERT(mIsFile,
"This should only be called when this object has been created "
"from an nsIFile to note that the nsIFile is a directory");
mDirState = aIsDir ? BlobDirState::eIsDir : BlobDirState::eIsNotDir;
}
/**
* Returns true if the nsIFile that this object wraps is a directory.
*/
@@ -558,7 +580,8 @@ public:
{
MOZ_ASSERT(mDirState != BlobDirState::eUnknownIfDir,
"Must only be used by callers for whom the code paths are "
"know to call LookupAndCacheIsDirectory()");
"know to call LookupAndCacheIsDirectory() or "
"SetIsDirectory()");
return mDirState == BlobDirState::eIsDir;
}
@@ -591,6 +614,13 @@ public:
protected:
virtual ~BlobImplBase() {}
/**
* Returns a new, effectively-unique serial number. This should be used
* by implementations to obtain a serial number for GetSerialNumber().
* The implementation is thread safe.
*/
static uint64_t NextSerialNumber();
indexedDB::FileInfo* GetFileInfo() const
{
NS_ASSERTION(IsStoredFile(), "Should only be called on stored files!");
@@ -612,6 +642,8 @@ protected:
int64_t mLastModificationDate;
const uint64_t mSerialNumber;
// Protected by IndexedDatabaseManager::FileMutex()
nsTArray<nsRefPtr<indexedDB::FileInfo>> mFileInfos;
};
@@ -871,7 +903,7 @@ public:
virtual int64_t GetLastModified(ErrorResult& aRv) override;
virtual void SetLastModified(int64_t aLastModified) override;
virtual void GetMozFullPathInternal(nsAString& aFullPath,
ErrorResult& aRv) override;
ErrorResult& aRv) const override;
virtual void GetInternalStream(nsIInputStream** aInputStream,
ErrorResult& aRv) override;
@@ -943,83 +975,6 @@ private:
bool mIsTemporary;
};
class FileList final : public nsIDOMFileList,
public nsWrapperCache
{
~FileList() {}
public:
explicit FileList(nsISupports *aParent) : mParent(aParent)
{
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FileList)
NS_DECL_NSIDOMFILELIST
virtual JSObject* WrapObject(JSContext *cx,
JS::Handle<JSObject*> aGivenProto) override;
nsISupports* GetParentObject()
{
return mParent;
}
void Disconnect()
{
mParent = nullptr;
}
bool Append(File *aFile) { return mFiles.AppendElement(aFile); }
bool Remove(uint32_t aIndex) {
if (aIndex < mFiles.Length()) {
mFiles.RemoveElementAt(aIndex);
return true;
}
return false;
}
void Clear() { return mFiles.Clear(); }
static FileList* FromSupports(nsISupports* aSupports)
{
#ifdef DEBUG
{
nsCOMPtr<nsIDOMFileList> list_qi = do_QueryInterface(aSupports);
// If this assertion fires the QI implementation for the object in
// question doesn't use the nsIDOMFileList pointer as the nsISupports
// pointer. That must be fixed, or we'll crash...
NS_ASSERTION(list_qi == static_cast<nsIDOMFileList*>(aSupports),
"Uh, fix QI!");
}
#endif
return static_cast<FileList*>(aSupports);
}
File* Item(uint32_t aIndex)
{
return mFiles.SafeElementAt(aIndex);
}
File* IndexedGetter(uint32_t aIndex, bool& aFound)
{
aFound = aIndex < mFiles.Length();
return aFound ? mFiles.ElementAt(aIndex) : nullptr;
}
uint32_t Length()
{
return mFiles.Length();
}
private:
nsTArray<nsRefPtr<File>> mFiles;
nsISupports *mParent;
};
} // namespace dom
} // namespace mozilla
+85
View File
@@ -0,0 +1,85 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/FileList.h"
#include "mozilla/dom/FileListBinding.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFiles, mParent)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileList)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFileList)
NS_INTERFACE_MAP_ENTRY(nsIDOMFileList)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(FileList)
NS_IMPL_CYCLE_COLLECTING_RELEASE(FileList)
/* static */ already_AddRefed<FileList>
FileList::Create(nsISupports* aParent, FileListClonedData* aData)
{
MOZ_ASSERT(aData);
nsRefPtr<FileList> fileList = new FileList(aParent);
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = aData->BlobImpls();
for (uint32_t i = 0; i < blobImpls.Length(); ++i) {
const nsRefPtr<BlobImpl>& blobImpl = blobImpls[i];
MOZ_ASSERT(blobImpl);
MOZ_ASSERT(blobImpl->IsFile());
nsRefPtr<File> file = File::Create(aParent, blobImpl);
MOZ_ASSERT(file);
if (NS_WARN_IF(!fileList->Append(file))) {
return nullptr;
}
}
return fileList.forget();
}
JSObject*
FileList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return mozilla::dom::FileListBinding::Wrap(aCx, this, aGivenProto);
}
NS_IMETHODIMP
FileList::GetLength(uint32_t* aLength)
{
*aLength = Length();
return NS_OK;
}
NS_IMETHODIMP
FileList::Item(uint32_t aIndex, nsISupports** aFile)
{
nsCOMPtr<nsIDOMBlob> file = Item(aIndex);
file.forget(aFile);
return NS_OK;
}
already_AddRefed<FileListClonedData>
FileList::CreateClonedData() const
{
nsTArray<nsRefPtr<BlobImpl>> blobImpls;
for (uint32_t i = 0; i < mFiles.Length(); ++i) {
blobImpls.AppendElement(mFiles[i]->Impl());
}
nsRefPtr<FileListClonedData> data = new FileListClonedData(blobImpls);
return data.forget();
}
NS_IMPL_ISUPPORTS0(FileListClonedData)
} // namespace dom
} // namespace mozilla
+130
View File
@@ -0,0 +1,130 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_FileList_h
#define mozilla_dom_FileList_h
#include "nsIDOMFileList.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace dom {
class BlobImpls;
class File;
class FileListClonedData final : public nsISupports
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
explicit FileListClonedData(const nsTArray<nsRefPtr<BlobImpl>>& aBlobImpls)
: mBlobImpls(aBlobImpls)
{}
const nsTArray<nsRefPtr<BlobImpl>>& BlobImpls() const
{
return mBlobImpls;
}
private:
~FileListClonedData()
{}
const nsTArray<nsRefPtr<BlobImpl>> mBlobImpls;
};
class FileList final : public nsIDOMFileList,
public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FileList)
NS_DECL_NSIDOMFILELIST
explicit FileList(nsISupports* aParent)
: mParent(aParent)
{}
static already_AddRefed<FileList>
Create(nsISupports* aParent, FileListClonedData* aData);
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
nsISupports* GetParentObject()
{
return mParent;
}
bool Append(File* aFile)
{
return mFiles.AppendElement(aFile);
}
bool Remove(uint32_t aIndex)
{
if (aIndex < mFiles.Length()) {
mFiles.RemoveElementAt(aIndex);
return true;
}
return false;
}
void Clear()
{
return mFiles.Clear();
}
static FileList* FromSupports(nsISupports* aSupports)
{
#ifdef DEBUG
{
nsCOMPtr<nsIDOMFileList> list_qi = do_QueryInterface(aSupports);
// If this assertion fires the QI implementation for the object in
// question doesn't use the nsIDOMFileList pointer as the nsISupports
// pointer. That must be fixed, or we'll crash...
NS_ASSERTION(list_qi == static_cast<nsIDOMFileList*>(aSupports),
"Uh, fix QI!");
}
#endif
return static_cast<FileList*>(aSupports);
}
File* Item(uint32_t aIndex)
{
return mFiles.SafeElementAt(aIndex);
}
File* IndexedGetter(uint32_t aIndex, bool& aFound)
{
aFound = aIndex < mFiles.Length();
return aFound ? mFiles.ElementAt(aIndex) : nullptr;
}
uint32_t Length()
{
return mFiles.Length();
}
// Useful for cloning
already_AddRefed<FileListClonedData> CreateClonedData() const;
private:
~FileList() {}
nsTArray<nsRefPtr<File>> mFiles;
nsCOMPtr<nsISupports> mParent;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_FileList_h
+1 -1
View File
@@ -240,7 +240,7 @@ MultipartBlobImpl::SetLengthAndModifiedDate()
void
MultipartBlobImpl::GetMozFullPathInternal(nsAString& aFilename,
ErrorResult& aRv)
ErrorResult& aRv) const
{
if (!mIsFromNsIFile || mBlobImpls.Length() == 0) {
BlobImplBase::GetMozFullPathInternal(aFilename, aRv);
+1 -1
View File
@@ -102,7 +102,7 @@ public:
}
virtual void GetMozFullPathInternal(nsAString& aFullPath,
ErrorResult& aRv) override;
ErrorResult& aRv) const override;
virtual nsresult
SetMutable(bool aMutable) override;
+37 -24
View File
@@ -8,6 +8,8 @@
#include "MessageEvent.h"
#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/FileList.h"
#include "mozilla/dom/FileListBinding.h"
#include "mozilla/dom/MessagePort.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/PMessagePort.h"
@@ -25,7 +27,6 @@ namespace {
struct StructuredCloneInfo
{
PostMessageEvent* event;
bool subsumes;
nsPIDOMWindow* window;
// This hashtable contains the transferred ports - used to avoid duplicates.
@@ -85,12 +86,27 @@ PostMessageEvent::ReadStructuredClone(JSContext* cx,
if (tag == SCTAG_DOM_FILELIST) {
NS_ASSERTION(!data, "Data should be empty");
nsISupports* supports;
if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
// What we get back from the reader is a FileListClonedData.
// From that we create a new FileList.
FileListClonedData* fileListClonedData;
if (JS_ReadBytes(reader, &fileListClonedData, sizeof(fileListClonedData))) {
MOZ_ASSERT(fileListClonedData);
// nsRefPtr<FileList> needs to go out of scope before toObjectOrNull() is
// called because the static analysis thinks dereferencing XPCOM objects
// can GC (because in some cases it can!), and a return statement with a
// JSObject* type means that JSObject* is on the stack as a raw pointer
// while destructors are running.
JS::Rooted<JS::Value> val(cx);
if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
return val.toObjectOrNull();
{
nsRefPtr<FileList> fileList =
FileList::Create(scInfo->window, fileListClonedData);
if (!fileList || !ToJSValue(cx, fileList, &val)) {
return nullptr;
}
}
return &val.toObject();
}
}
@@ -116,7 +132,7 @@ PostMessageEvent::WriteStructuredClone(JSContext* cx,
// See if this is a File/Blob object.
{
Blob* blob = nullptr;
if (scInfo->subsumes && NS_SUCCEEDED(UNWRAP_OBJECT(Blob, obj, blob))) {
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, obj, blob))) {
BlobImpl* blobImpl = blob->Impl();
if (JS_WriteUint32Pair(writer, SCTAG_DOM_BLOB, 0) &&
JS_WriteBytes(writer, &blobImpl, sizeof(blobImpl))) {
@@ -126,21 +142,20 @@ PostMessageEvent::WriteStructuredClone(JSContext* cx,
}
}
nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
nsContentUtils::XPConnect()->
GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
if (wrappedNative) {
uint32_t scTag = 0;
nsISupports* supports = wrappedNative->Native();
nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
if (list && scInfo->subsumes)
scTag = SCTAG_DOM_FILELIST;
if (scTag)
return JS_WriteUint32Pair(writer, scTag, 0) &&
JS_WriteBytes(writer, &supports, sizeof(supports)) &&
scInfo->event->StoreISupports(supports);
// See if this is a FileList object.
{
FileList* fileList = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, obj, fileList))) {
nsRefPtr<FileListClonedData> fileListClonedData =
fileList->CreateClonedData();
MOZ_ASSERT(fileListClonedData);
FileListClonedData* ptr = fileListClonedData.get();
if (JS_WriteUint32Pair(writer, SCTAG_DOM_FILELIST, 0) &&
JS_WriteBytes(writer, &ptr, sizeof(ptr))) {
scInfo->event->StoreISupports(fileListClonedData);
return true;
}
}
}
const JSStructuredCloneCallbacks* runtimeCallbacks =
@@ -378,15 +393,13 @@ PostMessageEvent::Run()
bool
PostMessageEvent::Write(JSContext* aCx, JS::Handle<JS::Value> aMessage,
JS::Handle<JS::Value> aTransfer, bool aSubsumes,
nsPIDOMWindow* aWindow)
JS::Handle<JS::Value> aTransfer, nsPIDOMWindow* aWindow)
{
// We *must* clone the data here, or the JS::Value could be modified
// by script
StructuredCloneInfo scInfo;
scInfo.event = this;
scInfo.window = aWindow;
scInfo.subsumes = aSubsumes;
return mBuffer.write(aCx, aMessage, aTransfer, &sPostMessageCallbacks,
&scInfo);
+1 -2
View File
@@ -37,8 +37,7 @@ public:
bool aTrustedCaller);
bool Write(JSContext* aCx, JS::Handle<JS::Value> aMessage,
JS::Handle<JS::Value> aTransfer, bool aSubsumes,
nsPIDOMWindow* aWindow);
JS::Handle<JS::Value> aTransfer, nsPIDOMWindow* aWindow);
private:
~PostMessageEvent();
+8 -9
View File
@@ -117,9 +117,7 @@ WindowNamedPropertiesHandler::getOwnPropDescriptor(JSContext* aCx,
if (!WrapObject(aCx, childWin, &v)) {
return false;
}
aDesc.object().set(aProxy);
aDesc.value().set(v);
aDesc.setAttributes(JSPROP_ENUMERATE);
FillPropertyDescriptor(aDesc, aProxy, 0, v);
return true;
}
}
@@ -137,9 +135,7 @@ WindowNamedPropertiesHandler::getOwnPropDescriptor(JSContext* aCx,
if (!WrapObject(aCx, element, &v)) {
return false;
}
aDesc.object().set(aProxy);
aDesc.value().set(v);
aDesc.setAttributes(JSPROP_ENUMERATE);
FillPropertyDescriptor(aDesc, aProxy, 0, v);
return true;
}
@@ -153,9 +149,7 @@ WindowNamedPropertiesHandler::getOwnPropDescriptor(JSContext* aCx,
if (!WrapObject(aCx, result, cache, nullptr, &v)) {
return false;
}
aDesc.object().set(aProxy);
aDesc.value().set(v);
aDesc.setAttributes(JSPROP_ENUMERATE);
FillPropertyDescriptor(aDesc, aProxy, 0, v);
return true;
}
@@ -178,6 +172,11 @@ WindowNamedPropertiesHandler::ownPropNames(JSContext* aCx,
unsigned flags,
JS::AutoIdVector& aProps) const
{
if (!(flags & JSITER_HIDDEN)) {
// None of our named properties are enumerable.
return true;
}
// Grab the DOM window.
nsGlobalWindow* win = xpc::WindowOrNull(JS_GetGlobalForObject(aCx, aProxy));
nsTArray<nsString> names;
+2
View File
@@ -174,6 +174,7 @@ EXPORTS.mozilla.dom += [
'ElementInlines.h',
'EventSource.h',
'File.h',
'FileList.h',
'FragmentOrElement.h',
'FromParser.h',
'ImageEncoder.h',
@@ -231,6 +232,7 @@ UNIFIED_SOURCES += [
'EventSource.cpp',
'File.cpp',
'FileIOObject.cpp',
'FileList.cpp',
'FragmentOrElement.cpp',
'ImageEncoder.cpp',
'ImportManager.cpp',
+3
View File
@@ -7627,8 +7627,11 @@ nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable,
if (file) {
blobImpl = new BlobImplFile(file, false);
ErrorResult rv;
// Ensure that file data is cached no that the content process
// has this data available to it when passed over:
blobImpl->GetSize(rv);
blobImpl->GetLastModified(rv);
blobImpl->LookupAndCacheIsDirectory();
} else {
blobImpl = do_QueryInterface(data);
}
+27
View File
@@ -103,6 +103,9 @@
#include "nsIStyleSheetService.h"
#include "nsContentPermissionHelper.h"
#include "nsNetUtil.h"
#include "nsDocument.h"
#include "HTMLImageElement.h"
#include "mozilla/css/ImageLoader.h"
#ifdef XP_WIN
#undef GetClassName
@@ -3860,6 +3863,30 @@ nsDOMWindowUtils::LeaveChaosMode()
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::ForceUseCounterFlush(nsIDOMNode *aNode)
{
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
NS_ENSURE_ARG_POINTER(aNode);
if (nsCOMPtr<nsIDocument> doc = do_QueryInterface(aNode)) {
mozilla::css::ImageLoader* loader = doc->StyleImageLoader();
loader->FlushUseCounters();
static_cast<nsDocument*>(doc.get())->ReportUseCounters();
return NS_OK;
}
if (nsCOMPtr<nsIContent> content = do_QueryInterface(aNode)) {
if (HTMLImageElement* img = HTMLImageElement::FromContent(content)) {
img->FlushUseCounters();
return NS_OK;
}
}
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::HasRuleProcessorUsedByMultipleStyleSets(uint32_t aSheetType,
bool* aRetVal)
+1
View File
@@ -18,6 +18,7 @@
#include "nsIDocument.h"
#include "nsINode.h"
#include "nsIDOMWindow.h"
#include "nsIURI.h"
NS_IMPL_ISUPPORTS(nsDataDocumentContentPolicy, nsIContentPolicy)
+1 -1
View File
@@ -300,6 +300,7 @@ GK_ATOM(digit, "digit")
GK_ATOM(dir, "dir")
GK_ATOM(dirAutoSetBy, "dirAutoSetBy")
GK_ATOM(directionality, "directionality")
GK_ATOM(directory, "directory")
GK_ATOM(disableOutputEscaping, "disable-output-escaping")
GK_ATOM(disabled, "disabled")
GK_ATOM(disablehistory, "disablehistory")
@@ -2294,7 +2295,6 @@ GK_ATOM(containerRelevant, "container-relevant")
GK_ATOM(contentinfo, "contentinfo")
GK_ATOM(cycles, "cycles")
GK_ATOM(datatable, "datatable")
GK_ATOM(directory, "directory")
GK_ATOM(eventFromInput, "event-from-input")
GK_ATOM(grammar, "grammar")
GK_ATOM(gridcell, "gridcell")
+1 -4
View File
@@ -8354,13 +8354,10 @@ nsGlobalWindow::PostMessageMozOuter(JSContext* aCx, JS::Handle<JS::Value> aMessa
providedPrincipal,
nsContentUtils::IsCallerChrome());
nsIPrincipal* principal = GetPrincipal();
JS::Rooted<JS::Value> message(aCx, aMessage);
JS::Rooted<JS::Value> transfer(aCx, aTransfer);
bool subsumes;
if (NS_FAILED(callerPrin->Subsumes(principal, &subsumes)) ||
!event->Write(aCx, message, transfer, subsumes, this)) {
if (!event->Write(aCx, message, transfer, this)) {
aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
+22 -3
View File
@@ -433,14 +433,19 @@ nsHostObjectProtocolHandler::Traverse(const nsACString& aUri,
aCallback.NoteXPCOMChild(res->mObject);
}
static nsISupports*
GetDataObjectForSpec(const nsACString& aSpec)
{
DataInfo* info = GetDataInfo(aSpec);
return info ? info->mObject : nullptr;
}
static nsISupports*
GetDataObject(nsIURI* aURI)
{
nsCString spec;
aURI->GetSpec(spec);
DataInfo* info = GetDataInfo(spec);
return info ? info->mObject : nullptr;
return GetDataObjectForSpec(spec);
}
// -----------------------------------------------------------------------
@@ -616,6 +621,20 @@ NS_GetBlobForBlobURI(nsIURI* aURI, BlobImpl** aBlob)
return NS_OK;
}
nsresult
NS_GetBlobForBlobURISpec(const nsACString& aSpec, BlobImpl** aBlob)
{
*aBlob = nullptr;
nsCOMPtr<BlobImpl> blob = do_QueryInterface(GetDataObjectForSpec(aSpec));
if (!blob) {
return NS_ERROR_DOM_BAD_URI;
}
blob.forget(aBlob);
return NS_OK;
}
nsresult
NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream)
{
+3
View File
@@ -124,6 +124,9 @@ inline bool IsFontTableURI(nsIURI* aUri)
extern nsresult
NS_GetBlobForBlobURI(nsIURI* aURI, mozilla::dom::BlobImpl** aBlob);
extern nsresult
NS_GetBlobForBlobURISpec(const nsACString& aSpec, mozilla::dom::BlobImpl** aBlob);
extern nsresult
NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream);
+10
View File
@@ -1,6 +1,15 @@
[DEFAULT]
support-files =
file_messagemanager_unload.html
file_use_counter_outer.html
file_use_counter_svg_getElementById.svg
file_use_counter_svg_currentScale.svg
file_use_counter_svg_background.html
file_use_counter_svg_list_style_image.html
file_use_counter_svg_fill_pattern_definition.svg
file_use_counter_svg_fill_pattern.svg
file_use_counter_svg_fill_pattern_internal.svg
file_use_counter_svg_fill_pattern_data.svg
[browser_bug593387.js]
skip-if = e10s # Bug ?????? - test directly touches content (contentWindow.iframe.addEventListener)
@@ -16,3 +25,4 @@ skip-if = e10s # this tests non-e10s behavior. it's not expected to work in e10s
skip-if = true # Intermittent failures - bug 987493. Restore the skip-if above once fixed
[browser_bug1058164.js]
skip-if = e10s # We need bug 918634 to land before this can be tested with e10s.
[browser_use_counters.js]
+276
View File
@@ -0,0 +1,276 @@
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */
let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
const gHttpTestRoot = "http://example.com/browser/dom/base/test/";
/**
* Enable local telemetry recording for the duration of the tests.
*/
let gOldContentCanRecord = false;
add_task(function* test_initialize() {
gOldContentCanRecord = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
let old = telemetry.canRecordExtended;
telemetry.canRecordExtended = true;
return old;
});
info("canRecord for content: " + gOldContentCanRecord);
});
add_task(function* () {
// Check that use counters are incremented by SVGs loaded directly in iframes.
yield check_use_counter_iframe("file_use_counter_svg_getElementById.svg",
"SVGSVGELEMENT_GETELEMENTBYID");
yield check_use_counter_iframe("file_use_counter_svg_currentScale.svg",
"SVGSVGELEMENT_CURRENTSCALE_getter");
yield check_use_counter_iframe("file_use_counter_svg_currentScale.svg",
"SVGSVGELEMENT_CURRENTSCALE_setter");
// Check that use counters are incremented by SVGs loaded as images.
// Note that SVG images are not permitted to execute script, so we can only
// check for properties here.
yield check_use_counter_img("file_use_counter_svg_getElementById.svg",
"PROPERTY_FILL");
yield check_use_counter_img("file_use_counter_svg_currentScale.svg",
"PROPERTY_FILL");
// Check that use counters are incremented by directly loading SVGs
// that reference patterns defined in another SVG file.
yield check_use_counter_direct("file_use_counter_svg_fill_pattern.svg",
"PROPERTY_FILLOPACITY", /*xfail=*/true);
// Check that use counters are incremented by directly loading SVGs
// that reference patterns defined in the same file or in data: URLs.
yield check_use_counter_direct("file_use_counter_svg_fill_pattern_internal.svg",
"PROPERTY_FILLOPACITY");
// data: URLs don't correctly propagate to their referring document yet.
//yield check_use_counter_direct("file_use_counter_svg_fill_pattern_data.svg",
// "PROPERTY_FILL_OPACITY");
// Check that use counters are incremented by SVGs loaded as CSS images in
// pages loaded in iframes. Again, SVG images in CSS aren't permitted to
// execute script, so we need to use properties here.
yield check_use_counter_iframe("file_use_counter_svg_background.html",
"PROPERTY_FILL");
yield check_use_counter_iframe("file_use_counter_svg_list_style_image.html",
"PROPERTY_FILL");
// Check that even loads from the imglib cache update use counters. The
// background images should still be there, because we just loaded them
// in the last set of tests. But we won't get updated counts for the
// document counters, because we won't be re-parsing the SVG documents.
yield check_use_counter_iframe("file_use_counter_svg_background.html",
"PROPERTY_FILL", false);
yield check_use_counter_iframe("file_use_counter_svg_list_style_image.html",
"PROPERTY_FILL", false);
});
add_task(function* () {
yield ContentTask.spawn(gBrowser.selectedBrowser, { oldCanRecord: gOldContentCanRecord }, function (arg) {
Cu.import("resource://gre/modules/PromiseUtils.jsm");
yield new Promise(resolve => {
let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
telemetry.canRecordExtended = arg.oldCanRecord;
resolve();
});
});
});
function waitForDestroyedDocuments() {
let deferred = promise.defer();
SpecialPowers.exactGC(window, deferred.resolve);
return deferred.promise;
}
function waitForPageLoad(browser) {
return ContentTask.spawn(browser, null, function*() {
Cu.import("resource://gre/modules/PromiseUtils.jsm");
yield new Promise(resolve => {
let listener = () => {
removeEventListener("load", listener, true);
resolve();
}
addEventListener("load", listener, true);
});
});
}
function grabHistogramsFromContent(browser, use_counter_middlefix) {
return ContentTask.spawn(browser, { middlefix: use_counter_middlefix }, function* (arg) {
let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
function snapshot_histogram(name) {
return telemetry.getHistogramById(name).snapshot();
}
let histogram_page_name = "USE_COUNTER_" + arg.middlefix + "_PAGE";
let histogram_document_name = "USE_COUNTER_" + arg.middlefix + "_DOCUMENT";
let histogram_page = snapshot_histogram(histogram_page_name);
let histogram_document = snapshot_histogram(histogram_document_name);
return [histogram_page.sum, histogram_document.sum];
});
}
let check_use_counter_iframe = Task.async(function* (file, use_counter_middlefix, check_documents=true) {
info("checking " + file + " with histogram " + use_counter_middlefix);
let newTab = gBrowser.addTab( "about:blank");
gBrowser.selectedTab = newTab;
newTab.linkedBrowser.stop();
// Hold on to the current values of the telemetry histograms we're
// interested in.
let [histogram_page_before, histogram_document_before] =
yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
gBrowser.selectedBrowser.loadURI(gHttpTestRoot + "file_use_counter_outer.html");
yield waitForPageLoad(gBrowser.selectedBrowser);
// Inject our desired file into the iframe of the newly-loaded page.
yield ContentTask.spawn(gBrowser.selectedBrowser, { file: file }, function(opts) {
Cu.import("resource://gre/modules/PromiseUtils.jsm");
let deferred = PromiseUtils.defer();
let wu = content.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
let iframe = content.document.getElementById('content');
iframe.src = opts.file;
let listener = (event) => {
event.target.removeEventListener("load", listener, true);
// We flush the main document first, then the iframe's document to
// ensure any propagation that might happen from child->parent should
// have already happened when counters are reported to telemetry.
wu.forceUseCounterFlush(content.document);
wu.forceUseCounterFlush(iframe.contentDocument);
deferred.resolve();
};
iframe.addEventListener("load", listener, true);
return deferred.promise;
});
// Tear down the page.
gBrowser.removeTab(newTab);
// The histograms only get recorded when the document actually gets
// destroyed, which might not have happened yet due to GC/CC effects, etc.
// Try to force document destruction.
yield waitForDestroyedDocuments();
// Grab histograms again and compare.
let [histogram_page_after, histogram_document_after] =
yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
is(histogram_page_after, histogram_page_before + 1,
"page counts for " + use_counter_middlefix + " after are correct");
if (check_documents) {
is(histogram_document_after, histogram_document_before + 1,
"document counts " + use_counter_middlefix + " after are correct");
}
});
let check_use_counter_img = Task.async(function* (file, use_counter_middlefix) {
info("checking " + file + " as image with histogram " + use_counter_middlefix);
let newTab = gBrowser.addTab("about:blank");
gBrowser.selectedTab = newTab;
newTab.linkedBrowser.stop();
// Hold on to the current values of the telemetry histograms we're
// interested in.
let [histogram_page_before, histogram_document_before] =
yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
gBrowser.selectedBrowser.loadURI(gHttpTestRoot + "file_use_counter_outer.html");
yield waitForPageLoad(gBrowser.selectedBrowser);
// Inject our desired file into the img of the newly-loaded page.
yield ContentTask.spawn(gBrowser.selectedBrowser, { file: file }, function(opts) {
Cu.import("resource://gre/modules/PromiseUtils.jsm");
let deferred = PromiseUtils.defer();
let img = content.document.getElementById('display');
img.src = opts.file;
let listener = (event) => {
img.removeEventListener("load", listener, true);
// Flush for the image. It matters what order we do these in, so that
// the image can propagate its use counters to the document prior to the
// document reporting its use counters.
let wu = content.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
wu.forceUseCounterFlush(img);
// Flush for the main window.
wu.forceUseCounterFlush(content.document);
deferred.resolve();
};
img.addEventListener("load", listener, true);
return deferred.promise;
});
// Tear down the page.
gBrowser.removeTab(newTab);
// The histograms only get recorded when the document actually gets
// destroyed, which might not have happened yet due to GC/CC effects, etc.
// Try to force document destruction.
yield waitForDestroyedDocuments();
// Grab histograms again and compare.
let [histogram_page_after, histogram_document_after] =
yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
is(histogram_page_after, histogram_page_before + 1,
"page counts for " + use_counter_middlefix + " after are correct");
is(histogram_document_after, histogram_document_before + 1,
"document counts " + use_counter_middlefix + " after are correct");
});
let check_use_counter_direct = Task.async(function* (file, use_counter_middlefix, xfail=false) {
info("checking " + file + " with histogram " + use_counter_middlefix);
let newTab = gBrowser.addTab( "about:blank");
gBrowser.selectedTab = newTab;
newTab.linkedBrowser.stop();
// Hold on to the current values of the telemetry histograms we're
// interested in.
let [histogram_page_before, histogram_document_before] =
yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
gBrowser.selectedBrowser.loadURI(gHttpTestRoot + file);
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
Cu.import("resource://gre/modules/PromiseUtils.jsm");
yield new Promise(resolve => {
let listener = () => {
removeEventListener("load", listener, true);
let wu = content.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
wu.forceUseCounterFlush(content.document);
setTimeout(resolve, 0);
}
addEventListener("load", listener, true);
});
});
// Tear down the page.
gBrowser.removeTab(newTab);
// The histograms only get recorded when the document actually gets
// destroyed, which might not have happened yet due to GC/CC effects, etc.
// Try to force document destruction.
yield waitForDestroyedDocuments();
// Grab histograms again and compare.
let [histogram_page_after, histogram_document_after] =
yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
(xfail ? todo_is : is)(histogram_page_after, histogram_page_before + 1,
"page counts for " + use_counter_middlefix + " after are correct");
(xfail ? todo_is : is)(histogram_document_after, histogram_document_before + 1,
"document counts " + use_counter_middlefix + " after are correct");
});
+17
View File
@@ -0,0 +1,17 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=968923
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 968923</title>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=968923">Mozilla Bug 968923</a>
<img id="display" />
<iframe id="content">
</iframe>
</body>
</html>
@@ -0,0 +1,22 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=968923
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 968923</title>
<style>
/* Use a query string to work around imagelib caching.
Otherwise, we won't get use counters for this file. */
body { background-image: url('file_use_counter_svg_getElementById.svg?asbackground=1') }
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=968923">Mozilla Bug 968923</a>
<img id="display" />
<iframe id="content" src="about:blank">
</iframe>
</body>
</html>
@@ -0,0 +1,18 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4in" height="3in" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>Test graphic for hitting currentScale
</desc>
<script type="text/javascript"> <![CDATA[
document.documentElement.currentScale = document.documentElement.currentScale;
]]>
</script>
<image id="i1" x="200" y="200" width="100px" height="80px"
xlink:href="no-such-scheme:nothing">
</image>
<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>
</svg>

After

Width:  |  Height:  |  Size: 680 B

@@ -0,0 +1,15 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="8cm" height="4cm" viewBox="0 0 800 400" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<desc>Borrowed from http://www.w3.org/TR/SVG/pservers.html</desc>
<!-- Outline the drawing area in blue -->
<rect fill="none" stroke="blue"
x="1" y="1" width="798" height="398"/>
<!-- The ellipse is filled using a triangle pattern paint server
and stroked with black -->
<ellipse fill="url(http://example.com/browser/dom/base/test/file_use_counter_svg_fill_pattern_definition.svg#TrianglePattern)" stroke="black" stroke-width="5"
cx="400" cy="200" rx="350" ry="150" />
</svg>

After

Width:  |  Height:  |  Size: 763 B

@@ -0,0 +1,15 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="8cm" height="4cm" viewBox="0 0 800 400" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<desc>Borrowed from http://www.w3.org/TR/SVG/pservers.html</desc>
<!-- Outline the drawing area in blue -->
<rect fill="none" stroke="blue"
x="1" y="1" width="798" height="398"/>
<!-- The ellipse is filled using a triangle pattern paint server
and stroked with black -->
<ellipse fill="url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iIAogICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHdpZHRoPSI4Y20iIGhlaWdodD0iNGNtIiB2aWV3Qm94PSIwIDAgODAwIDQwMCIgdmVyc2lvbj0iMS4xIgogICAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGRlc2M+Qm9ycm93ZWQgZnJvbSBodHRwOi8vd3d3LnczLm9yZy9UUi9TVkcvcHNlcnZlcnMuaHRtbDwvZGVzYz4KICA8ZGVmcz4KICAgIDxwYXR0ZXJuIGlkPSJUcmlhbmdsZVBhdHRlcm4iIHBhdHRlcm5Vbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICAgICAgICB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIKICAgICAgICAgICAgIHZpZXdCb3g9IjAgMCAxMCAxMCIgPgogICAgICA8cGF0aCBkPSJNIDAgMCBMIDcgMCBMIDMuNSA3IHoiIGZpbGw9InJlZCIgZmlsbC1vcGFjaXR5PSIwLjciIHN0cm9rZT0iYmx1ZSIgLz4KICAgIDwvcGF0dGVybj4gCiAgPC9kZWZzPgo8L3N2Zz4K#TrianglePattern)" stroke="black" stroke-width="5"
cx="400" cy="200" rx="350" ry="150" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,14 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="8cm" height="4cm" viewBox="0 0 800 400" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<desc>Borrowed from http://www.w3.org/TR/SVG/pservers.html</desc>
<defs>
<pattern id="TrianglePattern" patternUnits="userSpaceOnUse"
x="0" y="0" width="100" height="100"
viewBox="0 0 10 10" >
<path d="M 0 0 L 7 0 L 3.5 7 z" fill="red" fill-opacity="0.7" stroke="blue" />
</pattern>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 591 B

@@ -0,0 +1,23 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="8cm" height="4cm" viewBox="0 0 800 400" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<desc>Borrowed from http://www.w3.org/TR/SVG/pservers.html</desc>
<!-- Outline the drawing area in blue -->
<rect fill="none" stroke="blue"
x="1" y="1" width="798" height="398"/>
<defs>
<pattern id="TrianglePattern" patternUnits="userSpaceOnUse"
x="0" y="0" width="100" height="100"
viewBox="0 0 10 10" >
<path d="M 0 0 L 7 0 L 3.5 7 z" fill="red" fill-opacity="0.7" stroke="blue" />
</pattern>
</defs>
<!-- The ellipse is filled using a triangle pattern paint server
and stroked with black -->
<ellipse fill="url(#TrianglePattern)" stroke="black" stroke-width="5"
cx="400" cy="200" rx="350" ry="150" />
</svg>

After

Width:  |  Height:  |  Size: 944 B

@@ -0,0 +1,23 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4in" height="3in" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>Test graphic for hitting getElementById
</desc>
<image id="i1" x="200" y="200" width="100px" height="80px"
xlink:href="no-such-scheme:nothing">
</image>
<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>
<script type="text/javascript"> <![CDATA[
var image = document.documentElement.getElementById("i1");
image.addEventListener("load",
function() {
document.documentElement.removeAttribute("class");
},
false);
]]>
</script>
</svg>

After

Width:  |  Height:  |  Size: 881 B

@@ -0,0 +1,22 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=968923
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 968923</title>
<style>
/* Use a query string to work around imagelib caching.
Otherwise, we won't get use counters for this file. */
ul { list-style-image: url('file_use_counter_svg_currentScale.svg?asliststyleimage=1') }
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=968923">Mozilla Bug 968923</a>
<ul>
<li>Some text</li>
<li>Some other text</li>
</ul>
</body>
</html>
@@ -0,0 +1,10 @@
<!DOCTYPE HTML>
<html>
<body>
<script>
onmessage = function(e) {
parent.postMessage(e.data, '*');
}
</script>
</body>
</html>
+7
View File
@@ -240,6 +240,8 @@ support-files =
file_audioLoop.html
file_webaudioLoop.html
file_webaudioLoop2.html
referrer_helper.js
referrer_testserver.sjs
[test_anonymousContent_api.html]
[test_anonymousContent_append_after_reflow.html]
@@ -663,6 +665,8 @@ support-files = referrerHelper.js
[test_bug1165501.html]
support-files = referrerHelper.js
[test_img_referrer.html]
[test_anchor_area_referrer.html]
[test_anchor_area_referrer_changing.html]
[test_caretPositionFromPoint.html]
[test_classList.html]
# This test fails on the Mac for some reason
@@ -784,3 +788,6 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g'
[test_script_loader_crossorigin_data_url.html]
[test_file_negative_date.html]
[test_nonascii_blob_url.html]
[test_window_element_enumeration.html]
[test_cloning_fileList.html]
support-files = script_cloning_fileList.js iframe_cloning_fileList.html
+98
View File
@@ -0,0 +1,98 @@
/*
* common functionality for iframe, anchor, and area referrer attribute tests
*/
const GET_RESULT = sjs + 'action=get-test-results';
const RESET_STATE = sjs + 'action=resetState';
SimpleTest.waitForExplicitFinish();
var advance = function() { tests.next(); };
/**
* Listen for notifications from the child.
* These are sent in case of error, or when the loads we await have completed.
*/
window.addEventListener("message", function(event) {
if (event.data == "childLoadComplete") {
// all loads happen, continue the test.
advance();
}
});
/**
* helper to perform an XHR
* to do checkIndividualResults and resetState
*/
function doXHR(aUrl, onSuccess, onFail) {
var xhr = new XMLHttpRequest();
xhr.responseType = "json";
xhr.onload = function () {
onSuccess(xhr);
};
xhr.onerror = function () {
onFail(xhr);
};
xhr.open('GET', aUrl, true);
xhr.send(null);
}
/**
* Grabs the results via XHR and passes to checker.
*/
function checkIndividualResults(aTestname, aExpectedReferrer, aName) {
var onload = xhr => {
var results = xhr.response;
info(JSON.stringify(xhr.response));
ok(aName in results, aName + " tests have to be performed.");
is(results[aName].policy, aExpectedReferrer, aTestname + ' --- ' + results[aName].policy + ' (' + results[aName].referrer + ')');
advance();
};
var onerror = xhr => {
ok(false, "Can't get results from the counter server.");
SimpleTest.finish();
};
doXHR(GET_RESULT, onload, onerror);
}
function resetState() {
doXHR(RESET_STATE,
advance,
function(xhr) {
ok(false, "error in reset state");
SimpleTest.finish();
});
}
/**
* testing if anchor and area referrer attributes are honoured (1174913)
*/
var tests = (function() {
// enable referrer attribute
yield SpecialPowers.pushPrefEnv({"set": [['network.http.enablePerElementReferrer', true]]}, advance);
var iframe = document.getElementById("testframe");
for (var j = 0; j < testCases.length; j++) {
var actions = testCases[j].ACTION;
var tests = testCases[j].TESTS;
for (var k = 0; k < actions.length; k++) {
var actionString = actions[k];
for (var i = 0; i < tests.length; i++) {
yield resetState();
var searchParams = new URLSearchParams();
searchParams.append(ACTION, actionString);
searchParams.append(NAME, tests[i].NAME);
for (var l of PARAMS) {
if (tests[i][l]) {
searchParams.append(window[l], tests[i][l]);
}
}
yield iframe.src = sjs + searchParams.toString();
yield checkIndividualResults(tests[i].DESC, tests[i].RESULT, tests[i].NAME);
};
};
};
// complete. Be sure to yield so we don't call this twice.
yield SimpleTest.finish();
})();
+180
View File
@@ -0,0 +1,180 @@
/*
* Test server for iframe, anchor, and area referrer attributes.
* https://bugzilla.mozilla.org/show_bug.cgi?id=1175736
*/
Components.utils.importGlobalProperties(["URLSearchParams"]);
const BASE_URL = 'example.com/tests/dom/base/test/referrer_testserver.sjs';
const SHARED_KEY = 'referrer_testserver.sjs';
const ATTRIBUTE_POLICY = 'attributePolicy';
const NEW_ATTRIBUTE_POLICY = 'newAttributePolicy';
const NAME = 'name';
const META_POLICY = 'metaPolicy';
const REL = 'rel';
function createTestUrl(aPolicy, aAction, aName, aType) {
return 'http://' + BASE_URL + '?' +
'action=' + aAction + '&' +
'policy=' + aPolicy + '&' +
'name=' + aName + '&' +
'type=' + aType;
}
function buildAnchorString(aMetaPolicy, aReferrerPolicy, aName, aRelString){
if (aReferrerPolicy) {
return `<a href="${createTestUrl(aReferrerPolicy, 'test', aName, 'link')}" referrer="${aReferrerPolicy}" id="link" ${aRelString}>${aReferrerPolicy}</a>`;
}
return `<a href="${createTestUrl(aMetaPolicy, 'test', aName, 'link')}" id="link" ${aRelString}>link</a>`;
}
function buildAreaString(aMetaPolicy, aReferrerPolicy, aName, aRelString){
var result = `<img src="file_mozfiledataurl_img.jpg" alt="image" usemap="#imageMap">`;
result += `<map name="imageMap">`;
if (aReferrerPolicy) {
result += `<area shape="circle" coords="1,1,1" href="${createTestUrl(aReferrerPolicy, 'test', aName, 'link')}" alt="theArea" referrer="${aReferrerPolicy}" id="link" ${aRelString}>`;
} else {
result += `<area shape="circle" coords="1,1,1" href="${createTestUrl(aMetaPolicy, 'test', aName, 'link')}" alt="theArea" id="link" ${aRelString}>`;
}
result += `</map>`;
return result;
}
// test page using anchor or area referrer attribute
function createAETestPageUsingRefferer(aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aRel, aStringBuilder, aChangingMethod) {
var metaString = '';
if (aMetaPolicy) {
metaString = '<head><meta name="referrer" content="' + aMetaPolicy + '"></head>';
}
var changeString = '';
if (aChangingMethod === 'setAttribute') {
changeString = `document.getElementById("link").setAttribute("referrer", "${aNewAttributePolicy}")`;
} else if (aChangingMethod === 'property') {
changeString = `document.getElementById("link").referrer = "${aNewAttributePolicy}"`;
}
var relString = '';
if (aRel) {
relString = `rel="noreferrer"`;
}
var elementString = aStringBuilder(aMetaPolicy, aAttributePolicy, aName, relString);
return `<!DOCTYPE HTML>
<html>
${metaString}
<body>
${elementString}
<script>
window.addEventListener("load", function() {
${changeString}
document.getElementById("link").click();
}.bind(window), false);
</script>
</body>
</html>`;
}
function handleRequest(request, response) {
var params = new URLSearchParams(request.queryString);
var action = params.get('action');
response.setHeader('Cache-Control', 'no-cache', false);
response.setHeader('Content-Type', 'text/html; charset=utf-8', false);
if (action === 'resetState') {
setSharedState(SHARED_KEY, "{}");
response.write("");
return;
}
if (action === 'get-test-results') {
// ?action=get-result
response.setHeader('Cache-Control', 'no-cache', false);
response.setHeader('Content-Type', 'text/plain', false);
response.write(getSharedState(SHARED_KEY));
return;
}
if (action === 'redirect') {
response.write('<script>parent.postMessage("childLoadComplete", "http://mochi.test:8888");</script>');
return;
}
if (action === 'test') {
// ?action=test&policy=origin&name=name
var policy = params.get('policy');
var name = params.get('name');
var type = params.get('type');
var result = getSharedState(SHARED_KEY);
result = result ? JSON.parse(result) : {};
var referrerLevel = "none";
var test = {}
if (request.hasHeader('Referer')) {
var referrer = request.getHeader("Referer");
if (referrer.indexOf("referrer_testserver") > 0) {
referrerLevel = "full";
} else if (referrer.indexOf("http://mochi.test:8888") == 0) {
referrerLevel = "origin";
} else {
// this is never supposed to happen
referrerLevel = "other-origin";
}
test.referrer = referrer;
} else {
test.referrer = '';
}
test.policy = referrerLevel;
test.expected = policy;
result[name] = test;
setSharedState(SHARED_KEY, JSON.stringify(result));
if (type === "link") {
// forward link click to redirect URL to finish test
var loc = "http://" + BASE_URL + "?action=redirect";
response.setStatusLine('1.1', 302, 'Found');
response.setHeader('Location', loc, false);
}
return;
}
// parse test arguments and start test
var attributePolicy = params.get(ATTRIBUTE_POLICY) || '';
var newAttributePolicy = params.get(NEW_ATTRIBUTE_POLICY) || '';
var metaPolicy = params.get(META_POLICY) || '';
var rel = params.get(REL) || '';
var name = params.get(NAME);
// anchor & area
var _getPage = createAETestPageUsingRefferer.bind(null, metaPolicy, attributePolicy, newAttributePolicy, name, rel);
var _getAnchorPage = _getPage.bind(null, buildAnchorString);
var _getAreaPage = _getPage.bind(null, buildAreaString);
// aMetaPolicy, aAttributePolicy, aNewAttributePolicy, aName, aChangingMethod, aStringBuilder
if (action === 'generate-anchor-policy-test') {
response.write(_getAnchorPage());
return;
}
if (action === 'generate-anchor-changing-policy-test-set-attribute') {
response.write(_getAnchorPage('setAttribute'));
return;
}
if (action === 'generate-anchor-changing-policy-test-property') {
response.write(_getAnchorPage('property'));
return;
}
if (action === 'generate-area-policy-test') {
response.write(_getAreaPage());
return;
}
if (action === 'generate-area-changing-policy-test-set-attribute') {
response.write(_getAreaPage('setAttribute'));
return;
}
if (action === 'generate-area-changing-policy-test-property') {
response.write(_getAreaPage('property'));
return;
}
response.write("I don't know action " + action);
return;
}
+15
View File
@@ -0,0 +1,15 @@
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.importGlobalProperties(["File"]);
let testFile = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get("ProfD", Ci.nsIFile);
testFile.append("prefs.js");
addMessageListener("file.open", function () {
sendAsyncMessage("file.opened", {
file: new File(testFile)
});
});
@@ -0,0 +1,116 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test anchor and area policy attribute for Bug 1174913</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<!--
Testing that anchor and area referrer attributes are honoured correctly
* anchor tag with referrer attribute (generate-anchor-policy-test)
* regression tests that meta referrer is still working even if attribute referrers are enabled (generate-anchor-policy-test)
https://bugzilla.mozilla.org/show_bug.cgi?id=1174913
-->
<script type="application/javascript;version=1.8">
const sjs = "/tests/dom/base/test/referrer_testserver.sjs?";
const ATTRIBUTE_POLICY = 'attributePolicy';
const NEW_ATTRIBUTE_POLICY = 'newAttributePolicy';
const NAME = 'name';
const META_POLICY = 'metaPolicy';
const DESC = 'description';
const RESULT = 'result';
const ACTION = 'action';
const TESTS = 'tests';
const REL = 'rel';
const PARAMS = ["ATTRIBUTE_POLICY", "NEW_ATTRIBUTE_POLICY", "META_POLICY", "REL"];
const testCases = [
{ACTION: ["generate-anchor-policy-test", "generate-area-policy-test"],
TESTS: [
{ATTRIBUTE_POLICY: 'unsafe-url',
NAME: 'unsafe-url-with-origin-in-meta',
META_POLICY: 'origin',
DESC: "unsafe-url (anchor) with origin in meta",
RESULT: 'full'},
{ATTRIBUTE_POLICY: 'origin',
NAME: 'origin-with-unsafe-url-in-meta',
META_POLICY: 'unsafe-url',
DESC: "origin (anchor) with unsafe-url in meta",
RESULT: 'origin'},
{ATTRIBUTE_POLICY: 'no-referrer',
NAME: 'no-referrer-with-origin-in-meta',
META_POLICY: 'origin',
DESC: "no-referrer (anchor) with origin in meta",
RESULT: 'none'},
{NAME: 'no-referrer-in-meta',
META_POLICY: 'no-referrer',
DESC: "no-referrer in meta",
RESULT: 'none'},
{ATTRIBUTE_POLICY: 'origin',
NAME: 'origin-with-no-meta',
META_POLICY: '',
DESC: "origin (anchor) with no meta",
RESULT: 'origin'},
// setting rel=noreferrer -> we expect no referrer
{ATTRIBUTE_POLICY: 'unsafe-url',
NAME: 'unsafe-url-with-origin-in-meta-rel',
META_POLICY: 'origin',
DESC: "unsafe-url (anchor) with origin in meta and rel=noreferrer",
RESULT: 'none',
REL: 'noreferrer'},
{ATTRIBUTE_POLICY: 'origin',
NAME: 'origin-with-unsafe-url-in-meta-rel',
META_POLICY: 'unsafe-url',
DESC: "origin (anchor) with unsafe-url in meta and rel=noreferrer",
RESULT: 'none',
REL: 'noreferrer'},
{ATTRIBUTE_POLICY: 'origin',
NAME: 'origin-with-no-meta-rel',
META_POLICY: '',
DESC: "origin (anchor) with no meta and rel=noreferrer",
RESULT: 'none',
REL: 'noreferrer'},
// setting invalid refer values -> we expect either full referrer (default)
// or whatever is specified in the meta referrer policy
{ATTRIBUTE_POLICY: 'origin-when-cross-origin',
NAME: 'origin-when-cross-origin-with-no-meta',
META_POLICY: '',
DESC: "origin-when-cross-origin (anchor) with no meta",
RESULT: 'full'},
{ATTRIBUTE_POLICY: 'default',
NAME: 'default-with-no-meta',
META_POLICY: '',
DESC: "default (anchor) with no meta",
RESULT: 'full'},
{ATTRIBUTE_POLICY: 'something',
NAME: 'something-with-no-meta',
META_POLICY: '',
DESC: "something (anchor) with no meta",
RESULT: 'full'},
{ATTRIBUTE_POLICY: 'origin-when-cross-origin',
NAME: 'origin-when-cross-origin-with-no-referrer-in-meta',
META_POLICY: 'no-referrer',
DESC: "origin-when-cross-origin (anchor) with no-referrer in meta",
RESULT: 'none'},
{ATTRIBUTE_POLICY: 'origin-when-cross-origin',
NAME: 'origin-when-cross-origin-with-unsafe-url-in-meta',
META_POLICY: 'unsafe-url',
DESC: "origin-when-cross-origin (anchor) with unsafe-url in meta",
RESULT: 'full'},
{ATTRIBUTE_POLICY: 'origin-when-cross-origin',
NAME: 'origin-when-cross-origin-with-origin-in-meta',
META_POLICY: 'origin',
DESC: "origin-when-cross-origin (anchor) with origin in meta",
RESULT: 'origin'}]}
];
</script>
<script type="application/javascript;version=1.7" src="/tests/dom/base/test/referrer_helper.js"></script>
</head>
<body onload="tests.next();">
<iframe id="testframe"></iframe>
</body>
</html>
@@ -0,0 +1,75 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test anchor and area policy attribute for Bug 1174913</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<!--
Testing that anchor and area referrer attributes are honoured correctly
This test is split due to errors on b2g
* testing setAttribute and .referrer (generate-anchor-changing-test)
https://bugzilla.mozilla.org/show_bug.cgi?id=1174913
-->
<script type="application/javascript;version=1.8">
const sjs = "/tests/dom/base/test/referrer_testserver.sjs?";
const ATTRIBUTE_POLICY = 'attributePolicy';
const NEW_ATTRIBUTE_POLICY = 'newAttributePolicy';
const NAME = 'name';
const META_POLICY = 'metaPolicy';
const DESC = 'description';
const RESULT = 'result';
const ACTION = 'action';
const TESTS = 'tests';
const REL = 'rel';
const PARAMS = ["ATTRIBUTE_POLICY", "NEW_ATTRIBUTE_POLICY", "META_POLICY", "REL"];
const testCases = [
{ACTION: ["generate-anchor-changing-policy-test-set-attribute", "generate-area-changing-policy-test-set-attribute"],
TESTS: [
{ATTRIBUTE_POLICY: 'unsafe-url',
NEW_ATTRIBUTE_POLICY: 'no-referrer',
NAME: 'no-referrer-unsafe-url-with-origin-in-meta',
META_POLICY: 'origin',
DESC: "no-referrer (anchor, orginally unsafe-url) with origin in meta",
RESULT: 'none'},
{ATTRIBUTE_POLICY: 'origin',
NEW_ATTRIBUTE_POLICY: 'unsafe-url',
NAME: 'unsafe-url-origin-with-no-referrer-in-meta',
META_POLICY: 'no-referrer',
DESC: "unsafe-url (anchor, orginally origin) with no-referrer in meta",
RESULT: 'full'},
{ATTRIBUTE_POLICY: 'origin',
NEW_ATTRIBUTE_POLICY: 'unsafe-url',
NAME: 'unsafe-url-origin-with-no-referrer-in-meta-rel',
META_POLICY: 'no-referrer',
DESC: "unsafe-url (anchor, orginally origin) with no-referrer in meta and rel=noreferrer",
RESULT: 'none',
REL: 'noreferrer'}]},
{ACTION: ["generate-anchor-changing-policy-test-property", "generate-area-changing-policy-test-property"],
TESTS: [
{ATTRIBUTE_POLICY: 'no-referrer',
NEW_ATTRIBUTE_POLICY: 'unsafe-url',
NAME: 'unsafe-url-no-referrer-with-origin-in-meta',
META_POLICY: 'origin',
DESC: "unsafe-url (anchor, orginally no-referrer) with origin in meta",
RESULT: 'full'},
{ATTRIBUTE_POLICY: 'no-referrer',
NEW_ATTRIBUTE_POLICY: 'unsafe-url',
NAME: 'unsafe-url-no-referrer-with-origin-in-meta-rel',
META_POLICY: 'origin',
DESC: "unsafe-url (anchor, orginally no-referrer) with origin in meta and rel=noreferrer",
RESULT: 'none',
REL: 'noreferrer'}]}
];
</script>
<script type="application/javascript;version=1.7" src="/tests/dom/base/test/referrer_helper.js"></script>
</head>
<body onload="tests.next();">
<iframe id="testframe"></iframe>
</body>
</html>
+85
View File
@@ -0,0 +1,85 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for cloning FileList</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display">
<input id="fileList" type="file"></input>
</p>
<pre id="test">
<script class="testbody" type="text/javascript">
var iframeScriptURL;
var url = SimpleTest.getTestFileURL("script_cloning_fileList.js");
var script = SpecialPowers.loadChromeScript(url);
script.addMessageListener("file.opened", onOpened);
function onOpened(message) {
var fileList = document.getElementById('fileList');
SpecialPowers.wrap(fileList).mozSetFileArray([message.file]);
// Just a simple test
var domFile = fileList.files[0];
is(domFile.name, "prefs.js", "fileName should be prefs.js");
var ifr = document.createElement('iframe');
ifr.src = iframeScriptURL;
ifr.onload = function() {
ifr.contentWindow.postMessage(fileList.files, "*");
}
document.body.appendChild(ifr);
}
function runTest() {
script.sendAsyncMessage("file.open");
}
onmessage = function(e) {
var fileList = document.getElementById('fileList');
ok(true, "Message received");
ok(e.data instanceof FileList, "The object is a FileList");
ok(e.data != fileList.files, "The object has been cloned!");
is(e.data.length, fileList.files.length, "The length matches");
is(e.data.length, 1, "1 element found!");
is(e.data[0].name, "prefs.js", "fileName should be prefs.js");
next();
}
var tests = [
function() {
// Same origin
iframeScriptURL = 'iframe_cloning_fileList.html';
runTest();
},
function() {
// Cross Origin
iframeScriptURL = 'http://example.com/tests/dom/base/test/iframe_cloning_fileList.html';
runTest();
}
];
function next() {
if (!tests.length) {
script.destroy();
SimpleTest.finish();
return;
}
var test = tests.shift();
test();
}
SimpleTest.waitForExplicitFinish();
next();
</script>
</pre>
</body>
</html>
@@ -0,0 +1,70 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=959992
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 959992</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=959992">Mozilla Bug 959992</a>
<p id="display"></p>
<div id="content" style="display: none">
<img id="one" name="two">
<div id="three" name="four"></div>
<div id=""></div>
<img name="">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 959992 **/
var names1 = Object.getOwnPropertyNames(window);
var names2 = [];
var gsp = Object.getPrototypeOf(Window.prototype);
var names3 = Object.getOwnPropertyNames(gsp);
for (var i in window) {
names2.push(i);
}
is(names1.indexOf(""), -1,
"Element with empty id/name should not be in our own prop list");
is(names2.indexOf(""), -1,
"Element with empty id/name name should not be in our enumeration list");
is(names3.indexOf(""), -1,
"Element with empty id/name should not be in GSP own prop list");
is(names1.indexOf("one"), -1,
"<img> with id should not be in our own prop list");
is(names2.indexOf("one"), -1,
"<img> with id should not be in our enumeration list");
isnot(names3.indexOf("one"), -1,
"<img> with id should be in GSP own prop list");
is(names1.indexOf("two"), -1,
"<img> with name should not be in our own prop list");
is(names2.indexOf("two"), -1,
"<img> with name should not be in our enumeration list");
isnot(names3.indexOf("two"), -1,
"<img> with name should be in GSP own prop list");
is(names1.indexOf("three"), -1,
"<div> with id should not be in our own prop list");
is(names2.indexOf("three"), -1,
"<div> with id should not be in our enumeration list");
todo_isnot(names3.indexOf("three"), -1,
"<div> with id should be in GSP own prop list");
is(names1.indexOf("four"), -1,
"<div> with name should not be in our own prop list");
is(names2.indexOf("four"), -1,
"<div> with name should not be in our enumeration list");
is(names3.indexOf("four"), -1,
"<div> with name should not be in GSP own prop list");
</script>
</pre>
</body>
</html>
@@ -29,26 +29,26 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1019417
"Frame with no name or empty name should not be in GSP own prop list");
is(names1.indexOf("x"), -1,
"Frame with about:blank loaded should not be in our own prop list");
isnot(names2.indexOf("x"), -1,
"Frame with about:blank loaded should be in our enumeration list");
is(names2.indexOf("x"), -1,
"Frame with about:blank loaded should not be in our enumeration list");
isnot(names3.indexOf("x"), -1,
"Frame with about:blank loaded should be in GSP own prop list");
is(names1.indexOf("y"), -1,
"Frame with same-origin loaded should not be in our own prop list");
isnot(names2.indexOf("y"), -1,
"Frame with same-origin loaded should be in our enumeration list");
is(names2.indexOf("y"), -1,
"Frame with same-origin loaded should not be in our enumeration list");
isnot(names3.indexOf("y"), -1,
"Frame with same-origin loaded should be in GSP own prop list");
is(names1.indexOf("z"), -1,
"Frame with cross-origin loaded should not be in our own prop list");
isnot(names2.indexOf("z"), -1,
"Frame with cross-origin loaded should be in our enumeration list");
is(names2.indexOf("z"), -1,
"Frame with cross-origin loaded should not be in our enumeration list");
isnot(names3.indexOf("z"), -1,
"Frame with cross-origin loaded should be in GSPown prop list");
"Frame with cross-origin loaded should be in GSP own prop list");
is(names1.indexOf("sameorigin"), -1,
"Frame with same-origin changed name should not be in our own prop list");
isnot(names2.indexOf("sameorigin"), -1,
"Frame with same-origin changed name should be in our enumeration list");
is(names2.indexOf("sameorigin"), -1,
"Frame with same-origin changed name should not be in our enumeration list");
isnot(names3.indexOf("sameorigin"), -1,
"Frame with same-origin changed name should be in GSP own prop list");
is(names1.indexOf("crossorigin"), -1,
-4
View File
@@ -481,10 +481,6 @@ DOMInterfaces = {
},
},
'FileList': {
'headerFile': 'mozilla/dom/File.h',
},
'FileReader': {
'nativeType': 'nsDOMFileReader',
'implicitJSContext': [ 'readAsArrayBuffer' ],
+1
View File
@@ -6,6 +6,7 @@ include protocol PBackground;
include protocol PBlob; // FIXME: bug 792908
include protocol PCache;
include protocol PCacheOp;
include protocol PCachePushStream;
include protocol PCacheStreamControl;
include protocol PFileDescriptorSet;
+98 -9
View File
@@ -26,8 +26,11 @@
#include "nsIScriptGlobalObject.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/DataTransferBinding.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/FileList.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/OSFileSystem.h"
namespace mozilla {
namespace dom {
@@ -36,10 +39,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
if (tmp->mFiles) {
tmp->mFiles->Disconnect();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles)
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@@ -139,11 +139,7 @@ DataTransfer::DataTransfer(nsISupports* aParent,
}
DataTransfer::~DataTransfer()
{
if (mFiles) {
mFiles->Disconnect();
}
}
{}
// static
already_AddRefed<DataTransfer>
@@ -302,6 +298,13 @@ DataTransfer::GetFiles(ErrorResult& aRv)
nsRefPtr<File> domFile;
if (file) {
#ifdef DEBUG
if (XRE_GetProcessType() == GeckoProcessType_Default) {
bool isDir;
file->IsDirectory(&isDir);
MOZ_ASSERT(!isDir, "How did we get here?");
}
#endif
domFile = File::CreateFromFile(GetParentObject(), file);
} else {
nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(supports);
@@ -840,6 +843,92 @@ DataTransfer::SetDragImage(nsIDOMElement* aImage, int32_t aX, int32_t aY)
return rv.StealNSResult();
}
static already_AddRefed<OSFileSystem>
MakeOrReuseFileSystem(const nsAString& aNewLocalRootPath,
OSFileSystem* aFS,
nsPIDOMWindow* aWindow)
{
MOZ_ASSERT(aWindow);
nsRefPtr<OSFileSystem> fs;
if (aFS) {
const nsAString& prevLocalRootPath = aFS->GetLocalRootPath();
if (aNewLocalRootPath == prevLocalRootPath) {
fs = aFS;
}
}
if (!fs) {
fs = new OSFileSystem(aNewLocalRootPath);
fs->Init(aWindow);
}
return fs.forget();
}
already_AddRefed<Promise>
DataTransfer::GetFilesAndDirectories(ErrorResult& aRv)
{
nsCOMPtr<nsINode> parentNode = do_QueryInterface(mParent);
if (!parentNode) {
return nullptr;
}
nsCOMPtr<nsIGlobalObject> global = parentNode->OwnerDoc()->GetScopeObject();
MOZ_ASSERT(global);
if (!global) {
return nullptr;
}
nsRefPtr<Promise> p = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
if (!mFiles) {
ErrorResult dummy;
GetFiles(dummy);
if (!mFiles) {
return nullptr;
}
}
Sequence<OwningFileOrDirectory> filesAndDirsSeq;
if (!filesAndDirsSeq.SetLength(mFiles->Length(), mozilla::fallible_t())) {
p->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
return p.forget();
}
nsPIDOMWindow* window = parentNode->OwnerDoc()->GetInnerWindow();
nsRefPtr<OSFileSystem> fs;
for (uint32_t i = 0; i < mFiles->Length(); ++i) {
if (mFiles->Item(i)->Impl()->IsDirectory()) {
#if defined(ANDROID) || defined(MOZ_B2G)
MOZ_ASSERT(false,
"Directory picking should have been redirected to normal "
"file picking for platforms that don't have a directory "
"picker");
#endif
nsAutoString path;
mFiles->Item(i)->GetMozFullPathInternal(path, aRv);
if (aRv.Failed()) {
return nullptr;
}
int32_t leafSeparatorIndex = path.RFind(FILE_PATH_SEPARATOR);
nsDependentSubstring dirname = Substring(path, 0, leafSeparatorIndex);
nsDependentSubstring basename = Substring(path, leafSeparatorIndex);
fs = MakeOrReuseFileSystem(dirname, fs, window);
filesAndDirsSeq[i].SetAsDirectory() = new Directory(fs, basename);
} else {
filesAndDirsSeq[i].SetAsFile() = mFiles->Item(i);
}
}
p->MaybeResolve(filesAndDirsSeq);
return p.forget();
}
void
DataTransfer::AddElement(Element& aElement, ErrorResult& aRv)
{
+5
View File
@@ -19,6 +19,7 @@
#include "nsAutoPtr.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/Promise.h"
class nsINode;
class nsITransferable;
@@ -33,6 +34,7 @@ namespace dom {
class DOMStringList;
class Element;
class FileList;
template<typename T> class Optional;
/**
@@ -145,6 +147,9 @@ public:
void ClearData(const mozilla::dom::Optional<nsAString>& aFormat,
mozilla::ErrorResult& aRv);
FileList* GetFiles(mozilla::ErrorResult& aRv);
already_AddRefed<Promise> GetFilesAndDirectories(ErrorResult& aRv);
void AddElement(Element& aElement, mozilla::ErrorResult& aRv);
uint32_t MozItemCount()
{
+5 -54
View File
@@ -36,8 +36,8 @@ DeviceStorageFileSystem::DeviceStorageFileSystem(
mString.Append('-');
mString.Append(mStorageName);
mIsTesting =
mozilla::Preferences::GetBool("device.storage.prompt.testing", false);
mRequiresPermissionChecks =
!mozilla::Preferences::GetBool("device.storage.prompt.testing", false);
// Get the permission name required to access the file system.
nsresult rv =
@@ -97,45 +97,10 @@ DeviceStorageFileSystem::GetWindow() const
return mDeviceStorage->GetOwner();
}
already_AddRefed<nsIFile>
DeviceStorageFileSystem::GetLocalFile(const nsAString& aRealPath) const
void
DeviceStorageFileSystem::GetRootName(nsAString& aRetval) const
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Should be on parent process!");
nsAutoString localPath;
FileSystemUtils::NormalizedPathToLocalPath(aRealPath, localPath);
localPath = mLocalRootPath + localPath;
nsCOMPtr<nsIFile> file;
nsresult rv = NS_NewLocalFile(localPath, false, getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return file.forget();
}
bool
DeviceStorageFileSystem::GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Should be on parent process!");
MOZ_ASSERT(aFile, "aFile Should not be null.");
aRealPath.Truncate();
nsAutoString filePath;
ErrorResult rv;
aFile->GetMozFullPathInternal(filePath, rv);
if (NS_WARN_IF(rv.Failed())) {
return false;
}
return LocalPathToRealPath(filePath, aRealPath);
}
const nsAString&
DeviceStorageFileSystem::GetRootName() const
{
return mStorageName;
aRetval = mStorageName;
}
bool
@@ -172,19 +137,5 @@ DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const
return fs->ToString() == mString;
}
bool
DeviceStorageFileSystem::LocalPathToRealPath(const nsAString& aLocalPath,
nsAString& aRealPath) const
{
nsAutoString path;
FileSystemUtils::LocalPathToNormalizedPath(aLocalPath, path);
if (!FileSystemUtils::IsDescendantPath(mNormalizedLocalRootPath, path)) {
aRealPath.Truncate();
return false;
}
aRealPath = Substring(path, mNormalizedLocalRootPath.Length());
return true;
}
} // namespace dom
} // namespace mozilla
+2 -15
View File
@@ -33,14 +33,8 @@ public:
virtual nsPIDOMWindow*
GetWindow() const override;
virtual already_AddRefed<nsIFile>
GetLocalFile(const nsAString& aRealPath) const override;
virtual bool
GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const override;
virtual const nsAString&
GetRootName() const override;
virtual void
GetRootName(nsAString& aRetval) const override;
virtual bool
IsSafeFile(nsIFile* aFile) const override;
@@ -51,16 +45,9 @@ private:
virtual
~DeviceStorageFileSystem();
bool
LocalPathToRealPath(const nsAString& aLocalPath, nsAString& aRealPath) const;
nsString mStorageType;
nsString mStorageName;
// The local path of the root. Only available in the parent process.
// In the child process, we don't use it and its value should be empty.
nsString mLocalRootPath;
nsString mNormalizedLocalRootPath;
nsDOMDeviceStorage* mDeviceStorage;
};
+1 -1
View File
@@ -87,7 +87,7 @@ Directory::GetName(nsString& aRetval) const
aRetval.Truncate();
if (mPath.IsEmpty()) {
aRetval = mFileSystem->GetRootName();
mFileSystem->GetRootName(aRetval);
return;
}
+52 -2
View File
@@ -8,6 +8,7 @@
#include "DeviceStorageFileSystem.h"
#include "nsCharSeparatedTokenizer.h"
#include "OSFileSystem.h"
namespace mozilla {
namespace dom {
@@ -37,12 +38,12 @@ FileSystemBase::FromString(const nsAString& aString)
new DeviceStorageFileSystem(storageType, storageName);
return f.forget();
}
return nullptr;
return nsRefPtr<OSFileSystem>(new OSFileSystem(aString)).forget();
}
FileSystemBase::FileSystemBase()
: mShutdown(false)
, mIsTesting(false)
, mRequiresPermissionChecks(true)
{
}
@@ -62,6 +63,41 @@ FileSystemBase::GetWindow() const
return nullptr;
}
already_AddRefed<nsIFile>
FileSystemBase::GetLocalFile(const nsAString& aRealPath) const
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Should be on parent process!");
nsAutoString localPath;
FileSystemUtils::NormalizedPathToLocalPath(aRealPath, localPath);
localPath = mLocalRootPath + localPath;
nsCOMPtr<nsIFile> file;
nsresult rv = NS_NewLocalFile(localPath, false, getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return file.forget();
}
bool
FileSystemBase::GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Should be on parent process!");
MOZ_ASSERT(aFile, "aFile Should not be null.");
aRealPath.Truncate();
nsAutoString filePath;
ErrorResult rv;
aFile->GetMozFullPathInternal(filePath, rv);
if (NS_WARN_IF(rv.Failed())) {
return false;
}
return LocalPathToRealPath(filePath, aRealPath);
}
bool
FileSystemBase::IsSafeFile(nsIFile* aFile) const
{
@@ -74,5 +110,19 @@ FileSystemBase::IsSafeDirectory(Directory* aDir) const
return false;
}
bool
FileSystemBase::LocalPathToRealPath(const nsAString& aLocalPath,
nsAString& aRealPath) const
{
nsAutoString path;
FileSystemUtils::LocalPathToNormalizedPath(aLocalPath, path);
if (!FileSystemUtils::IsDescendantPath(mNormalizedLocalRootPath, path)) {
aRealPath.Truncate();
return false;
}
aRealPath = Substring(path, mNormalizedLocalRootPath.Length());
return true;
}
} // namespace dom
} // namespace mozilla
+29 -11
View File
@@ -42,18 +42,24 @@ public:
virtual nsPIDOMWindow*
GetWindow() const;
/*
* Create nsIFile object with the given real path (absolute DOM path).
/**
* Create nsIFile object from the given real path (absolute DOM path).
*/
virtual already_AddRefed<nsIFile>
GetLocalFile(const nsAString& aRealPath) const = 0;
already_AddRefed<nsIFile>
GetLocalFile(const nsAString& aRealPath) const;
/*
* Get the virtual name of the root directory. This name will be exposed to
* the content page.
*/
virtual const nsAString&
GetRootName() const = 0;
virtual void
GetRootName(nsAString& aRetval) const = 0;
const nsAString&
GetLocalRootPath() const
{
return mLocalRootPath;
}
bool
IsShutdown() const
@@ -72,8 +78,8 @@ public:
* If succeeded, returns true. Otherwise, returns false and set aRealPath to
* empty string.
*/
virtual bool
GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const = 0;
bool
GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const;
/*
* Get the permission name required to access this file system.
@@ -85,13 +91,25 @@ public:
}
bool
IsTesting() const
RequiresPermissionChecks() const
{
return mIsTesting;
return mRequiresPermissionChecks;
}
protected:
virtual ~FileSystemBase();
bool
LocalPathToRealPath(const nsAString& aLocalPath, nsAString& aRealPath) const;
// The local path of the root (i.e. the OS path, with OS path separators, of
// the OS directory that acts as the root of this OSFileSystem).
// Only available in the parent process.
// In the child process, we don't use it and its value should be empty.
nsString mLocalRootPath;
// The same, but with path separators normalized to "/".
nsString mNormalizedLocalRootPath;
// The string representation of the file system.
nsString mString;
@@ -100,7 +118,7 @@ protected:
// The permission name required to access the file system.
nsCString mPermission;
bool mIsTesting;
bool mRequiresPermissionChecks;
};
} // namespace dom
@@ -122,7 +122,7 @@ FileSystemPermissionRequest::Run()
return NS_OK;
}
if (filesystem->IsTesting()) {
if (!filesystem->RequiresPermissionChecks()) {
Allow(JS::UndefinedHandleValue);
return NS_OK;
}
+1 -1
View File
@@ -58,7 +58,7 @@ FileSystemRequestParent::Dispatch(ContentParent* aParent,
return false;
}
if (!mFileSystem->IsTesting()) {
if (mFileSystem->RequiresPermissionChecks()) {
// Check the content process permission.
nsCString access;
+80
View File
@@ -0,0 +1,80 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/OSFileSystem.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsIFile.h"
#include "nsPIDOMWindow.h"
namespace mozilla {
namespace dom {
OSFileSystem::OSFileSystem(const nsAString& aRootDir)
{
mLocalRootPath = aRootDir;
FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath,
mNormalizedLocalRootPath);
// Non-mobile devices don't have the concept of separate permissions to
// access different parts of devices storage like Pictures, or Videos, etc.
mRequiresPermissionChecks = false;
mString = mLocalRootPath;
#ifdef DEBUG
mPermission.AssignLiteral("never-used");
#endif
}
void
OSFileSystem::Init(nsPIDOMWindow* aWindow)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(!mWindow, "No duple Init() calls");
MOZ_ASSERT(aWindow);
mWindow = aWindow;
}
nsPIDOMWindow*
OSFileSystem::GetWindow() const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
return mWindow;
}
void
OSFileSystem::GetRootName(nsAString& aRetval) const
{
return aRetval.AssignLiteral("/");
}
bool
OSFileSystem::IsSafeFile(nsIFile* aFile) const
{
// The concept of "safe files" is specific to the Device Storage API where
// files are only "safe" if they're of a type that is appropriate for the
// area of device storage that is being used.
MOZ_CRASH("Don't use OSFileSystem with the Device Storage API");
return true;
}
bool
OSFileSystem::IsSafeDirectory(Directory* aDir) const
{
// The concept of "safe directory" is specific to the Device Storage API
// where a directory is only "safe" if it belongs to the area of device
// storage that it is being used with.
MOZ_CRASH("Don't use OSFileSystem with the Device Storage API");
return true;
}
} // namespace dom
} // namespace mozilla
+46
View File
@@ -0,0 +1,46 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_OSFileSystem_h
#define mozilla_dom_OSFileSystem_h
#include "mozilla/dom/FileSystemBase.h"
namespace mozilla {
namespace dom {
class OSFileSystem : public FileSystemBase
{
public:
explicit OSFileSystem(const nsAString& aRootDir);
void
Init(nsPIDOMWindow* aWindow);
// Overrides FileSystemBase
virtual nsPIDOMWindow*
GetWindow() const override;
virtual void
GetRootName(nsAString& aRetval) const override;
virtual bool
IsSafeFile(nsIFile* aFile) const override;
virtual bool
IsSafeDirectory(Directory* aDir) const override;
private:
virtual ~OSFileSystem() {}
nsCOMPtr<nsPIDOMWindow> mWindow;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_OSFileSystem_h
+2
View File
@@ -11,6 +11,7 @@ EXPORTS.mozilla.dom += [
'FileSystemRequestParent.h',
'FileSystemTaskBase.h',
'FileSystemUtils.h',
'OSFileSystem.h',
]
UNIFIED_SOURCES += [
@@ -25,6 +26,7 @@ UNIFIED_SOURCES += [
'FileSystemUtils.cpp',
'GetDirectoryListingTask.cpp',
'GetFileOrDirectoryTask.cpp',
'OSFileSystem.cpp',
'RemoveTask.cpp',
]
+14
View File
@@ -11,6 +11,7 @@
#include "nsPresContext.h"
#include "nsMappedAttributes.h"
#include "nsSize.h"
#include "nsDocument.h"
#include "nsIDocument.h"
#include "nsIDOMMutationEvent.h"
#include "nsIScriptContext.h"
@@ -27,6 +28,7 @@
#include "mozilla/dom/HTMLFormElement.h"
#include "nsAttrValueOrString.h"
#include "imgLoader.h"
#include "Image.h"
// Responsive images!
#include "mozilla/dom/HTMLSourceElement.h"
@@ -1296,6 +1298,18 @@ HTMLImageElement::MediaFeatureValuesChanged()
QueueImageLoadTask();
}
void
HTMLImageElement::FlushUseCounters()
{
nsCOMPtr<imgIRequest> request;
GetRequest(CURRENT_REQUEST, getter_AddRefs(request));
nsCOMPtr<imgIContainer> container;
request->GetImage(getter_AddRefs(container));
static_cast<image::Image*>(container.get())->ReportUseCounters();
}
} // namespace dom
} // namespace mozilla
+6
View File
@@ -265,6 +265,12 @@ public:
const nsAString& aMediaAttr,
nsAString& aResult);
/**
* If this image's src pointers to an SVG document, flush the SVG document's
* use counters to telemetry. Only used for testing purposes.
*/
void FlushUseCounters();
protected:
virtual ~HTMLImageElement();
+134 -436
View File
@@ -10,7 +10,11 @@
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/Date.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/OSFileSystem.h"
#include "nsAttrValueInlines.h"
#include "nsCRTGlue.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsITextControlElement.h"
@@ -76,6 +80,7 @@
// input type=file
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileList.h"
#include "nsIFile.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
@@ -328,167 +333,6 @@ UploadLastDir::ContentPrefCallback::HandleError(nsresult error)
namespace {
/**
* This enumerator returns File objects after wrapping a single
* nsIFile representing a directory. It enumerates the files under that
* directory and its subdirectories as a flat list of files, ignoring/skipping
* over symbolic links.
*
* The enumeration involves I/O, so this class must NOT be used on the main
* thread or else the main thread could be blocked for a very long time.
*
* This enumerator does not walk the directory tree breadth-first, but it also
* is not guaranteed to walk it depth-first either (since it uses
* nsIFile::GetDirectoryEntries, which is not guaranteed to group a directory's
* subdirectories at the beginning of the list that it returns).
*/
class DirPickerRecursiveFileEnumerator final
: public nsISimpleEnumerator
{
~DirPickerRecursiveFileEnumerator() {}
public:
NS_DECL_ISUPPORTS
explicit DirPickerRecursiveFileEnumerator(nsIFile* aTopDir)
: mTopDir(aTopDir)
{
MOZ_ASSERT(!NS_IsMainThread(), "This class blocks on I/O!");
#ifdef DEBUG
{
bool isDir;
aTopDir->IsDirectory(&isDir);
MOZ_ASSERT(isDir);
}
#endif
if (NS_FAILED(aTopDir->GetParent(getter_AddRefs(mTopDirsParent)))) {
// This just means that the name of the picked directory won't be
// included in the File.path string.
mTopDirsParent = aTopDir;
}
nsCOMPtr<nsISimpleEnumerator> entries;
if (NS_SUCCEEDED(mTopDir->GetDirectoryEntries(getter_AddRefs(entries))) &&
entries) {
mDirEnumeratorStack.AppendElement(entries);
LookupAndCacheNext();
}
}
NS_IMETHOD
GetNext(nsISupports** aResult) override
{
MOZ_ASSERT(!NS_IsMainThread(),
"Walking the directory tree involves I/O, so using this "
"enumerator can block a thread for a long time!");
if (!mNextFile) {
return NS_ERROR_FAILURE;
}
// The parent for this object will be set on the main thread.
nsRefPtr<File> domFile = File::CreateFromFile(nullptr, mNextFile);
nsCString relDescriptor;
nsresult rv =
mNextFile->GetRelativeDescriptor(mTopDirsParent, relDescriptor);
NS_ENSURE_SUCCESS(rv, rv);
NS_ConvertUTF8toUTF16 path(relDescriptor);
nsAutoString leafName;
mNextFile->GetLeafName(leafName);
MOZ_ASSERT(leafName.Length() <= path.Length());
int32_t length = path.Length() - leafName.Length();
MOZ_ASSERT(length >= 0);
if (length > 0) {
// Note that we leave the trailing "/" on the path.
BlobImplFile* blobImpl = static_cast<BlobImplFile*>(domFile->Impl());
MOZ_ASSERT(blobImpl);
blobImpl->SetPath(Substring(path, 0, uint32_t(length)));
}
nsCOMPtr<nsIDOMBlob> blob = domFile.get();
blob.forget(aResult);
LookupAndCacheNext();
return NS_OK;
}
NS_IMETHOD
HasMoreElements(bool* aResult) override
{
*aResult = !!mNextFile;
return NS_OK;
}
private:
void
LookupAndCacheNext()
{
for (;;) {
if (mDirEnumeratorStack.IsEmpty()) {
mNextFile = nullptr;
break;
}
nsISimpleEnumerator* currentDirEntries =
mDirEnumeratorStack.LastElement();
bool hasMore;
DebugOnly<nsresult> rv = currentDirEntries->HasMoreElements(&hasMore);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (!hasMore) {
mDirEnumeratorStack.RemoveElementAt(mDirEnumeratorStack.Length() - 1);
continue;
}
nsCOMPtr<nsISupports> entry;
rv = currentDirEntries->GetNext(getter_AddRefs(entry));
MOZ_ASSERT(NS_SUCCEEDED(rv));
nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
MOZ_ASSERT(file);
bool isLink, isSpecial;
file->IsSymlink(&isLink);
file->IsSpecial(&isSpecial);
if (isLink || isSpecial) {
continue;
}
bool isDir;
file->IsDirectory(&isDir);
if (isDir) {
nsCOMPtr<nsISimpleEnumerator> subDirEntries;
rv = file->GetDirectoryEntries(getter_AddRefs(subDirEntries));
MOZ_ASSERT(NS_SUCCEEDED(rv) && subDirEntries);
mDirEnumeratorStack.AppendElement(subDirEntries);
continue;
}
#ifdef DEBUG
{
bool isFile;
file->IsFile(&isFile);
MOZ_ASSERT(isFile);
}
#endif
mNextFile.swap(file);
return;
}
}
private:
nsCOMPtr<nsIFile> mTopDir;
nsCOMPtr<nsIFile> mTopDirsParent; // May be mTopDir if no parent
nsCOMPtr<nsIFile> mNextFile;
nsTArray<nsCOMPtr<nsISimpleEnumerator> > mDirEnumeratorStack;
};
NS_IMPL_ISUPPORTS(DirPickerRecursiveFileEnumerator, nsISimpleEnumerator)
/**
* This may return nullptr if aDomFile's implementation of
* File::mozFullPathInternal does not successfully return a non-empty
@@ -519,119 +363,6 @@ DOMFileToLocalFile(File* aDomFile)
} // namespace
class DirPickerFileListBuilderTask final
: public nsRunnable
{
public:
DirPickerFileListBuilderTask(HTMLInputElement* aInput, nsIFile* aTopDir)
: mPreviousFileListLength(0)
, mInput(aInput)
, mTopDir(aTopDir)
, mFileListLength(0)
, mCanceled(false)
{}
NS_IMETHOD Run() {
if (!NS_IsMainThread()) {
// Build up list of File objects on this dedicated thread:
nsCOMPtr<nsISimpleEnumerator> iter =
new DirPickerRecursiveFileEnumerator(mTopDir);
bool hasMore = true;
nsCOMPtr<nsISupports> tmp;
while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
iter->GetNext(getter_AddRefs(tmp));
nsCOMPtr<nsIDOMBlob> domBlob = do_QueryInterface(tmp);
MOZ_ASSERT(domBlob);
mFileList.AppendElement(static_cast<File*>(domBlob.get()));
mFileListLength = mFileList.Length();
if (mCanceled) {
MOZ_ASSERT(!mInput, "This is bad - how did this happen?");
// There's no point dispatching to the main thread (that doesn't
// guarantee that we'll be destroyed there).
return NS_OK;
}
}
return NS_DispatchToMainThread(this);
}
// Now back on the main thread, set the list on our HTMLInputElement:
if (mCanceled || mFileList.IsEmpty()) {
return NS_OK;
}
MOZ_ASSERT(mInput->mDirPickerFileListBuilderTask,
"But we aren't canceled!");
if (mInput->mProgressTimer) {
mInput->mProgressTimerIsActive = false;
mInput->mProgressTimer->Cancel();
}
mInput->MaybeDispatchProgressEvent(true); // Last progress event.
mInput->mDirPickerFileListBuilderTask = nullptr; // Now null out.
if (mCanceled) { // The last progress event may have canceled us
return NS_OK;
}
// Recreate File with the correct parent object.
nsCOMPtr<nsIGlobalObject> global = mInput->OwnerDoc()->GetScopeObject();
for (uint32_t i = 0; i < mFileList.Length(); ++i) {
MOZ_ASSERT(!mFileList[i]->GetParentObject());
mFileList[i] = File::Create(global, mFileList[i]->Impl());
MOZ_ASSERT(mFileList[i]);
}
// The text control frame (if there is one) isn't going to send a change
// event because it will think this is done by a script.
// So, we can safely send one by ourself.
mInput->SetFiles(mFileList, true);
nsresult rv =
nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
NS_LITERAL_STRING("change"), true,
false);
// Clear mInput to make sure that it can't lose its last strong ref off the
// main thread (which may happen if our dtor runs off the main thread)!
mInput = nullptr;
return rv;
}
void Cancel()
{
MOZ_ASSERT(NS_IsMainThread() && !mCanceled);
// Clear mInput to make sure that it can't lose its last strong ref off the
// main thread (which may happen if our dtor runs off the main thread)!
mInput = nullptr;
mCanceled = true;
}
uint32_t GetFileListLength() const
{
return mFileListLength;
}
/**
* The number of files added to the FileList at the time the last progress
* event was fired.
*
* This is only read/set by HTMLInputElement on the main thread. The reason
* that this member is stored here rather than on HTMLInputElement is so that
* we don't increase the size of HTMLInputElement for something that's rarely
* used.
*/
uint32_t mPreviousFileListLength;
private:
nsRefPtr<HTMLInputElement> mInput;
nsCOMPtr<nsIFile> mTopDir;
nsTArray<nsRefPtr<File>> mFileList;
// We access the list length on both threads, so we need the indirection of
// this atomic member to make the access thread safe:
mozilla::Atomic<uint32_t> mFileListLength;
mozilla::Atomic<bool> mCanceled;
};
NS_IMETHODIMP
HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
@@ -642,50 +373,13 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
return NS_OK;
}
mInput->CancelDirectoryPickerScanIfRunning();
int16_t mode;
mFilePicker->GetMode(&mode);
if (mode == static_cast<int16_t>(nsIFilePicker::modeGetFolder)) {
// Directory picking is different, since we still need to do more I/O to
// build up the list of File objects. Since this may block for a
// long time, we need to build the list off on another dedicated thread to
// avoid blocking any other activities that the browser is carrying out.
// The user selected this directory, so we always save this dir, even if
// no files are found under it.
nsCOMPtr<nsIFile> pickedDir;
mFilePicker->GetFile(getter_AddRefs(pickedDir));
#ifdef DEBUG
{
bool isDir;
pickedDir->IsDirectory(&isDir);
MOZ_ASSERT(isDir);
}
#endif
HTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(
mInput->OwnerDoc(), pickedDir);
nsCOMPtr<nsIEventTarget> target
= do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
NS_ASSERTION(target, "Must have stream transport service");
mInput->StartProgressEventTimer();
// DirPickerFileListBuilderTask takes care of calling SetFiles() and
// dispatching the "change" event.
mInput->mDirPickerFileListBuilderTask =
new DirPickerFileListBuilderTask(mInput.get(), pickedDir.get());
return target->Dispatch(mInput->mDirPickerFileListBuilderTask,
NS_DISPATCH_NORMAL);
}
// Collect new selected filenames
nsTArray<nsRefPtr<File>> newFiles;
if (mode == static_cast<int16_t>(nsIFilePicker::modeOpenMultiple)) {
if (mode == static_cast<int16_t>(nsIFilePicker::modeOpenMultiple) ||
mode == static_cast<int16_t>(nsIFilePicker::modeGetFolder)) {
nsCOMPtr<nsISimpleEnumerator> iter;
nsresult rv = mFilePicker->GetDomfiles(getter_AddRefs(iter));
NS_ENSURE_SUCCESS(rv, rv);
@@ -1165,7 +859,6 @@ HTMLInputElement::HTMLInputElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
, mCanShowInvalidUI(true)
, mHasRange(false)
, mIsDraggingRange(false)
, mProgressTimerIsActive(false)
, mNumberControlSpinnerIsSpinning(false)
, mNumberControlSpinnerSpinsUp(false)
, mPickerRunning(false)
@@ -1190,9 +883,6 @@ HTMLInputElement::HTMLInputElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
HTMLInputElement::~HTMLInputElement()
{
if (mFileList) {
mFileList->Disconnect();
}
if (mNumberControlSpinnerIsSpinning) {
StopNumberControlSpinnerSpin(eDisallowDispatchingEvents);
}
@@ -1247,10 +937,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement,
NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles)
if (tmp->mFileList) {
tmp->mFileList->Disconnect();
tmp->mFileList = nullptr;
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
if (tmp->IsSingleLineTextControl(false)) {
tmp->mInputData.mState->Unlink();
}
@@ -1270,7 +957,6 @@ NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLInputElement)
nsIImageLoadingContent,
imgIOnloadBlocker,
nsIDOMNSEditableElement,
nsITimerCallback,
nsIConstraintValidation)
NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLFormElementWithState)
@@ -2794,58 +2480,6 @@ HTMLInputElement::GetFiles()
return mFileList;
}
void
HTMLInputElement::OpenDirectoryPicker(ErrorResult& aRv)
{
if (mType != NS_FORM_INPUT_FILE) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
}
InitFilePicker(FILE_PICKER_DIRECTORY);
}
void
HTMLInputElement::CancelDirectoryPickerScanIfRunning()
{
if (!mDirPickerFileListBuilderTask) {
return;
}
if (mProgressTimer) {
mProgressTimerIsActive = false;
mProgressTimer->Cancel();
}
mDirPickerFileListBuilderTask->Cancel();
mDirPickerFileListBuilderTask = nullptr;
}
void
HTMLInputElement::StartProgressEventTimer()
{
if (!mProgressTimer) {
mProgressTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
}
if (mProgressTimer) {
mProgressTimerIsActive = true;
mProgressTimer->Cancel();
mProgressTimer->InitWithCallback(this, kProgressEventInterval,
nsITimer::TYPE_ONE_SHOT);
}
}
// nsITimerCallback's only method
NS_IMETHODIMP
HTMLInputElement::Notify(nsITimer* aTimer)
{
if (mProgressTimer == aTimer) {
mProgressTimerIsActive = false;
MaybeDispatchProgressEvent(false);
return NS_OK;
}
// Just in case some JS user wants to QI to nsITimerCallback and play with us...
NS_WARNING("Unexpected timer!");
return NS_ERROR_INVALID_POINTER;
}
/* static */ void
HTMLInputElement::HandleNumberControlSpin(void* aData)
{
@@ -2866,65 +2500,6 @@ HTMLInputElement::HandleNumberControlSpin(void* aData)
}
}
void
HTMLInputElement::MaybeDispatchProgressEvent(bool aFinalProgress)
{
nsRefPtr<HTMLInputElement> kungFuDeathGrip;
if (aFinalProgress && mProgressTimerIsActive) {
// mProgressTimer may hold the last reference to us, so take another strong
// ref to make sure we don't die under Cancel() and leave this method
// running on deleted memory.
kungFuDeathGrip = this;
mProgressTimerIsActive = false;
mProgressTimer->Cancel();
}
uint32_t fileListLength = mDirPickerFileListBuilderTask->GetFileListLength();
if (mProgressTimerIsActive ||
fileListLength == mDirPickerFileListBuilderTask->mPreviousFileListLength) {
return;
}
if (!aFinalProgress) {
StartProgressEventTimer();
}
mDirPickerFileListBuilderTask->mPreviousFileListLength = fileListLength;
DispatchProgressEvent(NS_LITERAL_STRING(PROGRESS_STR),
false,
mDirPickerFileListBuilderTask->mPreviousFileListLength,
0);
}
void
HTMLInputElement::DispatchProgressEvent(const nsAString& aType,
bool aLengthComputable,
uint64_t aLoaded, uint64_t aTotal)
{
NS_ASSERTION(!aType.IsEmpty(), "missing event type");
ProgressEventInit init;
init.mBubbles = false;
init.mCancelable = true; // XXXkhuey why?
init.mLengthComputable = aLengthComputable;
init.mLoaded = aLoaded;
init.mTotal = (aTotal == UINT64_MAX) ? 0 : aTotal;
nsRefPtr<ProgressEvent> event =
ProgressEvent::Constructor(this, aType, init);
event->SetTrusted(true);
bool doDefaultAction;
nsresult rv = DispatchEvent(event, &doDefaultAction);
if (NS_SUCCEEDED(rv) && !doDefaultAction) {
CancelDirectoryPickerScanIfRunning();
}
}
nsresult
HTMLInputElement::UpdateFileList()
{
@@ -3305,7 +2880,9 @@ HTMLInputElement::Focus(ErrorResult& aError)
return;
}
// For file inputs, focus the button instead.
// For file inputs, focus the first button instead. In the case of there
// being two buttons (when the picker is a directory picker) the user can
// tab to the next one.
nsIFrame* frame = GetPrimaryFrame();
if (frame) {
for (nsIFrame* childFrame = frame->GetFirstPrincipalChild();
@@ -3969,7 +3546,21 @@ HTMLInputElement::MaybeInitPickers(EventChainPostVisitor& aVisitor)
return NS_OK;
}
if (mType == NS_FORM_INPUT_FILE) {
return InitFilePicker(FILE_PICKER_FILE);
// If the user clicked on the "Choose folder..." button we open the
// directory picker, else we open the file picker.
FilePickerType type = FILE_PICKER_FILE;
nsCOMPtr<nsIContent> target =
do_QueryInterface(aVisitor.mEvent->originalTarget);
if (target &&
target->GetParent() == this &&
target->IsRootOfNativeAnonymousSubtree() &&
target->HasAttr(kNameSpaceID_None, nsGkAtoms::directory)) {
MOZ_ASSERT(Preferences::GetBool("dom.input.dirpicker", false),
"No API or UI should have been exposed to allow this code to "
"be reached");
type = FILE_PICKER_DIRECTORY;
}
return InitFilePicker(type);
}
if (mType == NS_FORM_INPUT_COLOR) {
return InitColorPicker();
@@ -5155,7 +4746,10 @@ HTMLInputElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
{
nsChangeHint retval =
nsGenericHTMLFormElementWithState::GetAttributeChangeHint(aAttribute, aModType);
if (aAttribute == nsGkAtoms::type) {
if (aAttribute == nsGkAtoms::type ||
// The presence or absence of the 'directory' attribute determines what
// buttons we show for type=file.
aAttribute == nsGkAtoms::directory) {
NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
} else if (mType == NS_FORM_INPUT_IMAGE &&
(aAttribute == nsGkAtoms::alt ||
@@ -5200,6 +4794,110 @@ HTMLInputElement::GetAttributeMappingFunction() const
}
// Directory picking methods:
bool
HTMLInputElement::IsFilesAndDirectoriesSupported() const
{
// This method is supposed to return true if a file and directory picker
// supports the selection of both files and directories *at the same time*.
// Only Mac currently supports that. We could implement it for Mac, but
// currently we do not.
return false;
}
void
HTMLInputElement::ChooseDirectory(ErrorResult& aRv)
{
if (mType != NS_FORM_INPUT_FILE) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
InitFilePicker(
#if defined(ANDROID) || defined(MOZ_B2G)
// No native directory picker - redirect to plain file picker
FILE_PICKER_FILE
#else
FILE_PICKER_DIRECTORY
#endif
);
}
static already_AddRefed<OSFileSystem>
MakeOrReuseFileSystem(const nsAString& aNewLocalRootPath,
OSFileSystem* aFS,
nsPIDOMWindow* aWindow)
{
MOZ_ASSERT(aWindow);
nsRefPtr<OSFileSystem> fs;
if (aFS) {
const nsAString& prevLocalRootPath = aFS->GetLocalRootPath();
if (aNewLocalRootPath == prevLocalRootPath) {
fs = aFS;
}
}
if (!fs) {
fs = new OSFileSystem(aNewLocalRootPath);
fs->Init(aWindow);
}
return fs.forget();
}
already_AddRefed<Promise>
HTMLInputElement::GetFilesAndDirectories(ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
MOZ_ASSERT(global);
if (!global) {
return nullptr;
}
nsRefPtr<Promise> p = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
const nsTArray<nsRefPtr<File>>& filesAndDirs = GetFilesInternal();
Sequence<OwningFileOrDirectory> filesAndDirsSeq;
if (!filesAndDirsSeq.SetLength(filesAndDirs.Length(), mozilla::fallible_t())) {
p->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
return p.forget();
}
nsPIDOMWindow* window = OwnerDoc()->GetInnerWindow();
nsRefPtr<OSFileSystem> fs;
for (uint32_t i = 0; i < filesAndDirs.Length(); ++i) {
if (filesAndDirs[i]->IsDirectory()) {
#if defined(ANDROID) || defined(MOZ_B2G)
MOZ_ASSERT(false,
"Directory picking should have been redirected to normal "
"file picking for platforms that don't have a directory "
"picker");
#endif
nsAutoString path;
filesAndDirs[i]->GetMozFullPathInternal(path, aRv);
if (aRv.Failed()) {
return nullptr;
}
int32_t leafSeparatorIndex = path.RFind(FILE_PATH_SEPARATOR);
nsDependentSubstring dirname = Substring(path, 0, leafSeparatorIndex);
nsDependentSubstring basename = Substring(path, leafSeparatorIndex);
fs = MakeOrReuseFileSystem(dirname, fs, window);
filesAndDirsSeq[i].SetAsDirectory() = new Directory(fs, basename);
} else {
filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i];
}
}
p->MaybeResolve(filesAndDirsSeq);
return p.forget();
}
// Controllers Methods
nsIControllers*
+17 -31
View File
@@ -19,6 +19,7 @@
#include "nsIConstraintValidation.h"
#include "mozilla/dom/HTMLFormElement.h" // for HasEverTriedInvalidSubmit()
#include "mozilla/dom/HTMLInputElementBinding.h"
#include "mozilla/dom/Promise.h"
#include "nsIFilePicker.h"
#include "nsIContentPrefService2.h"
#include "mozilla/Decimal.h"
@@ -36,7 +37,6 @@ class EventChainPreVisitor;
namespace dom {
class Date;
class DirPickerFileListBuilderTask;
class File;
class FileList;
@@ -94,11 +94,8 @@ class HTMLInputElement final : public nsGenericHTMLFormElementWithState,
public nsITextControlElement,
public nsIPhonetic,
public nsIDOMNSEditableElement,
public nsITimerCallback,
public nsIConstraintValidation
{
friend class DirPickerFileListBuilderTask;
public:
using nsIConstraintValidation::GetValidationMessage;
using nsIConstraintValidation::CheckValidity;
@@ -276,14 +273,6 @@ public:
return mSelectionProperties;
}
// nsITimerCallback
NS_DECL_NSITIMERCALLBACK
// Avoid warning about the implementation of nsITimerCallback::Notify hiding
// our nsImageLoadingContent base class' implementation of
// imgINotificationObserver::Notify:
using nsImageLoadingContent::Notify;
bool HasPatternAttribute() const
{
return mHasPatternAttribute;
@@ -451,15 +440,6 @@ public:
FileList* GetFiles();
void OpenDirectoryPicker(ErrorResult& aRv);
void CancelDirectoryPickerScanIfRunning();
void StartProgressEventTimer();
void MaybeDispatchProgressEvent(bool aFinalProgress);
void DispatchProgressEvent(const nsAString& aType,
bool aLengthComputable,
uint64_t aLoaded, uint64_t aTotal);
// XPCOM GetFormAction() is OK
void SetFormAction(const nsAString& aValue, ErrorResult& aRv)
{
@@ -702,6 +682,22 @@ public:
ErrorResult& aRv, int32_t aSelectionStart = -1,
int32_t aSelectionEnd = -1);
bool DirectoryAttr() const
{
return HasAttr(kNameSpaceID_None, nsGkAtoms::directory);
}
void SetDirectoryAttr(bool aValue, ErrorResult& aRv)
{
SetHTMLBoolAttr(nsGkAtoms::directory, aValue, aRv);
}
bool IsFilesAndDirectoriesSupported() const;
already_AddRefed<Promise> GetFilesAndDirectories(ErrorResult& aRv);
void ChooseDirectory(ErrorResult& aRv);
// XPCOM GetAlign() is OK
void SetAlign(const nsAString& aValue, ErrorResult& aRv)
{
@@ -1291,8 +1287,6 @@ protected:
nsRefPtr<FileList> mFileList;
nsRefPtr<DirPickerFileListBuilderTask> mDirPickerFileListBuilderTask;
nsString mStaticDocFileList;
/**
@@ -1311,13 +1305,6 @@ protected:
*/
Decimal mRangeThumbDragStartValue;
/**
* Timer that is used when mType == NS_FORM_INPUT_FILE and the user selects a
* directory. It is used to fire progress events while the list of files
* under that directory tree is built.
*/
nsCOMPtr<nsITimer> mProgressTimer;
/**
* The selection properties cache for number controls. This is needed because
* the number controls don't recycle their text field, so the normal cache in
@@ -1361,7 +1348,6 @@ protected:
bool mCanShowInvalidUI : 1;
bool mHasRange : 1;
bool mIsDraggingRange : 1;
bool mProgressTimerIsActive : 1;
bool mNumberControlSpinnerIsSpinning : 1;
bool mNumberControlSpinnerSpinsUp : 1;
bool mPickerRunning : 1;
+1
View File
@@ -240,6 +240,7 @@ LOCAL_INCLUDES += [
'/dom/xul',
'/editor/libeditor',
'/editor/txmgr',
'/image',
'/layout/forms',
'/layout/generic',
'/layout/style',
+1 -1
View File
@@ -126,7 +126,7 @@ BlobImplSnapshot::CreateSlice(uint64_t aStart,
void
BlobImplSnapshot::GetMozFullPathInternal(nsAString& aFilename,
ErrorResult& aRv)
ErrorResult& aRv) const
{
AssertSanity();
MOZ_ASSERT(mIsFile);
+1 -1
View File
@@ -74,7 +74,7 @@ private:
#endif
virtual void
GetMozFullPathInternal(nsAString& aFullPath, ErrorResult& aRv) override;
GetMozFullPathInternal(nsAString& aFullPath, ErrorResult& aRv) const override;
virtual void
GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override;
+12 -1
View File
@@ -49,7 +49,7 @@ interface nsIJSRAIIHelper;
interface nsIContentPermissionRequest;
interface nsIObserver;
[scriptable, uuid(6064615a-a782-4d08-86db-26ef3851208a)]
[scriptable, uuid(e6d3ced6-fbce-4ea5-902a-c2055680d250)]
interface nsIDOMWindowUtils : nsISupports {
/**
@@ -1840,6 +1840,17 @@ interface nsIDOMWindowUtils : nsISupports {
* @param aSheetType One of the nsIStyleSheetService.*_SHEET constants.
*/
bool hasRuleProcessorUsedByMultipleStyleSets(in unsigned long aSheetType);
/*
* Force the use counters for the node's associated document(s) to be
* flushed to telemetry. For example, a document node will flush its own
* counters and an image node with an SVG source will flush the SVG
* document's counters. Normally, use counters are flushed to telemetry
* upon document destruction, but as document destruction is somewhat
* non-deterministic, we have this method here for more determinism when
* running tests.
*/
void forceUseCounterFlush(in nsIDOMNode aNode);
};
[scriptable, uuid(c694e359-7227-4392-a138-33c0cc1f15a6)]
+26 -6
View File
@@ -1925,7 +1925,7 @@ public:
NS_DECL_ISUPPORTS_INHERITED
virtual void
GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) override;
GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const override;
virtual already_AddRefed<BlobImpl>
CreateSlice(uint64_t aStart,
@@ -2085,10 +2085,10 @@ public:
SetLastModified(int64_t aLastModified) override;
virtual void
GetMozFullPath(nsAString& aName, ErrorResult& aRv) override;
GetMozFullPath(nsAString& aName, ErrorResult& aRv) const override;
virtual void
GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) override;
GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const override;
virtual uint64_t
GetSize(ErrorResult& aRv) override;
@@ -2096,6 +2096,9 @@ public:
virtual void
GetType(nsAString& aType) override;
virtual uint64_t
GetSerialNumber() const override;
virtual already_AddRefed<BlobImpl>
CreateSlice(uint64_t aStart,
uint64_t aLength,
@@ -2151,6 +2154,9 @@ public:
virtual void
LookupAndCacheIsDirectory() override;
virtual void
SetIsDirectory(bool aIsDir) override;
virtual bool
IsDirectory() const override;
@@ -2335,7 +2341,7 @@ NS_IMPL_QUERY_INTERFACE_INHERITED(BlobChild::RemoteBlobImpl,
void
BlobChild::
RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFilePath,
ErrorResult& aRv)
ErrorResult& aRv) const
{
if (!EventTargetIsOnCurrentThread(mActorTarget)) {
MOZ_CRASH("Not implemented!");
@@ -2790,14 +2796,14 @@ RemoteBlobImpl::SetLastModified(int64_t aLastModified)
void
BlobParent::
RemoteBlobImpl::GetMozFullPath(nsAString& aName, ErrorResult& aRv)
RemoteBlobImpl::GetMozFullPath(nsAString& aName, ErrorResult& aRv) const
{
mBlobImpl->GetMozFullPath(aName, aRv);
}
void
BlobParent::
RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv)
RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const
{
mBlobImpl->GetMozFullPathInternal(aFileName, aRv);
}
@@ -2816,6 +2822,13 @@ RemoteBlobImpl::GetType(nsAString& aType)
mBlobImpl->GetType(aType);
}
uint64_t
BlobParent::
RemoteBlobImpl::GetSerialNumber() const
{
return mBlobImpl->GetSerialNumber();
}
already_AddRefed<BlobImpl>
BlobParent::
RemoteBlobImpl::CreateSlice(uint64_t aStart,
@@ -2934,6 +2947,13 @@ RemoteBlobImpl::LookupAndCacheIsDirectory()
return mBlobImpl->LookupAndCacheIsDirectory();
}
void
BlobParent::
RemoteBlobImpl::SetIsDirectory(bool aIsDir)
{
return mBlobImpl->SetIsDirectory(aIsDir);
}
bool
BlobParent::
RemoteBlobImpl::IsDirectory() const
+25 -1
View File
@@ -12,6 +12,7 @@
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "nsIObserverService.h"
using namespace mozilla::ipc;
using namespace mozilla::jsipc;
@@ -19,7 +20,9 @@ using namespace mozilla::jsipc;
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(ContentBridgeChild, nsIContentChild)
NS_IMPL_ISUPPORTS(ContentBridgeChild,
nsIContentChild,
nsIObserver)
ContentBridgeChild::ContentBridgeChild(Transport* aTransport)
: mTransport(aTransport)
@@ -33,6 +36,10 @@ ContentBridgeChild::~ContentBridgeChild()
void
ContentBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
{
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
os->RemoveObserver(this, "content-child-shutdown");
}
MessageLoop::current()->PostTask(
FROM_HERE,
NewRunnableMethod(this, &ContentBridgeChild::DeferredDestroy));
@@ -47,6 +54,12 @@ ContentBridgeChild::Create(Transport* aTransport, ProcessId aOtherPid)
DebugOnly<bool> ok = bridge->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop());
MOZ_ASSERT(ok);
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
os->AddObserver(bridge, "content-child-shutdown", false);
}
return bridge;
}
@@ -167,5 +180,16 @@ ContentBridgeChild::DeallocPBlobChild(PBlobChild* aActor)
return nsIContentChild::DeallocPBlobChild(aActor);
}
NS_IMETHODIMP
ContentBridgeChild::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
if (!strcmp(aTopic, "content-child-shutdown")) {
Close();
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla
+3
View File
@@ -9,17 +9,20 @@
#include "mozilla/dom/PContentBridgeChild.h"
#include "mozilla/dom/nsIContentChild.h"
#include "nsIObserver.h"
namespace mozilla {
namespace dom {
class ContentBridgeChild final : public PContentBridgeChild
, public nsIContentChild
, public nsIObserver
{
public:
explicit ContentBridgeChild(Transport* aTransport);
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
static ContentBridgeChild*
Create(Transport* aTransport, ProcessId aOtherProcess);
+8
View File
@@ -757,6 +757,14 @@ ContentChild::AppendProcessId(nsACString& aName)
aName.Append(nsPrintfCString("(pid %u)", pid));
}
void
ContentChild::InitGraphicsDeviceData()
{
// Initialize the graphics platform. This may contact the parent process
// to read device preferences.
gfxPlatform::GetPlatform();
}
void
ContentChild::InitXPCOM()
{
+1
View File
@@ -76,6 +76,7 @@ public:
IPC::Channel* aChannel);
void InitProcessAttributes();
void InitXPCOM();
void InitGraphicsDeviceData();
static ContentChild* GetSingleton() {
return sSingleton;
+51 -1
View File
@@ -2019,9 +2019,13 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
MessageLoop::current()->PostTask(
FROM_HERE,
NewRunnableMethod(cp, &ContentParent::ShutDownProcess,
CLOSE_CHANNEL));
SEND_SHUTDOWN_MESSAGE));
}
cpm->RemoveContentProcess(this->ChildID());
if (mDriverCrashGuard) {
mDriverCrashGuard->NotifyCrashed();
}
}
void
@@ -5050,6 +5054,52 @@ ContentParent::RecvProfile(const nsCString& aProfile)
return true;
}
bool
ContentParent::RecvGetGraphicsDeviceInitData(DeviceInitData* aOut)
{
gfxPlatform::GetPlatform()->GetDeviceInitData(aOut);
return true;
}
bool
ContentParent::RecvBeginDriverCrashGuard(const uint32_t& aGuardType, bool* aOutCrashed)
{
// Only one driver crash guard should be active at a time, per-process.
MOZ_ASSERT(!mDriverCrashGuard);
UniquePtr<gfx::DriverCrashGuard> guard;
switch (gfx::CrashGuardType(aGuardType)) {
case gfx::CrashGuardType::D3D11Layers:
guard = MakeUnique<gfx::D3D11LayersCrashGuard>(this);
break;
case gfx::CrashGuardType::D3D9Video:
guard = MakeUnique<gfx::D3D9VideoCrashGuard>(this);
break;
case gfx::CrashGuardType::GLContext:
guard = MakeUnique<gfx::GLContextCrashGuard>(this);
break;
default:
MOZ_ASSERT_UNREACHABLE("unknown crash guard type");
return false;
}
if (guard->Crashed()) {
*aOutCrashed = true;
return true;
}
*aOutCrashed = false;
mDriverCrashGuard = Move(guard);
return true;
}
bool
ContentParent::RecvEndDriverCrashGuard(const uint32_t& aGuardType)
{
mDriverCrashGuard = nullptr;
return true;
}
} // namespace dom
} // namespace mozilla
+6
View File
@@ -25,6 +25,7 @@
#include "nsIDOMGeoPositionCallback.h"
#include "nsIDOMGeoPositionErrorCallback.h"
#include "PermissionMessageUtils.h"
#include "DriverCrashGuard.h"
#define CHILD_PROCESS_SHUTDOWN_MESSAGE NS_LITERAL_STRING("child-process-shutdown")
@@ -801,6 +802,8 @@ private:
virtual bool RecvGetGraphicsFeatureStatus(const int32_t& aFeature,
int32_t* aStatus,
bool* aSuccess) override;
virtual bool RecvBeginDriverCrashGuard(const uint32_t& aGuardType, bool* aOutCrashed) override;
virtual bool RecvEndDriverCrashGuard(const uint32_t& aGuardType) override;
virtual bool RecvAddIdleObserver(const uint64_t& observerId,
const uint32_t& aIdleTimeInS) override;
@@ -853,6 +856,7 @@ private:
virtual bool RecvGamepadListenerAdded() override;
virtual bool RecvGamepadListenerRemoved() override;
virtual bool RecvProfile(const nsCString& aProfile) override;
virtual bool RecvGetGraphicsDeviceInitData(DeviceInitData* aOut) override;
// If you add strong pointers to cycle collected objects here, be sure to
// release these objects in ShutDownProcess. See the comment there for more
@@ -938,6 +942,8 @@ private:
nsRefPtr<mozilla::ProfileGatherer> mGatherer;
#endif
nsCString mProfile;
UniquePtr<gfx::DriverCrashGuard> mDriverCrashGuard;
};
} // namespace dom
+3 -2
View File
@@ -99,10 +99,11 @@ bool
ContentProcess::Init()
{
mContent.Init(IOThreadChild::message_loop(),
ParentPid(),
IOThreadChild::channel());
ParentPid(),
IOThreadChild::channel());
mXREEmbed.Start();
mContent.InitXPCOM();
mContent.InitGraphicsDeviceData();
#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
SetUpSandboxEnvironment();
+4 -1
View File
@@ -91,6 +91,7 @@ FilePickerParent::FileSizeAndDateRunnable::Run()
ErrorResult rv;
mBlobs[i]->GetSize(rv);
mBlobs[i]->GetLastModified(rv);
mBlobs[i]->LookupAndCacheIsDirectory();
}
// Dispatch ourselves back on the main thread.
@@ -138,7 +139,8 @@ FilePickerParent::Done(int16_t aResult)
}
nsTArray<nsRefPtr<BlobImpl>> blobs;
if (mMode == nsIFilePicker::modeOpenMultiple) {
if (mMode == nsIFilePicker::modeOpenMultiple ||
mMode == nsIFilePicker::modeGetFolder) {
nsCOMPtr<nsISimpleEnumerator> iter;
NS_ENSURE_SUCCESS_VOID(mFilePicker->GetFiles(getter_AddRefs(iter)));
@@ -164,6 +166,7 @@ FilePickerParent::Done(int16_t aResult)
MOZ_ASSERT(!mRunnable);
mRunnable = new FileSizeAndDateRunnable(this, blobs);
// Dispatch to background thread to do I/O:
if (!mRunnable->Dispatch()) {
unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel);
}
+11
View File
@@ -60,6 +60,7 @@ include PluginTypes;
include ProtocolTypes;
include PContentPermission;
include BrowserConfiguration;
include GraphicsMessages;
// Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
// are put into different UnifiedProtocolsXX.cpp files.
@@ -929,6 +930,10 @@ parent:
sync GetGraphicsFeatureStatus(int32_t aFeature) returns (int32_t aStatus, bool aSuccess);
// Driver crash guards. aGuardType must be a member of CrashGuardType.
sync BeginDriverCrashGuard(uint32_t aGuardType) returns (bool crashDetected);
sync EndDriverCrashGuard(uint32_t aGuardType);
AddIdleObserver(uint64_t observerId, uint32_t idleTimeInS);
RemoveIdleObserver(uint64_t observerId, uint32_t idleTimeInS);
@@ -1061,6 +1066,12 @@ parent:
async Profile(nsCString aProfile);
/**
* Request graphics initialization information from the parent.
*/
sync GetGraphicsDeviceInitData()
returns (DeviceInitData aData);
both:
AsyncMessage(nsString aMessage, ClonedMessageData aData,
CpowEntry[] aCpows, Principal aPrincipal);
@@ -5,6 +5,8 @@
Reset=Reset
Submit=Submit Query
Browse=Browse…
ChooseFiles=Choose files…
ChooseDirs=Choose folder…
FileUpload=File Upload
# LOCALIZATION NOTE (IsIndexPromptWithSpace): The last character of the string
# should be a space (U+0020) in most locales. The prompt is followed by an
+7
View File
@@ -14,6 +14,7 @@
#include "mozilla/Preferences.h"
#include "mfapi.h"
#include "MFTDecoder.h"
#include "DriverCrashGuard.h"
const CLSID CLSID_VideoProcessorMFT =
{
@@ -90,6 +91,12 @@ D3D9DXVA2Manager::Init()
{
MOZ_ASSERT(NS_IsMainThread());
gfx::D3D9VideoCrashGuard crashGuard;
if (crashGuard.Crashed()) {
NS_WARNING("DXVA2D3D9 crash detected");
return E_FAIL;
}
// Create D3D9Ex.
HMODULE d3d9lib = LoadLibraryW(L"d3d9.dll");
NS_ENSURE_TRUE(d3d9lib, E_FAIL);
+2 -12
View File
@@ -26,7 +26,7 @@ enum nsPluginTagType {
// Do not make this interface scriptable, because the virtual functions in C++
// blocks will make script call the wrong functions.
[uuid(c4e26e5d-7a9b-4900-b567-e128c4be6e31)]
[uuid(8ff5f46e-96fa-4905-a75c-35aac30bdcee)]
interface nsIPluginInstanceOwner : nsISupports
{
/**
@@ -69,15 +69,6 @@ interface nsIPluginInstanceOwner : nsISupports
void *aHeadersData, uint32_t aHeadersDataLen) = 0;
%}
/**
* Show a status message in the host environment.
*/
void showStatus(in string aStatusMsg);
%{C++
NS_IMETHOD ShowStatus(const char16_t *aStatusMsg) = 0;
%}
/**
* Get the associated document.
*/
@@ -104,10 +95,9 @@ interface nsIPluginInstanceOwner : nsISupports
void getNetscapeWindow(in voidPtr aValue);
/**
* Show native context menu
* Convert between plugin, window, and screen coordinate spaces.
*/
%{C++
virtual NPError ShowNativeContextMenu(NPMenu* menu, void* event) = 0;
virtual NPBool ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
double *destX, double *destY, NPCoordinateSpace destSpace) = 0;
%}
+2 -18
View File
@@ -1066,7 +1066,7 @@ _destroystream(NPP npp, NPStream *pstream, NPError reason)
// the reference until it is to be deleted here. Deleting the wrapper will
// release the wrapped nsIOutputStream.
//
// The NPStream the plugin references should always be a sub-object of it's own
// The NPStream the plugin references should always be a sub-object of its own
// 'ndata', which is our nsNPAPIStramWrapper. See bug 548441.
NS_ASSERTION((char*)streamWrapper <= (char*)pstream &&
((char*)pstream) + sizeof(*pstream)
@@ -1084,23 +1084,7 @@ _destroystream(NPP npp, NPStream *pstream, NPError reason)
void
_status(NPP npp, const char *message)
{
if (!NS_IsMainThread()) {
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_status called from the wrong thread\n"));
return;
}
NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("NPN_Status: npp=%p, message=%s\n",
(void*)npp, message));
if (!npp || !npp->ndata) {
NS_WARNING("_status: npp or npp->ndata == 0");
return;
}
nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance*)npp->ndata;
PluginDestructionGuard guard(inst);
inst->ShowStatus(message);
// NPN_Status is no longer supported.
}
void
@@ -1604,15 +1604,6 @@ nsNPAPIPluginInstance::SetOwner(nsPluginInstanceOwner *aOwner)
mOwner = aOwner;
}
nsresult
nsNPAPIPluginInstance::ShowStatus(const char* message)
{
if (mOwner)
return mOwner->ShowStatus(message);
return NS_ERROR_FAILURE;
}
nsresult
nsNPAPIPluginInstance::AsyncSetWindow(NPWindow& window)
{
-1
View File
@@ -117,7 +117,6 @@ public:
nsresult GetJSContext(JSContext* *outContext);
nsPluginInstanceOwner* GetOwner();
void SetOwner(nsPluginInstanceOwner *aOwner);
nsresult ShowStatus(const char* message);
nsNPAPIPlugin* GetPlugin();
@@ -547,43 +547,6 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetURL(const char *aURL,
return rv;
}
NS_IMETHODIMP nsPluginInstanceOwner::ShowStatus(const char *aStatusMsg)
{
nsresult rv = NS_ERROR_FAILURE;
rv = this->ShowStatus(NS_ConvertUTF8toUTF16(aStatusMsg).get());
return rv;
}
NS_IMETHODIMP nsPluginInstanceOwner::ShowStatus(const char16_t *aStatusMsg)
{
nsresult rv = NS_ERROR_FAILURE;
if (!mPluginFrame) {
return rv;
}
nsCOMPtr<nsIDocShellTreeItem> docShellItem = mPluginFrame->PresContext()->GetDocShell();
if (NS_FAILED(rv) || !docShellItem) {
return rv;
}
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
rv = docShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
if (NS_FAILED(rv) || !treeOwner) {
return rv;
}
nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(treeOwner, &rv));
if (NS_FAILED(rv) || !browserChrome) {
return rv;
}
rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT,
aStatusMsg);
return rv;
}
NS_IMETHODIMP nsPluginInstanceOwner::GetDocument(nsIDocument* *aDocument)
{
nsCOMPtr<nsIContent> content = do_QueryReferent(mContent);
@@ -747,13 +710,6 @@ NS_IMETHODIMP nsPluginInstanceOwner::SetEventModel(int32_t eventModel)
#endif
}
// This is no longer used, just leaving it here so we don't have to change
// the nsIPluginInstanceOwner interface.
NPError nsPluginInstanceOwner::ShowNativeContextMenu(NPMenu* menu, void* event)
{
return NPERR_GENERIC_ERROR;
}
#ifdef XP_MACOSX
NPBool nsPluginInstanceOwner::ConvertPointPuppet(PuppetWidget *widget,
nsPluginFrame* pluginFrame,
-5
View File
@@ -63,11 +63,6 @@ public:
nsIInputStream *aPostStream,
void *aHeadersData, uint32_t aHeadersDataLen) override;
NS_IMETHOD ShowStatus(const char16_t *aStatusMsg) override;
// This can go away, just leaving it here to avoid changing the interface.
NPError ShowNativeContextMenu(NPMenu* menu, void* event) override;
NPBool ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
double *destX, double *destY, NPCoordinateSpace destSpace) override;
+1 -3
View File
@@ -1292,9 +1292,7 @@ void
_status(NPP aNPP,
const char* aMessage)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD_VOID();
NS_WARNING("Not yet implemented!");
// NPN_Status is no longer supported.
}
void
+5
View File
@@ -28,6 +28,11 @@ interface DataTransfer {
readonly attribute FileList? files;
};
partial interface DataTransfer {
[Throws, Pref="dom.input.dirpicker"]
Promise<sequence<(File or Directory)>> getFilesAndDirectories();
};
// Mozilla specific stuff
partial interface DataTransfer {
/*
+14 -2
View File
@@ -40,8 +40,6 @@ interface HTMLInputElement : HTMLElement {
readonly attribute HTMLFormElement? form;
[Pure]
readonly attribute FileList? files;
[Throws, Pref="dom.input.dirpicker"]
void openDirectoryPicker();
[Pure, SetterThrows]
attribute DOMString formAction;
[Pure, SetterThrows]
@@ -191,6 +189,20 @@ partial interface HTMLInputElement {
void setUserInput(DOMString input);
};
partial interface HTMLInputElement {
[Pref="dom.input.dirpicker", BinaryName="DirectoryAttr", SetterThrows]
attribute boolean directory;
[Pref="dom.input.dirpicker"]
readonly attribute boolean isFilesAndDirectoriesSupported;
[Throws, Pref="dom.input.dirpicker"]
Promise<sequence<(File or Directory)>> getFilesAndDirectories();
[Throws, Pref="dom.input.dirpicker"]
void chooseDirectory();
};
[NoInterfaceObject]
interface MozPhonetic {
[Pure, ChromeOnly]
+6
View File
@@ -27,6 +27,7 @@
#include "TextureGarbageBin.h"
#include "gfx2DGlue.h"
#include "gfxPrefs.h"
#include "DriverCrashGuard.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "OGLShaderProgram.h" // for ShaderProgramType
@@ -353,6 +354,11 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl)
return true;
}
GLContextCrashGuard crashGuard;
if (crashGuard.Crashed()) {
return false;
}
mWorkAroundDriverBugs = gfxPrefs::WorkAroundDriverBugs();
SymLoadStruct symbols[] = {
+25
View File
@@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=8 et :
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
namespace mozilla {
namespace gfx {
struct DeviceInitData
{
bool useAcceleration;
// Windows only.
bool useD3D11;
bool useD3D11WARP;
bool useD3D11ImageBridge;
bool d3d11TextureSharingWorks;
bool useD2D;
bool useD2D1;
};
} // namespace gfx
} // namespace mozilla
+4
View File
@@ -26,6 +26,10 @@ SOURCES += [
'SharedDIB.cpp',
]
IPDL_SOURCES = [
'GraphicsMessages.ipdlh',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
+503
View File
@@ -0,0 +1,503 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DriverCrashGuard.h"
#include "gfxPrefs.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsXULAppAPI.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Services.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/dom/ContentChild.h"
namespace mozilla {
namespace gfx {
static const size_t NUM_CRASH_GUARD_TYPES = size_t(CrashGuardType::NUM_TYPES);
static const char* sCrashGuardNames[NUM_CRASH_GUARD_TYPES] = {
"d3d11layers",
"d3d9video",
"glcontext",
};
DriverCrashGuard::DriverCrashGuard(CrashGuardType aType, dom::ContentParent* aContentParent)
: mType(aType)
, mMode(aContentParent ? Mode::Proxy : Mode::Normal)
, mInitialized(false)
, mGuardActivated(false)
, mCrashDetected(false)
{
MOZ_ASSERT(mType < CrashGuardType::NUM_TYPES);
mStatusPref.Assign("gfx.crash-guard.status.");
mStatusPref.Append(sCrashGuardNames[size_t(mType)]);
}
void
DriverCrashGuard::InitializeIfNeeded()
{
if (mInitialized) {
return;
}
mInitialized = true;
Initialize();
}
void
DriverCrashGuard::Initialize()
{
// Using DriverCrashGuard off the main thread currently does not work. Under
// e10s it could conceivably work by dispatching the IPC calls via the main
// thread. In the parent process this would be harder. For now, we simply
// exit early instead.
if (!NS_IsMainThread()) {
return;
}
mGfxInfo = services::GetGfxInfo();
if (XRE_IsContentProcess()) {
// Ask the parent whether or not activating the guard is okay. The parent
// won't bother if it detected a crash.
dom::ContentChild* cc = dom::ContentChild::GetSingleton();
cc->SendBeginDriverCrashGuard(uint32_t(mType), &mCrashDetected);
if (mCrashDetected) {
LogFeatureDisabled();
return;
}
ActivateGuard();
return;
}
// Always check whether or not the lock file exists. For example, we could
// have crashed creating a D3D9 device in the parent process, and on restart
// are now requesting one in the child process. We catch everything here.
if (RecoverFromCrash()) {
mCrashDetected = true;
return;
}
// If the environment has changed, we always activate the guard. In the
// parent process this performs main-thread disk I/O. Child process guards
// only incur an IPC cost, so if we're proxying for a child process, we
// play it safe and activate the guard as long as we don't expect it to
// crash.
if (CheckOrRefreshEnvironment() ||
(mMode == Mode::Proxy && GetStatus() != DriverInitStatus::Crashed))
{
ActivateGuard();
return;
}
// If we got here and our status is "crashed", then the environment has not
// updated and we do not want to attempt to use the driver again.
if (GetStatus() == DriverInitStatus::Crashed) {
mCrashDetected = true;
LogFeatureDisabled();
}
}
DriverCrashGuard::~DriverCrashGuard()
{
if (!mGuardActivated) {
return;
}
if (XRE_IsParentProcess()) {
if (mGuardFile) {
mGuardFile->Remove(false);
}
// If during our initialization, no other process encountered a crash, we
// proceed to mark the status as okay.
if (GetStatus() != DriverInitStatus::Crashed) {
SetStatus(DriverInitStatus::Okay);
}
} else {
dom::ContentChild::GetSingleton()->SendEndDriverCrashGuard(uint32_t(mType));
}
}
bool
DriverCrashGuard::Crashed()
{
InitializeIfNeeded();
// Note, we read mCrashDetected instead of GetStatus(), since in child
// processes we're not guaranteed that the prefs have been synced in
// time.
return mCrashDetected;
}
nsCOMPtr<nsIFile>
DriverCrashGuard::GetGuardFile()
{
MOZ_ASSERT(XRE_IsParentProcess());
nsCString filename;
filename.Assign(sCrashGuardNames[size_t(mType)]);
filename.Append(".guard");
nsCOMPtr<nsIFile> file;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, getter_AddRefs(file));
if (!file) {
return nullptr;
}
if (!NS_SUCCEEDED(file->AppendNative(filename))) {
return nullptr;
}
return file;
}
void
DriverCrashGuard::ActivateGuard()
{
mGuardActivated = true;
#ifdef MOZ_CRASHREPORTER
// Anotate crash reports only if we're a real guard. Otherwise, we could
// attribute a random parent process crash to a graphics problem in a child
// process.
if (mMode != Mode::Proxy) {
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("GraphicsStartupTest"),
NS_LITERAL_CSTRING("1"));
}
#endif
// If we're in the content process, the rest of the guarding is handled
// in the parent.
if (XRE_IsContentProcess()) {
return;
}
SetStatus(DriverInitStatus::Attempting);
if (mMode != Mode::Proxy) {
// In parent process guards, we use two tombstones to detect crashes: a
// preferences and a zero-byte file on the filesystem.
FlushPreferences();
// Create a temporary tombstone/lockfile.
FILE* fp = nullptr;
mGuardFile = GetGuardFile();
if (!mGuardFile || !NS_SUCCEEDED(mGuardFile->OpenANSIFileDesc("w", &fp))) {
return;
}
fclose(fp);
}
}
void
DriverCrashGuard::NotifyCrashed()
{
CheckOrRefreshEnvironment();
SetStatus(DriverInitStatus::Crashed);
FlushPreferences();
LogCrashRecovery();
}
bool
DriverCrashGuard::RecoverFromCrash()
{
MOZ_ASSERT(XRE_IsParentProcess());
nsCOMPtr<nsIFile> file = GetGuardFile();
bool exists;
if ((file &&
NS_SUCCEEDED(file->Exists(&exists)) &&
exists) ||
(GetStatus() == DriverInitStatus::Attempting))
{
// If we get here, we've just recovered from a crash. Disable acceleration
// until the environment changes.
if (file) {
file->Remove(false);
}
NotifyCrashed();
return true;
}
return false;
}
// Return true if the caller should proceed to guard for crashes. False if
// the environment has not changed. We persist the "changed" status across
// calls, so that after an environment changes, all guards for the new
// session are activated rather than just the first.
bool
DriverCrashGuard::CheckOrRefreshEnvironment()
{
// Our result can be cached statically since we don't check live prefs.
static bool sBaseInfoChanged = false;
static bool sBaseInfoChecked = false;
if (!sBaseInfoChecked) {
// None of the prefs we care about, so we cache the result statically.
sBaseInfoChecked = true;
sBaseInfoChanged = UpdateBaseEnvironment();
}
// Always update the full environment, even if the base info didn't change.
return UpdateEnvironment() ||
sBaseInfoChanged ||
GetStatus() == DriverInitStatus::Unknown;
}
bool
DriverCrashGuard::UpdateBaseEnvironment()
{
bool changed = false;
if (mGfxInfo) {
nsString value;
// Driver properties.
mGfxInfo->GetAdapterDriverVersion(value);
changed |= CheckAndUpdatePref("driverVersion", value);
mGfxInfo->GetAdapterDeviceID(value);
changed |= CheckAndUpdatePref("deviceID", value);
}
// Firefox properties.
changed |= CheckAndUpdatePref("appVersion", NS_LITERAL_STRING(MOZ_APP_VERSION));
return changed;
}
bool
DriverCrashGuard::FeatureEnabled(int aFeature, bool aDefault)
{
if (!mGfxInfo) {
return aDefault;
}
int32_t status;
if (!NS_SUCCEEDED(mGfxInfo->GetFeatureStatus(aFeature, &status))) {
return false;
}
return status == nsIGfxInfo::FEATURE_STATUS_OK;
}
bool
DriverCrashGuard::CheckAndUpdateBoolPref(const char* aPrefName, bool aCurrentValue)
{
std::string pref = GetFullPrefName(aPrefName);
bool oldValue;
if (NS_SUCCEEDED(Preferences::GetBool(pref.c_str(), &oldValue)) &&
oldValue == aCurrentValue)
{
return false;
}
Preferences::SetBool(pref.c_str(), aCurrentValue);
return true;
}
bool
DriverCrashGuard::CheckAndUpdatePref(const char* aPrefName, const nsAString& aCurrentValue)
{
std::string pref = GetFullPrefName(aPrefName);
nsAdoptingString oldValue = Preferences::GetString(pref.c_str());
if (oldValue == aCurrentValue) {
return false;
}
Preferences::SetString(pref.c_str(), aCurrentValue);
return true;
}
std::string
DriverCrashGuard::GetFullPrefName(const char* aPref)
{
return std::string("gfx.crash-guard.") +
std::string(sCrashGuardNames[uint32_t(mType)]) +
std::string(".") +
std::string(aPref);
}
DriverInitStatus
DriverCrashGuard::GetStatus() const
{
return (DriverInitStatus)Preferences::GetInt(mStatusPref.get(), 0);
}
void
DriverCrashGuard::SetStatus(DriverInitStatus aStatus)
{
MOZ_ASSERT(XRE_IsParentProcess());
Preferences::SetInt(mStatusPref.get(), int32_t(aStatus));
}
void
DriverCrashGuard::FlushPreferences()
{
MOZ_ASSERT(XRE_IsParentProcess());
if (nsIPrefService* prefService = Preferences::GetService()) {
prefService->SavePrefFile(nullptr);
}
}
D3D11LayersCrashGuard::D3D11LayersCrashGuard(dom::ContentParent* aContentParent)
: DriverCrashGuard(CrashGuardType::D3D11Layers, aContentParent)
{
}
void
D3D11LayersCrashGuard::Initialize()
{
if (!XRE_IsParentProcess()) {
// We assume the parent process already performed crash detection for
// graphics devices.
return;
}
DriverCrashGuard::Initialize();
// If no telemetry states have been recorded, this will set the state to okay.
// Otherwise, it will have no effect.
RecordTelemetry(TelemetryState::Okay);
}
bool
D3D11LayersCrashGuard::UpdateEnvironment()
{
// Our result can be cached statically since we don't check live prefs.
static bool checked = false;
static bool changed = false;
if (checked) {
return changed;
}
checked = true;
// Feature status.
#if defined(XP_WIN)
bool d2dEnabled = gfxPrefs::Direct2DForceEnabled() ||
(!gfxPrefs::Direct2DDisabled() && FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT2D));
changed |= CheckAndUpdateBoolPref("feature-d2d", d2dEnabled);
bool d3d11Enabled = !gfxPrefs::LayersPreferD3D9();
if (!FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS)) {
d3d11Enabled = false;
}
changed |= CheckAndUpdateBoolPref("feature-d3d11", d3d11Enabled);
#endif
if (!changed) {
return false;
}
RecordTelemetry(TelemetryState::EnvironmentChanged);
return true;
}
void
D3D11LayersCrashGuard::LogCrashRecovery()
{
RecordTelemetry(TelemetryState::RecoveredFromCrash);
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "D3D11 layers just crashed; D3D11 will be disabled.";
}
void
D3D11LayersCrashGuard::LogFeatureDisabled()
{
RecordTelemetry(TelemetryState::FeatureDisabled);
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "D3D11 layers disabled due to a prior crash.";
}
void
D3D11LayersCrashGuard::RecordTelemetry(TelemetryState aState)
{
// D3D11LayersCrashGuard is a no-op in the child process.
if (!XRE_IsParentProcess()) {
return;
}
// Since we instantiate this class more than once, make sure we only record
// the first state (since that is really all we care about).
static bool sTelemetryStateRecorded = false;
if (sTelemetryStateRecorded) {
return;
}
Telemetry::Accumulate(Telemetry::GRAPHICS_DRIVER_STARTUP_TEST, int32_t(aState));
sTelemetryStateRecorded = true;
}
D3D9VideoCrashGuard::D3D9VideoCrashGuard(dom::ContentParent* aContentParent)
: DriverCrashGuard(CrashGuardType::D3D9Video, aContentParent)
{
}
bool
D3D9VideoCrashGuard::UpdateEnvironment()
{
// We don't care about any extra preferences here.
return false;
}
void
D3D9VideoCrashGuard::LogCrashRecovery()
{
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "DXVA2D3D9 just crashed; hardware video will be disabled.";
}
void
D3D9VideoCrashGuard::LogFeatureDisabled()
{
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "DXVA2D3D9 video decoding is disabled due to a previous crash.";
}
GLContextCrashGuard::GLContextCrashGuard(dom::ContentParent* aContentParent)
: DriverCrashGuard(CrashGuardType::GLContext, aContentParent)
{
}
bool
GLContextCrashGuard::UpdateEnvironment()
{
static bool checked = false;
static bool changed = false;
if (checked) {
return changed;
}
checked = true;
#if defined(XP_WIN)
changed |= CheckAndUpdateBoolPref("gfx.driver-init.webgl-angle-force-d3d11",
gfxPrefs::WebGLANGLEForceD3D11());
changed |= CheckAndUpdateBoolPref("gfx.driver-init.webgl-angle-try-d3d11",
gfxPrefs::WebGLANGLETryD3D11());
changed |= CheckAndUpdateBoolPref("gfx.driver-init.webgl-angle-force-warp",
gfxPrefs::WebGLANGLEForceWARP());
changed |= CheckAndUpdateBoolPref("gfx.driver-init.webgl-angle",
FeatureEnabled(nsIGfxInfo::FEATURE_WEBGL_ANGLE, false));
changed |= CheckAndUpdateBoolPref("gfx.driver-init.direct3d11-angle",
FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE, false));
#endif
return changed;
}
void
GLContextCrashGuard::LogCrashRecovery()
{
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "GLContext just crashed and is now disabled.";
}
void
GLContextCrashGuard::LogFeatureDisabled()
{
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "GLContext is disabled due to a previous crash.";
}
} // namespace gfx
} // namespace mozilla
+161
View File
@@ -0,0 +1,161 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef gfx_src_DriverCrashGuard_h__
#define gfx_src_DriverCrashGuard_h__
#include "gfxCore.h"
#include "nsCOMPtr.h"
#include "nsIGfxInfo.h"
#include "nsIFile.h"
#include "nsString.h"
#include <string>
namespace mozilla {
namespace dom {
class ContentParent;
} // namespace dom
namespace gfx {
enum class DriverInitStatus
{
// Drivers have not been initialized yet.
Unknown,
// We're attempting to initialize drivers.
Attempting,
// Drivers were successfully initialized last run.
Okay,
// We crashed during driver initialization, and have restarted.
Crashed
};
enum class CrashGuardType : uint32_t
{
D3D11Layers,
D3D9Video,
GLContext,
NUM_TYPES
};
// DriverCrashGuard is used to detect crashes at graphics driver callsites.
//
// If the graphics environment is unrecognized or has changed since the last
// session, the crash guard will activate and will detect any crashes within
// the scope of the guard object.
//
// If a callsite has a previously encountered crash, and the environment has
// not changed since the last session, then the guard will set a status flag
// indicating that the driver should not be used.
class DriverCrashGuard
{
public:
DriverCrashGuard(CrashGuardType aType, dom::ContentParent* aContentParent);
virtual ~DriverCrashGuard();
bool Crashed();
void NotifyCrashed();
// These are the values reported to Telemetry (GRAPHICS_DRIVER_STARTUP_TEST).
// Values should not change; add new values to the end.
enum class TelemetryState {
Okay = 0,
EnvironmentChanged = 1,
RecoveredFromCrash = 2,
FeatureDisabled = 3
};
enum class Mode {
// Normal operation.
Normal,
// Acting as a proxy between the parent and child process.
Proxy
};
protected:
virtual void Initialize();
virtual bool UpdateEnvironment() = 0;
virtual void LogCrashRecovery() = 0;
virtual void LogFeatureDisabled() = 0;
// Helper functions.
bool FeatureEnabled(int aFeature, bool aDefault=true);
bool CheckAndUpdatePref(const char* aPrefName, const nsAString& aCurrentValue);
bool CheckAndUpdateBoolPref(const char* aPrefName, bool aCurrentValue);
std::string GetFullPrefName(const char* aPref);
private:
// Either process.
void InitializeIfNeeded();
bool CheckOrRefreshEnvironment();
bool UpdateBaseEnvironment();
DriverInitStatus GetStatus() const;
// Parent process only.
nsCOMPtr<nsIFile> GetGuardFile();
bool RecoverFromCrash();
void ActivateGuard();
void FlushPreferences();
void SetStatus(DriverInitStatus aStatus);
private:
CrashGuardType mType;
Mode mMode;
bool mInitialized;
bool mGuardActivated;
bool mCrashDetected;
nsCOMPtr<nsIFile> mGuardFile;
protected:
nsCString mStatusPref;
nsCOMPtr<nsIGfxInfo> mGfxInfo;
};
class D3D11LayersCrashGuard final : public DriverCrashGuard
{
public:
explicit D3D11LayersCrashGuard(dom::ContentParent* aContentParent = nullptr);
protected:
void Initialize() override;
bool UpdateEnvironment() override;
void LogCrashRecovery() override;
void LogFeatureDisabled() override;
private:
void RecordTelemetry(TelemetryState aState);
};
class D3D9VideoCrashGuard final : public DriverCrashGuard
{
public:
explicit D3D9VideoCrashGuard(dom::ContentParent* aContentParent = nullptr);
protected:
bool UpdateEnvironment() override;
void LogCrashRecovery() override;
void LogFeatureDisabled() override;
};
class GLContextCrashGuard final : public DriverCrashGuard
{
public:
explicit GLContextCrashGuard(dom::ContentParent* aContentParent = nullptr);
protected:
bool UpdateEnvironment() override;
void LogCrashRecovery() override;
void LogFeatureDisabled() override;
};
} // namespace gfx
} // namespace mozilla
#endif // gfx_src_DriverCrashGuard_h__
-236
View File
@@ -1,236 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DriverInitCrashDetection.h"
#include "gfxPrefs.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsXULAppAPI.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Services.h"
#include "mozilla/gfx/Logging.h"
namespace mozilla {
namespace gfx {
bool DriverInitCrashDetection::sEnvironmentHasBeenUpdated = false;
DriverInitCrashDetection::DriverInitCrashDetection()
: mIsChromeProcess(XRE_GetProcessType() == GeckoProcessType_Default)
{
if (!mIsChromeProcess) {
// We assume the parent process already performed crash detection for
// graphics devices.
return;
}
if (!InitLockFilePath()) {
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "Failed to create the graphics startup lockfile.";
return;
}
if (RecoverFromDriverInitCrash()) {
// This is the first time we're checking for a crash recovery, so print
// a message and disable acceleration for anyone who asks for it.
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "Recovered from graphics driver startup crash; acceleration disabled.";
return;
}
if (UpdateEnvironment() || sEnvironmentHasBeenUpdated) {
// Something in the environment changed, *or* a previous instance of this
// class already updated the environment. Allow a fresh attempt at driver
// acceleration. This doesn't mean the previous attempt failed, it just
// means we want to detect whether the new environment crashes.
AllowDriverInitAttempt();
sEnvironmentHasBeenUpdated = true;
return;
}
RecordTelemetry(TelemetryState::Okay);
}
DriverInitCrashDetection::~DriverInitCrashDetection()
{
if (mLockFile) {
mLockFile->Remove(false);
}
if (gfxPrefs::DriverInitStatus() == int32_t(DriverInitStatus::Attempting)) {
// If we attempted to initialize the driver, and got this far without
// crashing, assume everything is okay.
gfxPrefs::SetDriverInitStatus(int32_t(DriverInitStatus::Okay));
}
}
bool
DriverInitCrashDetection::DisableAcceleration() const
{
return gfxPrefs::DriverInitStatus() == int32_t(DriverInitStatus::Recovered);
}
bool
DriverInitCrashDetection::InitLockFilePath()
{
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, getter_AddRefs(mLockFile));
if (!mLockFile) {
return false;
}
if (!NS_SUCCEEDED(mLockFile->AppendNative(NS_LITERAL_CSTRING("gfxinit.lock")))) {
return false;
}
return true;
}
void
DriverInitCrashDetection::AllowDriverInitAttempt()
{
// Create a temporary tombstone/lockfile.
FILE* fp;
if (!NS_SUCCEEDED(mLockFile->OpenANSIFileDesc("w", &fp))) {
return;
}
fclose(fp);
gfxPrefs::SetDriverInitStatus(int32_t(DriverInitStatus::Attempting));
// Flush preferences, so if we crash, we don't think the environment has changed again.
FlushPreferences();
// If we crash, we'll just lose this. Not a big deal, next startup we'll
// record the failure.
RecordTelemetry(TelemetryState::EnvironmentChanged);
}
bool
DriverInitCrashDetection::RecoverFromDriverInitCrash()
{
bool exists;
if (mLockFile &&
NS_SUCCEEDED(mLockFile->Exists(&exists)) &&
exists)
{
// If we get here, we've just recovered from a crash. Disable acceleration
// until the environment changes. Since we may have crashed before
// preferences we're flushed, we cache the environment again, then flush
// preferences so child processes can start right away.
gfxPrefs::SetDriverInitStatus(int32_t(DriverInitStatus::Recovered));
UpdateEnvironment();
FlushPreferences();
RecordTelemetry(TelemetryState::RecoveredFromCrash);
return true;
}
if (gfxPrefs::DriverInitStatus() == int32_t(DriverInitStatus::Recovered)) {
// If we get here, we crashed in the current environment and have already
// disabled acceleration.
RecordTelemetry(TelemetryState::AccelerationDisabled);
return true;
}
return false;
}
bool
DriverInitCrashDetection::UpdateEnvironment()
{
mGfxInfo = services::GetGfxInfo();
bool changed = false;
if (mGfxInfo) {
nsString value;
// Driver properties.
mGfxInfo->GetAdapterDriverVersion(value);
changed |= CheckAndUpdatePref("gfx.driver-init.driverVersion", value);
mGfxInfo->GetAdapterDeviceID(value);
changed |= CheckAndUpdatePref("gfx.driver-init.deviceID", value);
// Feature status.
#if defined(XP_WIN)
bool d2dEnabled = gfxPrefs::Direct2DForceEnabled() ||
(!gfxPrefs::Direct2DDisabled() && FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT2D));
changed |= CheckAndUpdateBoolPref("gfx.driver-init.feature-d2d", d2dEnabled);
bool d3d11Enabled = !gfxPrefs::LayersPreferD3D9();
if (!FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS)) {
d3d11Enabled = false;
}
changed |= CheckAndUpdateBoolPref("gfx.driver-init.feature-d3d11", d3d11Enabled);
#endif
}
// Firefox properties.
changed |= CheckAndUpdatePref("gfx.driver-init.appVersion", NS_LITERAL_STRING(MOZ_APP_VERSION));
// Finally, mark as changed if the status has been reset by the user.
changed |= (gfxPrefs::DriverInitStatus() == int32_t(DriverInitStatus::None));
return changed;
}
bool
DriverInitCrashDetection::FeatureEnabled(int aFeature)
{
int32_t status;
if (!NS_SUCCEEDED(mGfxInfo->GetFeatureStatus(aFeature, &status))) {
return false;
}
return status == nsIGfxInfo::FEATURE_STATUS_OK;
}
bool
DriverInitCrashDetection::CheckAndUpdateBoolPref(const char* aPrefName, bool aCurrentValue)
{
bool oldValue;
if (NS_SUCCEEDED(Preferences::GetBool(aPrefName, &oldValue)) &&
oldValue == aCurrentValue)
{
return false;
}
Preferences::SetBool(aPrefName, aCurrentValue);
return true;
}
bool
DriverInitCrashDetection::CheckAndUpdatePref(const char* aPrefName, const nsAString& aCurrentValue)
{
nsAdoptingString oldValue = Preferences::GetString(aPrefName);
if (oldValue == aCurrentValue) {
return false;
}
Preferences::SetString(aPrefName, aCurrentValue);
return true;
}
void
DriverInitCrashDetection::FlushPreferences()
{
if (nsIPrefService* prefService = Preferences::GetService()) {
prefService->SavePrefFile(nullptr);
}
}
void
DriverInitCrashDetection::RecordTelemetry(TelemetryState aState)
{
// Since we run this in each child process, we only want the initial results
// from the chrome process.
if (XRE_GetProcessType() != GeckoProcessType_Default) {
return;
}
// Since we instantiate this class more than once, make sure we only record
// the first state (since that is really all we care about).
static bool sTelemetryStateRecorded = false;
if (sTelemetryStateRecorded) {
return;
}
Telemetry::Accumulate(Telemetry::GRAPHICS_DRIVER_STARTUP_TEST, int32_t(aState));
sTelemetryStateRecorded = true;
}
} // namespace gfx
} // namespace mozilla
-73
View File
@@ -1,73 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef gfx_src_DriverInitCrashDetection_h__
#define gfx_src_DriverInitCrashDetection_h__
#include "gfxCore.h"
#include "nsCOMPtr.h"
#include "nsIGfxInfo.h"
#include "nsIFile.h"
namespace mozilla {
namespace gfx {
enum class DriverInitStatus
{
// Drivers have not been initialized yet.
None,
// We're attempting to initialize drivers.
Attempting,
// Drivers were successfully initialized last run.
Okay,
// We crashed during driver initialization, and have restarted.
Recovered
};
class DriverInitCrashDetection
{
public:
DriverInitCrashDetection();
~DriverInitCrashDetection();
bool DisableAcceleration() const;
// These are the values reported to Telemetry (GRAPHICS_DRIVER_STARTUP_TEST).
// Values should not change; add new values to the end.
enum class TelemetryState {
Okay = 0,
EnvironmentChanged = 1,
RecoveredFromCrash = 2,
AccelerationDisabled = 3
};
private:
bool InitLockFilePath();
bool UpdateEnvironment();
bool CheckAndUpdatePref(const char* aPrefName, const nsAString& aCurrentValue);
bool CheckAndUpdateBoolPref(const char* aPrefName, bool aCurrentValue);
bool FeatureEnabled(int aFeature);
void AllowDriverInitAttempt();
bool RecoverFromDriverInitCrash();
void FlushPreferences();
void RecordTelemetry(TelemetryState aState);
private:
static bool sEnvironmentHasBeenUpdated;
private:
bool mIsChromeProcess;
nsCOMPtr<nsIGfxInfo> mGfxInfo;
nsCOMPtr<nsIFile> mLockFile;
};
} // namespace gfx
} // namespace mozilla
#endif // gfx_src_DriverInitCrashDetection_h__
+8 -2
View File
@@ -14,7 +14,7 @@ XPIDL_MODULE = 'gfx'
DEFINES['MOZ_APP_VERSION'] = '"%s"' % CONFIG['MOZ_APP_VERSION']
EXPORTS += [
'DriverInitCrashDetection.h',
'DriverCrashGuard.h',
'FilterSupport.h',
'gfxCore.h',
'gfxCrashReporterUtils.h',
@@ -54,7 +54,7 @@ if CONFIG['MOZ_X11']:
]
UNIFIED_SOURCES += [
'DriverInitCrashDetection.cpp',
'DriverCrashGuard.cpp',
'FilterSupport.cpp',
'gfxCrashReporterUtils.cpp',
'gfxTelemetry.cpp',
@@ -77,6 +77,12 @@ SOURCES += [
FAIL_ON_WARNINGS = True
include('/ipc/chromium/chromium-config.mozbuild')
LOCAL_INCLUDES += [
'/dom/ipc', # for ContentChild.h
]
FINAL_LIBRARY = 'xul'
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
+22
View File
@@ -108,6 +108,7 @@ class mozilla::gl::SkiaGLGlue : public GenericAtomicRefCounted {
#include "VsyncSource.h"
#include "SoftwareVsyncSource.h"
#include "nscore.h" // for NS_FREE_PERMANENT_DATA
#include "mozilla/dom/ContentChild.h"
namespace mozilla {
namespace layers {
@@ -2584,3 +2585,24 @@ gfxPlatform::NotifyCompositorCreated(LayersBackend aBackend)
obsvc->NotifyObservers(nullptr, "compositor:created", nullptr);
}
}
void
gfxPlatform::GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut)
{
MOZ_ASSERT(XRE_IsParentProcess());
aOut->useAcceleration() = ShouldUseLayersAcceleration();
}
void
gfxPlatform::UpdateDeviceInitData()
{
if (XRE_IsParentProcess()) {
// The parent process figures out device initialization on its own.
return;
}
mozilla::gfx::DeviceInitData data;
mozilla::dom::ContentChild::GetSingleton()->SendGetGraphicsDeviceInitData(&data);
SetDeviceInitData(data);
}
+18 -1
View File
@@ -49,6 +49,7 @@ class DataSourceSurface;
class ScaledFont;
class DrawEventRecorder;
class VsyncSource;
class DeviceInitData;
inline uint32_t
BackendTypeBit(BackendType b)
@@ -516,7 +517,8 @@ public:
virtual bool CanUseHardwareVideoDecoding();
static bool CanUseDirect3D11ANGLE();
// Returns whether or not layers acceleration should be used.
// Returns whether or not layers acceleration should be used. This should
// only be called on the parent process.
bool ShouldUseLayersAcceleration();
// Returns a prioritized list of all available compositor backends.
@@ -677,6 +679,10 @@ public:
virtual void TestDeviceReset(DeviceResetReason aReason)
{}
// Return information on how child processes should initialize graphics
// devices. Currently this is only used on Windows.
virtual void GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut);
protected:
gfxPlatform();
virtual ~gfxPlatform();
@@ -709,6 +715,17 @@ protected:
void InitBackendPrefs(uint32_t aCanvasBitmask, mozilla::gfx::BackendType aCanvasDefault,
uint32_t aContentBitmask, mozilla::gfx::BackendType aContentDefault);
/**
* If in a child process, triggers a refresh of device preferences.
*/
void UpdateDeviceInitData();
/**
* Called when new device preferences are available.
*/
virtual void SetDeviceInitData(mozilla::gfx::DeviceInitData& aData)
{}
/**
* returns the first backend named in the pref gfx.canvas.azure.backends
* which is a component of aBackendBitmask, a bitmask of backend types
+1 -3
View File
@@ -228,9 +228,6 @@ private:
DECL_GFX_PREF(Live, "gfx.direct2d.use1_1", Direct2DUse1_1, bool, false);
DECL_GFX_PREF(Live, "gfx.direct2d.allow1_0", Direct2DAllow1_0, bool, false);
DECL_GFX_PREF(Live, "gfx.draw-color-bars", CompositorDrawColorBars, bool, false);
// This should be set to values in the DriverInitStatus enumeration found in
// DriverInitCrashDetection.h.
DECL_GFX_PREF(Live, "gfx.driver-init.status", DriverInitStatus, int32_t, 0);
DECL_GFX_PREF(Once, "gfx.font_rendering.directwrite.enabled", DirectWriteFontRenderingEnabled, bool, false);
DECL_GFX_PREF(Live, "gfx.gralloc.fence-with-readpixels", GrallocFenceWithReadPixels, bool, false);
DECL_GFX_PREF(Live, "gfx.layerscope.enabled", LayerScopeEnabled, bool, false);
@@ -242,6 +239,7 @@ private:
DECL_GFX_PREF(Once, "gfx.max-texture-size", MaxTextureSize, int32_t, (int32_t)32767);
DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled", PerfWarnings, bool, false);
DECL_GFX_PREF(Live, "gfx.testing.device-reset", DeviceResetForTesting, int32_t, 0);
DECL_GFX_PREF(Live, "gfx.testing.device-fail", DeviceFailForTesting, bool, false);
DECL_GFX_PREF(Once, "gfx.touch.resample", TouchResampling, bool, false);
// These times should be in milliseconds
+138 -16
View File
@@ -71,7 +71,7 @@
#include "gfxPrefs.h"
#include "VsyncSource.h"
#include "DriverInitCrashDetection.h"
#include "DriverCrashGuard.h"
#include "gfxCrashReporterUtils.h"
#include "mozilla/dom/ContentParent.h"
@@ -372,6 +372,16 @@ public:
NS_IMPL_ISUPPORTS(D3D9SharedTextureReporter, nsIMemoryReporter)
// Device init data should only be used on child processes, so we protect it
// behind a getter here.
static DeviceInitData sDeviceInitDataDoNotUseDirectly;
static inline DeviceInitData&
GetParentDevicePrefs()
{
MOZ_ASSERT(XRE_IsContentProcess());
return sDeviceInitDataDoNotUseDirectly;
}
gfxWindowsPlatform::gfxWindowsPlatform()
: mRenderMode(RENDER_GDI)
, mIsWARP(false)
@@ -402,6 +412,7 @@ gfxWindowsPlatform::gfxWindowsPlatform()
mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3);
UpdateDeviceInitData();
InitializeDevices();
UpdateRenderMode();
@@ -508,7 +519,6 @@ gfxWindowsPlatform::HandleDeviceReset()
// Reset local state. Note: we leave feature status variables as-is. They
// will be recomputed by InitializeDevices().
mIsWARP = false;
mHasDeviceReset = false;
mHasFakeDeviceReset = false;
mDoesD3D11TextureSharingWork = false;
@@ -518,6 +528,9 @@ gfxWindowsPlatform::HandleDeviceReset()
imgLoader::Singleton()->ClearCache(false);
gfxAlphaBoxBlur::ShutdownBlurCache();
// Since we got a device reset, we must ask the parent process for an updated
// list of which devices to create.
UpdateDeviceInitData();
InitializeDevices();
return true;
}
@@ -1908,6 +1921,12 @@ CanUseWARP()
return true;
}
// The child process can only use WARP if the parent process is also using
// WARP.
if (XRE_IsContentProcess()) {
return GetParentDevicePrefs().useD3D11WARP();
}
// It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703.
if (!IsWin8OrLater() ||
gfxPrefs::LayersD3D11DisableWARP() ||
@@ -1921,6 +1940,19 @@ CanUseWARP()
FeatureStatus
gfxWindowsPlatform::CheckD3D11Support(bool* aCanUseHardware)
{
// Don't revive D3D11 support after a failure.
if (IsFeatureStatusFailure(mD3D11Status)) {
return mD3D11Status;
}
if (XRE_IsContentProcess()) {
if (!GetParentDevicePrefs().useD3D11()) {
return FeatureStatus::Blocked;
}
*aCanUseHardware = !GetParentDevicePrefs().useD3D11WARP();
return FeatureStatus::Available;
}
if (gfxPrefs::LayersD3D11ForceWARP()) {
*aCanUseHardware = false;
return FeatureStatus::Available;
@@ -1985,6 +2017,12 @@ gfxWindowsPlatform::AttemptD3D11DeviceCreation()
// Only test this when not using WARP since it can fail and cause
// GetDeviceRemovedReason to return weird values.
mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device);
// Assert that the child and parent process both computed texture sharing
// properly.
MOZ_ASSERT_IF(XRE_IsContentProcess(),
mDoesD3D11TextureSharingWork == GetParentDevicePrefs().d3d11TextureSharingWorks());
mD3D11Device->SetExceptionMode(0);
mIsWARP = false;
}
@@ -2081,26 +2119,31 @@ gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation()
}
}
void
gfxWindowsPlatform::SetDeviceInitData(mozilla::gfx::DeviceInitData& aData)
{
MOZ_ASSERT(XRE_IsContentProcess());
sDeviceInitDataDoNotUseDirectly = aData;
}
void
gfxWindowsPlatform::InitializeDevices()
{
// If we previously crashed initializing devices, or if we're in safe mode,
// bail out now.
DriverInitCrashDetection detectCrashes;
if (detectCrashes.DisableAcceleration() || InSafeMode()) {
// If acceleration is disabled, we refuse to initialize anything.
mAcceleration = CheckAccelerationSupport();
if (IsFeatureStatusFailure(mAcceleration)) {
return;
}
// If we previously crashed initializing devices, bail out now. This is
// effectively a parent-process only check, since the content process
// cannot create a lock file.
D3D11LayersCrashGuard detectCrashes;
if (detectCrashes.Crashed()) {
mAcceleration = FeatureStatus::Blocked;
return;
}
// If acceleration is disabled, we refuse to initialize anything.
if (!ShouldUseLayersAcceleration()) {
mAcceleration = FeatureStatus::Disabled;
return;
}
// At this point, as far as we know, we can probably accelerate.
mAcceleration = FeatureStatus::Available;
// If we're going to prefer D3D9, stop here. The rest of this function
// attempts to use D3D11 features.
if (gfxPrefs::LayersPreferD3D9()) {
@@ -2115,6 +2158,38 @@ gfxWindowsPlatform::InitializeDevices()
}
}
FeatureStatus
gfxWindowsPlatform::CheckAccelerationSupport()
{
// Don't retry acceleration if it failed earlier.
if (IsFeatureStatusFailure(mAcceleration)) {
return mAcceleration;
}
if (XRE_IsContentProcess()) {
return GetParentDevicePrefs().useAcceleration()
? FeatureStatus::Available
: FeatureStatus::Blocked;
}
if (InSafeMode()) {
return FeatureStatus::Blocked;
}
if (!ShouldUseLayersAcceleration()) {
return FeatureStatus::Disabled;
}
return FeatureStatus::Available;
}
bool
gfxWindowsPlatform::CanUseD3D11ImageBridge()
{
if (XRE_IsContentProcess()) {
if (!GetParentDevicePrefs().useD3D11ImageBridge()) {
return false;
}
}
return !mIsWARP;
}
void
gfxWindowsPlatform::InitializeD3D11()
{
@@ -2142,6 +2217,12 @@ gfxWindowsPlatform::InitializeD3D11()
return;
}
// Check if a failure was injected for testing.
if (gfxPrefs::DeviceFailForTesting()) {
mD3D11Status = FeatureStatus::Failed;
return;
}
// First try to create a hardware accelerated device.
if (canUseHardware) {
AttemptD3D11DeviceCreation();
@@ -2162,7 +2243,7 @@ gfxWindowsPlatform::InitializeD3D11()
mD3D11Status = FeatureStatus::Available;
MOZ_ASSERT(mD3D11Device);
if (!mIsWARP) {
if (CanUseD3D11ImageBridge()) {
AttemptD3D11ImageBridgeDeviceCreation();
}
@@ -2193,6 +2274,17 @@ IsD2DBlacklisted()
FeatureStatus
gfxWindowsPlatform::CheckD2DSupport()
{
// Don't revive D2D support after a failure.
if (IsFeatureStatusFailure(mD2DStatus)) {
return mD2DStatus;
}
if (XRE_IsContentProcess()) {
return GetParentDevicePrefs().useD2D()
? FeatureStatus::Available
: FeatureStatus::Blocked;
}
if (!gfxPrefs::Direct2DForceEnabled() && IsD2DBlacklisted()) {
return FeatureStatus::Blacklisted;
}
@@ -2246,9 +2338,18 @@ gfxWindowsPlatform::InitializeD2D()
FeatureStatus
gfxWindowsPlatform::CheckD2D1Support()
{
// Don't revive D2D1 support after a failure.
if (IsFeatureStatusFailure(mD2D1Status)) {
return mD2D1Status;
}
if (!Factory::SupportsD2D1()) {
return FeatureStatus::Unavailable;
}
if (XRE_IsContentProcess()) {
return GetParentDevicePrefs().useD2D1()
? FeatureStatus::Available
: FeatureStatus::Blocked;
}
if (!gfxPrefs::Direct2DUse1_1()) {
return FeatureStatus::Disabled;
}
@@ -2592,3 +2693,24 @@ gfxWindowsPlatform::GetD3D11Version()
}
return device->GetFeatureLevel();
}
void
gfxWindowsPlatform::GetDeviceInitData(DeviceInitData* aOut)
{
// Check for device resets before giving back new graphics information.
UpdateRenderMode();
gfxPlatform::GetDeviceInitData(aOut);
// IPDL initializes each field to false for us so we can early return.
if (GetD3D11Status() != FeatureStatus::Available) {
return;
}
aOut->useD3D11() = true;
aOut->useD3D11ImageBridge() = !!mD3D11ImageBridgeDevice;
aOut->d3d11TextureSharingWorks() = mDoesD3D11TextureSharingWork;
aOut->useD3D11WARP() = mIsWARP;
aOut->useD2D() = (GetD2DStatus() == FeatureStatus::Available);
aOut->useD2D1() = (GetD2D1Status() == FeatureStatus::Available);
}
+5
View File
@@ -283,12 +283,15 @@ public:
static mozilla::Atomic<size_t> sD3D9SurfaceImageUsed;
static mozilla::Atomic<size_t> sD3D9SharedTextureUsed;
void GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut) override;
protected:
bool AccelerateLayersByDefault() override {
return true;
}
void GetAcceleratedCompositorBackends(nsTArray<mozilla::layers::LayersBackend>& aBackends);
virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size);
void SetDeviceInitData(mozilla::gfx::DeviceInitData& aData) override;
protected:
RenderMode mRenderMode;
@@ -307,6 +310,7 @@ private:
void DisableD2D();
mozilla::gfx::FeatureStatus CheckAccelerationSupport();
mozilla::gfx::FeatureStatus CheckD3D11Support(bool* aCanUseHardware);
mozilla::gfx::FeatureStatus CheckD2DSupport();
mozilla::gfx::FeatureStatus CheckD2D1Support();
@@ -314,6 +318,7 @@ private:
void AttemptWARPDeviceCreation();
void AttemptD3D11ImageBridgeDeviceCreation();
bool AttemptD3D11ContentDeviceCreation();
bool CanUseD3D11ImageBridge();
IDXGIAdapter1 *GetDXGIAdapter();
bool IsDeviceReset(HRESULT hr, DeviceResetReason* aReason);
+2
View File
@@ -140,6 +140,8 @@ public:
virtual void SetHasError() = 0;
virtual ImageURL* GetURI() = 0;
virtual void ReportUseCounters() { }
};
class ImageResource : public Image
+54 -9
View File
@@ -17,37 +17,67 @@ using namespace dom;
namespace image {
bool
URISchemeIs(ImageURL* aURI, const char* aScheme)
{
bool schemeMatches = false;
if (NS_WARN_IF(NS_FAILED(aURI->SchemeIs(aScheme, &schemeMatches)))) {
return false;
}
return schemeMatches;
}
static Maybe<uint64_t>
BlobSerial(ImageURL* aURI)
{
nsAutoCString spec;
aURI->GetSpec(spec);
nsRefPtr<BlobImpl> blob;
if (NS_SUCCEEDED(NS_GetBlobForBlobURISpec(spec, getter_AddRefs(blob))) &&
blob) {
return Some(blob->GetSerialNumber());
}
return Nothing();
}
ImageCacheKey::ImageCacheKey(nsIURI* aURI)
: mURI(new ImageURL(aURI))
, mIsChrome(URISchemeIs(mURI, "chrome"))
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mURI);
bool isChrome;
mIsChrome = NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome;
if (URISchemeIs(mURI, "blob")) {
mBlobSerial = BlobSerial(mURI);
}
mHash = ComputeHash(mURI);
mHash = ComputeHash(mURI, mBlobSerial);
}
ImageCacheKey::ImageCacheKey(ImageURL* aURI)
: mURI(aURI)
, mIsChrome(URISchemeIs(mURI, "chrome"))
{
MOZ_ASSERT(mURI);
MOZ_ASSERT(aURI);
bool isChrome;
mIsChrome = NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome;
if (URISchemeIs(mURI, "blob")) {
mBlobSerial = BlobSerial(mURI);
}
mHash = ComputeHash(mURI);
mHash = ComputeHash(mURI, mBlobSerial);
}
ImageCacheKey::ImageCacheKey(const ImageCacheKey& aOther)
: mURI(aOther.mURI)
, mBlobSerial(aOther.mBlobSerial)
, mHash(aOther.mHash)
, mIsChrome(aOther.mIsChrome)
{ }
ImageCacheKey::ImageCacheKey(ImageCacheKey&& aOther)
: mURI(Move(aOther.mURI))
, mBlobSerial(Move(aOther.mBlobSerial))
, mHash(aOther.mHash)
, mIsChrome(aOther.mIsChrome)
{ }
@@ -55,6 +85,12 @@ ImageCacheKey::ImageCacheKey(ImageCacheKey&& aOther)
bool
ImageCacheKey::operator==(const ImageCacheKey& aOther) const
{
if (mBlobSerial || aOther.mBlobSerial) {
// If at least one of us has a blob serial, just compare those.
return mBlobSerial == aOther.mBlobSerial;
}
// For non-blob URIs, compare the URIs.
return *mURI == *aOther.mURI;
}
@@ -65,10 +101,19 @@ ImageCacheKey::Spec() const
}
/* static */ uint32_t
ImageCacheKey::ComputeHash(ImageURL* aURI)
ImageCacheKey::ComputeHash(ImageURL* aURI,
const Maybe<uint64_t>& aBlobSerial)
{
// Since we frequently call Hash() several times in a row on the same
// ImageCacheKey, as an optimization we compute our hash once and store it.
if (aBlobSerial) {
// For blob URIs, we hash the serial number of the underlying blob, so that
// different blob URIs which point to the same blob share a cache entry.
return HashGeneric(*aBlobSerial);
}
// For non-blob URIs, we hash the URI spec.
nsAutoCString spec;
aURI->GetSpec(spec);
return HashString(spec);
+5 -1
View File
@@ -10,6 +10,8 @@
#ifndef mozilla_image_src_ImageCacheKey_h
#define mozilla_image_src_ImageCacheKey_h
#include "mozilla/Maybe.h"
class nsIURI;
namespace mozilla {
@@ -42,9 +44,11 @@ public:
bool IsChrome() const { return mIsChrome; }
private:
static uint32_t ComputeHash(ImageURL* aURI);
static uint32_t ComputeHash(ImageURL* aURI,
const Maybe<uint64_t>& aBlobSerial);
nsRefPtr<ImageURL> mURI;
Maybe<uint64_t> mBlobSerial;
uint32_t mHash;
bool mIsChrome;
};
+10
View File
@@ -32,6 +32,7 @@
#include "SVGDocumentWrapper.h"
#include "nsIDOMEventListener.h"
#include "SurfaceCache.h"
#include "nsDocument.h"
// undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
#undef GetCurrentTime
@@ -1267,6 +1268,15 @@ VectorImage::PropagateUseCounters(nsIDocument* aParentDocument)
}
}
void
VectorImage::ReportUseCounters()
{
nsIDocument* doc = mSVGDocumentWrapper->GetDocument();
if (doc) {
static_cast<nsDocument*>(doc)->ReportUseCounters();
}
}
nsIntSize
VectorImage::OptimalImageSizeForDest(const gfxSize& aDest,
uint32_t aWhichFrame,
+2
View File
@@ -68,6 +68,8 @@ public:
void OnSVGDocumentLoaded();
void OnSVGDocumentError();
virtual void ReportUseCounters() override;
protected:
explicit VectorImage(ImageURL* aURI = nullptr);
virtual ~VectorImage();

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