mirror of
https://github.com/roytam1/mozilla45esr.git
synced 2026-05-26 06:25:03 +00:00
import changes from torbrowser-esr45.9-6.5:
bug21795, bug21514, bug1238694, bug1234246, bug1249522, bug1291543, bug1263334, bug1236639, bug1266963
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
<html><body>
|
||||
<script>
|
||||
function checkPaste(event)
|
||||
{
|
||||
let output = document.getElementById("output");
|
||||
output.textContent = checkPasteHelper(event);
|
||||
}
|
||||
|
||||
function checkPasteHelper(event)
|
||||
{
|
||||
let dt = event.clipboardData;
|
||||
if (dt.types.length != 2)
|
||||
return "Wrong number of types; got " + dt.types.length;
|
||||
|
||||
for (let type of dt.types) {
|
||||
if (type != "Files" && type != "application/x-moz-file")
|
||||
return "Invalid type for types; got" + type;
|
||||
}
|
||||
|
||||
for (let type of dt.mozTypesAt(0)) {
|
||||
if (type != "Files" && type != "application/x-moz-file")
|
||||
return "Invalid type for mozTypesAt; got" + type;
|
||||
}
|
||||
|
||||
if (dt.getData("text/plain"))
|
||||
return "text/plain found with getData";
|
||||
if (dt.mozGetDataAt("text/plain", 0))
|
||||
return "text/plain found with mozGetDataAt";
|
||||
|
||||
return "Passed";
|
||||
}
|
||||
</script>
|
||||
|
||||
<input id="input" onpaste="checkPaste(event)">
|
||||
<div id="output"></div>
|
||||
|
||||
</body></html>
|
||||
+36
-24
@@ -359,25 +359,8 @@ DataTransfer::GetFiles(nsIDOMFileList** aFileList)
|
||||
already_AddRefed<DOMStringList>
|
||||
DataTransfer::Types()
|
||||
{
|
||||
RefPtr<DOMStringList> types = new DOMStringList();
|
||||
if (mItems.Length()) {
|
||||
bool addFile = false;
|
||||
const nsTArray<TransferItem>& 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<DOMStringList>
|
||||
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<DOMStringList> types = new DOMStringList();
|
||||
if (aIndex < mItems.Length()) {
|
||||
bool addFile = false;
|
||||
// note that you can retrieve the types regardless of their principal
|
||||
nsTArray<TransferItem>& item = mItems[aIndex];
|
||||
for (uint32_t i = 0; i < item.Length(); i++)
|
||||
types->Add(item[i].mFormat);
|
||||
const nsTArray<TransferItem>& 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<TransferItem>& 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
|
||||
|
||||
@@ -165,7 +165,8 @@ public:
|
||||
}
|
||||
}
|
||||
already_AddRefed<DOMStringList> 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,
|
||||
|
||||
@@ -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<float>(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<float>(aOffset - mFirstFrameOffset) /
|
||||
vbr.NumBytes().value() * vbr.NumAudioFrames().value();
|
||||
frameIndex = std::min<int64_t>(vbr.NumAudioFrames().value(), frameIndex);
|
||||
@@ -624,7 +625,7 @@ MP3TrackDemuxer::AverageFrameLength() const {
|
||||
return static_cast<double>(mTotalFrameLen) / mNumParsedFrames;
|
||||
}
|
||||
const auto& vbr = mParser.VBRInfo();
|
||||
if (vbr.NumBytes() && vbr.NumAudioFrames()) {
|
||||
if (vbr.IsComplete() && vbr.NumAudioFrames().value() + 1) {
|
||||
return static_cast<double>(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()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<uint32_t> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<JSScript>();
|
||||
if (!script->hasBaselineScript())
|
||||
continue;
|
||||
AutoWritableJitCode awjc(script->baselineScript()->method());
|
||||
script->baselineScript()->toggleProfilerInstrumentation(enable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
|
||||
+12
-9
@@ -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
|
||||
|
||||
@@ -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) &&
|
||||
|
||||
+27
-27
@@ -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<CanGC>(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<JitCode*> code)
|
||||
IonCache::attachStub(MacroAssembler& masm, StubAttacher& attacher, CodeLocationJump lastJump,
|
||||
Handle<JitCode*> 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<CanGC>(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<JitCode*> 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())) {
|
||||
|
||||
@@ -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<JitCode*> code);
|
||||
void attachStub(MacroAssembler& masm, StubAttacher& attacher, CodeLocationJump lastJump,
|
||||
Handle<JitCode*> code);
|
||||
|
||||
// Combine both linkStub and attachStub into one function. In addition, it
|
||||
// produces a spew augmented with the attachKind string.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -776,6 +776,8 @@ enum class BarrierKind : uint32_t {
|
||||
TypeSet
|
||||
};
|
||||
|
||||
enum ReprotectCode { Reprotect = true, DontReprotect = false };
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
||||
@@ -518,8 +518,6 @@ class MOZ_STACK_CLASS AutoWritableJitCode
|
||||
}
|
||||
};
|
||||
|
||||
enum ReprotectCode { Reprotect = true, DontReprotect = false };
|
||||
|
||||
class MOZ_STACK_CLASS MaybeAutoWritableJitCode
|
||||
{
|
||||
mozilla::Maybe<AutoWritableJitCode> awjc_;
|
||||
|
||||
+2
-1
@@ -22,6 +22,7 @@ namespace jit {
|
||||
class Linker
|
||||
{
|
||||
MacroAssembler& masm;
|
||||
mozilla::Maybe<AutoWritableJitCode> 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())
|
||||
|
||||
+1
-11
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<intptr_t>(start);
|
||||
intptr_t pageStartPtr = startPtr & ~(pageSize - 1);
|
||||
void* pageStart = reinterpret_cast<void*>(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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user