зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c. a=merge
This commit is contained in:
Коммит
5f0aa6245d
|
@ -86,7 +86,7 @@ var NewPrefDialog = {
|
|||
}
|
||||
|
||||
// If item already in list, it's being changed, else added
|
||||
let item = document.querySelector(".pref-item[name=" + aPrefName.quote() + "]");
|
||||
let item = document.querySelector(".pref-item[name=\"" + CSS.escape(aPrefName) + "\"]");
|
||||
if (item) {
|
||||
this._positiveButton.textContent = gStringBundle.GetStringFromName("newPref.changeButton");
|
||||
} else {
|
||||
|
@ -463,7 +463,7 @@ var AboutConfig = {
|
|||
}
|
||||
|
||||
// If pref not already in list, refresh display as it's being added
|
||||
let item = document.querySelector(".pref-item[name=" + pref.name.quote() + "]");
|
||||
let item = document.querySelector(".pref-item[name=\"" + CSS.escape(pref.name) + "\"]");
|
||||
if (!item) {
|
||||
document.location.reload();
|
||||
return;
|
||||
|
|
|
@ -1103,7 +1103,10 @@ HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode,
|
|||
|
||||
nsAutoString type;
|
||||
if (aSourceNode->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
|
||||
!imgLoader::SupportImageWithMimeType(NS_ConvertUTF16toUTF8(type).get())) {
|
||||
!imgLoader::SupportImageWithMimeType(
|
||||
NS_ConvertUTF16toUTF8(type).get(),
|
||||
AcceptedMimeTypes::IMAGES_AND_DOCUMENTS)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
} else if (aSourceNode->Tag() == nsGkAtoms::img) {
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<html>
|
||||
<body>
|
||||
<picture>
|
||||
<source srcset="lime100x100.svg" type="image/svg+xml">
|
||||
<img src="red.png" width="100" height="100">
|
||||
</picture>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<html>
|
||||
<body>
|
||||
<picture>
|
||||
<source srcset="lime100x100.svg">
|
||||
<img src="red.png" width="100" height="100">
|
||||
</picture>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<html>
|
||||
<body>
|
||||
<img src="lime100x100.svg">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
|
||||
width="100" height="100">
|
||||
<rect width="100%" height="100%" fill="lime"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 141 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 82 B |
|
@ -41,6 +41,10 @@ skip-if(Android||B2G) == 649134-2.html 649134-2-ref.html
|
|||
fuzzy(1,149) == bug917595-iframe-1.html bug917595-1-ref.html
|
||||
skip-if(B2G) fuzzy-if(!B2G,3,640) == bug917595-exif-rotated.jpg bug917595-pixel-rotated.jpg # bug 1060869
|
||||
|
||||
# Test support for SVG-as-image in <picture> elements.
|
||||
pref(dom.image.picture.enabled,true) pref(dom.image.srcset.enabled,true) == bug1106522-1.html bug1106522-ref.html
|
||||
pref(dom.image.picture.enabled,true) pref(dom.image.srcset.enabled,true) == bug1106522-2.html bug1106522-ref.html
|
||||
|
||||
== href-attr-change-restyles.html href-attr-change-restyles-ref.html
|
||||
== figure.html figure-ref.html
|
||||
== pre-1.html pre-1-ref.html
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
using mozilla::dom::CrashReporterChild;
|
||||
|
||||
static const int MAX_PLUGIN_VOUCHER_LENGTH = 500000;
|
||||
static const int MAX_VOUCHER_LENGTH = 500000;
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include <stdlib.h> // for _exit()
|
||||
|
@ -255,6 +255,7 @@ GMPChild::CheckThread()
|
|||
|
||||
bool
|
||||
GMPChild::Init(const std::string& aPluginPath,
|
||||
const std::string& aVoucherPath,
|
||||
base::ProcessHandle aParentProcessHandle,
|
||||
MessageLoop* aIOLoop,
|
||||
IPC::Channel* aChannel)
|
||||
|
@ -268,6 +269,7 @@ GMPChild::Init(const std::string& aPluginPath,
|
|||
#endif
|
||||
|
||||
mPluginPath = aPluginPath;
|
||||
mVoucherPath = aVoucherPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -398,6 +400,7 @@ GMPChild::RecvStartPlugin()
|
|||
PreLoadLibraries(mPluginPath);
|
||||
#endif
|
||||
PreLoadPluginVoucher(mPluginPath);
|
||||
PreLoadSandboxVoucher();
|
||||
|
||||
nsCString libPath;
|
||||
if (!GetLibPath(libPath)) {
|
||||
|
@ -523,7 +526,7 @@ GMPChild::DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor)
|
|||
PGMPDecryptorChild*
|
||||
GMPChild::AllocPGMPDecryptorChild()
|
||||
{
|
||||
GMPDecryptorChild* actor = new GMPDecryptorChild(this, mPluginVoucher);
|
||||
GMPDecryptorChild* actor = new GMPDecryptorChild(this, mPluginVoucher, mSandboxVoucher);
|
||||
actor->AddRef();
|
||||
return actor;
|
||||
}
|
||||
|
@ -730,7 +733,7 @@ GMPChild::PreLoadPluginVoucher(const std::string& aPluginPath)
|
|||
std::streampos end = stream.tellg();
|
||||
stream.seekg (0, std::ios::beg);
|
||||
auto length = end - start;
|
||||
if (length > MAX_PLUGIN_VOUCHER_LENGTH) {
|
||||
if (length > MAX_VOUCHER_LENGTH) {
|
||||
NS_WARNING("Plugin voucher file too big!");
|
||||
return false;
|
||||
}
|
||||
|
@ -745,5 +748,33 @@ GMPChild::PreLoadPluginVoucher(const std::string& aPluginPath)
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
GMPChild::PreLoadSandboxVoucher()
|
||||
{
|
||||
std::ifstream stream;
|
||||
stream.open(mVoucherPath.c_str(), std::ios::binary);
|
||||
if (!stream.good()) {
|
||||
NS_WARNING("PreLoadSandboxVoucher can't find sandbox voucher file!");
|
||||
return;
|
||||
}
|
||||
|
||||
std::streampos start = stream.tellg();
|
||||
stream.seekg (0, std::ios::end);
|
||||
std::streampos end = stream.tellg();
|
||||
stream.seekg (0, std::ios::beg);
|
||||
auto length = end - start;
|
||||
if (length > MAX_VOUCHER_LENGTH) {
|
||||
NS_WARNING("PreLoadSandboxVoucher sandbox voucher file too big!");
|
||||
return;
|
||||
}
|
||||
|
||||
mSandboxVoucher.SetLength(length);
|
||||
stream.read((char*)mSandboxVoucher.Elements(), length);
|
||||
if (!stream) {
|
||||
NS_WARNING("PreLoadSandboxVoucher failed to read plugin voucher file!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
virtual ~GMPChild();
|
||||
|
||||
bool Init(const std::string& aPluginPath,
|
||||
const std::string& aVoucherPath,
|
||||
base::ProcessHandle aParentProcessHandle,
|
||||
MessageLoop* aIOLoop,
|
||||
IPC::Channel* aChannel);
|
||||
|
@ -52,6 +53,7 @@ public:
|
|||
private:
|
||||
|
||||
bool PreLoadPluginVoucher(const std::string& aPluginPath);
|
||||
void PreLoadSandboxVoucher();
|
||||
|
||||
bool GetLibPath(nsACString& aOutLibPath);
|
||||
|
||||
|
@ -97,12 +99,14 @@ private:
|
|||
|
||||
MessageLoop* mGMPMessageLoop;
|
||||
std::string mPluginPath;
|
||||
std::string mVoucherPath;
|
||||
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
|
||||
nsCString mPluginBinaryPath;
|
||||
#endif
|
||||
std::string mNodeId;
|
||||
GMPLoader* mGMPLoader;
|
||||
nsTArray<uint8_t> mPluginVoucher;
|
||||
nsTArray<uint8_t> mSandboxVoucher;
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
|
|
@ -27,10 +27,12 @@ namespace mozilla {
|
|||
namespace gmp {
|
||||
|
||||
GMPDecryptorChild::GMPDecryptorChild(GMPChild* aPlugin,
|
||||
const nsTArray<uint8_t>& aPluginVoucher)
|
||||
const nsTArray<uint8_t>& aPluginVoucher,
|
||||
const nsTArray<uint8_t>& aSandboxVoucher)
|
||||
: mSession(nullptr)
|
||||
, mPlugin(aPlugin)
|
||||
, mPluginVoucher(aPluginVoucher)
|
||||
, mSandboxVoucher(aSandboxVoucher)
|
||||
{
|
||||
MOZ_ASSERT(mPlugin);
|
||||
}
|
||||
|
@ -181,9 +183,8 @@ GMPDecryptorChild::GetSandboxVoucher(const uint8_t** aVoucher,
|
|||
if (!aVoucher || !aVoucherLength) {
|
||||
return;
|
||||
}
|
||||
const char* voucher = "placeholder_sandbox_voucher.";
|
||||
*aVoucher = (uint8_t*)voucher;
|
||||
*aVoucherLength = strlen(voucher);
|
||||
*aVoucher = mSandboxVoucher.Elements();
|
||||
*aVoucherLength = mSandboxVoucher.Length();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -25,7 +25,8 @@ public:
|
|||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPDecryptorChild);
|
||||
|
||||
explicit GMPDecryptorChild(GMPChild* aPlugin,
|
||||
const nsTArray<uint8_t>& aPluginVoucher);
|
||||
const nsTArray<uint8_t>& aPluginVoucher,
|
||||
const nsTArray<uint8_t>& aSandboxVoucher);
|
||||
|
||||
void Init(GMPDecryptor* aSession);
|
||||
|
||||
|
@ -122,8 +123,9 @@ private:
|
|||
GMPDecryptor* mSession;
|
||||
GMPChild* mPlugin;
|
||||
|
||||
// Reference to the voucher owned by the GMPChild.
|
||||
// Reference to the vouchers owned by the GMPChild.
|
||||
const nsTArray<uint8_t>& mPluginVoucher;
|
||||
const nsTArray<uint8_t>& mSandboxVoucher;
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
|
|
@ -29,18 +29,21 @@ bool
|
|||
GMPProcessChild::Init()
|
||||
{
|
||||
std::string pluginFilename;
|
||||
std::string voucherFilename;
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
// NB: need to be very careful in ensuring that the first arg
|
||||
// (after the binary name) here is indeed the plugin module path.
|
||||
// Keep in sync with dom/plugins/PluginModuleParent.
|
||||
std::vector<std::string> values = CommandLine::ForCurrentProcess()->argv();
|
||||
NS_ABORT_IF_FALSE(values.size() >= 2, "not enough args");
|
||||
NS_ABORT_IF_FALSE(values.size() >= 3, "not enough args");
|
||||
pluginFilename = values[1];
|
||||
voucherFilename = values[2];
|
||||
#elif defined(OS_WIN)
|
||||
std::vector<std::wstring> values = CommandLine::ForCurrentProcess()->GetLooseValues();
|
||||
NS_ABORT_IF_FALSE(values.size() >= 1, "not enough loose args");
|
||||
NS_ABORT_IF_FALSE(values.size() >= 2, "not enough loose args");
|
||||
pluginFilename = WideToUTF8(values[0]);
|
||||
voucherFilename = WideToUTF8(values[1]);
|
||||
#else
|
||||
#error Not implemented
|
||||
#endif
|
||||
|
@ -48,6 +51,7 @@ GMPProcessChild::Init()
|
|||
BackgroundHangMonitor::Startup();
|
||||
|
||||
return mPlugin.Init(pluginFilename,
|
||||
voucherFilename,
|
||||
ParentHandle(),
|
||||
IOThreadChild::message_loop(),
|
||||
IOThreadChild::channel());
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "GMPProcessParent.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsIFile.h"
|
||||
|
||||
#include "base/string_util.h"
|
||||
#include "base/process_util.h"
|
||||
|
@ -43,8 +45,19 @@ GMPProcessParent::~GMPProcessParent()
|
|||
bool
|
||||
GMPProcessParent::Launch(int32_t aTimeoutMs)
|
||||
{
|
||||
nsCOMPtr<nsIFile> greDir;
|
||||
NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir));
|
||||
if (!greDir) {
|
||||
NS_WARNING("GMPProcessParent can't get NS_GRE_DIR");
|
||||
return false;
|
||||
}
|
||||
greDir->AppendNative(NS_LITERAL_CSTRING("voucher.bin"));
|
||||
nsAutoCString voucherPath;
|
||||
greDir->GetNativePath(voucherPath);
|
||||
|
||||
vector<string> args;
|
||||
args.push_back(mGMPPath);
|
||||
args.push_back(string(voucherPath.BeginReading(), voucherPath.EndReading()));
|
||||
|
||||
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
||||
std::wstring wGMPPath = UTF8ToWide(mGMPPath.c_str());
|
||||
|
|
|
@ -2728,6 +2728,9 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
|||
bool scrollOffsetUpdated = aLayerMetrics.GetScrollOffsetUpdated()
|
||||
&& (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
|
||||
|
||||
bool smoothScrollRequested = aLayerMetrics.GetDoSmoothScroll()
|
||||
&& (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
|
||||
|
||||
if (aIsFirstPaint || isDefault) {
|
||||
// Initialize our internal state to something sane when the content
|
||||
// that was just painted is something we knew nothing about previously
|
||||
|
@ -2749,9 +2752,6 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
|||
needContentRepaint = true;
|
||||
}
|
||||
} else {
|
||||
bool smoothScrollRequested = aLayerMetrics.GetDoSmoothScroll()
|
||||
&& (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
|
||||
|
||||
// If we're not taking the aLayerMetrics wholesale we still need to pull
|
||||
// in some things into our local mFrameMetrics because these things are
|
||||
// determined by Gecko and our copy in mFrameMetrics may be stale.
|
||||
|
@ -2805,23 +2805,23 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
|||
// last thing we know was painted by Gecko.
|
||||
mLastDispatchedPaintMetrics = aLayerMetrics;
|
||||
}
|
||||
}
|
||||
|
||||
if (smoothScrollRequested) {
|
||||
// A smooth scroll has been requested for animation on the compositor
|
||||
// thread. This flag will be reset by the main thread when it receives
|
||||
// the scroll update acknowledgement.
|
||||
if (smoothScrollRequested) {
|
||||
// A smooth scroll has been requested for animation on the compositor
|
||||
// thread. This flag will be reset by the main thread when it receives
|
||||
// the scroll update acknowledgement.
|
||||
|
||||
APZC_LOG("%p smooth scrolling from %s to %s\n", this,
|
||||
Stringify(mFrameMetrics.GetScrollOffset()).c_str(),
|
||||
Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str());
|
||||
APZC_LOG("%p smooth scrolling from %s to %s\n", this,
|
||||
Stringify(mFrameMetrics.GetScrollOffset()).c_str(),
|
||||
Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str());
|
||||
|
||||
mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics);
|
||||
CancelAnimation();
|
||||
mLastDispatchedPaintMetrics = aLayerMetrics;
|
||||
StartSmoothScroll();
|
||||
mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics);
|
||||
CancelAnimation();
|
||||
mLastDispatchedPaintMetrics = aLayerMetrics;
|
||||
StartSmoothScroll();
|
||||
|
||||
scrollOffsetUpdated = true; // Ensure that AcknowledgeScrollUpdate is called
|
||||
}
|
||||
scrollOffsetUpdated = true; // Ensure that AcknowledgeScrollUpdate is called
|
||||
}
|
||||
|
||||
if (scrollOffsetUpdated) {
|
||||
|
|
|
@ -2304,10 +2304,19 @@ nsresult imgLoader::LoadImageWithChannel(nsIChannel *channel, imgINotificationOb
|
|||
return rv;
|
||||
}
|
||||
|
||||
bool imgLoader::SupportImageWithMimeType(const char* aMimeType)
|
||||
bool
|
||||
imgLoader::SupportImageWithMimeType(const char* aMimeType,
|
||||
AcceptedMimeTypes aAccept
|
||||
/* = AcceptedMimeTypes::IMAGES */)
|
||||
{
|
||||
nsAutoCString mimeType(aMimeType);
|
||||
ToLowerCase(mimeType);
|
||||
|
||||
if (aAccept == AcceptedMimeTypes::IMAGES_AND_DOCUMENTS &&
|
||||
mimeType.EqualsLiteral("image/svg+xml")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Image::GetDecoderType(mimeType.get()) != Image::eDecoderType_unknown;
|
||||
}
|
||||
|
||||
|
|
|
@ -206,6 +206,11 @@ private:
|
|||
uint32_t mSize;
|
||||
};
|
||||
|
||||
MOZ_BEGIN_ENUM_CLASS(AcceptedMimeTypes, uint8_t)
|
||||
IMAGES,
|
||||
IMAGES_AND_DOCUMENTS,
|
||||
MOZ_END_ENUM_CLASS(AcceptedMimeTypes)
|
||||
|
||||
class imgLoader MOZ_FINAL : public imgILoader,
|
||||
public nsIContentSniffer,
|
||||
public imgICache,
|
||||
|
@ -271,8 +276,22 @@ public:
|
|||
imgRequestProxy **_retval);
|
||||
|
||||
static nsresult GetMimeTypeFromContent(const char* aContents, uint32_t aLength, nsACString& aContentType);
|
||||
// exported for use by mimei.cpp in libxul sdk builds
|
||||
static NS_EXPORT_(bool) SupportImageWithMimeType(const char* aMimeType);
|
||||
|
||||
/**
|
||||
* Returns true if the given mime type may be interpreted as an image.
|
||||
*
|
||||
* Some MIME types may be interpreted as both images and documents. (At the
|
||||
* moment only "image/svg+xml" falls into this category, but there may be more
|
||||
* in the future.) Callers which want this function to return true for such
|
||||
* MIME types should pass AcceptedMimeTypes::IMAGES_AND_DOCUMENTS for @aAccept.
|
||||
*
|
||||
* @param aMimeType The MIME type to evaluate.
|
||||
* @param aAcceptedMimeTypes Which kinds of MIME types to treat as images.
|
||||
*/
|
||||
static NS_EXPORT_(bool)
|
||||
SupportImageWithMimeType(const char* aMimeType,
|
||||
AcceptedMimeTypes aAccept =
|
||||
AcceptedMimeTypes::IMAGES);
|
||||
|
||||
static void GlobalInit(); // for use by the factory
|
||||
static void Shutdown(); // for use by the factory
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/Interpreter-inl.h"
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
@ -1237,11 +1238,24 @@ MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!args.get(0).isNullOrUndefined()) {
|
||||
RootedValue adderVal(cx);
|
||||
if (!JSObject::getProperty(cx, obj, obj, cx->names().set, &adderVal))
|
||||
return false;
|
||||
|
||||
if (!IsCallable(adderVal))
|
||||
return ReportIsNotFunction(cx, adderVal);
|
||||
|
||||
bool isOriginalAdder = IsNativeFunction(adderVal, MapObject::set);
|
||||
RootedValue mapVal(cx, ObjectValue(*obj));
|
||||
FastInvokeGuard fig(cx, adderVal);
|
||||
InvokeArgs &args2 = fig.args();
|
||||
|
||||
ForOfIterator iter(cx);
|
||||
if (!iter.init(args[0]))
|
||||
return false;
|
||||
RootedValue pairVal(cx);
|
||||
RootedObject pairObj(cx);
|
||||
AutoHashableValueRooter hkey(cx);
|
||||
ValueMap *map = obj->getData();
|
||||
while (true) {
|
||||
bool done;
|
||||
|
@ -1263,20 +1277,32 @@ MapObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
if (!JSObject::getElement(cx, pairObj, pairObj, 0, &key))
|
||||
return false;
|
||||
|
||||
AutoHashableValueRooter hkey(cx);
|
||||
if (!hkey.setValue(cx, key))
|
||||
return false;
|
||||
|
||||
RootedValue val(cx);
|
||||
if (!JSObject::getElement(cx, pairObj, pairObj, 1, &val))
|
||||
return false;
|
||||
|
||||
RelocatableValue rval(val);
|
||||
if (!map->put(hkey, rval)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
if (isOriginalAdder) {
|
||||
if (!hkey.setValue(cx, key))
|
||||
return false;
|
||||
|
||||
RelocatableValue rval(val);
|
||||
if (!map->put(hkey, rval)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
WriteBarrierPost(cx->runtime(), map, key);
|
||||
} else {
|
||||
if (!args2.init(2))
|
||||
return false;
|
||||
|
||||
args2.setCallee(adderVal);
|
||||
args2.setThis(mapVal);
|
||||
args2[0].set(key);
|
||||
args2[1].set(val);
|
||||
|
||||
if (!fig.invoke(cx))
|
||||
return false;
|
||||
}
|
||||
WriteBarrierPost(cx->runtime(), map, key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1801,6 +1827,18 @@ SetObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!args.get(0).isNullOrUndefined()) {
|
||||
RootedValue adderVal(cx);
|
||||
if (!JSObject::getProperty(cx, obj, obj, cx->names().add, &adderVal))
|
||||
return false;
|
||||
|
||||
if (!IsCallable(adderVal))
|
||||
return ReportIsNotFunction(cx, adderVal);
|
||||
|
||||
bool isOriginalAdder = IsNativeFunction(adderVal, SetObject::add);
|
||||
RootedValue setVal(cx, ObjectValue(*obj));
|
||||
FastInvokeGuard fig(cx, adderVal);
|
||||
InvokeArgs &args2 = fig.args();
|
||||
|
||||
RootedValue keyVal(cx);
|
||||
ForOfIterator iter(cx);
|
||||
if (!iter.init(args[0]))
|
||||
|
@ -1813,13 +1851,26 @@ SetObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
return false;
|
||||
if (done)
|
||||
break;
|
||||
if (!key.setValue(cx, keyVal))
|
||||
return false;
|
||||
if (!set->put(key)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
|
||||
if (isOriginalAdder) {
|
||||
if (!key.setValue(cx, keyVal))
|
||||
return false;
|
||||
if (!set->put(key)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
WriteBarrierPost(cx->runtime(), set, keyVal);
|
||||
} else {
|
||||
if (!args2.init(1))
|
||||
return false;
|
||||
|
||||
args2.setCallee(adderVal);
|
||||
args2.setThis(setVal);
|
||||
args2[0].set(keyVal);
|
||||
|
||||
if (!fig.invoke(cx))
|
||||
return false;
|
||||
}
|
||||
WriteBarrierPost(cx->runtime(), set, keyVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,11 @@
|
|||
|
||||
#include "builtin/SelfHostingDefines.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/Interpreter-inl.h"
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
@ -98,6 +100,20 @@ WeakSetObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
if (!args.get(0).isNullOrUndefined()) {
|
||||
RootedObject map(cx, &obj->getReservedSlot(WEAKSET_MAP_SLOT).toObject());
|
||||
|
||||
RootedValue adderVal(cx);
|
||||
if (!JSObject::getProperty(cx, obj, obj, cx->names().add, &adderVal))
|
||||
return false;
|
||||
|
||||
if (!IsCallable(adderVal))
|
||||
return ReportIsNotFunction(cx, adderVal);
|
||||
|
||||
JSFunction *adder;
|
||||
bool isOriginalAdder = IsFunctionObject(adderVal, &adder) &&
|
||||
IsSelfHostedFunctionWithName(adder, cx->names().WeakSet_add);
|
||||
RootedValue setVal(cx, ObjectValue(*obj));
|
||||
FastInvokeGuard fig(cx, adderVal);
|
||||
InvokeArgs &args2 = fig.args();
|
||||
|
||||
JS::ForOfIterator iter(cx);
|
||||
if (!iter.init(args[0]))
|
||||
return false;
|
||||
|
@ -112,14 +128,26 @@ WeakSetObject::construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
if (done)
|
||||
break;
|
||||
|
||||
if (keyVal.isPrimitive()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
|
||||
return false;
|
||||
}
|
||||
if (isOriginalAdder) {
|
||||
if (keyVal.isPrimitive()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
keyObject = &keyVal.toObject();
|
||||
if (!SetWeakMapEntry(cx, map, keyObject, placeholder))
|
||||
return false;
|
||||
keyObject = &keyVal.toObject();
|
||||
if (!SetWeakMapEntry(cx, map, keyObject, placeholder))
|
||||
return false;
|
||||
} else {
|
||||
if (!args2.init(1))
|
||||
return false;
|
||||
|
||||
args2.setCallee(adderVal);
|
||||
args2.setThis(setVal);
|
||||
args2[0].set(keyVal);
|
||||
|
||||
if (!fig.invoke(cx))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
load(libdir + "asserts.js");
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
var k1 = {};
|
||||
var v1 = 42;
|
||||
var k2 = {};
|
||||
var v2 = 42;
|
||||
var k3 = {};
|
||||
var v3 = 43;
|
||||
var k4 = {};
|
||||
var v4 = 44;
|
||||
|
||||
function test_patched() {
|
||||
let orig = Map.prototype.set;
|
||||
|
||||
// If adder is modified, constructor should call it.
|
||||
var called = false;
|
||||
|
||||
Map.prototype.set = function(k, v) {
|
||||
assertEq(k, k1);
|
||||
assertEq(v, v1);
|
||||
orig.call(this, k2, v2);
|
||||
called = true;
|
||||
};
|
||||
|
||||
var arr = [[k1, v1]];
|
||||
|
||||
var m = new Map(arr);
|
||||
|
||||
assertEq(called, true);
|
||||
assertEq(m.size, 1);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), true);
|
||||
assertEq(m.get(k1), undefined);
|
||||
assertEq(m.get(k2), v2);
|
||||
|
||||
Map.prototype.set = orig;
|
||||
}
|
||||
|
||||
function test_proxy1() {
|
||||
let orig = Map.prototype.set;
|
||||
|
||||
// If adder is modified, constructor should call it.
|
||||
var called = false;
|
||||
|
||||
Map.prototype.set = new Proxy(function(k, v) {
|
||||
assertEq(k, k1);
|
||||
assertEq(v, v1);
|
||||
orig.call(this, k2, v2);
|
||||
called = true;
|
||||
}, {});
|
||||
|
||||
var arr = [[k1, v1]];
|
||||
|
||||
var m = new Map(arr);
|
||||
|
||||
assertEq(called, true);
|
||||
assertEq(m.size, 1);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), true);
|
||||
assertEq(m.get(k1), undefined);
|
||||
assertEq(m.get(k2), v2);
|
||||
|
||||
Map.prototype.set = orig;
|
||||
}
|
||||
|
||||
function test_proxy2() {
|
||||
let orig = Map.prototype.set;
|
||||
|
||||
// If adder is modified, constructor should call it.
|
||||
var called = false;
|
||||
|
||||
Map.prototype.set = new Proxy(function() {
|
||||
}, {
|
||||
apply: function(target, that, args) {
|
||||
var [k, v] = args;
|
||||
assertEq(k, k1);
|
||||
assertEq(v, v1);
|
||||
orig.call(that, k2, v2);
|
||||
called = true;
|
||||
}
|
||||
});
|
||||
|
||||
var arr = [[k1, v1]];
|
||||
|
||||
var m = new Map(arr);
|
||||
|
||||
assertEq(called, true);
|
||||
assertEq(m.size, 1);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), true);
|
||||
assertEq(m.get(k1), undefined);
|
||||
assertEq(m.get(k2), v2);
|
||||
|
||||
Map.prototype.set = orig;
|
||||
}
|
||||
|
||||
function test_change1() {
|
||||
let orig = Map.prototype.set;
|
||||
|
||||
// Change to adder in GetIterator(..) call should be ignored.
|
||||
var called = false;
|
||||
var modified = false;
|
||||
|
||||
var arr = [[k1, v1]];
|
||||
|
||||
var proxy_arr = new Proxy(arr, {
|
||||
get: function(target, name) {
|
||||
if (name == std_iterator) {
|
||||
modified = true;
|
||||
Map.prototype.set = function() {
|
||||
called = true;
|
||||
};
|
||||
}
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
|
||||
var m = new Map(proxy_arr);
|
||||
|
||||
assertEq(modified, true);
|
||||
assertEq(called, false);
|
||||
assertEq(m.size, 1);
|
||||
assertEq(m.has(k1), true);
|
||||
assertEq(m.has(k2), false);
|
||||
assertEq(m.get(k1), v1);
|
||||
assertEq(m.get(k2), undefined);
|
||||
|
||||
Map.prototype.set = orig;
|
||||
}
|
||||
|
||||
function test_change2() {
|
||||
let orig = Map.prototype.set;
|
||||
|
||||
// Change to adder in adder(...) call should be ignored.
|
||||
var called = false;
|
||||
var count = 0;
|
||||
|
||||
Map.prototype.set = function(k, v) {
|
||||
if (count == 0) {
|
||||
assertEq(k, k1);
|
||||
assertEq(v, v1);
|
||||
orig.call(this, k3, v3);
|
||||
Map.prototype.set = function() {
|
||||
called = true;
|
||||
};
|
||||
count = 1;
|
||||
} else {
|
||||
assertEq(k, k2);
|
||||
assertEq(v, v2);
|
||||
orig.call(this, k4, v4);
|
||||
count = 2;
|
||||
}
|
||||
};
|
||||
|
||||
var arr = [[k1, v1], [k2, v2]];
|
||||
|
||||
var m = new Map(arr);
|
||||
|
||||
assertEq(called, false);
|
||||
assertEq(count, 2);
|
||||
assertEq(m.size, 2);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), false);
|
||||
assertEq(m.has(k3), true);
|
||||
assertEq(m.has(k4), true);
|
||||
assertEq(m.get(k1), undefined);
|
||||
assertEq(m.get(k2), undefined);
|
||||
assertEq(m.get(k3), v3);
|
||||
assertEq(m.get(k4), v4);
|
||||
|
||||
Map.prototype.set = orig;
|
||||
}
|
||||
|
||||
function test_error() {
|
||||
let orig = Map.prototype.set;
|
||||
|
||||
var arr = [[k1, v1]];
|
||||
|
||||
// Map should throw TypeError if adder is not callable.
|
||||
Map.prototype.set = null;
|
||||
assertThrowsInstanceOf(() => new Map(arr), TypeError);
|
||||
Map.prototype.set = {};
|
||||
assertThrowsInstanceOf(() => new Map(arr), TypeError);
|
||||
|
||||
// Map should propagate error thrown by adder.
|
||||
Map.prototype.set = function() {
|
||||
throw SyntaxError();
|
||||
};
|
||||
assertThrowsInstanceOf(() => new Map(arr), SyntaxError);
|
||||
|
||||
Map.prototype.set = orig;
|
||||
}
|
||||
|
||||
function test() {
|
||||
test_patched();
|
||||
test_proxy1();
|
||||
test_proxy2();
|
||||
test_change1();
|
||||
test_change2();
|
||||
test_error();
|
||||
}
|
||||
|
||||
test();
|
|
@ -0,0 +1,183 @@
|
|||
load(libdir + "asserts.js");
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
var k1 = {};
|
||||
var k2 = {};
|
||||
var k3 = {};
|
||||
var k4 = {};
|
||||
|
||||
function test_patched() {
|
||||
let orig = Set.prototype.add;
|
||||
|
||||
// If adder is modified, constructor should call it.
|
||||
var called = false;
|
||||
|
||||
Set.prototype.add = function(k, v) {
|
||||
assertEq(k, k1);
|
||||
orig.call(this, k2);
|
||||
called = true;
|
||||
};
|
||||
|
||||
var arr = [k1];
|
||||
|
||||
var m = new Set(arr);
|
||||
|
||||
assertEq(called, true);
|
||||
assertEq(m.size, 1);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), true);
|
||||
|
||||
Set.prototype.add = orig;
|
||||
}
|
||||
|
||||
function test_proxy1() {
|
||||
let orig = Set.prototype.add;
|
||||
|
||||
// If adder is modified, constructor should call it.
|
||||
var called = false;
|
||||
|
||||
Set.prototype.add = new Proxy(function(k, v) {
|
||||
assertEq(k, k1);
|
||||
orig.call(this, k2);
|
||||
called = true;
|
||||
}, {});
|
||||
|
||||
var arr = [k1];
|
||||
|
||||
var m = new Set(arr);
|
||||
|
||||
assertEq(called, true);
|
||||
assertEq(m.size, 1);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), true);
|
||||
|
||||
Set.prototype.add = orig;
|
||||
}
|
||||
|
||||
function test_proxy2() {
|
||||
let orig = Set.prototype.add;
|
||||
|
||||
// If adder is modified, constructor should call it.
|
||||
var called = false;
|
||||
|
||||
Set.prototype.add = new Proxy(function() {
|
||||
}, {
|
||||
apply: function(target, that, args) {
|
||||
var [k, v] = args;
|
||||
assertEq(k, k1);
|
||||
orig.call(that, k2);
|
||||
called = true;
|
||||
}
|
||||
});
|
||||
|
||||
var arr = [k1];
|
||||
|
||||
var m = new Set(arr);
|
||||
|
||||
assertEq(called, true);
|
||||
assertEq(m.size, 1);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), true);
|
||||
|
||||
Set.prototype.add = orig;
|
||||
}
|
||||
|
||||
function test_change1() {
|
||||
let orig = Set.prototype.add;
|
||||
|
||||
// Change to adder in GetIterator(..) call should be ignored.
|
||||
var called = false;
|
||||
var modified = false;
|
||||
|
||||
var arr = [k1];
|
||||
|
||||
var proxy_arr = new Proxy(arr, {
|
||||
get: function(target, name) {
|
||||
if (name == std_iterator) {
|
||||
modified = true;
|
||||
Set.prototype.add = function() {
|
||||
called = true;
|
||||
};
|
||||
}
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
|
||||
var m = new Set(proxy_arr);
|
||||
|
||||
assertEq(modified, true);
|
||||
assertEq(called, false);
|
||||
assertEq(m.size, 1);
|
||||
assertEq(m.has(k1), true);
|
||||
assertEq(m.has(k2), false);
|
||||
|
||||
Set.prototype.add = orig;
|
||||
}
|
||||
|
||||
function test_change2() {
|
||||
let orig = Set.prototype.add;
|
||||
|
||||
// Change to adder in adder(...) call should be ignored.
|
||||
var called = false;
|
||||
var count = 0;
|
||||
|
||||
Set.prototype.add = function(k, v) {
|
||||
if (count == 0) {
|
||||
assertEq(k, k1);
|
||||
orig.call(this, k3);
|
||||
Set.prototype.add = function() {
|
||||
called = true;
|
||||
};
|
||||
count = 1;
|
||||
} else {
|
||||
assertEq(k, k2);
|
||||
orig.call(this, k4);
|
||||
count = 2;
|
||||
}
|
||||
};
|
||||
|
||||
var arr = [k1, k2];
|
||||
|
||||
var m = new Set(arr);
|
||||
|
||||
assertEq(called, false);
|
||||
assertEq(count, 2);
|
||||
assertEq(m.size, 2);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), false);
|
||||
assertEq(m.has(k3), true);
|
||||
assertEq(m.has(k4), true);
|
||||
|
||||
Set.prototype.add = orig;
|
||||
}
|
||||
|
||||
function test_error() {
|
||||
let orig = Set.prototype.add;
|
||||
|
||||
var arr = [k1];
|
||||
|
||||
// Set should throw TypeError if adder is not callable.
|
||||
Set.prototype.add = null;
|
||||
assertThrowsInstanceOf(() => new Set(arr), TypeError);
|
||||
Set.prototype.add = {};
|
||||
assertThrowsInstanceOf(() => new Set(arr), TypeError);
|
||||
|
||||
// Set should propagate error thrown by adder.
|
||||
Set.prototype.add = function() {
|
||||
throw SyntaxError();
|
||||
};
|
||||
assertThrowsInstanceOf(() => new Set(arr), SyntaxError);
|
||||
|
||||
Set.prototype.add = orig;
|
||||
}
|
||||
|
||||
function test() {
|
||||
test_patched();
|
||||
test_proxy1();
|
||||
test_proxy2();
|
||||
test_change1();
|
||||
test_change2();
|
||||
test_error();
|
||||
}
|
||||
|
||||
test();
|
|
@ -0,0 +1,199 @@
|
|||
load(libdir + "asserts.js");
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
var k1 = {};
|
||||
var v1 = 42;
|
||||
var k2 = {};
|
||||
var v2 = 42;
|
||||
var k3 = {};
|
||||
var v3 = 43;
|
||||
var k4 = {};
|
||||
var v4 = 44;
|
||||
|
||||
function test_patched() {
|
||||
let orig = WeakMap.prototype.set;
|
||||
|
||||
// If adder is modified, constructor should call it.
|
||||
var called = false;
|
||||
|
||||
WeakMap.prototype.set = function(k, v) {
|
||||
assertEq(k, k1);
|
||||
assertEq(v, v1);
|
||||
orig.call(this, k2, v2);
|
||||
called = true;
|
||||
};
|
||||
|
||||
var arr = [[k1, v1]];
|
||||
|
||||
var m = new WeakMap(arr);
|
||||
|
||||
assertEq(called, true);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), true);
|
||||
assertEq(m.get(k1), undefined);
|
||||
assertEq(m.get(k2), v2);
|
||||
|
||||
WeakMap.prototype.set = orig;
|
||||
}
|
||||
|
||||
function test_proxy1() {
|
||||
let orig = WeakMap.prototype.set;
|
||||
|
||||
// If adder is modified, constructor should call it.
|
||||
var called = false;
|
||||
|
||||
WeakMap.prototype.set = new Proxy(function(k, v) {
|
||||
assertEq(k, k1);
|
||||
assertEq(v, v1);
|
||||
orig.call(this, k2, v2);
|
||||
called = true;
|
||||
}, {});
|
||||
|
||||
var arr = [[k1, v1]];
|
||||
|
||||
var m = new WeakMap(arr);
|
||||
|
||||
assertEq(called, true);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), true);
|
||||
assertEq(m.get(k1), undefined);
|
||||
assertEq(m.get(k2), v2);
|
||||
|
||||
WeakMap.prototype.set = orig;
|
||||
}
|
||||
|
||||
function test_proxy2() {
|
||||
let orig = WeakMap.prototype.set;
|
||||
|
||||
// If adder is modified, constructor should call it.
|
||||
var called = false;
|
||||
|
||||
WeakMap.prototype.set = new Proxy(function() {
|
||||
}, {
|
||||
apply: function(target, that, args) {
|
||||
var [k, v] = args;
|
||||
assertEq(k, k1);
|
||||
assertEq(v, v1);
|
||||
orig.call(that, k2, v2);
|
||||
called = true;
|
||||
}
|
||||
});
|
||||
|
||||
var arr = [[k1, v1]];
|
||||
|
||||
var m = new WeakMap(arr);
|
||||
|
||||
assertEq(called, true);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), true);
|
||||
assertEq(m.get(k1), undefined);
|
||||
assertEq(m.get(k2), v2);
|
||||
|
||||
WeakMap.prototype.set = orig;
|
||||
}
|
||||
|
||||
function test_change1() {
|
||||
let orig = WeakMap.prototype.set;
|
||||
|
||||
// Change to adder in GetIterator(..) call should be ignored.
|
||||
var called = false;
|
||||
var modified = false;
|
||||
|
||||
var arr = [[k1, v1]];
|
||||
|
||||
var proxy_arr = new Proxy(arr, {
|
||||
get: function(target, name) {
|
||||
if (name == std_iterator) {
|
||||
modified = true;
|
||||
WeakMap.prototype.set = function() {
|
||||
called = true;
|
||||
};
|
||||
}
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
|
||||
var m = new WeakMap(proxy_arr);
|
||||
|
||||
assertEq(modified, true);
|
||||
assertEq(called, false);
|
||||
assertEq(m.has(k1), true);
|
||||
assertEq(m.has(k2), false);
|
||||
assertEq(m.get(k1), v1);
|
||||
assertEq(m.get(k2), undefined);
|
||||
|
||||
WeakMap.prototype.set = orig;
|
||||
}
|
||||
|
||||
function test_change2() {
|
||||
let orig = WeakMap.prototype.set;
|
||||
|
||||
// Change to adder in adder(...) call should be ignored.
|
||||
var called = false;
|
||||
var count = 0;
|
||||
|
||||
WeakMap.prototype.set = function(k, v) {
|
||||
if (count == 0) {
|
||||
assertEq(k, k1);
|
||||
assertEq(v, v1);
|
||||
orig.call(this, k3, v3);
|
||||
WeakMap.prototype.set = function() {
|
||||
called = true;
|
||||
};
|
||||
count = 1;
|
||||
} else {
|
||||
assertEq(k, k2);
|
||||
assertEq(v, v2);
|
||||
orig.call(this, k4, v4);
|
||||
count = 2;
|
||||
}
|
||||
};
|
||||
|
||||
var arr = [[k1, v1], [k2, v2]];
|
||||
|
||||
var m = new WeakMap(arr);
|
||||
|
||||
assertEq(called, false);
|
||||
assertEq(count, 2);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), false);
|
||||
assertEq(m.has(k3), true);
|
||||
assertEq(m.has(k4), true);
|
||||
assertEq(m.get(k1), undefined);
|
||||
assertEq(m.get(k2), undefined);
|
||||
assertEq(m.get(k3), v3);
|
||||
assertEq(m.get(k4), v4);
|
||||
|
||||
WeakMap.prototype.set = orig;
|
||||
}
|
||||
|
||||
function test_error() {
|
||||
let orig = WeakMap.prototype.set;
|
||||
|
||||
var arr = [[k1, v1]];
|
||||
|
||||
// WeakMap should throw TypeError if adder is not callable.
|
||||
WeakMap.prototype.set = null;
|
||||
assertThrowsInstanceOf(() => new WeakMap(arr), TypeError);
|
||||
WeakMap.prototype.set = {};
|
||||
assertThrowsInstanceOf(() => new WeakMap(arr), TypeError);
|
||||
|
||||
// WeakMap should propagate error thrown by adder.
|
||||
WeakMap.prototype.set = function() {
|
||||
throw SyntaxError();
|
||||
};
|
||||
assertThrowsInstanceOf(() => new WeakMap(arr), SyntaxError);
|
||||
|
||||
WeakMap.prototype.set = orig;
|
||||
}
|
||||
|
||||
function test() {
|
||||
test_patched();
|
||||
test_proxy1();
|
||||
test_proxy2();
|
||||
test_change1();
|
||||
test_change2();
|
||||
test_error();
|
||||
}
|
||||
|
||||
test();
|
|
@ -0,0 +1,178 @@
|
|||
load(libdir + "asserts.js");
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
var k1 = {};
|
||||
var k2 = {};
|
||||
var k3 = {};
|
||||
var k4 = {};
|
||||
|
||||
function test_patched() {
|
||||
let orig = WeakSet.prototype.add;
|
||||
|
||||
// If adder is modified, constructor should call it.
|
||||
var called = false;
|
||||
|
||||
WeakSet.prototype.add = function(k, v) {
|
||||
assertEq(k, k1);
|
||||
orig.call(this, k2);
|
||||
called = true;
|
||||
};
|
||||
|
||||
var arr = [k1];
|
||||
|
||||
var m = new WeakSet(arr);
|
||||
|
||||
assertEq(called, true);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), true);
|
||||
|
||||
WeakSet.prototype.add = orig;
|
||||
}
|
||||
|
||||
function test_proxy1() {
|
||||
let orig = WeakSet.prototype.add;
|
||||
|
||||
// If adder is modified, constructor should call it.
|
||||
var called = false;
|
||||
|
||||
WeakSet.prototype.add = new Proxy(function(k, v) {
|
||||
assertEq(k, k1);
|
||||
orig.call(this, k2);
|
||||
called = true;
|
||||
}, {});
|
||||
|
||||
var arr = [k1];
|
||||
|
||||
var m = new WeakSet(arr);
|
||||
|
||||
assertEq(called, true);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), true);
|
||||
|
||||
WeakSet.prototype.add = orig;
|
||||
}
|
||||
|
||||
function test_proxy2() {
|
||||
let orig = WeakSet.prototype.add;
|
||||
|
||||
// If adder is modified, constructor should call it.
|
||||
var called = false;
|
||||
|
||||
WeakSet.prototype.add = new Proxy(function() {
|
||||
}, {
|
||||
apply: function(target, that, args) {
|
||||
var [k, v] = args;
|
||||
assertEq(k, k1);
|
||||
orig.call(that, k2);
|
||||
called = true;
|
||||
}
|
||||
});
|
||||
|
||||
var arr = [k1];
|
||||
|
||||
var m = new WeakSet(arr);
|
||||
|
||||
assertEq(called, true);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), true);
|
||||
|
||||
WeakSet.prototype.add = orig;
|
||||
}
|
||||
|
||||
function test_change1() {
|
||||
let orig = WeakSet.prototype.add;
|
||||
|
||||
// Change to adder in GetIterator(..) call should be ignored.
|
||||
var called = false;
|
||||
var modified = false;
|
||||
|
||||
var arr = [k1];
|
||||
|
||||
var proxy_arr = new Proxy(arr, {
|
||||
get: function(target, name) {
|
||||
if (name == std_iterator) {
|
||||
modified = true;
|
||||
WeakSet.prototype.add = function() {
|
||||
called = true;
|
||||
};
|
||||
}
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
|
||||
var m = new WeakSet(proxy_arr);
|
||||
|
||||
assertEq(modified, true);
|
||||
assertEq(called, false);
|
||||
assertEq(m.has(k1), true);
|
||||
assertEq(m.has(k2), false);
|
||||
|
||||
WeakSet.prototype.add = orig;
|
||||
}
|
||||
|
||||
function test_change2() {
|
||||
let orig = WeakSet.prototype.add;
|
||||
|
||||
// Change to adder in adder(...) call should be ignored.
|
||||
var called = false;
|
||||
var count = 0;
|
||||
|
||||
WeakSet.prototype.add = function(k, v) {
|
||||
if (count == 0) {
|
||||
assertEq(k, k1);
|
||||
orig.call(this, k3);
|
||||
WeakSet.prototype.add = function() {
|
||||
called = true;
|
||||
};
|
||||
count = 1;
|
||||
} else {
|
||||
assertEq(k, k2);
|
||||
orig.call(this, k4);
|
||||
count = 2;
|
||||
}
|
||||
};
|
||||
|
||||
var arr = [k1, k2];
|
||||
|
||||
var m = new WeakSet(arr);
|
||||
|
||||
assertEq(called, false);
|
||||
assertEq(count, 2);
|
||||
assertEq(m.has(k1), false);
|
||||
assertEq(m.has(k2), false);
|
||||
assertEq(m.has(k3), true);
|
||||
assertEq(m.has(k4), true);
|
||||
|
||||
WeakSet.prototype.add = orig;
|
||||
}
|
||||
|
||||
function test_error() {
|
||||
let orig = WeakSet.prototype.add;
|
||||
|
||||
var arr = [k1];
|
||||
|
||||
// WeakSet should throw TypeError if adder is not callable.
|
||||
WeakSet.prototype.add = null;
|
||||
assertThrowsInstanceOf(() => new WeakSet(arr), TypeError);
|
||||
WeakSet.prototype.add = {};
|
||||
assertThrowsInstanceOf(() => new WeakSet(arr), TypeError);
|
||||
|
||||
// WeakSet should propagate error thrown by adder.
|
||||
WeakSet.prototype.add = function() {
|
||||
throw SyntaxError();
|
||||
};
|
||||
assertThrowsInstanceOf(() => new WeakSet(arr), SyntaxError);
|
||||
|
||||
WeakSet.prototype.add = orig;
|
||||
}
|
||||
|
||||
function test() {
|
||||
test_patched();
|
||||
test_proxy1();
|
||||
test_proxy2();
|
||||
test_change1();
|
||||
test_change2();
|
||||
test_error();
|
||||
}
|
||||
|
||||
test();
|
|
@ -1,11 +0,0 @@
|
|||
var s = 'Abc1234"\'\t987\u00ff';
|
||||
assertEq(isLatin1(s), true);
|
||||
assertEq(s.toSource(), '(new String("Abc1234\\"\'\\t987\\xFF"))');
|
||||
var res = s.quote();
|
||||
assertEq(res, '"Abc1234\\"\'\\t987\\xFF"');
|
||||
assertEq(isLatin1(res), true);
|
||||
|
||||
s = 'Abc1234"\'\t\u1200987\u00ff';
|
||||
assertEq(isLatin1(s), false);
|
||||
assertEq(s.toSource(), '(new String("Abc1234\\"\'\\t\\u1200987\\xFF"))');
|
||||
assertEq(s.quote(), '"Abc1234\\"\'\\t\\u1200987\\xFF"');
|
|
@ -4274,10 +4274,15 @@ LIRGenerator::visitBlock(MBasicBlock *block)
|
|||
// basic block. This is used to handle fallible code which is moved/added
|
||||
// into split edge blocks, which do not have resume points. See
|
||||
// SplitCriticalEdgesForBlock.
|
||||
//
|
||||
// When folding conditions, we might create split-edge blocks which have
|
||||
// multiple predecessors, in such case it is invalid to have any instruction
|
||||
// in these blocks, as these blocks have no associated pc, thus we cannot
|
||||
// safely bailout from such block.
|
||||
if (lastResumePoint_) {
|
||||
for (size_t s = 0; s < block->numSuccessors(); s++) {
|
||||
MBasicBlock *succ = block->getSuccessor(s);
|
||||
if (!succ->entryResumePoint()) {
|
||||
if (!succ->entryResumePoint() && succ->numPredecessors() == 1) {
|
||||
MOZ_ASSERT(succ->isSplitEdge());
|
||||
MOZ_ASSERT(succ->phisBegin() == succ->phisEnd());
|
||||
succ->setEntryResumePoint(lastResumePoint_);
|
||||
|
|
|
@ -146,6 +146,13 @@ Sink(MIRGenerator *mir, MIRGraph &graph)
|
|||
if (!ins->canClone())
|
||||
continue;
|
||||
|
||||
// If the block is a split-edge block, which is created for folding
|
||||
// test conditions, then the block has no resume point and has
|
||||
// multiple predecessors. In such case, we cannot safely move
|
||||
// bailing instruction to these blocks as we have no way to bailout.
|
||||
if (!usesDominator->entryResumePoint() && usesDominator->numPredecessors() != 1)
|
||||
continue;
|
||||
|
||||
JitSpewDef(JitSpew_Sink, " Can Clone & Recover, sink instruction\n", ins);
|
||||
JitSpew(JitSpew_Sink, " into Block %u", usesDominator->id());
|
||||
|
||||
|
|
|
@ -3051,6 +3051,7 @@ GCRuntime::refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind)
|
|||
// instead of reporting it.
|
||||
if (!allowGC) {
|
||||
MOZ_ASSERT(!mustCollectNow);
|
||||
js_ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -3099,7 +3100,12 @@ GCRuntime::refillFreeListOffMainThread(ExclusiveContext *cx, AllocKind thingKind
|
|||
while (rt->isHeapBusy())
|
||||
HelperThreadState().wait(GlobalHelperThreadState::PRODUCER);
|
||||
|
||||
return allocator->arenas.allocateFromArena(zone, thingKind, maybeStartBGAlloc);
|
||||
void *thing = allocator->arenas.allocateFromArena(zone, thingKind, maybeStartBGAlloc);
|
||||
if (thing)
|
||||
return thing;
|
||||
|
||||
js_ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* static */ void *
|
||||
|
|
|
@ -482,24 +482,6 @@ IsString(HandleValue v)
|
|||
|
||||
#if JS_HAS_TOSOURCE
|
||||
|
||||
/*
|
||||
* String.prototype.quote is generic (as are most string methods), unlike
|
||||
* toSource, toString, and valueOf.
|
||||
*/
|
||||
static bool
|
||||
str_quote(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedString str(cx, ThisToStringForStringProto(cx, args));
|
||||
if (!str)
|
||||
return false;
|
||||
str = js_QuoteString(cx, str, '"');
|
||||
if (!str)
|
||||
return false;
|
||||
args.rval().setString(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
str_toSource_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
|
@ -3976,7 +3958,6 @@ str_concat(JSContext *cx, unsigned argc, Value *vp)
|
|||
|
||||
static const JSFunctionSpec string_methods[] = {
|
||||
#if JS_HAS_TOSOURCE
|
||||
JS_FN("quote", str_quote, 0,JSFUN_GENERIC_NATIVE),
|
||||
JS_FN(js_toSource_str, str_toSource, 0,0),
|
||||
#endif
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/Interpreter-inl.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::gc;
|
||||
|
||||
|
@ -528,6 +530,20 @@ WeakMap_construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
|
||||
// ES6 23.3.1.1 steps 5-6, 11.
|
||||
if (!args.get(0).isNullOrUndefined()) {
|
||||
// Steps 7a-b.
|
||||
RootedValue adderVal(cx);
|
||||
if (!JSObject::getProperty(cx, obj, obj, cx->names().set, &adderVal))
|
||||
return false;
|
||||
|
||||
// Step 7c.
|
||||
if (!IsCallable(adderVal))
|
||||
return ReportIsNotFunction(cx, adderVal);
|
||||
|
||||
bool isOriginalAdder = IsNativeFunction(adderVal, WeakMap_set);
|
||||
RootedValue mapVal(cx, ObjectValue(*obj));
|
||||
FastInvokeGuard fig(cx, adderVal);
|
||||
InvokeArgs &args2 = fig.args();
|
||||
|
||||
// Steps 7d-e.
|
||||
JS::ForOfIterator iter(cx);
|
||||
if (!iter.init(args[0]))
|
||||
|
@ -566,14 +582,27 @@ WeakMap_construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
return false;
|
||||
|
||||
// Steps 12k-l.
|
||||
if (keyVal.isPrimitive()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
|
||||
return false;
|
||||
}
|
||||
if (isOriginalAdder) {
|
||||
if (keyVal.isPrimitive()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
keyObject = &keyVal.toObject();
|
||||
if (!SetWeakMapEntry(cx, obj, keyObject, val))
|
||||
return false;
|
||||
keyObject = &keyVal.toObject();
|
||||
if (!SetWeakMapEntry(cx, obj, keyObject, val))
|
||||
return false;
|
||||
} else {
|
||||
if (!args2.init(2))
|
||||
return false;
|
||||
|
||||
args2.setCallee(adderVal);
|
||||
args2.setThis(mapVal);
|
||||
args2[0].set(keyVal);
|
||||
args2[1].set(val);
|
||||
|
||||
if (!fig.invoke(cx))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
|
||||
var BUGNUMBER = 1106428;
|
||||
var int32x4 = SIMD.int32x4;
|
||||
|
||||
var summary = 'int32x4 bool';
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* https://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function test() {
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
var a = int32x4.bool(true, false, true, false);
|
||||
assertEq(a.x, -1);
|
||||
assertEq(a.y, 0);
|
||||
assertEq(a.z, -1);
|
||||
assertEq(a.w, 0);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
}
|
||||
|
||||
test();
|
|
@ -1,50 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
This test case uses String.quote(), because that method uses the JS printf()
|
||||
functions internally. The printf() functions print to a char* buffer, causing
|
||||
conversion to and from UTF-8 if UTF-8 is enabled. If not, UTF-8 sequences are
|
||||
converted to ASCII \uXXXX sequences. Thus, both return values are acceptable.
|
||||
*/
|
||||
|
||||
var BUGNUMBER = 315974;
|
||||
var summary = 'Test internal printf() for wide characters';
|
||||
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
enterFunc (String (BUGNUMBER));
|
||||
|
||||
var goodSurrogatePair = '\uD841\uDC42';
|
||||
var badSurrogatePair = '\uD841badbad';
|
||||
|
||||
var goodSurrogatePairQuotedUtf8 = '"\uD841\uDC42"';
|
||||
var badSurrogatePairQuotedUtf8 = 'no error thrown!';
|
||||
var goodSurrogatePairQuotedNoUtf8 = '"\\uD841\\uDC42"';
|
||||
var badSurrogatePairQuotedNoUtf8 = '"\\uD841badbad"';
|
||||
|
||||
var status = summary + ': String.quote() should pay respect to surrogate pairs';
|
||||
var actual = goodSurrogatePair.quote();
|
||||
/* Result in case UTF-8 is enabled. */
|
||||
var expect = goodSurrogatePairQuotedUtf8;
|
||||
/* Result in case UTF-8 is disabled. */
|
||||
if (actual != expect && actual == goodSurrogatePairQuotedNoUtf8)
|
||||
expect = actual;
|
||||
reportCompare(expect, actual, status);
|
||||
|
||||
/*
|
||||
* A malformed surrogate pair throws an out-of-memory error,
|
||||
* but only if UTF-8 is enabled.
|
||||
*/
|
||||
status = summary + ': String.quote() should throw error on bad surrogate pair';
|
||||
/* Out of memory is not catchable. */
|
||||
actual = badSurrogatePair.quote();
|
||||
/* Come here only if UTF-8 is disabled. */
|
||||
reportCompare(badSurrogatePairQuotedNoUtf8, actual, status);
|
||||
|
||||
exitFunc (String (BUGNUMBER));
|
|
@ -14,8 +14,7 @@ var voids = [null, undefined];
|
|||
function noop() { }
|
||||
|
||||
var generics = {
|
||||
String: [{ quote: [] },
|
||||
{ substring: [] },
|
||||
String: [{ substring: [] },
|
||||
{ toLowerCase: [] },
|
||||
{ toUpperCase: [] },
|
||||
{ charAt: [] },
|
||||
|
|
|
@ -113,7 +113,7 @@ var Match =
|
|||
switch (typeof exp) {
|
||||
case "string":
|
||||
if (act !== exp)
|
||||
throw new MatchError("expected " + exp.quote() + ", got " + quote(act));
|
||||
throw new MatchError("expected " + quote(exp) + ", got " + quote(act));
|
||||
return true;
|
||||
case "boolean":
|
||||
case "number":
|
||||
|
@ -131,7 +131,7 @@ var Match =
|
|||
|
||||
for (var key in exp) {
|
||||
if (!(key in act))
|
||||
throw new MatchError("expected property " + key.quote() + " not found in " + quote(act));
|
||||
throw new MatchError("expected property " + quote(key) + " not found in " + quote(act));
|
||||
match(act[key], exp[key]);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "jsprototypes.h"
|
||||
|
||||
#define FOR_EACH_COMMON_PROPERTYNAME(macro) \
|
||||
macro(add, add, "add") \
|
||||
macro(anonymous, anonymous, "anonymous") \
|
||||
macro(Any, Any, "Any") \
|
||||
macro(apply, apply, "apply") \
|
||||
|
@ -213,6 +214,7 @@
|
|||
macro(variable, variable, "variable") \
|
||||
macro(void0, void0, "(void 0)") \
|
||||
macro(watch, watch, "watch") \
|
||||
macro(WeakSet_add, WeakSet_add, "WeakSet_add") \
|
||||
macro(writable, writable, "writable") \
|
||||
macro(w, w, "w") \
|
||||
macro(x, x, "x") \
|
||||
|
|
|
@ -244,7 +244,7 @@ ShapeTable::search(jsid id, bool adding)
|
|||
}
|
||||
}
|
||||
|
||||
/* NOTREACHED */
|
||||
MOZ_CRASH("Shape::search failed to find an expected entry.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace mozilla {
|
|||
template <typename T>
|
||||
struct IsPixel : FalseType {};
|
||||
|
||||
// See struct decleration for a description of each unit type
|
||||
// See struct declaration for a description of each unit type.
|
||||
struct CSSPixel;
|
||||
struct LayoutDevicePixel;
|
||||
struct LayerPixel;
|
||||
|
|
|
@ -2096,6 +2096,16 @@ ScrollFrameHelper::ScrollToWithOrigin(nsPoint aScrollPosition,
|
|||
mLastSmoothScrollOrigin = aOrigin;
|
||||
mScrollGeneration = ++sScrollGenerationCounter;
|
||||
|
||||
if (!nsLayoutUtils::GetDisplayPort(mOuter->GetContent())) {
|
||||
// If this frame doesn't have a displayport then there won't be an
|
||||
// APZC instance for it and so there won't be anything to process
|
||||
// this smooth scroll request. We should set a displayport on this
|
||||
// frame to force an APZC which can handle the request.
|
||||
nsLayoutUtils::CalculateAndSetDisplayPortMargins(
|
||||
mOuter->GetScrollTargetFrame(),
|
||||
nsLayoutUtils::RepaintMode::DoNotRepaint);
|
||||
}
|
||||
|
||||
// Schedule a paint to ensure that the frame metrics get updated on
|
||||
// the compositor thread.
|
||||
mOuter->SchedulePaint();
|
||||
|
|
|
@ -238,7 +238,7 @@ var Addons = {
|
|||
|
||||
_getElementForAddon: function(aKey) {
|
||||
let list = document.getElementById("addons-list");
|
||||
let element = list.querySelector("div[addonID=" + aKey.quote() + "]");
|
||||
let element = list.querySelector("div[addonID=\"" + CSS.escape(aKey) + "\"]");
|
||||
return element;
|
||||
},
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ var NewPrefDialog = {
|
|||
}
|
||||
|
||||
// If item already in list, it's being changed, else added
|
||||
let item = document.querySelector(".pref-item[name=" + aPrefName.quote() + "]");
|
||||
let item = document.querySelector(".pref-item[name=\"" + CSS.escape(aPrefName) + "\"]");
|
||||
if (item) {
|
||||
this._positiveButton.textContent = gStringBundle.GetStringFromName("newPref.changeButton");
|
||||
} else {
|
||||
|
@ -472,7 +472,7 @@ var AboutConfig = {
|
|||
}
|
||||
|
||||
// If pref not already in list, refresh display as it's being added
|
||||
let item = document.querySelector(".pref-item[name=" + pref.name.quote() + "]");
|
||||
let item = document.querySelector(".pref-item[name=\"" + CSS.escape(pref.name) + "\"]");
|
||||
if (!item) {
|
||||
document.location.reload();
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
. "$topsrcdir/mobile/android/config/mozconfigs/common"
|
||||
|
||||
# L10n
|
||||
ac_add_options --with-l10n-base=../../l10n-central
|
||||
|
||||
# Global options
|
||||
ac_add_options --disable-tests
|
||||
|
||||
# Mozilla-Central nightlies only since this has a cost in performance
|
||||
ac_add_options --enable-js-diagnostics
|
||||
|
||||
# Android
|
||||
ac_add_options --with-android-min-sdk=10
|
||||
ac_add_options --target=arm-linux-androideabi
|
||||
|
||||
ac_add_options --with-system-zlib
|
||||
ac_add_options --enable-updater
|
||||
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
|
||||
|
||||
export MOZILLA_OFFICIAL=1
|
||||
export MOZ_DISABLE_GECKOVIEW=1
|
||||
|
||||
ac_add_options --with-branding=mobile/android/branding/nightly
|
||||
|
||||
ac_add_options --disable-stdcxx-compat
|
||||
|
||||
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
|
|
@ -0,0 +1,29 @@
|
|||
. "$topsrcdir/mobile/android/config/mozconfigs/common"
|
||||
|
||||
# L10n
|
||||
ac_add_options --with-l10n-base=../../l10n-central
|
||||
|
||||
# Global options
|
||||
ac_add_options --disable-tests
|
||||
|
||||
# Mozilla-Central nightlies only since this has a cost in performance
|
||||
ac_add_options --enable-js-diagnostics
|
||||
|
||||
# Android
|
||||
ac_add_options --with-android-min-sdk=9
|
||||
ac_add_options --with-android-max-sdk=9
|
||||
ac_add_options --enable-android-resource-constrained
|
||||
ac_add_options --target=arm-linux-androideabi
|
||||
|
||||
ac_add_options --with-system-zlib
|
||||
ac_add_options --enable-updater
|
||||
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
|
||||
|
||||
export MOZILLA_OFFICIAL=1
|
||||
export MOZ_DISABLE_GECKOVIEW=1
|
||||
|
||||
ac_add_options --with-branding=mobile/android/branding/nightly
|
||||
|
||||
ac_add_options --disable-stdcxx-compat
|
||||
|
||||
. "$topsrcdir/mobile/android/config/mozconfigs/common.override"
|
|
@ -445,6 +445,10 @@ To see more help for a specific command, run:
|
|||
|
||||
fn = getattr(instance, handler.method)
|
||||
|
||||
if args.debug_command:
|
||||
import pdb
|
||||
pdb.set_trace()
|
||||
|
||||
try:
|
||||
result = fn(**vars(args.command_args))
|
||||
|
||||
|
@ -603,6 +607,8 @@ To see more help for a specific command, run:
|
|||
global_group.add_argument('-h', '--help', dest='help',
|
||||
action='store_true', default=False,
|
||||
help='Show this help message.')
|
||||
global_group.add_argument('--debug-command', action='store_true',
|
||||
help='Start a Python debugger when command is dispatched.')
|
||||
|
||||
for args, kwargs in self.global_arguments:
|
||||
global_group.add_argument(*args, **kwargs)
|
||||
|
|
|
@ -104,6 +104,55 @@ static const uint8_t PERMIT_FRANCE_GOV_NAME_CONSTRAINTS_DATA[] =
|
|||
"\x30\x05\x82\x03" ".nc"
|
||||
"\x30\x05\x82\x03" ".tf";
|
||||
|
||||
// If useRoots is true, we only use root certificates in the candidate list.
|
||||
// If useRoots is false, we only use non-root certificates in the list.
|
||||
static Result
|
||||
FindIssuerInner(ScopedCERTCertList& candidates, bool useRoots,
|
||||
Input encodedIssuerName, TrustDomain::IssuerChecker& checker,
|
||||
/*out*/ bool& keepGoing)
|
||||
{
|
||||
keepGoing = true;
|
||||
for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
|
||||
!CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
|
||||
bool candidateIsRoot = !!n->cert->isRoot;
|
||||
if (candidateIsRoot != useRoots) {
|
||||
continue;
|
||||
}
|
||||
Input certDER;
|
||||
Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
|
||||
if (rv != Success) {
|
||||
continue; // probably too big
|
||||
}
|
||||
|
||||
Input anssiSubject;
|
||||
rv = anssiSubject.Init(ANSSI_SUBJECT_DATA, sizeof(ANSSI_SUBJECT_DATA) - 1);
|
||||
if (rv != Success) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
// TODO: Use CERT_CompareName or equivalent
|
||||
if (InputsAreEqual(encodedIssuerName, anssiSubject)) {
|
||||
Input anssiNameConstraints;
|
||||
if (anssiNameConstraints.Init(
|
||||
PERMIT_FRANCE_GOV_NAME_CONSTRAINTS_DATA,
|
||||
sizeof(PERMIT_FRANCE_GOV_NAME_CONSTRAINTS_DATA) - 1)
|
||||
!= Success) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
rv = checker.Check(certDER, &anssiNameConstraints, keepGoing);
|
||||
} else {
|
||||
rv = checker.Check(certDER, nullptr, keepGoing);
|
||||
}
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (!keepGoing) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
Result
|
||||
NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
|
||||
IssuerChecker& checker, Time)
|
||||
|
@ -116,40 +165,19 @@ NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
|
|||
&encodedIssuerNameSECItem, 0,
|
||||
false));
|
||||
if (candidates) {
|
||||
for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
|
||||
!CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
|
||||
Input certDER;
|
||||
Result rv = certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
|
||||
if (rv != Success) {
|
||||
continue; // probably too big
|
||||
}
|
||||
|
||||
bool keepGoing;
|
||||
Input anssiSubject;
|
||||
rv = anssiSubject.Init(ANSSI_SUBJECT_DATA,
|
||||
sizeof(ANSSI_SUBJECT_DATA) - 1);
|
||||
if (rv != Success) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
// TODO: Use CERT_CompareName or equivalent
|
||||
if (InputsAreEqual(encodedIssuerName, anssiSubject)) {
|
||||
Input anssiNameConstraints;
|
||||
if (anssiNameConstraints.Init(
|
||||
PERMIT_FRANCE_GOV_NAME_CONSTRAINTS_DATA,
|
||||
sizeof(PERMIT_FRANCE_GOV_NAME_CONSTRAINTS_DATA) - 1)
|
||||
!= Success) {
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
rv = checker.Check(certDER, &anssiNameConstraints, keepGoing);
|
||||
} else {
|
||||
rv = checker.Check(certDER, nullptr, keepGoing);
|
||||
}
|
||||
// First, try all the root certs; then try all the non-root certs.
|
||||
bool keepGoing;
|
||||
Result rv = FindIssuerInner(candidates, true, encodedIssuerName, checker,
|
||||
keepGoing);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (keepGoing) {
|
||||
rv = FindIssuerInner(candidates, false, encodedIssuerName, checker,
|
||||
keepGoing);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
if (!keepGoing) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,12 +30,17 @@ class GeckoInstance(object):
|
|||
"browser.tabs.remote.autostart.1": False,
|
||||
"browser.tabs.remote.autostart.2": False}
|
||||
|
||||
def __init__(self, host, port, bin, profile, app_args=None, symbols_path=None,
|
||||
gecko_log=None, prefs=None):
|
||||
def __init__(self, host, port, bin, profile=None, app_args=None, symbols_path=None,
|
||||
gecko_log=None, prefs=None, ):
|
||||
self.marionette_host = host
|
||||
self.marionette_port = port
|
||||
self.bin = bin
|
||||
self.profile_path = profile
|
||||
# Check if it is a Profile object or a path to profile
|
||||
self.profile = None
|
||||
if isinstance(profile, Profile):
|
||||
self.profile = profile
|
||||
else:
|
||||
self.profile_path = profile
|
||||
self.prefs = prefs
|
||||
self.app_args = app_args or []
|
||||
self.runner = None
|
||||
|
@ -47,12 +52,14 @@ class GeckoInstance(object):
|
|||
profile_args["preferences"]["marionette.defaultPrefs.port"] = self.marionette_port
|
||||
if self.prefs:
|
||||
profile_args["preferences"].update(self.prefs)
|
||||
if not self.profile_path:
|
||||
profile_args["restore"] = False
|
||||
profile = Profile(**profile_args)
|
||||
else:
|
||||
profile_args["path_from"] = self.profile_path
|
||||
profile = Profile.clone(**profile_args)
|
||||
|
||||
if hasattr(self, "profile_path") and self.profile is None:
|
||||
if not self.profile_path:
|
||||
profile_args["restore"] = False
|
||||
self.profile = Profile(**profile_args)
|
||||
else:
|
||||
profile_args["path_from"] = self.profile_path
|
||||
self.profile = Profile.clone(**profile_args)
|
||||
|
||||
process_args = {
|
||||
'processOutputLine': [NullOutput()],
|
||||
|
@ -101,20 +108,27 @@ class GeckoInstance(object):
|
|||
'MOZ_CRASHREPORTER_NO_REPORT': '1', })
|
||||
self.runner = Runner(
|
||||
binary=self.bin,
|
||||
profile=profile,
|
||||
profile=self.profile,
|
||||
cmdargs=['-no-remote', '-marionette'] + self.app_args,
|
||||
env=env,
|
||||
symbols_path=self.symbols_path,
|
||||
process_args=process_args)
|
||||
self.runner.start()
|
||||
|
||||
def close(self):
|
||||
def close(self, restart=False):
|
||||
if not restart:
|
||||
self.profile = None
|
||||
|
||||
if self.runner:
|
||||
self.runner.stop()
|
||||
self.runner.cleanup()
|
||||
|
||||
def restart(self, prefs=None):
|
||||
self.close()
|
||||
def restart(self, prefs=None, clean=True):
|
||||
if clean:
|
||||
self.profile.cleanup()
|
||||
self.profile = None
|
||||
|
||||
self.close(restart=True)
|
||||
if prefs:
|
||||
self.prefs = prefs
|
||||
else:
|
||||
|
|
|
@ -475,6 +475,7 @@ class Marionette(object):
|
|||
self.host = host
|
||||
self.port = self.local_port = port
|
||||
self.bin = bin
|
||||
self.profile = profile
|
||||
self.instance = None
|
||||
self.session = None
|
||||
self.session_id = None
|
||||
|
@ -512,7 +513,7 @@ class Marionette(object):
|
|||
KeyError):
|
||||
instance_class = geckoinstance.GeckoInstance
|
||||
self.instance = instance_class(host=self.host, port=self.port,
|
||||
bin=self.bin, profile=profile,
|
||||
bin=self.bin, profile=self.profile,
|
||||
app_args=app_args, symbols_path=symbols_path,
|
||||
gecko_log=gecko_log)
|
||||
self.instance.start()
|
||||
|
@ -528,7 +529,7 @@ class Marionette(object):
|
|||
binary=emulator_binary,
|
||||
userdata=emulator_img,
|
||||
resolution=emulator_res,
|
||||
profile=profile,
|
||||
profile=self.profile,
|
||||
adb_path=adb_path,
|
||||
process_args=process_args)
|
||||
self.emulator = self.runner.device
|
||||
|
@ -773,20 +774,20 @@ class Marionette(object):
|
|||
self.start_session()
|
||||
self._reset_timeouts()
|
||||
|
||||
def restart_with_clean_profile(self):
|
||||
def restart(self, clean=False):
|
||||
"""
|
||||
This will terminate the currently running instance, and spawn a new instance
|
||||
with a clean profile.
|
||||
with the same profile and then reuse the session id when creating a session again.
|
||||
|
||||
: param prefs: A dictionary whose keys are preference names.
|
||||
"""
|
||||
if not self.instance:
|
||||
raise errors.MarionetteException("enforce_gecko_prefs can only be called " \
|
||||
raise errors.MarionetteException("restart can only be called " \
|
||||
"on gecko instances launched by Marionette")
|
||||
self.delete_session()
|
||||
self.instance.restart()
|
||||
self.instance.restart(clean=clean)
|
||||
assert(self.wait_for_port()), "Timed out waiting for port!"
|
||||
self.start_session()
|
||||
self.start_session(session_id=self.session_id)
|
||||
self._reset_timeouts()
|
||||
|
||||
def absolute_url(self, relative_url):
|
||||
|
|
|
@ -27,6 +27,12 @@ class TestLog(MarionetteTestCase):
|
|||
self.assertFalse(bool_value)
|
||||
|
||||
def test_clean_profile(self):
|
||||
self.marionette.restart_with_clean_profile()
|
||||
self.marionette.restart(clean=True)
|
||||
with self.assertRaisesRegexp(JavascriptException, "Error getting pref"):
|
||||
bool_value = self.marionette.execute_script("return SpecialPowers.getBoolPref('marionette.test.bool');")
|
||||
|
||||
def test_can_restart_the_browser(self):
|
||||
self.marionette.enforce_gecko_prefs({"marionette.test.restart": True})
|
||||
self.marionette.restart()
|
||||
bool_value = self.marionette.execute_script("return SpecialPowers.getBoolPref('marionette.test.restart');")
|
||||
self.assertTrue(bool_value)
|
|
@ -54,10 +54,8 @@ class Emulator(Device):
|
|||
self.sdcard = None
|
||||
if sdcard:
|
||||
self.sdcard = self.create_sdcard(sdcard)
|
||||
self.userdata = os.path.join(self.arch.sysdir, 'userdata.img')
|
||||
if userdata:
|
||||
self.userdata = tempfile.NamedTemporaryFile(prefix='qemu-userdata')
|
||||
shutil.copyfile(userdata, self.userdata)
|
||||
self.userdata = tempfile.NamedTemporaryFile(prefix='qemu-userdata')
|
||||
self.initdata = userdata if userdata else os.path.join(self.arch.sysdir, 'userdata.img')
|
||||
self.no_window = no_window
|
||||
|
||||
self.battery = EmulatorBattery(self)
|
||||
|
@ -72,7 +70,9 @@ class Emulator(Device):
|
|||
qemu_args = [self.arch.binary,
|
||||
'-kernel', self.arch.kernel,
|
||||
'-sysdir', self.arch.sysdir,
|
||||
'-data', self.userdata]
|
||||
'-data', self.userdata.name,
|
||||
'-initdata', self.initdata,
|
||||
'-wipe-data']
|
||||
if self.no_window:
|
||||
qemu_args.append('-no-window')
|
||||
if self.sdcard:
|
||||
|
@ -162,7 +162,9 @@ class Emulator(Device):
|
|||
if self.proc:
|
||||
self.proc.kill()
|
||||
self.proc = None
|
||||
|
||||
# Remove temporary user data image
|
||||
if self.userdata:
|
||||
self.userdata.close()
|
||||
# Remove temporary sdcard
|
||||
if self.sdcard and os.path.isfile(self.sdcard):
|
||||
os.remove(self.sdcard)
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include "mozilla/CycleCollectedJSRuntime.h"
|
||||
#include <algorithm>
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/DOMJSClass.h"
|
||||
|
@ -96,6 +97,7 @@ class IncrementalFinalizeRunnable : public nsRunnable
|
|||
nsTArray<nsISupports*> mSupports;
|
||||
DeferredFinalizeArray mDeferredFinalizeFunctions;
|
||||
uint32_t mFinalizeFunctionToRun;
|
||||
bool mReleasing;
|
||||
|
||||
static const PRTime SliceMillis = 10; /* ms */
|
||||
|
||||
|
@ -1124,6 +1126,7 @@ IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSRuntime
|
|||
DeferredFinalizerTable& aFinalizers)
|
||||
: mRuntime(aRt)
|
||||
, mFinalizeFunctionToRun(0)
|
||||
, mReleasing(false)
|
||||
{
|
||||
this->mSupports.SwapElements(aSupports);
|
||||
DeferredFinalizeFunctionHolder* function =
|
||||
|
@ -1143,39 +1146,46 @@ IncrementalFinalizeRunnable::~IncrementalFinalizeRunnable()
|
|||
void
|
||||
IncrementalFinalizeRunnable::ReleaseNow(bool aLimited)
|
||||
{
|
||||
//MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mDeferredFinalizeFunctions.Length() != 0,
|
||||
"We should have at least ReleaseSliceNow to run");
|
||||
MOZ_ASSERT(mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length(),
|
||||
"No more finalizers to run?");
|
||||
if (mReleasing) {
|
||||
MOZ_ASSERT(false, "Try to avoid re-entering ReleaseNow!");
|
||||
return;
|
||||
}
|
||||
{
|
||||
mozilla::AutoRestore<bool> ar(mReleasing);
|
||||
mReleasing = true;
|
||||
MOZ_ASSERT(mDeferredFinalizeFunctions.Length() != 0,
|
||||
"We should have at least ReleaseSliceNow to run");
|
||||
MOZ_ASSERT(mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length(),
|
||||
"No more finalizers to run?");
|
||||
|
||||
TimeDuration sliceTime = TimeDuration::FromMilliseconds(SliceMillis);
|
||||
TimeStamp started = TimeStamp::Now();
|
||||
bool timeout = false;
|
||||
do {
|
||||
const DeferredFinalizeFunctionHolder& function =
|
||||
mDeferredFinalizeFunctions[mFinalizeFunctionToRun];
|
||||
if (aLimited) {
|
||||
bool done = false;
|
||||
while (!timeout && !done) {
|
||||
/*
|
||||
* We don't want to read the clock too often, so we try to
|
||||
* release slices of 100 items.
|
||||
*/
|
||||
done = function.run(100, function.data);
|
||||
timeout = TimeStamp::Now() - started >= sliceTime;
|
||||
}
|
||||
if (done) {
|
||||
TimeDuration sliceTime = TimeDuration::FromMilliseconds(SliceMillis);
|
||||
TimeStamp started = TimeStamp::Now();
|
||||
bool timeout = false;
|
||||
do {
|
||||
const DeferredFinalizeFunctionHolder& function =
|
||||
mDeferredFinalizeFunctions[mFinalizeFunctionToRun];
|
||||
if (aLimited) {
|
||||
bool done = false;
|
||||
while (!timeout && !done) {
|
||||
/*
|
||||
* We don't want to read the clock too often, so we try to
|
||||
* release slices of 100 items.
|
||||
*/
|
||||
done = function.run(100, function.data);
|
||||
timeout = TimeStamp::Now() - started >= sliceTime;
|
||||
}
|
||||
if (done) {
|
||||
++mFinalizeFunctionToRun;
|
||||
}
|
||||
if (timeout) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
function.run(UINT32_MAX, function.data);
|
||||
++mFinalizeFunctionToRun;
|
||||
}
|
||||
if (timeout) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
function.run(UINT32_MAX, function.data);
|
||||
++mFinalizeFunctionToRun;
|
||||
}
|
||||
} while (mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length());
|
||||
} while (mFinalizeFunctionToRun < mDeferredFinalizeFunctions.Length());
|
||||
}
|
||||
|
||||
if (mFinalizeFunctionToRun == mDeferredFinalizeFunctions.Length()) {
|
||||
MOZ_ASSERT(mRuntime->mFinalizeRunnable == this);
|
||||
|
@ -1210,7 +1220,21 @@ IncrementalFinalizeRunnable::Run()
|
|||
void
|
||||
CycleCollectedJSRuntime::FinalizeDeferredThings(DeferredFinalizeType aType)
|
||||
{
|
||||
MOZ_ASSERT(!mFinalizeRunnable);
|
||||
/*
|
||||
* If the previous GC created a runnable to finalize objects
|
||||
* incrementally, and if it hasn't finished yet, finish it now. We
|
||||
* don't want these to build up. We also don't want to allow any
|
||||
* existing incremental finalize runnables to run after a
|
||||
* non-incremental GC, since they are often used to detect leaks.
|
||||
*/
|
||||
if (mFinalizeRunnable) {
|
||||
mFinalizeRunnable->ReleaseNow(false);
|
||||
if (mFinalizeRunnable) {
|
||||
// If we re-entered ReleaseNow, we couldn't delete mFinalizeRunnable and
|
||||
// we need to just continue processing it.
|
||||
return;
|
||||
}
|
||||
}
|
||||
mFinalizeRunnable = new IncrementalFinalizeRunnable(this,
|
||||
mDeferredSupports,
|
||||
mDeferredFinalizerTable);
|
||||
|
@ -1261,17 +1285,6 @@ CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If the previous GC created a runnable to finalize objects
|
||||
* incrementally, and if it hasn't finished yet, finish it now. We
|
||||
* don't want these to build up. We also don't want to allow any
|
||||
* existing incremental finalize runnables to run after a
|
||||
* non-incremental GC, since they are often used to detect leaks.
|
||||
*/
|
||||
if (mFinalizeRunnable) {
|
||||
mFinalizeRunnable->ReleaseNow(false);
|
||||
}
|
||||
|
||||
// Do any deferred finalization of native objects.
|
||||
FinalizeDeferredThings(JS::WasIncrementalGC(mJSRuntime) ? FinalizeIncrementally :
|
||||
FinalizeNow);
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include <sys/mount.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
@ -27,6 +26,7 @@
|
|||
|
||||
#if defined(HAVE_SYS_QUOTA_H) && defined(HAVE_LINUX_QUOTA_H)
|
||||
#define USE_LINUX_QUOTACTL
|
||||
#include <sys/mount.h>
|
||||
#include <sys/quota.h>
|
||||
#endif
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче