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. {