diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini
index 44e5d15d5..3224ce585 100644
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -25,6 +25,7 @@ support-files =
bug792517.html
bug792517.sjs
bug839103.css
+ clipboard_pastefile.html
discovery.html
domplate_test.js
download_page.html
@@ -283,6 +284,7 @@ skip-if = os == 'win' || e10s # Bug 1159268 - Need a content-process safe versio
[browser_canonizeURL.js]
skip-if = e10s # Bug 1094510 - test hits the network in e10s mode only
[browser_clipboard.js]
+[browser_clipboard_pastefile.js]
[browser_contentAreaClick.js]
[browser_contextSearchTabPosition.js]
skip-if = os == "mac" || e10s # bug 967013; e10s: bug 1094761 - test hits the network in e10s, causing next test to crash
diff --git a/browser/base/content/test/general/browser_clipboard_pastefile.js b/browser/base/content/test/general/browser_clipboard_pastefile.js
new file mode 100644
index 000000000..094628f42
--- /dev/null
+++ b/browser/base/content/test/general/browser_clipboard_pastefile.js
@@ -0,0 +1,59 @@
+// This test is used to check that pasting files removes all non-file data from
+// event.clipboardData.
+
+add_task(function*() {
+ var searchbar = document.getElementById("searchbar");
+
+ searchbar.focus();
+ searchbar.value = "Text";
+ searchbar.select();
+
+ yield new Promise((resolve, reject) => {
+ searchbar.addEventListener("copy", function copyEvent(event) {
+ searchbar.removeEventListener("copy", copyEvent, true);
+ event.clipboardData.setData("text/plain", "Alternate");
+ // For this test, it doesn't matter that the file isn't actually a file.
+ event.clipboardData.setData("application/x-moz-file", "Sample");
+ event.preventDefault();
+ resolve();
+ }, true)
+
+ EventUtils.synthesizeKey("c", { accelKey: true });
+ });
+
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser,
+ "https://example.com/browser/browser/base/content/test/general/clipboard_pastefile.html");
+ let browser = tab.linkedBrowser;
+
+ yield ContentTask.spawn(browser, { }, function* (arg) {
+ content.document.getElementById("input").focus();
+ });
+
+ yield BrowserTestUtils.synthesizeKey("v", { accelKey: true }, browser);
+
+ let output = yield ContentTask.spawn(browser, { }, function* (arg) {
+ return content.document.getElementById("output").textContent;
+ });
+ is (output, "Passed", "Paste file");
+
+ searchbar.focus();
+
+ yield new Promise((resolve, reject) => {
+ searchbar.addEventListener("paste", function copyEvent(event) {
+ searchbar.removeEventListener("paste", copyEvent, true);
+
+ let dt = event.clipboardData;
+ is(dt.types.length, 3, "number of types");
+ ok(dt.types.contains("text/plain"), "text/plain exists in types");
+ ok(dt.mozTypesAt(0).contains("text/plain"), "text/plain exists in mozTypesAt");
+ is(dt.getData("text/plain"), "Alternate", "text/plain returned in getData");
+ is(dt.mozGetDataAt("text/plain", 0), "Alternate", "text/plain returned in mozGetDataAt");
+
+ resolve();
+ }, true);
+
+ EventUtils.synthesizeKey("v", { accelKey: true });
+ });
+
+ yield BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/base/content/test/general/clipboard_pastefile.html b/browser/base/content/test/general/clipboard_pastefile.html
new file mode 100644
index 000000000..fcbf60ed2
--- /dev/null
+++ b/browser/base/content/test/general/clipboard_pastefile.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
diff --git a/dom/events/DataTransfer.cpp b/dom/events/DataTransfer.cpp
index 2ad0effce..a6c934215 100644
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -359,25 +359,8 @@ DataTransfer::GetFiles(nsIDOMFileList** aFileList)
already_AddRefed
DataTransfer::Types()
{
- RefPtr types = new DOMStringList();
- if (mItems.Length()) {
- bool addFile = false;
- const nsTArray& item = mItems[0];
- for (uint32_t i = 0; i < item.Length(); i++) {
- const nsString& format = item[i].mFormat;
- types->Add(format);
- if (!addFile) {
- addFile = format.EqualsASCII(kFileMime) ||
- format.EqualsASCII("application/x-moz-file-promise");
- }
- }
-
- if (addFile) {
- types->Add(NS_LITERAL_STRING("Files"));
- }
- }
-
- return types.forget();
+ ErrorResult rv;
+ return MozTypesAt(0, rv);
}
NS_IMETHODIMP
@@ -545,7 +528,7 @@ DataTransfer::GetMozSourceNode(nsIDOMNode** aSourceNode)
}
already_AddRefed
-DataTransfer::MozTypesAt(uint32_t aIndex, ErrorResult& aRv)
+DataTransfer::MozTypesAt(uint32_t aIndex, ErrorResult& aRv) const
{
// Only the first item is valid for clipboard events
if (aIndex > 0 &&
@@ -557,10 +540,28 @@ DataTransfer::MozTypesAt(uint32_t aIndex, ErrorResult& aRv)
RefPtr types = new DOMStringList();
if (aIndex < mItems.Length()) {
+ bool addFile = false;
// note that you can retrieve the types regardless of their principal
- nsTArray& item = mItems[aIndex];
- for (uint32_t i = 0; i < item.Length(); i++)
- types->Add(item[i].mFormat);
+ const nsTArray& item = mItems[aIndex];
+ for (uint32_t i = 0; i < item.Length(); i++) {
+ const nsString& format = item[i].mFormat;
+ types->Add(format);
+ if (!addFile) {
+ addFile = format.EqualsASCII(kFileMime);
+ }
+ }
+
+ if (addFile) {
+ // If this is a content caller, and a file is in the data transfer, remove
+ // the non-file types. This prevents alternate text forms of the file
+ // from being returned.
+ if (!nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
+ types->Clear();
+ types->Add(NS_LITERAL_STRING(kFileMime));
+ }
+
+ types->Add(NS_LITERAL_STRING("Files"));
+ }
}
return types.forget();
@@ -602,12 +603,23 @@ DataTransfer::GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
-
nsAutoString format;
GetRealFormat(aFormat, format);
nsTArray& item = mItems[aIndex];
+ // If this is a content caller, and a file is in the data transfer, only
+ // return the file type.
+ if (!format.EqualsLiteral(kFileMime) &&
+ !nsContentUtils::IsSystemPrincipal(aSubjectPrincipal)) {
+ uint32_t count = item.Length();
+ for (uint32_t i = 0; i < count; i++) {
+ if (item[i].mFormat.EqualsLiteral(kFileMime)) {
+ return NS_OK;
+ }
+ }
+ }
+
// Check if the caller is allowed to access the drag data. Callers with
// chrome privileges can always read the data. During the
// drop event, allow retrieving the data except in the case where the
diff --git a/dom/events/DataTransfer.h b/dom/events/DataTransfer.h
index 2b1f90a51..48fbac975 100644
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -165,7 +165,8 @@ public:
}
}
already_AddRefed MozTypesAt(uint32_t aIndex,
- mozilla::ErrorResult& aRv);
+ mozilla::ErrorResult& aRv) const;
+
void MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
mozilla::ErrorResult& aRv);
void MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
diff --git a/dom/media/MP3Demuxer.cpp b/dom/media/MP3Demuxer.cpp
index 2ec509978..773f99626 100644
--- a/dom/media/MP3Demuxer.cpp
+++ b/dom/media/MP3Demuxer.cpp
@@ -204,7 +204,7 @@ MP3TrackDemuxer::FastSeek(const TimeUnit& aTime) {
if (!aTime.ToMicroseconds()) {
// Quick seek to the beginning of the stream.
mFrameIndex = 0;
- } else if (vbr.IsTOCPresent()) {
+ } else if (vbr.IsTOCPresent() && Duration().ToMicroseconds() > 0) {
// Use TOC for more precise seeking.
const float durationFrac = static_cast(aTime.ToMicroseconds()) /
Duration().ToMicroseconds();
@@ -347,7 +347,7 @@ MP3TrackDemuxer::Duration() const {
int64_t numFrames = 0;
const auto numAudioFrames = mParser.VBRInfo().NumAudioFrames();
- if (numAudioFrames) {
+ if (mParser.VBRInfo().IsValid() && numAudioFrames.valueOr(0) + 1 > 1) {
// VBR headers don't include the VBR header frame.
numFrames = numAudioFrames.value() + 1;
} else {
@@ -356,7 +356,9 @@ MP3TrackDemuxer::Duration() const {
// Unknown length, we can't estimate duration.
return TimeUnit::FromMicroseconds(-1);
}
- numFrames = (streamLen - mFirstFrameOffset) / AverageFrameLength();
+ if (AverageFrameLength() > 0) {
+ numFrames = (streamLen - mFirstFrameOffset) / AverageFrameLength();
+ }
}
return Duration(numFrames);
}
@@ -511,7 +513,6 @@ MP3TrackDemuxer::GetNextFrame(const MediaByteRange& aRange) {
if (mNumParsedFrames == 1) {
// First frame parsed, let's read VBR info if available.
- // TODO: read info that helps with seeking (bug 1163667).
ByteReader reader(frame->Data(), frame->Size());
mParser.ParseVBRHeader(&reader);
reader.DiscardRemaining();
@@ -532,7 +533,7 @@ MP3TrackDemuxer::OffsetFromFrameIndex(int64_t aFrameIndex) const {
int64_t offset = 0;
const auto& vbr = mParser.VBRInfo();
- if (vbr.NumBytes() && vbr.NumAudioFrames()) {
+ if (vbr.IsComplete()) {
offset = mFirstFrameOffset + aFrameIndex * vbr.NumBytes().value() /
vbr.NumAudioFrames().value();
} else if (AverageFrameLength() > 0) {
@@ -548,7 +549,7 @@ MP3TrackDemuxer::FrameIndexFromOffset(int64_t aOffset) const {
int64_t frameIndex = 0;
const auto& vbr = mParser.VBRInfo();
- if (vbr.NumBytes() && vbr.NumAudioFrames()) {
+ if (vbr.IsComplete()) {
frameIndex = static_cast(aOffset - mFirstFrameOffset) /
vbr.NumBytes().value() * vbr.NumAudioFrames().value();
frameIndex = std::min(vbr.NumAudioFrames().value(), frameIndex);
@@ -624,7 +625,7 @@ MP3TrackDemuxer::AverageFrameLength() const {
return static_cast(mTotalFrameLen) / mNumParsedFrames;
}
const auto& vbr = mParser.VBRInfo();
- if (vbr.NumBytes() && vbr.NumAudioFrames()) {
+ if (vbr.IsComplete() && vbr.NumAudioFrames().value() + 1) {
return static_cast(vbr.NumBytes().value()) /
(vbr.NumAudioFrames().value() + 1);
}
@@ -960,6 +961,21 @@ FrameParser::VBRHeader::IsTOCPresent() const {
return mTOC.size() == vbr_header::TOC_SIZE;
}
+bool
+FrameParser::VBRHeader::IsValid() const {
+ return mType != NONE;
+}
+
+bool
+FrameParser::VBRHeader::IsComplete() const {
+ return IsValid() &&
+ mNumAudioFrames.valueOr(0) > 0 &&
+ mNumBytes.valueOr(0) > 0 &&
+ // We don't care about the scale for any computations here.
+ // mScale < 101 &&
+ true;
+}
+
int64_t
FrameParser::VBRHeader::Offset(float aDurationFac) const {
if (!IsTOCPresent()) {
diff --git a/dom/media/MP3Demuxer.h b/dom/media/MP3Demuxer.h
index f42126d76..54e380a9c 100644
--- a/dom/media/MP3Demuxer.h
+++ b/dom/media/MP3Demuxer.h
@@ -232,6 +232,12 @@ public:
// Returns true iff Xing/Info TOC (table of contents) is present.
bool IsTOCPresent() const;
+ // Returns whether the header is valid (type XING or VBRI).
+ bool IsValid() const;
+
+ // Returns whether the header is valid and contains reasonable non-zero field values.
+ bool IsComplete() const;
+
// Returns the byte offset for the given duration percentage as a factor
// (0: begin, 1.0: end).
int64_t Offset(float aDurationFac) const;
diff --git a/js/src/asmjs/AsmJSModule.cpp b/js/src/asmjs/AsmJSModule.cpp
index 9e707d06d..05b115342 100644
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -18,6 +18,7 @@
#include "asmjs/AsmJSModule.h"
+#include "mozilla/Atomics.h"
#include "mozilla/BinarySearch.h"
#include "mozilla/Compression.h"
#include "mozilla/EnumeratedRange.h"
@@ -51,6 +52,7 @@ using namespace js;
using namespace js::jit;
using namespace js::wasm;
using namespace js::frontend;
+using mozilla::Atomic;
using mozilla::BinarySearch;
using mozilla::Compression::LZ4;
using mozilla::MakeEnumeratedRange;
@@ -61,6 +63,12 @@ using mozilla::PodZero;
using mozilla::Swap;
using JS::GenericNaN;
+// Limit the number of concurrent wasm code allocations per process. Note that
+// on Linux, the real maximum is ~32k, as each module requires 2 maps (RW/RX),
+// and the kernel's default max_map_count is ~65k.
+static Atomic wasmCodeAllocations(0);
+static const uint32_t MaxWasmCodeAllocations = 16384;
+
static uint8_t*
AllocateExecutableMemory(ExclusiveContext* cx, size_t bytes)
{
@@ -68,9 +76,14 @@ AllocateExecutableMemory(ExclusiveContext* cx, size_t bytes)
// a multiple of ExecutableCodePageSize.
bytes = JS_ROUNDUP(bytes, ExecutableCodePageSize);
- void* p = AllocateExecutableMemory(bytes, ProtectionSetting::Writable);
- if (!p)
+ void* p = nullptr;
+ if (wasmCodeAllocations++ < MaxWasmCodeAllocations)
+ p = AllocateExecutableMemory(bytes, ProtectionSetting::Writable);
+ if (!p) {
+ wasmCodeAllocations--;
ReportOutOfMemory(cx);
+ }
+
return (uint8_t*)p;
}
@@ -122,6 +135,8 @@ AsmJSModule::~AsmJSModule()
}
uint32_t size = JS_ROUNDUP(pod.totalBytes_, ExecutableCodePageSize);
+ MOZ_ASSERT(wasmCodeAllocations > 0);
+ wasmCodeAllocations--;
DeallocateExecutableMemory(code_, size);
}
diff --git a/js/src/irregexp/NativeRegExpMacroAssembler.cpp b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
index 4255f9595..003000cc3 100644
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -465,8 +465,6 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext* cx, bool match_only)
writePerfSpewerJitCodeProfile(code, "RegExp");
#endif
- AutoWritableJitCode awjc(code);
-
for (size_t i = 0; i < labelPatches.length(); i++) {
LabelPatch& v = labelPatches[i];
MOZ_ASSERT(!v.label);
diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp
index 197763852..a13394a8b 100644
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -249,14 +249,12 @@ BaselineCompiler::compile()
// All barriers are emitted off-by-default, toggle them on if needed.
if (cx->zone()->needsIncrementalBarrier())
- baselineScript->toggleBarriers(true);
+ baselineScript->toggleBarriers(true, DontReprotect);
// If profiler instrumentation is enabled, toggle instrumentation on.
if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
baselineScript->toggleProfilerInstrumentation(true);
- AutoWritableJitCode awjc(code);
-
// Patch IC loads using IC entries.
for (size_t i = 0; i < icLoadLabels_.length(); i++) {
CodeOffset label = icLoadLabels_[i].label;
diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp
index 293868a74..7d45707a1 100644
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -1028,8 +1028,6 @@ BaselineScript::toggleProfilerInstrumentation(bool enable)
JitSpew(JitSpew_BaselineIC, " toggling profiling %s for BaselineScript %p",
enable ? "on" : "off", this);
- AutoWritableJitCode awjc(method());
-
// Toggle the jump
CodeLocationLabel enterToggleLocation(method_, CodeOffset(profilerEnterToggleOffset_));
CodeLocationLabel exitToggleLocation(method_, CodeOffset(profilerExitToggleOffset_));
@@ -1141,11 +1139,16 @@ jit::AddSizeOfBaselineData(JSScript* script, mozilla::MallocSizeOf mallocSizeOf,
void
jit::ToggleBaselineProfiling(JSRuntime* runtime, bool enable)
{
+ JitRuntime* jrt = runtime->jitRuntime();
+ if (!jrt)
+ return;
+
for (ZonesIter zone(runtime, SkipAtoms); !zone.done(); zone.next()) {
for (gc::ZoneCellIter i(zone, gc::AllocKind::SCRIPT); !i.done(); i.next()) {
JSScript* script = i.get();
if (!script->hasBaselineScript())
continue;
+ AutoWritableJitCode awjc(script->baselineScript()->method());
script->baselineScript()->toggleProfilerInstrumentation(enable);
}
}
diff --git a/js/src/jit/BaselineJIT.h b/js/src/jit/BaselineJIT.h
index 1b35e6e3a..08ff74ec1 100644
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -356,8 +356,8 @@ struct BaselineScript
templateScope_ = templateScope;
}
- void toggleBarriers(bool enabled) {
- method()->togglePreBarriers(enabled);
+ void toggleBarriers(bool enabled, ReprotectCode reprotect = Reprotect) {
+ method()->togglePreBarriers(enabled, reprotect);
}
bool containsCodeAddress(uint8_t* addr) const {
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index 7e50379d2..3cff04052 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -1446,7 +1446,7 @@ JitCompartment::generateRegExpExecStub(JSContext* cx)
#endif
if (cx->zone()->needsIncrementalBarrier())
- code->togglePreBarriers(true);
+ code->togglePreBarriers(true, DontReprotect);
return code;
}
@@ -1579,7 +1579,7 @@ JitCompartment::generateRegExpTestStub(JSContext* cx)
#endif
if (cx->zone()->needsIncrementalBarrier())
- code->togglePreBarriers(true);
+ code->togglePreBarriers(true, DontReprotect);
return code;
}
@@ -8263,61 +8263,58 @@ CodeGenerator::link(JSContext* cx, CompilerConstraintList* constraints)
// Adopt fallback shared stubs from the compiler into the ion script.
ionScript->adoptFallbackStubs(&stubSpace_);
- {
- AutoWritableJitCode awjc(code);
- Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, invalidateEpilogueData_),
+ Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, invalidateEpilogueData_),
+ ImmPtr(ionScript),
+ ImmPtr((void*)-1));
+
+ for (size_t i = 0; i < ionScriptLabels_.length(); i++) {
+ Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, ionScriptLabels_[i]),
ImmPtr(ionScript),
ImmPtr((void*)-1));
-
- for (size_t i = 0; i < ionScriptLabels_.length(); i++) {
- Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, ionScriptLabels_[i]),
- ImmPtr(ionScript),
- ImmPtr((void*)-1));
- }
+ }
#ifdef JS_TRACE_LOGGING
- TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
- for (uint32_t i = 0; i < patchableTraceLoggers_.length(); i++) {
- Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, patchableTraceLoggers_[i]),
- ImmPtr(logger),
- ImmPtr(nullptr));
- }
-
- if (patchableTLScripts_.length() > 0) {
- MOZ_ASSERT(TraceLogTextIdEnabled(TraceLogger_Scripts));
- TraceLoggerEvent event(logger, TraceLogger_Scripts, script);
- ionScript->setTraceLoggerEvent(event);
- uint32_t textId = event.payload()->textId();
- for (uint32_t i = 0; i < patchableTLScripts_.length(); i++) {
- Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, patchableTLScripts_[i]),
- ImmPtr((void*) uintptr_t(textId)),
- ImmPtr((void*)0));
- }
- }
-#endif
- // Patch shared stub IC loads using IC entries
- for (size_t i = 0; i < sharedStubs_.length(); i++) {
- CodeOffset label = sharedStubs_[i].label;
-
- IonICEntry& entry = ionScript->sharedStubList()[i];
- entry = sharedStubs_[i].entry;
- Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label),
- ImmPtr(&entry),
- ImmPtr((void*)-1));
-
- MOZ_ASSERT(entry.hasStub());
- MOZ_ASSERT(entry.firstStub()->isFallback());
-
- entry.firstStub()->toFallbackStub()->fixupICEntry(&entry);
- }
-
- // for generating inline caches during the execution.
- if (runtimeData_.length())
- ionScript->copyRuntimeData(&runtimeData_[0]);
- if (cacheList_.length())
- ionScript->copyCacheEntries(&cacheList_[0], masm);
+ TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
+ for (uint32_t i = 0; i < patchableTraceLoggers_.length(); i++) {
+ Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, patchableTraceLoggers_[i]),
+ ImmPtr(logger),
+ ImmPtr(nullptr));
}
+ if (patchableTLScripts_.length() > 0) {
+ MOZ_ASSERT(TraceLogTextIdEnabled(TraceLogger_Scripts));
+ TraceLoggerEvent event(logger, TraceLogger_Scripts, script);
+ ionScript->setTraceLoggerEvent(event);
+ uint32_t textId = event.payload()->textId();
+ for (uint32_t i = 0; i < patchableTLScripts_.length(); i++) {
+ Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, patchableTLScripts_[i]),
+ ImmPtr((void*) uintptr_t(textId)),
+ ImmPtr((void*)0));
+ }
+ }
+#endif
+ // Patch shared stub IC loads using IC entries
+ for (size_t i = 0; i < sharedStubs_.length(); i++) {
+ CodeOffset label = sharedStubs_[i].label;
+
+ IonICEntry& entry = ionScript->sharedStubList()[i];
+ entry = sharedStubs_[i].entry;
+ Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label),
+ ImmPtr(&entry),
+ ImmPtr((void*)-1));
+
+ MOZ_ASSERT(entry.hasStub());
+ MOZ_ASSERT(entry.firstStub()->isFallback());
+
+ entry.firstStub()->toFallbackStub()->fixupICEntry(&entry);
+ }
+
+ // for generating inline caches during the execution.
+ if (runtimeData_.length())
+ ionScript->copyRuntimeData(&runtimeData_[0]);
+ if (cacheList_.length())
+ ionScript->copyCacheEntries(&cacheList_[0], masm);
+
JitSpew(JitSpew_Codegen, "Created IonScript %p (raw %p)",
(void*) ionScript, (void*) code->raw());
@@ -8367,7 +8364,7 @@ CodeGenerator::link(JSContext* cx, CompilerConstraintList* constraints)
// since a GC can occur during code generation. All barriers are emitted
// off-by-default, and are toggled on here if necessary.
if (cx->zone()->needsIncrementalBarrier())
- ionScript->toggleBarriers(true);
+ ionScript->toggleBarriers(true, DontReprotect);
// Attach any generated script counts to the script.
if (IonScriptCounts* counts = extractScriptCounts())
diff --git a/js/src/jit/ExecutableAllocator.h b/js/src/jit/ExecutableAllocator.h
index cc0ad2b66..b3cebaa7f 100644
--- a/js/src/jit/ExecutableAllocator.h
+++ b/js/src/jit/ExecutableAllocator.h
@@ -372,10 +372,12 @@ class ExecutableAllocator
static void makeWritable(void* start, size_t size)
{
+ ReprotectRegion(start, size, ProtectionSetting::Writable);
}
static void makeExecutable(void* start, size_t size)
{
+ ReprotectRegion(start, size, ProtectionSetting::Executable);
}
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp
index 05ae0e2ca..68e34c59f 100644
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -744,14 +744,14 @@ JitCompartment::toggleBarriers(bool enabled)
{
// Toggle barriers in compartment wide stubs that have patchable pre barriers.
if (regExpExecStub_)
- regExpExecStub_->togglePreBarriers(enabled);
+ regExpExecStub_->togglePreBarriers(enabled, Reprotect);
if (regExpTestStub_)
- regExpTestStub_->togglePreBarriers(enabled);
+ regExpTestStub_->togglePreBarriers(enabled, Reprotect);
// Toggle barriers in baseline IC stubs.
for (ICStubCodeMap::Enum e(*stubCodes_); !e.empty(); e.popFront()) {
JitCode* code = *e.front().value().unsafeGet();
- code->togglePreBarriers(enabled);
+ code->togglePreBarriers(enabled, Reprotect);
}
}
@@ -879,20 +879,23 @@ JitCode::finalize(FreeOp* fop)
}
void
-JitCode::togglePreBarriers(bool enabled)
+JitCode::togglePreBarriers(bool enabled, ReprotectCode reprotect)
{
- AutoWritableJitCode awjc(this);
uint8_t* start = code_ + preBarrierTableOffset();
CompactBufferReader reader(start, start + preBarrierTableBytes_);
- while (reader.more()) {
+ if (!reader.more())
+ return;
+
+ MaybeAutoWritableJitCode awjc(this, reprotect);
+ do {
size_t offset = reader.readUnsigned();
CodeLocationLabel loc(this, CodeOffset(offset));
if (enabled)
Assembler::ToggleToCmp(loc);
else
Assembler::ToggleToJmp(loc);
- }
+ } while (reader.more());
}
IonScript::IonScript()
@@ -1270,9 +1273,9 @@ IonScript::Destroy(FreeOp* fop, IonScript* script)
}
void
-IonScript::toggleBarriers(bool enabled)
+IonScript::toggleBarriers(bool enabled, ReprotectCode reprotect)
{
- method()->togglePreBarriers(enabled);
+ method()->togglePreBarriers(enabled, reprotect);
}
void
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 2ef59b496..8f2c33bab 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6374,7 +6374,12 @@ IonBuilder::jsop_funapply(uint32_t argc)
if (argument->type() != MIRType_MagicOptimizedArguments) {
// Optimize fun.apply(self, array) if the length is sane and there are no holes.
TemporaryTypeSet* objTypes = argument->resultTypeSet();
- if (native && native->isNative() && native->native() == fun_apply &&
+#ifdef XP_WIN
+ bool opt = false;
+#else
+ bool opt = true;
+#endif
+ if (opt && native && native->isNative() && native->native() == fun_apply &&
objTypes &&
objTypes->getKnownClass(constraints()) == &ArrayObject::class_ &&
!objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW) &&
diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp
index 869652138..1f4af1e29 100644
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -99,20 +99,6 @@ IonCache::CacheName(IonCache::Kind kind)
return names[kind];
}
-IonCache::LinkStatus
-IonCache::linkCode(JSContext* cx, MacroAssembler& masm, IonScript* ion, JitCode** code)
-{
- Linker linker(masm);
- *code = linker.newCode(cx, ION_CODE);
- if (!*code)
- return LINK_ERROR;
-
- if (ion->invalidated())
- return CACHE_FLUSHED;
-
- return LINK_GOOD;
-}
-
const size_t IonCache::MAX_STUBS = 16;
// Helper class which encapsulates logic to attach a stub to an IC by hooking
@@ -239,27 +225,20 @@ class IonCache::StubAttacher
void patchRejoinJump(MacroAssembler& masm, JitCode* code) {
rejoinOffset_.fixup(&masm);
CodeLocationJump rejoinJump(code, rejoinOffset_);
- AutoWritableJitCode awjc(code);
PatchJump(rejoinJump, rejoinLabel_);
}
void patchStubCodePointer(JitCode* code) {
if (hasStubCodePatchOffset_) {
- AutoWritableJitCode awjc(code);
Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, stubCodePatchOffset_),
ImmPtr(code), STUB_ADDR);
}
}
void patchNextStubJump(MacroAssembler& masm, JitCode* code) {
- // Patch the previous nextStubJump of the last stub, or the jump from the
- // codeGen, to jump into the newly allocated code.
- PatchJump(cache_.lastJump_, CodeLocationLabel(code), Reprotect);
-
// If this path is not taken, we are producing an entry which can no
// longer go back into the update function.
if (hasNextStubOffset_) {
- AutoWritableJitCode awjc(code);
nextStubOffset_.fixup(&masm);
CodeLocationJump nextStubJump(code, nextStubOffset_);
PatchJump(nextStubJump, cache_.fallbackLabel_);
@@ -285,21 +264,41 @@ IonCache::emitInitialJump(MacroAssembler& masm, RepatchLabel& entry)
}
void
-IonCache::attachStub(MacroAssembler& masm, StubAttacher& attacher, Handle code)
+IonCache::attachStub(MacroAssembler& masm, StubAttacher& attacher, CodeLocationJump lastJump,
+ Handle code)
{
MOZ_ASSERT(canAttachStub());
incrementStubCount();
+ // Patch the previous nextStubJump of the last stub, or the jump from the
+ // codeGen, to jump into the newly allocated code.
+ PatchJump(lastJump, CodeLocationLabel(code), Reprotect);
+}
+
+IonCache::LinkStatus
+IonCache::linkCode(JSContext* cx, MacroAssembler& masm, StubAttacher& attacher, IonScript* ion,
+ JitCode** code)
+{
+ Linker linker(masm);
+ *code = linker.newCode(cx, ION_CODE);
+ if (!*code)
+ return LINK_ERROR;
+
+ if (ion->invalidated())
+ return CACHE_FLUSHED;
+
// Update the success path to continue after the IC initial jump.
- attacher.patchRejoinJump(masm, code);
+ attacher.patchRejoinJump(masm, *code);
// Replace the STUB_ADDR constant by the address of the generated stub, such
// as it can be kept alive even if the cache is flushed (see
// MarkJitExitFrame).
- attacher.patchStubCodePointer(code);
+ attacher.patchStubCodePointer(*code);
// Update the failure path.
- attacher.patchNextStubJump(masm, code);
+ attacher.patchNextStubJump(masm, *code);
+
+ return LINK_GOOD;
}
bool
@@ -307,12 +306,13 @@ IonCache::linkAndAttachStub(JSContext* cx, MacroAssembler& masm, StubAttacher& a
IonScript* ion, const char* attachKind,
JS::TrackedOutcome trackedOutcome)
{
+ CodeLocationJump lastJumpBefore = lastJump_;
Rooted code(cx);
{
// Need to exit the AutoFlushICache context to flush the cache
// before attaching the stub below.
AutoFlushICache afc("IonCache");
- LinkStatus status = linkCode(cx, masm, ion, code.address());
+ LinkStatus status = linkCode(cx, masm, attacher, ion, code.address());
if (status != LINK_GOOD)
return status != LINK_ERROR;
}
@@ -330,7 +330,7 @@ IonCache::linkAndAttachStub(JSContext* cx, MacroAssembler& masm, StubAttacher& a
writePerfSpewerJitCodeProfile(code, "IonCache");
#endif
- attachStub(masm, attacher, code);
+ attachStub(masm, attacher, lastJumpBefore, code);
// Add entry to native => bytecode mapping for this stub if needed.
if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) {
diff --git a/js/src/jit/IonCaches.h b/js/src/jit/IonCaches.h
index 2600c271f..9655be2f2 100644
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -293,10 +293,13 @@ class IonCache
// monitoring/allocation caused an invalidation of the running ion script,
// this function returns CACHE_FLUSHED. In case of allocation issue this
// function returns LINK_ERROR.
- LinkStatus linkCode(JSContext* cx, MacroAssembler& masm, IonScript* ion, JitCode** code);
+ LinkStatus linkCode(JSContext* cx, MacroAssembler& masm, StubAttacher& attacher, IonScript* ion,
+ JitCode** code);
+
// Fixup variables and update jumps in the list of stubs. Increment the
// number of attached stubs accordingly.
- void attachStub(MacroAssembler& masm, StubAttacher& attacher, Handle code);
+ void attachStub(MacroAssembler& masm, StubAttacher& attacher, CodeLocationJump lastJump,
+ Handle code);
// Combine both linkStub and attachStub into one function. In addition, it
// produces a spew augmented with the attachKind string.
diff --git a/js/src/jit/IonCode.h b/js/src/jit/IonCode.h
index 8156b9cb8..263d1d038 100644
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -123,7 +123,7 @@ class JitCode : public gc::TenuredCell
hasBytecodeMap_ = true;
}
- void togglePreBarriers(bool enabled);
+ void togglePreBarriers(bool enabled, ReprotectCode reprotect);
// If this JitCode object has been, effectively, corrupted due to
// invalidation patching, then we have to remember this so we don't try and
@@ -512,7 +512,7 @@ struct IonScript
MOZ_ASSERT(locIndex < runtimeSize_);
return (CacheLocation*) &runtimeData()[locIndex];
}
- void toggleBarriers(bool enabled);
+ void toggleBarriers(bool enabled, ReprotectCode reprotect = Reprotect);
void purgeCaches();
void unlinkFromRuntime(FreeOp* fop);
void copySnapshots(const SnapshotWriter* writer);
diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h
index 8030f1609..471cc4205 100644
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -776,6 +776,8 @@ enum class BarrierKind : uint32_t {
TypeSet
};
+enum ReprotectCode { Reprotect = true, DontReprotect = false };
+
} // namespace jit
} // namespace js
diff --git a/js/src/jit/JitCompartment.h b/js/src/jit/JitCompartment.h
index a5364cd85..5f176648b 100644
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -518,8 +518,6 @@ class MOZ_STACK_CLASS AutoWritableJitCode
}
};
-enum ReprotectCode { Reprotect = true, DontReprotect = false };
-
class MOZ_STACK_CLASS MaybeAutoWritableJitCode
{
mozilla::Maybe awjc_;
diff --git a/js/src/jit/Linker.h b/js/src/jit/Linker.h
index 4575d2e31..a0fd487da 100644
--- a/js/src/jit/Linker.h
+++ b/js/src/jit/Linker.h
@@ -22,6 +22,7 @@ namespace jit {
class Linker
{
MacroAssembler& masm;
+ mozilla::Maybe awjc;
JitCode* fail(JSContext* cx) {
ReportOutOfMemory(cx);
@@ -68,7 +69,7 @@ class Linker
return nullptr;
if (masm.oom())
return fail(cx);
- AutoWritableJitCode awjc(result, bytesNeeded);
+ awjc.emplace(result, bytesNeeded);
code->copyFrom(masm);
masm.link(code);
if (masm.embedsNurseryPointers())
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index aab664210..717e5557c 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2304,17 +2304,7 @@ LIRGenerator::visitFunctionEnvironment(MFunctionEnvironment* ins)
void
LIRGenerator::visitInterruptCheck(MInterruptCheck* ins)
{
- // Implicit interrupt checks require asm.js signal handlers to be installed.
- // They also require writable JIT code: reprotecting in patchIonBackedges
- // would be expensive and using AutoWritableJitCode in the signal handler
- // is complicated because there could be another AutoWritableJitCode on the
- // stack.
- LInstructionHelper<0, 0, 0>* lir;
- if (GetJitContext()->runtime->canUseSignalHandlers()) {
- lir = new(alloc()) LInterruptCheckImplicit();
- } else {
- lir = new(alloc()) LInterruptCheck();
- }
+ LInstructionHelper<0, 0, 0>* lir = new(alloc()) LInterruptCheck();
add(lir, ins);
assignSafepoint(lir, ins);
}
diff --git a/js/src/jit/ProcessExecutableMemory.cpp b/js/src/jit/ProcessExecutableMemory.cpp
index f34cd458e..2a18210bf 100644
--- a/js/src/jit/ProcessExecutableMemory.cpp
+++ b/js/src/jit/ProcessExecutableMemory.cpp
@@ -219,7 +219,12 @@ DeallocateProcessExecutableMemory(void* addr, size_t bytes)
static DWORD
ProtectionSettingToFlags(ProtectionSetting protection)
{
- return PAGE_EXECUTE_READWRITE;
+ switch (protection) {
+ case ProtectionSetting::Protected: return PAGE_NOACCESS;
+ case ProtectionSetting::Writable: return PAGE_READWRITE;
+ case ProtectionSetting::Executable: return PAGE_EXECUTE_READ;
+ }
+ MOZ_CRASH();
}
static void
@@ -283,7 +288,12 @@ DeallocateProcessExecutableMemory(void* addr, size_t bytes)
static unsigned
ProtectionSettingToFlags(ProtectionSetting protection)
{
- return PROT_READ | PROT_WRITE | PROT_EXEC;
+ switch (protection) {
+ case ProtectionSetting::Protected: return PROT_NONE;
+ case ProtectionSetting::Writable: return PROT_READ | PROT_WRITE;
+ case ProtectionSetting::Executable: return PROT_READ | PROT_EXEC;
+ }
+ MOZ_CRASH();
}
static void
@@ -605,3 +615,37 @@ js::jit::CanLikelyAllocateMoreExecutableMemory()
return execMemory.bytesAllocated() + BufferSize <= MaxCodeBytesPerProcess;
}
+
+bool
+js::jit::ReprotectRegion(void* start, size_t size, ProtectionSetting protection)
+{
+ // Calculate the start of the page containing this region,
+ // and account for this extra memory within size.
+ size_t pageSize = gc::SystemPageSize();
+ intptr_t startPtr = reinterpret_cast(start);
+ intptr_t pageStartPtr = startPtr & ~(pageSize - 1);
+ void* pageStart = reinterpret_cast(pageStartPtr);
+ size += (startPtr - pageStartPtr);
+
+ // Round size up
+ size += (pageSize - 1);
+ size &= ~(pageSize - 1);
+
+ MOZ_ASSERT((uintptr_t(pageStart) % pageSize) == 0);
+
+ execMemory.assertValidAddress(pageStart, size);
+
+ #ifdef XP_WIN
+ DWORD oldProtect;
+ DWORD flags = ProtectionSettingToFlags(protection);
+ if (!VirtualProtect(pageStart, size, flags, &oldProtect))
+ return false;
+ #else
+ unsigned flags = ProtectionSettingToFlags(protection);
+ if (mprotect(pageStart, size, flags))
+ return false;
+ #endif
+
+ execMemory.assertValidAddress(pageStart, size);
+ return true;
+}
diff --git a/js/src/jit/ProcessExecutableMemory.h b/js/src/jit/ProcessExecutableMemory.h
index 7884706e3..078ce7cb7 100644
--- a/js/src/jit/ProcessExecutableMemory.h
+++ b/js/src/jit/ProcessExecutableMemory.h
@@ -23,6 +23,8 @@ enum class ProtectionSetting {
Executable,
};
+extern MOZ_MUST_USE bool ReprotectRegion(void* start, size_t size, ProtectionSetting protection);
+
// Functions called at process start-up/shutdown to initialize/release the
// executable memory region.
extern MOZ_MUST_USE bool InitProcessExecutableMemory();
diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp
index a64df068b..4e305886b 100644
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -748,7 +748,7 @@ ICStubCompiler::getStubCode()
// All barriers are emitted off-by-default, enable them if needed.
if (cx->zone()->needsIncrementalBarrier())
- newStubCode->togglePreBarriers(true);
+ newStubCode->togglePreBarriers(true, DontReprotect);
// Cache newly compiled stubcode.
if (!comp->putStubCode(cx, stubKey, newStubCode))
diff --git a/layout/xul/nsXULPopupManager.cpp b/layout/xul/nsXULPopupManager.cpp
index a3aa753a5..1107f0259 100644
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -2517,6 +2517,12 @@ nsXULPopupManager::KeyDown(nsIDOMKeyEvent* aKeyEvent)
if (!mActiveMenuBar && (!item || item->PopupType() != ePopupTypeMenu))
return NS_OK;
+ // Since a menu was open, stop propagation of the event to keep other event
+ // listeners from becoming confused.
+ if (!item || item->IgnoreKeys() != eIgnoreKeys_Handled) {
+ aKeyEvent->StopPropagation();
+ }
+
int32_t menuAccessKey = -1;
// If the key just pressed is the access key (usually Alt),
@@ -2547,19 +2553,15 @@ nsXULPopupManager::KeyDown(nsIDOMKeyEvent* aKeyEvent)
Rollup(0, false, nullptr, nullptr);
else if (mActiveMenuBar)
mActiveMenuBar->MenuClosed();
+
+ // Clear the item to avoid bugs as it may have been deleted during rollup.
+ item = nullptr;
}
aKeyEvent->StopPropagation();
aKeyEvent->PreventDefault();
}
}
- // Since a menu was open, stop propagation of the event to keep other event
- // listeners from becoming confused.
-
- if (!item || item->IgnoreKeys() != eIgnoreKeys_Handled) {
- aKeyEvent->StopPropagation();
- }
-
aKeyEvent->StopCrossProcessForwarding();
return NS_OK;
}
diff --git a/testing/mochitest/chrome/test_sanityChromeUtils.xul b/testing/mochitest/chrome/test_sanityChromeUtils.xul
index ca1915b37..f3ac45d6f 100644
--- a/testing/mochitest/chrome/test_sanityChromeUtils.xul
+++ b/testing/mochitest/chrome/test_sanityChromeUtils.xul
@@ -65,7 +65,9 @@
var dragfile = [[
{ type : "application/x-moz-file",
data : testFile,
- eqTest : function(actualData, expectedData) {return expectedData.equals(actualData);} }
+ eqTest : function(actualData, expectedData) {return expectedData.equals(actualData);} },
+ { type : "Files",
+ data : null }
]];
function doOnDrop(aEvent) {
diff --git a/toolkit/content/tests/widgets/window_menubar.xul b/toolkit/content/tests/widgets/window_menubar.xul
index 41b74f356..b7669e0b3 100644
--- a/toolkit/content/tests/widgets/window_menubar.xul
+++ b/toolkit/content/tests/widgets/window_menubar.xul
@@ -706,6 +706,30 @@ var popupTests = [
}
},
+{
+ testname: "Open menu and press alt key by itself - open menu",
+ events: [ "DOMMenuBarActive menubar",
+ "popupshowing filepopup", "DOMMenuItemActive filemenu",
+ "DOMMenuItemActive item1", "popupshown filepopup" ],
+ test: function() { synthesizeKey("F", { altKey: true }); },
+ result: function (testname) {
+ checkOpen("filemenu", testname);
+ }
+},
+{
+ testname: "Open menu and press alt key by itself - close menu",
+ events: [ "popuphiding filepopup", "popuphidden filepopup",
+ "DOMMenuItemInactive item1", "DOMMenuInactive filepopup",
+ "DOMMenuBarInactive menubar", "DOMMenuItemInactive filemenu",
+ "DOMMenuItemInactive filemenu" ],
+ test: function() {
+ synthesizeKey("VK_ALT", { });
+ },
+ result: function (testname) {
+ checkClosed("filemenu", testname);
+ }
+},
+
// Fllowing 4 tests are a test of bug 616797, don't insert any new tests
// between them.
{