Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2023-07-06 10:39:29 +08:00
commit 9f1550fb53
10 changed files with 211 additions and 120 deletions

View file

@ -340,9 +340,14 @@ GetDirectoryListingTaskParent::IOWork()
nsCOMPtr<nsIFile> currFile = do_QueryInterface(supp);
MOZ_ASSERT(currFile);
bool isSpecial, isFile;
if (NS_WARN_IF(NS_FAILED(currFile->IsSpecial(&isSpecial))) ||
isSpecial) {
bool isLink, isSpecial, isFile;
if (NS_WARN_IF(NS_FAILED(currFile->IsSymlink(&isLink)) ||
NS_FAILED(currFile->IsSpecial(&isSpecial))) ||
// Although we allow explicit individual selection of symlinks via the
// file picker, we do not process symlinks in directory traversal. Our
// specific policy decision is documented at
// https://bugzilla.mozilla.org/show_bug.cgi?id=1813299#c20
isLink || isSpecial) {
continue;
}
if (NS_WARN_IF(NS_FAILED(currFile->IsFile(&isFile)) ||

View file

@ -333,13 +333,8 @@ GetFilesHelperBase::ExploreDirectory(const nsAString& aDOMPath, nsIFile* aFile)
return NS_OK;
}
nsresult rv = AddExploredDirectory(aFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsISimpleEnumerator> entries;
rv = aFile->GetDirectoryEntries(getter_AddRefs(entries));
nsresult rv = aFile->GetDirectoryEntries(getter_AddRefs(entries));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -361,7 +356,12 @@ GetFilesHelperBase::ExploreDirectory(const nsAString& aDOMPath, nsIFile* aFile)
bool isLink, isSpecial, isFile, isDir;
if (NS_WARN_IF(NS_FAILED(currFile->IsSymlink(&isLink)) ||
NS_FAILED(currFile->IsSpecial(&isSpecial))) ||
isSpecial) {
isSpecial ||
// Although we allow explicit individual selection of symlinks via the
// file picker, we do not process symlinks in directory traversal. Our
// specific policy decision is documented at
// https://bugzilla.mozilla.org/show_bug.cgi?id=1813299#c20
isLink) {
continue;
}
@ -371,11 +371,6 @@ GetFilesHelperBase::ExploreDirectory(const nsAString& aDOMPath, nsIFile* aFile)
continue;
}
// We don't want to explore loops of links.
if (isDir && isLink && !ShouldFollowSymLink(currFile)) {
continue;
}
// The new domPath
nsAutoString domPath;
domPath.Assign(aDOMPath);
@ -415,69 +410,6 @@ GetFilesHelperBase::ExploreDirectory(const nsAString& aDOMPath, nsIFile* aFile)
return NS_OK;
}
nsresult
GetFilesHelperBase::AddExploredDirectory(nsIFile* aDir)
{
nsresult rv;
#ifdef DEBUG
bool isDir;
rv = aDir->IsDirectory(&isDir);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(isDir, "Why are we here?");
#endif
bool isLink;
rv = aDir->IsSymlink(&isLink);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoCString path;
if (!isLink) {
nsAutoString path16;
rv = aDir->GetPath(path16);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
path = NS_ConvertUTF16toUTF8(path16);
} else {
rv = aDir->GetNativeTarget(path);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
mExploredDirectories.PutEntry(path);
return NS_OK;
}
bool
GetFilesHelperBase::ShouldFollowSymLink(nsIFile* aDir)
{
#ifdef DEBUG
bool isLink, isDir;
if (NS_WARN_IF(NS_FAILED(aDir->IsSymlink(&isLink)) ||
NS_FAILED(aDir->IsDirectory(&isDir)))) {
return false;
}
MOZ_ASSERT(isLink && isDir, "Why are we here?");
#endif
nsAutoCString targetPath;
if (NS_WARN_IF(NS_FAILED(aDir->GetNativeTarget(targetPath)))) {
return false;
}
return !mExploredDirectories.Contains(targetPath);
}
void
GetFilesHelper::ResolveOrRejectPromise(Promise* aPromise)
{

View file

@ -9,10 +9,8 @@
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/File.h"
#include "nsClassHashtable.h"
#include "nsCycleCollectionTraversalCallback.h"
#include "nsTArray.h"
#include "nsTHashtable.h"
#include "nsThreadUtils.h"
class nsIGlobalObject;
@ -57,17 +55,10 @@ protected:
nsresult
ExploreDirectory(const nsAString& aDOMPath, nsIFile* aFile);
nsresult
AddExploredDirectory(nsIFile* aDirectory);
bool
ShouldFollowSymLink(nsIFile* aDirectory);
bool mRecursiveFlag;
// We populate this array in the I/O thread with the BlobImpl.
FallibleTArray<RefPtr<BlobImpl>> mTargetBlobImplArray;
nsTHashtable<nsCStringHashKey> mExploredDirectories;
};
// Retrieving the list of files can be very time/IO consuming. We use this

View file

@ -38,6 +38,10 @@ function test_getFilesAndDirectories(aDirectory, aRecursive, aNext) {
if (data[i] instanceof File) {
is(data[i].webkitRelativePath, createRelativePath(dir, data[i]), "File.webkitRelativePath should be called: parentdir.path + '/' + file.name: " + data[i].webkitRelativePath);
}
ok(
!data[i].webkitRelativePath.endsWith("symlink.txt"),
"We should never see a path ending with symlink.txt, our symlink sentinel."
);
}
}
);

View file

@ -36,6 +36,15 @@ function createTreeFile(depth, parent) {
if (depth == 0) {
nextFile.append('file.txt');
nextFile.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600);
#ifdef XP_UNIX
// It's not possible to create symlinks on windows by default or on our
// Android platforms, so we can't create the symlink file there. Our
// callers that care are aware of this.
var linkFile = parent.clone();
linkFile.append("symlink.txt");
createSymLink(nextFile.path, linkFile.path);
#endif
} else {
nextFile.append('subdir' + depth);
nextFile.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700);
@ -84,6 +93,15 @@ function createTestFile() {
file2.append('bar.txt');
file2.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600);
#ifdef XP_UNIX
// It's not possible to create symlinks on windows by default or on our
// Android platforms, so we can't create the symlink file there. Our
// callers that care are aware of this.
var linkFile = dir.clone();
linkFile.append("symlink.txt");
createSymLink(file1.path, linkFile.path);
#endif
return tmpFile;
}
@ -127,3 +145,14 @@ addMessageListener("file.open", function (e) {
file: File.createFromNsIFile(testFile)
});
});
addMessageListener("symlink.open", function (e) {
let testDir = createTestFile();
let testFile = testDir.clone();
testFile.append("subdir");
testFile.append("symlink.txt");
File.createFromNsIFile(testFile).then(function (file) {
sendAsyncMessage("symlink.opened", { dir: testDir.path, file });
});
});

View file

@ -9,11 +9,26 @@
<body>
<input id="inputFileWebkitDirectory" type="file" webkitdirectory></input>
<input id="inputFileWebkitDirectoryAndDirectory" type="file" webkitdirectory allowdirs></input>
<input id="inputFileWebkitFile" type="file"></input>
<input id="inputFileDirectory" type="file" allowdirs></input>
<input id="inputFileDirectoryChange" type="file" webkitdirectory></input>
<script type="application/javascript;version=1.7">
// Populate the given input type=file `aInputFile`'s `files` attribute by:
// - loading `script_fileList.js` in the parent process
// - telling it to generate the "test" template directory pattern which will
// create "foo.txt", "subdir/bar.txt", and if symlinks are available on the
// platform, "symlink.txt" which will be a symlink to "foo.txt". (Note that
// we explicitly expect the symlink to be filtered out if generated, and
// during the enhancement of the test we verified the file was created on
// linux by running the test before fixing the GetFilesHelper logic to filter
// the symlink out and verifying the subsequent `test_fileList` check failed.)
// - Triggering the mock file picker with the base directory of the "test"
// template directory.
//
// It's expected that `test_fileList` will be used after this step completes in
// order to validate the results.
function populateInputFile(aInputFile) {
var url = SimpleTest.getTestFileURL("script_fileList.js");
var script = SpecialPowers.loadChromeScript(url);
@ -52,6 +67,8 @@ function checkFile(file, fileList, dirName) {
ok(false, "File not found.");
}
// Validate the contents of the given input type=file `aInputFile`'s' `files`
// property against the expected list of files `aWhat`.
function test_fileList(aInputFile, aWhat) {
var input = document.getElementById(aInputFile);
var fileList = input.files;
@ -70,6 +87,65 @@ function test_fileList(aInputFile, aWhat) {
next();
}
// Verify that we can explicitly select a symlink and it will not be filtered
// out. This is really a verification that GetFileHelper's file-handling logic
// https://searchfox.org/mozilla-central/rev/065102493dfc49234120c37fc6a334a5b1d86d9e/dom/filesystem/GetFilesHelper.cpp#81-86
// does not proactively take an action to filter out a selected symlink.
//
// This is a glass box test that is not entirely realistic for our actual system
// file pickers but does reflect what will happen in the drag-and-drop case
// for `HTMLInputElement::MozSetDndFilesAndDirectories` and this helps ensure
// that future implementation changes will behave as expected. Specifically,
// the presence of webkitdirectory will result in the file picker using
// `modeGetFolder` which will only allow selection of a directory and forbid
// file selection.
//
// This test explicitly does not validate HTMLInputElement's non-webkitdirectory
// file selection mechanism because it does not involve GetFileHelper.
async function test_individualSymlink(aInputFile) {
const input = document.getElementById(aInputFile);
// -- Create the symlink and get a `File` instance pointing at it.
const url = SimpleTest.getTestFileURL("script_fileList.js");
const script = SpecialPowers.loadChromeScript(url);
let opened = new Promise(resolve => script.addMessageListener("symlink.opened", resolve));
script.sendAsyncMessage("symlink.open", {});
let { dir, file: symlinkFile } = await opened;
info(`symlink.open provided dir: ${dir}`)
// -- Have the picker pick it
var MockFilePicker = SpecialPowers.MockFilePicker;
MockFilePicker.init(window, "A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeOpen);
MockFilePicker.displayDirectory = dir;
let pickerShown = new Promise(resolve => {
MockFilePicker.showCallback = function() {
// This is where we are diverging from a realistic scenario in order to get
// the expected coverage.
MockFilePicker.setFiles([symlinkFile]);
resolve();
}
});
MockFilePicker.returnValue = MockFilePicker.returnOK;
let changeEvent = waitForEvent(input, "change");
input.click();
await pickerShown;
await changeEvent;
MockFilePicker.cleanup();
script.destroy();
// -- Verify that we see the symlink.
let fileList = input.files;
is(fileList.length, 1, "There should be 1 file.");
is(fileList[0].name, "symlink.txt", "The file should be the symlink.");
next();
}
function test_webkitdirectory_attribute() {
var a = document.createElement("input");
a.setAttribute("type", "file");
@ -169,6 +245,16 @@ var tests = [
function() { test_fileList('inputFileWebkitDirectory', testDirData) },
function() { test_fileList('inputFileWebkitDirectoryAndDirectory', testDirData) },
function() { test_fileList('inputFileDirectory', null); },
function() {
#ifdef XP_WIN // Symlinks are not available on Windows and so will not be created.
info("Skipping individual symlink check on Windows and Android.");
next();
return;
#endif
test_individualSymlink("inputFileWebkitFile").catch(err => ok(false, `Problem in symlink case: ${err}`));
},
test_webkitdirectory_attribute,

View file

@ -588,6 +588,15 @@ class JS_FRIEND_API(AutoEnterPolicy)
inline void recordLeave() {}
#endif
private:
// This operator needs to be deleted explicitly, otherwise Visual C++ will
// create it automatically when it is part of the export JS API. In that
// case, compile would fail because HandleId is not allowed to be assigned
// and consequently instantiation of assign operator of mozilla::Maybe
// would fail. See bug 1325351 comment 16. Copy constructor is removed at
// the same time for consistency.
AutoEnterPolicy(const AutoEnterPolicy&) = delete;
AutoEnterPolicy& operator=(const AutoEnterPolicy&) = delete;
};
#ifdef JS_DEBUG

View file

@ -102,16 +102,11 @@ public:
}
/**
* Maybe<T*> can be copy-constructed from a Maybe<U*> if U* and T* are
* compatible, or from Maybe<decltype(nullptr)>.
* Maybe<T> can be copy-constructed from a Maybe<U> if U is convertible to T.
*/
template<typename U,
typename =
typename std::enable_if<std::is_pointer<T>::value &&
(std::is_same<U, decltype(nullptr)>::value ||
(std::is_pointer<U>::value &&
std::is_base_of<typename std::remove_pointer<T>::type,
typename std::remove_pointer<U>::type>::value))>::type>
typename std::enable_if<std::is_convertible<U, T>::value>::type>
MOZ_IMPLICIT
Maybe(const Maybe<U>& aOther)
: mIsSome(false)
@ -131,16 +126,11 @@ public:
}
/**
* Maybe<T*> can be move-constructed from a Maybe<U*> if U* and T* are
* compatible, or from Maybe<decltype(nullptr)>.
* Maybe<T> can be move-constructed from a Maybe<U> if U is convertible to T.
*/
template<typename U,
typename =
typename std::enable_if<std::is_pointer<T>::value &&
(std::is_same<U, decltype(nullptr)>::value ||
(std::is_pointer<U>::value &&
std::is_base_of<typename std::remove_pointer<T>::type,
typename std::remove_pointer<U>::type>::value))>::type>
typename std::enable_if<std::is_convertible<U, T>::value>::type>
MOZ_IMPLICIT
Maybe(Maybe<U>&& aOther)
: mIsSome(false)
@ -156,13 +146,7 @@ public:
if (&aOther != this) {
if (aOther.mIsSome) {
if (mIsSome) {
// XXX(seth): The correct code for this branch, below, can't be used
// due to a bug in Visual Studio 2010. See bug 1052940.
/*
ref() = aOther.ref();
*/
reset();
emplace(*aOther);
} else {
emplace(*aOther);
}
@ -173,6 +157,23 @@ public:
return *this;
}
template<typename U,
typename =
typename std::enable_if<std::is_convertible<U, T>::value>::type>
Maybe& operator=(const Maybe<U>& aOther)
{
if (aOther.isSome()) {
if (mIsSome) {
ref() = aOther.ref();
} else {
emplace(*aOther);
}
} else {
reset();
}
return *this;
}
Maybe& operator=(Maybe&& aOther)
{
MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
@ -191,6 +192,25 @@ public:
return *this;
}
template<typename U,
typename =
typename std::enable_if<std::is_convertible<U, T>::value>::type>
Maybe& operator=(Maybe<U>&& aOther)
{
if (aOther.isSome()) {
if (mIsSome) {
ref() = Move(aOther.ref());
} else {
emplace(Move(*aOther));
}
aOther.reset();
} else {
reset();
}
return *this;
}
/* Methods that check whether this Maybe contains a value */
explicit operator bool() const { return isSome(); }
bool isSome() const { return mIsSome; }
@ -443,11 +463,12 @@ public:
* if you need to construct a Maybe value that holds a const, volatile, or
* reference value, you need to use emplace() instead.
*/
template<typename T>
Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type>
template<typename T,
typename U = typename std::remove_cv<
typename std::remove_reference<T>::type>::type>
Maybe<U>
Some(T&& aValue)
{
typedef typename RemoveCV<typename RemoveReference<T>::Type>::Type U;
Maybe<U> value;
value.emplace(Forward<T>(aValue));
return value;

View file

@ -1344,7 +1344,7 @@ nsCORSPreflightListener::CheckPreflightRequestApproved(nsIRequest* aRequest)
headerVal);
nsTArray<nsCString> headers;
nsCCharSeparatedTokenizer headerTokens(headerVal, ',');
bool allowAllHeaders = false;
bool wildcard = false;
while(headerTokens.hasMoreTokens()) {
const nsDependentCSubstring& header = headerTokens.nextToken();
if (header.IsEmpty()) {
@ -1356,19 +1356,31 @@ nsCORSPreflightListener::CheckPreflightRequestApproved(nsIRequest* aRequest)
return NS_ERROR_DOM_BAD_URI;
}
if (header.EqualsLiteral("*") && !mWithCredentials) {
allowAllHeaders = true;
wildcard = true;
} else {
headers.AppendElement(header);
}
}
if (!allowAllHeaders) {
for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) {
if (!headers.Contains(mPreflightHeaders[i],
nsCaseInsensitiveCStringArrayComparator())) {
LogBlockedRequest(aRequest, "CORSMissingAllowHeaderFromPreflight",
NS_ConvertUTF8toUTF16(mPreflightHeaders[i]).get());
return NS_ERROR_DOM_BAD_URI;
}
for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) {
if (wildcard
// Access-Control-Allow-Headers is '*', so we should skip these checks.
#if 0
&& !mPreflightHeaders[i].LowerCaseEqualsASCII("authorization")
// However, according to the spec, 'Authorization' isn't allowed to be
// wildcarded here and must always be explicitly mentioned.
// Fixme: Mainstream keeps this disabled because nobody obeys this rule.
// This should be flipped on when either mainstream does or when there's enough
// effort to make websites adhere to the spec, to keep our implementation
// in line with the consensus on the web.
#endif
) {
continue;
}
if (!headers.Contains(mPreflightHeaders[i],
nsCaseInsensitiveCStringArrayComparator())) {
LogBlockedRequest(aRequest, "CORSMissingAllowHeaderFromPreflight",
NS_ConvertUTF8toUTF16(mPreflightHeaders[i]).get());
return NS_ERROR_DOM_BAD_URI;
}
}

View file

@ -3028,6 +3028,8 @@ nsLocalFile::IsExecutable(bool* aResult)
"com",
"cpl",
"crt",
"der",
"diagcab",
"exe",
"fxp", // FoxPro compiled app
"hlp",