From 6f4615d2361267b46933f05b3b2680beeb929e0d Mon Sep 17 00:00:00 2001 From: Carl Corcoran Date: Sun, 6 Aug 2017 08:46:50 +0200 Subject: [PATCH 001/105] Bug 1333126 - Use win64 PE unwind metadata to improve client-side stack walking; r=gsvelto This includes tests that cover both regular CFI stack walking as well as pathological corner cases. MozReview-Commit-ID: GDARnPSemyu --HG-- extra : source : 1a140f07e06de7e7d96a37d693db832311b301ae --- .../minidump-analyzer/MinidumpAnalyzerUtils.h | 149 +++++++ .../MozStackFrameSymbolizer.cpp | 120 ++++++ .../MozStackFrameSymbolizer.h | 48 +++ .../Win64ModuleUnwindMetadata.cpp | 265 ++++++++++++ .../Win64ModuleUnwindMetadata.h | 57 +++ .../minidump-analyzer/minidump-analyzer.cpp | 116 +----- .../crashreporter/minidump-analyzer/moz.build | 12 + toolkit/crashreporter/test/CrashTestUtils.jsm | 19 + toolkit/crashreporter/test/moz.build | 5 + toolkit/crashreporter/test/nsTestCrasher.cpp | 105 +++++ .../test/unit/head_crashreporter.js | 41 +- .../crashreporter/test/unit/head_win64cfi.js | 193 +++++++++ .../unit/test_crash_win64cfi_alloc_large.js | 7 + .../unit/test_crash_win64cfi_alloc_small.js | 7 + .../test/unit/test_crash_win64cfi_epilog.js | 7 + ...est_crash_win64cfi_infinite_code_chain.exe | Bin 0 -> 1440 bytes ...test_crash_win64cfi_infinite_code_chain.js | 17 + ...st_crash_win64cfi_infinite_entry_chain.exe | Bin 0 -> 1440 bytes ...est_crash_win64cfi_infinite_entry_chain.js | 17 + ...t_crash_win64cfi_invalid_exception_rva.exe | Bin 0 -> 1440 bytes ...st_crash_win64cfi_invalid_exception_rva.js | 16 + .../unit/test_crash_win64cfi_not_a_pe.exe | 1 + .../test/unit/test_crash_win64cfi_not_a_pe.js | 15 + .../unit/test_crash_win64cfi_push_nonvol.js | 7 + .../unit/test_crash_win64cfi_save_nonvol.js | 7 + .../test_crash_win64cfi_save_nonvol_far.js | 7 + .../unit/test_crash_win64cfi_save_xmm128.js | 7 + .../test_crash_win64cfi_save_xmm128_far.js | 7 + .../unit/test_crash_win64cfi_unknown_op.js | 13 + toolkit/crashreporter/test/unit/xpcshell.ini | 62 +++ .../test/win64UnwindInfoTests.asm | 382 ++++++++++++++++++ 31 files changed, 1606 insertions(+), 103 deletions(-) create mode 100644 toolkit/crashreporter/minidump-analyzer/MinidumpAnalyzerUtils.h create mode 100644 toolkit/crashreporter/minidump-analyzer/MozStackFrameSymbolizer.cpp create mode 100644 toolkit/crashreporter/minidump-analyzer/MozStackFrameSymbolizer.h create mode 100644 toolkit/crashreporter/minidump-analyzer/Win64ModuleUnwindMetadata.cpp create mode 100644 toolkit/crashreporter/minidump-analyzer/Win64ModuleUnwindMetadata.h create mode 100644 toolkit/crashreporter/test/unit/head_win64cfi.js create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_alloc_large.js create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_alloc_small.js create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_epilog.js create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_code_chain.exe create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_code_chain.js create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_entry_chain.exe create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_entry_chain.js create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_invalid_exception_rva.exe create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_invalid_exception_rva.js create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_not_a_pe.exe create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_not_a_pe.js create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_push_nonvol.js create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_save_nonvol.js create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_save_nonvol_far.js create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_save_xmm128.js create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_save_xmm128_far.js create mode 100644 toolkit/crashreporter/test/unit/test_crash_win64cfi_unknown_op.js create mode 100644 toolkit/crashreporter/test/win64UnwindInfoTests.asm diff --git a/toolkit/crashreporter/minidump-analyzer/MinidumpAnalyzerUtils.h b/toolkit/crashreporter/minidump-analyzer/MinidumpAnalyzerUtils.h new file mode 100644 index 000000000000..e2a8bd826bfa --- /dev/null +++ b/toolkit/crashreporter/minidump-analyzer/MinidumpAnalyzerUtils.h @@ -0,0 +1,149 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MinidumpAnalyzerUtils_h +#define MinidumpAnalyzerUtils_h + +#ifdef XP_WIN +#include +#endif + +#include +#include +#include +#include + +namespace CrashReporter { + +struct MinidumpAnalyzerOptions { + bool fullMinidump; + std::string forceUseModule; +}; + +extern MinidumpAnalyzerOptions gMinidumpAnalyzerOptions; + +#ifdef XP_WIN + +#if !defined(_MSC_VER) +static inline std::string +WideToMBCP(const std::wstring& wide, unsigned int cp, bool* success = nullptr) +{ + char* buffer = nullptr; + int buffer_size = WideCharToMultiByte(cp, 0, wide.c_str(), + -1, nullptr, 0, nullptr, nullptr); + if (buffer_size == 0) { + if (success) { + *success = false; + } + + return ""; + } + + buffer = new char[buffer_size]; + if (buffer == nullptr) { + if (success) { + *success = false; + } + + return ""; + } + + WideCharToMultiByte(cp, 0, wide.c_str(), + -1, buffer, buffer_size, nullptr, nullptr); + std::string mb = buffer; + delete [] buffer; + + if (success) { + *success = true; + } + + return mb; +} +#endif /* !defined(_MSC_VER) */ + +static inline std::wstring +UTF8ToWide(const std::string& aUtf8Str, bool *aSuccess = nullptr) +{ + wchar_t* buffer = nullptr; + int buffer_size = MultiByteToWideChar(CP_UTF8, 0, aUtf8Str.c_str(), + -1, nullptr, 0); + if (buffer_size == 0) { + if (aSuccess) { + *aSuccess = false; + } + + return L""; + } + + buffer = new wchar_t[buffer_size]; + + if (buffer == nullptr) { + if (aSuccess) { + *aSuccess = false; + } + + return L""; + } + + MultiByteToWideChar(CP_UTF8, 0, aUtf8Str.c_str(), + -1, buffer, buffer_size); + std::wstring str = buffer; + delete [] buffer; + + if (aSuccess) { + *aSuccess = true; + } + + return str; +} + +static inline std::string +WideToMBCS(const std::wstring &inp) { + int buffer_size = WideCharToMultiByte(CP_ACP, 0, inp.c_str(), -1, + nullptr, 0, NULL, NULL); + if (buffer_size == 0) { + return ""; + } + + std::vector buffer(buffer_size); + buffer[0] = 0; + + WideCharToMultiByte(CP_ACP, 0, inp.c_str(), -1, + buffer.data(), buffer_size, NULL, NULL); + + return buffer.data(); +} + +static inline std::string +UTF8toMBCS(const std::string &inp) { + std::wstring wide = UTF8ToWide(inp); + std::string ret = WideToMBCS(wide); + return ret; +} + +#endif // XP_WIN + +// Check if a file exists at the specified path + +static inline bool +FileExists(const std::string& aPath) +{ +#if defined(XP_WIN) + DWORD attrs = GetFileAttributes(UTF8ToWide(aPath).c_str()); + return (attrs != INVALID_FILE_ATTRIBUTES); +#else // Non-Windows + struct stat sb; + int ret = stat(aPath.c_str(), &sb); + if (ret == -1 || !(sb.st_mode & S_IFREG)) { + return false; + } + + return true; +#endif // XP_WIN +} + +} // namespace + +#endif // MinidumpAnalyzerUtils_h diff --git a/toolkit/crashreporter/minidump-analyzer/MozStackFrameSymbolizer.cpp b/toolkit/crashreporter/minidump-analyzer/MozStackFrameSymbolizer.cpp new file mode 100644 index 000000000000..9683bc903e40 --- /dev/null +++ b/toolkit/crashreporter/minidump-analyzer/MozStackFrameSymbolizer.cpp @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if XP_WIN && HAVE_64BIT_BUILD + +#include "MozStackFrameSymbolizer.h" + +#include "MinidumpAnalyzerUtils.h" + +#include "processor/cfi_frame_info.h" + +#include +#include +#include + +namespace CrashReporter { + + extern MinidumpAnalyzerOptions gMinidumpAnalyzerOptions; + + using google_breakpad::CFIFrameInfo; + +MozStackFrameSymbolizer::MozStackFrameSymbolizer() : + StackFrameSymbolizer(nullptr, nullptr) +{ +} + +MozStackFrameSymbolizer::SymbolizerResult +MozStackFrameSymbolizer::FillSourceLineInfo(const CodeModules* modules, + const SystemInfo* system_info, + StackFrame* stack_frame) +{ + SymbolizerResult ret = StackFrameSymbolizer::FillSourceLineInfo( + modules, system_info, stack_frame); + + if (ret == kNoError && this->HasImplementation() && + stack_frame->function_name.empty()) { + // Breakpad's Stackwalker::InstructionAddressSeemsValid only considers an + // address valid if it has associated symbols. + // + // This makes sense for complete & accurate symbols, but ours may be + // incomplete or wrong. Returning a function name tells Breakpad we + // recognize this address as code, so it's OK to use in stack scanning. + // This function is only called with addresses that land in this module. + // + // This allows us to fall back to stack scanning in the case where we were + // unable to provide CFI. + stack_frame->function_name = ""; + } + return ret; +} + +CFIFrameInfo* +MozStackFrameSymbolizer::FindCFIFrameInfo(const StackFrame* frame) +{ + std::string modulePath; + + // For unit testing, support loading a specified module instead of + // the real one. + bool moduleHasBeenReplaced = false; + if (gMinidumpAnalyzerOptions.forceUseModule.size() > 0) { + modulePath = gMinidumpAnalyzerOptions.forceUseModule; + moduleHasBeenReplaced = true; + } else { + if (!frame->module) { + return nullptr; + } + modulePath = frame->module->code_file(); + } + + // Get/create the unwind parser. + auto itMod = mModuleMap.find(modulePath); + std::shared_ptr unwindParser; + if (itMod != mModuleMap.end()) { + unwindParser = itMod->second; + } else { + unwindParser.reset(new ModuleUnwindParser(modulePath)); + mModuleMap[modulePath] = unwindParser; + } + + UnwindCFI cfi; + DWORD offsetAddr; + + if (moduleHasBeenReplaced) { + // If we are replacing a module, addresses will never line up. + // So just act like the 1st entry is correct. + offsetAddr = unwindParser->GetAnyOffsetAddr(); + } else { + offsetAddr = frame->instruction - frame->module->base_address(); + } + + if (!unwindParser->GetCFI(offsetAddr, cfi)) { + return nullptr; + } + + std::unique_ptr rules(new CFIFrameInfo()); + + static const size_t exprSize = 50; + char expr[exprSize]; + if (cfi.stackSize == 0) { + snprintf(expr, exprSize, "$rsp"); + } else { + snprintf(expr, exprSize, "$rsp %d +", cfi.stackSize); + } + rules->SetCFARule(expr); + + if (cfi.ripOffset == 0) { + snprintf(expr, exprSize, ".cfa ^"); + } else { + snprintf(expr, exprSize, ".cfa %d - ^", cfi.ripOffset); + } + rules->SetRARule(expr); + + return rules.release(); +} + +} // namespace CrashReporter + +#endif // XP_WIN && HAVE_64BIT_BUILD diff --git a/toolkit/crashreporter/minidump-analyzer/MozStackFrameSymbolizer.h b/toolkit/crashreporter/minidump-analyzer/MozStackFrameSymbolizer.h new file mode 100644 index 000000000000..49d2b75dcc12 --- /dev/null +++ b/toolkit/crashreporter/minidump-analyzer/MozStackFrameSymbolizer.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MozStackFrameSymbolizer_h +#define MozStackFrameSymbolizer_h + +#if XP_WIN && HAVE_64BIT_BUILD + +#include "Win64ModuleUnwindMetadata.h" + +#include "google_breakpad/processor/stack_frame_symbolizer.h" +#include "google_breakpad/processor/stack_frame.h" + +#include + +namespace CrashReporter { + +using google_breakpad::CodeModule; +using google_breakpad::CodeModules; +using google_breakpad::SourceLineResolverInterface; +using google_breakpad::StackFrame; +using google_breakpad::StackFrameSymbolizer; +using google_breakpad::SymbolSupplier; +using google_breakpad::SystemInfo; + +class MozStackFrameSymbolizer : public StackFrameSymbolizer { + using google_breakpad::StackFrameSymbolizer::SymbolizerResult; + + std::map> mModuleMap; + +public: + MozStackFrameSymbolizer(); + + virtual SymbolizerResult FillSourceLineInfo(const CodeModules* modules, + const SystemInfo* system_info, + StackFrame* stack_frame); + + virtual class google_breakpad::CFIFrameInfo* FindCFIFrameInfo( + const StackFrame* frame); +}; + +} // namespace CrashReporter + +#endif // XP_WIN && HAVE_64BIT_BUILD + +#endif // MozStackFrameSymbolizer_h diff --git a/toolkit/crashreporter/minidump-analyzer/Win64ModuleUnwindMetadata.cpp b/toolkit/crashreporter/minidump-analyzer/Win64ModuleUnwindMetadata.cpp new file mode 100644 index 000000000000..635218c5366d --- /dev/null +++ b/toolkit/crashreporter/minidump-analyzer/Win64ModuleUnwindMetadata.cpp @@ -0,0 +1,265 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if XP_WIN && HAVE_64BIT_BUILD + +#include "Win64ModuleUnwindMetadata.h" + +#include "MinidumpAnalyzerUtils.h" + +#include +#include +#include +#include +#include +#include + +namespace CrashReporter { + +union UnwindCode { + struct { + uint8_t offset_in_prolog; + uint8_t unwind_operation_code : 4; + uint8_t operation_info : 4; + }; + USHORT frame_offset; +}; + +enum UnwindOperationCodes { + UWOP_PUSH_NONVOL = 0, // info == register number + UWOP_ALLOC_LARGE = 1, // no info, alloc size in next 2 slots + UWOP_ALLOC_SMALL = 2, // info == size of allocation / 8 - 1 + UWOP_SET_FPREG = 3, // no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 + UWOP_SAVE_NONVOL = 4, // info == register number, offset in next slot + UWOP_SAVE_NONVOL_FAR = 5, // info == register number, offset in next 2 slots + UWOP_SAVE_XMM = 6, // Version 1; undocumented + UWOP_EPILOG = 6, // Version 2; undocumented + UWOP_SAVE_XMM_FAR = 7, // Version 1; undocumented + UWOP_SPARE = 7, // Version 2; undocumented + UWOP_SAVE_XMM128 = 8, // info == XMM reg number, offset in next slot + UWOP_SAVE_XMM128_FAR = 9, // info == XMM reg number, offset in next 2 slots + UWOP_PUSH_MACHFRAME = 10 // info == 0: no error-code, 1: error-code +}; + +struct UnwindInfo { + uint8_t version : 3; + uint8_t flags : 5; + uint8_t size_of_prolog; + uint8_t count_of_codes; + uint8_t frame_register : 4; + uint8_t frame_offset : 4; + UnwindCode unwind_code[1]; +}; + +ModuleUnwindParser::~ModuleUnwindParser() +{ + if (mImg) { + ImageUnload(mImg); + } +} + +void* +ModuleUnwindParser::RvaToVa(ULONG aRva) +{ + return ImageRvaToVa( + mImg->FileHeader, mImg->MappedAddress, aRva, &mImg->LastRvaSection); +} + +ModuleUnwindParser::ModuleUnwindParser(const std::string& aPath) + : mPath(aPath) +{ + // Convert wchar to native charset because ImageLoad only takes + // a PSTR as input. + std::string code_file = UTF8toMBCS(aPath); + + mImg = ImageLoad((PSTR)code_file.c_str(), NULL); + if (!mImg || !mImg->FileHeader) { + return; + } + + PIMAGE_OPTIONAL_HEADER64 optional_header = &mImg->FileHeader->OptionalHeader; + if (optional_header->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + return; + } + + DWORD exception_rva = optional_header-> + DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress; + + DWORD exception_size = optional_header-> + DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size; + + auto funcs = (PIMAGE_RUNTIME_FUNCTION_ENTRY)RvaToVa(exception_rva); + if (!funcs) { + return; + } + + for (DWORD i = 0; i < exception_size / sizeof(*funcs); i++) { + mUnwindMap[funcs[i].BeginAddress] = &funcs[i]; + } +} + +bool +ModuleUnwindParser::GenerateCFIForFunction(IMAGE_RUNTIME_FUNCTION_ENTRY& aFunc, + UnwindCFI& aRet) +{ + DWORD unwind_rva = aFunc.UnwindInfoAddress; + // Holds RVA to all visited IMAGE_RUNTIME_FUNCTION_ENTRY, to avoid + // circular references. + std::set visited; + + // Follow chained function entries + while (unwind_rva & 0x1) { + unwind_rva ^= 0x1; + + if (visited.end() != visited.find(unwind_rva)) { + return false; + } + visited.insert(unwind_rva); + + auto chained_func = (PIMAGE_RUNTIME_FUNCTION_ENTRY)RvaToVa(unwind_rva); + if (!chained_func) { + return false; + } + unwind_rva = chained_func->UnwindInfoAddress; + } + + visited.insert(unwind_rva); + + auto unwind_info = (UnwindInfo*)RvaToVa(unwind_rva); + if (!unwind_info) { + return false; + } + + DWORD stack_size = 8; // minimal stack size is 8 for RIP + DWORD rip_offset = 8; + do { + for (uint8_t c = 0; c < unwind_info->count_of_codes; c++) { + UnwindCode* unwind_code = &unwind_info->unwind_code[c]; + switch (unwind_code->unwind_operation_code) { + case UWOP_PUSH_NONVOL: { + stack_size += 8; + break; + } + case UWOP_ALLOC_LARGE: { + if (unwind_code->operation_info == 0) { + c++; + if (c < unwind_info->count_of_codes) { + stack_size += (unwind_code + 1)->frame_offset * 8; + } + } else { + c += 2; + if (c < unwind_info->count_of_codes) { + stack_size += (unwind_code + 1)->frame_offset | + ((unwind_code + 2)->frame_offset << 16); + } + } + break; + } + case UWOP_ALLOC_SMALL: { + stack_size += unwind_code->operation_info * 8 + 8; + break; + } + case UWOP_SET_FPREG: + // To correctly track RSP when it's been transferred to another + // register, we would need to emit CFI records for every unwind op. + // For simplicity, don't emit CFI records for this function as + // we know it will be incorrect after this point. + return false; + case UWOP_SAVE_NONVOL: + case UWOP_SAVE_XMM: // also v2 UWOP_EPILOG + case UWOP_SAVE_XMM128: { + c++; // skip slot with offset + break; + } + case UWOP_SAVE_NONVOL_FAR: + case UWOP_SAVE_XMM_FAR: // also v2 UWOP_SPARE + case UWOP_SAVE_XMM128_FAR: { + c += 2; // skip 2 slots with offset + break; + } + case UWOP_PUSH_MACHFRAME: { + if (unwind_code->operation_info) { + stack_size += 88; + } else { + stack_size += 80; + } + rip_offset += 80; + break; + } + default: { + return false; + } + } + } + + if (unwind_info->flags & UNW_FLAG_CHAININFO) { + auto chained_func = (PIMAGE_RUNTIME_FUNCTION_ENTRY)( + (unwind_info->unwind_code + + ((unwind_info->count_of_codes + 1) & ~1))); + + if (visited.end() != visited.find(chained_func->UnwindInfoAddress)) { + return false; // Circular reference + } + + visited.insert(chained_func->UnwindInfoAddress); + + unwind_info = (UnwindInfo*)RvaToVa(chained_func->UnwindInfoAddress); + } else { + unwind_info = nullptr; + } + } while (unwind_info); + + aRet.beginAddress = aFunc.BeginAddress; + aRet.size = aFunc.EndAddress - aFunc.BeginAddress; + aRet.stackSize = stack_size; + aRet.ripOffset = rip_offset; + return true; +} + +// For unit testing we sometimes need any address that's valid in this module. +// Just return the first address we know of. +DWORD +ModuleUnwindParser::GetAnyOffsetAddr() const { + if (mUnwindMap.size() < 1) { + return 0; + } + return mUnwindMap.begin()->first; +} + +bool +ModuleUnwindParser::GetCFI(DWORD aAddress, UnwindCFI& aRet) +{ + // Figure out the begin address of the requested address. + auto itUW = mUnwindMap.lower_bound(aAddress + 1); + if (itUW == mUnwindMap.begin()) { + return false; // address before this module. + } + --itUW; + + // Ensure that the function entry is big enough to contain this address. + IMAGE_RUNTIME_FUNCTION_ENTRY& func = *itUW->second; + if (aAddress > func.EndAddress) { + return false; + } + + // Do we have CFI for this function already? + auto itCFI = mCFIMap.find(aAddress); + if (itCFI != mCFIMap.end()) { + aRet = itCFI->second; + return true; + } + + // No, generate it. + if (!GenerateCFIForFunction(func, aRet)) { + return false; + } + + mCFIMap[func.BeginAddress] = aRet; + return true; +} + +} // namespace + +#endif // XP_WIN && HAVE_64BIT_BUILD diff --git a/toolkit/crashreporter/minidump-analyzer/Win64ModuleUnwindMetadata.h b/toolkit/crashreporter/minidump-analyzer/Win64ModuleUnwindMetadata.h new file mode 100644 index 000000000000..3abd80642fc7 --- /dev/null +++ b/toolkit/crashreporter/minidump-analyzer/Win64ModuleUnwindMetadata.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef Win64ModuleUnwindMetadata_h +#define Win64ModuleUnwindMetadata_h + +#if XP_WIN && HAVE_64BIT_BUILD + +#include +#include +#include + +#include +#include +#include + +namespace CrashReporter { + +struct UnwindCFI +{ + uint32_t beginAddress; + uint32_t size; + uint32_t stackSize; + uint32_t ripOffset; +}; + +// Does lazy-parsing of unwind info. +class ModuleUnwindParser { + PLOADED_IMAGE mImg; + std::string mPath; + + // Maps begin address to exception record. + // Populated upon construction. + std::map mUnwindMap; + + // Maps begin address to CFI. + // Populated as needed. + std::map mCFIMap; + + bool GenerateCFIForFunction(IMAGE_RUNTIME_FUNCTION_ENTRY& aFunc, + UnwindCFI& aRet); + void* RvaToVa(ULONG aRva); + +public: + explicit ModuleUnwindParser(const std::string& aPath); + ~ModuleUnwindParser(); + bool GetCFI(DWORD aAddress, UnwindCFI& aRet); + DWORD GetAnyOffsetAddr() const; +}; + +} // namespace + +#endif // XP_WIN && HAVE_64BIT_BUILD + +#endif // Win64ModuleUnwindMetadata_h diff --git a/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp b/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp index 3cdeee2edbb0..a68c4e585b43 100644 --- a/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp +++ b/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp @@ -32,12 +32,11 @@ #endif -// Path of the minidump to be analyzed. -static string gMinidumpPath; +#include "MinidumpAnalyzerUtils.h" -// When set to true print out the full minidump analysis, otherwise only -// include the crashing thread in the output. -static bool gFullMinidump = false; +#if XP_WIN && HAVE_64BIT_BUILD +#include "MozStackFrameSymbolizer.h" +#endif namespace CrashReporter { @@ -62,77 +61,10 @@ using google_breakpad::ProcessResult; using google_breakpad::ProcessState; using google_breakpad::StackFrame; -#ifdef XP_WIN +MinidumpAnalyzerOptions gMinidumpAnalyzerOptions; -#if !defined(_MSC_VER) -static string WideToMBCP(const wstring& wide, - unsigned int cp, - bool* success = nullptr) -{ - char* buffer = nullptr; - int buffer_size = WideCharToMultiByte(cp, 0, wide.c_str(), - -1, nullptr, 0, nullptr, nullptr); - if(buffer_size == 0) { - if (success) - *success = false; - return ""; - } - - buffer = new char[buffer_size]; - if(buffer == nullptr) { - if (success) - *success = false; - return ""; - } - - WideCharToMultiByte(cp, 0, wide.c_str(), - -1, buffer, buffer_size, nullptr, nullptr); - string mb = buffer; - delete [] buffer; - - if (success) - *success = true; - - return mb; -} -#endif /* !defined(_MSC_VER) */ - -static wstring UTF8ToWide(const string& aUtf8Str, bool *aSuccess = nullptr) -{ - wchar_t* buffer = nullptr; - int buffer_size = MultiByteToWideChar(CP_UTF8, 0, aUtf8Str.c_str(), - -1, nullptr, 0); - if (buffer_size == 0) { - if (aSuccess) { - *aSuccess = false; - } - - return L""; - } - - buffer = new wchar_t[buffer_size]; - - if (buffer == nullptr) { - if (aSuccess) { - *aSuccess = false; - } - - return L""; - } - - MultiByteToWideChar(CP_UTF8, 0, aUtf8Str.c_str(), - -1, buffer, buffer_size); - wstring str = buffer; - delete [] buffer; - - if (aSuccess) { - *aSuccess = true; - } - - return str; -} - -#endif +// Path of the minidump to be analyzed. +static string gMinidumpPath; struct ModuleCompare { bool operator() (const CodeModule* aLhs, const CodeModule* aRhs) const { @@ -317,7 +249,8 @@ ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot) // Record the crashing thread index only if this is a full minidump // and all threads' stacks are present, otherwise only the crashing // thread stack is written out and this field is set to 0. - crashInfo["crashing_thread"] = gFullMinidump ? requestingThread : 0; + crashInfo["crashing_thread"] = + gMinidumpAnalyzerOptions.fullMinidump ? requestingThread : 0; } } else { crashInfo["type"] = Json::Value(Json::nullValue); @@ -345,7 +278,7 @@ ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot) Json::Value threads(Json::arrayValue); int threadCount = aProcessState.threads()->size(); - if (!gFullMinidump && (requestingThread != -1)) { + if (!gMinidumpAnalyzerOptions.fullMinidump && (requestingThread != -1)) { // Only add the crashing thread Json::Value thread; Json::Value stack(Json::arrayValue); @@ -374,9 +307,14 @@ ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot) static bool ProcessMinidump(Json::Value& aRoot, const string& aDumpFile) { +#if XP_WIN && HAVE_64BIT_BUILD + MozStackFrameSymbolizer symbolizer; + MinidumpProcessor minidumpProcessor(&symbolizer, false); +#else BasicSourceLineResolver resolver; // We don't have a valid symbol resolver so we pass nullptr instead. MinidumpProcessor minidumpProcessor(nullptr, &resolver); +#endif // Process the minidump. Minidump dump(aDumpFile); @@ -415,25 +353,6 @@ OpenAppend(const string& aFilename) return file; } -// Check if a file exists at the specified path - -static bool -FileExists(const string& aPath) -{ -#if defined(XP_WIN) - DWORD attrs = GetFileAttributes(UTF8ToWide(aPath).c_str()); - return (attrs != INVALID_FILE_ATTRIBUTES); -#else // Non-Windows - struct stat sb; - int ret = stat(aPath.c_str(), &sb); - if (ret == -1 || !(sb.st_mode & S_IFREG)) { - return false; - } - - return true; -#endif // XP_WIN -} - // Update the extra data file by adding the StackTraces field holding the // JSON output of this program. @@ -473,7 +392,10 @@ ParseArguments(int argc, char** argv) { for (int i = 1; i < argc - 1; i++) { if (strcmp(argv[i], "--full") == 0) { - gFullMinidump = true; + gMinidumpAnalyzerOptions.fullMinidump = true; + } else if ((strcmp(argv[i], "--force-use-module") == 0) && (i < argc - 2)) { + gMinidumpAnalyzerOptions.forceUseModule = argv[i + 1]; + ++i; } else { exit(EXIT_FAILURE); } diff --git a/toolkit/crashreporter/minidump-analyzer/moz.build b/toolkit/crashreporter/minidump-analyzer/moz.build index 7c7923bb5ba7..f40d1c139494 100644 --- a/toolkit/crashreporter/minidump-analyzer/moz.build +++ b/toolkit/crashreporter/minidump-analyzer/moz.build @@ -27,6 +27,18 @@ if CONFIG['OS_TARGET'] != 'Android': if CONFIG['OS_TARGET'] == 'Darwin': DIST_SUBDIR = 'crashreporter.app/Contents/MacOS' +if CONFIG['OS_TARGET'] == 'WINNT' and CONFIG['CPU_ARCH'] == 'x86_64': + UNIFIED_SOURCES += [ + 'MozStackFrameSymbolizer.cpp', + 'Win64ModuleUnwindMetadata.cpp', + ] + + OS_LIBS += [ + 'Dbghelp', + 'Imagehlp' + ] + + # Don't use the STL wrappers in the crashreporter clients; they don't # link with -lmozalloc, and it really doesn't matter here anyway. DisableStlWrapping() diff --git a/toolkit/crashreporter/test/CrashTestUtils.jsm b/toolkit/crashreporter/test/CrashTestUtils.jsm index 83be13562256..ba5b15742110 100644 --- a/toolkit/crashreporter/test/CrashTestUtils.jsm +++ b/toolkit/crashreporter/test/CrashTestUtils.jsm @@ -8,6 +8,7 @@ this.CrashTestUtils = { crash: null, dumpHasStream: null, dumpHasInstructionPointerMemory: null, + dumpWin64CFITestSymbols: null, // Constants for crash() // Keep these in sync with nsTestCrasher.cpp! @@ -18,6 +19,18 @@ this.CrashTestUtils = { CRASH_MOZ_CRASH: 4, CRASH_ABORT: 5, CRASH_UNCAUGHT_EXCEPTION: 6, + CRASH_X64CFI_NO_MANS_LAND: 7, + CRASH_X64CFI_LAUNCHER: 8, + CRASH_X64CFI_UNKNOWN_OPCODE: 9, + CRASH_X64CFI_PUSH_NONVOL: 10, + CRASH_X64CFI_ALLOC_SMALL: 11, + CRASH_X64CFI_ALLOC_LARGE: 12, + CRASH_X64CFI_SAVE_NONVOL: 15, + CRASH_X64CFI_SAVE_NONVOL_FAR: 16, + CRASH_X64CFI_SAVE_XMM128: 17, + CRASH_X64CFI_SAVE_XMM128_FAR: 18, + CRASH_X64CFI_EPILOG: 19, + CRASH_X64CFI_EOF: 20, // Constants for dumpHasStream() // From google_breakpad/common/minidump_format.h @@ -63,3 +76,9 @@ CrashTestUtils.dumpCheckMemory = lib.declare("DumpCheckMemory", ctypes.default_abi, ctypes.bool, ctypes.char.ptr); + +CrashTestUtils.getWin64CFITestFnAddrOffset = + lib.declare("GetWin64CFITestFnAddrOffset", + ctypes.default_abi, + ctypes.int32_t, + ctypes.int16_t); diff --git a/toolkit/crashreporter/test/moz.build b/toolkit/crashreporter/test/moz.build index 152a16bd9c50..0adaa52310cf 100644 --- a/toolkit/crashreporter/test/moz.build +++ b/toolkit/crashreporter/test/moz.build @@ -24,6 +24,11 @@ SOURCES += [ 'ExceptionThrower.cpp', ] +if CONFIG['OS_TARGET'] == 'WINNT' and CONFIG['CPU_ARCH'] == 'x86_64': + SOURCES += [ + 'win64UnwindInfoTests.asm', + ] + if CONFIG['CLANG_CL']: SOURCES['ExceptionThrower.cpp'].flags += [ '-Xclang', diff --git a/toolkit/crashreporter/test/nsTestCrasher.cpp b/toolkit/crashreporter/test/nsTestCrasher.cpp index dd62598b23b6..e69f1c2163f0 100644 --- a/toolkit/crashreporter/test/nsTestCrasher.cpp +++ b/toolkit/crashreporter/test/nsTestCrasher.cpp @@ -43,6 +43,24 @@ void PureVirtualCall() b.use(); // make sure b's actually used } +extern "C" { +#if XP_WIN && HAVE_64BIT_BUILD + // Implementation in win64unwindInfoTests.asm + uint64_t x64CrashCFITest_NO_MANS_LAND(uint64_t returnpfn, void*); + uint64_t x64CrashCFITest_Launcher(uint64_t returnpfn, void* testProc); + uint64_t x64CrashCFITest_UnknownOpcode(uint64_t returnpfn, void*); + uint64_t x64CrashCFITest_PUSH_NONVOL(uint64_t returnpfn, void*); + uint64_t x64CrashCFITest_ALLOC_SMALL(uint64_t returnpfn, void*); + uint64_t x64CrashCFITest_ALLOC_LARGE(uint64_t returnpfn, void*); + uint64_t x64CrashCFITest_SAVE_NONVOL(uint64_t returnpfn, void*); + uint64_t x64CrashCFITest_SAVE_NONVOL_FAR(uint64_t returnpfn, void*); + uint64_t x64CrashCFITest_SAVE_XMM128(uint64_t returnpfn, void*); + uint64_t x64CrashCFITest_SAVE_XMM128_FAR(uint64_t returnpfn, void*); + uint64_t x64CrashCFITest_EPILOG(uint64_t returnpfn, void*); + uint64_t x64CrashCFITest_EOF(uint64_t returnpfn, void*); +#endif // XP_WIN && HAVE_64BIT_BUILD +} + // Keep these in sync with CrashTestUtils.jsm! const int16_t CRASH_INVALID_POINTER_DEREF = 0; const int16_t CRASH_PURE_VIRTUAL_CALL = 1; @@ -50,6 +68,55 @@ const int16_t CRASH_OOM = 3; const int16_t CRASH_MOZ_CRASH = 4; const int16_t CRASH_ABORT = 5; const int16_t CRASH_UNCAUGHT_EXCEPTION = 6; +const int16_t CRASH_X64CFI_NO_MANS_LAND = 7; +const int16_t CRASH_X64CFI_LAUNCHER = 8; +const int16_t CRASH_X64CFI_UNKNOWN_OPCODE = 9; +const int16_t CRASH_X64CFI_PUSH_NONVOL = 10; +const int16_t CRASH_X64CFI_ALLOC_SMALL = 11; +const int16_t CRASH_X64CFI_ALLOC_LARGE = 12; +const int16_t CRASH_X64CFI_SAVE_NONVOL = 15; +const int16_t CRASH_X64CFI_SAVE_NONVOL_FAR = 16; +const int16_t CRASH_X64CFI_SAVE_XMM128 = 17; +const int16_t CRASH_X64CFI_SAVE_XMM128_FAR = 18; +const int16_t CRASH_X64CFI_EPILOG = 19; +const int16_t CRASH_X64CFI_EOF = 20; + +#if XP_WIN && HAVE_64BIT_BUILD + +typedef decltype(&x64CrashCFITest_UnknownOpcode) win64CFITestFnPtr_t; + +static std::map +GetWin64CFITestMap() { + std::map ret = { + { CRASH_X64CFI_NO_MANS_LAND, x64CrashCFITest_NO_MANS_LAND}, + { CRASH_X64CFI_LAUNCHER, x64CrashCFITest_Launcher}, + { CRASH_X64CFI_UNKNOWN_OPCODE, x64CrashCFITest_UnknownOpcode}, + { CRASH_X64CFI_PUSH_NONVOL, x64CrashCFITest_PUSH_NONVOL}, + { CRASH_X64CFI_ALLOC_SMALL, x64CrashCFITest_ALLOC_SMALL }, + { CRASH_X64CFI_ALLOC_LARGE, x64CrashCFITest_ALLOC_LARGE }, + { CRASH_X64CFI_SAVE_NONVOL, x64CrashCFITest_SAVE_NONVOL }, + { CRASH_X64CFI_SAVE_NONVOL_FAR, x64CrashCFITest_SAVE_NONVOL_FAR }, + { CRASH_X64CFI_SAVE_XMM128, x64CrashCFITest_SAVE_XMM128 }, + { CRASH_X64CFI_SAVE_XMM128_FAR, x64CrashCFITest_SAVE_XMM128_FAR }, + { CRASH_X64CFI_EPILOG, x64CrashCFITest_EPILOG }, + { CRASH_X64CFI_EOF, x64CrashCFITest_EOF } + }; + // ret values point to jump table entries, not the actual function bodies. + // Get the correct pointer by calling the function with returnpfn=1 + for (auto it = ret.begin(); it != ret.end(); ++ it) { + it->second = (win64CFITestFnPtr_t)it->second(1, nullptr); + } + return ret; +} + +void ReserveStack() { + // This ensures our tests have enough reserved stack space. + uint8_t* p = (uint8_t*)alloca(1024000); + // This ensures we don't optimized away this meaningless code at build time. + mozilla::Unused << (int)(uint64_t)p; +} + +#endif // XP_WIN && HAVE_64BIT_BUILD extern "C" NS_EXPORT void Crash(int16_t how) @@ -84,6 +151,27 @@ void Crash(int16_t how) ThrowException(); break; } +#if XP_WIN && HAVE_64BIT_BUILD + case CRASH_X64CFI_UNKNOWN_OPCODE: + case CRASH_X64CFI_PUSH_NONVOL: + case CRASH_X64CFI_ALLOC_SMALL: + case CRASH_X64CFI_ALLOC_LARGE: + case CRASH_X64CFI_SAVE_NONVOL: + case CRASH_X64CFI_SAVE_NONVOL_FAR: + case CRASH_X64CFI_SAVE_XMM128: + case CRASH_X64CFI_SAVE_XMM128_FAR: + case CRASH_X64CFI_EPILOG: { + ReserveStack(); + auto m = GetWin64CFITestMap(); + if (m.find(how) == m.end()) { + break; + } + auto pfnTest = m[how]; + auto pfnLauncher = m[CRASH_X64CFI_LAUNCHER]; + pfnLauncher(0, pfnTest); + break; + } +#endif // XP_WIN && HAVE_64BIT_BUILD default: break; } @@ -119,3 +207,20 @@ void TryOverrideExceptionHandler() SetUnhandledExceptionFilter(HandleException); } #endif + +extern "C" NS_EXPORT uint32_t +GetWin64CFITestFnAddrOffset(int16_t fnid) { +#if XP_WIN && HAVE_64BIT_BUILD + // fnid uses the same constants as Crash(). + // Returns the RVA of the requested function. + // Returns 0 on failure. + auto m = GetWin64CFITestMap(); + if (m.find(fnid) == m.end()) { + return 0; + } + uint64_t moduleBase = (uint64_t)GetModuleHandleW(L"testcrasher.dll"); + return ((uint64_t)m[fnid]) - moduleBase; +#else + return 0; +#endif // XP_WIN && HAVE_64BIT_BUILD +} diff --git a/toolkit/crashreporter/test/unit/head_crashreporter.js b/toolkit/crashreporter/test/unit/head_crashreporter.js index 8fbc78328297..026d8bb4f224 100644 --- a/toolkit/crashreporter/test/unit/head_crashreporter.js +++ b/toolkit/crashreporter/test/unit/head_crashreporter.js @@ -1,8 +1,9 @@ -var {utils: Cu} = Components; +var {utils: Cu, classes: Cc, interfaces: Ci} = Components; Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/Services.jsm", this); Cu.import("resource://testing-common/AppData.jsm", this); +Cu.import("resource://gre/modules/AppConstants.jsm"); function getEventDir() { return OS.Path.join(do_get_tempdir().path, "crash-events"); @@ -24,15 +25,17 @@ function getEventDir() { * * @param callback * A JavaScript function to be called after the subprocess - * crashes. It will be passed (minidump, extra), where - * minidump is an nsIFile of the minidump file produced, - * and extra is an object containing the key,value pairs from - * the .extra file. + * crashes. It will be passed (minidump, extra, extrafile), where + * - minidump is an nsIFile of the minidump file produced, + * - extra is an object containing the key,value pairs from + * the .extra file. + * - extrafile is an nsIFile of the extra file * * @param canReturnZero * If true, the subprocess may return with a zero exit code. * Certain types of crashes may not cause the process to * exit with an error. + * */ function do_crash(setup, callback, canReturnZero) { // get current process filename (xpcshell) @@ -100,6 +103,32 @@ function getMinidump() { return null; } +function runMinidumpAnalyzer(dumpFile, additionalArgs) { + if (AppConstants.platform !== "win") { + return; + } + + // find minidump-analyzer executable. + let ds = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties); + let bin = ds.get("XREExeF", Ci.nsIFile); + ok(bin && bin.exists()); + bin = bin.parent; + ok(bin && bin.exists()); + bin.append("minidump-analyzer.exe"); + ok(bin.exists()); + + let process = Cc["@mozilla.org/process/util;1"] + .createInstance(Ci.nsIProcess); + process.init(bin); + let args = []; + if (additionalArgs) { + args = args.concat(additionalArgs); + } + args.push(dumpFile.path); + process.run(true /* blocking */, args, args.length); +} + function handleMinidump(callback) { // find minidump let minidump = getMinidump(); @@ -131,7 +160,7 @@ function handleMinidump(callback) { let extra = parseKeyValuePairsFromFile(extrafile); if (callback) { - callback(minidump, extra); + callback(minidump, extra, extrafile); } if (minidump.exists()) { diff --git a/toolkit/crashreporter/test/unit/head_win64cfi.js b/toolkit/crashreporter/test/unit/head_win64cfi.js new file mode 100644 index 000000000000..042cadc64a5f --- /dev/null +++ b/toolkit/crashreporter/test/unit/head_win64cfi.js @@ -0,0 +1,193 @@ +/* import-globals-from head_crashreporter.js */ + +let gTestCrasherSyms = null; +let gModules = null; + +// Returns the offset (int) of an IP with a given base address. +// This is effectively (ip - base), except a bit more complication due to +// Javascript's shaky handling of 64-bit integers. +// base & ip are passed as hex strings. +function getModuleOffset(base, ip) { + let i = 0; + // Find where the two addresses diverge, which enables us to perform a 32-bit + // subtraction. + // e.g. "0x1111111111112222" + // - "0x1111111111111111" + // becomes 2222 - 1111 + for (; i < base.length; ++i) { + if (base[i] != ip[i]) { + break; + } + } + if (i == base.length) { + return 0; + } + let lhs2 = "0x" + base.substring(i); + let rhs2 = "0x" + ip.substring(i); + return parseInt(rhs2) - parseInt(lhs2); +} + +// Uses gTestCrasherSyms to convert an address to a symbol. +function findNearestTestCrasherSymbol(addr) { + addr += 1; // Breakpad sometimes offsets addresses; correct for this. + let closestDistance = null; + let closestSym = null; + for (let sym in gTestCrasherSyms) { + if (addr >= gTestCrasherSyms[sym]) { + let thisDistance = addr - gTestCrasherSyms[sym]; + if (closestDistance === null || thisDistance < closestDistance) { + closestDistance = thisDistance; + closestSym = sym; + } + } + } + if (closestSym === null) { + return null; + } + return { symbol: closestSym, offset: closestDistance } +} + +// Populate known symbols for testcrasher.dll. +// Use the same prop names as from CrashTestUtils to avoid the need for mapping. +function initTestCrasherSymbols() { + gTestCrasherSyms = { }; + for (let k in CrashTestUtils) { + // Not all keys here are valid symbol names. getWin64CFITestFnAddrOffset + // will return 0 in those cases, no need to filter here. + if (Number.isInteger(CrashTestUtils[k])) { + let t = CrashTestUtils.getWin64CFITestFnAddrOffset(CrashTestUtils[k]); + if (t > 0) { + gTestCrasherSyms[k] = t; + } + } + } +} + +function stackFrameToString(frameIndex, frame) { + // Calculate the module offset. + let ip = frame.ip; + let symbol = ""; + let moduleOffset = "unknown_offset"; + let filename = "unknown_module"; + + if (typeof frame.module_index !== "undefined" && (frame.module_index >= 0) + && (frame.module_index < gModules.length)) { + + let base = gModules[frame.module_index].base_addr; + moduleOffset = getModuleOffset(base, ip); + filename = gModules[frame.module_index].filename; + + if (filename === "testcrasher.dll") { + let nearestSym = findNearestTestCrasherSymbol(moduleOffset); + if (nearestSym !== null) { + symbol = nearestSym.symbol + "+" + nearestSym.offset.toString(16); + } + } + } + + let ret = "frames[" + frameIndex + "] ip=" + ip + + " " + symbol + + ", module:" + filename + + ", trust:" + frame.trust + + ", moduleOffset:" + moduleOffset.toString(16); + return ret; +} + +function dumpStackFrames(frames, maxFrames) { + for (let i = 0; i < Math.min(maxFrames, frames.length); ++i) { + do_print(stackFrameToString(i, frames[i])); + } +} + +// Test that the top of the given stack (from extra data) matches the given +// expected frames. +// +// expected is { symbol: "", trust: "" } +function assertStack(stack, expected) { + for (let i = 0; i < stack.length; ++i) { + if (i >= expected.length) { + ok("Top stack frames were expected"); + return; + } + let frame = stack[i]; + let expectedFrame = expected[i]; + let dumpThisFrame = function() { + do_print(" Actual frame: " + stackFrameToString(i, frame)); + do_print("Expected { symbol: " + expectedFrame.symbol + ", trust: " + expectedFrame.trust + "}"); + }; + + if (expectedFrame.trust) { + if (frame.trust !== expectedFrame.trust) { + dumpThisFrame(); + do_print("Expected frame trust did not match."); + ok(false); + } + } + + if (expectedFrame.symbol) { + if (typeof frame.module_index === "undefined") { + // Without a module_index, it happened in an unknown module. Currently + // you can't specify an expected "unknown" module. + do_print("Unknown symbol in unknown module."); + ok(false); + } + if (frame.module_index < 0 || frame.module_index >= gModules.length) { + dumpThisFrame(); + do_print("Unknown module."); + ok(false); + return; + } + let base = gModules[frame.module_index].base_addr; + let moduleOffset = getModuleOffset(base, frame.ip); + let filename = gModules[frame.module_index].filename; + if (filename == "testcrasher.dll") { + let nearestSym = findNearestTestCrasherSymbol(moduleOffset); + if (nearestSym === null) { + dumpThisFrame(); + do_print("Unknown symbol."); + ok(false); + return; + } + + if (nearestSym.symbol !== expectedFrame.symbol) { + dumpThisFrame(); + do_print("Mismatching symbol."); + ok(false); + } + } + } + } +} + +// Performs a crash, runs minidump-analyzer, and checks expected stack analysis. +// +// how: The crash to perform. Constants defined in both CrashTestUtils.jsm +// and nsTestCrasher.cpp (i.e. CRASH_X64CFI_PUSH_NONVOL) +// expectedStack: An array of {"symbol", "trust"} where trust is "cfi", +// "context", "scan", et al. May be null if you don't need to check the stack. +// minidumpAnalyzerArgs: An array of additional arguments to pass to +// minidump-analyzer.exe. +function do_x64CFITest(how, expectedStack, minidumpAnalyzerArgs) { + + // Setup is run in the subprocess so we cannot use any closures. + let setupFn = "crashType = CrashTestUtils." + how + ";"; + + let callbackFn = function(minidumpFile, extra, extraFile) { + runMinidumpAnalyzer(minidumpFile, minidumpAnalyzerArgs); + + // Refresh updated extra data + extra = parseKeyValuePairsFromFile(extraFile); + + initTestCrasherSymbols(); + let stackTraces = JSON.parse(extra.StackTraces); + let crashingThreadIndex = stackTraces.crash_info.crashing_thread; + gModules = stackTraces.modules; + let crashingFrames = stackTraces.threads[crashingThreadIndex].frames; + + dumpStackFrames(crashingFrames, 10); + + assertStack(crashingFrames, expectedStack); + }; + + do_crash(setupFn, callbackFn, true, true); +} diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_alloc_large.js b/toolkit/crashreporter/test/unit/test_crash_win64cfi_alloc_large.js new file mode 100644 index 000000000000..83aa3625a941 --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_alloc_large.js @@ -0,0 +1,7 @@ + +function run_test() { + do_x64CFITest("CRASH_X64CFI_ALLOC_LARGE", [ + { symbol: "CRASH_X64CFI_ALLOC_LARGE", trust: "context" }, + { symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" } + ]); +} diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_alloc_small.js b/toolkit/crashreporter/test/unit/test_crash_win64cfi_alloc_small.js new file mode 100644 index 000000000000..f79d233e9183 --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_alloc_small.js @@ -0,0 +1,7 @@ + +function run_test() { + do_x64CFITest("CRASH_X64CFI_ALLOC_SMALL", [ + { symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" }, + { symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" } + ]); +} diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_epilog.js b/toolkit/crashreporter/test/unit/test_crash_win64cfi_epilog.js new file mode 100644 index 000000000000..2a9027810853 --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_epilog.js @@ -0,0 +1,7 @@ + +function run_test() { + do_x64CFITest("CRASH_X64CFI_EPILOG", [ + { symbol: "CRASH_X64CFI_EPILOG", trust: "context" }, + { symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" } + ]); +} diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_code_chain.exe b/toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_code_chain.exe new file mode 100644 index 0000000000000000000000000000000000000000..5283cc5df7e0a7abb20dd540cc203c4266702fff GIT binary patch literal 1440 zcmah}%}*0i5T61?EcPP}n0j(+){m%Z3=|HYYAmwE0!>RoghpCu12yfoDT@N(U{f)H zjR`8;FzKJ5MhNjl0>&sU8ZjY=Ui?ZEIC%76vd(PxL8(S3nVosR`OUmHZ)dl?X9g+( zpbAw{0G7~_xYXyrAIlgv9e-|uXNU9lC7ma4?;45@bIDX8(X~&#pc1B%)n0tj7lgzlDKBQmcS6iOHY;p|M9)ZFWK@BwnK`HN{E`VIqH*oCtE5UK+us2 zholgI4P#W3(`+fvg{h8Ykw<5z9xiCMoF_~L_!?nK;w{}mdY8jpr?>9Bl4>&iNtJ3| zoZvb0qQv*@9GYG$H|2GuK964Jry_mz+?(eqyG`!3w8_J^Hu+26N-mdE z>vs#8-Jp3fz}uvb7vlyjxJjetdL|^z^ zmVSk$KhYJ=V(h?PV`b!HjNG^^$n*STTng#cESkO*@Zw#S3!6=P9WKPeW<-A#l}DVEgvCt7<=)fhcl+0Gzkh95TWFm;*YP6TY8^UfskC11^3W{NY^ig`$3=>>nRG;HR`@2u*j%$Y zqdskvr_LQeK0Y4qA4FaO`=FU}jB4|U_eT9|wHnZc^9Qj;!d!^accAf{gwdVb_OWKd z*muX~*lyK>HGCSZ&QoC3@lcxt&>5=Ka~PghCv`?W7@bGKsH=l&jL|+!deEm{|0d<{ xd!w>_B<7CZ1i0Gb>uB*bHaNnu*nTG6<0Dk+KZ|x7^%wf|f9k|NYD)H{tlv4%8B+iN literal 0 HcmV?d00001 diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_code_chain.js b/toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_code_chain.js new file mode 100644 index 000000000000..1e475d588959 --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_code_chain.js @@ -0,0 +1,17 @@ + +function run_test() { + // Test that minidump-analyzer gracefully handles chained + // unwind code entries that form a circular reference + // (infinite loop). + let exe = do_get_file("test_crash_win64cfi_infinite_code_chain.exe"); + ok(exe); + + // Perform a crash. We won't get unwind info, but make sure the stack scan + // still works. + do_x64CFITest("CRASH_X64CFI_ALLOC_SMALL", + [ + { symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" }, + { symbol: "CRASH_X64CFI_NO_MANS_LAND", trust: null } + ], + ["--force-use-module", exe.path]); +} diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_entry_chain.exe b/toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_entry_chain.exe new file mode 100644 index 0000000000000000000000000000000000000000..de48576f6536cc6895f23297752690da1a694a10 GIT binary patch literal 1440 zcmah}-%C?*6hAke+H5}<8tbWR+jiJLm3xb*`ub=X=igb3W&Mzvp|;*4jM@ zWdKl)s;U6$a39j{Aiwaa=uxGUUJQ)>&2FtF?FZI^RXbFC@jxQV_HC-1K%9qm+&p8N-Vh z@|z{;O)UKxmp_TIJ!g%SkPk6(!-A;H2v2b6Wo;dG^t)mFC8M)*m17 zONl}0dQ=MghZ~)KUnCX@4McW8{Nz1a9lB?+TsYs^LbF7(rOlZTmnhC=(h;Ru5zY|C z=9m}fV_@S1s7?ZK#&R$^&0u_9k>pGUFgXu`iK~GMjL|tvy3waz z|0bp1d$p`}Fy@X90bKO>+B_`{^^R~Xwwp=s_yqMD*36>aK>dYpLH|$fct%yxxs>%A DNxT&; literal 0 HcmV?d00001 diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_entry_chain.js b/toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_entry_chain.js new file mode 100644 index 000000000000..f2dbb81498b6 --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_infinite_entry_chain.js @@ -0,0 +1,17 @@ + +function run_test() { + // Test that minidump-analyzer gracefully handles chained + // IMAGE_RUNTIME_FUNCTION_ENTRY items that form a circular reference + // (infinite loop). + let exe = do_get_file("test_crash_win64cfi_infinite_entry_chain.exe"); + ok(exe); + + // Perform a crash. We won't get unwind info, but make sure the stack scan + // still works. + do_x64CFITest("CRASH_X64CFI_ALLOC_SMALL", + [ + { symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" }, + { symbol: "CRASH_X64CFI_NO_MANS_LAND", trust: null } + ], + ["--force-use-module", exe.path]); +} diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_invalid_exception_rva.exe b/toolkit/crashreporter/test/unit/test_crash_win64cfi_invalid_exception_rva.exe new file mode 100644 index 0000000000000000000000000000000000000000..ab4ce326bdf34b9e417107d2d22adb3d01b006ee GIT binary patch literal 1440 zcmah}-%C?*6hBv;+H5}<8tbWReEUJgC3O1G)1sMInmRT?vXPr^=wiDY^I9rCG?sy0 z0xNwp*q>05B0UKjQ96qZ#Ec&L#Xui=>>*g^+}*2lMIAWbbH1PRIp_O5-+Q+9o@pot zfC|)n9$*nIiBnzwy)0qaeC)Xyo*l~8FLLf|eb-=Qh)*Qr1Ib{F?+;2+T;}`2d~#Ug zBNFfG^zpHHC@h#vMy8eCteJoL@W%Fxt{t=1nQ3+ZVdeqa3dg(I`>NfVrM)-OKS*5t zW}X&+(A{#de)3(_u^(_8YRb%DB;({IXPpgE&l1I^dErKC{kDe$7{^NU)Uv%UjY(Wl>$cS37IqH*oCtEB$KtM=_ zf^rbRjxnmqYqpr@#8e?s;L+U^*wcBCse9s zVVv=n1)25kR86hsviKYq7m)?Zrm~)|%c7V5nU}x1@69pFZd0yV+ms=DoARZ1d2MY? zt=}!CcLSCMKdYy5*LMuLZ~JvNlsEah39Zg|QSq~cm|h8Bww9aP&0v($@gn1R5ku~C zN&00y{Rx*lgRui=jh2v)>g2{HQJG_paVw@)GH802!GmvAD*9~F=WwFF#djwq(PM|v zucbZy5J^e&mw-Tv>X+if z(yfRT@{cq-{N8Xh92^Sof%wV$)H-y}Vrjk9<)$JuTk4##m_%`XCWQ#i3Tq-vpKE>2 zs82iPsdLB1#>PT@1IWu^A2d_Kh&GS-Zq&b4s{vg&cMxkN%!wHN4m6&FFnV+QKGsYa z%`78Kn`6#a1-8&BusKeGjbl)g0N{)jV04(l__Q*?nG9fZ903zo3zZn7b9CuJpL+e9 zlz#7x^7i4VD{>RyN{hFn#ogE-grd>?OnS#hsMLQ3?KbK!^y&Yp6VIqFI+wD3127&K AdjJ3c literal 0 HcmV?d00001 diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_invalid_exception_rva.js b/toolkit/crashreporter/test/unit/test_crash_win64cfi_invalid_exception_rva.js new file mode 100644 index 000000000000..90ba30b1442a --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_invalid_exception_rva.js @@ -0,0 +1,16 @@ + +function run_test() { + // Test that minidump-analyzer gracefully handles an invalid pointer to the + // exception unwind information. + let exe = do_get_file("test_crash_win64cfi_invalid_exception_rva.exe"); + ok(exe); + + // Perform a crash. We won't get unwind info, but make sure the stack scan + // still works. + do_x64CFITest("CRASH_X64CFI_ALLOC_SMALL", + [ + { symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" }, + { symbol: "CRASH_X64CFI_NO_MANS_LAND", trust: null } + ], + ["--force-use-module", exe.path]); +} diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_not_a_pe.exe b/toolkit/crashreporter/test/unit/test_crash_win64cfi_not_a_pe.exe new file mode 100644 index 000000000000..5fa1d76ffc60 --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_not_a_pe.exe @@ -0,0 +1 @@ +this is not a valid PE file. \ No newline at end of file diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_not_a_pe.js b/toolkit/crashreporter/test/unit/test_crash_win64cfi_not_a_pe.js new file mode 100644 index 000000000000..02caa81534d4 --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_not_a_pe.js @@ -0,0 +1,15 @@ + +function run_test() { + // Test that minidump-analyzer gracefully handles corrupt PE files. + let exe = do_get_file("test_crash_win64cfi_not_a_pe.exe"); + ok(exe); + + // Perform a crash. We won't get unwind info, but make sure the stack scan + // still works. + do_x64CFITest("CRASH_X64CFI_ALLOC_SMALL", + [ + { symbol: "CRASH_X64CFI_ALLOC_SMALL", trust: "context" }, + { symbol: "CRASH_X64CFI_NO_MANS_LAND", trust: null } + ], + ["--force-use-module", exe.path]); +} diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_push_nonvol.js b/toolkit/crashreporter/test/unit/test_crash_win64cfi_push_nonvol.js new file mode 100644 index 000000000000..8097dc858558 --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_push_nonvol.js @@ -0,0 +1,7 @@ + +function run_test() { + do_x64CFITest("CRASH_X64CFI_PUSH_NONVOL", [ + { symbol: "CRASH_X64CFI_PUSH_NONVOL", trust: "context" }, + { symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" } + ]); +} diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_save_nonvol.js b/toolkit/crashreporter/test/unit/test_crash_win64cfi_save_nonvol.js new file mode 100644 index 000000000000..9e38e8ea7f34 --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_save_nonvol.js @@ -0,0 +1,7 @@ + +function run_test() { + do_x64CFITest("CRASH_X64CFI_SAVE_NONVOL", [ + { symbol: "CRASH_X64CFI_SAVE_NONVOL", trust: "context" }, + { symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" } + ]); +} diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_save_nonvol_far.js b/toolkit/crashreporter/test/unit/test_crash_win64cfi_save_nonvol_far.js new file mode 100644 index 000000000000..2a1e47e7d667 --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_save_nonvol_far.js @@ -0,0 +1,7 @@ + +function run_test() { + do_x64CFITest("CRASH_X64CFI_SAVE_NONVOL_FAR", [ + { symbol: "CRASH_X64CFI_SAVE_NONVOL_FAR", trust: "context" }, + { symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" } + ]); +} diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_save_xmm128.js b/toolkit/crashreporter/test/unit/test_crash_win64cfi_save_xmm128.js new file mode 100644 index 000000000000..6aef92324edb --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_save_xmm128.js @@ -0,0 +1,7 @@ + +function run_test() { + do_x64CFITest("CRASH_X64CFI_SAVE_XMM128", [ + { symbol: "CRASH_X64CFI_SAVE_XMM128", trust: "context" }, + { symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" } + ]); +} diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_save_xmm128_far.js b/toolkit/crashreporter/test/unit/test_crash_win64cfi_save_xmm128_far.js new file mode 100644 index 000000000000..32e297548d24 --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_save_xmm128_far.js @@ -0,0 +1,7 @@ + +function run_test() { + do_x64CFITest("CRASH_X64CFI_SAVE_XMM128_FAR", [ + { symbol: "CRASH_X64CFI_SAVE_XMM128_FAR", trust: "context" }, + { symbol: "CRASH_X64CFI_LAUNCHER", trust: "cfi" } + ]); +} diff --git a/toolkit/crashreporter/test/unit/test_crash_win64cfi_unknown_op.js b/toolkit/crashreporter/test/unit/test_crash_win64cfi_unknown_op.js new file mode 100644 index 000000000000..4447cbd9adb0 --- /dev/null +++ b/toolkit/crashreporter/test/unit/test_crash_win64cfi_unknown_op.js @@ -0,0 +1,13 @@ + +function run_test() { + // In the case of an unknown unwind code or missing CFI, + // make certain we can still walk the stack via stack scan. The crashing + // function places NO_MANS_LAND on the stack so it will get picked up via + // stack scan. + do_x64CFITest("CRASH_X64CFI_UNKNOWN_OPCODE", [ + { symbol: "CRASH_X64CFI_UNKNOWN_OPCODE", trust: "context" }, + // Trust may either be scan or frame_pointer; we don't really care as + // long as the address is expected. + { symbol: "CRASH_X64CFI_NO_MANS_LAND", trust: null } + ]); +} diff --git a/toolkit/crashreporter/test/unit/xpcshell.ini b/toolkit/crashreporter/test/unit/xpcshell.ini index 25bea51bd25c..0ac4cc209e3e 100644 --- a/toolkit/crashreporter/test/unit/xpcshell.ini +++ b/toolkit/crashreporter/test/unit/xpcshell.ini @@ -37,3 +37,65 @@ skip-if = (os != 'win' && os != 'linux') || (os=='linux' && bits==32) [test_crash_AsyncShutdown.js] [test_event_files.js] [test_crash_terminator.js] + +[test_crash_win64cfi_unknown_op.js] +head = head_crashreporter.js head_win64cfi.js +skip-if = os != 'win' || bits != 64 + +[test_crash_win64cfi_push_nonvol.js] +head = head_crashreporter.js head_win64cfi.js +skip-if = os != 'win' || bits != 64 + +[test_crash_win64cfi_alloc_small.js] +head = head_crashreporter.js head_win64cfi.js +skip-if = os != 'win' || bits != 64 + +[test_crash_win64cfi_alloc_large.js] +head = head_crashreporter.js head_win64cfi.js +skip-if = os != 'win' || bits != 64 + +[test_crash_win64cfi_save_nonvol.js] +head = head_crashreporter.js head_win64cfi.js +skip-if = os != 'win' || bits != 64 + +[test_crash_win64cfi_save_nonvol_far.js] +head = head_crashreporter.js head_win64cfi.js +skip-if = os != 'win' || bits != 64 + +[test_crash_win64cfi_save_xmm128.js] +head = head_crashreporter.js head_win64cfi.js +skip-if = os != 'win' || bits != 64 + +[test_crash_win64cfi_save_xmm128_far.js] +head = head_crashreporter.js head_win64cfi.js +skip-if = os != 'win' || bits != 64 + +[test_crash_win64cfi_epilog.js] +head = head_crashreporter.js head_win64cfi.js +skip-if = os != 'win' || bits != 64 + +[test_crash_win64cfi_infinite_entry_chain.js] +head = head_crashreporter.js head_win64cfi.js +skip-if = os != 'win' || bits != 64 +support-files = test_crash_win64cfi_infinite_entry_chain.exe + +[test_crash_win64cfi_infinite_code_chain.js] +head = head_crashreporter.js head_win64cfi.js +skip-if = os != 'win' || bits != 64 +support-files = test_crash_win64cfi_infinite_code_chain.exe + +[test_crash_win64cfi_invalid_exception_rva.js] +head = head_crashreporter.js head_win64cfi.js +skip-if = os != 'win' || bits != 64 +support-files = test_crash_win64cfi_invalid_exception_rva.exe + +[test_crash_win64cfi_not_a_pe.js] +head = head_crashreporter.js head_win64cfi.js +skip-if = os != 'win' || bits != 64 +support-files = test_crash_win64cfi_not_a_pe.exe + + + + + + diff --git a/toolkit/crashreporter/test/win64UnwindInfoTests.asm b/toolkit/crashreporter/test/win64UnwindInfoTests.asm new file mode 100644 index 000000000000..905d395b943b --- /dev/null +++ b/toolkit/crashreporter/test/win64UnwindInfoTests.asm @@ -0,0 +1,382 @@ +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. + +; Comments indicate stack memory layout during execution. +; For example at the top of a function, where RIP just points to the return +; address, the stack looks like +; rip = [ra] +; And after pushing rax to the stack, +; rip = [rax][ra] +; And then, after allocating 20h bytes on the stack, +; rip = [..20..][rax][ra] +; And then, after pushing a function pointer, +; rip = [pfn][..20..][rax][ra] + +include ksamd64.inc + +.code + +; It helps to add padding between functions so they're not right up against +; each other. Adds clarity to debugging, and gives a bit of leeway when +; searching for symbols (e.g. a function whose last instruction is CALL +; would push a return address that's in the next function.) +PaddingBetweenFunctions macro + repeat 10h + int 3 + endm +endm + +DoCrash macro + mov rax, 7 + mov byte ptr [rax], 9 +endm + +PaddingBetweenFunctions + +; There is no rip addressing mode in x64. The only way to get the value +; of rip is to call a function, and pop it from the stack. +WhoCalledMe proc + pop rax ; rax is now ra + push rax ; Restore ra so this function can return. + sub rax, 5 ; Correct for the size of the call instruction + ret +WhoCalledMe endp + +PaddingBetweenFunctions + +; Any function that we expect to test against on the stack, we'll need its +; real address. If we use function pointers in C, we'll get the address to jump +; table entries. This bit of code at the beginning of each function will +; return the real address we'd expect to see in stack traces. +; +; rcx (1st arg) = mode +; rax (return) = address of either NO_MANS_LAND or this function. +; +; When mode is 0, we place the address of NO_MANS_LAND in RAX, for the function +; to use as it wants. This is just for convenience because almost all functions +; here need this address at some point. +; +; When mode is 1, the address of this function is returned. +TestHeader macro + call WhoCalledMe + test rcx, rcx + je continue_test + ret +continue_test: + inc rcx + call x64CrashCFITest_NO_MANS_LAND + xor rcx, rcx +endm + +; The point of this is to add a stack frame to test against. +; void* x64CrashCFITest_Launcher(int getAddress, void* pTestFn) +x64CrashCFITest_Launcher proc frame + TestHeader + + .endprolog + call rdx + ret +x64CrashCFITest_Launcher endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_NO_MANS_LAND(uint64_t mode); +; Not meant to be called. Only when mode = 1 in order to return its address. +; Place this function's address on the stack so the stack scanning algorithm +; thinks this is a return address, and places it on the stack trace. +x64CrashCFITest_NO_MANS_LAND proc frame + TestHeader + .endprolog + ret +x64CrashCFITest_NO_MANS_LAND endp + +PaddingBetweenFunctions + +; Test that we: +; - handle unknown opcodes gracefully +; - fall back to other stack unwind strategies if CFI doesn't work +; +; In order to properly unwind this frame, we'd need to fully support +; SET_FPREG with offsets, plus restoring registers via PUSH_NONVOL. +; To do this, sprinkle the stack with bad return addresses +; and stack pointers. +x64CrashCFITest_UnknownOpcode proc frame + TestHeader + + push rax + .allocstack 8 + + push rbp + .pushreg rbp + + push rax + push rsp + push rax + push rsp + .allocstack 20h + ; rsp = [rsp][pfn][rsp][pfn][rbp][pfn][ra] + + lea rbp, [rsp+10h] + .setframe rbp, 10h + ; rsp = [rsp][pfn] [rsp][pfn][rbp][pfn][ra] + ; rbp = ^ + + .endprolog + + ; Now modify RSP so measuring stack size from unwind ops will not help + ; finding the return address. + push rax + push rsp + ; rsp = [rsp][pfn][rsp][pfn] [rsp][pfn][rbp][pfn][ra] + + DoCrash + +x64CrashCFITest_UnknownOpcode endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_PUSH_NONVOL(uint64_t mode); +; +; Test correct handling of PUSH_NONVOL unwind code. +; +x64CrashCFITest_PUSH_NONVOL proc frame + TestHeader + + push r10 + .pushreg r10 + push r15 + .pushreg r15 + push rbx + .pushreg rbx + push rsi + .pushreg rsi + push rbp + .pushreg rbp + ; rsp = [rbp][rsi][rbx][r15][r10][ra] + + push rax + .allocstack 8 + ; rsp = [pfn][rbp][rsi][rbx][r15][r10][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_PUSH_NONVOL endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_ALLOC_SMALL(uint64_t mode); +; +; Small allocations are between 8bytes and 512kb-8bytes +; +x64CrashCFITest_ALLOC_SMALL proc frame + TestHeader + + ; Trash rbp to force stack scan. This will force + ; correct behavior for test_crash_win64cfi_not_a_pe, et al. + xor rbp, rbp + + push rax + push rax + push rax + push rax + .allocstack 20h + ; rsp = [pfn][pfn][pfn][pfn][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_ALLOC_SMALL endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_ALLOC_LARGE(uint64_t mode); +; +; Allocations between 512kb and 4gb +; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack +; space for this. +x64CrashCFITest_ALLOC_LARGE proc frame + TestHeader + + sub rsp, 0a000h + .allocstack 0a000h + ; rsp = [..640kb..][ra] + + mov qword ptr [rsp], rax + ; rsp = [pfn][..640kb-8..][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_ALLOC_LARGE endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_SAVE_NONVOL(uint64_t mode); +; +; Test correct handling of SAVE_NONVOL unwind code. +; +x64CrashCFITest_SAVE_NONVOL proc frame + TestHeader + + sub rsp, 30h + .allocstack 30h + ; rsp = [..30..][ra] + + mov qword ptr [rsp+28h], r10 + .savereg r10, 28h + mov qword ptr [rsp+20h], rbp + .savereg rbp, 20h + mov qword ptr [rsp+18h], rsi + .savereg rsi, 18h + mov qword ptr [rsp+10h], rbx + .savereg rbx, 10h + mov qword ptr [rsp+8], r15 + .savereg r15, 8 + ; rsp = [r15][rbx][rsi][rbp][r10][ra] + + mov qword ptr [rsp], rax + + ; rsp = [pfn][r15][rbx][rsi][rbp][r10][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_SAVE_NONVOL endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_SAVE_NONVOL_FAR(uint64_t mode); +; +; Similar to the test above but adding 640kb to most offsets. +; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack +; space for this. +x64CrashCFITest_SAVE_NONVOL_FAR proc frame + TestHeader + + sub rsp, 0a0030h + .allocstack 0a0030h + ; rsp = [..640k..][..30..][ra] + + mov qword ptr [rsp+28h+0a0000h], r10 + .savereg r10, 28h+0a0000h + mov qword ptr [rsp+20h+0a0000h], rbp + .savereg rbp, 20h+0a0000h + mov qword ptr [rsp+18h+0a0000h], rsi + .savereg rsi, 18h+0a0000h + mov qword ptr [rsp+10h+0a0000h], rbx + .savereg rbx, 10h+0a0000h + mov qword ptr [rsp+8+0a0000h], r15 + .savereg r15, 8+0a0000h + ; rsp = [..640k..][..8..][r15][rbx][rsi][rbp][r10][ra] + + mov qword ptr [rsp], rax + + ; rsp = [pfn][..640k..][r15][rbx][rsi][rbp][r10][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_SAVE_NONVOL_FAR endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_SAVE_XMM128(uint64_t mode); +; +; Test correct handling of SAVE_XMM128 unwind code. +x64CrashCFITest_SAVE_XMM128 proc frame + TestHeader + + sub rsp, 30h + .allocstack 30h + ; rsp = [..30..][ra] + + movdqu [rsp+20h], xmm6 + .savexmm128 xmm6, 20h + ; rsp = [..20..][xmm6][ra] + + movdqu [rsp+10h], xmm15 + .savexmm128 xmm15, 10h + ; rsp = [..10..][xmm15][xmm6][ra] + + mov qword ptr [rsp], rax + ; rsp = [pfn][..8..][xmm15][xmm6][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_SAVE_XMM128 endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_SAVE_XMM128(uint64_t mode); +; +; Similar to the test above but adding 640kb to most offsets. +; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack +; space for this. +x64CrashCFITest_SAVE_XMM128_FAR proc frame + TestHeader + + sub rsp, 0a0030h + .allocstack 0a0030h + ; rsp = [..640kb..][..30..][ra] + + movdqu [rsp+20h+0a0000h], xmm6 + .savexmm128 xmm6, 20h+0a0000h + ; rsp = [..640kb..][..20..][xmm6][ra] + + movdqu [rsp+10h+0a0000h], xmm6 + .savexmm128 xmm15, 10h+0a0000h + ; rsp = [..640kb..][..10..][xmm15][xmm6][ra] + + mov qword ptr [rsp], rax + ; rsp = [pfn][..640kb..][..8..][xmm15][xmm6][ra] + + .endprolog + + DoCrash + +x64CrashCFITest_SAVE_XMM128_FAR endp + +PaddingBetweenFunctions + +; void* x64CrashCFITest_EPILOG(uint64_t mode); +; +; The epilog unwind op will also set the unwind version to 2. +; Test that we don't choke on UWOP_EPILOG or version 2 unwind info. +x64CrashCFITest_EPILOG proc frame + TestHeader + + push rax + .allocstack 8 + ; rsp = [pfn][ra] + + .endprolog + + DoCrash + + .beginepilog + + ret + +x64CrashCFITest_EPILOG endp + +PaddingBetweenFunctions + +; Having an EOF symbol at the end of this file contains symbolication to this +; file. So addresses beyond this file don't get mistakenly symbolicated as a +; meaningful function name. +x64CrashCFITest_EOF proc frame + TestHeader + .endprolog + ret +x64CrashCFITest_EOF endp + +end From 57317a8455fa0405ed9bfd8167f047998c9dcfb2 Mon Sep 17 00:00:00 2001 From: Brian Grinstead Date: Thu, 26 Oct 2017 00:18:15 -0700 Subject: [PATCH 002/105] Bug 1329262 - Enable compact themes in mozscreenshots. r=MattN MozReview-Commit-ID: A574iIkQgkW --HG-- extra : rebase_source : 707e7aeba0426ecb9f95bc7fb3f364f95b9f3cf2 --- .../extension/configurations/LightweightThemes.jsm | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/LightweightThemes.jsm b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/LightweightThemes.jsm index f4edde24e369..8e9cf7a7979d 100644 --- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/LightweightThemes.jsm +++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/LightweightThemes.jsm @@ -71,5 +71,19 @@ this.LightweightThemes = { }); }, }, + + compactLight: { + selectors: ["#navigator-toolbox"], + applyConfig() { + LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme("firefox-compact-light@mozilla.org"); + }, + }, + + compactDark: { + selectors: ["#navigator-toolbox"], + applyConfig() { + LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme("firefox-compact-dark@mozilla.org"); + }, + }, }, }; From 91d5705b0df0cf8e280953d67309ffa9f4448f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Thu, 26 Oct 2017 09:20:53 +0200 Subject: [PATCH 003/105] Bug 1408446 - Avoid the 'dialog' features to prevent blank add-on popups on Linux, r=kmag. --- browser/components/extensions/ext-windows.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/browser/components/extensions/ext-windows.js b/browser/components/extensions/ext-windows.js index 8b1371dae5ab..e959303c7ae0 100644 --- a/browser/components/extensions/ext-windows.js +++ b/browser/components/extensions/ext-windows.js @@ -145,13 +145,13 @@ this.windows = class extends ExtensionAPI { args.appendElement(mkstr(aboutNewTabService.newTabURL)); } - let features = ["chrome"]; + let features = ["chrome", "dialog=no"]; if (createData.type === null || createData.type == "normal") { - features.push("dialog=no", "all"); + features.push("all"); } else { // All other types create "popup"-type windows by default. - features.push("dialog", "resizable", "minimizable", "centerscreen", "titlebar", "close"); + features.push("resizable", "minimizable", "centerscreen", "titlebar", "close"); } if (createData.incognito !== null) { From 1c2f14ee70832772b5aade1bddf6e739d6755043 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Thu, 26 Oct 2017 09:37:20 +0200 Subject: [PATCH 004/105] Bug 1408058 part 1 - Track preliminary object IC stubs better. r=evilpie --- js/src/jit/CacheIR.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index 5abd387c194a..4e7a6f73c4fe 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -771,13 +771,11 @@ GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId, Handle maybeEmitIdGuard(id); if (holder) { EnsureTrackPropertyTypes(cx_, holder, id); - if (obj == holder) { - // See the comment in StripPreliminaryObjectStubs. - if (IsPreliminaryObject(obj)) - preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary; - else - preliminaryObjectAction_ = PreliminaryObjectAction::Unlink; - } + // See the comment in StripPreliminaryObjectStubs. + if (IsPreliminaryObject(obj)) + preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary; + else + preliminaryObjectAction_ = PreliminaryObjectAction::Unlink; } EmitReadSlotResult(writer, obj, holder, shape, objId); EmitReadSlotReturn(writer, obj, holder, shape); From 3cf15ea9aca1fa7ca174eb65476a12a9c4fd90e0 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Thu, 26 Oct 2017 09:37:50 +0200 Subject: [PATCH 005/105] Bug 1408058 part 2 - Teach scalar replacement about GuardGroup and GuardUnboxedExpando. r=nbp --- js/src/jit/ScalarReplacement.cpp | 70 +++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/js/src/jit/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp index 08fd6546f6ac..6f188171e54a 100644 --- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -151,7 +151,10 @@ static bool IsObjectEscaped(MInstruction* ins, JSObject* objDefault) { MOZ_ASSERT(ins->type() == MIRType::Object); - MOZ_ASSERT(IsOptimizableObjectInstruction(ins) || ins->isGuardShape() || + MOZ_ASSERT(IsOptimizableObjectInstruction(ins) || + ins->isGuardShape() || + ins->isGuardObjectGroup() || + ins->isGuardUnboxedExpando() || ins->isFunctionEnvironment()); JitSpewDef(JitSpew_Escape, "Check object\n", ins); @@ -243,6 +246,38 @@ IsObjectEscaped(MInstruction* ins, JSObject* objDefault) break; } + case MDefinition::Opcode::GuardObjectGroup: { + MGuardObjectGroup* guard = def->toGuardObjectGroup(); + MOZ_ASSERT(!ins->isGuardObjectGroup()); + if (obj->group() != guard->group()) { + JitSpewDef(JitSpew_Escape, "has a non-matching guard group\n", guard); + return true; + } + if (IsObjectEscaped(def->toInstruction(), obj)) { + JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", def); + return true; + } + break; + } + + case MDefinition::Opcode::GuardUnboxedExpando: { + MGuardUnboxedExpando* guard = def->toGuardUnboxedExpando(); + MOZ_ASSERT(!ins->isGuardUnboxedExpando()); + if (guard->requireExpando()) { + JitSpewDef(JitSpew_Escape, "requires an unboxed expando object\n", guard); + return true; + } + if (obj->as().maybeExpando()) { + JitSpewDef(JitSpew_Escape, "has an expando object\n", guard); + return true; + } + if (IsObjectEscaped(def->toInstruction(), obj)) { + JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", def); + return true; + } + break; + } + case MDefinition::Opcode::Lambda: case MDefinition::Opcode::LambdaArrow: { if (IsLambdaEscaped(def->toInstruction(), obj)) { @@ -311,6 +346,8 @@ class ObjectMemoryView : public MDefinitionVisitorDefaultNoop void visitStoreSlot(MStoreSlot* ins); void visitLoadSlot(MLoadSlot* ins); void visitGuardShape(MGuardShape* ins); + void visitGuardObjectGroup(MGuardObjectGroup* ins); + void visitGuardUnboxedExpando(MGuardUnboxedExpando* ins); void visitFunctionEnvironment(MFunctionEnvironment* ins); void visitLambda(MLambda* ins); void visitLambdaArrow(MLambdaArrow* ins); @@ -324,6 +361,7 @@ class ObjectMemoryView : public MDefinitionVisitorDefaultNoop private: void storeOffset(MInstruction* ins, size_t offset, MDefinition* value); void loadOffset(MInstruction* ins, size_t offset); + void visitObjectGuard(MInstruction* ins, MDefinition* operand); }; const char* ObjectMemoryView::phaseName = "Scalar Replacement of Object"; @@ -626,19 +664,41 @@ ObjectMemoryView::visitLoadSlot(MLoadSlot* ins) } void -ObjectMemoryView::visitGuardShape(MGuardShape* ins) +ObjectMemoryView::visitObjectGuard(MInstruction* ins, MDefinition* operand) { - // Skip loads made on other objects. - if (ins->object() != obj_) + MOZ_ASSERT(ins->numOperands() == 1); + MOZ_ASSERT(ins->getOperand(0) == operand); + MOZ_ASSERT(ins->type() == MIRType::Object); + + // Skip guards on other objects. + if (operand != obj_) return; - // Replace the shape guard by its object. + // Replace the guard by its object. ins->replaceAllUsesWith(obj_); // Remove original instruction. ins->block()->discard(ins); } +void +ObjectMemoryView::visitGuardShape(MGuardShape* ins) +{ + visitObjectGuard(ins, ins->object()); +} + +void +ObjectMemoryView::visitGuardObjectGroup(MGuardObjectGroup* ins) +{ + visitObjectGuard(ins, ins->object()); +} + +void +ObjectMemoryView::visitGuardUnboxedExpando(MGuardUnboxedExpando* ins) +{ + visitObjectGuard(ins, ins->object()); +} + void ObjectMemoryView::visitFunctionEnvironment(MFunctionEnvironment* ins) { From b99ddfe0e7006df8bfc4f459c36a9725c0520d77 Mon Sep 17 00:00:00 2001 From: Jan de Mooij Date: Thu, 26 Oct 2017 09:38:22 +0200 Subject: [PATCH 006/105] Bug 1408058 part 3 - Inline slot reads on the prototype in Ion based on Baseline feedback. r=evilpie --- js/public/TrackedOptimizationInfo.h | 2 +- js/src/jit/BaselineInspector.cpp | 95 +++++++++++++++++++++++++++++ js/src/jit/BaselineInspector.h | 4 ++ js/src/jit/IonBuilder.cpp | 53 ++++++++++++++++ js/src/jit/IonBuilder.h | 2 + 5 files changed, 155 insertions(+), 1 deletion(-) diff --git a/js/public/TrackedOptimizationInfo.h b/js/public/TrackedOptimizationInfo.h index b50d302a6e21..4490b754bbb0 100644 --- a/js/public/TrackedOptimizationInfo.h +++ b/js/public/TrackedOptimizationInfo.h @@ -24,9 +24,9 @@ namespace JS { _(GetProp_Unboxed) \ _(GetProp_CommonGetter) \ _(GetProp_InlineAccess) \ + _(GetProp_InlineProtoAccess) \ _(GetProp_Innerize) \ _(GetProp_InlineCache) \ - _(GetProp_SharedCache) \ _(GetProp_ModuleNamespace) \ \ _(SetProp_CommonSetter) \ diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index 52860c7ba9a9..a439b48433f7 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -1222,6 +1222,101 @@ BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shap return true; } +static bool +GetCacheIRReceiverForProtoReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* receiver, + JSObject** holderResult) +{ + // We match: + // + // GuardIsObject 0 + // + // 1: LoadObject holder + // GuardShape 1 + // LoadFixedSlotResult 1 or LoadDynamicSlotResult 1 + + *receiver = ReceiverGuard(); + CacheIRReader reader(stub->stubInfo()); + + ObjOperandId objId = ObjOperandId(0); + if (!reader.matchOp(CacheOp::GuardIsObject, objId)) + return false; + + if (!MatchCacheIRReceiverGuard(reader, stub, stub->stubInfo(), objId, receiver)) + return false; + + if (!reader.matchOp(CacheOp::LoadObject)) + return false; + ObjOperandId holderId = reader.objOperandId(); + JSObject* holder = stub->stubInfo()->getStubField(stub, reader.stubOffset()).get(); + + if (!reader.matchOp(CacheOp::GuardShape, holderId)) + return false; + Shape* holderShape = stub->stubInfo()->getStubField(stub, reader.stubOffset()); + + if (!reader.matchOpEither(CacheOp::LoadFixedSlotResult, CacheOp::LoadDynamicSlotResult)) + return false; + if (reader.objOperandId() != holderId) + return false; + + if (holder->maybeShape() != holderShape) + return false; + if (*holderResult && *holderResult != holder) + return false; + + *holderResult = holder; + return true; +} + +bool +BaselineInspector::maybeInfoForProtoReadSlot(jsbytecode* pc, ReceiverVector& receivers, + ObjectGroupVector& convertUnboxedGroups, + JSObject** holder) +{ + // This is like maybeInfoForPropertyOp, but for when the property exists on + // the prototype. + + MOZ_ASSERT(receivers.empty()); + MOZ_ASSERT(convertUnboxedGroups.empty()); + MOZ_ASSERT(!*holder); + + if (!hasBaselineScript()) + return true; + + MOZ_ASSERT(isValidPC(pc)); + const ICEntry& entry = icEntryFromPC(pc); + + ICStub* stub = entry.firstStub(); + while (stub->next()) { + ReceiverGuard receiver; + if (stub->isCacheIR_Monitored()) { + if (!GetCacheIRReceiverForProtoReadSlot(stub->toCacheIR_Monitored(), &receiver, + holder)) + { + receivers.clear(); + return true; + } + } else { + receivers.clear(); + return true; + } + + if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) + return false; + + stub = stub->next(); + } + + if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) + receivers.clear(); + + // Don't inline if there are more than 5 receivers. + if (receivers.length() > 5) + receivers.clear(); + + MOZ_ASSERT_IF(!receivers.empty(), *holder); + return true; +} + static MIRType GetCacheIRExpectedInputType(ICCacheIR_Monitored* stub) { diff --git a/js/src/jit/BaselineInspector.h b/js/src/jit/BaselineInspector.h index 93af4e0c3421..cbf8532df5ce 100644 --- a/js/src/jit/BaselineInspector.h +++ b/js/src/jit/BaselineInspector.h @@ -108,6 +108,10 @@ class BaselineInspector MOZ_MUST_USE bool maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups); + MOZ_MUST_USE bool maybeInfoForProtoReadSlot(jsbytecode* pc, ReceiverVector& receivers, + ObjectGroupVector& convertUnboxedGroups, + JSObject** holder); + SetElemICInspector setElemICInspector(jsbytecode* pc) { return makeICInspector(pc, ICStub::SetElem_Fallback); } diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 15f54eb4e536..08a4def7c2d9 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -10387,6 +10387,13 @@ IonBuilder::jsop_getprop(PropertyName* name) if (emitted) return Ok(); + // Try to emit a property access on the prototype based on baseline + // caches. + trackOptimizationAttempt(TrackedStrategy::GetProp_InlineProtoAccess); + MOZ_TRY(getPropTryInlineProtoAccess(&emitted, obj, name, types)); + if (emitted) + return Ok(); + // Try to emit loads from a module namespace. trackOptimizationAttempt(TrackedStrategy::GetProp_ModuleNamespace); MOZ_TRY(getPropTryModuleNamespace(&emitted, obj, name, barrier, types)); @@ -11297,6 +11304,52 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName return Ok(); } +AbortReasonOr +IonBuilder::getPropTryInlineProtoAccess(bool* emitted, MDefinition* obj, PropertyName* name, + TemporaryTypeSet* types) +{ + MOZ_ASSERT(*emitted == false); + + BaselineInspector::ReceiverVector receivers(alloc()); + BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); + JSObject* holder = nullptr; + if (!inspector->maybeInfoForProtoReadSlot(pc, receivers, convertUnboxedGroups, &holder)) + return abort(AbortReason::Alloc); + + if (!canInlinePropertyOpShapes(receivers)) + return Ok(); + + MOZ_ASSERT(holder); + holder = checkNurseryObject(holder); + + BarrierKind barrier; + MOZ_TRY_VAR(barrier, PropertyReadOnPrototypeNeedsTypeBarrier(this, obj, name, types)); + + MIRType rvalType = types->getKnownMIRType(); + if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType)) + rvalType = MIRType::Value; + + // Guard on the receiver shapes/groups. + obj = convertUnboxedObjects(obj, convertUnboxedGroups); + obj = addGuardReceiverPolymorphic(obj, receivers); + if (!obj) + return abort(AbortReason::Alloc); + + // Guard on the holder's shape. + MInstruction* holderDef = constant(ObjectValue(*holder)); + Shape* holderShape = holder->as().shape(); + holderDef = addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard); + + Shape* propShape = holderShape->searchLinear(NameToId(name)); + MOZ_ASSERT(propShape); + + MOZ_TRY(loadSlot(holderDef, propShape, rvalType, barrier, types)); + + trackOptimizationSuccess(); + *emitted = true; + return Ok(); +} + AbortReasonOr IonBuilder::getPropAddCache(MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types) diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 164a1c0a0567..85ec031603e5 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -244,6 +244,8 @@ class IonBuilder TemporaryTypeSet* types, bool innerized = false); AbortReasonOr getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types); + AbortReasonOr getPropTryInlineProtoAccess(bool* emitted, MDefinition* obj, PropertyName* name, + TemporaryTypeSet* types); AbortReasonOr getPropTryTypedObject(bool* emitted, MDefinition* obj, PropertyName* name); AbortReasonOr getPropTryScalarPropOfTypedObject(bool* emitted, MDefinition* typedObj, int32_t fieldOffset, From 3e8854a63402c6011634da912bf79470fb584028 Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Thu, 26 Oct 2017 10:34:46 +0200 Subject: [PATCH 007/105] Backed out 1 changesets (bug 1408446) for browser-chrome failures in browser/components/extensions/test/browser/browser_ext_windows.js r=backout a=backout on a CLOSED TREE Backed out changeset 2311fe1447d8 (bug 1408446) --- browser/components/extensions/ext-windows.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/browser/components/extensions/ext-windows.js b/browser/components/extensions/ext-windows.js index e959303c7ae0..8b1371dae5ab 100644 --- a/browser/components/extensions/ext-windows.js +++ b/browser/components/extensions/ext-windows.js @@ -145,13 +145,13 @@ this.windows = class extends ExtensionAPI { args.appendElement(mkstr(aboutNewTabService.newTabURL)); } - let features = ["chrome", "dialog=no"]; + let features = ["chrome"]; if (createData.type === null || createData.type == "normal") { - features.push("all"); + features.push("dialog=no", "all"); } else { // All other types create "popup"-type windows by default. - features.push("resizable", "minimizable", "centerscreen", "titlebar", "close"); + features.push("dialog", "resizable", "minimizable", "centerscreen", "titlebar", "close"); } if (createData.incognito !== null) { From 04fee04208ef68d2012ac344a679c98eb1fd92c2 Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Thu, 26 Oct 2017 04:53:23 -0400 Subject: [PATCH 008/105] Bug 1411586 - mfbt is missing BUGZILLA_COMPONENTS defined in-tree. r=froydnj --- mfbt/moz.build | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mfbt/moz.build b/mfbt/moz.build index b3e616afa149..078f7651a604 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -4,6 +4,9 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +with Files("**"): + BUG_COMPONENT = ("Core", "MFBT") + TEST_DIRS += ['tests'] # On win we build two mfbt libs - mfbt linked to crt dlls here and in From c4d8ae8c99b6b9ea38d9859e93f999f0993fc2b9 Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Thu, 26 Oct 2017 04:53:25 -0400 Subject: [PATCH 009/105] Bug 1411585 - nsprpub/* has many missing files with no BUGZILLA_COMPONENT mapping in-tree. r=ted --- moz.build | 3 +++ 1 file changed, 3 insertions(+) diff --git a/moz.build b/moz.build index 5bbaea6dfd9d..8f6f91fed3cb 100644 --- a/moz.build +++ b/moz.build @@ -45,6 +45,9 @@ with Files('**/l10n.toml'): with Files('README.txt'): BUG_COMPONENT = ('Core', 'General') +with Files("nsprpub/**"): + BUG_COMPONENT = ("NSPR", "NSPR") + with Files('**/Makefile.in'): BUG_COMPONENT = ('Core', 'Build Config') FINAL = True From 06e22a681925daef3a3dc2cda3243c86c45479e6 Mon Sep 17 00:00:00 2001 From: Shawn Huang Date: Thu, 26 Oct 2017 16:59:18 +0800 Subject: [PATCH 010/105] Bug 1389561 - Part 1: Ensure origin initialized in Maintenance::DirectoryWork. r=janv Make sure origins had been initialized in Maintenance::DirectoryWork before getting QuotaObject. It's possible that DatabaseMaintenance::Run before origins initialized. --- dom/indexedDB/ActorsParent.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index de613cc4eec0..06ffc9df84fa 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -18602,6 +18602,7 @@ Maintenance::DirectoryWork() continue; } + nsCString suffix; nsCString group; nsCString origin; nsTArray databasePaths; @@ -18659,17 +18660,17 @@ Maintenance::DirectoryWork() // Found a database. if (databasePaths.IsEmpty()) { + MOZ_ASSERT(suffix.IsEmpty()); MOZ_ASSERT(group.IsEmpty()); MOZ_ASSERT(origin.IsEmpty()); int64_t dummyTimeStamp; bool dummyPersisted; - nsCString dummySuffix; if (NS_WARN_IF(NS_FAILED( quotaManager->GetDirectoryMetadata2(originDir, &dummyTimeStamp, &dummyPersisted, - dummySuffix, + suffix, group, origin)))) { // Not much we can do here... @@ -18687,6 +18688,21 @@ Maintenance::DirectoryWork() group, origin, Move(databasePaths))); + + nsCOMPtr directory; + + // Idle maintenance may occur before origin is initailized. + // Ensure origin is initialized first. It will initialize all origins + // for temporary storage including IDB origins. + rv = quotaManager->EnsureOriginIsInitialized(persistenceType, + suffix, + group, + origin, + getter_AddRefs(directory)); + + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } } } } From c1cf97283659f1735c1d570ba06ef2c269e30139 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Thu, 26 Oct 2017 10:03:51 +0100 Subject: [PATCH 011/105] Bug 1411636 - Remove unnecessary recalculation of GCRuntime::isFull r=sfink --- js/src/jsapi-tests/testGCFinalizeCallback.cpp | 1 - js/src/jsgc.cpp | 16 ---------------- 2 files changed, 17 deletions(-) diff --git a/js/src/jsapi-tests/testGCFinalizeCallback.cpp b/js/src/jsapi-tests/testGCFinalizeCallback.cpp index 14204b3923d0..669080b2ee3e 100644 --- a/js/src/jsapi-tests/testGCFinalizeCallback.cpp +++ b/js/src/jsapi-tests/testGCFinalizeCallback.cpp @@ -111,7 +111,6 @@ BEGIN_TEST(testGCFinalizeCallback) while (cx->runtime()->gc.isIncrementalGCInProgress()) cx->runtime()->gc.debugGCSlice(budget); CHECK(!cx->runtime()->gc.isIncrementalGCInProgress()); - CHECK(!cx->runtime()->gc.isFullGc()); CHECK(checkMultipleGroups()); CHECK(checkFinalizeStatus()); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index e7e10293487e..81bff772134d 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -6396,9 +6396,6 @@ GCRuntime::allCCVisibleZonesWereCollected() const // These exceptions ensure that when the CC requests a full GC the gray mark // state ends up valid even it we don't collect all of the zones. - if (isFull) - return true; - for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { if (!zone->isCollecting() && !zone->usedByHelperThread() && @@ -6423,19 +6420,6 @@ GCRuntime::endSweepPhase(bool destroyingRuntime, AutoLockForExclusiveAccess& loc MOZ_ASSERT_IF(destroyingRuntime, !sweepOnBackgroundThread); - /* - * Recalculate whether GC was full or not as this may have changed due to - * newly created zones. Can only change from full to not full. - */ - if (isFull) { - for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { - if (!zone->isCollecting()) { - isFull = false; - break; - } - } - } - { gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::DESTROY); From e69f1e327d6360246e7ed7e37f2d98ec4e533736 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Thu, 26 Oct 2017 10:03:51 +0100 Subject: [PATCH 012/105] Bug 1408375 - Move malloc threshold check to malloc allocation r=sfink --- js/src/gc/Allocator.cpp | 2 +- js/src/gc/GCRuntime.h | 109 ++++++++++++++++++++------------------ js/src/gc/Zone.h | 65 +++++++++++++++-------- js/src/jsgc.cpp | 114 ++++++++++++++++++++++++++-------------- 4 files changed, 176 insertions(+), 114 deletions(-) diff --git a/js/src/gc/Allocator.cpp b/js/src/gc/Allocator.cpp index ef854c9f5785..4ac20d2f4fc0 100644 --- a/js/src/gc/Allocator.cpp +++ b/js/src/gc/Allocator.cpp @@ -423,7 +423,7 @@ GCRuntime::allocateArena(Chunk* chunk, Zone* zone, AllocKind thingKind, // Trigger an incremental slice if needed. if (checkThresholds) - maybeAllocTriggerGC(zone, lock); + maybeAllocTriggerZoneGC(zone, lock); return arena; } diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 01f4733f95b4..36b801feeb81 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -653,7 +653,13 @@ typedef HashMap, SystemAllocPolicy> R using AllocKinds = mozilla::EnumSet; -template +enum TriggerKind +{ + NoTrigger = 0, + IncrementalTrigger, + NonIncrementalTrigger +}; + class MemoryCounter { // Bytes counter to measure memory pressure for GC scheduling. It counts @@ -666,56 +672,49 @@ class MemoryCounter // Initial GC trigger threshold. GCLockData initialMaxBytes_; - // Whether a GC has been triggered as a result of bytes_ exceeding - // maxBytes_. - mozilla::Atomic triggered_; + // The counter value at the start of a GC. + ActiveThreadData bytesAtStartOfGC_; + + // Which kind of GC has been triggered if any. + mozilla::Atomic triggered_; public: - MemoryCounter() - : bytes_(0), - maxBytes_(0), - initialMaxBytes_(0), - triggered_(false) - { } - - void updateOnGC(const AutoLockGC& lock) { - if (isTooMuchMalloc()) - maxBytes_ *= 2; - else - maxBytes_ = std::max(initialMaxBytes_.ref(), size_t(maxBytes_ * 0.9)); - reset(); - } - - void setMax(size_t newMax, const AutoLockGC& lock) { - initialMaxBytes_ = newMax; - maxBytes_ = initialMaxBytes_; - reset(); - } - - bool update(T* owner, size_t bytes) { - bytes_ += bytes; - if (MOZ_UNLIKELY(isTooMuchMalloc())) { - if (!triggered_) - triggered_ = owner->triggerGCForTooMuchMalloc(); - } - return triggered_; - } - - void adopt(MemoryCounter& other) { - bytes_ += other.bytes(); - other.reset(); - } + MemoryCounter(); size_t bytes() const { return bytes_; } size_t maxBytes() const { return maxBytes_; } size_t initialMaxBytes(const AutoLockGC& lock) const { return initialMaxBytes_; } - bool isTooMuchMalloc() const { return bytes_ >= maxBytes_; } + TriggerKind triggered() const { return triggered_; } + + void setMax(size_t newMax, const AutoLockGC& lock); + + void update(size_t bytes) { + bytes_ += bytes; + } + + void adopt(MemoryCounter& other); + + TriggerKind shouldTriggerGC(const GCSchedulingTunables& tunables) const { + if (MOZ_LIKELY(bytes_ < maxBytes_ * tunables.allocThresholdFactor())) + return NoTrigger; + + if (bytes_ < maxBytes_) + return IncrementalTrigger; + + return NonIncrementalTrigger; + } + + bool shouldResetIncrementalGC(const GCSchedulingTunables& tunables) const { + return bytes_ > maxBytes_ * tunables.allocThresholdFactorAvoidInterrupt(); + } + + void recordTrigger(TriggerKind trigger); + + void updateOnGCStart(); + void updateOnGCEnd(const GCSchedulingTunables& tunables, const AutoLockGC& lock); private: - void reset() { - bytes_ = 0; - triggered_ = false; - } + void reset(); }; class GCRuntime @@ -740,7 +739,7 @@ class GCRuntime uint32_t getParameter(JSGCParamKey key, const AutoLockGC& lock); MOZ_MUST_USE bool triggerGC(JS::gcreason::Reason reason); - void maybeAllocTriggerGC(Zone* zone, const AutoLockGC& lock); + void maybeAllocTriggerZoneGC(Zone* zone, const AutoLockGC& lock); // The return value indicates if we were able to do the GC. bool triggerZoneGC(Zone* zone, JS::gcreason::Reason reason, size_t usedBytes, size_t thresholdBytes); @@ -847,7 +846,16 @@ class GCRuntime MOZ_MUST_USE bool addBlackRootsTracer(JSTraceDataOp traceOp, void* data); void removeBlackRootsTracer(JSTraceDataOp traceOp, void* data); - bool triggerGCForTooMuchMalloc() { + int32_t getMallocBytes() const { return mallocCounter.bytes(); } + size_t maxMallocBytesAllocated() const { return mallocCounter.maxBytes(); } + void setMaxMallocBytes(size_t value, const AutoLockGC& lock); + + bool updateMallocCounter(size_t nbytes) { + mallocCounter.update(nbytes); + TriggerKind trigger = mallocCounter.shouldTriggerGC(tunables); + if (MOZ_LIKELY(trigger == NoTrigger) || trigger <= mallocCounter.triggered()) + return false; + if (!triggerGC(JS::gcreason::TOO_MUCH_MALLOC)) return false; @@ -855,15 +863,12 @@ class GCRuntime // to access mallocCounter here since triggerGC() will return false in // that case. stats().recordTrigger(mallocCounter.bytes(), mallocCounter.maxBytes()); + + mallocCounter.recordTrigger(trigger); return true; } - int32_t getMallocBytes() const { return mallocCounter.bytes(); } - size_t maxMallocBytesAllocated() const { return mallocCounter.maxBytes(); } - bool isTooMuchMalloc() const { return mallocCounter.isTooMuchMalloc(); } - void setMaxMallocBytes(size_t value, const AutoLockGC& lock); - bool updateMallocCounter(size_t nbytes) { return mallocCounter.update(this, nbytes); } - void updateMallocCountersOnGC(); + void updateMallocCountersOnGCStart(); void setGCCallback(JSGCCallback callback, void* data); void callGCCallback(JSGCStatus status) const; @@ -1422,7 +1427,7 @@ class GCRuntime CallbackVector updateWeakPointerZonesCallbacks; CallbackVector updateWeakPointerCompartmentCallbacks; - MemoryCounter mallocCounter; + MemoryCounter mallocCounter; /* * The trace operations to trace embedding-specific GC roots. One is for diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h index dc3ef4a4846d..2f6f880da836 100644 --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -419,11 +419,35 @@ struct Zone : public JS::shadow::Zone, // Malloc counter to measure memory pressure for GC scheduling. This // counter should be used only when it's not possible to know the size of // a free. - js::gc::MemoryCounter gcMallocCounter; + js::gc::MemoryCounter gcMallocCounter; // Counter of JIT code executable memory for GC scheduling. Also imprecise, // since wasm can generate code that outlives a zone. - js::gc::MemoryCounter jitCodeCounter; + js::gc::MemoryCounter jitCodeCounter; + + void updateMemoryCounter(js::gc::MemoryCounter& counter, size_t nbytes) { + JSRuntime* rt = runtimeFromAnyThread(); + + counter.update(nbytes); + auto trigger = counter.shouldTriggerGC(rt->gc.tunables); + if (MOZ_LIKELY(trigger == js::gc::NoTrigger) || trigger <= counter.triggered()) + return; + + if (!js::CurrentThreadCanAccessRuntime(rt)) + return; + + bool wouldInterruptGC = rt->gc.isIncrementalGCInProgress() && !isCollecting(); + if (wouldInterruptGC && !counter.shouldResetIncrementalGC(rt->gc.tunables)) + return; + + if (!rt->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC, + counter.bytes(), counter.maxBytes())) + { + return; + } + + counter.recordTrigger(trigger); + } public: js::RegExpZone regExps; @@ -432,21 +456,11 @@ struct Zone : public JS::shadow::Zone, bool addTypeDescrObject(JSContext* cx, HandleObject obj); - bool triggerGCForTooMuchMalloc() { - JSRuntime* rt = runtimeFromAnyThread(); - - if (js::CurrentThreadCanAccessRuntime(rt)) { - return rt->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC, - gcMallocCounter.bytes(), gcMallocCounter.maxBytes()); - } - return false; - } - void setGCMaxMallocBytes(size_t value, const js::AutoLockGC& lock) { gcMallocCounter.setMax(value, lock); } void updateMallocCounter(size_t nbytes) { - gcMallocCounter.update(this, nbytes); + updateMemoryCounter(gcMallocCounter, nbytes); } void adoptMallocBytes(Zone* other) { gcMallocCounter.adopt(other->gcMallocCounter); @@ -454,16 +468,23 @@ struct Zone : public JS::shadow::Zone, size_t GCMaxMallocBytes() const { return gcMallocCounter.maxBytes(); } size_t GCMallocBytes() const { return gcMallocCounter.bytes(); } - void updateJitCodeMallocBytes(size_t size) { jitCodeCounter.update(this, size); } - - // Updates all the memory counters after GC. - void updateAllMallocBytesOnGC(const js::AutoLockGC& lock) { - gcMallocCounter.updateOnGC(lock); - jitCodeCounter.updateOnGC(lock); + void updateJitCodeMallocBytes(size_t nbytes) { + updateMemoryCounter(jitCodeCounter, nbytes); } - bool isTooMuchMalloc() const { - return gcMallocCounter.isTooMuchMalloc() || - jitCodeCounter.isTooMuchMalloc(); + + void updateAllGCMallocCountersOnGCStart() { + gcMallocCounter.updateOnGCStart(); + jitCodeCounter.updateOnGCStart(); + } + void updateAllGCMallocCountersOnGCEnd(const js::AutoLockGC& lock) { + auto& gc = runtimeFromAnyThread()->gc; + gcMallocCounter.updateOnGCEnd(gc.tunables, lock); + jitCodeCounter.updateOnGCEnd(gc.tunables, lock); + } + js::gc::TriggerKind shouldTriggerGCForTooMuchMalloc() { + auto& gc = runtimeFromAnyThread()->gc; + return std::max(gcMallocCounter.shouldTriggerGC(gc.tunables), + jitCodeCounter.shouldTriggerGC(gc.tunables)); } private: diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 81bff772134d..5d87253d7cb7 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1890,6 +1890,63 @@ ZoneHeapThreshold::updateForRemovedArena(const GCSchedulingTunables& tunables) gcTriggerBytes_ -= amount; } +MemoryCounter::MemoryCounter() + : bytes_(0), + maxBytes_(0), + initialMaxBytes_(0), + triggered_(NoTrigger) +{} + +void +MemoryCounter::updateOnGCStart() +{ + // Record the current byte count at the start of GC. + bytesAtStartOfGC_ = bytes_; +} + +void +MemoryCounter::updateOnGCEnd(const GCSchedulingTunables& tunables, const AutoLockGC& lock) +{ + // Update the trigger threshold at the end of GC and adjust the current + // byte count to reflect bytes allocated since the start of GC. + MOZ_ASSERT(bytes_ >= bytesAtStartOfGC_); + if (shouldTriggerGC(tunables)) + maxBytes_ *= 2; + else + maxBytes_ = std::max(initialMaxBytes_.ref(), size_t(maxBytes_ * 0.9)); + bytes_ -= bytesAtStartOfGC_; + triggered_ = NoTrigger; +} + +void +MemoryCounter::setMax(size_t newMax, const AutoLockGC& lock) +{ + initialMaxBytes_ = newMax; + maxBytes_ = initialMaxBytes_; + reset(); +} + +void +MemoryCounter::adopt(MemoryCounter& other) +{ + update(other.bytes()); + other.reset(); +} + +void +MemoryCounter::recordTrigger(TriggerKind trigger) +{ + MOZ_ASSERT(trigger > triggered_); + triggered_ = trigger; +} + +void +MemoryCounter::reset() +{ + bytes_ = 0; + triggered_ = NoTrigger; +} + void GCMarker::delayMarkingArena(Arena* arena) { @@ -3143,7 +3200,7 @@ GCRuntime::triggerGC(JS::gcreason::Reason reason) } void -GCRuntime::maybeAllocTriggerGC(Zone* zone, const AutoLockGC& lock) +GCRuntime::maybeAllocTriggerZoneGC(Zone* zone, const AutoLockGC& lock) { MOZ_ASSERT(!JS::CurrentThreadIsHeapCollecting()); @@ -3153,8 +3210,6 @@ GCRuntime::maybeAllocTriggerGC(Zone* zone, const AutoLockGC& lock) return; } - // Check GC bytes triggers. - size_t usedBytes = zone->usage.gcBytes(); size_t thresholdBytes = zone->threshold.gcTriggerBytes(); @@ -3192,26 +3247,6 @@ GCRuntime::maybeAllocTriggerGC(Zone* zone, const AutoLockGC& lock) return; } } - - // Check malloc bytes triggers. - - wouldInterruptCollection = isIncrementalGCInProgress() && !isFull; - float fullGCThresholdFactor = - wouldInterruptCollection ? tunables.allocThresholdFactorAvoidInterrupt() - : tunables.allocThresholdFactor(); - - size_t mallocBytes = mallocCounter.bytes(); - size_t mallocThesholdBytes = mallocCounter.maxBytes() * fullGCThresholdFactor; - if (mallocBytes > mallocThesholdBytes) { - stats().recordTrigger(mallocBytes, mallocThesholdBytes); - MOZ_ALWAYS_TRUE(triggerGC(JS::gcreason::TOO_MUCH_MALLOC)); - return; - } - - mallocBytes = zone->GCMallocBytes(); - mallocThesholdBytes = zone->GCMaxMallocBytes() * zoneGCThresholdFactor; - if (mallocBytes > mallocThesholdBytes) - triggerZoneGC(zone, JS::gcreason::TOO_MUCH_MALLOC, mallocBytes, mallocThesholdBytes); } bool @@ -4249,7 +4284,7 @@ GCRuntime::beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcces if (isIncremental) markCompartments(); - updateMallocCountersOnGC(); + updateMallocCountersOnGCStart(); /* * Process any queued source compressions during the start of a major @@ -4332,19 +4367,15 @@ GCRuntime::markCompartments() } void -GCRuntime::updateMallocCountersOnGC() +GCRuntime::updateMallocCountersOnGCStart() { - AutoLockGC lock(rt); - // Update the malloc counters for any zones we are collecting. - for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { - if (zone->isCollecting()) - zone->updateAllMallocBytesOnGC(lock); - } + for (GCZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) + zone->updateAllGCMallocCountersOnGCStart(); // Update the runtime malloc counter only if we are doing a full GC. if (isFull) - mallocCounter.updateOnGC(lock); + mallocCounter.updateOnGCStart(); } template @@ -5622,6 +5653,7 @@ GCRuntime::endSweepingSweepGroup(FreeOp* fop, SliceBudget& budget) zone->changeGCState(Zone::Sweep, Zone::Finished); zone->threshold.updateAfterGC(zone->usage.gcBytes(), invocationKind, tunables, schedulingState, lock); + zone->updateAllGCMallocCountersOnGCEnd(lock); } /* Start background thread to sweep zones if required. */ @@ -6420,6 +6452,12 @@ GCRuntime::endSweepPhase(bool destroyingRuntime, AutoLockForExclusiveAccess& loc MOZ_ASSERT_IF(destroyingRuntime, !sweepOnBackgroundThread); + // Update the runtime malloc counter only if we were doing a full GC. + if (isFull) { + AutoLockGC lock(rt); + mallocCounter.updateOnGCEnd(tunables, lock); + } + { gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::DESTROY); @@ -7085,7 +7123,7 @@ GCRuntime::budgetIncrementalGC(bool nonincrementalByAPI, JS::gcreason::Reason re return resetIncrementalGC(unsafeReason, lock); } - if (isTooMuchMalloc()) { + if (mallocCounter.shouldTriggerGC(tunables) == NonIncrementalTrigger) { budget.makeUnlimited(); stats().nonincremental(AbortReason::MallocBytesTrigger); } @@ -7101,7 +7139,7 @@ GCRuntime::budgetIncrementalGC(bool nonincrementalByAPI, JS::gcreason::Reason re stats().nonincremental(AbortReason::GCBytesTrigger); } - if (zone->isTooMuchMalloc()) { + if (zone->shouldTriggerGCForTooMuchMalloc() == NonIncrementalTrigger) { CheckZoneIsScheduled(zone, reason, "malloc bytes"); budget.makeUnlimited(); stats().nonincremental(AbortReason::MallocBytesTrigger); @@ -7145,9 +7183,7 @@ class AutoScheduleZonesForGC } // This ensures we collect zones that have reached the malloc limit. - // TODO: Start collecting these zones earlier like we do for the GC - // bytes trigger above (bug 1384049). - if (zone->isTooMuchMalloc()) + if (zone->shouldTriggerGCForTooMuchMalloc()) zone->scheduleGC(); } } @@ -7655,7 +7691,7 @@ GCRuntime::minorGC(JS::gcreason::Reason reason, gcstats::PhaseKind phase) { AutoLockGC lock(rt); for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) - maybeAllocTriggerGC(zone, lock); + maybeAllocTriggerZoneGC(zone, lock); } } @@ -7856,7 +7892,7 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) rt->gc.mergeCompartments(source, target); AutoLockGC lock(rt); - rt->gc.maybeAllocTriggerGC(target->zone(), lock); + rt->gc.maybeAllocTriggerZoneGC(target->zone(), lock); } void From a6405ef24266f33d582384d8005203c7af54d216 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Thu, 26 Oct 2017 10:03:51 +0100 Subject: [PATCH 013/105] Bug 1408375 - Move max malloc bytes parameter to GCSchedulingTunables r=sfink --- js/src/gc/GCRuntime.h | 14 ++++++++++---- js/src/gc/Zone.cpp | 2 +- js/src/jsapi.h | 6 +++--- js/src/jsgc.cpp | 16 +++++++++++----- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 36b801feeb81..7a430ae41cec 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -148,6 +148,13 @@ class GCSchedulingTunables */ UnprotectedData gcMaxBytes_; + /* + * JSGC_MAX_MALLOC_BYTES + * + * Initial malloc bytes threshold. + */ + UnprotectedData maxMallocBytes_; + /* * JSGC_MAX_NURSERY_BYTES * @@ -251,6 +258,7 @@ class GCSchedulingTunables GCSchedulingTunables(); size_t gcMaxBytes() const { return gcMaxBytes_; } + size_t maxMallocBytes() const { return maxMallocBytes_; } size_t gcMaxNurseryBytes() const { return gcMaxNurseryBytes_; } size_t gcZoneAllocThresholdBase() const { return gcZoneAllocThresholdBase_; } float allocThresholdFactor() const { return allocThresholdFactor_; } @@ -271,6 +279,8 @@ class GCSchedulingTunables MOZ_MUST_USE bool setParameter(JSGCParamKey key, uint32_t value, const AutoLockGC& lock); void resetParameter(JSGCParamKey key, const AutoLockGC& lock); + void setMaxMallocBytes(size_t value); + private: void setHighFrequencyLowLimit(uint64_t value); void setHighFrequencyHighLimit(uint64_t value); @@ -669,9 +679,6 @@ class MemoryCounter // GC trigger threshold for memory allocations. size_t maxBytes_; - // Initial GC trigger threshold. - GCLockData initialMaxBytes_; - // The counter value at the start of a GC. ActiveThreadData bytesAtStartOfGC_; @@ -683,7 +690,6 @@ class MemoryCounter size_t bytes() const { return bytes_; } size_t maxBytes() const { return maxBytes_; } - size_t initialMaxBytes(const AutoLockGC& lock) const { return initialMaxBytes_; } TriggerKind triggered() const { return triggered_; } void setMax(size_t newMax, const AutoLockGC& lock); diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp index 044e78e0a56c..6d302e105916 100644 --- a/js/src/gc/Zone.cpp +++ b/js/src/gc/Zone.cpp @@ -69,7 +69,7 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup* group) AutoLockGC lock(rt); threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables, rt->gc.schedulingState, lock); - setGCMaxMallocBytes(rt->gc.maxMallocBytesAllocated() * 0.9, lock); + setGCMaxMallocBytes(rt->gc.tunables.maxMallocBytes(), lock); jitCodeCounter.setMax(jit::MaxCodeBytesPerProcess * 0.8, lock); } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index c3a077e3052c..88730ef739f7 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1835,10 +1835,10 @@ typedef enum JSGCParamKey { JSGC_MAX_BYTES = 0, /** - * Number of JS_malloc bytes before last ditch GC. + * Initial value for the malloc bytes threshold. * - * Pref; javascript.options.mem.high_water_mark - * Default: 0xffffffff + * Pref: javascript.options.mem.high_water_mark + * Default: TuningDefaults::MaxMallocBytes */ JSGC_MAX_MALLOC_BYTES = 1, diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 5d87253d7cb7..d1d02c150aec 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1377,6 +1377,12 @@ GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, const AutoL return true; } +void +GCSchedulingTunables::setMaxMallocBytes(size_t value) +{ + maxMallocBytes_ = value; +} + void GCSchedulingTunables::setHighFrequencyLowLimit(uint64_t newLimit) { @@ -1415,6 +1421,7 @@ GCSchedulingTunables::setMaxEmptyChunkCount(uint32_t value) GCSchedulingTunables::GCSchedulingTunables() : gcMaxBytes_(0), + maxMallocBytes_(TuningDefaults::MaxMallocBytes), gcMaxNurseryBytes_(0), gcZoneAllocThresholdBase_(TuningDefaults::GCZoneAllocThresholdBase), allocThresholdFactor_(TuningDefaults::AllocThresholdFactor), @@ -1796,9 +1803,10 @@ js::RemoveRawValueRoot(JSContext* cx, Value* vp) void GCRuntime::setMaxMallocBytes(size_t value, const AutoLockGC& lock) { + tunables.setMaxMallocBytes(value); mallocCounter.setMax(value, lock); for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) - zone->setGCMaxMallocBytes(value * 0.9, lock); + zone->setGCMaxMallocBytes(value, lock); } double @@ -1893,7 +1901,6 @@ ZoneHeapThreshold::updateForRemovedArena(const GCSchedulingTunables& tunables) MemoryCounter::MemoryCounter() : bytes_(0), maxBytes_(0), - initialMaxBytes_(0), triggered_(NoTrigger) {} @@ -1913,7 +1920,7 @@ MemoryCounter::updateOnGCEnd(const GCSchedulingTunables& tunables, const AutoLoc if (shouldTriggerGC(tunables)) maxBytes_ *= 2; else - maxBytes_ = std::max(initialMaxBytes_.ref(), size_t(maxBytes_ * 0.9)); + maxBytes_ = std::max(tunables.maxMallocBytes(), size_t(maxBytes_ * 0.9)); bytes_ -= bytesAtStartOfGC_; triggered_ = NoTrigger; } @@ -1921,8 +1928,7 @@ MemoryCounter::updateOnGCEnd(const GCSchedulingTunables& tunables, const AutoLoc void MemoryCounter::setMax(size_t newMax, const AutoLockGC& lock) { - initialMaxBytes_ = newMax; - maxBytes_ = initialMaxBytes_; + maxBytes_ = newMax; reset(); } From 7c3b9b11557b60ea7f4afee955209a87f6e7b4be Mon Sep 17 00:00:00 2001 From: Ethan Lin Date: Thu, 26 Oct 2017 17:36:54 +0800 Subject: [PATCH 014/105] Bug 1411860 - Create nsDisplaySubDocument unconditionally. r=mattwoodrow MozReview-Commit-ID: ELXI1SYZTa4 --HG-- extra : rebase_source : 601f82f054edc4e54470ea80a316aa41edc2e1f4 --- layout/generic/nsSubDocumentFrame.cpp | 14 +++++++------- layout/painting/nsDisplayList.cpp | 1 + layout/painting/nsDisplayList.h | 11 +++++++++++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp index 3545b412bf95..6b61380cb2d0 100644 --- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -576,13 +576,13 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, childItems.AppendToTop(resolutionItem); needsOwnLayer = false; } - if (needsOwnLayer) { - // We always want top level content documents to be in their own layer. - nsDisplaySubDocument* layerItem = new (aBuilder) nsDisplaySubDocument( - aBuilder, subdocRootFrame ? subdocRootFrame : this, this, - &childItems, flags); - childItems.AppendToTop(layerItem); - } + + // We always want top level content documents to be in their own layer. + nsDisplaySubDocument* layerItem = new (aBuilder) nsDisplaySubDocument( + aBuilder, subdocRootFrame ? subdocRootFrame : this, this, + &childItems, flags); + childItems.AppendToTop(layerItem); + layerItem->SetShouldFlattenAway(!needsOwnLayer); // If we're using containers for root frames, then the earlier call // to AddCanvasBackgroundColorItem won't have been able to add an diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 581335c03a80..54912a83bed4 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -6847,6 +6847,7 @@ nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder, nsDisplayList* aList, uint32_t aFlags) : nsDisplayOwnLayer(aBuilder, aFrame, aList, aBuilder->CurrentActiveScrolledRoot(), aFlags) , mScrollParentId(aBuilder->GetCurrentScrollParentId()) + , mShouldFlatten(false) , mSubDocFrame(aSubDocFrame) { MOZ_COUNT_CTOR(nsDisplaySubDocument); diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index 96751e2c27bc..93225d75da6a 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -5096,6 +5096,16 @@ public: virtual bool ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) const override; + virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override + { + return mShouldFlatten; + } + + void SetShouldFlattenAway(bool aShouldFlatten) + { + mShouldFlatten = aShouldFlatten; + } + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) const override; @@ -5107,6 +5117,7 @@ public: protected: ViewID mScrollParentId; bool mForceDispatchToContentRegion; + bool mShouldFlatten; nsSubDocumentFrame* mSubDocFrame; }; From 6bb51d72d4a67e4bfc2c2ef0759083ceec74a4b7 Mon Sep 17 00:00:00 2001 From: Ethan Lin Date: Thu, 26 Oct 2017 17:36:54 +0800 Subject: [PATCH 015/105] Bug 1411886 - Rebuild the dependent frame's display items in retained display list mode. r=mattwoodrow MozReview-Commit-ID: F7239qCoKuI --HG-- extra : rebase_source : db52d743caa8da001707ca7b7116c07a1d22acbc --- layout/painting/RetainedDisplayListBuilder.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/layout/painting/RetainedDisplayListBuilder.cpp b/layout/painting/RetainedDisplayListBuilder.cpp index ffe00345a1d6..cfebf2a3f148 100644 --- a/layout/painting/RetainedDisplayListBuilder.cpp +++ b/layout/painting/RetainedDisplayListBuilder.cpp @@ -53,6 +53,9 @@ void MarkFramesWithItemsAndImagesModified(nsDisplayList* aList) if (invalidate) { i->FrameForInvalidation()->MarkNeedsDisplayItemRebuild(); + if (i->GetDependentFrame()) { + i->GetDependentFrame()->MarkNeedsDisplayItemRebuild(); + } } } if (i->GetChildren()) { From fa6c47dce55766966e91953d32467a3a37b98ec8 Mon Sep 17 00:00:00 2001 From: sotaro Date: Thu, 26 Oct 2017 19:40:24 +0900 Subject: [PATCH 016/105] Bug 1410766 - Re-acquireDevices in gpu process when SimulateDeviceReset() is called r=dvander --- gfx/ipc/GPUParent.cpp | 11 +++++++++++ gfx/ipc/GPUParent.h | 1 + gfx/ipc/GPUProcessManager.cpp | 4 ++++ gfx/ipc/PGPU.ipdl | 4 ++++ ipc/ipdl/sync-messages.ini | 2 ++ 5 files changed, 22 insertions(+) diff --git a/gfx/ipc/GPUParent.cpp b/gfx/ipc/GPUParent.cpp index 1a60cd23adbe..81672b0682b3 100644 --- a/gfx/ipc/GPUParent.cpp +++ b/gfx/ipc/GPUParent.cpp @@ -330,6 +330,17 @@ GPUParent::RecvGetDeviceStatus(GPUDeviceData* aOut) return IPC_OK(); } +mozilla::ipc::IPCResult +GPUParent::RecvSimulateDeviceReset(GPUDeviceData* aOut) +{ +#if defined(XP_WIN) + DeviceManagerDx::Get()->ForceDeviceReset(ForcedDeviceResetReason::COMPOSITOR_UPDATED); + DeviceManagerDx::Get()->MaybeResetAndReacquireDevices(); +#endif + RecvGetDeviceStatus(aOut); + return IPC_OK(); +} + mozilla::ipc::IPCResult GPUParent::RecvNewContentCompositorManager(Endpoint&& aEndpoint) { diff --git a/gfx/ipc/GPUParent.h b/gfx/ipc/GPUParent.h index 50d71cd1086c..cee283cf1195 100644 --- a/gfx/ipc/GPUParent.h +++ b/gfx/ipc/GPUParent.h @@ -48,6 +48,7 @@ public: mozilla::ipc::IPCResult RecvNewContentVRManager(Endpoint&& aEndpoint) override; mozilla::ipc::IPCResult RecvNewContentVideoDecoderManager(Endpoint&& aEndpoint) override; mozilla::ipc::IPCResult RecvGetDeviceStatus(GPUDeviceData* aOutStatus) override; + mozilla::ipc::IPCResult RecvSimulateDeviceReset(GPUDeviceData* aOutStatus) override; mozilla::ipc::IPCResult RecvAddLayerTreeIdMapping(const LayerTreeIdMapping& aMapping) override; mozilla::ipc::IPCResult RecvRemoveLayerTreeIdMapping(const LayerTreeIdMapping& aMapping) override; mozilla::ipc::IPCResult RecvNotifyGpuObservers(const nsCString& aTopic) override; diff --git a/gfx/ipc/GPUProcessManager.cpp b/gfx/ipc/GPUProcessManager.cpp index f6ab6cc4e4e0..0a3dd42f240f 100644 --- a/gfx/ipc/GPUProcessManager.cpp +++ b/gfx/ipc/GPUProcessManager.cpp @@ -421,6 +421,10 @@ GPUProcessManager::SimulateDeviceReset() gfxPlatform::GetPlatform()->CompositorUpdated(); if (mProcess) { + GPUDeviceData data; + if (mGPUChild->SendSimulateDeviceReset(&data)) { + gfxPlatform::GetPlatform()->ImportGPUDeviceData(data); + } OnRemoteProcessDeviceReset(mProcess); } else { OnInProcessDeviceReset(); diff --git a/gfx/ipc/PGPU.ipdl b/gfx/ipc/PGPU.ipdl index 2527aa3ac0a4..cd0464d5f1a3 100644 --- a/gfx/ipc/PGPU.ipdl +++ b/gfx/ipc/PGPU.ipdl @@ -80,6 +80,10 @@ parent: // one is available (i.e., Init has completed). sync GetDeviceStatus() returns (GPUDeviceData status); + // Request to simulate device reset and to get the updated DeviceStatus from + // the GPU process. This blocks until one is available (i.e., Init has completed). + sync SimulateDeviceReset() returns (GPUDeviceData status); + // Have a message be broadcasted to the GPU process by the GPU process // observer service. async NotifyGpuObservers(nsCString aTopic); diff --git a/ipc/ipdl/sync-messages.ini b/ipc/ipdl/sync-messages.ini index 0483c0fc1f93..c47a7f845610 100644 --- a/ipc/ipdl/sync-messages.ini +++ b/ipc/ipdl/sync-messages.ini @@ -930,6 +930,8 @@ description = description = [PGPU::GetDeviceStatus] description = +[PGPU::SimulateDeviceReset] +description = [PAPZCTreeManager::ReceiveMultiTouchInputEvent] description = [PAPZCTreeManager::ReceiveMouseInputEvent] From 0ae243650caa0853e4c01861f6a2a359c639ca84 Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Thu, 26 Oct 2017 10:41:49 +0300 Subject: [PATCH 017/105] Bug 1411866 - Update cubeb from upstream to cf5ddc5. r=padenot,kinetik MozReview-Commit-ID: IPgIZ6oBVfg --- media/libcubeb/README_MOZILLA | 2 +- media/libcubeb/src/cubeb_audiounit.cpp | 26 ++++++++++++++++++-------- media/libcubeb/src/cubeb_utils.h | 2 +- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/media/libcubeb/README_MOZILLA b/media/libcubeb/README_MOZILLA index f2de53433642..506d2dc5434b 100644 --- a/media/libcubeb/README_MOZILLA +++ b/media/libcubeb/README_MOZILLA @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system. The cubeb git repository is: git://github.com/kinetiknz/cubeb.git -The git commit ID used was 40cd4ad03b68a215e985979c96783824f19e288f (2017-10-03 13:38:45 +0200) +The git commit ID used was cf5ddc5316dd1ab3ee7f54b2dcbcc9980e556d13 (2017-10-26 09:48:04 +1300) diff --git a/media/libcubeb/src/cubeb_audiounit.cpp b/media/libcubeb/src/cubeb_audiounit.cpp index d06ed9247869..348528489a8e 100644 --- a/media/libcubeb/src/cubeb_audiounit.cpp +++ b/media/libcubeb/src/cubeb_audiounit.cpp @@ -651,13 +651,19 @@ audiounit_reinit_stream(cubeb_stream * stm, device_flags_value flags) * device. This is considered the most expected behavior for the user. */ if (flags & DEV_INPUT) { r = audiounit_set_device_info(stm, 0, INPUT); - assert(r == CUBEB_OK); + if (r != CUBEB_OK) { + LOG("(%p) Set input device info failed. This can happen when last media device is unplugged", stm); + return CUBEB_ERROR; + } } /* Always use the default output on reinit. This is not correct in every case * but it is sufficient for Firefox and prevent reinit from reporting failures. * It will change soon when reinit mechanism will be updated. */ r = audiounit_set_device_info(stm, 0, OUTPUT); - assert(r == CUBEB_OK); + if (r != CUBEB_OK) { + LOG("(%p) Set output device info failed. This can happen when last media device is unplugged", stm); + return CUBEB_ERROR; + } if (audiounit_setup_stream(stm) != CUBEB_OK) { LOG("(%p) Stream reinit failed.", stm); @@ -697,6 +703,8 @@ event_addr_to_string(AudioObjectPropertySelector selector) } } +static int audiounit_uninstall_system_changed_callback(cubeb_stream * stm); + static OSStatus audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count, const AudioObjectPropertyAddress * addresses, @@ -775,6 +783,9 @@ audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count, // Get/SetProperties method from inside notify callback dispatch_async(stm->context->serial_queue, ^() { if (audiounit_reinit_stream(stm, switch_side) != CUBEB_OK) { + if (audiounit_uninstall_system_changed_callback(stm) != CUBEB_OK) { + LOG("(%p) Could not uninstall the device changed callback", stm); + } stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); LOG("(%p) Could not reopen the stream after switching.", stm); } @@ -921,7 +932,7 @@ audiounit_uninstall_system_changed_callback(cubeb_stream * stm) { OSStatus r; - if (stm->output_unit) { + if (has_output(stm)) { r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); if (r != noErr) { @@ -929,7 +940,7 @@ audiounit_uninstall_system_changed_callback(cubeb_stream * stm) } } - if (stm->input_unit) { + if (has_input(stm)) { r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); if (r != noErr) { @@ -2498,7 +2509,9 @@ audiounit_stream_init(cubeb * context, cubeb_state_callback state_callback, void * user_ptr) { - std::unique_ptr stm(nullptr, audiounit_stream_destroy); + std::unique_ptr stm(new cubeb_stream(context), + audiounit_stream_destroy); + context->active_streams += 1; int r; assert(context); @@ -2509,8 +2522,6 @@ audiounit_stream_init(cubeb * context, return CUBEB_ERROR_INVALID_PARAMETER; } - stm.reset(new cubeb_stream(context)); - /* These could be different in the future if we have both * full-duplex stream and different devices for input vs output. */ stm->data_callback = data_callback; @@ -2539,7 +2550,6 @@ audiounit_stream_init(cubeb * context, // It's not critical to lock here, because no other thread has been started // yet, but it allows to assert that the lock has been taken in // `audiounit_setup_stream`. - context->active_streams += 1; auto_lock lock(stm->mutex); r = audiounit_setup_stream(stm.get()); } diff --git a/media/libcubeb/src/cubeb_utils.h b/media/libcubeb/src/cubeb_utils.h index 09665606247b..dc08fec9928d 100644 --- a/media/libcubeb/src/cubeb_utils.h +++ b/media/libcubeb/src/cubeb_utils.h @@ -17,7 +17,7 @@ #include #include #include -#if defined(WIN32) +#if defined(_WIN32) #include "cubeb_utils_win.h" #else #include "cubeb_utils_unix.h" From a46587ea41680846ce52aa7925a7fb465f3ea6d8 Mon Sep 17 00:00:00 2001 From: Jesse Ruderman Date: Thu, 26 Oct 2017 09:05:02 -0400 Subject: [PATCH 018/105] Bug 1281695 - Add crashtest. r=me --- dom/media/tests/crashtests/1281695.html | 24 ++++++++++++++++++++++ dom/media/tests/crashtests/crashtests.list | 1 + 2 files changed, 25 insertions(+) create mode 100644 dom/media/tests/crashtests/1281695.html diff --git a/dom/media/tests/crashtests/1281695.html b/dom/media/tests/crashtests/1281695.html new file mode 100644 index 000000000000..9865c3a5099b --- /dev/null +++ b/dom/media/tests/crashtests/1281695.html @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/dom/media/tests/crashtests/crashtests.list b/dom/media/tests/crashtests/crashtests.list index c89314b172a2..9d462333e70d 100644 --- a/dom/media/tests/crashtests/crashtests.list +++ b/dom/media/tests/crashtests/crashtests.list @@ -14,6 +14,7 @@ load 855796.html load 860143.html load 861958.html load 863929.html +load 1281695.html load 1306476.html load 1348381.html load 1367930_1.html From f8a9383a3daf9931b7bb6c01b2389a9adea9ea9e Mon Sep 17 00:00:00 2001 From: Jesse Ruderman Date: Thu, 26 Oct 2017 09:05:03 -0400 Subject: [PATCH 019/105] Bug 1185191 - Add crashtest. r=me --- dom/media/tests/crashtests/1185191.html | 21 +++++++++++++++++++++ dom/media/tests/crashtests/crashtests.list | 1 + 2 files changed, 22 insertions(+) create mode 100644 dom/media/tests/crashtests/1185191.html diff --git a/dom/media/tests/crashtests/1185191.html b/dom/media/tests/crashtests/1185191.html new file mode 100644 index 000000000000..a1b931e51821 --- /dev/null +++ b/dom/media/tests/crashtests/1185191.html @@ -0,0 +1,21 @@ + + + + + + + diff --git a/dom/media/tests/crashtests/crashtests.list b/dom/media/tests/crashtests/crashtests.list index 9d462333e70d..ac350011b34f 100644 --- a/dom/media/tests/crashtests/crashtests.list +++ b/dom/media/tests/crashtests/crashtests.list @@ -14,6 +14,7 @@ load 855796.html load 860143.html load 861958.html load 863929.html +load 1185191.html load 1281695.html load 1306476.html load 1348381.html From 064ab5680a8f7cfdf5df4cc40dd560f7dc42c77c Mon Sep 17 00:00:00 2001 From: Ya-Chieh Wu Date: Wed, 25 Oct 2017 20:11:00 -0400 Subject: [PATCH 020/105] Bug 1410826 - Check IsPrimaryFrame() first in nsIFrame::HasAnimationOfTransform. r=mats MozReview-Commit-ID: BFJGmr19ESL --- layout/generic/nsFrame.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 3a56150adc55..f8d2e94cafdc 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1486,10 +1486,9 @@ nsIFrame::HasAnimationOfTransform(EffectSet* aEffectSet) const EffectSet* effects = aEffectSet ? aEffectSet : EffectSet::GetEffectSet(this); - return mContent && + return IsPrimaryFrame() && nsLayoutUtils::HasAnimationOfProperty(effects, eCSSProperty_transform) && - IsFrameOfType(eSupportsCSSTransforms) && - IsPrimaryFrame(); + IsFrameOfType(eSupportsCSSTransforms); } bool From c8f0b1e5a140d08a9f32659541a845b110614dc9 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Thu, 26 Oct 2017 22:10:30 +0900 Subject: [PATCH 021/105] Bug 1396499 - Part 1: Reuse Array object for expression stack values in GeneratorObject::suspend. r=jandem --- js/src/jit/BaselineCompiler.cpp | 14 ++++++-------- js/src/vm/GeneratorObject.cpp | 24 ++++++++++++++++++------ js/src/vm/GeneratorObject.h | 3 +++ 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 0ef979d63579..07540d0c0c60 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -4837,24 +4837,22 @@ BaselineCompiler::emit_JSOP_RESUME() { masm.unboxObject(exprStackSlot, scratch2); - Register initLength = regs.takeAny(); + Register arrayLength = regs.takeAny(); masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2); - masm.load32(Address(scratch2, ObjectElements::offsetOfInitializedLength()), initLength); + masm.load32(Address(scratch2, ObjectElements::offsetOfLength()), arrayLength); + masm.store32(Imm32(0), Address(scratch2, ObjectElements::offsetOfLength())); Label loop, loopDone; masm.bind(&loop); - masm.branchTest32(Assembler::Zero, initLength, initLength, &loopDone); + masm.branchTest32(Assembler::Zero, arrayLength, arrayLength, &loopDone); { masm.pushValue(Address(scratch2, 0)); masm.addPtr(Imm32(sizeof(Value)), scratch2); - masm.sub32(Imm32(1), initLength); + masm.sub32(Imm32(1), arrayLength); masm.jump(&loop); } masm.bind(&loopDone); - - masm.guardedCallPreBarrier(exprStackSlot, MIRType::Value); - masm.storeValue(NullValue(), exprStackSlot); - regs.add(initLength); + regs.add(arrayLength); } masm.bind(&noExprStack); diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index e635f5a5290d..4115e5219c29 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -11,6 +11,7 @@ #include "jsatominlines.h" #include "jsscriptinlines.h" +#include "vm/ArrayObject-inl.h" #include "vm/NativeObject-inl.h" #include "vm/Stack-inl.h" @@ -67,7 +68,7 @@ GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD || *pc == JSOP_AWAIT); Rooted genObj(cx, &obj->as()); - MOZ_ASSERT(!genObj->hasExpressionStack()); + MOZ_ASSERT(!genObj->hasExpressionStack() || genObj->isExpressionStackEmpty()); MOZ_ASSERT_IF(*pc == JSOP_AWAIT, genObj->callee().isAsync()); MOZ_ASSERT_IF(*pc == JSOP_YIELD, genObj->callee().isStarGenerator() || @@ -81,9 +82,20 @@ GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame ArrayObject* stack = nullptr; if (nvalues > 0) { - stack = NewDenseCopiedArray(cx, nvalues, vp); - if (!stack) - return false; + do { + if (genObj->hasExpressionStack()) { + auto result = genObj->expressionStack().setOrExtendDenseElements( + cx, 0, vp, nvalues, ShouldUpdateTypes::DontUpdate); + if (result == DenseElementResult::Success) + break; + if (result == DenseElementResult::Failure) + return false; + } + + stack = NewDenseCopiedArray(cx, nvalues, vp); + if (!stack) + return false; + } while (false); } uint32_t yieldAndAwaitIndex = GET_UINT24(pc); @@ -169,13 +181,13 @@ GeneratorObject::resume(JSContext* cx, InterpreterActivation& activation, if (genObj->hasArgsObj()) activation.regs().fp()->initArgsObj(genObj->argsObj()); - if (genObj->hasExpressionStack()) { + if (genObj->hasExpressionStack() && !genObj->isExpressionStackEmpty()) { uint32_t len = genObj->expressionStack().length(); MOZ_ASSERT(activation.regs().spForStackDepth(len)); const Value* src = genObj->expressionStack().getDenseElements(); mozilla::PodCopy(activation.regs().sp, src, len); activation.regs().sp += len; - genObj->clearExpressionStack(); + genObj->expressionStack().setLengthInt32(0); } JSScript* script = callee->nonLazyScript(); diff --git a/js/src/vm/GeneratorObject.h b/js/src/vm/GeneratorObject.h index 014ecdfe9e6c..a4da631937a1 100644 --- a/js/src/vm/GeneratorObject.h +++ b/js/src/vm/GeneratorObject.h @@ -100,6 +100,9 @@ class GeneratorObject : public NativeObject bool hasExpressionStack() const { return getFixedSlot(EXPRESSION_STACK_SLOT).isObject(); } + bool isExpressionStackEmpty() const { + return expressionStack().length() == 0; + } ArrayObject& expressionStack() const { return getFixedSlot(EXPRESSION_STACK_SLOT).toObject().as(); } From afdbd65a3b7225628cbb94e123ed3c2a7f224911 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Thu, 26 Oct 2017 22:10:31 +0900 Subject: [PATCH 022/105] Bug 1396499 - Part 2: Do not hold reference to expression stack values in GeneratorObject::suspend. r=jandem --- js/src/jit/BaselineCompiler.cpp | 13 +++++++------ js/src/vm/GeneratorObject.cpp | 9 ++++++--- js/src/vm/GeneratorObject.h | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 07540d0c0c60..1c6e4a1aa7f5 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -4837,22 +4837,23 @@ BaselineCompiler::emit_JSOP_RESUME() { masm.unboxObject(exprStackSlot, scratch2); - Register arrayLength = regs.takeAny(); + Register initLength = regs.takeAny(); masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2); - masm.load32(Address(scratch2, ObjectElements::offsetOfLength()), arrayLength); - masm.store32(Imm32(0), Address(scratch2, ObjectElements::offsetOfLength())); + masm.load32(Address(scratch2, ObjectElements::offsetOfInitializedLength()), initLength); + masm.store32(Imm32(0), Address(scratch2, ObjectElements::offsetOfInitializedLength())); Label loop, loopDone; masm.bind(&loop); - masm.branchTest32(Assembler::Zero, arrayLength, arrayLength, &loopDone); + masm.branchTest32(Assembler::Zero, initLength, initLength, &loopDone); { masm.pushValue(Address(scratch2, 0)); + masm.guardedCallPreBarrier(Address(scratch2, 0), MIRType::Value); masm.addPtr(Imm32(sizeof(Value)), scratch2); - masm.sub32(Imm32(1), arrayLength); + masm.sub32(Imm32(1), initLength); masm.jump(&loop); } masm.bind(&loopDone); - regs.add(arrayLength); + regs.add(initLength); } masm.bind(&noExprStack); diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index 4115e5219c29..e3802932d39e 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -84,10 +84,13 @@ GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame if (nvalues > 0) { do { if (genObj->hasExpressionStack()) { + MOZ_ASSERT(genObj->expressionStack().getDenseInitializedLength() == 0); auto result = genObj->expressionStack().setOrExtendDenseElements( cx, 0, vp, nvalues, ShouldUpdateTypes::DontUpdate); - if (result == DenseElementResult::Success) + if (result == DenseElementResult::Success) { + MOZ_ASSERT(genObj->expressionStack().getDenseInitializedLength() == nvalues); break; + } if (result == DenseElementResult::Failure) return false; } @@ -182,12 +185,12 @@ GeneratorObject::resume(JSContext* cx, InterpreterActivation& activation, activation.regs().fp()->initArgsObj(genObj->argsObj()); if (genObj->hasExpressionStack() && !genObj->isExpressionStackEmpty()) { - uint32_t len = genObj->expressionStack().length(); + uint32_t len = genObj->expressionStack().getDenseInitializedLength(); MOZ_ASSERT(activation.regs().spForStackDepth(len)); const Value* src = genObj->expressionStack().getDenseElements(); mozilla::PodCopy(activation.regs().sp, src, len); activation.regs().sp += len; - genObj->expressionStack().setLengthInt32(0); + genObj->expressionStack().setDenseInitializedLength(0); } JSScript* script = callee->nonLazyScript(); diff --git a/js/src/vm/GeneratorObject.h b/js/src/vm/GeneratorObject.h index a4da631937a1..2030ed9602cf 100644 --- a/js/src/vm/GeneratorObject.h +++ b/js/src/vm/GeneratorObject.h @@ -101,7 +101,7 @@ class GeneratorObject : public NativeObject return getFixedSlot(EXPRESSION_STACK_SLOT).isObject(); } bool isExpressionStackEmpty() const { - return expressionStack().length() == 0; + return expressionStack().getDenseInitializedLength() == 0; } ArrayObject& expressionStack() const { return getFixedSlot(EXPRESSION_STACK_SLOT).toObject().as(); From ab2b1fb004ac1573c79e04ac198e6fd759c8e5c1 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Thu, 26 Oct 2017 22:10:31 +0900 Subject: [PATCH 023/105] Bug 1385112 - Part 2: Add testcase. r=anba --- js/src/jit-test/tests/parser/bug-1385112.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 js/src/jit-test/tests/parser/bug-1385112.js diff --git a/js/src/jit-test/tests/parser/bug-1385112.js b/js/src/jit-test/tests/parser/bug-1385112.js new file mode 100644 index 000000000000..a84ecd1d2674 --- /dev/null +++ b/js/src/jit-test/tests/parser/bug-1385112.js @@ -0,0 +1 @@ +eval(`let \\u{${"0".repeat(0x8000)}65} = 123;`); From 0ed394bfdf74b5211800259a1608bdba06f02b82 Mon Sep 17 00:00:00 2001 From: Jessica Jong Date: Thu, 26 Oct 2017 14:08:10 +0800 Subject: [PATCH 024/105] Bug 1378079 - Part 1: Gecko changes for adding attribute 'is' to parser. r=hsivonen --HG-- extra : rebase_source : 413505a2aa4fe229da957906697f172527361b38 --- parser/html/javasrc/AttributeName.java | 275 +++++++++++++------------ parser/html/nsHtml5AttributeName.cpp | 175 ++++++++-------- parser/html/nsHtml5AttributeName.h | 1 + 3 files changed, 230 insertions(+), 221 deletions(-) diff --git a/parser/html/javasrc/AttributeName.java b/parser/html/javasrc/AttributeName.java index 8d9ab7d84555..49182eee8f49 100644 --- a/parser/html/javasrc/AttributeName.java +++ b/parser/html/javasrc/AttributeName.java @@ -1178,6 +1178,7 @@ public final class AttributeName public static final AttributeName CLOSURE = new AttributeName(ALL_NO_NS, SAME_LOCAL("closure"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG); public static final AttributeName CLOSE = new AttributeName(ALL_NO_NS, SAME_LOCAL("close"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG); public static final AttributeName CLASS = new AttributeName(ALL_NO_NS, SAME_LOCAL("class"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG); + public static final AttributeName IS = new AttributeName(ALL_NO_NS, SAME_LOCAL("is"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG); public static final AttributeName KEYSYSTEM = new AttributeName(ALL_NO_NS, SAME_LOCAL("keysystem"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG); public static final AttributeName KEYSPLINES = new AttributeName(ALL_NO_NS, SVG_DIFFERENT("keysplines", "keySplines"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG); public static final AttributeName LOWSRC = new AttributeName(ALL_NO_NS, SAME_LOCAL("lowsrc"), ALL_NO_PREFIX, NCNAME_HTML | NCNAME_FOREIGN | NCNAME_LANG); @@ -1272,14 +1273,14 @@ public final class AttributeName ARIA_SETSIZE, LANG, FLOOD_OPACITY, - PATTERN, + PATTERNTRANSFORM, ARIA_DESCRIBEDBY, SPACING, ONBEFORECUT, LIMITINGCONEANGLE, ONINPUT, CLIP_PATH, - SRCSET, + ROWS, VALUETYPE, Y, ARIA_VALUEMIN, @@ -1294,8 +1295,8 @@ public final class AttributeName ONFOCUSOUT, SLOPE, COLS, - FONTFAMILY, - COLUMNALIGN, + FONT_STYLE, + COLUMNLINES, RX, MIN, K3, @@ -1322,11 +1323,11 @@ public final class AttributeName KEYPOINTS, CHAR, ONDRAGLEAVE, - MAXSIZE, - DEPTH, - LETTER_SPACING, - TEXT_ANCHOR, - REQUIREDFEATURES, + LOWSRC, + CONTEXTMENU, + KEYTIMES, + TEXT_DECORATION, + REQUIRED, VIEWBOX, DY, END, @@ -1380,14 +1381,14 @@ public final class AttributeName ONERROR, BIAS, CLASS, - PRESERVEASPECTRATIO, - CONTENTEDITABLE, - FONTWEIGHT, - FILTERUNITS, - ONSTOP, - STITCHTILES, - WIDTH, - INPUTMODE, + PRESERVEALPHA, + ALTTEXT, + FILTER, + FONT_SIZE_ADJUST, + RT, + RESTART, + WRITING_MODE, + GROUPALIGN, REQUIREDEXTENSIONS, ELEVATION, DX, @@ -1494,23 +1495,23 @@ public final class AttributeName START, CLASSID, CLOSURE, - KEYSPLINES, - OFFSET, - ROWSPACING, - VERSION, - CONTENT, - FONT_STRETCH, - FONTSTYLE, - FONT_VARIANT, - FONT_SIZE, - MULTIPLE, - POSTER, - PATTERNCONTENTUNITS, - TEXT_RENDERING, - TEXT, - COLUMNSPAN, - COLUMNWIDTH, - ONCUT, + KEYSYSTEM, + MINSIZE, + ROWSPAN, + SUBSCRIPTSHIFT, + CONTROLS, + ENCTYPE, + FONT_WEIGHT, + FONT_FAMILY, + FONTSIZE, + LIST, + ONSTART, + PATTERNUNITS, + SYSTEMLANGUAGE, + TEXTLENGTH, + ACCUMULATE, + COLUMNSPACING, + ONSUBMIT, RESULT, VALUES, VALUE, @@ -1723,40 +1724,41 @@ public final class AttributeName CROSSORIGIN, CURSOR, CLOSE, - KEYSYSTEM, - LOWSRC, - MINSIZE, - PRESERVEALPHA, - ROWSPAN, - ROWS, - SUBSCRIPTSHIFT, - ALTTEXT, - CONTROLS, - CONTEXTMENU, - ENCTYPE, - FILTER, - FONT_WEIGHT, - FONT_STYLE, - FONT_FAMILY, - FONT_SIZE_ADJUST, - FONTSIZE, - KEYTIMES, - LIST, - RT, - ONSTART, - PATTERNTRANSFORM, - PATTERNUNITS, - RESTART, - SYSTEMLANGUAGE, - TEXT_DECORATION, - TEXTLENGTH, - WRITING_MODE, - ACCUMULATE, - COLUMNLINES, - COLUMNSPACING, - GROUPALIGN, - ONSUBMIT, - REQUIRED, + IS, + KEYSPLINES, + MAXSIZE, + OFFSET, + PRESERVEASPECTRATIO, + ROWSPACING, + SRCSET, + VERSION, + CONTENTEDITABLE, + CONTENT, + DEPTH, + FONT_STRETCH, + FONTWEIGHT, + FONTSTYLE, + FONTFAMILY, + FONT_VARIANT, + FILTERUNITS, + FONT_SIZE, + LETTER_SPACING, + MULTIPLE, + ONSTOP, + POSTER, + PATTERN, + PATTERNCONTENTUNITS, + STITCHTILES, + TEXT_RENDERING, + TEXT_ANCHOR, + TEXT, + WIDTH, + COLUMNSPAN, + COLUMNALIGN, + COLUMNWIDTH, + INPUTMODE, + ONCUT, + REQUIREDFEATURES, }; private final static int[] ATTRIBUTE_HASHES = { 1872034503, @@ -1765,14 +1767,14 @@ public final class AttributeName 1680511804, 1786740932, 1917953597, - 2007021895, + 2007019632, 1680159328, 1723309623, 1754907227, 1820262641, 1905902311, 1933123337, - 1991625270, + 1991392548, 2026893641, 71827457, 1680251485, @@ -1787,8 +1789,8 @@ public final class AttributeName 1922679610, 1941440197, 1983347764, - 2001710298, - 2016810187, + 2001669450, + 2016787611, 2073034754, 57205395, 911736834, @@ -1815,11 +1817,11 @@ public final class AttributeName 1937336473, 1966384692, 1972996699, - 1988784439, - 2000752725, - 2004846654, - 2009079867, - 2024647008, + 1988132214, + 2000162011, + 2004199576, + 2009071951, + 2024616088, 2060474743, 2082471938, 53006051, @@ -1873,14 +1875,14 @@ public final class AttributeName 1972908839, 1982640164, 1983461061, - 1990107683, - 2000096287, - 2001634458, - 2001826027, - 2006459190, - 2008401563, - 2010716309, - 2019887833, + 1990062797, + 1999273799, + 2001578182, + 2001814704, + 2005925890, + 2008084807, + 2010452700, + 2018908874, 2024794274, 2034765641, 2065694722, @@ -1987,23 +1989,23 @@ public final class AttributeName 1975062341, 1983266615, 1983416119, - 1987422362, - 1989522022, - 1991220282, - 1993343287, - 2000160071, - 2001527900, - 2001669449, - 2001732764, - 2001898809, - 2005342360, - 2006824246, - 2007064819, - 2009041198, - 2009231684, - 2016711994, - 2017010843, - 2023342821, + 1987410233, + 1988788535, + 1991021879, + 1991643278, + 2000125224, + 2001210183, + 2001634459, + 2001710299, + 2001898808, + 2004957380, + 2006516551, + 2007064812, + 2008408414, + 2009141482, + 2015950026, + 2016910397, + 2023146024, 2024763702, 2026741958, 2026975253, @@ -2216,39 +2218,40 @@ public final class AttributeName 1983290011, 1983398182, 1983432389, - 1987410233, - 1988132214, - 1988788535, - 1990062797, - 1991021879, - 1991392548, - 1991643278, - 1999273799, - 2000125224, - 2000162011, - 2001210183, - 2001578182, - 2001634459, - 2001669450, - 2001710299, - 2001814704, - 2001898808, - 2004199576, - 2004957380, - 2005925890, - 2006516551, - 2007019632, - 2007064812, - 2008084807, - 2008408414, - 2009071951, - 2009141482, - 2010452700, - 2015950026, - 2016787611, - 2016910397, - 2018908874, - 2023146024, - 2024616088, + 1984430082, + 1987422362, + 1988784439, + 1989522022, + 1990107683, + 1991220282, + 1991625270, + 1993343287, + 2000096287, + 2000160071, + 2000752725, + 2001527900, + 2001634458, + 2001669449, + 2001710298, + 2001732764, + 2001826027, + 2001898809, + 2004846654, + 2005342360, + 2006459190, + 2006824246, + 2007021895, + 2007064819, + 2008401563, + 2009041198, + 2009079867, + 2009231684, + 2010716309, + 2016711994, + 2016810187, + 2017010843, + 2019887833, + 2023342821, + 2024647008, }; } diff --git a/parser/html/nsHtml5AttributeName.cpp b/parser/html/nsHtml5AttributeName.cpp index 6014a8789462..e456c9893da5 100644 --- a/parser/html/nsHtml5AttributeName.cpp +++ b/parser/html/nsHtml5AttributeName.cpp @@ -570,6 +570,7 @@ nsHtml5AttributeName* nsHtml5AttributeName::ATTR_CURSOR = nullptr; nsHtml5AttributeName* nsHtml5AttributeName::ATTR_CLOSURE = nullptr; nsHtml5AttributeName* nsHtml5AttributeName::ATTR_CLOSE = nullptr; nsHtml5AttributeName* nsHtml5AttributeName::ATTR_CLASS = nullptr; +nsHtml5AttributeName* nsHtml5AttributeName::ATTR_IS = nullptr; nsHtml5AttributeName* nsHtml5AttributeName::ATTR_KEYSYSTEM = nullptr; nsHtml5AttributeName* nsHtml5AttributeName::ATTR_KEYSPLINES = nullptr; nsHtml5AttributeName* nsHtml5AttributeName::ATTR_LOWSRC = nullptr; @@ -660,16 +661,16 @@ nsHtml5AttributeName* nsHtml5AttributeName::ATTR_REFY = nullptr; nsHtml5AttributeName** nsHtml5AttributeName::ATTRIBUTE_NAMES = 0; static int32_t const ATTRIBUTE_HASHES_DATA[] = { 1872034503, 1748971848, 1972744954, 1680511804, 1786740932, 1917953597, - 2007021895, 1680159328, 1723309623, 1754907227, 1820262641, 1905902311, - 1933123337, 1991625270, 2026893641, 71827457, 1680251485, 1689130184, + 2007019632, 1680159328, 1723309623, 1754907227, 1820262641, 1905902311, + 1933123337, 1991392548, 2026893641, 71827457, 1680251485, 1689130184, 1747295467, 1754606246, 1757053236, 1804069019, 1854302364, 1889633006, - 1910503637, 1922679610, 1941440197, 1983347764, 2001710298, 2016810187, + 1910503637, 1922679610, 1941440197, 1983347764, 2001669450, 2016787611, 2073034754, 57205395, 911736834, 1680181850, 1680347981, 1684319541, 1704262346, 1734404167, 1747906667, 1752985897, 1754792749, 1756471625, 1776114564, 1790814502, 1814558026, 1823841492, 1864698185, 1881750231, 1902640276, 1908462185, 1916210285, 1922566877, 1924583073, 1937336473, - 1966384692, 1972996699, 1988784439, 2000752725, 2004846654, 2009079867, - 2024647008, 2060474743, 2082471938, 53006051, 60345635, 885522434, + 1966384692, 1972996699, 1988132214, 2000162011, 2004199576, 2009071951, + 2024616088, 2060474743, 2082471938, 53006051, 60345635, 885522434, 1038141480, 1680165487, 1680198381, 1680323325, 1680433915, 1681969220, 1687164232, 1691145478, 1716303957, 1732771842, 1740096054, 1747479606, 1748503880, 1749856356, 1754214628, 1754645079, 1754858317, 1756190926, @@ -678,8 +679,8 @@ static int32_t const ATTRIBUTE_HASHES_DATA[] = { 1884267068, 1894552650, 1905541832, 1906421049, 1910441627, 1915025672, 1916337499, 1922400908, 1922665179, 1924443742, 1924773438, 1934917290, 1941286708, 1943317364, 1972151670, 1972908839, 1982640164, 1983461061, - 1990107683, 2000096287, 2001634458, 2001826027, 2006459190, 2008401563, - 2010716309, 2019887833, 2024794274, 2034765641, 2065694722, 2081423362, + 1990062797, 1999273799, 2001578182, 2001814704, 2005925890, 2008084807, + 2010452700, 2018908874, 2024794274, 2034765641, 2065694722, 2081423362, 2089811970, 52488851, 55077603, 59825747, 68157441, 878182402, 901775362, 1037879561, 1680140893, 1680165436, 1680165613, 1680185931, 1680230940, 1680311085, 1680345685, 1680411449, 1680446153, 1681733672, @@ -697,9 +698,9 @@ static int32_t const ATTRIBUTE_HASHES_DATA[] = { 1922677495, 1923088386, 1924517489, 1924629705, 1932959284, 1933369607, 1934970504, 1939976792, 1941435445, 1941550652, 1965512429, 1966442279, 1972656710, 1972904518, 1972922984, 1975062341, 1983266615, 1983416119, - 1987422362, 1989522022, 1991220282, 1993343287, 2000160071, 2001527900, - 2001669449, 2001732764, 2001898809, 2005342360, 2006824246, 2007064819, - 2009041198, 2009231684, 2016711994, 2017010843, 2023342821, 2024763702, + 1987410233, 1988788535, 1991021879, 1991643278, 2000125224, 2001210183, + 2001634459, 2001710299, 2001898808, 2004957380, 2006516551, 2007064812, + 2008408414, 2009141482, 2015950026, 2016910397, 2023146024, 2024763702, 2026741958, 2026975253, 2060302634, 2065170434, 2066743298, 2075005220, 2081947650, 2083520514, 2091784484, 50917059, 52489043, 53537523, 56685811, 57210387, 59830867, 60817409, 71303169, 72351745, @@ -735,12 +736,12 @@ static int32_t const ATTRIBUTE_HASHES_DATA[] = { 1941438085, 1941454586, 1942026440, 1965349396, 1965561677, 1966439670, 1966454567, 1972196486, 1972744939, 1972863609, 1972904522, 1972909592, 1972962123, 1974849131, 1982254612, 1983157559, 1983290011, 1983398182, - 1983432389, 1987410233, 1988132214, 1988788535, 1990062797, 1991021879, - 1991392548, 1991643278, 1999273799, 2000125224, 2000162011, 2001210183, - 2001578182, 2001634459, 2001669450, 2001710299, 2001814704, 2001898808, - 2004199576, 2004957380, 2005925890, 2006516551, 2007019632, 2007064812, - 2008084807, 2008408414, 2009071951, 2009141482, 2010452700, 2015950026, - 2016787611, 2016910397, 2018908874, 2023146024, 2024616088 + 1983432389, 1984430082, 1987422362, 1988784439, 1989522022, 1990107683, + 1991220282, 1991625270, 1993343287, 2000096287, 2000160071, 2000752725, + 2001527900, 2001634458, 2001669449, 2001710298, 2001732764, 2001826027, + 2001898809, 2004846654, 2005342360, 2006459190, 2006824246, 2007021895, + 2007064819, 2008401563, 2009041198, 2009079867, 2009231684, 2010716309, + 2016711994, 2016810187, 2017010843, 2019887833, 2023342821, 2024647008 }; staticJArray nsHtml5AttributeName::ATTRIBUTE_HASHES = { ATTRIBUTE_HASHES_DATA, MOZ_ARRAY_LENGTH(ATTRIBUTE_HASHES_DATA) }; void @@ -1692,6 +1693,8 @@ nsHtml5AttributeName::initializeStatics() ALL_NO_NS, SAME_LOCAL(nsGkAtoms::close), ALL_NO_PREFIX); ATTR_CLASS = new nsHtml5AttributeName( ALL_NO_NS, SAME_LOCAL(nsGkAtoms::_class), ALL_NO_PREFIX); + ATTR_IS = new nsHtml5AttributeName( + ALL_NO_NS, SAME_LOCAL(nsGkAtoms::is), ALL_NO_PREFIX); ATTR_KEYSYSTEM = new nsHtml5AttributeName( ALL_NO_NS, SAME_LOCAL(nsGkAtoms::keysystem), ALL_NO_PREFIX); ATTR_KEYSPLINES = new nsHtml5AttributeName( @@ -1898,21 +1901,21 @@ nsHtml5AttributeName::initializeStatics() ALL_NO_NS, SAME_LOCAL(nsGkAtoms::ry), ALL_NO_PREFIX); ATTR_REFY = new nsHtml5AttributeName( ALL_NO_NS, SVG_DIFFERENT(nsGkAtoms::refy, nsGkAtoms::refY), ALL_NO_PREFIX); - ATTRIBUTE_NAMES = new nsHtml5AttributeName*[491]; + ATTRIBUTE_NAMES = new nsHtml5AttributeName*[492]; ATTRIBUTE_NAMES[0] = ATTR_ONCLICK; ATTRIBUTE_NAMES[1] = ATTR_DATETIME; ATTRIBUTE_NAMES[2] = ATTR_ONDRAGOVER; ATTRIBUTE_NAMES[3] = ATTR_ARIA_SETSIZE; ATTRIBUTE_NAMES[4] = ATTR_LANG; ATTRIBUTE_NAMES[5] = ATTR_FLOOD_OPACITY; - ATTRIBUTE_NAMES[6] = ATTR_PATTERN; + ATTRIBUTE_NAMES[6] = ATTR_PATTERNTRANSFORM; ATTRIBUTE_NAMES[7] = ATTR_ARIA_DESCRIBEDBY; ATTRIBUTE_NAMES[8] = ATTR_SPACING; ATTRIBUTE_NAMES[9] = ATTR_ONBEFORECUT; ATTRIBUTE_NAMES[10] = ATTR_LIMITINGCONEANGLE; ATTRIBUTE_NAMES[11] = ATTR_ONINPUT; ATTRIBUTE_NAMES[12] = ATTR_CLIP_PATH; - ATTRIBUTE_NAMES[13] = ATTR_SRCSET; + ATTRIBUTE_NAMES[13] = ATTR_ROWS; ATTRIBUTE_NAMES[14] = ATTR_VALUETYPE; ATTRIBUTE_NAMES[15] = ATTR_Y; ATTRIBUTE_NAMES[16] = ATTR_ARIA_VALUEMIN; @@ -1927,8 +1930,8 @@ nsHtml5AttributeName::initializeStatics() ATTRIBUTE_NAMES[25] = ATTR_ONFOCUSOUT; ATTRIBUTE_NAMES[26] = ATTR_SLOPE; ATTRIBUTE_NAMES[27] = ATTR_COLS; - ATTRIBUTE_NAMES[28] = ATTR_FONTFAMILY; - ATTRIBUTE_NAMES[29] = ATTR_COLUMNALIGN; + ATTRIBUTE_NAMES[28] = ATTR_FONT_STYLE; + ATTRIBUTE_NAMES[29] = ATTR_COLUMNLINES; ATTRIBUTE_NAMES[30] = ATTR_RX; ATTRIBUTE_NAMES[31] = ATTR_MIN; ATTRIBUTE_NAMES[32] = ATTR_K3; @@ -1955,11 +1958,11 @@ nsHtml5AttributeName::initializeStatics() ATTRIBUTE_NAMES[53] = ATTR_KEYPOINTS; ATTRIBUTE_NAMES[54] = ATTR_CHAR; ATTRIBUTE_NAMES[55] = ATTR_ONDRAGLEAVE; - ATTRIBUTE_NAMES[56] = ATTR_MAXSIZE; - ATTRIBUTE_NAMES[57] = ATTR_DEPTH; - ATTRIBUTE_NAMES[58] = ATTR_LETTER_SPACING; - ATTRIBUTE_NAMES[59] = ATTR_TEXT_ANCHOR; - ATTRIBUTE_NAMES[60] = ATTR_REQUIREDFEATURES; + ATTRIBUTE_NAMES[56] = ATTR_LOWSRC; + ATTRIBUTE_NAMES[57] = ATTR_CONTEXTMENU; + ATTRIBUTE_NAMES[58] = ATTR_KEYTIMES; + ATTRIBUTE_NAMES[59] = ATTR_TEXT_DECORATION; + ATTRIBUTE_NAMES[60] = ATTR_REQUIRED; ATTRIBUTE_NAMES[61] = ATTR_VIEWBOX; ATTRIBUTE_NAMES[62] = ATTR_DY; ATTRIBUTE_NAMES[63] = ATTR_END; @@ -2013,14 +2016,14 @@ nsHtml5AttributeName::initializeStatics() ATTRIBUTE_NAMES[111] = ATTR_ONERROR; ATTRIBUTE_NAMES[112] = ATTR_BIAS; ATTRIBUTE_NAMES[113] = ATTR_CLASS; - ATTRIBUTE_NAMES[114] = ATTR_PRESERVEASPECTRATIO; - ATTRIBUTE_NAMES[115] = ATTR_CONTENTEDITABLE; - ATTRIBUTE_NAMES[116] = ATTR_FONTWEIGHT; - ATTRIBUTE_NAMES[117] = ATTR_FILTERUNITS; - ATTRIBUTE_NAMES[118] = ATTR_ONSTOP; - ATTRIBUTE_NAMES[119] = ATTR_STITCHTILES; - ATTRIBUTE_NAMES[120] = ATTR_WIDTH; - ATTRIBUTE_NAMES[121] = ATTR_INPUTMODE; + ATTRIBUTE_NAMES[114] = ATTR_PRESERVEALPHA; + ATTRIBUTE_NAMES[115] = ATTR_ALTTEXT; + ATTRIBUTE_NAMES[116] = ATTR_FILTER; + ATTRIBUTE_NAMES[117] = ATTR_FONT_SIZE_ADJUST; + ATTRIBUTE_NAMES[118] = ATTR_RT; + ATTRIBUTE_NAMES[119] = ATTR_RESTART; + ATTRIBUTE_NAMES[120] = ATTR_WRITING_MODE; + ATTRIBUTE_NAMES[121] = ATTR_GROUPALIGN; ATTRIBUTE_NAMES[122] = ATTR_REQUIREDEXTENSIONS; ATTRIBUTE_NAMES[123] = ATTR_ELEVATION; ATTRIBUTE_NAMES[124] = ATTR_DX; @@ -2127,23 +2130,23 @@ nsHtml5AttributeName::initializeStatics() ATTRIBUTE_NAMES[225] = ATTR_START; ATTRIBUTE_NAMES[226] = ATTR_CLASSID; ATTRIBUTE_NAMES[227] = ATTR_CLOSURE; - ATTRIBUTE_NAMES[228] = ATTR_KEYSPLINES; - ATTRIBUTE_NAMES[229] = ATTR_OFFSET; - ATTRIBUTE_NAMES[230] = ATTR_ROWSPACING; - ATTRIBUTE_NAMES[231] = ATTR_VERSION; - ATTRIBUTE_NAMES[232] = ATTR_CONTENT; - ATTRIBUTE_NAMES[233] = ATTR_FONT_STRETCH; - ATTRIBUTE_NAMES[234] = ATTR_FONTSTYLE; - ATTRIBUTE_NAMES[235] = ATTR_FONT_VARIANT; - ATTRIBUTE_NAMES[236] = ATTR_FONT_SIZE; - ATTRIBUTE_NAMES[237] = ATTR_MULTIPLE; - ATTRIBUTE_NAMES[238] = ATTR_POSTER; - ATTRIBUTE_NAMES[239] = ATTR_PATTERNCONTENTUNITS; - ATTRIBUTE_NAMES[240] = ATTR_TEXT_RENDERING; - ATTRIBUTE_NAMES[241] = ATTR_TEXT; - ATTRIBUTE_NAMES[242] = ATTR_COLUMNSPAN; - ATTRIBUTE_NAMES[243] = ATTR_COLUMNWIDTH; - ATTRIBUTE_NAMES[244] = ATTR_ONCUT; + ATTRIBUTE_NAMES[228] = ATTR_KEYSYSTEM; + ATTRIBUTE_NAMES[229] = ATTR_MINSIZE; + ATTRIBUTE_NAMES[230] = ATTR_ROWSPAN; + ATTRIBUTE_NAMES[231] = ATTR_SUBSCRIPTSHIFT; + ATTRIBUTE_NAMES[232] = ATTR_CONTROLS; + ATTRIBUTE_NAMES[233] = ATTR_ENCTYPE; + ATTRIBUTE_NAMES[234] = ATTR_FONT_WEIGHT; + ATTRIBUTE_NAMES[235] = ATTR_FONT_FAMILY; + ATTRIBUTE_NAMES[236] = ATTR_FONTSIZE; + ATTRIBUTE_NAMES[237] = ATTR_LIST; + ATTRIBUTE_NAMES[238] = ATTR_ONSTART; + ATTRIBUTE_NAMES[239] = ATTR_PATTERNUNITS; + ATTRIBUTE_NAMES[240] = ATTR_SYSTEMLANGUAGE; + ATTRIBUTE_NAMES[241] = ATTR_TEXTLENGTH; + ATTRIBUTE_NAMES[242] = ATTR_ACCUMULATE; + ATTRIBUTE_NAMES[243] = ATTR_COLUMNSPACING; + ATTRIBUTE_NAMES[244] = ATTR_ONSUBMIT; ATTRIBUTE_NAMES[245] = ATTR_RESULT; ATTRIBUTE_NAMES[246] = ATTR_VALUES; ATTRIBUTE_NAMES[247] = ATTR_VALUE; @@ -2356,40 +2359,41 @@ nsHtml5AttributeName::initializeStatics() ATTRIBUTE_NAMES[454] = ATTR_CROSSORIGIN; ATTRIBUTE_NAMES[455] = ATTR_CURSOR; ATTRIBUTE_NAMES[456] = ATTR_CLOSE; - ATTRIBUTE_NAMES[457] = ATTR_KEYSYSTEM; - ATTRIBUTE_NAMES[458] = ATTR_LOWSRC; - ATTRIBUTE_NAMES[459] = ATTR_MINSIZE; - ATTRIBUTE_NAMES[460] = ATTR_PRESERVEALPHA; - ATTRIBUTE_NAMES[461] = ATTR_ROWSPAN; - ATTRIBUTE_NAMES[462] = ATTR_ROWS; - ATTRIBUTE_NAMES[463] = ATTR_SUBSCRIPTSHIFT; - ATTRIBUTE_NAMES[464] = ATTR_ALTTEXT; - ATTRIBUTE_NAMES[465] = ATTR_CONTROLS; - ATTRIBUTE_NAMES[466] = ATTR_CONTEXTMENU; - ATTRIBUTE_NAMES[467] = ATTR_ENCTYPE; - ATTRIBUTE_NAMES[468] = ATTR_FILTER; - ATTRIBUTE_NAMES[469] = ATTR_FONT_WEIGHT; - ATTRIBUTE_NAMES[470] = ATTR_FONT_STYLE; - ATTRIBUTE_NAMES[471] = ATTR_FONT_FAMILY; - ATTRIBUTE_NAMES[472] = ATTR_FONT_SIZE_ADJUST; - ATTRIBUTE_NAMES[473] = ATTR_FONTSIZE; - ATTRIBUTE_NAMES[474] = ATTR_KEYTIMES; - ATTRIBUTE_NAMES[475] = ATTR_LIST; - ATTRIBUTE_NAMES[476] = ATTR_RT; - ATTRIBUTE_NAMES[477] = ATTR_ONSTART; - ATTRIBUTE_NAMES[478] = ATTR_PATTERNTRANSFORM; - ATTRIBUTE_NAMES[479] = ATTR_PATTERNUNITS; - ATTRIBUTE_NAMES[480] = ATTR_RESTART; - ATTRIBUTE_NAMES[481] = ATTR_SYSTEMLANGUAGE; - ATTRIBUTE_NAMES[482] = ATTR_TEXT_DECORATION; - ATTRIBUTE_NAMES[483] = ATTR_TEXTLENGTH; - ATTRIBUTE_NAMES[484] = ATTR_WRITING_MODE; - ATTRIBUTE_NAMES[485] = ATTR_ACCUMULATE; - ATTRIBUTE_NAMES[486] = ATTR_COLUMNLINES; - ATTRIBUTE_NAMES[487] = ATTR_COLUMNSPACING; - ATTRIBUTE_NAMES[488] = ATTR_GROUPALIGN; - ATTRIBUTE_NAMES[489] = ATTR_ONSUBMIT; - ATTRIBUTE_NAMES[490] = ATTR_REQUIRED; + ATTRIBUTE_NAMES[457] = ATTR_IS; + ATTRIBUTE_NAMES[458] = ATTR_KEYSPLINES; + ATTRIBUTE_NAMES[459] = ATTR_MAXSIZE; + ATTRIBUTE_NAMES[460] = ATTR_OFFSET; + ATTRIBUTE_NAMES[461] = ATTR_PRESERVEASPECTRATIO; + ATTRIBUTE_NAMES[462] = ATTR_ROWSPACING; + ATTRIBUTE_NAMES[463] = ATTR_SRCSET; + ATTRIBUTE_NAMES[464] = ATTR_VERSION; + ATTRIBUTE_NAMES[465] = ATTR_CONTENTEDITABLE; + ATTRIBUTE_NAMES[466] = ATTR_CONTENT; + ATTRIBUTE_NAMES[467] = ATTR_DEPTH; + ATTRIBUTE_NAMES[468] = ATTR_FONT_STRETCH; + ATTRIBUTE_NAMES[469] = ATTR_FONTWEIGHT; + ATTRIBUTE_NAMES[470] = ATTR_FONTSTYLE; + ATTRIBUTE_NAMES[471] = ATTR_FONTFAMILY; + ATTRIBUTE_NAMES[472] = ATTR_FONT_VARIANT; + ATTRIBUTE_NAMES[473] = ATTR_FILTERUNITS; + ATTRIBUTE_NAMES[474] = ATTR_FONT_SIZE; + ATTRIBUTE_NAMES[475] = ATTR_LETTER_SPACING; + ATTRIBUTE_NAMES[476] = ATTR_MULTIPLE; + ATTRIBUTE_NAMES[477] = ATTR_ONSTOP; + ATTRIBUTE_NAMES[478] = ATTR_POSTER; + ATTRIBUTE_NAMES[479] = ATTR_PATTERN; + ATTRIBUTE_NAMES[480] = ATTR_PATTERNCONTENTUNITS; + ATTRIBUTE_NAMES[481] = ATTR_STITCHTILES; + ATTRIBUTE_NAMES[482] = ATTR_TEXT_RENDERING; + ATTRIBUTE_NAMES[483] = ATTR_TEXT_ANCHOR; + ATTRIBUTE_NAMES[484] = ATTR_TEXT; + ATTRIBUTE_NAMES[485] = ATTR_WIDTH; + ATTRIBUTE_NAMES[486] = ATTR_COLUMNSPAN; + ATTRIBUTE_NAMES[487] = ATTR_COLUMNALIGN; + ATTRIBUTE_NAMES[488] = ATTR_COLUMNWIDTH; + ATTRIBUTE_NAMES[489] = ATTR_INPUTMODE; + ATTRIBUTE_NAMES[490] = ATTR_ONCUT; + ATTRIBUTE_NAMES[491] = ATTR_REQUIREDFEATURES; } void @@ -2807,6 +2811,7 @@ nsHtml5AttributeName::releaseStatics() delete ATTR_CLOSURE; delete ATTR_CLOSE; delete ATTR_CLASS; + delete ATTR_IS; delete ATTR_KEYSYSTEM; delete ATTR_KEYSPLINES; delete ATTR_LOWSRC; diff --git a/parser/html/nsHtml5AttributeName.h b/parser/html/nsHtml5AttributeName.h index 92742a2d6fa5..50f9c413f998 100644 --- a/parser/html/nsHtml5AttributeName.h +++ b/parser/html/nsHtml5AttributeName.h @@ -581,6 +581,7 @@ class nsHtml5AttributeName static nsHtml5AttributeName* ATTR_CLOSURE; static nsHtml5AttributeName* ATTR_CLOSE; static nsHtml5AttributeName* ATTR_CLASS; + static nsHtml5AttributeName* ATTR_IS; static nsHtml5AttributeName* ATTR_KEYSYSTEM; static nsHtml5AttributeName* ATTR_KEYSPLINES; static nsHtml5AttributeName* ATTR_LOWSRC; From d70b8d543c81f93fa1396019780d89f6065e1044 Mon Sep 17 00:00:00 2001 From: Jessica Jong Date: Thu, 26 Oct 2017 14:08:35 +0800 Subject: [PATCH 025/105] Bug 1378079 - Part 2: Introduce throw-on-dynamic-markup-insertion counter. r=hsivonen Per spec, document objects have a throw-on-dynamic-markup-insertion counter, which is used in conjunction with the create an element for the token algorithm to prevent custom element constructors from being able to use document.open(), document.close(), and document.write() when they are invoked by the parser. --HG-- extra : rebase_source : 7cbc10c77765b84bbaf07a05f205f02169a79ee0 --- dom/base/nsDocument.cpp | 3 ++- dom/base/nsIDocument.h | 38 +++++++++++++++++++++++++++++++++++++ dom/html/nsHTMLDocument.cpp | 15 +++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index afb75ea9e959..b55a761ae2b4 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -1596,7 +1596,8 @@ nsIDocument::nsIDocument() mNotifiedPageForUseCounter(0), mIncCounters(), mUserHasInteracted(false), - mServoRestyleRootDirtyBits(0) + mServoRestyleRootDirtyBits(0), + mThrowOnDynamicMarkupInsertionCounter(0) { SetIsInDocument(); for (auto& cnt : mIncCounters) { diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index bfa0b0510118..9c6fc2d831cc 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -3195,6 +3195,22 @@ public: inline void SetServoRestyleRoot(nsINode* aRoot, uint32_t aDirtyBits); + bool ShouldThrowOnDynamicMarkupInsertion() + { + return mThrowOnDynamicMarkupInsertionCounter; + } + + void IncrementThrowOnDynamicMarkupInsertionCounter() + { + ++mThrowOnDynamicMarkupInsertionCounter; + } + + void DecrementThrowOnDynamicMarkupInsertionCounter() + { + MOZ_ASSERT(mThrowOnDynamicMarkupInsertionCounter); + --mThrowOnDynamicMarkupInsertionCounter; + } + protected: bool GetUseCounter(mozilla::UseCounter aUseCounter) { @@ -3736,6 +3752,11 @@ protected: // root corresponds to. nsCOMPtr mServoRestyleRoot; uint32_t mServoRestyleRootDirtyBits; + + // Used in conjunction with the create-an-element-for-the-token algorithm to + // prevent custom element constructors from being able to use document.open(), + // document.close(), and document.write() when they are invoked by the parser. + uint32_t mThrowOnDynamicMarkupInsertionCounter; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID) @@ -3792,6 +3813,23 @@ private: uint32_t mMicroTaskLevel; }; +class MOZ_RAII AutoSetThrowOnDynamicMarkupInsertionCounter final { + public: + explicit AutoSetThrowOnDynamicMarkupInsertionCounter( + nsIDocument* aDocument) + : mDocument(aDocument) + { + mDocument->IncrementThrowOnDynamicMarkupInsertionCounter(); + } + + ~AutoSetThrowOnDynamicMarkupInsertionCounter() { + mDocument->DecrementThrowOnDynamicMarkupInsertionCounter(); + } + + private: + nsIDocument* mDocument; +}; + // XXX These belong somewhere else nsresult NS_NewHTMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData = false); diff --git a/dom/html/nsHTMLDocument.cpp b/dom/html/nsHTMLDocument.cpp index ff19b7d3b0ff..8b09fd24c668 100644 --- a/dom/html/nsHTMLDocument.cpp +++ b/dom/html/nsHTMLDocument.cpp @@ -1502,6 +1502,11 @@ nsHTMLDocument::Open(JSContext* cx, return nullptr; } + if (ShouldThrowOnDynamicMarkupInsertion()) { + aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return nullptr; + } + nsAutoCString contentType; contentType.AssignLiteral("text/html"); @@ -1841,6 +1846,11 @@ nsHTMLDocument::Close(ErrorResult& rv) return; } + if (ShouldThrowOnDynamicMarkupInsertion()) { + rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + if (!mParser || !mParser->IsScriptCreated()) { return; } @@ -1925,6 +1935,11 @@ nsHTMLDocument::WriteCommon(JSContext *cx, return NS_ERROR_DOM_INVALID_STATE_ERR; } + + if (ShouldThrowOnDynamicMarkupInsertion()) { + return NS_ERROR_DOM_INVALID_STATE_ERR; + } + if (mParserAborted) { // Hixie says aborting the parser doesn't undefine the insertion point. // However, since we null out mParser in that case, we track the From f783bf3a9545ab91c44c73decbdd4747c783368d Mon Sep 17 00:00:00 2001 From: Jessica Jong Date: Thu, 26 Oct 2017 14:55:41 +0800 Subject: [PATCH 026/105] Bug 1378079 - Part 3: Complete the steps related to custom elements in "create an element for a token". r=hsivonen,smaug --HG-- extra : rebase_source : c393051fc6f025ff4c013b5349b53c8a5ba2c3c4 --- dom/base/CustomElementRegistry.cpp | 6 +- dom/base/nsContentCreatorFunctions.h | 4 +- dom/html/nsHTMLContentSink.cpp | 25 +- .../mochitest/webcomponents/mochitest.ini | 3 +- ...ent_throw_on_dynamic_markup_insertion.html | 66 +++++ .../test_document_register_base_queue.html | 48 --- .../test_document_register_parser.html | 4 +- parser/html/nsHtml5TreeOperation.cpp | 280 ++++++++++++------ parser/html/nsHtml5TreeOperation.h | 4 + .../microtasks-and-constructors.html.ini | 3 - ...ucts-custom-element-synchronously.html.ini | 5 - ...rser-sets-attributes-and-children.html.ini | 5 - .../parser-uses-constructed-element.html.ini | 8 - .../reactions/Document.html.ini | 12 - .../reactions/Element.html.ini | 19 -- .../reactions/HTMLAnchorElement.html.ini | 5 - .../reactions/HTMLOutputElement.html.ini | 3 - .../reactions/HTMLTableElement.html.ini | 32 -- .../reactions/HTMLTableRowElement.html.ini | 5 - .../HTMLTableSectionElement.html.ini | 8 - .../custom-elements/reactions/Range.html.ini | 5 - 21 files changed, 285 insertions(+), 265 deletions(-) create mode 100644 dom/tests/mochitest/webcomponents/test_custom_element_throw_on_dynamic_markup_insertion.html delete mode 100644 dom/tests/mochitest/webcomponents/test_document_register_base_queue.html delete mode 100644 testing/web-platform/meta/custom-elements/parser/parser-constructs-custom-element-synchronously.html.ini delete mode 100644 testing/web-platform/meta/custom-elements/parser/parser-sets-attributes-and-children.html.ini delete mode 100644 testing/web-platform/meta/custom-elements/parser/parser-uses-constructed-element.html.ini delete mode 100644 testing/web-platform/meta/custom-elements/reactions/HTMLAnchorElement.html.ini delete mode 100644 testing/web-platform/meta/custom-elements/reactions/HTMLTableElement.html.ini delete mode 100644 testing/web-platform/meta/custom-elements/reactions/HTMLTableRowElement.html.ini delete mode 100644 testing/web-platform/meta/custom-elements/reactions/HTMLTableSectionElement.html.ini delete mode 100644 testing/web-platform/meta/custom-elements/reactions/Range.html.ini diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp index ddac1589cd41..35a000c0af58 100644 --- a/dom/base/CustomElementRegistry.cpp +++ b/dom/base/CustomElementRegistry.cpp @@ -1051,8 +1051,12 @@ CustomElementReactionsStack::PopAndInvokeElementQueue() // element, see https://github.com/w3c/webcomponents/issues/635. // We usually report the error to entry global in gecko, so just follow the // same behavior here. + // This may be null if it's called from parser, see the case of + // attributeChangedCallback in + // https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token + // In that case, the exception of callback reactions will be automatically + // reported in CallSetup. nsIGlobalObject* global = GetEntryGlobal(); - MOZ_ASSERT(global, "Should always have a entry global here!"); InvokeReactions(elementQueue, global); } diff --git a/dom/base/nsContentCreatorFunctions.h b/dom/base/nsContentCreatorFunctions.h index 9576d9ba8c30..a5c210500b01 100644 --- a/dom/base/nsContentCreatorFunctions.h +++ b/dom/base/nsContentCreatorFunctions.h @@ -24,6 +24,7 @@ namespace mozilla { namespace dom { class Element; class NodeInfo; +struct CustomElementDefinition; } // namespace dom } // namespace mozilla @@ -41,7 +42,8 @@ nsresult NS_NewHTMLElement(mozilla::dom::Element** aResult, already_AddRefed&& aNodeInfo, mozilla::dom::FromParser aFromParser, - const nsAString* aIs = nullptr); + const nsAString* aIs = nullptr, + mozilla::dom::CustomElementDefinition* aDefinition = nullptr); // First argument should be nsHTMLTag, but that adds dependency to parser // for a bunch of files. diff --git a/dom/html/nsHTMLContentSink.cpp b/dom/html/nsHTMLContentSink.cpp index 7b4f3b6972b3..16087d6e7975 100644 --- a/dom/html/nsHTMLContentSink.cpp +++ b/dom/html/nsHTMLContentSink.cpp @@ -251,7 +251,8 @@ DoCustomElementCreate(Element** aElement, nsIDocument* aDoc, nsAtom* aLocalName, nsresult NS_NewHTMLElement(Element** aResult, already_AddRefed&& aNodeInfo, - FromParser aFromParser, const nsAString* aIs) + FromParser aFromParser, const nsAString* aIs, + mozilla::dom::CustomElementDefinition* aDefinition) { *aResult = nullptr; @@ -268,8 +269,8 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed&& // We only handle the "synchronous custom elements flag is set" now. // For the unset case (e.g. cloning a node), see bug 1319342 for that. // Step 4. - CustomElementDefinition* definition = nullptr; - if (CustomElementRegistry::IsCustomElementEnabled()) { + CustomElementDefinition* definition = aDefinition; + if (!definition && CustomElementRegistry::IsCustomElementEnabled()) { definition = nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(), nodeInfo->LocalName(), @@ -294,9 +295,20 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed&& bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT || aFromParser == dom::NOT_FROM_PARSER; // Per discussion in https://github.com/w3c/webcomponents/issues/635, - // use entry global in those places that are called from JS APIs. - nsIGlobalObject* global = GetEntryGlobal(); - MOZ_ASSERT(global); + // use entry global in those places that are called from JS APIs and use the + // node document's global object if it is called from parser. + nsIGlobalObject* global; + if (aFromParser == dom::NOT_FROM_PARSER) { + global = GetEntryGlobal(); + } else { + global = nodeInfo->GetDocument()->GetScopeObject(); + } + if (!global) { + // In browser chrome code, one may have access to a document which doesn't + // have scope object anymore. + return NS_ERROR_FAILURE; + } + AutoEntryScript aes(global, "create custom elements"); JSContext* cx = aes.cx(); ErrorResult rv; @@ -336,6 +348,7 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed&& // Step 6.2. NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser)); + (*aResult)->SetCustomElementData(new CustomElementData(definition->mType)); nsContentUtils::EnqueueUpgradeReaction(*aResult, definition); return NS_OK; } diff --git a/dom/tests/mochitest/webcomponents/mochitest.ini b/dom/tests/mochitest/webcomponents/mochitest.ini index d8736ce77379..43570c3a13fa 100644 --- a/dom/tests/mochitest/webcomponents/mochitest.ini +++ b/dom/tests/mochitest/webcomponents/mochitest.ini @@ -10,6 +10,7 @@ support-files = [test_content_element.html] [test_custom_element_adopt_callbacks.html] [test_custom_element_callback_innerhtml.html] +skip-if = true # disabled - See bug 1390396 [test_custom_element_clone_callbacks_extended.html] [test_custom_element_htmlconstructor.html] skip-if = os == 'android' # bug 1323645 @@ -20,6 +21,7 @@ support-files = [test_custom_element_in_shadow.html] skip-if = true # disabled - See bug 1390396 [test_custom_element_register_invalid_callbacks.html] +[test_custom_element_throw_on_dynamic_markup_insertion.html] [test_custom_element_get.html] [test_custom_element_when_defined.html] [test_custom_element_upgrade.html] @@ -34,7 +36,6 @@ support-files = [test_document_adoptnode.html] [test_document_importnode.html] [test_document_register.html] -[test_document_register_base_queue.html] [test_document_register_lifecycle.html] skip-if = true # disabled - See bug 1390396 [test_document_register_parser.html] diff --git a/dom/tests/mochitest/webcomponents/test_custom_element_throw_on_dynamic_markup_insertion.html b/dom/tests/mochitest/webcomponents/test_custom_element_throw_on_dynamic_markup_insertion.html new file mode 100644 index 000000000000..b5ef66860501 --- /dev/null +++ b/dom/tests/mochitest/webcomponents/test_custom_element_throw_on_dynamic_markup_insertion.html @@ -0,0 +1,66 @@ + + + + + Test throw on dynamic markup insertion when creating element synchronously from parser + + + + +Bug 1378079 +
+ + + + + diff --git a/dom/tests/mochitest/webcomponents/test_document_register_base_queue.html b/dom/tests/mochitest/webcomponents/test_document_register_base_queue.html deleted file mode 100644 index c839857b45ba..000000000000 --- a/dom/tests/mochitest/webcomponents/test_document_register_base_queue.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - Test for document.registerElement lifecycle callback - - - - - -Bug 783129 - - - - - diff --git a/dom/tests/mochitest/webcomponents/test_document_register_parser.html b/dom/tests/mochitest/webcomponents/test_document_register_parser.html index bc4cdcbacb83..a4fb63139f78 100644 --- a/dom/tests/mochitest/webcomponents/test_document_register_parser.html +++ b/dom/tests/mochitest/webcomponents/test_document_register_parser.html @@ -11,7 +11,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=783129 var extendedButtonProto = Object.create(HTMLButtonElement.prototype); var buttonCallbackCalled = false; -extendedButtonProto.createdCallback = function() { +extendedButtonProto.connectedCallback = function() { is(buttonCallbackCalled, false, "created callback for x-button should only be called once."); is(this.tagName, "BUTTON", "Only the