зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c. a=merge
This commit is contained in:
Коммит
5684ae54bc
|
@ -899,19 +899,6 @@ public:
|
|||
bool NeedsDOMUIEvent() const
|
||||
{ return !(mStateFlags & eIgnoreDOMUIEvent); }
|
||||
|
||||
/**
|
||||
* Get/set survivingInUpdate bit on child indicating that parent recollects
|
||||
* its children.
|
||||
*/
|
||||
bool IsSurvivingInUpdate() const { return mStateFlags & eSurvivingInUpdate; }
|
||||
void SetSurvivingInUpdate(bool aIsSurviving)
|
||||
{
|
||||
if (aIsSurviving)
|
||||
mStateFlags |= eSurvivingInUpdate;
|
||||
else
|
||||
mStateFlags &= ~eSurvivingInUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set repositioned bit indicating that the accessible was moved in
|
||||
* the accessible tree, i.e. the accessible tree structure differs from DOM.
|
||||
|
@ -1039,11 +1026,10 @@ protected:
|
|||
eGroupInfoDirty = 1 << 5, // accessible needs to update group info
|
||||
eKidsMutating = 1 << 6, // subtree is being mutated
|
||||
eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
|
||||
eSurvivingInUpdate = 1 << 8, // parent drops children to recollect them
|
||||
eRelocated = 1 << 9, // accessible was moved in tree
|
||||
eNoXBLKids = 1 << 10, // accessible don't allows XBL children
|
||||
eNoKidsFromDOM = 1 << 11, // accessible doesn't allow children from DOM
|
||||
eHasTextKids = 1 << 12, // accessible have a text leaf in children
|
||||
eRelocated = 1 << 8, // accessible was moved in tree
|
||||
eNoXBLKids = 1 << 9, // accessible don't allows XBL children
|
||||
eNoKidsFromDOM = 1 << 10, // accessible doesn't allow children from DOM
|
||||
eHasTextKids = 1 << 11, // accessible have a text leaf in children
|
||||
|
||||
eLastStateFlag = eNoKidsFromDOM
|
||||
};
|
||||
|
@ -1144,7 +1130,7 @@ protected:
|
|||
nsTArray<Accessible*> mChildren;
|
||||
int32_t mIndexInParent;
|
||||
|
||||
static const uint8_t kStateFlagsBits = 13;
|
||||
static const uint8_t kStateFlagsBits = 12;
|
||||
static const uint8_t kContextFlagsBits = 3;
|
||||
static const uint8_t kTypeBits = 6;
|
||||
static const uint8_t kGenericTypesBits = 16;
|
||||
|
|
|
@ -306,9 +306,11 @@ this.AccessFu = { // jshint ignore:line
|
|||
break;
|
||||
case "Accessibility:NextObject":
|
||||
case "Accessibility:PreviousObject": {
|
||||
let rule = data ?
|
||||
data.rule.substr(0, 1).toUpperCase() + data.rule.substr(1).toLowerCase() :
|
||||
"Simple";
|
||||
let rule = "Simple";
|
||||
if (data && data.rule && data.rule.length) {
|
||||
rule = data.rule.substr(0, 1).toUpperCase() +
|
||||
data.rule.substr(1).toLowerCase();
|
||||
}
|
||||
let method = event.replace(/Accessibility:(\w+)Object/, "move$1");
|
||||
this.Input.moveCursor(method, rule, "gesture");
|
||||
break;
|
||||
|
|
|
@ -46,7 +46,7 @@ ia2Accessible::QueryInterface(REFIID iid, void** ppv)
|
|||
*ppv = static_cast<IAccessible2_3*>(this);
|
||||
else if (IID_IAccessible2_2 == iid)
|
||||
*ppv = static_cast<IAccessible2_2*>(this);
|
||||
else if (IID_IAccessible2 == iid && !Compatibility::IsIA2Off())
|
||||
else if (IID_IAccessible2 == iid)
|
||||
*ppv = static_cast<IAccessible2*>(this);
|
||||
|
||||
if (*ppv) {
|
||||
|
|
|
@ -139,7 +139,7 @@ Compatibility::Init()
|
|||
|
||||
HMODULE jawsHandle = ::GetModuleHandleW(L"jhook");
|
||||
if (jawsHandle)
|
||||
sConsumers |= (IsModuleVersionLessThan(jawsHandle, 8, 2173)) ?
|
||||
sConsumers |= (IsModuleVersionLessThan(jawsHandle, 18, 4315)) ?
|
||||
OLDJAWS : JAWS;
|
||||
|
||||
if (::GetModuleHandleW(L"gwm32inc"))
|
||||
|
@ -224,11 +224,15 @@ ReadCOMRegDefaultString(const nsString& aRegPath, nsAString& aOutBuf)
|
|||
{
|
||||
aOutBuf.Truncate();
|
||||
|
||||
nsAutoString fullyQualifiedRegPath;
|
||||
fullyQualifiedRegPath.AppendLiteral(u"SOFTWARE\\Classes\\");
|
||||
fullyQualifiedRegPath.Append(aRegPath);
|
||||
|
||||
// Get the required size and type of the registry value.
|
||||
// We expect either REG_SZ or REG_EXPAND_SZ.
|
||||
DWORD type;
|
||||
DWORD bufLen = 0;
|
||||
LONG result = ::RegGetValue(HKEY_CLASSES_ROOT, aRegPath.get(),
|
||||
LONG result = ::RegGetValue(HKEY_LOCAL_MACHINE, fullyQualifiedRegPath.get(),
|
||||
nullptr, RRF_RT_ANY, &type, nullptr, &bufLen);
|
||||
if (result != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) {
|
||||
return false;
|
||||
|
@ -239,7 +243,7 @@ ReadCOMRegDefaultString(const nsString& aRegPath, nsAString& aOutBuf)
|
|||
|
||||
aOutBuf.SetLength((bufLen + 1) / sizeof(char16_t));
|
||||
|
||||
result = ::RegGetValue(HKEY_CLASSES_ROOT, aRegPath.get(), nullptr,
|
||||
result = ::RegGetValue(HKEY_LOCAL_MACHINE, fullyQualifiedRegPath.get(), nullptr,
|
||||
flags, nullptr, aOutBuf.BeginWriting(), &bufLen);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
aOutBuf.Truncate();
|
||||
|
|
|
@ -19,16 +19,16 @@ namespace a11y {
|
|||
class Compatibility
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Return true if IAccessible2 disabled.
|
||||
*/
|
||||
static bool IsIA2Off() { return !!(sConsumers & OLDJAWS); }
|
||||
|
||||
/**
|
||||
* Return true if JAWS mode is enabled.
|
||||
*/
|
||||
static bool IsJAWS() { return !!(sConsumers & (JAWS | OLDJAWS)); }
|
||||
|
||||
/**
|
||||
* Return true if using an e10s incompatible Jaws.
|
||||
*/
|
||||
static bool IsOldJAWS() { return !!(sConsumers & OLDJAWS); }
|
||||
|
||||
/**
|
||||
* Return true if WE mode is enabled.
|
||||
*/
|
||||
|
|
|
@ -39,22 +39,21 @@ a11y::PlatformInit()
|
|||
|
||||
nsWinUtils::MaybeStartWindowEmulation();
|
||||
ia2AccessibleText::InitTextChangeData();
|
||||
if (BrowserTabsRemoteAutostart()) {
|
||||
mscom::InterceptorLog::Init();
|
||||
UniquePtr<RegisteredProxy> regCustomProxy(
|
||||
mscom::RegisterProxy());
|
||||
gRegCustomProxy = regCustomProxy.release();
|
||||
UniquePtr<RegisteredProxy> regProxy(
|
||||
mscom::RegisterProxy(L"ia2marshal.dll"));
|
||||
gRegProxy = regProxy.release();
|
||||
UniquePtr<RegisteredProxy> regAccTlb(
|
||||
mscom::RegisterTypelib(L"oleacc.dll",
|
||||
RegistrationFlags::eUseSystemDirectory));
|
||||
gRegAccTlb = regAccTlb.release();
|
||||
UniquePtr<RegisteredProxy> regMiscTlb(
|
||||
mscom::RegisterTypelib(L"Accessible.tlb"));
|
||||
gRegMiscTlb = regMiscTlb.release();
|
||||
}
|
||||
|
||||
mscom::InterceptorLog::Init();
|
||||
UniquePtr<RegisteredProxy> regCustomProxy(
|
||||
mscom::RegisterProxy());
|
||||
gRegCustomProxy = regCustomProxy.release();
|
||||
UniquePtr<RegisteredProxy> regProxy(
|
||||
mscom::RegisterProxy(L"ia2marshal.dll"));
|
||||
gRegProxy = regProxy.release();
|
||||
UniquePtr<RegisteredProxy> regAccTlb(
|
||||
mscom::RegisterTypelib(L"oleacc.dll",
|
||||
RegistrationFlags::eUseSystemDirectory));
|
||||
gRegAccTlb = regAccTlb.release();
|
||||
UniquePtr<RegisteredProxy> regMiscTlb(
|
||||
mscom::RegisterTypelib(L"Accessible.tlb"));
|
||||
gRegMiscTlb = regMiscTlb.release();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -12,4 +12,4 @@ skip-if = !e10s || !crashreporter
|
|||
[browser_autoSubmitRequest.js]
|
||||
skip-if = !e10s || !crashreporter
|
||||
[browser_noPermanentKey.js]
|
||||
skip-if = !e10s || !crashreporter
|
||||
skip-if = !e10s || !crashreporter || (os == "linux" && bits == 32 && debug) # Bug 1383315
|
||||
|
|
|
@ -950,6 +950,7 @@ const DownloadsViewPrototype = {
|
|||
*/
|
||||
onDownloadBatchEnded() {
|
||||
this._loading = false;
|
||||
this._updateViews();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1029,6 +1030,19 @@ const DownloadsViewPrototype = {
|
|||
_updateView() {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
/**
|
||||
* Computes aggregate values and propagates the changes to our views.
|
||||
*/
|
||||
_updateViews() {
|
||||
// Do not update the status indicators during batch loads of download items.
|
||||
if (this._loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._refreshProperties();
|
||||
this._views.forEach(this._updateView, this);
|
||||
},
|
||||
};
|
||||
|
||||
// DownloadsIndicatorData
|
||||
|
@ -1065,13 +1079,6 @@ DownloadsIndicatorDataCtor.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
// Callback functions from DownloadsData
|
||||
|
||||
onDataLoadCompleted() {
|
||||
DownloadsViewPrototype.onDataLoadCompleted.call(this);
|
||||
this._updateViews();
|
||||
},
|
||||
|
||||
onDownloadAdded(download) {
|
||||
DownloadsViewPrototype.onDownloadAdded.call(this, download);
|
||||
this._itemCount++;
|
||||
|
@ -1149,20 +1156,6 @@ DownloadsIndicatorDataCtor.prototype = {
|
|||
},
|
||||
_attentionSuppressed: false,
|
||||
|
||||
/**
|
||||
* Computes aggregate values and propagates the changes to our views.
|
||||
*/
|
||||
_updateViews() {
|
||||
// Do not update the status indicators during batch loads of download items.
|
||||
if (this._loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._refreshProperties();
|
||||
|
||||
this._views.forEach(this._updateView, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the specified view with the current aggregate values.
|
||||
*
|
||||
|
@ -1297,15 +1290,6 @@ DownloadsSummaryData.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
// Callback functions from DownloadsData - see the documentation in
|
||||
// DownloadsViewPrototype for more information on what these functions
|
||||
// are used for.
|
||||
|
||||
onDataLoadCompleted() {
|
||||
DownloadsViewPrototype.onDataLoadCompleted.call(this);
|
||||
this._updateViews();
|
||||
},
|
||||
|
||||
onDownloadAdded(download) {
|
||||
DownloadsViewPrototype.onDownloadAdded.call(this, download);
|
||||
this._downloads.unshift(download);
|
||||
|
@ -1331,19 +1315,6 @@ DownloadsSummaryData.prototype = {
|
|||
|
||||
// Propagation of properties to our views
|
||||
|
||||
/**
|
||||
* Computes aggregate values and propagates the changes to our views.
|
||||
*/
|
||||
_updateViews() {
|
||||
// Do not update the status indicators during batch loads of download items.
|
||||
if (this._loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._refreshProperties();
|
||||
this._views.forEach(this._updateView, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the specified view with the current aggregate values.
|
||||
*
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* This file contains tests for the Preferences search bar.
|
||||
*/
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
// Enabling Searching functionatily. Will display search bar form this testcase forward.
|
||||
add_task(async function() {
|
||||
await SpecialPowers.pushPrefEnv({"set": [["browser.preferences.search", true]]});
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* This file contains tests for the Preferences search bar.
|
||||
*/
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
// Enabling Searching functionatily. Will display search bar form this testcase forward.
|
||||
add_task(async function() {
|
||||
await SpecialPowers.pushPrefEnv({"set": [
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* This file contains tests for the Preferences search bar.
|
||||
*/
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
// Enabling Searching functionatily. Will display search bar form this testcase forward.
|
||||
add_task(async function() {
|
||||
await SpecialPowers.pushPrefEnv({"set": [["browser.preferences.search", true]]});
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
/* 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 "CanRunScriptChecker.h"
|
||||
#include "CustomMatchers.h"
|
||||
|
||||
void CanRunScriptChecker::registerMatchers(MatchFinder* AstMatcher) {
|
||||
auto InvalidArg =
|
||||
// We want to find any expression,
|
||||
ignoreTrivials(expr(
|
||||
// which has a refcounted pointer type,
|
||||
hasType(pointerType(
|
||||
pointee(hasDeclaration(cxxRecordDecl(isRefCounted()))))),
|
||||
// and which is not this,
|
||||
unless(cxxThisExpr()),
|
||||
// and which is not a method call on a smart ptr,
|
||||
unless(cxxMemberCallExpr(on(hasType(isSmartPtrToRefCounted())))),
|
||||
// and which is not a parameter of the parent function,
|
||||
unless(declRefExpr(to(parmVarDecl()))),
|
||||
// and which is not a MOZ_KnownLive wrapped value.
|
||||
unless(callExpr(callee(
|
||||
functionDecl(hasName("MOZ_KnownLive"))))),
|
||||
expr().bind("invalidArg")));
|
||||
|
||||
auto OptionalInvalidExplicitArg =
|
||||
anyOf(
|
||||
// We want to find any argument which is invalid.
|
||||
hasAnyArgument(InvalidArg),
|
||||
|
||||
// This makes this matcher optional.
|
||||
anything());
|
||||
|
||||
// Please not that the hasCanRunScriptAnnotation() matchers are not present
|
||||
// directly in the cxxMemberCallExpr, callExpr and constructExpr matchers
|
||||
// because we check that the corresponding functions can run script later in
|
||||
// the checker code.
|
||||
AstMatcher->addMatcher(
|
||||
expr(
|
||||
anyOf(
|
||||
// We want to match a method call expression,
|
||||
cxxMemberCallExpr(
|
||||
// which optionally has an invalid arg,
|
||||
OptionalInvalidExplicitArg,
|
||||
// or which optionally has an invalid implicit this argument,
|
||||
anyOf(
|
||||
// which derefs into an invalid arg,
|
||||
on(cxxOperatorCallExpr(
|
||||
anyOf(
|
||||
hasAnyArgument(InvalidArg),
|
||||
anything()))),
|
||||
// or is an invalid arg.
|
||||
on(InvalidArg),
|
||||
|
||||
anything()),
|
||||
expr().bind("callExpr")),
|
||||
// or a regular call expression,
|
||||
callExpr(
|
||||
// which optionally has an invalid arg.
|
||||
OptionalInvalidExplicitArg,
|
||||
expr().bind("callExpr")),
|
||||
// or a construct expression,
|
||||
cxxConstructExpr(
|
||||
// which optionally has an invalid arg.
|
||||
OptionalInvalidExplicitArg,
|
||||
expr().bind("constructExpr"))),
|
||||
|
||||
anyOf(
|
||||
// We want to match the parent function.
|
||||
forFunction(functionDecl().bind("nonCanRunScriptParentFunction")),
|
||||
|
||||
// ... optionally.
|
||||
anything())),
|
||||
this);
|
||||
}
|
||||
|
||||
void CanRunScriptChecker::onStartOfTranslationUnit() {
|
||||
IsFuncSetBuilt = false;
|
||||
CanRunScriptFuncs.clear();
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// This class is a callback used internally to match function declarations
|
||||
/// with the MOZ_CAN_RUN_SCRIPT annotation, adding these functions and all
|
||||
/// the methods they override to the can-run-script function set.
|
||||
class FuncSetCallback : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
FuncSetCallback(std::unordered_set<const FunctionDecl*> &FuncSet)
|
||||
: CanRunScriptFuncs(FuncSet) {}
|
||||
|
||||
void run(const MatchFinder::MatchResult &Result) override;
|
||||
|
||||
private:
|
||||
/// This method recursively adds all the methods overriden by the given
|
||||
/// paremeter.
|
||||
void addAllOverriddenMethodsRecursively(const CXXMethodDecl* Method);
|
||||
|
||||
std::unordered_set<const FunctionDecl*> &CanRunScriptFuncs;
|
||||
};
|
||||
|
||||
void FuncSetCallback::run(const MatchFinder::MatchResult &Result) {
|
||||
const FunctionDecl* Func =
|
||||
Result.Nodes.getNodeAs<FunctionDecl>("canRunScriptFunction");
|
||||
|
||||
CanRunScriptFuncs.insert(Func);
|
||||
|
||||
// If this is a method, we check the methods it overrides.
|
||||
if (auto* Method = dyn_cast<CXXMethodDecl>(Func)) {
|
||||
addAllOverriddenMethodsRecursively(Method);
|
||||
}
|
||||
}
|
||||
|
||||
void FuncSetCallback::addAllOverriddenMethodsRecursively(
|
||||
const CXXMethodDecl* Method) {
|
||||
for (auto OverriddenMethod : Method->overridden_methods()) {
|
||||
CanRunScriptFuncs.insert(OverriddenMethod);
|
||||
|
||||
// If this is not the definition, we also add the definition (if it
|
||||
// exists) to the set.
|
||||
if (!OverriddenMethod->isThisDeclarationADefinition()) {
|
||||
if (auto Def = OverriddenMethod->getDefinition()) {
|
||||
CanRunScriptFuncs.insert(Def);
|
||||
}
|
||||
}
|
||||
|
||||
addAllOverriddenMethodsRecursively(OverriddenMethod);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void CanRunScriptChecker::buildFuncSet(ASTContext *Context) {
|
||||
// We create a match finder.
|
||||
MatchFinder Finder;
|
||||
// We create the callback which will be called when we find a function with
|
||||
// a MOZ_CAN_RUN_SCRIPT annotation.
|
||||
FuncSetCallback Callback(CanRunScriptFuncs);
|
||||
// We add the matcher to the finder, linking it to our callback.
|
||||
Finder.addMatcher(functionDecl(hasCanRunScriptAnnotation())
|
||||
.bind("canRunScriptFunction"),
|
||||
&Callback);
|
||||
|
||||
// We start the analysis, given the ASTContext our main checker is in.
|
||||
Finder.matchAST(*Context);
|
||||
}
|
||||
|
||||
void CanRunScriptChecker::check(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
|
||||
// If the set of functions which can run script is not yet built, then build
|
||||
// it.
|
||||
if (!IsFuncSetBuilt) {
|
||||
buildFuncSet(Result.Context);
|
||||
IsFuncSetBuilt = true;
|
||||
}
|
||||
|
||||
const char* ErrorInvalidArg =
|
||||
"arguments must all be strong refs or parent parameters when calling a "
|
||||
"function marked as MOZ_CAN_RUN_SCRIPT (including the implicit object "
|
||||
"argument)";
|
||||
|
||||
const char* ErrorNonCanRunScriptParent =
|
||||
"functions marked as MOZ_CAN_RUN_SCRIPT can only be called from "
|
||||
"functions also marked as MOZ_CAN_RUN_SCRIPT";
|
||||
const char* NoteNonCanRunScriptParent =
|
||||
"parent function declared here";
|
||||
|
||||
const Expr* InvalidArg = Result.Nodes.getNodeAs<Expr>("invalidArg");
|
||||
|
||||
const CallExpr* Call = Result.Nodes.getNodeAs<CallExpr>("callExpr");
|
||||
// If we don't find the FunctionDecl linked to this call or if it's not marked
|
||||
// as can-run-script, consider that we didn't find a match.
|
||||
if (Call && (!Call->getDirectCallee() ||
|
||||
!CanRunScriptFuncs.count(Call->getDirectCallee()))) {
|
||||
Call = nullptr;
|
||||
}
|
||||
|
||||
const CXXConstructExpr* Construct =
|
||||
Result.Nodes.getNodeAs<CXXConstructExpr>("constructExpr");
|
||||
|
||||
// If we don't find the CXXConstructorDecl linked to this construct expression
|
||||
// or if it's not marked as can-run-script, consider that we didn't find a
|
||||
// match.
|
||||
if (Construct && (!Construct->getConstructor() ||
|
||||
!CanRunScriptFuncs.count(Construct->getConstructor()))) {
|
||||
Construct = nullptr;
|
||||
}
|
||||
|
||||
const FunctionDecl* ParentFunction =
|
||||
Result.Nodes.getNodeAs<FunctionDecl>("nonCanRunScriptParentFunction");
|
||||
// If the parent function can run script, consider that we didn't find a match
|
||||
// because we only care about parent functions which can't run script.
|
||||
if (ParentFunction && CanRunScriptFuncs.count(ParentFunction)) {
|
||||
ParentFunction = nullptr;
|
||||
}
|
||||
|
||||
|
||||
// Get the call range from either the CallExpr or the ConstructExpr.
|
||||
SourceRange CallRange;
|
||||
if (Call) {
|
||||
CallRange = Call->getSourceRange();
|
||||
} else if (Construct) {
|
||||
CallRange = Construct->getSourceRange();
|
||||
} else {
|
||||
// If we have neither a Call nor a Construct, we have nothing do to here.
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have an invalid argument in the call, we emit the diagnostic to
|
||||
// signal it.
|
||||
if (InvalidArg) {
|
||||
diag(CallRange.getBegin(), ErrorInvalidArg, DiagnosticIDs::Error)
|
||||
<< CallRange;
|
||||
}
|
||||
|
||||
// If the parent function is not marked as MOZ_CAN_RUN_SCRIPT, we emit an
|
||||
// error and a not indicating it.
|
||||
if (ParentFunction) {
|
||||
diag(CallRange.getBegin(), ErrorNonCanRunScriptParent, DiagnosticIDs::Error)
|
||||
<< CallRange;
|
||||
|
||||
diag(ParentFunction->getLocation(), NoteNonCanRunScriptParent,
|
||||
DiagnosticIDs::Note);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/* 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 CanRunScriptChecker_h__
|
||||
#define CanRunScriptChecker_h__
|
||||
|
||||
#include "plugin.h"
|
||||
#include <unordered_set>
|
||||
|
||||
class CanRunScriptChecker : public BaseCheck {
|
||||
public:
|
||||
CanRunScriptChecker(StringRef CheckName,
|
||||
ContextType *Context = nullptr)
|
||||
: BaseCheck(CheckName, Context) {}
|
||||
void registerMatchers(MatchFinder* AstMatcher) override;
|
||||
void check(const MatchFinder::MatchResult &Result) override;
|
||||
|
||||
// Simply initialize the can-run-script function set at the beginning of each
|
||||
// translation unit.
|
||||
void onStartOfTranslationUnit() override;
|
||||
|
||||
private:
|
||||
/// Runs the inner matcher on the AST to find all the can-run-script
|
||||
/// functions using custom rules (not only the annotation).
|
||||
void buildFuncSet(ASTContext *Context);
|
||||
|
||||
bool IsFuncSetBuilt;
|
||||
std::unordered_set<const FunctionDecl*> CanRunScriptFuncs;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
CHECK(ArithmeticArgChecker, "arithmetic-argument")
|
||||
CHECK(AssertAssignmentChecker, "assignment-in-assert")
|
||||
CHECK(CanRunScriptChecker, "can-run-script")
|
||||
CHECK(DanglingOnTemporaryChecker, "dangling-on-temporary")
|
||||
CHECK(ExplicitImplicitChecker, "implicit-constructor")
|
||||
CHECK(ExplicitOperatorBoolChecker, "explicit-operator-bool")
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "ArithmeticArgChecker.h"
|
||||
#include "AssertAssignmentChecker.h"
|
||||
#include "CanRunScriptChecker.h"
|
||||
#include "DanglingOnTemporaryChecker.h"
|
||||
#include "ExplicitImplicitChecker.h"
|
||||
#include "ExplicitOperatorBoolChecker.h"
|
||||
|
|
|
@ -65,6 +65,12 @@ AST_MATCHER(FunctionDecl, hasNoAddRefReleaseOnReturnAttr) {
|
|||
return hasCustomAnnotation(&Node, "moz_no_addref_release_on_return");
|
||||
}
|
||||
|
||||
/// This matcher will match any function declaration that is marked as being
|
||||
/// allowed to run script.
|
||||
AST_MATCHER(FunctionDecl, hasCanRunScriptAnnotation) {
|
||||
return hasCustomAnnotation(&Node, "moz_can_run_script");
|
||||
}
|
||||
|
||||
/// This matcher will match all arithmetic binary operators.
|
||||
AST_MATCHER(BinaryOperator, binaryArithmeticOperator) {
|
||||
BinaryOperatorKind OpCode = Node.getOpcode();
|
||||
|
@ -137,11 +143,15 @@ AST_MATCHER(MemberExpr, isAddRefOrRelease) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/// This matcher will select classes which are refcounted.
|
||||
/// This matcher will select classes which are refcounted AND have an mRefCnt
|
||||
/// member.
|
||||
AST_MATCHER(CXXRecordDecl, hasRefCntMember) {
|
||||
return isClassRefCounted(&Node) && getClassRefCntMember(&Node);
|
||||
}
|
||||
|
||||
/// This matcher will select classes which are refcounted.
|
||||
AST_MATCHER(CXXRecordDecl, isRefCounted) { return isClassRefCounted(&Node); }
|
||||
|
||||
AST_MATCHER(QualType, hasVTable) { return typeHasVTable(Node); }
|
||||
|
||||
AST_MATCHER(CXXRecordDecl, hasNeedsNoVTableTypeAttr) {
|
||||
|
@ -239,6 +249,17 @@ AST_MATCHER(CXXRecordDecl, isLambdaDecl) { return Node.isLambda(); }
|
|||
|
||||
AST_MATCHER(QualType, isRefPtr) { return typeIsRefPtr(Node); }
|
||||
|
||||
AST_MATCHER(QualType, isSmartPtrToRefCounted) {
|
||||
auto *D = getNonTemplateSpecializedCXXRecordDecl(Node);
|
||||
if (!D) {
|
||||
return false;
|
||||
}
|
||||
|
||||
D = D->getCanonicalDecl();
|
||||
|
||||
return D && hasCustomAnnotation(D, "moz_is_smartptr_to_refcounted");
|
||||
}
|
||||
|
||||
AST_MATCHER(CXXRecordDecl, hasBaseClasses) {
|
||||
const CXXRecordDecl *Decl = Node.getCanonicalDecl();
|
||||
|
||||
|
@ -260,6 +281,50 @@ AST_MATCHER(FunctionDecl, isMozMustReturnFromCaller) {
|
|||
const FunctionDecl *Decl = Node.getCanonicalDecl();
|
||||
return Decl && hasCustomAnnotation(Decl, "moz_must_return_from_caller");
|
||||
}
|
||||
|
||||
#if CLANG_VERSION_FULL < 309
|
||||
/// DISCLAIMER: This is a copy/paste from the Clang source code starting from
|
||||
/// Clang 3.9, so that this matcher is supported in lower versions.
|
||||
///
|
||||
/// \brief Matches declaration of the function the statement belongs to
|
||||
///
|
||||
/// Given:
|
||||
/// \code
|
||||
/// F& operator=(const F& o) {
|
||||
/// std::copy_if(o.begin(), o.end(), begin(), [](V v) { return v > 0; });
|
||||
/// return *this;
|
||||
/// }
|
||||
/// \endcode
|
||||
/// returnStmt(forFunction(hasName("operator=")))
|
||||
/// matches 'return *this'
|
||||
/// but does match 'return > 0'
|
||||
AST_MATCHER_P(Stmt, forFunction, internal::Matcher<FunctionDecl>,
|
||||
InnerMatcher) {
|
||||
const auto &Parents = Finder->getASTContext().getParents(Node);
|
||||
|
||||
llvm::SmallVector<ast_type_traits::DynTypedNode, 8> Stack(Parents.begin(),
|
||||
Parents.end());
|
||||
while(!Stack.empty()) {
|
||||
const auto &CurNode = Stack.back();
|
||||
Stack.pop_back();
|
||||
if(const auto *FuncDeclNode = CurNode.get<FunctionDecl>()) {
|
||||
if(InnerMatcher.matches(*FuncDeclNode, Finder, Builder)) {
|
||||
return true;
|
||||
}
|
||||
} else if(const auto *LambdaExprNode = CurNode.get<LambdaExpr>()) {
|
||||
if(InnerMatcher.matches(*LambdaExprNode->getCallOperator(),
|
||||
Finder, Builder)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
for(const auto &Parent: Finder->getASTContext().getParents(CurNode))
|
||||
Stack.push_back(Parent);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -428,6 +428,29 @@ inline bool inThirdPartyPath(const Decl *D, ASTContext *context) {
|
|||
return inThirdPartyPath(Loc, SM);
|
||||
}
|
||||
|
||||
inline CXXRecordDecl* getNonTemplateSpecializedCXXRecordDecl(QualType Q) {
|
||||
auto *D = Q->getAsCXXRecordDecl();
|
||||
|
||||
if (!D) {
|
||||
auto TemplateQ = Q->getAs<TemplateSpecializationType>();
|
||||
if (!TemplateQ) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto TemplateDecl = TemplateQ->getTemplateName().getAsTemplateDecl();
|
||||
if (!TemplateDecl) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
D = dyn_cast_or_null<CXXRecordDecl>(TemplateDecl->getTemplatedDecl());
|
||||
if (!D) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return D;
|
||||
}
|
||||
|
||||
inline bool inThirdPartyPath(const Decl *D) {
|
||||
return inThirdPartyPath(D, &D->getASTContext());
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ SOURCES += ['!ThirdPartyPaths.cpp']
|
|||
UNIFIED_SOURCES += [
|
||||
'ArithmeticArgChecker.cpp',
|
||||
'AssertAssignmentChecker.cpp',
|
||||
'CanRunScriptChecker.cpp',
|
||||
'CustomTypeAnnotation.cpp',
|
||||
'DanglingOnTemporaryChecker.cpp',
|
||||
'DiagnosticsMatcher.cpp',
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
#include <mozilla/RefPtr.h>
|
||||
|
||||
#define MOZ_CAN_RUN_SCRIPT __attribute__((annotate("moz_can_run_script")))
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void test() {
|
||||
|
||||
}
|
||||
|
||||
void test_parent() { // expected-note {{parent function declared here}}
|
||||
test(); // expected-error {{functions marked as MOZ_CAN_RUN_SCRIPT can only be called from functions also marked as MOZ_CAN_RUN_SCRIPT}}
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void test_parent2() {
|
||||
test();
|
||||
}
|
||||
|
||||
struct RefCountedBase;
|
||||
MOZ_CAN_RUN_SCRIPT void test2(RefCountedBase* param) {
|
||||
|
||||
}
|
||||
|
||||
struct RefCountedBase {
|
||||
void AddRef();
|
||||
void Release();
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void method_test() {
|
||||
test();
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void method_test2() {
|
||||
test2(this);
|
||||
}
|
||||
|
||||
virtual void method_test3() {
|
||||
test();
|
||||
}
|
||||
};
|
||||
|
||||
void test2_parent() { // expected-note {{parent function declared here}}
|
||||
test2(new RefCountedBase); // expected-error {{arguments must all be strong refs or parent parameters when calling a function marked as MOZ_CAN_RUN_SCRIPT (including the implicit object argument)}} \
|
||||
// expected-error {{functions marked as MOZ_CAN_RUN_SCRIPT can only be called from functions also marked as MOZ_CAN_RUN_SCRIPT}}
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void test2_parent2() {
|
||||
test2(new RefCountedBase); // expected-error {{arguments must all be strong refs or parent parameters when calling a function marked as MOZ_CAN_RUN_SCRIPT (including the implicit object argument)}}
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void test2_parent3(RefCountedBase* param) {
|
||||
test2(param);
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void test2_parent4() {
|
||||
RefPtr<RefCountedBase> refptr = new RefCountedBase;
|
||||
test2(refptr);
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void test2_parent5() {
|
||||
test2(MOZ_KnownLive(new RefCountedBase));
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void test2_parent6() {
|
||||
RefPtr<RefCountedBase> refptr = new RefCountedBase;
|
||||
refptr->method_test();
|
||||
refptr->method_test2();
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void test2_parent7() {
|
||||
RefCountedBase* t = new RefCountedBase;
|
||||
t->method_test(); // expected-error {{arguments must all be strong refs or parent parameters when calling a function marked as MOZ_CAN_RUN_SCRIPT (including the implicit object argument)}}
|
||||
t->method_test2(); // expected-error {{arguments must all be strong refs or parent parameters when calling a function marked as MOZ_CAN_RUN_SCRIPT (including the implicit object argument)}}
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void test3(int* param) {}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void test3_parent() {
|
||||
test3(new int);
|
||||
}
|
||||
|
||||
struct RefCountedChild : public RefCountedBase {
|
||||
virtual void method_test3() override;
|
||||
};
|
||||
|
||||
void RefCountedChild::method_test3() {
|
||||
test();
|
||||
}
|
||||
|
||||
struct RefCountedSubChild : public RefCountedChild {
|
||||
MOZ_CAN_RUN_SCRIPT void method_test3() override;
|
||||
};
|
||||
|
||||
void RefCountedSubChild::method_test3() {
|
||||
test();
|
||||
}
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT void test4() {
|
||||
RefPtr<RefCountedBase> refptr1 = new RefCountedChild;
|
||||
refptr1->method_test3();
|
||||
|
||||
RefPtr<RefCountedBase> refptr2 = new RefCountedSubChild;
|
||||
refptr2->method_test3();
|
||||
|
||||
RefPtr<RefCountedChild> refptr3 = new RefCountedSubChild;
|
||||
refptr3->method_test3();
|
||||
|
||||
RefPtr<RefCountedSubChild> refptr4 = new RefCountedSubChild;
|
||||
refptr4->method_test3();
|
||||
}
|
|
@ -10,6 +10,7 @@ Library('clang-plugin-tests')
|
|||
SOURCES += [
|
||||
'TestAssertWithAssignment.cpp',
|
||||
'TestBadImplicitConversionCtor.cpp',
|
||||
'TestCanRunScript.cpp',
|
||||
'TestCustomHeap.cpp',
|
||||
'TestDanglingOnTemporary.cpp',
|
||||
'TestExplicitOperatorBool.cpp',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/******************************************************************************
|
||||
** This file is an amalgamation of many separate C source files from SQLite
|
||||
** version 3.20.0. By combining all the individual C code files into this
|
||||
** version 3.20.1. By combining all the individual C code files into this
|
||||
** single large file, the entire code can be compiled as a single translation
|
||||
** unit. This allows many compilers to do optimizations that would not be
|
||||
** possible if the files were compiled separately. Performance improvements
|
||||
|
@ -1150,9 +1150,9 @@ extern "C" {
|
|||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.20.0"
|
||||
#define SQLITE_VERSION_NUMBER 3020000
|
||||
#define SQLITE_SOURCE_ID "2017-08-01 13:24:15 9501e22dfeebdcefa783575e47c60b514d7c2e0cad73b2a496c0bc4b680900a8"
|
||||
#define SQLITE_VERSION "3.20.1"
|
||||
#define SQLITE_VERSION_NUMBER 3020001
|
||||
#define SQLITE_SOURCE_ID "2017-08-24 16:21:36 8d3a7ea6c5690d6b7c3767558f4f01b511c55463e3f9e64506801fe9b74dce34"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
|
@ -77122,7 +77122,8 @@ SQLITE_API void sqlite3_result_pointer(
|
|||
){
|
||||
Mem *pOut = pCtx->pOut;
|
||||
assert( sqlite3_mutex_held(pOut->db->mutex) );
|
||||
sqlite3VdbeMemSetNull(pOut);
|
||||
sqlite3VdbeMemRelease(pOut);
|
||||
pOut->flags = MEM_Null;
|
||||
sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor);
|
||||
}
|
||||
SQLITE_API void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
|
||||
|
@ -200275,7 +200276,7 @@ static void fts5SourceIdFunc(
|
|||
){
|
||||
assert( nArg==0 );
|
||||
UNUSED_PARAM2(nArg, apUnused);
|
||||
sqlite3_result_text(pCtx, "fts5: 2017-08-01 13:24:15 9501e22dfeebdcefa783575e47c60b514d7c2e0cad73b2a496c0bc4b680900a8", -1, SQLITE_TRANSIENT);
|
||||
sqlite3_result_text(pCtx, "fts5: 2017-08-24 16:21:36 8d3a7ea6c5690d6b7c3767558f4f01b511c55463e3f9e64506801fe9b74dce34", -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
static int fts5Init(sqlite3 *db){
|
||||
|
|
|
@ -121,9 +121,9 @@ extern "C" {
|
|||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.20.0"
|
||||
#define SQLITE_VERSION_NUMBER 3020000
|
||||
#define SQLITE_SOURCE_ID "2017-08-01 13:24:15 9501e22dfeebdcefa783575e47c60b514d7c2e0cad73b2a496c0bc4b680900a8"
|
||||
#define SQLITE_VERSION "3.20.1"
|
||||
#define SQLITE_VERSION_NUMBER 3020001
|
||||
#define SQLITE_SOURCE_ID "2017-08-24 16:21:36 8d3a7ea6c5690d6b7c3767558f4f01b511c55463e3f9e64506801fe9b74dce34"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
|
||||
#include "ChromeUtils.h"
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
#include "WrapperFactory.h"
|
||||
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
|
||||
|
@ -126,6 +129,141 @@ ThreadSafeChromeUtils::Base64URLDecode(GlobalObject& aGlobal,
|
|||
aRetval.set(buffer);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ChromeUtils::WaiveXrays(GlobalObject& aGlobal,
|
||||
JS::HandleValue aVal,
|
||||
JS::MutableHandleValue aRetval,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
JS::RootedValue value(aGlobal.Context(), aVal);
|
||||
if (!xpc::WrapperFactory::WaiveXrayAndWrap(aGlobal.Context(), &value)) {
|
||||
aRv.NoteJSContextException(aGlobal.Context());
|
||||
} else {
|
||||
aRetval.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ChromeUtils::UnwaiveXrays(GlobalObject& aGlobal,
|
||||
JS::HandleValue aVal,
|
||||
JS::MutableHandleValue aRetval,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!aVal.isObject()) {
|
||||
aRetval.set(aVal);
|
||||
return;
|
||||
}
|
||||
|
||||
JS::RootedObject obj(aGlobal.Context(), js::UncheckedUnwrap(&aVal.toObject()));
|
||||
if (!JS_WrapObject(aGlobal.Context(), &obj)) {
|
||||
aRv.NoteJSContextException(aGlobal.Context());
|
||||
} else {
|
||||
aRetval.setObject(*obj);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ChromeUtils::GetClassName(GlobalObject& aGlobal,
|
||||
JS::HandleObject aObj,
|
||||
bool aUnwrap,
|
||||
nsAString& aRetval)
|
||||
{
|
||||
JS::RootedObject obj(aGlobal.Context(), aObj);
|
||||
if (aUnwrap) {
|
||||
obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
|
||||
}
|
||||
|
||||
aRetval = NS_ConvertUTF8toUTF16(nsDependentCString(js::GetObjectClass(obj)->name));
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ChromeUtils::ShallowClone(GlobalObject& aGlobal,
|
||||
JS::HandleObject aObj,
|
||||
JS::HandleObject aTarget,
|
||||
JS::MutableHandleObject aRetval,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
JSContext* cx = aGlobal.Context();
|
||||
|
||||
auto cleanup = MakeScopeExit([&] () {
|
||||
aRv.NoteJSContextException(cx);
|
||||
});
|
||||
|
||||
JS::Rooted<JS::IdVector> ids(cx, JS::IdVector(cx));
|
||||
JS::AutoValueVector values(cx);
|
||||
|
||||
{
|
||||
JS::RootedObject obj(cx, js::CheckedUnwrap(aObj));
|
||||
if (!obj) {
|
||||
js::ReportAccessDenied(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (js::IsScriptedProxy(obj)) {
|
||||
JS_ReportErrorASCII(cx, "Shallow cloning a proxy object is not allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
JSAutoCompartment ac(cx, obj);
|
||||
|
||||
if (!JS_Enumerate(cx, obj, &ids) ||
|
||||
!values.reserve(ids.length())) {
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::PropertyDescriptor> desc(cx);
|
||||
JS::RootedId id(cx);
|
||||
for (jsid idVal : ids) {
|
||||
id = idVal;
|
||||
if (!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc)) {
|
||||
continue;
|
||||
}
|
||||
if (desc.setter() || desc.getter()) {
|
||||
continue;
|
||||
}
|
||||
values.infallibleAppend(desc.value());
|
||||
}
|
||||
}
|
||||
|
||||
JS::RootedObject obj(cx);
|
||||
{
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
if (aTarget) {
|
||||
JS::RootedObject target(cx, js::CheckedUnwrap(aTarget));
|
||||
if (!target) {
|
||||
js::ReportAccessDenied(cx);
|
||||
return;
|
||||
}
|
||||
ac.emplace(cx, target);
|
||||
}
|
||||
|
||||
obj = JS_NewPlainObject(cx);
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
JS::RootedValue value(cx);
|
||||
JS::RootedId id(cx);
|
||||
for (uint32_t i = 0; i < ids.length(); i++) {
|
||||
id = ids[i];
|
||||
value = values[i];
|
||||
|
||||
JS_MarkCrossZoneId(cx, id);
|
||||
if (!JS_WrapValue(cx, &value) ||
|
||||
!JS_SetPropertyById(cx, obj, id, value)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aTarget && !JS_WrapObject(cx, &obj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cleanup.release();
|
||||
aRetval.set(obj);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ChromeUtils::OriginAttributesToSuffix(dom::GlobalObject& aGlobal,
|
||||
const dom::OriginAttributesDictionary& aAttrs,
|
||||
|
|
|
@ -124,6 +124,27 @@ public:
|
|||
const nsAString& aUrl,
|
||||
const dom::CompileScriptOptionsDictionary& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static void WaiveXrays(GlobalObject& aGlobal,
|
||||
JS::HandleValue aVal,
|
||||
JS::MutableHandleValue aRetval,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static void UnwaiveXrays(GlobalObject& aGlobal,
|
||||
JS::HandleValue aVal,
|
||||
JS::MutableHandleValue aRetval,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static void GetClassName(GlobalObject& aGlobal,
|
||||
JS::HandleObject aObj,
|
||||
bool aUnwrap,
|
||||
nsAString& aRetval);
|
||||
|
||||
static void ShallowClone(GlobalObject& aGlobal,
|
||||
JS::HandleObject aObj,
|
||||
JS::HandleObject aTarget,
|
||||
JS::MutableHandleObject aRetval,
|
||||
ErrorResult& aRv);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -90,3 +90,7 @@ method DataTransfer.mozSetDataAt
|
|||
method DataTransfer.mozGetDataAt
|
||||
attribute DataTransfer.mozUserCancelled
|
||||
attribute DataTransfer.mozSourceNode
|
||||
|
||||
// JavaScript feature usage
|
||||
custom JS_asmjs uses asm.js
|
||||
custom JS_wasm uses WebAssembly
|
||||
|
|
|
@ -8921,12 +8921,9 @@ nsContentUtils::GetWindowRoot(nsIDocument* aDoc)
|
|||
bool
|
||||
nsContentUtils::IsPreloadType(nsContentPolicyType aType)
|
||||
{
|
||||
if (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
|
||||
aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
|
||||
aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
|
||||
aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
|
||||
aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -9765,19 +9765,23 @@ already_AddRefed<nsIURI>
|
|||
nsDocument::ResolvePreloadImage(nsIURI *aBaseURI,
|
||||
const nsAString& aSrcAttr,
|
||||
const nsAString& aSrcsetAttr,
|
||||
const nsAString& aSizesAttr)
|
||||
const nsAString& aSizesAttr,
|
||||
bool *aIsImgSet)
|
||||
{
|
||||
nsString sourceURL;
|
||||
bool isImgSet;
|
||||
if (mPreloadPictureDepth == 1 && !mPreloadPictureFoundSource.IsVoid()) {
|
||||
// We're in a <picture> element and found a URI from a source previous to
|
||||
// this image, use it.
|
||||
sourceURL = mPreloadPictureFoundSource;
|
||||
isImgSet = true;
|
||||
} else {
|
||||
// Otherwise try to use this <img> as a source
|
||||
HTMLImageElement::SelectSourceForTagWithAttrs(this, false, aSrcAttr,
|
||||
aSrcsetAttr, aSizesAttr,
|
||||
NullString(), NullString(),
|
||||
sourceURL);
|
||||
isImgSet = !aSrcsetAttr.IsEmpty();
|
||||
}
|
||||
|
||||
// Empty sources are not loaded by <img> (i.e. not resolved to the baseURI)
|
||||
|
@ -9795,6 +9799,8 @@ nsDocument::ResolvePreloadImage(nsIURI *aBaseURI,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
*aIsImgSet = isImgSet;
|
||||
|
||||
// We don't clear mPreloadPictureFoundSource because subsequent <img> tags in
|
||||
// this this <picture> share the same <sources> (though this is not valid per
|
||||
// spec)
|
||||
|
@ -9803,16 +9809,12 @@ nsDocument::ResolvePreloadImage(nsIURI *aBaseURI,
|
|||
|
||||
void
|
||||
nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
|
||||
ReferrerPolicy aReferrerPolicy)
|
||||
ReferrerPolicy aReferrerPolicy, bool aIsImgSet)
|
||||
{
|
||||
// Early exit if the img is already present in the img-cache
|
||||
// which indicates that the "real" load has already started and
|
||||
// that we shouldn't preload it.
|
||||
int16_t blockingStatus;
|
||||
if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this)) ||
|
||||
!nsContentUtils::CanLoadImage(uri, static_cast<nsIDocument *>(this),
|
||||
this, NodePrincipal(), &blockingStatus,
|
||||
nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD)) {
|
||||
if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -9831,6 +9833,10 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
|
|||
MOZ_CRASH("Unknown CORS mode!");
|
||||
}
|
||||
|
||||
nsContentPolicyType policyType =
|
||||
aIsImgSet ? nsIContentPolicy::TYPE_IMAGESET :
|
||||
nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD;
|
||||
|
||||
// Image not in cache - trigger preload
|
||||
RefPtr<imgRequestProxy> request;
|
||||
nsresult rv =
|
||||
|
@ -9844,7 +9850,7 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
|
|||
loadFlags,
|
||||
NS_LITERAL_STRING("img"),
|
||||
getter_AddRefs(request),
|
||||
nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD);
|
||||
policyType);
|
||||
|
||||
// Pin image-reference to avoid evicting it from the img-cache before
|
||||
// the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
|
||||
|
|
|
@ -823,11 +823,14 @@ public:
|
|||
ResolvePreloadImage(nsIURI *aBaseURI,
|
||||
const nsAString& aSrcAttr,
|
||||
const nsAString& aSrcsetAttr,
|
||||
const nsAString& aSizesAttr) override;
|
||||
const nsAString& aSizesAttr,
|
||||
bool *aIsImgSet) override;
|
||||
|
||||
virtual void MaybePreLoadImage(nsIURI* uri,
|
||||
const nsAString &aCrossOriginAttr,
|
||||
ReferrerPolicy aReferrerPolicy) override;
|
||||
ReferrerPolicy aReferrerPolicy,
|
||||
bool aIsImgSet) override;
|
||||
|
||||
virtual void ForgetImagePreload(nsIURI* aURI) override;
|
||||
|
||||
virtual void MaybePreconnect(nsIURI* uri,
|
||||
|
|
|
@ -974,7 +974,7 @@ nsGenericDOMDataNode::GetText()
|
|||
uint32_t
|
||||
nsGenericDOMDataNode::TextLength() const
|
||||
{
|
||||
return mText.GetLength();
|
||||
return TextDataLength();
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -228,6 +228,11 @@ public:
|
|||
rv = ReplaceData(aOffset, aCount, aData);
|
||||
}
|
||||
|
||||
uint32_t TextDataLength() const
|
||||
{
|
||||
return mText.GetLength();
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -2403,21 +2403,27 @@ public:
|
|||
* nesting and possible sources, which are used to inform URL selection
|
||||
* responsive <picture> or <img srcset> images. Unset attributes are expected
|
||||
* to be marked void.
|
||||
* If this image is for <picture> or <img srcset>, aIsImgSet will be set to
|
||||
* true, false otherwise.
|
||||
*/
|
||||
virtual already_AddRefed<nsIURI>
|
||||
ResolvePreloadImage(nsIURI *aBaseURI,
|
||||
const nsAString& aSrcAttr,
|
||||
const nsAString& aSrcsetAttr,
|
||||
const nsAString& aSizesAttr) = 0;
|
||||
const nsAString& aSizesAttr,
|
||||
bool *aIsImgSet) = 0;
|
||||
/**
|
||||
* Called by nsParser to preload images. Can be removed and code moved
|
||||
* to nsPreloadURIs::PreloadURIs() in file nsParser.cpp whenever the
|
||||
* parser-module is linked with gklayout-module. aCrossOriginAttr should
|
||||
* be a void string if the attr is not present.
|
||||
* aIsImgSet is the value got from calling ResolvePreloadImage, it is true
|
||||
* when this image is for loading <picture> or <img srcset> images.
|
||||
*/
|
||||
virtual void MaybePreLoadImage(nsIURI* uri,
|
||||
const nsAString& aCrossOriginAttr,
|
||||
ReferrerPolicyEnum aReferrerPolicy) = 0;
|
||||
ReferrerPolicyEnum aReferrerPolicy,
|
||||
bool aIsImgSet) = 0;
|
||||
|
||||
/**
|
||||
* Called by images to forget an image preload when they start doing
|
||||
|
|
|
@ -3747,8 +3747,7 @@ AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
|
|||
#endif // DEBUG
|
||||
|
||||
void
|
||||
SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject,
|
||||
UseCounter aUseCounter)
|
||||
SetDocumentAndPageUseCounter(JSObject* aObject, UseCounter aUseCounter)
|
||||
{
|
||||
nsGlobalWindow* win = xpc::WindowGlobalOrNull(js::UncheckedUnwrap(aObject));
|
||||
if (win && win->GetDocument()) {
|
||||
|
|
|
@ -3376,8 +3376,7 @@ CreateHTMLElement(const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
|
|||
JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv);
|
||||
|
||||
void
|
||||
SetDocumentAndPageUseCounter(JSContext* aCx, JSObject* aObject,
|
||||
UseCounter aUseCounter);
|
||||
SetDocumentAndPageUseCounter(JSObject* aObject, UseCounter aUseCounter);
|
||||
|
||||
// Warnings
|
||||
void
|
||||
|
|
|
@ -626,6 +626,16 @@ DOMInterfaces = {
|
|||
'nativeType': 'mozilla::dom::time::TimeManager',
|
||||
},
|
||||
|
||||
'MozStorageAsyncStatementParams': {
|
||||
'headerFile': 'mozilla/storage/mozStorageAsyncStatementParams.h',
|
||||
'nativeType': 'mozilla::storage::AsyncStatementParams',
|
||||
},
|
||||
|
||||
'MozStorageStatementParams': {
|
||||
'headerFile': 'mozilla/storage/mozStorageStatementParams.h',
|
||||
'nativeType': 'mozilla::storage::StatementParams',
|
||||
},
|
||||
|
||||
'MozStorageStatementRow': {
|
||||
'headerFile': 'mozilla/storage/mozStorageStatementRow.h',
|
||||
'nativeType': 'mozilla::storage::StatementRow',
|
||||
|
|
|
@ -7878,7 +7878,7 @@ class CGPerSignatureCall(CGThing):
|
|||
|
||||
if useCounterName:
|
||||
# Generate a telemetry call for when [UseCounter] is used.
|
||||
code = "SetDocumentAndPageUseCounter(cx, obj, eUseCounter_%s);\n" % useCounterName
|
||||
code = "SetDocumentAndPageUseCounter(obj, eUseCounter_%s);\n" % useCounterName
|
||||
cgThings.append(CGGeneric(code))
|
||||
|
||||
self.cgRoot = CGList(cgThings)
|
||||
|
|
|
@ -2795,7 +2795,7 @@ nsTextEditorState::HasNonEmptyValue()
|
|||
if (mTextEditor && mBoundFrame && mEditorInitialized &&
|
||||
!mIsCommittingComposition) {
|
||||
bool empty;
|
||||
nsresult rv = mTextEditor->GetDocumentIsEmpty(&empty);
|
||||
nsresult rv = mTextEditor->DocumentIsEmpty(&empty);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return !empty;
|
||||
}
|
||||
|
|
|
@ -15,18 +15,33 @@ function getGlobalState(key)
|
|||
{
|
||||
var data;
|
||||
getObjectState(key, function(x) {
|
||||
data = x.wrappedJSObject.data;
|
||||
data = x && x.wrappedJSObject.data;
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
function finishBlockedRequest(request, response, query)
|
||||
{
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
response.setHeader("Content-Type", "application/javascript", false);
|
||||
response.write("window.script_executed_" + query[1] + " = true; ok(true, 'Script " + query[1] + " executed');");
|
||||
response.finish();
|
||||
}
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
var query = request.queryString.split('&');
|
||||
switch (query[0]) {
|
||||
case "blocked":
|
||||
setGlobalState(response, query[1]);
|
||||
response.processAsync();
|
||||
var alreadyUnblocked = getGlobalState(query[1]);
|
||||
if (alreadyUnblocked) {
|
||||
// the unblock request came before the blocked request, just go on and finish synchronously
|
||||
finishBlockedRequest(request, response, query);
|
||||
} else {
|
||||
setGlobalState(response, query[1]);
|
||||
response.processAsync();
|
||||
}
|
||||
break;
|
||||
|
||||
case "unblock":
|
||||
|
@ -36,11 +51,12 @@ function handleRequest(request, response)
|
|||
response.write("\x89PNG"); // just a broken image is enough for our purpose
|
||||
|
||||
var blockedResponse = getGlobalState(query[1]);
|
||||
blockedResponse.setStatusLine(request.httpVersion, 200, "OK");
|
||||
blockedResponse.setHeader("Cache-Control", "no-cache", false);
|
||||
blockedResponse.setHeader("Content-Type", "application/javascript", false);
|
||||
blockedResponse.write("window.script_executed_" + query[1] + " = true; ok(true, 'Script " + query[1] + " executed');");
|
||||
blockedResponse.finish();
|
||||
if (!blockedResponse) {
|
||||
// the unblock request came before the blocked request, remember to not block it
|
||||
setGlobalState(true, query[1]);
|
||||
} else {
|
||||
finishBlockedRequest(request, blockedResponse, query);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -69,6 +69,38 @@ interface ChromeUtils : ThreadSafeChromeUtils {
|
|||
[NewObject, Throws]
|
||||
static Promise<PrecompiledScript>
|
||||
compileScript(DOMString url, optional CompileScriptOptionsDictionary options);
|
||||
|
||||
/**
|
||||
* Waive Xray on a given value. Identity op for primitives.
|
||||
*/
|
||||
[Throws]
|
||||
static any waiveXrays(any val);
|
||||
|
||||
/**
|
||||
* Strip off Xray waivers on a given value. Identity op for primitives.
|
||||
*/
|
||||
[Throws]
|
||||
static any unwaiveXrays(any val);
|
||||
|
||||
/**
|
||||
* Gets the name of the JSClass of the object.
|
||||
*
|
||||
* if |unwrap| is true, all wrappers are unwrapped first. Unless you're
|
||||
* specifically trying to detect whether the object is a proxy, this is
|
||||
* probably what you want.
|
||||
*/
|
||||
static DOMString getClassName(object obj, optional boolean unwrap = true);
|
||||
|
||||
/**
|
||||
* Clones the properties of the given object into a new object in the given
|
||||
* target compartment (or the caller compartment if no target is provided).
|
||||
* Property values themeselves are not cloned.
|
||||
*
|
||||
* Ignores non-enumerable properties, properties on prototypes, and properties
|
||||
* with getters or setters.
|
||||
*/
|
||||
[Throws]
|
||||
static object shallowClone(object obj, optional object? target = null);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/* -*- Mode: IDL; 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/. */
|
||||
|
||||
[ChromeOnly]
|
||||
interface MozStorageAsyncStatementParams
|
||||
{
|
||||
readonly attribute unsigned long length;
|
||||
|
||||
[Throws]
|
||||
getter any(unsigned long index);
|
||||
|
||||
[Throws]
|
||||
getter any(DOMString name);
|
||||
|
||||
[Throws]
|
||||
setter creator void(unsigned long index, any arg);
|
||||
|
||||
[Throws]
|
||||
setter creator void(DOMString name, any arg);
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
/* -*- Mode: IDL; 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/. */
|
||||
|
||||
[ChromeOnly]
|
||||
interface MozStorageStatementParams
|
||||
{
|
||||
readonly attribute unsigned long length;
|
||||
|
||||
[Throws]
|
||||
getter any(unsigned long index);
|
||||
|
||||
[Throws]
|
||||
getter any(DOMString name);
|
||||
|
||||
[Throws]
|
||||
setter creator void(unsigned long index, any arg);
|
||||
|
||||
[Throws]
|
||||
setter creator void(DOMString name, any arg);
|
||||
};
|
|
@ -699,6 +699,8 @@ WEBIDL_FILES = [
|
|||
'MouseEvent.webidl',
|
||||
'MouseScrollEvent.webidl',
|
||||
'MozSelfSupport.webidl',
|
||||
'MozStorageAsyncStatementParams.webidl',
|
||||
'MozStorageStatementParams.webidl',
|
||||
'MozStorageStatementRow.webidl',
|
||||
'MozTimeManager.webidl',
|
||||
'MozWakeLock.webidl',
|
||||
|
|
|
@ -3753,12 +3753,6 @@ EditorBase::IsTextNode(nsIDOMNode* aNode)
|
|||
return (nodeType == nsIDOMNode::TEXT_NODE);
|
||||
}
|
||||
|
||||
bool
|
||||
EditorBase::IsTextNode(nsINode* aNode)
|
||||
{
|
||||
return aNode->NodeType() == nsIDOMNode::TEXT_NODE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMNode>
|
||||
EditorBase::GetChildAt(nsIDOMNode* aParent, int32_t aOffset)
|
||||
{
|
||||
|
@ -5023,18 +5017,6 @@ EditorBase::FinalizeSelection()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
Element*
|
||||
EditorBase::GetRoot()
|
||||
{
|
||||
if (!mRootElement) {
|
||||
// Let GetRootElement() do the work
|
||||
nsCOMPtr<nsIDOMElement> root;
|
||||
GetRootElement(getter_AddRefs(root));
|
||||
}
|
||||
|
||||
return mRootElement;
|
||||
}
|
||||
|
||||
Element*
|
||||
EditorBase::GetEditorRoot()
|
||||
{
|
||||
|
|
|
@ -877,7 +877,10 @@ public:
|
|||
virtual bool AreNodesSameType(nsIContent* aNode1, nsIContent* aNode2);
|
||||
|
||||
static bool IsTextNode(nsIDOMNode* aNode);
|
||||
static bool IsTextNode(nsINode* aNode);
|
||||
static bool IsTextNode(nsINode* aNode)
|
||||
{
|
||||
return aNode->NodeType() == nsIDOMNode::TEXT_NODE;
|
||||
}
|
||||
|
||||
static nsCOMPtr<nsIDOMNode> GetChildAt(nsIDOMNode* aParent, int32_t aOffset);
|
||||
static nsIContent* GetNodeAtRangeOffsetPoint(nsINode* aParentOrNode,
|
||||
|
@ -969,7 +972,16 @@ public:
|
|||
/**
|
||||
* Fast non-refcounting editor root element accessor
|
||||
*/
|
||||
Element* GetRoot();
|
||||
Element* GetRoot()
|
||||
{
|
||||
if (!mRootElement) {
|
||||
// Let GetRootElement() do the work
|
||||
nsCOMPtr<nsIDOMElement> root;
|
||||
GetRootElement(getter_AddRefs(root));
|
||||
}
|
||||
|
||||
return mRootElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Likewise, but gets the editor's root instead, which is different for HTML
|
||||
|
|
|
@ -358,28 +358,12 @@ TextEditRules::DidDoAction(Selection* aSelection,
|
|||
NS_IMETHODIMP_(bool)
|
||||
TextEditRules::DocumentIsEmpty()
|
||||
{
|
||||
if (mBogusNode) {
|
||||
return true;
|
||||
bool retVal = false;
|
||||
if (!mTextEditor || NS_FAILED(mTextEditor->DocumentIsEmpty(&retVal))) {
|
||||
retVal = true;
|
||||
}
|
||||
|
||||
// Even if there is no bogus node, we should detect as empty document
|
||||
// if all children are text node and these are no content.
|
||||
if (NS_WARN_IF(!mTextEditor)) {
|
||||
return true;
|
||||
}
|
||||
Element* rootElement = mTextEditor->GetRoot();
|
||||
if (!rootElement) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (nsIContent* child = rootElement->GetFirstChild();
|
||||
child; child = child->GetNextSibling()) {
|
||||
if (!EditorBase::IsTextNode(child) ||
|
||||
child->Length()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -115,6 +115,11 @@ public:
|
|||
*/
|
||||
static void FillBufWithPWChars(nsAString* aOutString, int32_t aLength);
|
||||
|
||||
bool HasBogusNode()
|
||||
{
|
||||
return !!mBogusNode;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void InitFields();
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "nsString.h"
|
||||
#include "nsStringFwd.h"
|
||||
#include "nsSubstringTuple.h"
|
||||
#include "nsTextNode.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsXPCOM.h"
|
||||
|
||||
|
@ -899,17 +900,41 @@ TextEditor::GetInputEventTargetContent()
|
|||
return target.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
TextEditor::DocumentIsEmpty(bool* aIsEmpty)
|
||||
{
|
||||
NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (static_cast<TextEditRules*>(mRules.get())->HasBogusNode()) {
|
||||
*aIsEmpty = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Even if there is no bogus node, we should be detected as empty document
|
||||
// if all the children are text nodes and these have no content.
|
||||
Element* rootElement = GetRoot();
|
||||
if (!rootElement) {
|
||||
*aIsEmpty = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
for (nsIContent* child = rootElement->GetFirstChild();
|
||||
child; child = child->GetNextSibling()) {
|
||||
if (!EditorBase::IsTextNode(child) ||
|
||||
static_cast<nsTextNode*>(child)->TextDataLength()) {
|
||||
*aIsEmpty = false;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
*aIsEmpty = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TextEditor::GetDocumentIsEmpty(bool* aDocumentIsEmpty)
|
||||
{
|
||||
NS_ENSURE_TRUE(aDocumentIsEmpty, NS_ERROR_NULL_POINTER);
|
||||
|
||||
NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
// Protect the edit rules object from dying
|
||||
nsCOMPtr<nsIEditRules> rules(mRules);
|
||||
*aDocumentIsEmpty = rules->DocumentIsEmpty();
|
||||
return NS_OK;
|
||||
return DocumentIsEmpty(aDocumentIsEmpty);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -78,6 +78,7 @@ public:
|
|||
nsISelectionController* aSelCon, uint32_t aFlags,
|
||||
const nsAString& aValue) override;
|
||||
|
||||
nsresult DocumentIsEmpty(bool* aIsEmpty);
|
||||
NS_IMETHOD GetDocumentIsEmpty(bool* aDocumentIsEmpty) override;
|
||||
|
||||
NS_IMETHOD DeleteSelection(EDirection aAction,
|
||||
|
|
|
@ -310,9 +310,11 @@ RotatedContentBuffer::BorrowDrawTargetForQuadrantUpdate(const IntRect& aBounds,
|
|||
|
||||
if (aSetTransform) {
|
||||
mLoanedDrawTarget->SetTransform(transform);
|
||||
mSetTransform = true;
|
||||
} else {
|
||||
MOZ_ASSERT(aOutMatrix);
|
||||
*aOutMatrix = transform;
|
||||
mSetTransform = false;
|
||||
}
|
||||
|
||||
return mLoanedDrawTarget;
|
||||
|
@ -324,7 +326,9 @@ BorrowDrawTarget::ReturnDrawTarget(gfx::DrawTarget*& aReturned)
|
|||
MOZ_ASSERT(mLoanedDrawTarget);
|
||||
MOZ_ASSERT(aReturned == mLoanedDrawTarget);
|
||||
if (mLoanedDrawTarget) {
|
||||
mLoanedDrawTarget->SetTransform(mLoanedTransform);
|
||||
if (mSetTransform) {
|
||||
mLoanedDrawTarget->SetTransform(mLoanedTransform);
|
||||
}
|
||||
mLoanedDrawTarget = nullptr;
|
||||
}
|
||||
aReturned = nullptr;
|
||||
|
@ -743,7 +747,8 @@ RotatedContentBuffer::BeginPaint(PaintedLayer* aLayer,
|
|||
|
||||
RefPtr<CapturedPaintState>
|
||||
RotatedContentBuffer::BorrowDrawTargetForRecording(PaintState& aPaintState,
|
||||
DrawIterator* aIter)
|
||||
DrawIterator* aIter,
|
||||
bool aSetTransform)
|
||||
{
|
||||
if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
|
||||
return nullptr;
|
||||
|
@ -752,7 +757,7 @@ RotatedContentBuffer::BorrowDrawTargetForRecording(PaintState& aPaintState,
|
|||
Matrix transform;
|
||||
DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),
|
||||
BUFFER_BOTH, aIter,
|
||||
false,
|
||||
aSetTransform,
|
||||
&transform);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
|
@ -831,21 +836,17 @@ RotatedContentBuffer::BorrowDrawTargetForPainting(PaintState& aPaintState,
|
|||
DrawIterator* aIter /* = nullptr */)
|
||||
{
|
||||
RefPtr<CapturedPaintState> capturedState =
|
||||
BorrowDrawTargetForRecording(aPaintState, aIter);
|
||||
BorrowDrawTargetForRecording(aPaintState, aIter, true);
|
||||
|
||||
if (!capturedState) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// BorrowDrawTargetForRecording doesn't apply the transform, so we have to.
|
||||
RefPtr<DrawTarget> target = capturedState->mTarget;
|
||||
target->SetTransform(capturedState->mTargetTransform);
|
||||
|
||||
if (!RotatedContentBuffer::PrepareDrawTargetForPainting(capturedState)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return target;
|
||||
return capturedState->mTarget;
|
||||
}
|
||||
|
||||
already_AddRefed<SourceSurface>
|
||||
|
|
|
@ -162,6 +162,11 @@ protected:
|
|||
// correctly restore state when it is returned.
|
||||
RefPtr<gfx::DrawTarget> mLoanedDrawTarget;
|
||||
gfx::Matrix mLoanedTransform;
|
||||
|
||||
// This flag denotes whether or not a transform was already applied
|
||||
// to mLoanedDrawTarget and thus needs to be reset to mLoanedTransform
|
||||
// upon returning the drawtarget.
|
||||
bool mSetTransform;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -298,12 +303,13 @@ public:
|
|||
DrawIterator* aIter = nullptr);
|
||||
|
||||
/**
|
||||
* Borrow a draw target for recording. The aOutTransform is not applied
|
||||
* to the returned DrawTarget, BUT it is required to be painting in the right
|
||||
* location whenever drawing does happen.
|
||||
* Borrow a draw target for recording. The required transform for correct painting
|
||||
* is not applied to the returned DrawTarget by default, BUT it is
|
||||
* required to be whenever drawing does happen.
|
||||
*/
|
||||
RefPtr<CapturedPaintState> BorrowDrawTargetForRecording(PaintState& aPaintState,
|
||||
DrawIterator* aIter);
|
||||
DrawIterator* aIter,
|
||||
bool aSetTransform = false);
|
||||
|
||||
nsIntRegion ExpandDrawRegion(PaintState& aPaintState,
|
||||
DrawIterator* aIter,
|
||||
|
@ -385,6 +391,7 @@ protected:
|
|||
* ReturnDrawTarget will by default restore the transform on the draw target.
|
||||
* But it is the callers responsibility to restore the clip.
|
||||
* The caller should flush the draw target, if necessary.
|
||||
* If aSetTransform is false, the required transform will be set in aOutTransform.
|
||||
*/
|
||||
gfx::DrawTarget*
|
||||
BorrowDrawTargetForQuadrantUpdate(const gfx::IntRect& aBounds,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCollation.h"
|
||||
#include "mozilla/intl/LocaleService.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsString.h"
|
||||
|
||||
|
@ -117,7 +118,20 @@ nsCollation::Initialize(const nsACString& locale)
|
|||
{
|
||||
NS_ENSURE_TRUE((!mInit), NS_ERROR_ALREADY_INITIALIZED);
|
||||
|
||||
mLocale = locale;
|
||||
// Check whether locale parameter is valid. If no, use application locale
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UCollator* collator = ucol_open(PromiseFlatCString(locale).get(), &status);
|
||||
if (U_SUCCESS(status)) {
|
||||
mLocale = locale;
|
||||
} else {
|
||||
status = U_ZERO_ERROR;
|
||||
mozilla::LocaleService::GetInstance()->GetAppLocaleAsLangTag(mLocale);
|
||||
collator = ucol_open(mLocale.get(), &status);
|
||||
if (NS_WARN_IF(U_FAILURE(status))) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
ucol_close(collator);
|
||||
|
||||
mInit = true;
|
||||
return NS_OK;
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/* -*- 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 "gtest/gtest.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsCollationCID.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsICollation.h"
|
||||
#include "nsString.h"
|
||||
|
||||
TEST(Collation, AllocateRowSortKey) {
|
||||
nsCOMPtr<nsICollationFactory> colFactory =
|
||||
do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID);
|
||||
ASSERT_TRUE(colFactory);
|
||||
|
||||
// Don't throw error even if locale name is invalid
|
||||
nsCOMPtr<nsICollation> collator;
|
||||
nsresult rv =
|
||||
colFactory->CreateCollationForLocale(NS_LITERAL_CSTRING("$languageName"),
|
||||
getter_AddRefs(collator));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
uint8_t* sortKey1 = nullptr;
|
||||
uint32_t sortKeyLen1 = 0;
|
||||
// Don't throw error even if locale name is invalid
|
||||
rv = collator->AllocateRawSortKey(nsICollation::kCollationStrengthDefault,
|
||||
NS_LITERAL_STRING("ABC"),
|
||||
&sortKey1, &sortKeyLen1);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
uint8_t* sortKey2 = nullptr;
|
||||
uint32_t sortKeyLen2 = 0;
|
||||
// Don't throw error even if locale name is invalid
|
||||
rv = collator->AllocateRawSortKey(nsICollation::kCollationStrengthDefault,
|
||||
NS_LITERAL_STRING("DEF"),
|
||||
&sortKey2, &sortKeyLen2);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
int32_t result;
|
||||
rv = collator->CompareRawSortKey(sortKey1, sortKeyLen1,
|
||||
sortKey2, sortKeyLen2, &result);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
ASSERT_TRUE(result < 0);
|
||||
|
||||
free(sortKey1);
|
||||
free(sortKey2);
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'TestCollation.cpp',
|
||||
'TestLocaleService.cpp',
|
||||
'TestLocaleServiceNegotiate.cpp',
|
||||
]
|
||||
|
|
|
@ -8,10 +8,12 @@
|
|||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include <guiddef.h>
|
||||
#include <objidl.h>
|
||||
#include <winnt.h>
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -56,9 +58,14 @@ struct DUALSTRINGARRAY
|
|||
|
||||
struct OBJREF_STANDARD
|
||||
{
|
||||
static size_t SizeOfFixedLenHeader()
|
||||
{
|
||||
return sizeof(mStd);
|
||||
}
|
||||
|
||||
size_t SizeOf() const
|
||||
{
|
||||
return sizeof(mStd) + mResAddr.SizeOf();
|
||||
return SizeOfFixedLenHeader() + mResAddr.SizeOf();
|
||||
}
|
||||
|
||||
STDOBJREF mStd;
|
||||
|
@ -67,9 +74,14 @@ struct OBJREF_STANDARD
|
|||
|
||||
struct OBJREF_HANDLER
|
||||
{
|
||||
static size_t SizeOfFixedLenHeader()
|
||||
{
|
||||
return sizeof(mStd) + sizeof(mClsid);
|
||||
}
|
||||
|
||||
size_t SizeOf() const
|
||||
{
|
||||
return sizeof(mStd) + sizeof(mClsid) + mResAddr.SizeOf();
|
||||
return SizeOfFixedLenHeader() + mResAddr.SizeOf();
|
||||
}
|
||||
|
||||
STDOBJREF mStd;
|
||||
|
@ -77,6 +89,19 @@ struct OBJREF_HANDLER
|
|||
DUALSTRINGARRAY mResAddr;
|
||||
};
|
||||
|
||||
struct OBJREF_CUSTOM
|
||||
{
|
||||
static size_t SizeOfFixedLenHeader()
|
||||
{
|
||||
return sizeof(mClsid) + sizeof(mCbExtension) + sizeof(mReserved);
|
||||
}
|
||||
|
||||
CLSID mClsid;
|
||||
uint32_t mCbExtension;
|
||||
uint32_t mReserved;
|
||||
uint8_t mPayload[1];
|
||||
};
|
||||
|
||||
enum OBJREF_FLAGS
|
||||
{
|
||||
OBJREF_TYPE_STANDARD = 0x00000001UL,
|
||||
|
@ -112,6 +137,7 @@ struct OBJREF
|
|||
union {
|
||||
OBJREF_STANDARD mObjRefStd;
|
||||
OBJREF_HANDLER mObjRefHandler;
|
||||
OBJREF_CUSTOM mObjRefCustom;
|
||||
// There are others but we're not supporting them here
|
||||
};
|
||||
};
|
||||
|
@ -183,7 +209,7 @@ StripHandlerFromOBJREF(NotNull<IStream*> aStream, const uint64_t aStartPos,
|
|||
seekTo.QuadPart = sizeof(STDOBJREF) + sizeof(CLSID);
|
||||
hr = aStream->Seek(seekTo, STREAM_SEEK_CUR, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t numEntries;
|
||||
|
@ -259,5 +285,108 @@ StripHandlerFromOBJREF(NotNull<IStream*> aStream, const uint64_t aStartPos,
|
|||
return SUCCEEDED(aStream->Seek(seekTo, STREAM_SEEK_CUR, nullptr));
|
||||
}
|
||||
|
||||
uint32_t
|
||||
GetOBJREFSize(NotNull<IStream*> aStream)
|
||||
{
|
||||
// Make a clone so that we don't manipulate aStream's seek pointer
|
||||
RefPtr<IStream> cloned;
|
||||
HRESULT hr = aStream->Clone(getter_AddRefs(cloned));
|
||||
if (FAILED(hr)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t accumulatedSize = 0;
|
||||
|
||||
ULONG bytesRead;
|
||||
|
||||
uint32_t signature;
|
||||
hr = cloned->Read(&signature, sizeof(signature), &bytesRead);
|
||||
if (FAILED(hr) || bytesRead != sizeof(signature) ||
|
||||
signature != OBJREF_SIGNATURE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
accumulatedSize += bytesRead;
|
||||
|
||||
uint32_t type;
|
||||
hr = cloned->Read(&type, sizeof(type), &bytesRead);
|
||||
if (FAILED(hr) || bytesRead != sizeof(type)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
accumulatedSize += bytesRead;
|
||||
|
||||
IID iid;
|
||||
hr = cloned->Read(&iid, sizeof(iid), &bytesRead);
|
||||
if (FAILED(hr) || bytesRead != sizeof(iid) || !IsValidGUID(iid)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
accumulatedSize += bytesRead;
|
||||
|
||||
LARGE_INTEGER seekTo;
|
||||
|
||||
if (type == OBJREF_TYPE_CUSTOM) {
|
||||
CLSID clsid;
|
||||
hr = cloned->Read(&clsid, sizeof(CLSID), &bytesRead);
|
||||
if (FAILED(hr) || bytesRead != sizeof(CLSID)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(clsid == CLSID_StdMarshal);
|
||||
if (clsid != CLSID_StdMarshal) {
|
||||
// We can only calulate the size if the payload is a standard OBJREF as
|
||||
// identified by clsid == CLSID_StdMarshal.
|
||||
return 0;
|
||||
}
|
||||
|
||||
accumulatedSize += bytesRead;
|
||||
|
||||
seekTo.QuadPart = sizeof(OBJREF_CUSTOM::mCbExtension) +
|
||||
sizeof(OBJREF_CUSTOM::mReserved);
|
||||
hr = cloned->Seek(seekTo, STREAM_SEEK_CUR, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
accumulatedSize += seekTo.LowPart;
|
||||
|
||||
uint32_t payloadLen = GetOBJREFSize(WrapNotNull(cloned.get()));
|
||||
if (!payloadLen) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
accumulatedSize += payloadLen;
|
||||
return accumulatedSize;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case OBJREF_TYPE_STANDARD:
|
||||
seekTo.QuadPart = OBJREF_STANDARD::SizeOfFixedLenHeader();
|
||||
break;
|
||||
case OBJREF_TYPE_HANDLER:
|
||||
seekTo.QuadPart = OBJREF_HANDLER::SizeOfFixedLenHeader();
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
hr = cloned->Seek(seekTo, STREAM_SEEK_CUR, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
accumulatedSize += seekTo.LowPart;
|
||||
|
||||
uint16_t numEntries;
|
||||
hr = cloned->Read(&numEntries, sizeof(numEntries), &bytesRead);
|
||||
if (FAILED(hr) || bytesRead != sizeof(numEntries)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
accumulatedSize += DUALSTRINGARRAY::SizeFromNumEntries(numEntries);
|
||||
return accumulatedSize;
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -29,6 +29,16 @@ StripHandlerFromOBJREF(NotNull<IStream*> aStream,
|
|||
const uint64_t aStart,
|
||||
const uint64_t aEnd);
|
||||
|
||||
/**
|
||||
* Given a buffer containing a serialized proxy to an interface, this function
|
||||
* returns the length of the serialized data.
|
||||
* @param aStream IStream containing a serialized proxy. The stream pointer
|
||||
* must be positioned at the beginning of the OBJREF.
|
||||
* @return The size of the serialized proxy, or 0 on error.
|
||||
*/
|
||||
uint32_t
|
||||
GetOBJREFSize(NotNull<IStream*> aStream);
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "mozilla/mscom/EnsureMTA.h"
|
||||
#include "mozilla/mscom/ProxyStream.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
#include "InterfaceRegistrationAnnotator.h"
|
||||
|
@ -34,8 +33,7 @@ ProxyStream::ProxyStream()
|
|||
// reconstructing the stream from a buffer anyway.
|
||||
ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf,
|
||||
const int aInitBufSize)
|
||||
: mStream(InitStream(aInitBuf, static_cast<const UINT>(aInitBufSize)))
|
||||
, mGlobalLockedBuf(nullptr)
|
||||
: mGlobalLockedBuf(nullptr)
|
||||
, mHGlobal(nullptr)
|
||||
, mBufSize(aInitBufSize)
|
||||
{
|
||||
|
@ -43,6 +41,16 @@ ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf,
|
|||
NS_NAMED_LITERAL_CSTRING(kCrashReportKey, "ProxyStreamUnmarshalStatus");
|
||||
#endif
|
||||
|
||||
HRESULT createStreamResult = CreateStream(aInitBuf, aInitBufSize,
|
||||
getter_AddRefs(mStream));
|
||||
if (FAILED(createStreamResult)) {
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
nsPrintfCString hrAsStr("0x%08X", createStreamResult);
|
||||
CrashReporter::AnnotateCrashReport(kCrashReportKey, hrAsStr);
|
||||
#endif // defined(MOZ_CRASHREPORTER)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aInitBufSize) {
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
CrashReporter::AnnotateCrashReport(kCrashReportKey,
|
||||
|
@ -98,67 +106,6 @@ ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf,
|
|||
#endif // defined(MOZ_CRASHREPORTER)
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<IStream>
|
||||
ProxyStream::InitStream(const BYTE* aInitBuf, const UINT aInitBufSize)
|
||||
{
|
||||
if (!aInitBuf || !aInitBufSize) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
RefPtr<IStream> stream;
|
||||
|
||||
if (IsWin8OrLater()) {
|
||||
// This function is not safe for us to use until Windows 8
|
||||
stream = already_AddRefed<IStream>(::SHCreateMemStream(aInitBuf, aInitBufSize));
|
||||
if (!stream) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
HGLOBAL hglobal = ::GlobalAlloc(GMEM_MOVEABLE, aInitBufSize);
|
||||
if (!hglobal) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// stream takes ownership of hglobal if this call is successful
|
||||
hr = ::CreateStreamOnHGlobal(hglobal, TRUE, getter_AddRefs(stream));
|
||||
if (FAILED(hr)) {
|
||||
::GlobalFree(hglobal);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The default stream size is derived from ::GlobalSize(hglobal), which due
|
||||
// to rounding may be larger than aInitBufSize. We forcibly set the correct
|
||||
// stream size here.
|
||||
ULARGE_INTEGER streamSize;
|
||||
streamSize.QuadPart = aInitBufSize;
|
||||
hr = stream->SetSize(streamSize);
|
||||
if (FAILED(hr)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* streamBuf = ::GlobalLock(hglobal);
|
||||
if (!streamBuf) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memcpy(streamBuf, aInitBuf, aInitBufSize);
|
||||
|
||||
::GlobalUnlock(hglobal);
|
||||
}
|
||||
|
||||
// Ensure that the stream is rewound
|
||||
LARGE_INTEGER streamOffset;
|
||||
streamOffset.QuadPart = 0;
|
||||
hr = stream->Seek(streamOffset, STREAM_SEEK_SET, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return stream.forget();
|
||||
}
|
||||
|
||||
ProxyStream::ProxyStream(ProxyStream&& aOther)
|
||||
: mGlobalLockedBuf(nullptr)
|
||||
, mHGlobal(nullptr)
|
||||
|
|
|
@ -46,10 +46,6 @@ public:
|
|||
return this == &aOther;
|
||||
}
|
||||
|
||||
private:
|
||||
static already_AddRefed<IStream> InitStream(const BYTE* aInitBuf,
|
||||
const UINT aInitBufSize);
|
||||
|
||||
private:
|
||||
RefPtr<IStream> mStream;
|
||||
BYTE* mGlobalLockedBuf;
|
||||
|
|
|
@ -11,11 +11,14 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#include "mozilla/mscom/Objref.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
|
||||
#include <objbase.h>
|
||||
#include <objidl.h>
|
||||
#include <shlwapi.h>
|
||||
#include <winnt.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
@ -98,6 +101,126 @@ GetContainingModuleHandle()
|
|||
return reinterpret_cast<uintptr_t>(thisModule);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CreateStream(const uint8_t* aInitBuf, const uint32_t aInitBufSize,
|
||||
IStream** aOutStream)
|
||||
{
|
||||
if (!aInitBufSize || !aOutStream) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
*aOutStream = nullptr;
|
||||
|
||||
HRESULT hr;
|
||||
RefPtr<IStream> stream;
|
||||
|
||||
if (IsWin8OrLater()) {
|
||||
// SHCreateMemStream is not safe for us to use until Windows 8. On older
|
||||
// versions of Windows it is not thread-safe and it creates IStreams that do
|
||||
// not support the full IStream API.
|
||||
|
||||
// If aInitBuf is null then initSize must be 0.
|
||||
UINT initSize = aInitBuf ? aInitBufSize : 0;
|
||||
stream = already_AddRefed<IStream>(::SHCreateMemStream(aInitBuf, initSize));
|
||||
if (!stream) {
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
if (!aInitBuf) {
|
||||
// Now we'll set the required size
|
||||
ULARGE_INTEGER newSize;
|
||||
newSize.QuadPart = aInitBufSize;
|
||||
hr = stream->SetSize(newSize);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
HGLOBAL hglobal = ::GlobalAlloc(GMEM_MOVEABLE, aInitBufSize);
|
||||
if (!hglobal) {
|
||||
return HRESULT_FROM_WIN32(::GetLastError());
|
||||
}
|
||||
|
||||
// stream takes ownership of hglobal if this call is successful
|
||||
hr = ::CreateStreamOnHGlobal(hglobal, TRUE, getter_AddRefs(stream));
|
||||
if (FAILED(hr)) {
|
||||
::GlobalFree(hglobal);
|
||||
return hr;
|
||||
}
|
||||
|
||||
// The default stream size is derived from ::GlobalSize(hglobal), which due
|
||||
// to rounding may be larger than aInitBufSize. We forcibly set the correct
|
||||
// stream size here.
|
||||
ULARGE_INTEGER streamSize;
|
||||
streamSize.QuadPart = aInitBufSize;
|
||||
hr = stream->SetSize(streamSize);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (aInitBuf) {
|
||||
ULONG bytesWritten;
|
||||
hr = stream->Write(aInitBuf, aInitBufSize, &bytesWritten);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (bytesWritten != aInitBufSize) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the stream is rewound
|
||||
LARGE_INTEGER streamOffset;
|
||||
streamOffset.QuadPart = 0LL;
|
||||
hr = stream->Seek(streamOffset, STREAM_SEEK_SET, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
stream.forget(aOutStream);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CopySerializedProxy(IStream* aInStream, IStream** aOutStream)
|
||||
{
|
||||
if (!aInStream || !aOutStream) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
*aOutStream = nullptr;
|
||||
|
||||
uint32_t desiredStreamSize = GetOBJREFSize(WrapNotNull(aInStream));
|
||||
if (!desiredStreamSize) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
RefPtr<IStream> stream;
|
||||
HRESULT hr = CreateStream(nullptr, desiredStreamSize, getter_AddRefs(stream));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
ULARGE_INTEGER numBytesToCopy;
|
||||
numBytesToCopy.QuadPart = desiredStreamSize;
|
||||
hr = aInStream->CopyTo(stream, numBytesToCopy, nullptr, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
LARGE_INTEGER seekTo;
|
||||
seekTo.QuadPart = 0LL;
|
||||
hr = stream->Seek(seekTo, STREAM_SEEK_SET, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
stream.forget(aOutStream);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#if defined(MOZILLA_INTERNAL_API)
|
||||
|
||||
void
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <guiddef.h>
|
||||
|
||||
struct IStream;
|
||||
struct IUnknown;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -23,6 +24,28 @@ bool IsProxy(IUnknown* aUnknown);
|
|||
bool IsValidGUID(REFGUID aCheckGuid);
|
||||
uintptr_t GetContainingModuleHandle();
|
||||
|
||||
/**
|
||||
* Given a buffer, create a new IStream object.
|
||||
* @param aBuf Buffer containing data to initialize the stream. This parameter
|
||||
* may be nullptr, causing the stream to be created with aBufLen
|
||||
* bytes of uninitialized data.
|
||||
* @param aBufLen Length of data in aBuf, or desired stream size if aBuf is
|
||||
* nullptr.
|
||||
* @param aOutStream Outparam to receive the newly created stream.
|
||||
* @return HRESULT error code.
|
||||
*/
|
||||
uint32_t CreateStream(const uint8_t* aBuf, const uint32_t aBufLen,
|
||||
IStream** aOutStream);
|
||||
|
||||
/**
|
||||
* Creates a deep copy of a proxy contained in a stream.
|
||||
* @param aInStream Stream containing the proxy to copy. Its seek pointer must
|
||||
* be positioned to point at the beginning of the proxy data.
|
||||
* @param aOutStream Outparam to receive the newly created stream.
|
||||
* @return HRESULT error code.
|
||||
*/
|
||||
uint32_t CopySerializedProxy(IStream* aInStream, IStream** aOutStream);
|
||||
|
||||
#if defined(MOZILLA_INTERNAL_API)
|
||||
void GUIDToString(REFGUID aGuid, nsAString& aOutString);
|
||||
#endif // defined(MOZILLA_INTERNAL_API)
|
||||
|
|
|
@ -524,12 +524,61 @@ ModuleNamespaceObject::ProxyHandler::getOwnPropertyDescriptor(JSContext* cx, Han
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ValidatePropertyDescriptor(JSContext* cx, Handle<PropertyDescriptor> desc,
|
||||
bool expectedWritable, bool expectedEnumerable,
|
||||
bool expectedConfigurable, HandleValue expectedValue,
|
||||
ObjectOpResult& result)
|
||||
{
|
||||
if (desc.isAccessorDescriptor())
|
||||
return result.fail(JSMSG_CANT_REDEFINE_PROP);
|
||||
|
||||
if (desc.hasWritable() && desc.writable() != expectedWritable)
|
||||
return result.fail(JSMSG_CANT_REDEFINE_PROP);
|
||||
|
||||
if (desc.hasEnumerable() && desc.enumerable() != expectedEnumerable)
|
||||
return result.fail(JSMSG_CANT_REDEFINE_PROP);
|
||||
|
||||
if (desc.hasConfigurable() && desc.configurable() != expectedConfigurable)
|
||||
return result.fail(JSMSG_CANT_REDEFINE_PROP);
|
||||
|
||||
if (desc.hasValue()) {
|
||||
bool same;
|
||||
if (!SameValue(cx, desc.value(), expectedValue, &same))
|
||||
return false;
|
||||
if (!same)
|
||||
return result.fail(JSMSG_CANT_REDEFINE_PROP);
|
||||
}
|
||||
|
||||
return result.succeed();
|
||||
}
|
||||
|
||||
bool
|
||||
ModuleNamespaceObject::ProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
|
||||
Handle<PropertyDescriptor> desc,
|
||||
ObjectOpResult& result) const
|
||||
{
|
||||
return result.failReadOnly();
|
||||
if (JSID_IS_SYMBOL(id)) {
|
||||
if (JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().toStringTag) {
|
||||
RootedValue value(cx, StringValue(cx->names().Module));
|
||||
return ValidatePropertyDescriptor(cx, desc, false, false, false, value, result);
|
||||
}
|
||||
return result.fail(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE);
|
||||
}
|
||||
|
||||
const IndirectBindingMap& bindings = proxy->as<ModuleNamespaceObject>().bindings();
|
||||
ModuleEnvironmentObject* env;
|
||||
Shape* shape;
|
||||
if (!bindings.lookup(id, &env, &shape))
|
||||
return result.fail(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE);
|
||||
|
||||
RootedValue value(cx, env->getSlot(shape->slot()));
|
||||
if (value.isMagic(JS_UNINITIALIZED_LEXICAL)) {
|
||||
ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, id);
|
||||
return false;
|
||||
}
|
||||
|
||||
return ValidatePropertyDescriptor(cx, desc, true, true, false, value, result);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -1323,7 +1323,6 @@ PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
|
|||
|
||||
// Steps 3-10.
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
bool needsWrapping = false;
|
||||
|
||||
// If the constructor is called via an Xray wrapper, then the newTarget
|
||||
// hasn't been unwrapped. We want that because, while the actual instance
|
||||
|
@ -1352,6 +1351,9 @@ PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
|
|||
// just end up with a rejected Promise. Really, we want to chain the two
|
||||
// Promises, with the unprivileged one resolved with the resolution of the
|
||||
// privileged one.
|
||||
|
||||
bool needsWrapping = false;
|
||||
RootedObject proto(cx);
|
||||
if (IsWrapper(newTarget)) {
|
||||
JSObject* unwrappedNewTarget = CheckedUnwrap(newTarget);
|
||||
MOZ_ASSERT(unwrappedNewTarget);
|
||||
|
@ -1367,15 +1369,15 @@ PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
|
|||
// Promise subclasses don't get the special Xray treatment, so
|
||||
// we only need to do the complex wrapping and unwrapping scheme
|
||||
// described above for instances of Promise itself.
|
||||
if (newTarget == promiseCtor)
|
||||
if (newTarget == promiseCtor) {
|
||||
needsWrapping = true;
|
||||
if (!GetBuiltinPrototype(cx, JSProto_Promise, &proto))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RootedObject proto(cx);
|
||||
if (needsWrapping) {
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
return false;
|
||||
if (!cx->compartment()->wrap(cx, &proto))
|
||||
return false;
|
||||
} else {
|
||||
|
|
|
@ -2314,8 +2314,7 @@ Parser<FullParseHandler, char16_t>::moduleBody(ModuleSharedContext* modulesc)
|
|||
if (!AtomToPrintableString(context, name, &str))
|
||||
return null();
|
||||
|
||||
JS_ReportErrorNumberLatin1(context, GetErrorMessage, nullptr,
|
||||
JSMSG_MISSING_EXPORT, str.ptr());
|
||||
errorAt(TokenStream::NoOffset, JSMSG_MISSING_EXPORT, str.ptr());
|
||||
return null();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
if (helperThreadCount() == 0)
|
||||
quit();
|
||||
|
||||
load(libdir + "asserts.js")
|
||||
|
||||
// Don't assert.
|
||||
offThreadCompileModule("export { x };");
|
||||
assertThrowsInstanceOf(() => finishOffThreadModule(), SyntaxError);
|
|
@ -1487,7 +1487,7 @@ void
|
|||
MacroAssembler::Push(Register reg)
|
||||
{
|
||||
ma_push(reg);
|
||||
adjustFrame(sizeof(intptr_t));
|
||||
adjustFrame(int32_t(sizeof(intptr_t)));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1495,7 +1495,7 @@ MacroAssembler::Push(const Imm32 imm)
|
|||
{
|
||||
ma_li(ScratchRegister, imm);
|
||||
ma_push(ScratchRegister);
|
||||
adjustFrame(sizeof(intptr_t));
|
||||
adjustFrame(int32_t(sizeof(intptr_t)));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1503,7 +1503,7 @@ MacroAssembler::Push(const ImmWord imm)
|
|||
{
|
||||
ma_li(ScratchRegister, imm);
|
||||
ma_push(ScratchRegister);
|
||||
adjustFrame(sizeof(intptr_t));
|
||||
adjustFrame(int32_t(sizeof(intptr_t)));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1517,28 +1517,28 @@ MacroAssembler::Push(const ImmGCPtr ptr)
|
|||
{
|
||||
ma_li(ScratchRegister, ptr);
|
||||
ma_push(ScratchRegister);
|
||||
adjustFrame(sizeof(intptr_t));
|
||||
adjustFrame(int32_t(sizeof(intptr_t)));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::Push(FloatRegister f)
|
||||
{
|
||||
ma_push(f);
|
||||
adjustFrame(sizeof(double));
|
||||
adjustFrame(int32_t(sizeof(double)));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::Pop(Register reg)
|
||||
{
|
||||
ma_pop(reg);
|
||||
adjustFrame(-sizeof(intptr_t));
|
||||
adjustFrame(-int32_t(sizeof(intptr_t)));
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssembler::Pop(FloatRegister f)
|
||||
{
|
||||
ma_pop(f);
|
||||
adjustFrame(-sizeof(double));
|
||||
adjustFrame(-int32_t(sizeof(double)));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#include <float.h>
|
||||
|
||||
#include "jit/AtomicOperations.h"
|
||||
#include "jit/mips32/Assembler-mips32.h"
|
||||
#include "vm/Runtime.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
|
@ -380,6 +381,7 @@ SimInstruction::instructionType() const
|
|||
case ff_movz:
|
||||
case ff_movn:
|
||||
case ff_movci:
|
||||
case ff_sync:
|
||||
return kRegisterType;
|
||||
default:
|
||||
return kUnsupported;
|
||||
|
@ -448,6 +450,8 @@ SimInstruction::instructionType() const
|
|||
case op_ldc1:
|
||||
case op_swc1:
|
||||
case op_sdc1:
|
||||
case op_ll:
|
||||
case op_sc:
|
||||
return kImmediateType;
|
||||
// 26 bits immediate type instructions. e.g.: j imm26.
|
||||
case op_j:
|
||||
|
@ -1143,10 +1147,10 @@ GetCachePageLocked(SimulatorProcess::ICacheMap& i_cache, void* page)
|
|||
SimulatorProcess::ICacheMap::AddPtr p = i_cache.lookupForAdd(page);
|
||||
if (p)
|
||||
return p->value();
|
||||
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
CachePage* new_page = js_new<CachePage>();
|
||||
if (!i_cache.add(p, page, new_page))
|
||||
return nullptr;
|
||||
if (!new_page || !i_cache.add(p, page, new_page))
|
||||
oomUnsafe.crash("Simulator CachePage");
|
||||
return new_page;
|
||||
}
|
||||
|
||||
|
@ -1275,6 +1279,9 @@ Simulator::Simulator()
|
|||
FPUregisters_[i] = 0;
|
||||
}
|
||||
FCSR_ = 0;
|
||||
LLBit_ = false;
|
||||
LLAddr_ = 0;
|
||||
lastLLValue_ = 0;
|
||||
|
||||
// The ra and pc are initialized to a known bad value that will cause an
|
||||
// access violation if the simulator ever tries to execute it.
|
||||
|
@ -1350,11 +1357,10 @@ class Redirection
|
|||
}
|
||||
}
|
||||
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
Redirection* redir = (Redirection*)js_malloc(sizeof(Redirection));
|
||||
if (!redir) {
|
||||
MOZ_ReportAssertionFailure("[unhandlable oom] Simulator redirection",
|
||||
__FILE__, __LINE__);
|
||||
MOZ_CRASH();
|
||||
oomUnsafe.crash("Simulator redirection");
|
||||
}
|
||||
new(redir) Redirection(nativeFunction, type);
|
||||
return redir;
|
||||
|
@ -1445,13 +1451,6 @@ Simulator::setFpuRegisterFloat(int fpureg, float value)
|
|||
*mozilla::BitwiseCast<float*>(&FPUregisters_[fpureg]) = value;
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::setFpuRegisterFloat(int fpureg, int64_t value)
|
||||
{
|
||||
setFpuRegister(fpureg, value & 0xffffffff);
|
||||
setFpuRegister(fpureg + 1, value >> 32);
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::setFpuRegisterDouble(int fpureg, double value)
|
||||
{
|
||||
|
@ -1460,13 +1459,6 @@ Simulator::setFpuRegisterDouble(int fpureg, double value)
|
|||
*mozilla::BitwiseCast<double*>(&FPUregisters_[fpureg]) = value;
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::setFpuRegisterDouble(int fpureg, int64_t value)
|
||||
{
|
||||
setFpuRegister(fpureg, value & 0xffffffff);
|
||||
setFpuRegister(fpureg + 1, value >> 32);
|
||||
}
|
||||
|
||||
// Get the register from the architecture state. This function does handle
|
||||
// the special case of accessing the PC register.
|
||||
int32_t
|
||||
|
@ -1629,33 +1621,100 @@ Simulator::get_pc() const
|
|||
void
|
||||
Simulator::startInterrupt(WasmActivation* activation)
|
||||
{
|
||||
MOZ_CRASH("NIY");
|
||||
JS::ProfilingFrameIterator::RegisterState state;
|
||||
state.pc = (void*) get_pc();
|
||||
state.fp = (void*) getRegister(fp);
|
||||
state.sp = (void*) getRegister(sp);
|
||||
state.lr = (void*) getRegister(ra);
|
||||
activation->startInterrupt(state);
|
||||
}
|
||||
|
||||
// The signal handler only redirects the PC to the interrupt stub when the PC is
|
||||
// in function code. However, this guard is racy for the simulator since the
|
||||
// signal handler samples PC in the middle of simulating an instruction and thus
|
||||
// the current PC may have advanced once since the signal handler's guard. So we
|
||||
// re-check here.
|
||||
void
|
||||
Simulator::handleWasmInterrupt()
|
||||
{
|
||||
MOZ_CRASH("NIY");
|
||||
void* pc = (void*)get_pc();
|
||||
void* fp = (void*)getRegister(Register::fp);
|
||||
|
||||
WasmActivation* activation = wasm::ActivationIfInnermost(TlsContext.get());
|
||||
const wasm::CodeSegment* segment;
|
||||
const wasm::Code* code = activation->compartment()->wasm.lookupCode(pc, &segment);
|
||||
if (!code || !segment->containsFunctionPC(pc))
|
||||
return;
|
||||
|
||||
// fp can be null during the prologue/epilogue of the entry function.
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
startInterrupt(activation);
|
||||
set_pc(int32_t(segment->interruptCode()));
|
||||
}
|
||||
|
||||
// The MIPS cannot do unaligned reads and writes. On some MIPS platforms an
|
||||
// interrupt is caused. On others it does a funky rotation thing. For now we
|
||||
// simply disallow unaligned reads, but at some point we may want to move to
|
||||
// emulating the rotate behaviour. Note that simulator runs have the runtime
|
||||
// system running directly on the host system and only generated code is
|
||||
// executed in the simulator. Since the host is typically IA32 we will not
|
||||
// get the correct MIPS-like behaviour on unaligned accesses.
|
||||
|
||||
// WebAssembly memories contain an extra region of guard pages (see
|
||||
// WasmArrayRawBuffer comment). The guard pages catch out-of-bounds accesses
|
||||
// using a signal handler that redirects PC to a stub that safely reports an
|
||||
// error. However, if the handler is hit by the simulator, the PC is in C++ code
|
||||
// and cannot be redirected. Therefore, we must avoid hitting the handler by
|
||||
// redirecting in the simulator before the real handler would have been hit.
|
||||
bool
|
||||
Simulator::handleWasmFault(int32_t addr, unsigned numBytes)
|
||||
{
|
||||
JSContext* cx = TlsContext.get();
|
||||
WasmActivation* act = wasm::ActivationIfInnermost(cx);
|
||||
if (!act)
|
||||
return false;
|
||||
|
||||
void* pc = reinterpret_cast<void*>(get_pc());
|
||||
uint8_t* fp = reinterpret_cast<uint8_t*>(getRegister(Register::fp));
|
||||
|
||||
// Cache the wasm::Code to avoid lookup on every load/store.
|
||||
if (!wasm_code_ || !wasm_code_->containsCodePC(pc))
|
||||
wasm_code_ = act->compartment()->wasm.lookupCode(pc);
|
||||
if (!wasm_code_)
|
||||
return false;
|
||||
|
||||
wasm::Instance* instance = wasm::LookupFaultingInstance(*wasm_code_, pc, fp);
|
||||
if (!instance || !instance->memoryAccessInGuardRegion((uint8_t*)addr, numBytes))
|
||||
return false;
|
||||
|
||||
LLBit_ = false;
|
||||
|
||||
const wasm::CodeSegment* segment;
|
||||
const wasm::MemoryAccess* memoryAccess = instance->code().lookupMemoryAccess(pc, &segment);
|
||||
if (!memoryAccess) {
|
||||
startInterrupt(act);
|
||||
if (!instance->code().containsCodePC(pc, &segment))
|
||||
MOZ_CRASH("Cannot map PC to trap handler");
|
||||
set_pc(int32_t(segment->outOfBoundsCode()));
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(memoryAccess->hasTrapOutOfLineCode());
|
||||
set_pc(int32_t(memoryAccess->trapOutOfLineCode(segment->base())));
|
||||
return true;
|
||||
}
|
||||
|
||||
// MIPS memory instructions (except lwl/r and swl/r) trap on unaligned memory
|
||||
// access enabling the OS to handle them via trap-and-emulate.
|
||||
// Note that simulator runs have the runtime system running directly on the host
|
||||
// system and only generated code is executed in the simulator.
|
||||
// Since the host is typically IA32 it will not trap on unaligned memory access.
|
||||
// We assume that that executing correct generated code will not produce unaligned
|
||||
// memory access, so we explicitly check for address alignment and trap.
|
||||
// Note that trapping does not occur when executing wasm code, which requires that
|
||||
// unaligned memory access provides correct result.
|
||||
int
|
||||
Simulator::readW(uint32_t addr, SimInstruction* instr)
|
||||
{
|
||||
if (addr < 0x400) {
|
||||
// This has to be a NULL-dereference, drop into debugger.
|
||||
printf("Memory read from bad address: 0x%08x, pc=0x%08" PRIxPTR "\n",
|
||||
addr, reinterpret_cast<intptr_t>(instr));
|
||||
MOZ_CRASH();
|
||||
}
|
||||
if ((addr & kPointerAlignmentMask) == 0) {
|
||||
if (handleWasmFault(addr, 4))
|
||||
return -1;
|
||||
|
||||
if ((addr & kPointerAlignmentMask) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
|
||||
intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
|
||||
return *ptr;
|
||||
}
|
||||
|
@ -1669,14 +1728,12 @@ Simulator::readW(uint32_t addr, SimInstruction* instr)
|
|||
void
|
||||
Simulator::writeW(uint32_t addr, int value, SimInstruction* instr)
|
||||
{
|
||||
if (addr < 0x400) {
|
||||
// This has to be a NULL-dereference, drop into debugger.
|
||||
printf("Memory write to bad address: 0x%08x, pc=0x%08" PRIxPTR "\n",
|
||||
addr, reinterpret_cast<intptr_t>(instr));
|
||||
MOZ_CRASH();
|
||||
}
|
||||
if ((addr & kPointerAlignmentMask) == 0) {
|
||||
if (handleWasmFault(addr, 4))
|
||||
return;
|
||||
|
||||
if ((addr & kPointerAlignmentMask) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
|
||||
intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
|
||||
LLBit_ = false;
|
||||
*ptr = value;
|
||||
return;
|
||||
}
|
||||
|
@ -1689,7 +1746,10 @@ Simulator::writeW(uint32_t addr, int value, SimInstruction* instr)
|
|||
double
|
||||
Simulator::readD(uint32_t addr, SimInstruction* instr)
|
||||
{
|
||||
if ((addr & kDoubleAlignmentMask) == 0) {
|
||||
if (handleWasmFault(addr, 8))
|
||||
return NAN;
|
||||
|
||||
if ((addr & kDoubleAlignmentMask) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
|
||||
double* ptr = reinterpret_cast<double*>(addr);
|
||||
return *ptr;
|
||||
}
|
||||
|
@ -1703,8 +1763,12 @@ Simulator::readD(uint32_t addr, SimInstruction* instr)
|
|||
void
|
||||
Simulator::writeD(uint32_t addr, double value, SimInstruction* instr)
|
||||
{
|
||||
if ((addr & kDoubleAlignmentMask) == 0) {
|
||||
if (handleWasmFault(addr, 8))
|
||||
return;
|
||||
|
||||
if ((addr & kDoubleAlignmentMask) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
|
||||
double* ptr = reinterpret_cast<double*>(addr);
|
||||
LLBit_ = false;
|
||||
*ptr = value;
|
||||
return;
|
||||
}
|
||||
|
@ -1717,7 +1781,10 @@ Simulator::writeD(uint32_t addr, double value, SimInstruction* instr)
|
|||
uint16_t
|
||||
Simulator::readHU(uint32_t addr, SimInstruction* instr)
|
||||
{
|
||||
if ((addr & 1) == 0) {
|
||||
if (handleWasmFault(addr, 2))
|
||||
return 0xffff;
|
||||
|
||||
if ((addr & 1) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
|
||||
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
|
||||
return *ptr;
|
||||
}
|
||||
|
@ -1731,7 +1798,10 @@ Simulator::readHU(uint32_t addr, SimInstruction* instr)
|
|||
int16_t
|
||||
Simulator::readH(uint32_t addr, SimInstruction* instr)
|
||||
{
|
||||
if ((addr & 1) == 0) {
|
||||
if (handleWasmFault(addr, 2))
|
||||
return -1;
|
||||
|
||||
if ((addr & 1) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
|
||||
int16_t* ptr = reinterpret_cast<int16_t*>(addr);
|
||||
return *ptr;
|
||||
}
|
||||
|
@ -1745,8 +1815,12 @@ Simulator::readH(uint32_t addr, SimInstruction* instr)
|
|||
void
|
||||
Simulator::writeH(uint32_t addr, uint16_t value, SimInstruction* instr)
|
||||
{
|
||||
if ((addr & 1) == 0) {
|
||||
if (handleWasmFault(addr, 2))
|
||||
return;
|
||||
|
||||
if ((addr & 1) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
|
||||
uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
|
||||
LLBit_ = false;
|
||||
*ptr = value;
|
||||
return;
|
||||
}
|
||||
|
@ -1759,8 +1833,12 @@ Simulator::writeH(uint32_t addr, uint16_t value, SimInstruction* instr)
|
|||
void
|
||||
Simulator::writeH(uint32_t addr, int16_t value, SimInstruction* instr)
|
||||
{
|
||||
if ((addr & 1) == 0) {
|
||||
if (handleWasmFault(addr, 2))
|
||||
return;
|
||||
|
||||
if ((addr & 1) == 0 || wasm::InCompiledCode(reinterpret_cast<void *>(get_pc()))) {
|
||||
int16_t* ptr = reinterpret_cast<int16_t*>(addr);
|
||||
LLBit_ = false;
|
||||
*ptr = value;
|
||||
return;
|
||||
}
|
||||
|
@ -1773,6 +1851,9 @@ Simulator::writeH(uint32_t addr, int16_t value, SimInstruction* instr)
|
|||
uint32_t
|
||||
Simulator::readBU(uint32_t addr)
|
||||
{
|
||||
if (handleWasmFault(addr, 1))
|
||||
return 0xff;
|
||||
|
||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
|
||||
return *ptr;
|
||||
}
|
||||
|
@ -1780,6 +1861,9 @@ Simulator::readBU(uint32_t addr)
|
|||
int32_t
|
||||
Simulator::readB(uint32_t addr)
|
||||
{
|
||||
if (handleWasmFault(addr, 1))
|
||||
return -1;
|
||||
|
||||
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
|
||||
return *ptr;
|
||||
}
|
||||
|
@ -1787,17 +1871,75 @@ Simulator::readB(uint32_t addr)
|
|||
void
|
||||
Simulator::writeB(uint32_t addr, uint8_t value)
|
||||
{
|
||||
if (handleWasmFault(addr, 1))
|
||||
return;
|
||||
|
||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
|
||||
LLBit_ = false;
|
||||
*ptr = value;
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::writeB(uint32_t addr, int8_t value)
|
||||
{
|
||||
if (handleWasmFault(addr, 1))
|
||||
return;
|
||||
|
||||
int8_t* ptr = reinterpret_cast<int8_t*>(addr);
|
||||
LLBit_ = false;
|
||||
*ptr = value;
|
||||
}
|
||||
|
||||
int
|
||||
Simulator::loadLinkedW(uint32_t addr, SimInstruction* instr)
|
||||
{
|
||||
if ((addr & kPointerAlignmentMask) == 0) {
|
||||
volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(addr);
|
||||
int32_t value = *ptr;
|
||||
lastLLValue_ = value;
|
||||
LLAddr_ = addr;
|
||||
// Note that any memory write or "external" interrupt should reset this value to false.
|
||||
LLBit_ = true;
|
||||
return value;
|
||||
}
|
||||
printf("Unaligned read at 0x%08x, pc=0x%08" PRIxPTR "\n",
|
||||
addr,
|
||||
reinterpret_cast<intptr_t>(instr));
|
||||
MOZ_CRASH();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Simulator::storeConditionalW(uint32_t addr, int value, SimInstruction* instr)
|
||||
{
|
||||
// Correct behavior in this case, as defined by architecture, is to just return 0,
|
||||
// but there is no point at allowing that. It is certainly an indicator of a bug.
|
||||
if (addr != LLAddr_) {
|
||||
printf("SC to bad address: 0x%08x, pc=0x%08" PRIxPTR ", expected: 0x%08x\n",
|
||||
addr, reinterpret_cast<intptr_t>(instr), LLAddr_);
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
if ((addr & kPointerAlignmentMask) == 0) {
|
||||
SharedMem<int32_t*> ptr = SharedMem<int32_t*>::shared(reinterpret_cast<int32_t*>(addr));
|
||||
|
||||
if (!LLBit_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
LLBit_ = false;
|
||||
LLAddr_ = 0;
|
||||
int32_t expected = lastLLValue_;
|
||||
int32_t old = AtomicOperations::compareExchangeSeqCst(ptr, expected, int32_t(value));
|
||||
return (old == expected) ? 1:0;
|
||||
}
|
||||
printf("Unaligned SC at 0x%08x, pc=0x%08" PRIxPTR "\n",
|
||||
addr,
|
||||
reinterpret_cast<intptr_t>(instr));
|
||||
MOZ_CRASH();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
Simulator::stackLimit() const
|
||||
{
|
||||
|
@ -1860,6 +2002,8 @@ typedef int32_t (*Prototype_Int_DoubleIntInt)(double arg0, int32_t arg1, int32_t
|
|||
typedef int32_t (*Prototype_Int_IntDoubleIntInt)(int32_t arg0, double arg1, int32_t arg2,
|
||||
int32_t arg3);
|
||||
typedef float (*Prototype_Float32_Float32)(float arg0);
|
||||
typedef float (*Prototype_Float32_Float32Float32)(float arg0, float arg1);
|
||||
typedef float (*Prototype_Float32_IntInt)(int arg0, int arg1);
|
||||
|
||||
typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1);
|
||||
typedef double (*Prototype_Double_IntInt)(int32_t arg0, int32_t arg1);
|
||||
|
@ -2019,6 +2163,22 @@ Simulator::softwareInterrupt(SimInstruction* instr)
|
|||
setCallResultFloat(fresult);
|
||||
break;
|
||||
}
|
||||
case Args_Float32_Float32Float32: {
|
||||
float fval0;
|
||||
float fval1;
|
||||
fval0 = getFpuRegisterFloat(12);
|
||||
fval1 = getFpuRegisterFloat(14);
|
||||
Prototype_Float32_Float32Float32 target = reinterpret_cast<Prototype_Float32_Float32Float32>(external);
|
||||
float fresult = target(fval0,fval1);
|
||||
setCallResultFloat(fresult);
|
||||
break;
|
||||
}
|
||||
case Args_Float32_IntInt: {
|
||||
Prototype_Float32_IntInt target = reinterpret_cast<Prototype_Float32_IntInt>(external);
|
||||
float fresult = target(arg0, arg1);
|
||||
setCallResultFloat(fresult);
|
||||
break;
|
||||
}
|
||||
case Args_Double_Int: {
|
||||
Prototype_Double_Int target = reinterpret_cast<Prototype_Double_Int>(external);
|
||||
double dresult = target(arg0);
|
||||
|
@ -2406,6 +2566,7 @@ Simulator::configureTypeRegister(SimInstruction* instr,
|
|||
case ff_movn:
|
||||
case ff_movz:
|
||||
case ff_movci:
|
||||
case ff_sync:
|
||||
// No action taken on decode.
|
||||
break;
|
||||
case ff_div:
|
||||
|
@ -2524,7 +2685,6 @@ Simulator::decodeTypeRegister(SimInstruction* instr)
|
|||
MOZ_CRASH();
|
||||
break;
|
||||
case rs_cfc1:
|
||||
setRegister(rt_reg, alu_out);
|
||||
case rs_mfc1:
|
||||
setRegister(rt_reg, alu_out);
|
||||
break;
|
||||
|
@ -2545,7 +2705,6 @@ Simulator::decodeTypeRegister(SimInstruction* instr)
|
|||
case rs_s:
|
||||
float f, ft_value, fs_value;
|
||||
uint32_t cc, fcsr_cc;
|
||||
int64_t i64;
|
||||
fs_value = getFpuRegisterFloat(fs_reg);
|
||||
ft_value = getFpuRegisterFloat(ft_reg);
|
||||
cc = instr->fcccValue();
|
||||
|
@ -2649,37 +2808,30 @@ Simulator::decodeTypeRegister(SimInstruction* instr)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case ff_cvt_l_fmt: { // Mips32r2: Truncate float to 64-bit long-word.
|
||||
float rounded = truncf(fs_value);
|
||||
i64 = static_cast<int64_t>(rounded);
|
||||
setFpuRegisterFloat(fd_reg, i64);
|
||||
break;
|
||||
}
|
||||
case ff_round_l_fmt: { // Mips32r2 instruction.
|
||||
float rounded =
|
||||
fs_value > 0 ? std::floor(fs_value + 0.5) : std::ceil(fs_value - 0.5);
|
||||
i64 = static_cast<int64_t>(rounded);
|
||||
setFpuRegisterFloat(fd_reg, i64);
|
||||
break;
|
||||
}
|
||||
case ff_trunc_l_fmt: { // Mips32r2 instruction.
|
||||
float rounded = truncf(fs_value);
|
||||
i64 = static_cast<int64_t>(rounded);
|
||||
setFpuRegisterFloat(fd_reg, i64);
|
||||
break;
|
||||
}
|
||||
case ff_floor_l_fmt: // Mips32r2 instruction.
|
||||
i64 = static_cast<int64_t>(std::floor(fs_value));
|
||||
setFpuRegisterFloat(fd_reg, i64);
|
||||
break;
|
||||
case ff_ceil_l_fmt: // Mips32r2 instruction.
|
||||
i64 = static_cast<int64_t>(std::ceil(fs_value));
|
||||
setFpuRegisterFloat(fd_reg, i64);
|
||||
break;
|
||||
case ff_cvt_l_fmt:
|
||||
case ff_round_l_fmt:
|
||||
case ff_trunc_l_fmt:
|
||||
case ff_floor_l_fmt:
|
||||
case ff_ceil_l_fmt:
|
||||
case ff_cvt_ps_s:
|
||||
case ff_c_f_fmt:
|
||||
MOZ_CRASH();
|
||||
break;
|
||||
case ff_movf_fmt:
|
||||
if (testFCSRBit(fcsr_cc)) {
|
||||
setFpuRegisterFloat(fd_reg, getFpuRegisterFloat(fs_reg));
|
||||
}
|
||||
break;
|
||||
case ff_movz_fmt:
|
||||
if (rt == 0) {
|
||||
setFpuRegisterFloat(fd_reg, getFpuRegisterFloat(fs_reg));
|
||||
}
|
||||
break;
|
||||
case ff_movn_fmt:
|
||||
if (rt != 0) {
|
||||
setFpuRegisterFloat(fd_reg, getFpuRegisterFloat(fs_reg));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
@ -2798,33 +2950,11 @@ Simulator::decodeTypeRegister(SimInstruction* instr)
|
|||
case ff_cvt_s_fmt: // Convert double to float (single).
|
||||
setFpuRegisterFloat(fd_reg, static_cast<float>(ds_value));
|
||||
break;
|
||||
case ff_cvt_l_fmt: { // Mips32r2: Truncate double to 64-bit long-word.
|
||||
double rounded = trunc(ds_value);
|
||||
i64 = static_cast<int64_t>(rounded);
|
||||
setFpuRegisterDouble(fd_reg, i64);
|
||||
break;
|
||||
}
|
||||
case ff_trunc_l_fmt: { // Mips32r2 instruction.
|
||||
double rounded = trunc(ds_value);
|
||||
i64 = static_cast<int64_t>(rounded);
|
||||
setFpuRegisterDouble(fd_reg, i64);
|
||||
break;
|
||||
}
|
||||
case ff_round_l_fmt: { // Mips32r2 instruction.
|
||||
double rounded =
|
||||
ds_value > 0 ? std::floor(ds_value + 0.5) : std::ceil(ds_value - 0.5);
|
||||
i64 = static_cast<int64_t>(rounded);
|
||||
setFpuRegisterDouble(fd_reg, i64);
|
||||
break;
|
||||
}
|
||||
case ff_floor_l_fmt: // Mips32r2 instruction.
|
||||
i64 = static_cast<int64_t>(std::floor(ds_value));
|
||||
setFpuRegisterDouble(fd_reg, i64);
|
||||
break;
|
||||
case ff_ceil_l_fmt: // Mips32r2 instruction.
|
||||
i64 = static_cast<int64_t>(std::ceil(ds_value));
|
||||
setFpuRegisterDouble(fd_reg, i64);
|
||||
break;
|
||||
case ff_cvt_l_fmt:
|
||||
case ff_trunc_l_fmt:
|
||||
case ff_round_l_fmt:
|
||||
case ff_floor_l_fmt:
|
||||
case ff_ceil_l_fmt:
|
||||
case ff_c_f_fmt:
|
||||
MOZ_CRASH();
|
||||
break;
|
||||
|
@ -2863,13 +2993,7 @@ Simulator::decodeTypeRegister(SimInstruction* instr)
|
|||
break;
|
||||
case rs_l:
|
||||
switch (instr->functionFieldRaw()) {
|
||||
case ff_cvt_d_fmt: // Mips32r2 instruction.
|
||||
// Watch the signs here, we want 2 32-bit vals
|
||||
// to make a sign-64.
|
||||
i64 = static_cast<uint32_t>(getFpuRegister(fs_reg));
|
||||
i64 |= static_cast<int64_t>(getFpuRegister(fs_reg + 1)) << 32;
|
||||
setFpuRegisterDouble(fd_reg, static_cast<double>(i64));
|
||||
break;
|
||||
case ff_cvt_d_fmt:
|
||||
case ff_cvt_s_fmt:
|
||||
MOZ_CRASH();
|
||||
break;
|
||||
|
@ -2962,6 +3086,20 @@ Simulator::decodeTypeRegister(SimInstruction* instr)
|
|||
softwareInterrupt(instr);
|
||||
}
|
||||
break;
|
||||
case ff_sync:
|
||||
switch(instr->bits(10, 6)){
|
||||
case 0x0:
|
||||
case 0x4:
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
case 0x12:
|
||||
case 0x13:
|
||||
AtomicOperations::fenceSeqCst();
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
break;
|
||||
// Conditional moves.
|
||||
case ff_movn:
|
||||
if (rt) setRegister(rd_reg, rs);
|
||||
|
@ -3249,6 +3387,14 @@ Simulator::decodeTypeImmediate(SimInstruction* instr)
|
|||
case op_sdc1:
|
||||
addr = rs + se_imm16;
|
||||
break;
|
||||
case op_ll:
|
||||
addr = rs + se_imm16;
|
||||
alu_out = loadLinkedW(addr, instr);
|
||||
break;
|
||||
case op_sc:
|
||||
addr = rs + se_imm16;
|
||||
alu_out = storeConditionalW(addr,rt,instr);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
@ -3294,6 +3440,8 @@ Simulator::decodeTypeImmediate(SimInstruction* instr)
|
|||
case op_lbu:
|
||||
case op_lhu:
|
||||
case op_lwr:
|
||||
case op_ll:
|
||||
case op_sc:
|
||||
setRegister(rt_reg, alu_out);
|
||||
break;
|
||||
case op_sb:
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "jit/IonTypes.h"
|
||||
#include "threading/Thread.h"
|
||||
#include "vm/MutexIDs.h"
|
||||
#include "wasm/WasmCode.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
|
@ -261,6 +262,9 @@ class Simulator {
|
|||
inline double readD(uint32_t addr, SimInstruction* instr);
|
||||
inline void writeD(uint32_t addr, double value, SimInstruction* instr);
|
||||
|
||||
inline int32_t loadLinkedW(uint32_t addr, SimInstruction* instr);
|
||||
inline int32_t storeConditionalW(uint32_t addr, int32_t value, SimInstruction* instr);
|
||||
|
||||
// Executing is handled based on the instruction type.
|
||||
void decodeTypeRegister(SimInstruction* instr);
|
||||
|
||||
|
@ -294,6 +298,9 @@ class Simulator {
|
|||
void handleWasmInterrupt();
|
||||
void startInterrupt(WasmActivation* act);
|
||||
|
||||
// Handle any wasm faults, returning true if the fault was handled.
|
||||
bool handleWasmFault(int32_t addr, unsigned numBytes);
|
||||
|
||||
// Executes one instruction.
|
||||
void instructionDecode(SimInstruction* instr);
|
||||
// Execute one instruction placed in a branch delay slot.
|
||||
|
@ -336,6 +343,10 @@ class Simulator {
|
|||
// FPU control register.
|
||||
uint32_t FCSR_;
|
||||
|
||||
bool LLBit_;
|
||||
uint32_t LLAddr_;
|
||||
int32_t lastLLValue_;
|
||||
|
||||
// Simulator support.
|
||||
char* stack_;
|
||||
uintptr_t stackLimit_;
|
||||
|
@ -343,8 +354,9 @@ class Simulator {
|
|||
int icount_;
|
||||
int break_count_;
|
||||
|
||||
// wasm async interrupt support
|
||||
// wasm async interrupt / fault support
|
||||
bool wasm_interrupt_;
|
||||
wasm::SharedCode wasm_code_;
|
||||
|
||||
// Debugger input.
|
||||
char* lastDebuggerInput_;
|
||||
|
|
|
@ -113,6 +113,14 @@ class FloatRegister : public FloatRegisterMIPSShared
|
|||
: reg_(Encoding(FloatRegisters::invalid_freg)), kind_(Codes::Double)
|
||||
{ }
|
||||
|
||||
static uint32_t SetSize(SetType x) {
|
||||
// Count the number of non-aliased registers.
|
||||
x |= x >> Codes::TotalPhys;
|
||||
x &= Codes::AllPhysMask;
|
||||
static_assert(Codes::AllPhysMask <= 0xffffffff, "We can safely use CountPopulation32");
|
||||
return mozilla::CountPopulation32(x);
|
||||
}
|
||||
|
||||
bool operator==(const FloatRegister& other) const {
|
||||
MOZ_ASSERT(!isInvalid());
|
||||
MOZ_ASSERT(!other.isInvalid());
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
#include "jit/mips64/Assembler-mips64.h"
|
||||
#include "threading/LockGuard.h"
|
||||
#include "vm/Runtime.h"
|
||||
#include "wasm/WasmInstance.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
|
||||
#define I8(v) static_cast<int8_t>(v)
|
||||
#define I16(v) static_cast<int16_t>(v)
|
||||
|
@ -1267,7 +1269,7 @@ Simulator::Simulator()
|
|||
pc_modified_ = false;
|
||||
icount_ = 0;
|
||||
break_count_ = 0;
|
||||
resume_pc_ = 0;
|
||||
wasm_interrupt_ = false;
|
||||
break_pc_ = nullptr;
|
||||
break_instr_ = 0;
|
||||
single_stepping_ = false;
|
||||
|
@ -1610,6 +1612,18 @@ Simulator::get_pc() const
|
|||
return registers_[pc];
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::startInterrupt(WasmActivation* activation)
|
||||
{
|
||||
MOZ_CRASH("NIY");
|
||||
}
|
||||
|
||||
void
|
||||
Simulator::handleWasmInterrupt()
|
||||
{
|
||||
MOZ_CRASH("NIY");
|
||||
}
|
||||
|
||||
// The MIPS cannot do unaligned reads and writes. On some MIPS platforms an
|
||||
// interrupt is caused. On others it does a funky rotation thing. For now we
|
||||
// simply disallow unaligned reads, but at some point we may want to move to
|
||||
|
@ -2720,6 +2734,7 @@ Simulator::decodeTypeRegister(SimInstruction* instr)
|
|||
break;
|
||||
case rs_cfc1:
|
||||
setRegister(rt_reg, alu_out);
|
||||
MOZ_FALLTHROUGH;
|
||||
case rs_mfc1:
|
||||
setRegister(rt_reg, alu_out);
|
||||
break;
|
||||
|
@ -2808,6 +2823,7 @@ Simulator::decodeTypeRegister(SimInstruction* instr)
|
|||
// Rounding modes are not yet supported.
|
||||
MOZ_ASSERT((FCSR_ & 3) == 0);
|
||||
// In rounding mode 0 it should behave like ROUND.
|
||||
MOZ_FALLTHROUGH;
|
||||
case ff_round_w_fmt: { // Round double to word (round half to even).
|
||||
float rounded = std::floor(fs_value + 0.5);
|
||||
int32_t result = I32(rounded);
|
||||
|
@ -2943,6 +2959,7 @@ Simulator::decodeTypeRegister(SimInstruction* instr)
|
|||
// Rounding modes are not yet supported.
|
||||
MOZ_ASSERT((FCSR_ & 3) == 0);
|
||||
// In rounding mode 0 it should behave like ROUND.
|
||||
MOZ_FALLTHROUGH;
|
||||
case ff_round_w_fmt: { // Round double to word (round half to even).
|
||||
double rounded = std::floor(ds_value + 0.5);
|
||||
int32_t result = I32(rounded);
|
||||
|
@ -3314,6 +3331,7 @@ Simulator::decodeTypeImmediate(SimInstruction* instr)
|
|||
} else {
|
||||
next_pc = current_pc + kBranchReturnOffset;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
@ -3722,7 +3740,6 @@ Simulator::execute()
|
|||
// Get the PC to simulate. Cannot use the accessor here as we need the
|
||||
// raw PC value and not the one used as input to arithmetic instructions.
|
||||
int64_t program_counter = get_pc();
|
||||
WasmActivation* activation = TlsContext.get()->wasmActivationStack();
|
||||
|
||||
while (program_counter != end_sim_pc) {
|
||||
if (enableStopSimAt && (icount_ == Simulator::StopSimAt)) {
|
||||
|
@ -3735,12 +3752,9 @@ Simulator::execute()
|
|||
instructionDecode(instr);
|
||||
icount_++;
|
||||
|
||||
int64_t rpc = resume_pc_;
|
||||
if (MOZ_UNLIKELY(rpc != 0)) {
|
||||
// wasm signal handler ran and we have to adjust the pc.
|
||||
activation->setResumePC((void*)get_pc());
|
||||
set_pc(rpc);
|
||||
resume_pc_ = 0;
|
||||
if (MOZ_UNLIKELY(wasm_interrupt_)) {
|
||||
handleWasmInterrupt();
|
||||
wasm_interrupt_ = false;
|
||||
}
|
||||
}
|
||||
program_counter = get_pc();
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
#include "vm/MutexIDs.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class WasmActivation;
|
||||
|
||||
namespace jit {
|
||||
|
||||
class Simulator;
|
||||
|
@ -193,9 +196,12 @@ class Simulator {
|
|||
template <typename T>
|
||||
T get_pc_as() const { return reinterpret_cast<T>(get_pc()); }
|
||||
|
||||
void set_resume_pc(void* value) {
|
||||
resume_pc_ = int64_t(value);
|
||||
}
|
||||
void trigger_wasm_interrupt() {
|
||||
// This can be called several times if a single interrupt isn't caught
|
||||
// and handled by the simulator, but this is fine; once the current
|
||||
// instruction is done executing, the interrupt will be handled anyhow.
|
||||
wasm_interrupt_ = true;
|
||||
}
|
||||
|
||||
void enable_single_stepping(SingleStepCallback cb, void* arg);
|
||||
void disable_single_stepping();
|
||||
|
@ -297,6 +303,9 @@ class Simulator {
|
|||
void increaseStopCounter(uint32_t code);
|
||||
void printStopInfo(uint32_t code);
|
||||
|
||||
// Handle a wasm interrupt triggered by an async signal handler.
|
||||
void handleWasmInterrupt();
|
||||
void startInterrupt(WasmActivation* act);
|
||||
|
||||
// Executes one instruction.
|
||||
void instructionDecode(SimInstruction* instr);
|
||||
|
@ -345,7 +354,8 @@ class Simulator {
|
|||
int64_t icount_;
|
||||
int64_t break_count_;
|
||||
|
||||
int64_t resume_pc_;
|
||||
// wasm async interrupt support
|
||||
bool wasm_interrupt_;
|
||||
|
||||
// Debugger input.
|
||||
char* lastDebuggerInput_;
|
||||
|
|
|
@ -657,6 +657,12 @@ JS_SetAccumulateTelemetryCallback(JSContext* cx, JSAccumulateTelemetryDataCallba
|
|||
cx->runtime()->setTelemetryCallback(cx->runtime(), callback);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
JS_SetSetUseCounterCallback(JSContext* cx, JSSetUseCounterCallback callback)
|
||||
{
|
||||
cx->runtime()->setUseCounterCallback(cx->runtime(), callback);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSObject*)
|
||||
JS_CloneObject(JSContext* cx, HandleObject obj, HandleObject protoArg)
|
||||
{
|
||||
|
|
|
@ -172,6 +172,24 @@ typedef void
|
|||
extern JS_FRIEND_API(void)
|
||||
JS_SetAccumulateTelemetryCallback(JSContext* cx, JSAccumulateTelemetryDataCallback callback);
|
||||
|
||||
/*
|
||||
* Use counter names passed to the accumulate use counter callback.
|
||||
*
|
||||
* It's OK to for these enum values to change as they will be mapped to a
|
||||
* fixed member of the mozilla::UseCounter enum by the callback.
|
||||
*/
|
||||
|
||||
enum class JSUseCounter {
|
||||
ASMJS,
|
||||
WASM
|
||||
};
|
||||
|
||||
typedef void
|
||||
(*JSSetUseCounterCallback)(JSObject* obj, JSUseCounter counter);
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
JS_SetSetUseCounterCallback(JSContext* cx, JSSetUseCounterCallback callback);
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
JS_GetIsSecureContext(JSCompartment* compartment);
|
||||
|
||||
|
|
|
@ -740,3 +740,7 @@ skip script test262/language/expressions/class/dstr-meth-static-obj-ptrn-rest-ob
|
|||
skip script test262/language/expressions/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-obj-nested-rest.js
|
||||
skip script test262/language/expressions/class/dstr-async-gen-meth-static-dflt-obj-ptrn-rest-obj-nested-rest.js
|
||||
skip script test262/language/expressions/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-nested-obj.js
|
||||
|
||||
# https://github.com/tc39/test262/pull/1170
|
||||
skip script test262/language/module-code/namespace/internals/define-own-property.js
|
||||
skip script test262/language/module-code/namespace/internals/set.js
|
||||
|
|
|
@ -444,6 +444,19 @@ JSRuntime::setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback
|
|||
rt->telemetryCallback = callback;
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::setUseCounter(JSObject* obj, JSUseCounter counter)
|
||||
{
|
||||
if (useCounterCallback)
|
||||
(*useCounterCallback)(obj, counter);
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::setUseCounterCallback(JSRuntime* rt, JSSetUseCounterCallback callback)
|
||||
{
|
||||
rt->useCounterCallback = callback;
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes* rtSizes)
|
||||
{
|
||||
|
|
|
@ -455,6 +455,10 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
|
||||
/* Call this to accumulate telemetry data. */
|
||||
js::ActiveThreadData<JSAccumulateTelemetryDataCallback> telemetryCallback;
|
||||
|
||||
/* Call this to accumulate use counter data. */
|
||||
js::ActiveThreadData<JSSetUseCounterCallback> useCounterCallback;
|
||||
|
||||
public:
|
||||
// Accumulates data for Firefox telemetry. |id| is the ID of a JS_TELEMETRY_*
|
||||
// histogram. |key| provides an additional key to identify the histogram.
|
||||
|
@ -463,6 +467,13 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
|||
|
||||
void setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback callback);
|
||||
|
||||
// Sets the use counter for a specific feature, measuring the presence or
|
||||
// absence of usage of a feature on a specific web page and document which
|
||||
// the passed JSObject belongs to.
|
||||
void setUseCounter(JSObject* obj, JSUseCounter counter);
|
||||
|
||||
void setUseCounterCallback(JSRuntime* rt, JSSetUseCounterCallback callback);
|
||||
|
||||
public:
|
||||
js::UnprotectedData<js::OffThreadPromiseRuntimeState> offThreadPromiseState;
|
||||
|
||||
|
|
|
@ -1206,5 +1206,8 @@ Module::instantiate(JSContext* cx,
|
|||
uint32_t mode = uint32_t(metadata().isAsmJS() ? Telemetry::ASMJS : Telemetry::WASM);
|
||||
cx->runtime()->addTelemetry(JS_TELEMETRY_AOT_USAGE, mode);
|
||||
|
||||
JSUseCounter useCounter = metadata().isAsmJS() ? JSUseCounter::ASMJS : JSUseCounter::WASM;
|
||||
cx->runtime()->setUseCounter(instance, useCounter);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1174,7 +1174,7 @@ wasm::GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel)
|
|||
// Reclaim the reserve space.
|
||||
masm.addToStackPtr(Imm32(2 * sizeof(intptr_t)));
|
||||
masm.as_jr(HeapReg);
|
||||
masm.loadPtr(Address(StackPointer, -sizeof(intptr_t)), HeapReg);
|
||||
masm.loadPtr(Address(StackPointer, -int32_t(sizeof(intptr_t))), HeapReg);
|
||||
#elif defined(JS_CODEGEN_ARM)
|
||||
{
|
||||
// Be careful not to clobber scratch registers before they are saved.
|
||||
|
|
|
@ -2663,6 +2663,21 @@ AccumulateTelemetryCallback(int id, uint32_t sample, const char* key)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SetUseCounterCallback(JSObject* obj, JSUseCounter counter)
|
||||
{
|
||||
switch (counter) {
|
||||
case JSUseCounter::ASMJS:
|
||||
SetDocumentAndPageUseCounter(obj, eUseCounter_custom_JS_asmjs);
|
||||
break;
|
||||
case JSUseCounter::WASM:
|
||||
SetDocumentAndPageUseCounter(obj, eUseCounter_custom_JS_wasm);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected JSUseCounter id");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
CompartmentNameCallback(JSContext* cx, JSCompartment* comp,
|
||||
char* buf, size_t bufsize)
|
||||
|
@ -2865,6 +2880,7 @@ XPCJSRuntime::Initialize(JSContext* cx)
|
|||
JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
|
||||
js::SetPreserveWrapperCallback(cx, PreserveWrapper);
|
||||
JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback);
|
||||
JS_SetSetUseCounterCallback(cx, SetUseCounterCallback);
|
||||
js::SetWindowProxyClass(cx, &OuterWindowProxyClass);
|
||||
js::SetXrayJitInfo(&gXrayJitInfo);
|
||||
JS::SetProcessLargeAllocationFailureCallback(OnLargeAllocationFailureCallback);
|
||||
|
|
|
@ -426,6 +426,9 @@
|
|||
*
|
||||
* The static analyses that are performed by the plugin are as follows:
|
||||
*
|
||||
* MOZ_CAN_RUN_SCRIPT: Applies to functions which can run script. Callers of
|
||||
* this function must also be marked as MOZ_CAN_RUN_SCRIPT, and all refcounted
|
||||
* arguments must be strongly held in the caller.
|
||||
* MOZ_MUST_OVERRIDE: Applies to all C++ member functions. All immediate
|
||||
* subclasses must provide an exact override of this method; if a subclass
|
||||
* does not override this method, the compiler will emit an error. This
|
||||
|
@ -478,6 +481,14 @@
|
|||
* are disallowed by default unless they are marked as MOZ_IMPLICIT. This
|
||||
* attribute must be used for constructors which intend to provide implicit
|
||||
* conversions.
|
||||
* MOZ_IS_REFPTR: Applies to class declarations of ref pointer to mark them as
|
||||
* such for use with static-analysis.
|
||||
* A ref pointer is an object wrapping a pointer and automatically taking care
|
||||
* of its refcounting upon construction/destruction/transfer of ownership.
|
||||
* This annotation implies MOZ_IS_SMARTPTR_TO_REFCOUNTED.
|
||||
* MOZ_IS_SMARTPTR_TO_REFCOUNTED: Applies to class declarations of smart
|
||||
* pointers to ref counted classes to mark them as such for use with
|
||||
* static-analysis.
|
||||
* MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT: Applies to functions. Makes it a compile
|
||||
* time error to pass arithmetic expressions on variables to the function.
|
||||
* MOZ_OWNING_REF: Applies to declarations of pointers to reference counted
|
||||
|
@ -576,6 +587,7 @@
|
|||
* MOZ_MUST_RETURN_FROM_CALLER function or method.
|
||||
*/
|
||||
#ifdef MOZ_CLANG_PLUGIN
|
||||
# define MOZ_CAN_RUN_SCRIPT __attribute__((annotate("moz_can_run_script")))
|
||||
# define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override")))
|
||||
# define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
|
||||
# define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class")))
|
||||
|
@ -590,6 +602,9 @@
|
|||
MOZ_TRIVIAL_CTOR_DTOR
|
||||
# endif
|
||||
# define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
|
||||
# define MOZ_IS_SMARTPTR_TO_REFCOUNTED __attribute__((annotate("moz_is_smartptr_to_refcounted")))
|
||||
# define MOZ_IS_REFPTR __attribute__((annotate("moz_is_refptr"))) \
|
||||
MOZ_IS_SMARTPTR_TO_REFCOUNTED
|
||||
# define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT __attribute__((annotate("moz_no_arith_expr_in_arg")))
|
||||
# define MOZ_OWNING_REF __attribute__((annotate("moz_strong_ref")))
|
||||
# define MOZ_NON_OWNING_REF __attribute__((annotate("moz_weak_ref")))
|
||||
|
@ -627,6 +642,7 @@
|
|||
__attribute__((annotate("moz_heap_allocator"))) \
|
||||
_Pragma("clang diagnostic pop")
|
||||
#else
|
||||
# define MOZ_CAN_RUN_SCRIPT /* nothing */
|
||||
# define MOZ_MUST_OVERRIDE /* nothing */
|
||||
# define MOZ_STACK_CLASS /* nothing */
|
||||
# define MOZ_NONHEAP_CLASS /* nothing */
|
||||
|
@ -635,6 +651,8 @@
|
|||
# define MOZ_TRIVIAL_CTOR_DTOR /* nothing */
|
||||
# define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS /* nothing */
|
||||
# define MOZ_IMPLICIT /* nothing */
|
||||
# define MOZ_IS_SMARTPTR_TO_REFCOUNTED /* nothing */
|
||||
# define MOZ_IS_REFPTR /* nothing */
|
||||
# define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT /* nothing */
|
||||
# define MOZ_HEAP_ALLOCATOR /* nothing */
|
||||
# define MOZ_OWNING_REF /* nothing */
|
||||
|
|
|
@ -44,7 +44,7 @@ struct RefPtrTraits
|
|||
} // namespace mozilla
|
||||
|
||||
template <class T>
|
||||
class RefPtr
|
||||
class MOZ_IS_REFPTR RefPtr
|
||||
{
|
||||
private:
|
||||
void
|
||||
|
|
|
@ -21,10 +21,20 @@
|
|||
#ifdef MOZ_CLANG_PLUGIN
|
||||
|
||||
#ifdef __cplusplus
|
||||
/**
|
||||
* MOZ_KnownLive - used to opt an argument out of the CanRunScript checker so
|
||||
* that we don't check it if is a strong ref.
|
||||
*
|
||||
* Example:
|
||||
* canRunScript(MOZ_KnownLive(rawPointer));
|
||||
*/
|
||||
template <typename T>
|
||||
static MOZ_ALWAYS_INLINE T* MOZ_KnownLive(T* ptr) { return ptr; }
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
/**
|
||||
* MOZ_AssertAssignmentTest - used in MOZ_ASSERT in order to test the possible
|
||||
* presence of assignment instead of logical comparisons.
|
||||
*
|
||||
|
|
|
@ -1030,6 +1030,11 @@ public abstract class GeckoApp extends GeckoActivity
|
|||
|
||||
@Override // GeckoView.ContentListener
|
||||
public void onFullScreen(final GeckoView view, final boolean fullScreen) {
|
||||
if (fullScreen) {
|
||||
SnackbarBuilder.builder(this)
|
||||
.message(R.string.fullscreen_warning)
|
||||
.duration(Snackbar.LENGTH_LONG).buildAndShow();
|
||||
}
|
||||
ThreadUtils.assertOnUiThread();
|
||||
ActivityUtils.setFullScreen(this, fullScreen);
|
||||
}
|
||||
|
|
|
@ -857,3 +857,5 @@ is simply hidden from the Activity Stream panel. -->
|
|||
<!ENTITY private_tab_panel_description "&brandShortName; blocks parts of the pages that may track your browsing activity.">
|
||||
<!ENTITY private_tab_panel_description2 "We won\'t remember any history, but downloaded files and new bookmarks will still be saved to your device.">
|
||||
<!ENTITY private_tab_learn_more "Want to learn more?">
|
||||
|
||||
<!ENTITY fullscreen_warning "Entered full screen">
|
||||
|
|
|
@ -636,4 +636,6 @@
|
|||
<string name="private_tab_panel_description">&private_tab_panel_description;</string>
|
||||
<string name="private_tab_panel_description2">&private_tab_panel_description2;</string>
|
||||
<string name="private_tab_learn_more">&private_tab_learn_more;</string>
|
||||
|
||||
<string name="fullscreen_warning">&fullscreen_warning;</string>
|
||||
</resources>
|
||||
|
|
|
@ -5775,7 +5775,7 @@ pref("security.mixed_content.hsts_priming_request_timeout", 2000);
|
|||
// a NullPrincipal as the security context.
|
||||
// Otherwise it will inherit the origin from parent node, this is the legacy
|
||||
// behavior of Firefox.
|
||||
pref("security.data_uri.unique_opaque_origin", false);
|
||||
pref("security.data_uri.unique_opaque_origin", true);
|
||||
|
||||
// TODO: Bug 1380959: Block toplevel data: URI navigations
|
||||
// If true, all toplevel data: URI navigations will be blocked.
|
||||
|
|
|
@ -66,7 +66,7 @@ W32API_VERSION=3.14
|
|||
GCONF_VERSION=1.2.1
|
||||
STARTUP_NOTIFICATION_VERSION=0.8
|
||||
DBUS_VERSION=0.60
|
||||
SQLITE_VERSION=3.20.0
|
||||
SQLITE_VERSION=3.20.1
|
||||
|
||||
dnl Set various checks
|
||||
dnl ========================================================
|
||||
|
|
|
@ -995,8 +995,9 @@ nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL,
|
|||
const nsAString& aImageReferrerPolicy)
|
||||
{
|
||||
nsCOMPtr<nsIURI> baseURI = BaseURIForPreload();
|
||||
bool isImgSet = false;
|
||||
nsCOMPtr<nsIURI> uri = mDocument->ResolvePreloadImage(baseURI, aURL, aSrcset,
|
||||
aSizes);
|
||||
aSizes, &isImgSet);
|
||||
if (uri && ShouldPreloadURI(uri)) {
|
||||
// use document wide referrer policy
|
||||
mozilla::net::ReferrerPolicy referrerPolicy = mSpeculationReferrerPolicy;
|
||||
|
@ -1006,7 +1007,7 @@ nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL,
|
|||
referrerPolicy = imageReferrerPolicy;
|
||||
}
|
||||
|
||||
mDocument->MaybePreLoadImage(uri, aCrossOrigin, referrerPolicy);
|
||||
mDocument->MaybePreLoadImage(uri, aCrossOrigin, referrerPolicy, isImgSet);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
import os
|
||||
import sys
|
||||
from distutils.spawn import find_executable
|
||||
|
||||
import py
|
||||
import pytest
|
||||
|
||||
from mozlint import cli
|
||||
|
@ -39,7 +39,7 @@ def test_cli_run_with_fix(run, capfd):
|
|||
assert out.endswith('{}\n')
|
||||
|
||||
|
||||
@pytest.mark.skipif(not py.path.local.sysfind("echo"), reason="No `echo` executable found.")
|
||||
@pytest.mark.skipif(not find_executable("echo"), reason="No `echo` executable found.")
|
||||
def test_cli_run_with_edit(run, parser, capfd):
|
||||
os.environ['EDITOR'] = 'echo'
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@ SandboxBrokerClient::~SandboxBrokerClient()
|
|||
|
||||
int
|
||||
SandboxBrokerClient::DoCall(const Request* aReq, const char* aPath,
|
||||
void* aResponseBuff, bool expectFd)
|
||||
const char* aPath2, void* aResponseBuff,
|
||||
bool expectFd)
|
||||
{
|
||||
// Remap /proc/self to the actual pid, so that the broker can open
|
||||
// it. This happens here instead of in the broker to follow the
|
||||
|
@ -63,28 +64,38 @@ SandboxBrokerClient::DoCall(const Request* aReq, const char* aPath,
|
|||
}
|
||||
}
|
||||
|
||||
struct iovec ios[2];
|
||||
struct iovec ios[3];
|
||||
int respFds[2];
|
||||
|
||||
// Set up iovecs for request + path.
|
||||
ios[0].iov_base = const_cast<Request*>(aReq);
|
||||
ios[0].iov_len = sizeof(*aReq);
|
||||
ios[1].iov_base = const_cast<char*>(path);
|
||||
ios[1].iov_len = strlen(path);
|
||||
ios[1].iov_len = strlen(path) + 1;
|
||||
if (aPath2 != nullptr) {
|
||||
ios[2].iov_base = const_cast<char*>(aPath2);
|
||||
ios[2].iov_len = strlen(aPath2) + 1;
|
||||
} else {
|
||||
ios[2].iov_base = nullptr;
|
||||
ios[2].iov_len = 0;
|
||||
}
|
||||
if (ios[1].iov_len > kMaxPathLen) {
|
||||
return -ENAMETOOLONG;
|
||||
}
|
||||
if (ios[2].iov_len > kMaxPathLen) {
|
||||
return -ENAMETOOLONG;
|
||||
}
|
||||
|
||||
// Create response socket and send request.
|
||||
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, respFds) < 0) {
|
||||
return -errno;
|
||||
}
|
||||
const ssize_t sent = SendWithFd(mFileDesc, ios, 2, respFds[1]);
|
||||
const ssize_t sent = SendWithFd(mFileDesc, ios, 3, respFds[1]);
|
||||
const int sendErrno = errno;
|
||||
MOZ_ASSERT(sent < 0 ||
|
||||
static_cast<size_t>(sent) == ios[0].iov_len
|
||||
+ ios[1].iov_len);
|
||||
|
||||
+ ios[1].iov_len
|
||||
+ ios[2].iov_len);
|
||||
close(respFds[1]);
|
||||
if (sent < 0) {
|
||||
close(respFds[0]);
|
||||
|
@ -145,7 +156,7 @@ int
|
|||
SandboxBrokerClient::Open(const char* aPath, int aFlags)
|
||||
{
|
||||
Request req = { SANDBOX_FILE_OPEN, aFlags, 0 };
|
||||
int maybeFd = DoCall(&req, aPath, nullptr, true);
|
||||
int maybeFd = DoCall(&req, aPath, nullptr, nullptr, true);
|
||||
if (maybeFd >= 0) {
|
||||
// NSPR has opinions about file flags. Fix O_CLOEXEC.
|
||||
if ((aFlags & O_CLOEXEC) == 0) {
|
||||
|
@ -159,56 +170,77 @@ int
|
|||
SandboxBrokerClient::Access(const char* aPath, int aMode)
|
||||
{
|
||||
Request req = { SANDBOX_FILE_ACCESS, aMode, 0 };
|
||||
return DoCall(&req, aPath, nullptr, false);
|
||||
return DoCall(&req, aPath, nullptr, nullptr, false);
|
||||
}
|
||||
|
||||
int
|
||||
SandboxBrokerClient::Stat(const char* aPath, statstruct* aStat)
|
||||
{
|
||||
Request req = { SANDBOX_FILE_STAT, 0, sizeof(statstruct) };
|
||||
return DoCall(&req, aPath, (void*)aStat, false);
|
||||
return DoCall(&req, aPath, nullptr, (void*)aStat, false);
|
||||
}
|
||||
|
||||
int
|
||||
SandboxBrokerClient::LStat(const char* aPath, statstruct* aStat)
|
||||
{
|
||||
Request req = { SANDBOX_FILE_STAT, O_NOFOLLOW, sizeof(statstruct) };
|
||||
return DoCall(&req, aPath, (void*)aStat, false);
|
||||
return DoCall(&req, aPath, nullptr, (void*)aStat, false);
|
||||
}
|
||||
|
||||
int
|
||||
SandboxBrokerClient::Chmod(const char* aPath, int aMode)
|
||||
{
|
||||
Request req = {SANDBOX_FILE_CHMOD, aMode, 0};
|
||||
return DoCall(&req, aPath, nullptr, false);
|
||||
return DoCall(&req, aPath, nullptr, nullptr, false);
|
||||
}
|
||||
|
||||
int
|
||||
SandboxBrokerClient::Link(const char* aOldPath, const char* aNewPath)
|
||||
{
|
||||
Request req = {SANDBOX_FILE_LINK, 0, 0};
|
||||
return DoCall(&req, aOldPath, aNewPath, nullptr, false);
|
||||
}
|
||||
|
||||
int
|
||||
SandboxBrokerClient::Symlink(const char* aOldPath, const char* aNewPath)
|
||||
{
|
||||
Request req = {SANDBOX_FILE_SYMLINK, 0, 0};
|
||||
return DoCall(&req, aOldPath, aNewPath, nullptr, false);
|
||||
}
|
||||
|
||||
int
|
||||
SandboxBrokerClient::Rename(const char* aOldPath, const char* aNewPath)
|
||||
{
|
||||
Request req = {SANDBOX_FILE_RENAME, 0, 0};
|
||||
return DoCall(&req, aOldPath, aNewPath, nullptr, false);
|
||||
}
|
||||
|
||||
int
|
||||
SandboxBrokerClient::Mkdir(const char* aPath, int aMode)
|
||||
{
|
||||
Request req = {SANDBOX_FILE_MKDIR, aMode, 0};
|
||||
return DoCall(&req, aPath, nullptr, false);
|
||||
return DoCall(&req, aPath, nullptr, nullptr, false);
|
||||
}
|
||||
|
||||
int
|
||||
SandboxBrokerClient::Unlink(const char* aPath)
|
||||
{
|
||||
Request req = {SANDBOX_FILE_UNLINK, 0, 0};
|
||||
return DoCall(&req, aPath, nullptr, false);
|
||||
return DoCall(&req, aPath, nullptr, nullptr, false);
|
||||
}
|
||||
|
||||
int
|
||||
SandboxBrokerClient::Rmdir(const char* aPath)
|
||||
{
|
||||
Request req = {SANDBOX_FILE_RMDIR, 0, 0};
|
||||
return DoCall(&req, aPath, nullptr, false);
|
||||
return DoCall(&req, aPath, nullptr, nullptr, false);
|
||||
}
|
||||
|
||||
int
|
||||
SandboxBrokerClient::Readlink(const char* aPath, void* aBuff, size_t aSize)
|
||||
{
|
||||
Request req = {SANDBOX_FILE_READLINK, 0, aSize};
|
||||
return DoCall(&req, aPath, aBuff, false);
|
||||
return DoCall(&req, aPath, nullptr, aBuff, false);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -35,7 +35,10 @@ class SandboxBrokerClient final : private SandboxBrokerCommon {
|
|||
int Stat(const char* aPath, statstruct* aStat);
|
||||
int LStat(const char* aPath, statstruct* aStat);
|
||||
int Chmod(const char* aPath, int aMode);
|
||||
int Link(const char* aPath, const char* aPath2);
|
||||
int Mkdir(const char* aPath, int aMode);
|
||||
int Symlink(const char* aOldPath, const char* aNewPath);
|
||||
int Rename(const char* aOldPath, const char* aNewPath);
|
||||
int Unlink(const char* aPath);
|
||||
int Rmdir(const char* aPath);
|
||||
int Readlink(const char* aPath, void* aBuf, size_t aBufSize);
|
||||
|
@ -45,6 +48,7 @@ class SandboxBrokerClient final : private SandboxBrokerCommon {
|
|||
|
||||
int DoCall(const Request* aReq,
|
||||
const char* aPath,
|
||||
const char* aPath2,
|
||||
void *aReponseBuff,
|
||||
bool expectFd);
|
||||
};
|
||||
|
|
|
@ -446,6 +446,27 @@ private:
|
|||
return broker->Chmod(path, mode);
|
||||
}
|
||||
|
||||
static intptr_t LinkTrap(ArgsRef aArgs, void *aux) {
|
||||
auto broker = static_cast<SandboxBrokerClient*>(aux);
|
||||
auto path = reinterpret_cast<const char*>(aArgs.args[0]);
|
||||
auto path2 = reinterpret_cast<const char*>(aArgs.args[1]);
|
||||
return broker->Link(path, path2);
|
||||
}
|
||||
|
||||
static intptr_t SymlinkTrap(ArgsRef aArgs, void *aux) {
|
||||
auto broker = static_cast<SandboxBrokerClient*>(aux);
|
||||
auto path = reinterpret_cast<const char*>(aArgs.args[0]);
|
||||
auto path2 = reinterpret_cast<const char*>(aArgs.args[1]);
|
||||
return broker->Symlink(path, path2);
|
||||
}
|
||||
|
||||
static intptr_t RenameTrap(ArgsRef aArgs, void *aux) {
|
||||
auto broker = static_cast<SandboxBrokerClient*>(aux);
|
||||
auto path = reinterpret_cast<const char*>(aArgs.args[0]);
|
||||
auto path2 = reinterpret_cast<const char*>(aArgs.args[1]);
|
||||
return broker->Rename(path, path2);
|
||||
}
|
||||
|
||||
static intptr_t MkdirTrap(ArgsRef aArgs, void* aux) {
|
||||
auto broker = static_cast<SandboxBrokerClient*>(aux);
|
||||
auto path = reinterpret_cast<const char*>(aArgs.args[0]);
|
||||
|
@ -594,8 +615,14 @@ public:
|
|||
return Trap(StatAtTrap, mBroker);
|
||||
case __NR_chmod:
|
||||
return Trap(ChmodTrap, mBroker);
|
||||
case __NR_link:
|
||||
return Trap(LinkTrap, mBroker);
|
||||
case __NR_mkdir:
|
||||
return Trap(MkdirTrap, mBroker);
|
||||
case __NR_symlink:
|
||||
return Trap(SymlinkTrap, mBroker);
|
||||
case __NR_rename:
|
||||
return Trap(RenameTrap, mBroker);
|
||||
case __NR_rmdir:
|
||||
return Trap(RmdirTrap, mBroker);
|
||||
case __NR_unlink:
|
||||
|
@ -614,7 +641,10 @@ public:
|
|||
CASES_FOR_lstat:
|
||||
CASES_FOR_fstatat:
|
||||
case __NR_chmod:
|
||||
case __NR_link:
|
||||
case __NR_mkdir:
|
||||
case __NR_symlink:
|
||||
case __NR_rename:
|
||||
case __NR_rmdir:
|
||||
case __NR_unlink:
|
||||
case __NR_readlink:
|
||||
|
@ -811,6 +841,9 @@ public:
|
|||
case __NR_fallocate:
|
||||
return Allow();
|
||||
|
||||
case __NR_get_mempolicy:
|
||||
return Allow();
|
||||
|
||||
#endif // DESKTOP
|
||||
|
||||
#ifdef __NR_getrandom
|
||||
|
|
|
@ -486,6 +486,19 @@ DoStat(const char* aPath, void* aBuff, int aFlags)
|
|||
return statsyscall(aPath, (statstruct*)aBuff);
|
||||
}
|
||||
|
||||
static int
|
||||
DoLink(const char* aPath, const char* aPath2,
|
||||
SandboxBrokerCommon::Operation aOper)
|
||||
{
|
||||
if (aOper == SandboxBrokerCommon::Operation::SANDBOX_FILE_LINK) {
|
||||
return link(aPath, aPath2);
|
||||
}
|
||||
if (aOper == SandboxBrokerCommon::Operation::SANDBOX_FILE_SYMLINK) {
|
||||
return symlink(aPath, aPath2);
|
||||
}
|
||||
MOZ_CRASH("SandboxBroker: Unknown link operation");
|
||||
}
|
||||
|
||||
size_t
|
||||
SandboxBroker::ConvertToRealPath(char* aPath, size_t aBufSize, size_t aPathLen)
|
||||
{
|
||||
|
@ -574,7 +587,12 @@ SandboxBroker::ThreadMain(void)
|
|||
|
||||
while (true) {
|
||||
struct iovec ios[2];
|
||||
char recvBuf[kMaxPathLen + 1];
|
||||
// We will receive the path strings in 1 buffer and split them back up.
|
||||
char recvBuf[2 * (kMaxPathLen + 1)];
|
||||
char pathBuf[kMaxPathLen + 1];
|
||||
char pathBuf2[kMaxPathLen + 1];
|
||||
size_t pathLen = 0;
|
||||
size_t pathLen2 = 0;
|
||||
char respBuf[kMaxPathLen + 1]; // Also serves as struct stat
|
||||
Request req;
|
||||
Response resp;
|
||||
|
@ -583,10 +601,13 @@ SandboxBroker::ThreadMain(void)
|
|||
// Make sure stat responses fit in the response buffer
|
||||
MOZ_ASSERT((kMaxPathLen + 1) > sizeof(struct stat));
|
||||
|
||||
// This makes our string handling below a bit less error prone.
|
||||
memset(recvBuf, 0, sizeof(recvBuf));
|
||||
|
||||
ios[0].iov_base = &req;
|
||||
ios[0].iov_len = sizeof(req);
|
||||
ios[1].iov_base = recvBuf;
|
||||
ios[1].iov_len = sizeof(recvBuf) - 1;
|
||||
ios[1].iov_len = sizeof(recvBuf);
|
||||
|
||||
const ssize_t recvd = RecvWithFd(mFileDesc, ios, 2, &respfd);
|
||||
if (recvd == 0) {
|
||||
|
@ -618,6 +639,7 @@ SandboxBroker::ThreadMain(void)
|
|||
|
||||
// Initialize the response with the default failure.
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
memset(&respBuf, 0, sizeof(respBuf));
|
||||
resp.mError = -EACCES;
|
||||
ios[0].iov_base = &resp;
|
||||
ios[0].iov_len = sizeof(resp);
|
||||
|
@ -625,28 +647,62 @@ SandboxBroker::ThreadMain(void)
|
|||
ios[1].iov_len = 0;
|
||||
int openedFd = -1;
|
||||
|
||||
size_t origPathLen = static_cast<size_t>(recvd) - sizeof(req);
|
||||
// Null-terminate to get a C-style string.
|
||||
MOZ_RELEASE_ASSERT(origPathLen < sizeof(recvBuf));
|
||||
recvBuf[origPathLen] = '\0';
|
||||
// Clear permissions
|
||||
int perms;
|
||||
|
||||
// Look up the pathname but first translate relative paths.
|
||||
// (Make a copy so we can get back the original path if needed.)
|
||||
char pathBuf[kMaxPathLen + 1];
|
||||
base::strlcpy(pathBuf, recvBuf, sizeof(pathBuf));
|
||||
size_t pathLen = ConvertToRealPath(pathBuf, sizeof(pathBuf), origPathLen);
|
||||
int perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
|
||||
// Find end of first string, make sure the buffer is still
|
||||
// 0 terminated.
|
||||
size_t recvBufLen = static_cast<size_t>(recvd) - sizeof(req);
|
||||
if (recvBufLen > 0 && recvBuf[recvBufLen - 1] != 0) {
|
||||
SANDBOX_LOG_ERROR("corrupted path buffer from pid %d", mChildPid);
|
||||
shutdown(mFileDesc, SHUT_RD);
|
||||
break;
|
||||
}
|
||||
|
||||
// We don't have read permissions on the requested dir.
|
||||
// Did we arrive from a symlink in a path that is not writable?
|
||||
// Then try to figure out the original path and see if that is readable.
|
||||
if (!(perms & MAY_READ)) {
|
||||
// Work on the original path,
|
||||
// this reverses ConvertToRealPath above.
|
||||
int symlinkPerms = SymlinkPermissions(recvBuf, origPathLen);
|
||||
if (symlinkPerms > 0) {
|
||||
perms = symlinkPerms;
|
||||
// First path should fit in maximum path length buffer.
|
||||
size_t first_len = strlen(recvBuf);
|
||||
if (first_len <= kMaxPathLen) {
|
||||
strcpy(pathBuf, recvBuf);
|
||||
// Skip right over the terminating 0, and try to copy in the
|
||||
// second path, if any. If there's no path, this will hit a
|
||||
// 0 immediately (we nulled the buffer before receiving).
|
||||
// We do not assume the second path is 0-terminated, this is
|
||||
// enforced below.
|
||||
strncpy(pathBuf2, recvBuf + first_len + 1, kMaxPathLen + 1);
|
||||
|
||||
// First string is guaranteed to be 0-terminated.
|
||||
pathLen = first_len;
|
||||
|
||||
// Look up the first pathname but first translate relative paths.
|
||||
pathLen = ConvertToRealPath(pathBuf, sizeof(pathBuf), pathLen);
|
||||
perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
|
||||
|
||||
// We don't have read permissions on the requested dir.
|
||||
// Did we arrive from a symlink in a path that is not writable?
|
||||
// Then try to figure out the original path and see if that is readable.
|
||||
if (!(perms & MAY_READ)) {
|
||||
// Work on the original path,
|
||||
// this reverses ConvertToRealPath above.
|
||||
int symlinkPerms = SymlinkPermissions(recvBuf, first_len);
|
||||
if (symlinkPerms > 0) {
|
||||
perms = symlinkPerms;
|
||||
}
|
||||
}
|
||||
|
||||
// Same for the second path.
|
||||
pathLen2 = strnlen(pathBuf2, kMaxPathLen);
|
||||
if (pathLen2 > 0) {
|
||||
// Force 0 termination.
|
||||
pathBuf2[pathLen2] = '\0';
|
||||
pathLen2 = ConvertToRealPath(pathBuf2, sizeof(pathBuf2), pathLen2);
|
||||
int perms2 = mPolicy->Lookup(nsDependentCString(pathBuf2, pathLen2));
|
||||
|
||||
// Take the intersection of the permissions for both paths.
|
||||
perms &= perms2;
|
||||
}
|
||||
} else {
|
||||
// Failed to receive intelligible paths.
|
||||
perms = 0;
|
||||
}
|
||||
|
||||
// And now perform the operation if allowed.
|
||||
|
@ -711,6 +767,31 @@ SandboxBroker::ThreadMain(void)
|
|||
}
|
||||
break;
|
||||
|
||||
case SANDBOX_FILE_LINK:
|
||||
case SANDBOX_FILE_SYMLINK:
|
||||
if (permissive || AllowOperation(W_OK, perms)) {
|
||||
if (DoLink(pathBuf, pathBuf2, req.mOp) == 0) {
|
||||
resp.mError = 0;
|
||||
} else {
|
||||
resp.mError = -errno;
|
||||
}
|
||||
} else {
|
||||
AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case SANDBOX_FILE_RENAME:
|
||||
if (permissive || AllowOperation(W_OK, perms)) {
|
||||
if (rename(pathBuf, pathBuf2) == 0) {
|
||||
resp.mError = 0;
|
||||
} else {
|
||||
resp.mError = -errno;
|
||||
}
|
||||
} else {
|
||||
AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
|
||||
}
|
||||
break;
|
||||
|
||||
case SANDBOX_FILE_MKDIR:
|
||||
if (permissive || AllowOperation(W_OK | X_OK, perms)) {
|
||||
if (mkdir(pathBuf, req.mFlags) == 0) {
|
||||
|
@ -756,12 +837,9 @@ SandboxBroker::ThreadMain(void)
|
|||
|
||||
case SANDBOX_FILE_READLINK:
|
||||
if (permissive || AllowOperation(R_OK, perms)) {
|
||||
ssize_t respSize = readlink(pathBuf, (char*)&respBuf, sizeof(respBuf) - 1);
|
||||
ssize_t respSize = readlink(pathBuf, (char*)&respBuf, sizeof(respBuf));
|
||||
if (respSize >= 0) {
|
||||
if (respSize > 0) {
|
||||
// Null-terminate for nsDependentCString.
|
||||
MOZ_RELEASE_ASSERT(static_cast<size_t>(respSize) < sizeof(respBuf));
|
||||
respBuf[respSize] = '\0';
|
||||
if (respSize > 0) {
|
||||
// Record the mapping so we can invert the file to the original
|
||||
// symlink.
|
||||
nsDependentCString orig(pathBuf, pathLen);
|
||||
|
|
|
@ -30,7 +30,10 @@ public:
|
|||
SANDBOX_FILE_ACCESS,
|
||||
SANDBOX_FILE_STAT,
|
||||
SANDBOX_FILE_CHMOD,
|
||||
SANDBOX_FILE_LINK,
|
||||
SANDBOX_FILE_SYMLINK,
|
||||
SANDBOX_FILE_MKDIR,
|
||||
SANDBOX_FILE_RENAME,
|
||||
SANDBOX_FILE_RMDIR,
|
||||
SANDBOX_FILE_UNLINK,
|
||||
SANDBOX_FILE_READLINK,
|
||||
|
|
|
@ -231,9 +231,7 @@ UniquePtr<SandboxBroker::Policy>
|
|||
SandboxBrokerPolicyFactory::GetContentPolicy(int aPid, bool aFileProcess)
|
||||
{
|
||||
// Policy entries that vary per-process (currently the only reason
|
||||
// that can happen is because they contain the pid) are added here,
|
||||
// as well as entries that depend on preferences or paths not available
|
||||
// in early startup.
|
||||
// that can happen is because they contain the pid) are added here.
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// File broker usage is controlled through a pref.
|
||||
|
@ -274,11 +272,6 @@ SandboxBrokerPolicyFactory::GetContentPolicy(int aPid, bool aFileProcess)
|
|||
policy->AddPath(rdonly, nsPrintfCString("/proc/%d/statm", aPid).get());
|
||||
policy->AddPath(rdonly, nsPrintfCString("/proc/%d/smaps", aPid).get());
|
||||
|
||||
// Bug 1384804, notably comment 15
|
||||
// Used by libnuma, included by x265/ffmpeg, who falls back
|
||||
// to get_mempolicy if this fails
|
||||
policy->AddPath(rdonly, nsPrintfCString("/proc/%d/status", aPid).get());
|
||||
|
||||
// userContent.css and the extensions dir sit in the profile, which is
|
||||
// normally blocked and we can't get the profile dir earlier in startup,
|
||||
// so this must happen here.
|
||||
|
|
|
@ -64,9 +64,18 @@ protected:
|
|||
int Chmod(const char* aPath, int aMode) {
|
||||
return mClient->Chmod(aPath, aMode);
|
||||
}
|
||||
int Link(const char* aPath, const char* bPath) {
|
||||
return mClient->Link(aPath, bPath);
|
||||
}
|
||||
int Mkdir(const char* aPath, int aMode) {
|
||||
return mClient->Mkdir(aPath, aMode);
|
||||
}
|
||||
int Symlink(const char* aPath, const char* bPath) {
|
||||
return mClient->Symlink(aPath, bPath);
|
||||
}
|
||||
int Rename(const char* aPath, const char* bPath) {
|
||||
return mClient->Rename(aPath, bPath);
|
||||
}
|
||||
int Rmdir(const char* aPath) {
|
||||
return mClient->Rmdir(aPath);
|
||||
}
|
||||
|
@ -271,6 +280,43 @@ TEST_F(SandboxBrokerTest, Chmod)
|
|||
PrePostTestCleanup();
|
||||
}
|
||||
|
||||
TEST_F(SandboxBrokerTest, Link)
|
||||
{
|
||||
PrePostTestCleanup();
|
||||
|
||||
int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
|
||||
ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
|
||||
close(fd);
|
||||
ASSERT_EQ(0, Link("/tmp/blublu", "/tmp/blublublu"));
|
||||
EXPECT_EQ(0, Access("/tmp/blublublu", F_OK));
|
||||
// Not whitelisted target path
|
||||
EXPECT_EQ(-EACCES, Link("/tmp/blublu", "/tmp/nope"));
|
||||
EXPECT_EQ(0, unlink("/tmp/blublublu"));
|
||||
EXPECT_EQ(0, unlink("/tmp/blublu"));
|
||||
|
||||
PrePostTestCleanup();
|
||||
}
|
||||
|
||||
TEST_F(SandboxBrokerTest, Symlink)
|
||||
{
|
||||
PrePostTestCleanup();
|
||||
|
||||
int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
|
||||
ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
|
||||
close(fd);
|
||||
ASSERT_EQ(0, Symlink("/tmp/blublu", "/tmp/blublublu"));
|
||||
EXPECT_EQ(0, Access("/tmp/blublublu", F_OK));
|
||||
statstruct aStat;
|
||||
ASSERT_EQ(0, lstatsyscall("/tmp/blublublu", &aStat));
|
||||
EXPECT_EQ((mode_t)S_IFLNK, aStat.st_mode & S_IFMT);
|
||||
// Not whitelisted target path
|
||||
EXPECT_EQ(-EACCES, Symlink("/tmp/blublu", "/tmp/nope"));
|
||||
EXPECT_EQ(0, unlink("/tmp/blublublu"));
|
||||
EXPECT_EQ(0, unlink("/tmp/blublu"));
|
||||
|
||||
PrePostTestCleanup();
|
||||
}
|
||||
|
||||
TEST_F(SandboxBrokerTest, Mkdir)
|
||||
{
|
||||
PrePostTestCleanup();
|
||||
|
@ -290,6 +336,24 @@ TEST_F(SandboxBrokerTest, Mkdir)
|
|||
PrePostTestCleanup();
|
||||
}
|
||||
|
||||
TEST_F(SandboxBrokerTest, Rename)
|
||||
{
|
||||
PrePostTestCleanup();
|
||||
|
||||
ASSERT_EQ(0, mkdir("/tmp/blublu", 0600))
|
||||
<< "Creating dir /tmp/blublu failed.";
|
||||
EXPECT_EQ(0, Access("/tmp/blublu", F_OK));
|
||||
ASSERT_EQ(0, Rename("/tmp/blublu", "/tmp/blublublu"));
|
||||
EXPECT_EQ(0, Access("/tmp/blublublu", F_OK));
|
||||
EXPECT_EQ(-ENOENT , Access("/tmp/blublu", F_OK));
|
||||
// Not whitelisted target path
|
||||
EXPECT_EQ(-EACCES, Rename("/tmp/blublublu", "/tmp/nope"))
|
||||
<< "Renaming dir without write access succeed.";
|
||||
EXPECT_EQ(0, rmdir("/tmp/blublublu"));
|
||||
|
||||
PrePostTestCleanup();
|
||||
}
|
||||
|
||||
TEST_F(SandboxBrokerTest, Rmdir)
|
||||
{
|
||||
PrePostTestCleanup();
|
||||
|
@ -332,7 +396,7 @@ TEST_F(SandboxBrokerTest, Readlink)
|
|||
int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
|
||||
ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
|
||||
close(fd);
|
||||
ASSERT_EQ(0, symlink("/tmp/blublu", "/tmp/blublublu"));
|
||||
ASSERT_EQ(0, Symlink("/tmp/blublu", "/tmp/blublublu"));
|
||||
EXPECT_EQ(0, Access("/tmp/blublublu", F_OK));
|
||||
char linkBuff[256];
|
||||
EXPECT_EQ(11, Readlink("/tmp/blublublu", linkBuff, sizeof(linkBuff)));
|
||||
|
|
|
@ -29,7 +29,6 @@ XPIDL_SOURCES += [
|
|||
'mozIStorageService.idl',
|
||||
'mozIStorageStatement.idl',
|
||||
'mozIStorageStatementCallback.idl',
|
||||
'mozIStorageStatementParams.idl',
|
||||
'mozIStorageVacuumParticipant.idl',
|
||||
'mozIStorageValueArray.idl',
|
||||
]
|
||||
|
@ -47,6 +46,8 @@ EXPORTS.mozilla += [
|
|||
# NOTE When adding something to this list, you probably need to add it to the
|
||||
# storage.h file too.
|
||||
EXPORTS.mozilla.storage += [
|
||||
'mozStorageAsyncStatementParams.h',
|
||||
'mozStorageStatementParams.h',
|
||||
'mozStorageStatementRow.h',
|
||||
'StatementCache.h',
|
||||
'Variant.h',
|
||||
|
|
|
@ -1,11 +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 "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(e65fe6e2-2643-463c-97e2-27665efe2386)]
|
||||
interface mozIStorageStatementParams : nsISupports {
|
||||
// Magic interface for parameter setting that implements nsIXPCScriptable.
|
||||
};
|
|
@ -18,12 +18,11 @@
|
|||
#include "StorageBaseStatementInternal.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsIXPConnectJSObjectHolder;
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
class AsyncStatementJSHelper;
|
||||
class AsyncStatementParamsHolder;
|
||||
class Connection;
|
||||
|
||||
class AsyncStatement final : public mozIStorageAsyncStatement
|
||||
|
@ -87,7 +86,7 @@ private:
|
|||
/**
|
||||
* Caches the JS 'params' helper for this statement.
|
||||
*/
|
||||
nsMainThreadPtrHandle<nsIXPConnectJSObjectHolder> mStatementParamsHolder;
|
||||
nsMainThreadPtrHandle<AsyncStatementParamsHolder> mStatementParamsHolder;
|
||||
|
||||
/**
|
||||
* Have we been explicitly finalized by the user?
|
||||
|
|
|
@ -33,7 +33,6 @@ AsyncStatementJSHelper::getParams(AsyncStatement *aStatement,
|
|||
JS::Value *_params)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsresult rv;
|
||||
|
||||
#ifdef DEBUG
|
||||
int32_t state;
|
||||
|
@ -42,32 +41,32 @@ AsyncStatementJSHelper::getParams(AsyncStatement *aStatement,
|
|||
"Invalid state to get the params object - all calls will fail!");
|
||||
#endif
|
||||
|
||||
JS::RootedObject scope(aCtx, aScopeObj);
|
||||
|
||||
if (!aStatement->mStatementParamsHolder) {
|
||||
nsCOMPtr<mozIStorageStatementParams> params =
|
||||
new AsyncStatementParams(aStatement);
|
||||
dom::GlobalObject global(aCtx, scope);
|
||||
if (global.Failed()) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global.GetAsSupports());
|
||||
|
||||
RefPtr<AsyncStatementParams> params(new AsyncStatementParams(window, aStatement));
|
||||
NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
JS::RootedObject scope(aCtx, aScopeObj);
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
|
||||
nsCOMPtr<nsIXPConnect> xpc(Service::getXPConnect());
|
||||
rv = xpc->WrapNativeHolder(
|
||||
aCtx,
|
||||
::JS_GetGlobalForObject(aCtx, scope),
|
||||
params,
|
||||
NS_GET_IID(mozIStorageStatementParams),
|
||||
getter_AddRefs(holder)
|
||||
);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
RefPtr<AsyncStatementParamsHolder> paramsHolder =
|
||||
new AsyncStatementParamsHolder(holder);
|
||||
RefPtr<AsyncStatementParamsHolder> paramsHolder = new AsyncStatementParamsHolder(params);
|
||||
NS_ENSURE_TRUE(paramsHolder, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
aStatement->mStatementParamsHolder =
|
||||
new nsMainThreadPtrHolder<nsIXPConnectJSObjectHolder>(
|
||||
"AsyncStatement::mStatementParamsHolder", paramsHolder);
|
||||
new nsMainThreadPtrHolder<AsyncStatementParamsHolder>(
|
||||
"Statement::mStatementParamsHolder", paramsHolder);
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> obj(aCtx);
|
||||
obj = aStatement->mStatementParamsHolder->GetJSObject();
|
||||
NS_ENSURE_STATE(obj);
|
||||
RefPtr<AsyncStatementParams> params(aStatement->mStatementParamsHolder->Get());
|
||||
JSObject* obj = params->WrapObject(aCtx, nullptr);
|
||||
if (!obj) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
_params->setObject(*obj);
|
||||
return NS_OK;
|
||||
|
@ -130,30 +129,14 @@ AsyncStatementJSHelper::Resolve(nsIXPConnectWrappedNative *aWrapper,
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// AsyncStatementParamsHolder
|
||||
|
||||
NS_IMPL_ISUPPORTS(AsyncStatementParamsHolder, nsIXPConnectJSObjectHolder);
|
||||
|
||||
JSObject*
|
||||
AsyncStatementParamsHolder::GetJSObject()
|
||||
{
|
||||
return mHolder->GetJSObject();
|
||||
}
|
||||
|
||||
AsyncStatementParamsHolder::AsyncStatementParamsHolder(nsIXPConnectJSObjectHolder* aHolder)
|
||||
: mHolder(aHolder)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mHolder);
|
||||
}
|
||||
NS_IMPL_ISUPPORTS0(AsyncStatementParamsHolder);
|
||||
|
||||
AsyncStatementParamsHolder::~AsyncStatementParamsHolder()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// We are considered dead at this point, so any wrappers for row or params
|
||||
// need to lose their reference to the statement.
|
||||
nsCOMPtr<nsIXPConnectWrappedNative> wrapper = do_QueryInterface(mHolder);
|
||||
nsCOMPtr<mozIStorageStatementParams> iObj = do_QueryWrappedNative(wrapper);
|
||||
AsyncStatementParams *obj = static_cast<AsyncStatementParams *>(iObj.get());
|
||||
obj->mStatement = nullptr;
|
||||
mParams->mStatement = nullptr;
|
||||
}
|
||||
|
||||
} // namespace storage
|
||||
|
|
|
@ -10,11 +10,12 @@
|
|||
#include "nsIXPCScriptable.h"
|
||||
#include "nsIXPConnect.h"
|
||||
|
||||
class AsyncStatement;
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
class AsyncStatement;
|
||||
class AsyncStatementParams;
|
||||
|
||||
/**
|
||||
* A modified version of StatementJSHelper that only exposes the async-specific
|
||||
* 'params' helper. We do not expose 'row' or 'step' as they do not apply to
|
||||
|
@ -35,17 +36,24 @@ private:
|
|||
* For cycle-avoidance reasons they do not hold reference-counted references,
|
||||
* so it is important we do this.
|
||||
*/
|
||||
class AsyncStatementParamsHolder final : public nsIXPConnectJSObjectHolder
|
||||
{
|
||||
class AsyncStatementParamsHolder final : public nsISupports {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
|
||||
|
||||
explicit AsyncStatementParamsHolder(nsIXPConnectJSObjectHolder* aHolder);
|
||||
explicit AsyncStatementParamsHolder(AsyncStatementParams* aParams)
|
||||
: mParams(aParams)
|
||||
{
|
||||
}
|
||||
|
||||
AsyncStatementParams* Get() const {
|
||||
MOZ_ASSERT(mParams);
|
||||
return mParams;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~AsyncStatementParamsHolder();
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> mHolder;
|
||||
|
||||
RefPtr<AsyncStatementParams> mParams;
|
||||
};
|
||||
|
||||
} // namespace storage
|
||||
|
|
|
@ -4,19 +4,14 @@
|
|||
* 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 "nsJSUtils.h"
|
||||
#include "nsMemory.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsJSUtils.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "mozilla/dom/MozStorageAsyncStatementParamsBinding.h"
|
||||
#include "mozStoragePrivateHelpers.h"
|
||||
#include "mozStorageAsyncStatement.h"
|
||||
#include "mozStorageAsyncStatementParams.h"
|
||||
#include "mozIStorageStatement.h"
|
||||
|
||||
#include "xpc_make_class.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
@ -24,107 +19,109 @@ namespace storage {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// AsyncStatementParams
|
||||
|
||||
AsyncStatementParams::AsyncStatementParams(AsyncStatement *aStatement)
|
||||
: mStatement(aStatement)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AsyncStatementParams, mWindow)
|
||||
|
||||
NS_INTERFACE_TABLE_HEAD(AsyncStatementParams)
|
||||
NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
|
||||
NS_INTERFACE_TABLE(AsyncStatementParams, nsISupports)
|
||||
NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(AsyncStatementParams)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncStatementParams)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncStatementParams)
|
||||
|
||||
AsyncStatementParams::AsyncStatementParams(nsPIDOMWindowInner* aWindow, AsyncStatement *aStatement)
|
||||
: mWindow(aWindow),
|
||||
mStatement(aStatement)
|
||||
{
|
||||
NS_ASSERTION(mStatement != nullptr, "mStatement is null");
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(
|
||||
AsyncStatementParams
|
||||
, mozIStorageStatementParams
|
||||
, nsIXPCScriptable
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// nsIXPCScriptable
|
||||
|
||||
#define XPC_MAP_CLASSNAME AsyncStatementParams
|
||||
#define XPC_MAP_QUOTED_CLASSNAME "AsyncStatementParams"
|
||||
#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_SETPROPERTY | \
|
||||
XPC_SCRIPTABLE_WANT_RESOLVE | \
|
||||
XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
|
||||
#include "xpc_map_end.h"
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatementParams::SetProperty(
|
||||
nsIXPConnectWrappedNative *aWrapper,
|
||||
JSContext *aCtx,
|
||||
JSObject *aScopeObj,
|
||||
jsid aId,
|
||||
JS::Value *_vp,
|
||||
bool *_retval
|
||||
)
|
||||
JSObject*
|
||||
AsyncStatementParams::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
NS_ENSURE_TRUE(mStatement, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (JSID_IS_INT(aId)) {
|
||||
int idx = JSID_TO_INT(aId);
|
||||
|
||||
nsCOMPtr<nsIVariant> variant(convertJSValToVariant(aCtx, *_vp));
|
||||
NS_ENSURE_TRUE(variant, NS_ERROR_UNEXPECTED);
|
||||
nsresult rv = mStatement->BindByIndex(idx, variant);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else if (JSID_IS_STRING(aId)) {
|
||||
JSString *str = JSID_TO_STRING(aId);
|
||||
nsAutoJSString autoStr;
|
||||
if (!autoStr.init(aCtx, str)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 name(autoStr);
|
||||
|
||||
nsCOMPtr<nsIVariant> variant(convertJSValToVariant(aCtx, *_vp));
|
||||
NS_ENSURE_TRUE(variant, NS_ERROR_UNEXPECTED);
|
||||
nsresult rv = mStatement->BindByName(name, variant);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
else {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
*_retval = true;
|
||||
return NS_OK;
|
||||
return dom::MozStorageAsyncStatementParamsBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncStatementParams::Resolve(nsIXPConnectWrappedNative *aWrapper,
|
||||
JSContext *aCtx,
|
||||
JSObject *aScopeObj,
|
||||
jsid aId,
|
||||
bool *aResolvedp,
|
||||
bool *_retval)
|
||||
void
|
||||
AsyncStatementParams::NamedGetter(JSContext* aCx,
|
||||
const nsAString& aName,
|
||||
bool& aFound,
|
||||
JS::MutableHandle<JS::Value> aResult,
|
||||
mozilla::ErrorResult& aRv)
|
||||
{
|
||||
JS::Rooted<JSObject*> scopeObj(aCtx, aScopeObj);
|
||||
|
||||
NS_ENSURE_TRUE(mStatement, NS_ERROR_NOT_INITIALIZED);
|
||||
// We do not throw at any point after this because we want to allow the
|
||||
// prototype chain to be checked for the property.
|
||||
|
||||
bool resolved = false;
|
||||
bool ok = true;
|
||||
if (JSID_IS_INT(aId)) {
|
||||
uint32_t idx = JSID_TO_INT(aId);
|
||||
// All indexes are good because we don't know how many parameters there
|
||||
// really are.
|
||||
ok = ::JS_DefineElement(aCtx, scopeObj, idx, JS::UndefinedHandleValue,
|
||||
JSPROP_RESOLVING);
|
||||
resolved = true;
|
||||
}
|
||||
else if (JSID_IS_STRING(aId)) {
|
||||
// We are unable to tell if there's a parameter with this name and so
|
||||
// we must assume that there is. This screws the rest of the prototype
|
||||
// chain, but people really shouldn't be depending on this anyways.
|
||||
JS::Rooted<jsid> id(aCtx, aId);
|
||||
ok = ::JS_DefinePropertyById(aCtx, scopeObj, id, JS::UndefinedHandleValue,
|
||||
JSPROP_RESOLVING);
|
||||
resolved = true;
|
||||
if (!mStatement) {
|
||||
aRv.Throw(NS_ERROR_NOT_INITIALIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
*_retval = ok;
|
||||
*aResolvedp = resolved && ok;
|
||||
return NS_OK;
|
||||
// Unfortunately there's no API that lets us return the parameter value.
|
||||
aFound = false;
|
||||
}
|
||||
|
||||
void
|
||||
AsyncStatementParams::NamedSetter(JSContext* aCx,
|
||||
const nsAString& aName,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
mozilla::ErrorResult& aRv)
|
||||
{
|
||||
if (!mStatement) {
|
||||
aRv.Throw(NS_ERROR_NOT_INITIALIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 name(aName);
|
||||
|
||||
nsCOMPtr<nsIVariant> variant(convertJSValToVariant(aCx, aValue));
|
||||
if (!variant) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
aRv = mStatement->BindByName(name, variant);
|
||||
}
|
||||
|
||||
void
|
||||
AsyncStatementParams::GetSupportedNames(nsTArray<nsString>& aNames)
|
||||
{
|
||||
// We don't know how many params there are, so we can't implement this for
|
||||
// AsyncStatementParams.
|
||||
}
|
||||
|
||||
void
|
||||
AsyncStatementParams::IndexedGetter(JSContext* aCx,
|
||||
uint32_t aIndex,
|
||||
bool& aFound,
|
||||
JS::MutableHandle<JS::Value> aResult,
|
||||
mozilla::ErrorResult& aRv)
|
||||
{
|
||||
if (!mStatement) {
|
||||
aRv.Throw(NS_ERROR_NOT_INITIALIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
// Unfortunately there's no API that lets us return the parameter value.
|
||||
aFound = false;
|
||||
}
|
||||
|
||||
void
|
||||
AsyncStatementParams::IndexedSetter(JSContext* aCx,
|
||||
uint32_t aIndex,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
mozilla::ErrorResult& aRv)
|
||||
{
|
||||
if (!mStatement) {
|
||||
aRv.Throw(NS_ERROR_NOT_INITIALIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIVariant> variant(convertJSValToVariant(aCx, aValue));
|
||||
if (!variant) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
aRv = mStatement->BindByIndex(aIndex, variant);
|
||||
}
|
||||
|
||||
} // namespace storage
|
||||
|
|
|
@ -7,34 +7,68 @@
|
|||
#ifndef mozilla_storage_mozStorageAsyncStatementParams_h_
|
||||
#define mozilla_storage_mozStorageAsyncStatementParams_h_
|
||||
|
||||
#include "mozIStorageStatementParams.h"
|
||||
#include "nsIXPCScriptable.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
class AsyncStatement;
|
||||
|
||||
/*
|
||||
* Since mozIStorageStatementParams is just a tagging interface we do not have
|
||||
* an async variant.
|
||||
*/
|
||||
class AsyncStatementParams final : public mozIStorageStatementParams
|
||||
, public nsIXPCScriptable
|
||||
class AsyncStatementParams final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
explicit AsyncStatementParams(AsyncStatement *aStatement);
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AsyncStatementParams)
|
||||
|
||||
// interfaces
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_MOZISTORAGESTATEMENTPARAMS
|
||||
NS_DECL_NSIXPCSCRIPTABLE
|
||||
explicit AsyncStatementParams(nsPIDOMWindowInner* aWindow, AsyncStatement* aStatement);
|
||||
|
||||
protected:
|
||||
void NamedGetter(JSContext* aCx,
|
||||
const nsAString& aName,
|
||||
bool& aFound,
|
||||
JS::MutableHandle<JS::Value> aResult,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
void NamedSetter(JSContext* aCx,
|
||||
const nsAString& aName,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
uint32_t Length() const {
|
||||
// WebIDL requires a .length property when there's an indexed getter.
|
||||
// Unfortunately we don't know how many params there are in the async case,
|
||||
// so we have to lie.
|
||||
return UINT16_MAX;
|
||||
}
|
||||
|
||||
void IndexedGetter(JSContext* aCx,
|
||||
uint32_t aIndex,
|
||||
bool& aFound,
|
||||
JS::MutableHandle<JS::Value> aResult,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
void IndexedSetter(JSContext* aCx,
|
||||
uint32_t aIndex,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
void GetSupportedNames(nsTArray<nsString>& aNames);
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsPIDOMWindowInner* GetParentObject() const
|
||||
{
|
||||
return mWindow;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~AsyncStatementParams() {}
|
||||
|
||||
AsyncStatement *mStatement;
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
AsyncStatement* mStatement;
|
||||
|
||||
friend class AsyncStatementParamsHolder;
|
||||
};
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче