mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
Merge remote-tracking branch 'origin/master' into media-works
This commit is contained in:
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -102,7 +102,7 @@ public:
|
||||
}
|
||||
|
||||
virtual void GetMozFullPathInternal(nsAString& aFullPath,
|
||||
ErrorResult& aRv) override;
|
||||
ErrorResult& aRv) const override;
|
||||
|
||||
virtual nsresult
|
||||
SetMutable(bool aMutable) override;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "nsIDocument.h"
|
||||
#include "nsINode.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIURI.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsDataDocumentContentPolicy, nsIContentPolicy)
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
})();
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -481,10 +481,6 @@ DOMInterfaces = {
|
||||
},
|
||||
},
|
||||
|
||||
'FileList': {
|
||||
'headerFile': 'mozilla/dom/File.h',
|
||||
},
|
||||
|
||||
'FileReader': {
|
||||
'nativeType': 'nsDOMFileReader',
|
||||
'implicitJSContext': [ 'readAsArrayBuffer' ],
|
||||
|
||||
Vendored
+1
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ Directory::GetName(nsString& aRetval) const
|
||||
aRetval.Truncate();
|
||||
|
||||
if (mPath.IsEmpty()) {
|
||||
aRetval = mFileSystem->GetRootName();
|
||||
mFileSystem->GetRootName(aRetval);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ FileSystemRequestParent::Dispatch(ContentParent* aParent,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mFileSystem->IsTesting()) {
|
||||
if (mFileSystem->RequiresPermissionChecks()) {
|
||||
// Check the content process permission.
|
||||
|
||||
nsCString access;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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',
|
||||
]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -240,6 +240,7 @@ LOCAL_INCLUDES += [
|
||||
'/dom/xul',
|
||||
'/editor/libeditor',
|
||||
'/editor/txmgr',
|
||||
'/image',
|
||||
'/layout/forms',
|
||||
'/layout/generic',
|
||||
'/layout/style',
|
||||
|
||||
@@ -126,7 +126,7 @@ BlobImplSnapshot::CreateSlice(uint64_t aStart,
|
||||
|
||||
void
|
||||
BlobImplSnapshot::GetMozFullPathInternal(nsAString& aFilename,
|
||||
ErrorResult& aRv)
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
AssertSanity();
|
||||
MOZ_ASSERT(mIsFile);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -76,6 +76,7 @@ public:
|
||||
IPC::Channel* aChannel);
|
||||
void InitProcessAttributes();
|
||||
void InitXPCOM();
|
||||
void InitGraphicsDeviceData();
|
||||
|
||||
static ContentChild* GetSingleton() {
|
||||
return sSingleton;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
%}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
/*
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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
|
||||
@@ -26,6 +26,10 @@ SOURCES += [
|
||||
'SharedDIB.cpp',
|
||||
]
|
||||
|
||||
IPDL_SOURCES = [
|
||||
'GraphicsMessages.ipdlh',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
@@ -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
|
||||
@@ -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__
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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']
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -140,6 +140,8 @@ public:
|
||||
virtual void SetHasError() = 0;
|
||||
|
||||
virtual ImageURL* GetURI() = 0;
|
||||
|
||||
virtual void ReportUseCounters() { }
|
||||
};
|
||||
|
||||
class ImageResource : public Image
|
||||
|
||||
+54
-9
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user