From 21b1a66af6bcd3eb4a2246bcd564dc578ffd8288 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 20 Jun 2018 17:56:19 +0200 Subject: [PATCH 01/43] Bug 1447591: Stub out a few Debugger APIs for wasm; r=yury --HG-- extra : rebase_source : 22a489d5a3d917f18087fba5342207fcb8d59f02 extra : histedit_source : a23c5f7d75cf8b8ffd6d01a3d00536f75d1591d8 --- devtools/server/actors/source.js | 4 +- .../tests/unit/test_frameactor_wasm-01.js | 5 +- js/src/builtin/TestingFunctions.cpp | 38 +++- js/src/jit-test/lib/wasm.js | 27 +-- js/src/jit-test/tests/debug/wasm-05.js | 115 ---------- js/src/jit-test/tests/debug/wasm-07.js | 28 +-- .../tests/debug/wasm-binary-sources.js | 6 +- .../jit-test/tests/debug/wasm-breakpoint.js | 105 +++++---- .../tests/debug/wasm-getAllColumnOffsets.js | 7 +- js/src/wasm/WasmBinaryToText.cpp | 57 +---- js/src/wasm/WasmBinaryToText.h | 3 +- js/src/wasm/WasmDebug.cpp | 207 ++---------------- js/src/wasm/WasmDebug.h | 38 +--- js/src/wasm/WasmTextToBinary.cpp | 19 +- js/src/wasm/WasmTextToBinary.h | 3 +- 15 files changed, 173 insertions(+), 489 deletions(-) delete mode 100644 js/src/jit-test/tests/debug/wasm-05.js diff --git a/devtools/server/actors/source.js b/devtools/server/actors/source.js index 677f017babc8..511701e4024d 100644 --- a/devtools/server/actors/source.js +++ b/devtools/server/actors/source.js @@ -792,8 +792,10 @@ const SourceActor = ActorClassWithSpec(sourceSpec, { if (!this.isSourceMapped) { const generatedLocation = GeneratedLocation.fromOriginalLocation(originalLocation); + const isWasm = this.source && this.source.introductionType === "wasm"; if (!this._setBreakpointAtGeneratedLocation(actor, generatedLocation) && - !noSliding) { + !noSliding && + !isWasm) { const query = { line: originalLine }; // For most cases, we have a real source to query for. The // only time we don't is for HTML pages. In that case we want diff --git a/devtools/server/tests/unit/test_frameactor_wasm-01.js b/devtools/server/tests/unit/test_frameactor_wasm-01.js index 883170eb26ae..4f0f43566a25 100644 --- a/devtools/server/tests/unit/test_frameactor_wasm-01.js +++ b/devtools/server/tests/unit/test_frameactor_wasm-01.js @@ -26,7 +26,10 @@ function run_test() { gClient, "test-stack", function(response, tabClient, threadClient) { gThreadClient = threadClient; - gThreadClient.reconfigure({ observeAsmJS: true }, function(response) { + gThreadClient.reconfigure({ + observeAsmJS: true, + wasmBinarySource: true + }, function(response) { Assert.equal(!!response.error, false); test_pause_frame(); }); diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index ffb3179696c9..c25346690241 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -659,28 +659,56 @@ WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp) if (!twoByteChars.initTwoByte(cx, args[0].toString())) return false; + bool withOffsets = false; if (args.hasDefined(1)) { - if (!args[1].isString()) { - ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a String"); + if (!args[1].isBoolean()) { + ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a boolean"); return false; } + withOffsets = ToBoolean(args[1]); } uintptr_t stackLimit = GetNativeStackLimit(cx); wasm::Bytes bytes; UniqueChars error; - if (!wasm::TextToBinary(twoByteChars.twoByteChars(), stackLimit, &bytes, &error)) { + wasm::Uint32Vector offsets; + if (!wasm::TextToBinary(twoByteChars.twoByteChars(), stackLimit, &bytes, &offsets, &error)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL, error.get() ? error.get() : "out of memory"); return false; } - RootedObject obj(cx, JS_NewUint8Array(cx, bytes.length())); + RootedObject binary(cx, JS_NewUint8Array(cx, bytes.length())); + if (!binary) + return false; + + memcpy(binary->as().viewDataUnshared(), bytes.begin(), bytes.length()); + + if (!withOffsets) { + args.rval().setObject(*binary); + return true; + } + + RootedObject obj(cx, JS_NewPlainObject(cx)); if (!obj) return false; - memcpy(obj->as().viewDataUnshared(), bytes.begin(), bytes.length()); + constexpr unsigned propAttrs = JSPROP_ENUMERATE; + if (!JS_DefineProperty(cx, obj, "binary", binary, propAttrs)) + return false; + + RootedObject jsOffsets(cx, JS_NewArrayObject(cx, offsets.length())); + if (!jsOffsets) + return false; + for (size_t i = 0; i < offsets.length(); i++) { + uint32_t offset = offsets[i]; + RootedValue offsetVal(cx, NumberValue(offset)); + if (!JS_SetElement(cx, jsOffsets, i, offsetVal)) + return false; + } + if (!JS_DefineProperty(cx, obj, "offsets", jsOffsets, propAttrs)) + return false; args.rval().setObject(*obj); return true; diff --git a/js/src/jit-test/lib/wasm.js b/js/src/jit-test/lib/wasm.js index 537bf5a2b245..92ca6f18bea8 100644 --- a/js/src/jit-test/lib/wasm.js +++ b/js/src/jit-test/lib/wasm.js @@ -116,14 +116,6 @@ function wasmFullPass(text, expected, maybeImports, ...args) { let instance = new WebAssembly.Instance(module, maybeImports); assertEq(typeof instance.exports.run, 'function', "A 'run' function must be exported."); assertEq(instance.exports.run(...args), expected, "Initial module must return the expected result."); - - let retext = wasmBinaryToText(binary); - let rebinary = wasmTextToBinary(retext); - - assertEq(WebAssembly.validate(rebinary), true, "Recreated binary must validate."); - let remodule = new WebAssembly.Module(rebinary); - let reinstance = new WebAssembly.Instance(remodule, maybeImports); - assertEq(reinstance.exports.run(...args), expected, "Reformed module must return the expected result"); } // Ditto, but expects a function named '$run' instead of exported with this name. @@ -134,15 +126,15 @@ function wasmFullPassI64(text, expected, maybeImports, ...args) { let augmentedSrc = _augmentSrc(text, [ { type: 'i64', func: '$run', args, expected } ]); let augmentedBinary = wasmTextToBinary(augmentedSrc); new WebAssembly.Instance(new WebAssembly.Module(augmentedBinary), maybeImports).exports.assert_0(); - - let retext = wasmBinaryToText(augmentedBinary); - new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(retext)), maybeImports).exports.assert_0(); } function wasmRunWithDebugger(wast, lib, init, done) { let g = newGlobal(''); let dbg = new Debugger(g); + // Enable binary source mode. + dbg.allowWasmBinarySource = true; + g.eval(` var wasm = wasmTextToBinary('${wast}'); var lib = ${lib || 'undefined'}; @@ -160,19 +152,6 @@ var m = new WebAssembly.Instance(new WebAssembly.Module(wasm), lib);`); done({dbg, result, error, wasmScript, g,}); } -function wasmGetScriptBreakpoints(wasmScript) { - var result = []; - var sourceText = wasmScript.source.text; - sourceText.split('\n').forEach(function (line, i) { - var lineOffsets = wasmScript.getLineOffsets(i + 1); - if (lineOffsets.length === 0) - return; - assertEq(lineOffsets.length, 1); - result.push({str: line.trim(), line: i + 1, offset: lineOffsets[0]}); - }); - return result; -} - const WasmHelpers = {}; (function() { diff --git a/js/src/jit-test/tests/debug/wasm-05.js b/js/src/jit-test/tests/debug/wasm-05.js deleted file mode 100644 index 9332baa275e3..000000000000 --- a/js/src/jit-test/tests/debug/wasm-05.js +++ /dev/null @@ -1,115 +0,0 @@ -// |jit-test| test-also-no-wasm-baseline -// Tests that wasm module scripts have text line to bytecode offset information -// when source text is generated. - -load(libdir + "asserts.js"); - -if (!wasmDebuggingIsSupported()) - quit(); - -// Checking if experimental format generates internal source map to binary file -// by querying debugger scripts getLineOffsets. -// (Notice that the source map will not be produced by wasmBinaryToText) -function getAllOffsets(wast) { - var sandbox = newGlobal(''); - var dbg = new Debugger(); - dbg.addDebuggee(sandbox); - sandbox.eval(` - var wasm = wasmTextToBinary('${wast}'); - var m = new WebAssembly.Instance(new WebAssembly.Module(wasm)); - `); - var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0]; - var lines = wasmScript.source.text.split('\n'); - return lines.map((l, n) => { return { str: l, offsets: wasmScript.getLineOffsets(n + 1) }; }); -} - -var result1 = getAllOffsets('(module \ - (func (nop)) \ - (func (drop (f32.sqrt (f32.add (f32.const 1.0) (f32.const 2.0))))) \ -)'); - -var nopLine = result1.filter(i => i.str.indexOf('nop') >= 0); -assertEq(nopLine.length, 1); -// The nopLine shall have single offset. -assertEq(nopLine[0].offsets.length, 1); -assertEq(nopLine[0].offsets[0] > 0, true); - -var singleOffsetLines = result1.filter(i => i.offsets.length === 1); -// There shall be total 8 lines with single offset. -assertEq(singleOffsetLines.length, 8); - -// Checking if all reported offsets can be resolved back to the corresponding -// line number. -function checkOffsetLineNumberMapping(wast, offsetsExpected) { - var sandbox = newGlobal(''); - var dbg = new Debugger(); - dbg.addDebuggee(sandbox); - sandbox.eval(` -var wasm = wasmTextToBinary('${wast}'); -var module = new WebAssembly.Module(wasm); -imports = {} -for (let descriptor of WebAssembly.Module.imports(module)) { - imports[descriptor.module] = {} - switch(descriptor.kind) { - case "function": imports[descriptor.module][descriptor.name] = new Function(''); break; - } -} -var instance = new WebAssembly.Instance(module, imports); -`); - var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0]; - assertEq(wasmScript.startLine, 1); - assertEq(wasmScript.lineCount >= 0, true); - var lines = wasmScript.source.text.split('\n'); - var offsetsFound = 0; - lines.forEach(function (l, n) { - var offsets = wasmScript.getLineOffsets(n + 1); - if (offsets.length < 1) return; - assertEq(offsets.length, 1); - assertEq(offsets[0] > 0, true); - offsetsFound++; - var loc = wasmScript.getOffsetLocation(offsets[0]); - assertEq(loc instanceof Object, true); - assertEq(loc.isEntryPoint, true); - assertEq(loc.lineNumber, n + 1); - assertEq(loc.columnNumber > 0, true); - }); - assertEq(offsetsFound, offsetsExpected); -} - -checkOffsetLineNumberMapping('(module (func))', 1); -checkOffsetLineNumberMapping('(module (func (nop)))', 2); -checkOffsetLineNumberMapping('(module (import "a" "b"))', 0); -checkOffsetLineNumberMapping('(module \ - (func (nop)) \ - (func (drop (f32.sqrt (f32.add (f32.const 1.0) (f32.const 2.0))))) \ -)', 8); -checkOffsetLineNumberMapping('(module \ - (func (local i32) i32.const 0 i32.const 1 set_local 0 get_local 0 call 0 i32.add nop drop) \ -)', 9); - -// Checking that there are no offsets are present in a wasm instance script for -// which debug mode was not enabled. -function getWasmScriptAfterDebuggerAttached(wast) { - var sandbox = newGlobal(''); - var dbg = new Debugger(); - sandbox.eval(` - var wasm = wasmTextToBinary('${wast}'); - var m = new WebAssembly.Instance(new WebAssembly.Module(wasm)); - `); - // Attaching after wasm instance is created. - dbg.addDebuggee(sandbox); - var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0]; - return wasmScript; -} - -var wasmScript1 = getWasmScriptAfterDebuggerAttached('(module (func (nop)))'); -var wasmLines1 = wasmScript1.source.text.split('\n'); -assertEq(wasmScript1.startLine, 1); -assertEq(wasmScript1.lineCount, 0); -assertEq(wasmLines1.every((l, n) => wasmScript1.getLineOffsets(n + 1).length == 0), true); - -// Checking that we must not resolve any location for any offset in a wasm -// instance which debug mode was not enabled. -var wasmScript2 = getWasmScriptAfterDebuggerAttached('(module (func (nop)))'); -for (var i = wasmTextToBinary('(module (func (nop)))').length - 1; i >= 0; i--) - assertThrowsInstanceOf(() => wasmScript2.getOffsetLocation(i), Error); diff --git a/js/src/jit-test/tests/debug/wasm-07.js b/js/src/jit-test/tests/debug/wasm-07.js index b045597c8ba0..890b6168390e 100644 --- a/js/src/jit-test/tests/debug/wasm-07.js +++ b/js/src/jit-test/tests/debug/wasm-07.js @@ -15,7 +15,9 @@ wasmRunWithDebugger( function ({dbg}) { offsets = []; dbg.onEnterFrame = function (frame) { - if (frame.type != 'wasmcall') return; + if (frame.type != 'wasmcall') { + return; + } offsets.push(frame.offset); frame.onStep = function () { offsets.push(frame.offset); @@ -24,16 +26,16 @@ wasmRunWithDebugger( offsets.push(frame.offset); }; }; - }, - function ({wasmScript, error}) { - assertEq(error, undefined); - assertEq(offsets.length, 5); - offsets.forEach(offset => { - var loc = wasmScript.getOffsetLocation(offset); - assertEq(loc.isEntryPoint, true); - assertEq(loc.lineNumber > 0, true); - assertEq(loc.columnNumber > 0, true); - assertEq(wasmScript.getLineOffsets(loc.lineNumber).length, 1); - }); - } + }, + function ({wasmScript, error}) { + assertEq(error, undefined); + assertEq(offsets.length, 5); + offsets.forEach(offset => { + var loc = wasmScript.getOffsetLocation(offset); + assertEq(loc.isEntryPoint, true); + assertEq(loc.lineNumber > 0, true); + assertEq(loc.columnNumber > 0, true); + assertEq(wasmScript.getLineOffsets(loc.lineNumber).length, 1); + }); + } ); diff --git a/js/src/jit-test/tests/debug/wasm-binary-sources.js b/js/src/jit-test/tests/debug/wasm-binary-sources.js index d352eef6543a..a0b9545829b7 100644 --- a/js/src/jit-test/tests/debug/wasm-binary-sources.js +++ b/js/src/jit-test/tests/debug/wasm-binary-sources.js @@ -19,8 +19,8 @@ assertEq(s.format, "wasm"); var source = s.source; -// The text is generated if wasm binary sources are disabled. -assertEq(source.text.includes('module'), true); +// The text is never generated with the native Debugger API. +assertEq(source.text.includes('module'), false); assertThrowsInstanceOf(() => source.binary, Error); // Enable binary sources. @@ -31,7 +31,7 @@ assertEq(s.format, "wasm"); var source2 = s.source; -// The text is '[wasm]' if wasm binary sources are enabled. +// The text is predefined if wasm binary sources are enabled. assertEq(source2.text, '[wasm]'); // The binary contains Uint8Array which is equal to wasm bytecode; arraysEqual(source2.binary, wasmTextToBinary('(module (func) (export "" 0))')); diff --git a/js/src/jit-test/tests/debug/wasm-breakpoint.js b/js/src/jit-test/tests/debug/wasm-breakpoint.js index 249342907b7e..039cd5e1c63a 100644 --- a/js/src/jit-test/tests/debug/wasm-breakpoint.js +++ b/js/src/jit-test/tests/debug/wasm-breakpoint.js @@ -6,21 +6,55 @@ load(libdir + "wasm.js"); if (!wasmDebuggingIsSupported()) quit(); -// Checking if we can stop at specified breakpoint. +function runTest(wast, initFunc, doneFunc) { + let g = newGlobal(''); + let dbg = new Debugger(g); + + g.eval(` +var { binary, offsets } = wasmTextToBinary('${wast}', /* offsets */ true); +var m = new WebAssembly.Instance(new WebAssembly.Module(binary));`); + + var { offsets } = g; + + var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0]; + + initFunc({ + dbg, + wasmScript, + g, + breakpoints: offsets + }); + + let result, error; + try { + result = g.eval("m.exports.test()"); + } catch (ex) { + error = ex; + } + + doneFunc({ + dbg, + result, + error, + wasmScript, + g + }); +} + + var onBreakpointCalled; -wasmRunWithDebugger( + +// Checking if we can stop at specified breakpoint. +runTest( '(module (func (nop) (nop)) (export "test" 0))', - undefined, - function ({wasmScript}) { - var breakpoints = wasmGetScriptBreakpoints(wasmScript); + function ({wasmScript, breakpoints}) { assertEq(breakpoints.length, 3); - assertEq(breakpoints[0].offset > 0, true); + assertEq(breakpoints[0] > 0, true); // Checking if breakpoints offsets are in ascending order. - assertEq(breakpoints[0].offset < breakpoints[1].offset, true); - assertEq(breakpoints[1].offset < breakpoints[2].offset, true); + assertEq(breakpoints[0] < breakpoints[1], true); + assertEq(breakpoints[1] < breakpoints[2], true); onBreakpointCalled = 0; - breakpoints.forEach(function (bp) { - var offset = bp.offset; + breakpoints.forEach(function (offset) { wasmScript.setBreakpoint(offset, { hit: (frame) => { assertEq(frame.offset, offset); @@ -36,18 +70,15 @@ wasmRunWithDebugger( ); // Checking if we can remove breakpoint one by one. -wasmRunWithDebugger( +runTest( '(module (func (nop) (nop)) (export "test" 0))', - undefined, - function ({wasmScript}) { - var breakpoints = wasmGetScriptBreakpoints(wasmScript); + function ({wasmScript, breakpoints}) { onBreakpointCalled = 0; var handlers = []; - breakpoints.forEach(function (bp, i) { - var offset = bp.offset; + breakpoints.forEach(function (offset, i) { wasmScript.setBreakpoint(offset, handlers[i] = { hit: (frame) => { - assertEq(frame.offset, breakpoints[0].offset); + assertEq(frame.offset, breakpoints[0]); onBreakpointCalled++; // Removing all handlers. handlers.forEach(h => wasmScript.clearBreakpoint(h)); @@ -62,18 +93,15 @@ wasmRunWithDebugger( ); // Checking if we can remove breakpoint one by one from a breakpoint handler. -wasmRunWithDebugger( +runTest( '(module (func (nop) (nop)) (export "test" 0))', - undefined, - function ({wasmScript}) { - var breakpoints = wasmGetScriptBreakpoints(wasmScript); + function ({wasmScript, breakpoints}) { onBreakpointCalled = 0; var handlers = []; - breakpoints.forEach(function (bp, i) { - var offset = bp.offset; + breakpoints.forEach(function (offset, i) { wasmScript.setBreakpoint(offset, handlers[i] = { hit: (frame) => { - assertEq(frame.offset, breakpoints[0].offset); + assertEq(frame.offset, breakpoints[0]); onBreakpointCalled++; // Removing all handlers. handlers.forEach(h => wasmScript.clearBreakpoint(h)); @@ -90,16 +118,13 @@ wasmRunWithDebugger( // Checking if we can remove breakpoint one by one from onEnterFrame, // but onStep will still work. var onStepCalled; -wasmRunWithDebugger( +runTest( '(module (func (nop) (nop)) (export "test" 0))', - undefined, - function ({dbg, wasmScript}) { - var breakpoints = wasmGetScriptBreakpoints(wasmScript); + function ({dbg, wasmScript, breakpoints}) { onBreakpointCalled = 0; onStepCalled = []; var handlers = []; - breakpoints.forEach(function (bp, i) { - var offset = bp.offset; + breakpoints.forEach(function (offset, i) { wasmScript.setBreakpoint(offset, handlers[i] = { hit: (frame) => { assertEq(false, true); @@ -125,17 +150,14 @@ wasmRunWithDebugger( ); // Checking if we can remove all breakpoints. -wasmRunWithDebugger( +runTest( '(module (func (nop) (nop)) (export "test" 0))', - undefined, - function ({wasmScript}) { - var breakpoints = wasmGetScriptBreakpoints(wasmScript); + function ({wasmScript, breakpoints}) { onBreakpointCalled = 0; - breakpoints.forEach(function (bp, i) { - var offset = bp.offset; + breakpoints.forEach(function (offset, i) { wasmScript.setBreakpoint(offset, { hit: (frame) => { - assertEq(frame.offset, breakpoints[0].offset); + assertEq(frame.offset, breakpoints[0]); onBreakpointCalled++; // Removing all handlers. wasmScript.clearAllBreakpoints(); @@ -150,14 +172,11 @@ wasmRunWithDebugger( ); // Checking if breakpoints are removed after debugger has been detached. -wasmRunWithDebugger( +runTest( '(module (func (nop) (nop)) (export "test" 0))', - undefined, - function ({dbg, wasmScript, g}) { - var breakpoints = wasmGetScriptBreakpoints(wasmScript); + function ({dbg, wasmScript, g, breakpoints}) { onBreakpointCalled = 0; - breakpoints.forEach(function (bp, i) { - var offset = bp.offset; + breakpoints.forEach(function (offset, i) { wasmScript.setBreakpoint(offset, { hit: (frame) => { onBreakpointCalled++; diff --git a/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js b/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js index 70e6ab1f3b92..703f64b96673 100644 --- a/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js +++ b/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js @@ -5,7 +5,7 @@ load(libdir + "asserts.js"); if (!wasmDebuggingIsSupported()) - quit(); + quit(); // Checking if experimental format generates internal source map to binary file // by querying debugger scripts getAllColumnOffsets. @@ -14,6 +14,7 @@ function getAllOffsets(wast) { var sandbox = newGlobal(''); var dbg = new Debugger(); dbg.addDebuggee(sandbox); + dbg.allowWasmBinarySource = true; sandbox.eval(` var wasm = wasmTextToBinary('${wast}'); var m = new WebAssembly.Instance(new WebAssembly.Module(wasm)); @@ -28,8 +29,10 @@ var offsets1 = getAllOffsets('(module \ )'); // There shall be total 8 lines with single and unique offset per line. -var usedOffsets = Object.create(null), usedLines = Object.create(null); +var usedOffsets = Object.create(null), + usedLines = Object.create(null); assertEq(offsets1.length, 8); + offsets1.forEach(({offset, lineNumber, columnNumber}) => { assertEq(offset > 0, true); assertEq(lineNumber > 0, true); diff --git a/js/src/wasm/WasmBinaryToText.cpp b/js/src/wasm/WasmBinaryToText.cpp index c7f5110d7ef0..344cc12a62a7 100644 --- a/js/src/wasm/WasmBinaryToText.cpp +++ b/js/src/wasm/WasmBinaryToText.cpp @@ -40,16 +40,13 @@ struct WasmRenderContext JSContext* cx; AstModule* module; WasmPrintBuffer& buffer; - GeneratedSourceMap* maybeSourceMap; uint32_t indent; uint32_t currentFuncIndex; - WasmRenderContext(JSContext* cx, AstModule* module, WasmPrintBuffer& buffer, - GeneratedSourceMap* sourceMap) + WasmRenderContext(JSContext* cx, AstModule* module, WasmPrintBuffer& buffer) : cx(cx), module(module), buffer(buffer), - maybeSourceMap(sourceMap), indent(0), currentFuncIndex(0) {} @@ -250,14 +247,6 @@ RenderBlockNameAndSignature(WasmRenderContext& c, const AstName& name, ExprType static bool RenderExpr(WasmRenderContext& c, AstExpr& expr, bool newLine = true); -#define MAP_AST_EXPR(c, expr) \ - if (c.maybeSourceMap) { \ - uint32_t lineno = c.buffer.lineno(); \ - uint32_t column = c.buffer.column(); \ - if (!c.maybeSourceMap->exprlocs().emplaceBack(lineno, column, expr.offset())) \ - return false; \ - } - /*****************************************************************************/ // binary format parsing and rendering @@ -266,7 +255,6 @@ RenderNop(WasmRenderContext& c, AstNop& nop) { if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, nop); return c.buffer.append("nop"); } @@ -278,7 +266,6 @@ RenderDrop(WasmRenderContext& c, AstDrop& drop) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, drop); return c.buffer.append("drop"); } @@ -287,7 +274,6 @@ RenderUnreachable(WasmRenderContext& c, AstUnreachable& unreachable) { if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, unreachable); return c.buffer.append("unreachable"); } @@ -311,7 +297,6 @@ RenderCall(WasmRenderContext& c, AstCall& call) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, call); if (call.op() == Op::Call) { if (!c.buffer.append("call ")) return false; @@ -334,7 +319,6 @@ RenderCallIndirect(WasmRenderContext& c, AstCallIndirect& call) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, call); if (!c.buffer.append("call_indirect ")) return false; return RenderRef(c, call.funcType()); @@ -346,7 +330,6 @@ RenderConst(WasmRenderContext& c, AstConst& cst) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, cst); if (!RenderValType(c, cst.val().type())) return false; if (!c.buffer.append(".const ")) @@ -374,7 +357,6 @@ RenderGetLocal(WasmRenderContext& c, AstGetLocal& gl) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, gl); if (!c.buffer.append("get_local ")) return false; return RenderRef(c, gl.local()); @@ -389,7 +371,6 @@ RenderSetLocal(WasmRenderContext& c, AstSetLocal& sl) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, sl); if (!c.buffer.append("set_local ")) return false; return RenderRef(c, sl.local()); @@ -404,7 +385,6 @@ RenderTeeLocal(WasmRenderContext& c, AstTeeLocal& tl) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, tl); if (!c.buffer.append("tee_local ")) return false; return RenderRef(c, tl.local()); @@ -416,7 +396,6 @@ RenderGetGlobal(WasmRenderContext& c, AstGetGlobal& gg) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, gg); if (!c.buffer.append("get_global ")) return false; return RenderRef(c, gg.global()); @@ -431,7 +410,6 @@ RenderSetGlobal(WasmRenderContext& c, AstSetGlobal& sg) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, sg); if (!c.buffer.append("set_global ")) return false; return RenderRef(c, sg.global()); @@ -453,7 +431,6 @@ RenderBlock(WasmRenderContext& c, AstBlock& block, bool isInline = false) if (!isInline && !RenderIndent(c)) return false; - MAP_AST_EXPR(c, block); if (block.op() == Op::Block) { if (!c.buffer.append("block")) return false; @@ -523,7 +500,6 @@ RenderGrowMemory(WasmRenderContext& c, AstGrowMemory& gm) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, gm); return c.buffer.append("grow_memory\n"); } @@ -536,7 +512,6 @@ RenderUnaryOperator(WasmRenderContext& c, AstUnaryOperator& unary) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, unary); const char* opStr; switch (unary.op()) { case Op::I32Eqz: opStr = "i32.eqz"; break; @@ -577,7 +552,6 @@ RenderBinaryOperator(WasmRenderContext& c, AstBinaryOperator& binary) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, binary); const char* opStr; switch (binary.op()) { case Op::I32Add: opStr = "i32.add"; break; @@ -643,7 +617,6 @@ RenderTernaryOperator(WasmRenderContext& c, AstTernaryOperator& ternary) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, ternary); const char* opStr; switch (ternary.op()) { case Op::Select: opStr = "select"; break; @@ -664,7 +637,6 @@ RenderComparisonOperator(WasmRenderContext& c, AstComparisonOperator& comp) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, comp); const char* opStr; switch (comp.op()) { case Op::I32Eq: opStr = "i32.eq"; break; @@ -714,7 +686,6 @@ RenderConversionOperator(WasmRenderContext& c, AstConversionOperator& conv) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, conv); const char* opStr; switch (conv.op()) { case Op::I32WrapI64: opStr = "i32.wrap/i64"; break; @@ -764,7 +735,6 @@ RenderExtraConversionOperator(WasmRenderContext& c, AstExtraConversionOperator& if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, conv); const char* opStr; switch (conv.op()) { case MiscOp::I32TruncSSatF32: opStr = "i32.trunc_s:sat/f32"; break; @@ -790,7 +760,6 @@ RenderIf(WasmRenderContext& c, AstIf& if_) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, if_); if (!c.buffer.append("if")) return false; if (!RenderBlockNameAndSignature(c, if_.name(), if_.type())) @@ -858,7 +827,6 @@ RenderLoad(WasmRenderContext& c, AstLoad& load) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, load); uint32_t defaultAlignLog2; switch (load.op()) { case Op::I32Load8S: @@ -950,7 +918,6 @@ RenderStore(WasmRenderContext& c, AstStore& store) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, store); uint32_t defaultAlignLog2; switch (store.op()) { case Op::I32Store8: @@ -1024,7 +991,6 @@ RenderBranch(WasmRenderContext& c, AstBranch& branch) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, branch); if (op == Op::BrIf ? !c.buffer.append("br_if ") : !c.buffer.append("br ")) return false; @@ -1046,7 +1012,6 @@ RenderBrTable(WasmRenderContext& c, AstBranchTable& table) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, table); if (!c.buffer.append("br_table ")) return false; @@ -1073,7 +1038,6 @@ RenderReturn(WasmRenderContext& c, AstReturn& ret) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, ret); return c.buffer.append("return"); } @@ -1091,7 +1055,6 @@ RenderAtomicCmpXchg(WasmRenderContext& c, AstAtomicCmpXchg& cmpxchg) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, cmpxchg); const char* opname; switch (cmpxchg.op()) { case ThreadOp::I32AtomicCmpXchg8U: opname = "i32.atomic.rmw8_u.cmpxchg"; break; @@ -1119,7 +1082,6 @@ RenderAtomicLoad(WasmRenderContext& c, AstAtomicLoad& load) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, load); const char* opname; switch (load.op()) { case ThreadOp::I32AtomicLoad8U: opname = "i32.atomic.load8_u"; break; @@ -1150,7 +1112,6 @@ RenderAtomicRMW(WasmRenderContext& c, AstAtomicRMW& rmw) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, rmw); const char* opname; switch (rmw.op()) { case ThreadOp::I32AtomicAdd: opname = "i32.atomic.rmw.add"; break; @@ -1216,7 +1177,6 @@ RenderAtomicStore(WasmRenderContext& c, AstAtomicStore& store) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, store); const char* opname; switch (store.op()) { case ThreadOp::I32AtomicStore8U: opname = "i32.atomic.store8_u"; break; @@ -1250,7 +1210,6 @@ RenderWait(WasmRenderContext& c, AstWait& wait) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, wait); const char* opname; switch (wait.op()) { case ThreadOp::I32Wait: opname = "i32.atomic.wait"; break; @@ -1296,7 +1255,6 @@ RenderMemCopy(WasmRenderContext& c, AstMemCopy& mc) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, mc); const char* opStr = "memory.copy"; return c.buffer.append(opStr, strlen(opStr)); @@ -1315,7 +1273,6 @@ RenderMemFill(WasmRenderContext& c, AstMemFill& mf) if (!RenderIndent(c)) return false; - MAP_AST_EXPR(c, mf); const char* opStr = "memory.fill"; return c.buffer.append(opStr, strlen(opStr)); @@ -1940,11 +1897,6 @@ RenderFunctionBody(WasmRenderContext& c, AstFunc& func, const AstModule::TypeDef return false; } - if (c.maybeSourceMap) { - if (!c.maybeSourceMap->exprlocs().emplaceBack(c.buffer.lineno(), c.buffer.column(), func.endOffset())) - return false; - } - return true; } @@ -2110,14 +2062,11 @@ RenderModule(WasmRenderContext& c, AstModule& module) return true; } -#undef MAP_AST_EXPR - /*****************************************************************************/ // Top-level functions bool -wasm::BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer, - GeneratedSourceMap* sourceMap /* = nullptr */) +wasm::BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer) { LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE); @@ -2126,7 +2075,7 @@ wasm::BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuf return false; WasmPrintBuffer buf(buffer); - WasmRenderContext c(cx, module, buf, sourceMap); + WasmRenderContext c(cx, module, buf); if (!RenderModule(c, *module)) { if (!cx->isExceptionPending()) diff --git a/js/src/wasm/WasmBinaryToText.h b/js/src/wasm/WasmBinaryToText.h index 2aad45917e14..b07ce2a29c24 100644 --- a/js/src/wasm/WasmBinaryToText.h +++ b/js/src/wasm/WasmBinaryToText.h @@ -35,8 +35,7 @@ namespace wasm { // representation. MOZ_MUST_USE bool -BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer, - GeneratedSourceMap* sourceMap = nullptr); +BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer); } // namespace wasm diff --git a/js/src/wasm/WasmDebug.cpp b/js/src/wasm/WasmDebug.cpp index 7ce1a7eee015..c65d2db67ef9 100644 --- a/js/src/wasm/WasmDebug.cpp +++ b/js/src/wasm/WasmDebug.cpp @@ -37,56 +37,6 @@ using namespace js::wasm; using mozilla::BinarySearchIf; -bool -GeneratedSourceMap::searchLineByOffset(JSContext* cx, uint32_t offset, size_t* exprlocIndex) -{ - MOZ_ASSERT(!exprlocs_.empty()); - size_t exprlocsLength = exprlocs_.length(); - - // Lazily build sorted array for fast log(n) lookup. - if (!sortedByOffsetExprLocIndices_) { - ExprLocIndexVector scratch; - auto indices = MakeUnique(); - if (!indices || !indices->resize(exprlocsLength) || !scratch.resize(exprlocsLength)) { - ReportOutOfMemory(cx); - return false; - } - sortedByOffsetExprLocIndices_ = std::move(indices); - - for (size_t i = 0; i < exprlocsLength; i++) - (*sortedByOffsetExprLocIndices_)[i] = i; - - auto compareExprLocViaIndex = [&](uint32_t i, uint32_t j, bool* lessOrEqualp) -> bool { - *lessOrEqualp = exprlocs_[i].offset <= exprlocs_[j].offset; - return true; - }; - MOZ_ALWAYS_TRUE(MergeSort(sortedByOffsetExprLocIndices_->begin(), exprlocsLength, - scratch.begin(), compareExprLocViaIndex)); - } - - // Allowing non-exact search and if BinarySearchIf returns out-of-bound - // index, moving the index to the last index. - auto lookupFn = [&](uint32_t i) -> int { - const ExprLoc& loc = exprlocs_[i]; - return offset == loc.offset ? 0 : offset < loc.offset ? -1 : 1; - }; - size_t match; - Unused << BinarySearchIf(sortedByOffsetExprLocIndices_->begin(), 0, exprlocsLength, lookupFn, &match); - if (match >= exprlocsLength) - match = exprlocsLength - 1; - *exprlocIndex = (*sortedByOffsetExprLocIndices_)[match]; - return true; -} - -size_t -GeneratedSourceMap::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const -{ - size_t size = exprlocs_.sizeOfExcludingThis(mallocSizeOf); - if (sortedByOffsetExprLocIndices_) - size += sortedByOffsetExprLocIndices_->sizeOfIncludingThis(mallocSizeOf); - return size; -} - DebugState::DebugState(SharedCode code, const ShareableBytes* maybeBytecode, bool binarySource) @@ -99,17 +49,14 @@ DebugState::DebugState(SharedCode code, } const char enabledMessage[] = - "Restart with developer tools open to view WebAssembly source"; + "Restart with developer tools open to view WebAssembly source."; -const char tooBigMessage[] = - "Unfortunately, this WebAssembly module is too big to view as text.\n" - "We are working hard to remove this limitation."; +const char noBinarySource[] = + "Configure the debugger to display WebAssembly bytecode."; const char notGeneratedMessage[] = "WebAssembly text generation was disabled."; -static const unsigned TooBig = 1000000; - static const uint32_t DefaultBinarySourceColumnNumber = 1; static const CallSite* @@ -129,98 +76,26 @@ DebugState::createText(JSContext* cx) if (!maybeBytecode_) { if (!buffer.append(enabledMessage)) return nullptr; - - MOZ_ASSERT(!maybeSourceMap_); } else if (binarySource_) { if (!buffer.append(notGeneratedMessage)) return nullptr; - return buffer.finishString(); - } else if (maybeBytecode_->bytes.length() > TooBig) { - if (!buffer.append(tooBigMessage)) - return nullptr; - - MOZ_ASSERT(!maybeSourceMap_); } else { - const Bytes& bytes = maybeBytecode_->bytes; - auto sourceMap = MakeUnique(); - if (!sourceMap) { - ReportOutOfMemory(cx); + if (!buffer.append(noBinarySource)) return nullptr; - } - maybeSourceMap_ = std::move(sourceMap); - - if (!BinaryToText(cx, bytes.begin(), bytes.length(), buffer, maybeSourceMap_.get())) - return nullptr; - -#if DEBUG - // Check that expression locations are sorted by line number. - uint32_t lastLineno = 0; - for (const ExprLoc& loc : maybeSourceMap_->exprlocs()) { - MOZ_ASSERT(lastLineno <= loc.lineno); - lastLineno = loc.lineno; - } -#endif } - return buffer.finishString(); } -bool -DebugState::ensureSourceMap(JSContext* cx) -{ - if (maybeSourceMap_ || !maybeBytecode_) - return true; - - // We just need to cache maybeSourceMap_, ignoring the text result. - return createText(cx); -} - -struct LineComparator -{ - const uint32_t lineno; - explicit LineComparator(uint32_t lineno) : lineno(lineno) {} - - int operator()(const ExprLoc& loc) const { - return lineno == loc.lineno ? 0 : lineno < loc.lineno ? -1 : 1; - } -}; - bool DebugState::getLineOffsets(JSContext* cx, size_t lineno, Vector* offsets) { if (!debugEnabled()) return true; - - if (binarySource_) { - const CallSite* callsite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), lineno); - if (callsite && !offsets->append(lineno)) - return false; + if (!binarySource_) return true; - } - - if (!ensureSourceMap(cx)) + const CallSite* callsite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), lineno); + if (callsite && !offsets->append(lineno)) return false; - - if (!maybeSourceMap_) - return true; // no source text available, keep offsets empty. - - ExprLocVector& exprlocs = maybeSourceMap_->exprlocs(); - - // Binary search for the expression with the specified line number and - // rewind to the first expression, if more than one expression on the same line. - size_t match; - if (!BinarySearchIf(exprlocs, 0, exprlocs.length(), LineComparator(lineno), &match)) - return true; - - while (match > 0 && exprlocs[match - 1].lineno == lineno) - match--; - - // Return all expression offsets that were printed on the specified line. - for (size_t i = match; i < exprlocs.length() && exprlocs[i].lineno == lineno; i++) { - if (!offsets->append(exprlocs[i].offset)) - return false; - } - return true; } @@ -229,25 +104,16 @@ DebugState::getAllColumnOffsets(JSContext* cx, Vector* offsets) { if (!metadata().debugEnabled) return true; - - if (binarySource_) { - for (const CallSite& callSite : metadata(Tier::Debug).callSites) { - if (callSite.kind() != CallSite::Breakpoint) - continue; - uint32_t offset = callSite.lineOrBytecode(); - if (!offsets->emplaceBack(offset, DefaultBinarySourceColumnNumber, offset)) - return false; - } + if (!binarySource_) return true; + for (const CallSite& callSite : metadata(Tier::Debug).callSites) { + if (callSite.kind() != CallSite::Breakpoint) + continue; + uint32_t offset = callSite.lineOrBytecode(); + if (!offsets->emplaceBack(offset, DefaultBinarySourceColumnNumber, offset)) + return false; } - - if (!ensureSourceMap(cx)) - return false; - - if (!maybeSourceMap_) - return true; // no source text available, keep offsets empty. - - return offsets->appendAll(maybeSourceMap_->exprlocs()); + return true; } bool @@ -256,30 +122,13 @@ DebugState::getOffsetLocation(JSContext* cx, uint32_t offset, bool* found, size_ *found = false; if (!debugEnabled()) return true; - - if (binarySource_) { - if (!SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset)) - return true; // offset was not found - *found = true; - *lineno = offset; - *column = DefaultBinarySourceColumnNumber; + if (!binarySource_) return true; - } - - if (!ensureSourceMap(cx)) - return false; - - if (!maybeSourceMap_ || maybeSourceMap_->exprlocs().empty()) - return true; // no source text available - - size_t foundAt; - if (!maybeSourceMap_->searchLineByOffset(cx, offset, &foundAt)) - return false; - - const ExprLoc& loc = maybeSourceMap_->exprlocs()[foundAt]; + if (!SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset)) + return true; // offset was not found *found = true; - *lineno = loc.lineno; - *column = loc.column; + *lineno = offset; + *column = DefaultBinarySourceColumnNumber; return true; } @@ -289,18 +138,10 @@ DebugState::totalSourceLines(JSContext* cx, uint32_t* count) *count = 0; if (!debugEnabled()) return true; - - if (binarySource_) { - if (maybeBytecode_) - *count = maybeBytecode_->length(); + if (!binarySource_) return true; - } - - if (!ensureSourceMap(cx)) - return false; - - if (maybeSourceMap_) - *count = maybeSourceMap_->totalLines(); + if (maybeBytecode_) + *count = maybeBytecode_->length(); return true; } @@ -708,8 +549,6 @@ DebugState::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* data) const { code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code, data); - if (maybeSourceMap_) - *data += maybeSourceMap_->sizeOfExcludingThis(mallocSizeOf); if (maybeBytecode_) *data += maybeBytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes); } diff --git a/js/src/wasm/WasmDebug.h b/js/src/wasm/WasmDebug.h index ab901bb1e1a9..d2b458f311b1 100644 --- a/js/src/wasm/WasmDebug.h +++ b/js/src/wasm/WasmDebug.h @@ -34,8 +34,8 @@ namespace wasm { struct MetadataTier; -// The generated source location for the AST node/expression. The offset field refers -// an offset in an binary format file. +// The generated source location for the AST node/expression. The offset field +// refers an offset in an binary format file. struct ExprLoc { @@ -48,39 +48,14 @@ struct ExprLoc {} }; -typedef Vector ExprLocVector; -typedef Vector ExprLocIndexVector; - -// The generated source map for WebAssembly binary file. This map is generated during -// building the text buffer (see BinaryToExperimentalText). - -class GeneratedSourceMap -{ - ExprLocVector exprlocs_; - UniquePtr sortedByOffsetExprLocIndices_; - uint32_t totalLines_; - - public: - explicit GeneratedSourceMap() : totalLines_(0) {} - ExprLocVector& exprlocs() { return exprlocs_; } - - uint32_t totalLines() { return totalLines_; } - void setTotalLines(uint32_t val) { totalLines_ = val; } - - bool searchLineByOffset(JSContext* cx, uint32_t offset, size_t* exprlocIndex); - - size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; -}; - -typedef UniquePtr UniqueGeneratedSourceMap; typedef HashMap, SystemAllocPolicy> StepModeCounters; -typedef HashMap, SystemAllocPolicy> WasmBreakpointSiteMap; +typedef HashMap, SystemAllocPolicy> + WasmBreakpointSiteMap; class DebugState { const SharedCode code_; const SharedBytes maybeBytecode_; - UniqueGeneratedSourceMap maybeSourceMap_; bool binarySource_; // State maintained when debugging is enabled. In this case, the Code is @@ -92,7 +67,6 @@ class DebugState StepModeCounters stepModeCounters_; void toggleDebugTrap(uint32_t offset, bool enabled); - bool ensureSourceMap(JSContext* cx); public: DebugState(SharedCode code, @@ -102,10 +76,6 @@ class DebugState const Bytes* maybeBytecode() const { return maybeBytecode_ ? &maybeBytecode_->bytes : nullptr; } bool binarySource() const { return binarySource_; } - // If the source bytecode was saved when this Code was constructed, this - // method will render the binary as text. Otherwise, a diagnostic string - // will be returned. - JSString* createText(JSContext* cx); bool getLineOffsets(JSContext* cx, size_t lineno, Vector* offsets); bool getAllColumnOffsets(JSContext* cx, Vector* offsets); diff --git a/js/src/wasm/WasmTextToBinary.cpp b/js/src/wasm/WasmTextToBinary.cpp index b307b3117ea5..df066677be6d 100644 --- a/js/src/wasm/WasmTextToBinary.cpp +++ b/js/src/wasm/WasmTextToBinary.cpp @@ -5544,7 +5544,7 @@ EncodeTableSection(Encoder& e, AstModule& module) } static bool -EncodeFunctionBody(Encoder& e, AstFunc& func) +EncodeFunctionBody(Encoder& e, Uint32Vector* offsets, AstFunc& func) { size_t bodySizeAt; if (!e.writePatchableVarU32(&bodySizeAt)) @@ -5559,10 +5559,14 @@ EncodeFunctionBody(Encoder& e, AstFunc& func) return false; for (AstExpr* expr : func.body()) { + if (!offsets->append(e.currentOffset())) + return false; if (!EncodeExpr(e, *expr)) return false; } + if (!offsets->append(e.currentOffset())) + return false; if (!e.writeOp(Op::End)) return false; @@ -5588,7 +5592,7 @@ EncodeStartSection(Encoder& e, AstModule& module) } static bool -EncodeCodeSection(Encoder& e, AstModule& module) +EncodeCodeSection(Encoder& e, Uint32Vector* offsets, AstModule& module) { if (module.funcs().empty()) return true; @@ -5601,7 +5605,7 @@ EncodeCodeSection(Encoder& e, AstModule& module) return false; for (AstFunc* func : module.funcs()) { - if (!EncodeFunctionBody(e, *func)) + if (!EncodeFunctionBody(e, offsets, *func)) return false; } @@ -5708,7 +5712,7 @@ EncodeElemSection(Encoder& e, AstModule& module) } static bool -EncodeModule(AstModule& module, Bytes* bytes) +EncodeModule(AstModule& module, Uint32Vector* offsets, Bytes* bytes) { Encoder e(*bytes); @@ -5745,7 +5749,7 @@ EncodeModule(AstModule& module, Bytes* bytes) if (!EncodeElemSection(e, module)) return false; - if (!EncodeCodeSection(e, module)) + if (!EncodeCodeSection(e, offsets, module)) return false; if (!EncodeDataSection(e, module)) @@ -5779,7 +5783,8 @@ EncodeBinaryModule(const AstModule& module, Bytes* bytes) /*****************************************************************************/ bool -wasm::TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, UniqueChars* error) +wasm::TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, Uint32Vector* offsets, + UniqueChars* error) { LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE); @@ -5794,5 +5799,5 @@ wasm::TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, Uni if (!ResolveModule(lifo, module, error)) return false; - return EncodeModule(*module, bytes); + return EncodeModule(*module, offsets, bytes); } diff --git a/js/src/wasm/WasmTextToBinary.h b/js/src/wasm/WasmTextToBinary.h index a67e14c2a1f1..0e525bb4aea6 100644 --- a/js/src/wasm/WasmTextToBinary.h +++ b/js/src/wasm/WasmTextToBinary.h @@ -29,7 +29,8 @@ namespace wasm { // other than out-of-memory an error message string will be stored in 'error'. extern MOZ_MUST_USE bool -TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, UniqueChars* error); +TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, Uint32Vector* offsets, + UniqueChars* error); } // namespace wasm } // namespace js From 1bf2162c020841456ec10cee57a1b4d243ef2375 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 21 Jun 2018 11:49:47 +0200 Subject: [PATCH 02/43] Bug 1447591: Remove wasm::BinaryToText; r=luke --HG-- extra : rebase_source : eaa5b8c96e804cabafd8958b9f7ab5b9ce5a4087 extra : histedit_source : 4800ac47dca92340204ffe5f8dd8ecb535c4706b --- js/src/builtin/TestingFunctions.cpp | 62 - .../tests/debug/wasm-getAllColumnOffsets.js | 1 - js/src/jit-test/tests/wasm/atomic.js | 69 - js/src/jit-test/tests/wasm/gc/structs.js | 9 - js/src/jit-test/tests/wasm/to-text.js | 293 -- js/src/moz.build | 3 - js/src/wasm/WasmAST.h | 28 +- js/src/wasm/WasmBinaryToAST.cpp | 2390 ----------------- js/src/wasm/WasmBinaryToAST.h | 37 - js/src/wasm/WasmBinaryToText.cpp | 2087 -------------- js/src/wasm/WasmBinaryToText.h | 44 - js/src/wasm/WasmDebug.cpp | 1 - js/src/wasm/WasmTextUtils.cpp | 80 - js/src/wasm/WasmTextUtils.h | 105 - js/src/wasm/WasmValidate.h | 3 - 15 files changed, 9 insertions(+), 5203 deletions(-) delete mode 100644 js/src/jit-test/tests/wasm/to-text.js delete mode 100644 js/src/wasm/WasmBinaryToAST.cpp delete mode 100644 js/src/wasm/WasmBinaryToAST.h delete mode 100644 js/src/wasm/WasmBinaryToText.cpp delete mode 100644 js/src/wasm/WasmBinaryToText.h delete mode 100644 js/src/wasm/WasmTextUtils.cpp delete mode 100644 js/src/wasm/WasmTextUtils.h diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index c25346690241..fff29feaacc9 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -63,7 +63,6 @@ #include "vm/StringType.h" #include "vm/TraceLogging.h" #include "wasm/AsmJS.h" -#include "wasm/WasmBinaryToText.h" #include "wasm/WasmJS.h" #include "wasm/WasmModule.h" #include "wasm/WasmSignalHandlers.h" @@ -714,63 +713,6 @@ WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp) return true; } -static bool -WasmBinaryToText(JSContext* cx, unsigned argc, Value* vp) -{ - if (!cx->options().wasm()) { - JS_ReportErrorASCII(cx, "wasm support unavailable"); - return false; - } - - CallArgs args = CallArgsFromVp(argc, vp); - - if (!args.get(0).isObject() || !args.get(0).toObject().is()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG); - return false; - } - - Rooted code(cx, &args[0].toObject().as()); - - if (!TypedArrayObject::ensureHasBuffer(cx, code)) - return false; - - if (code->isSharedMemory()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG); - return false; - } - - const uint8_t* bufferStart = code->bufferUnshared()->dataPointer(); - const uint8_t* bytes = bufferStart + code->byteOffset(); - uint32_t length = code->byteLength(); - - Vector copy(cx); - if (code->bufferUnshared()->hasInlineData()) { - if (!copy.append(bytes, length)) - return false; - bytes = copy.begin(); - } - - if (args.length() > 1) { - JS_ReportErrorASCII(cx, "wasm text format selection is not supported"); - return false; - } - - StringBuffer buffer(cx); - bool ok = wasm::BinaryToText(cx, bytes, length, buffer); - if (!ok) { - if (!cx->isExceptionPending()) - JS_ReportErrorASCII(cx, "wasm binary to text print error"); - return false; - } - - JSString* result = buffer.finishString(); - if (!result) - return false; - - args.rval().setString(result); - return true; -} - static bool WasmExtractCode(JSContext* cx, unsigned argc, Value* vp) { @@ -5706,10 +5648,6 @@ gc::ZealModeHelpText), "wasmTextToBinary(str)", " Translates the given text wasm module into its binary encoding."), - JS_FN_HELP("wasmBinaryToText", WasmBinaryToText, 1, 0, -"wasmBinaryToText(bin)", -" Translates binary encoding to text format"), - JS_FN_HELP("wasmExtractCode", WasmExtractCode, 1, 0, "wasmExtractCode(module[, tier])", " Extracts generated machine code from WebAssembly.Module. The tier is a string,\n" diff --git a/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js b/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js index 703f64b96673..c47f2ccd17b8 100644 --- a/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js +++ b/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js @@ -9,7 +9,6 @@ if (!wasmDebuggingIsSupported()) // Checking if experimental format generates internal source map to binary file // by querying debugger scripts getAllColumnOffsets. -// (Notice that the source map will not be produced by wasmBinaryToText) function getAllOffsets(wast) { var sandbox = newGlobal(''); var dbg = new Debugger(); diff --git a/js/src/jit-test/tests/wasm/atomic.js b/js/src/jit-test/tests/wasm/atomic.js index 23be9da9dae5..9dfb8d1e767d 100644 --- a/js/src/jit-test/tests/wasm/atomic.js +++ b/js/src/jit-test/tests/wasm/atomic.js @@ -84,79 +84,10 @@ for (let align of [1, 2, 4, 8]) { assertEq(valText(text), align == 4); } -// Check that the text output is sane. - -for (let [type,view] of [['i32','8_u'],['i32','16_u'],['i32',''],['i64','8_u'],['i64','16_u'],['i64','32_u'],['i64','']]) { - let addr = "i32.const 48"; - let value = `${type}.const 1`; - let value2 = `${type}.const 2`; - for (let op of ["add", "and", "or", "xor", "xchg"]) { - let operator = `${type}.atomic.rmw${view}.${op}`; - let text = `(module (memory 1 1 shared) - (func (result ${type}) (${operator} (${addr}) (${value}))) - (export "" 0))`; - checkRoundTrip(text, [addr, value, operator]); - } - { - let operator = `${type}.atomic.rmw${view}.cmpxchg`; - let text = `(module (memory 1 1 shared) - (func (result ${type}) (${operator} (${addr}) (${value}) (${value2}))) - (export "" 0))`; - checkRoundTrip(text, [addr, value, value2, operator]); - } - { - let operator = `${type}.atomic.load${view}`; - let text = `(module (memory 1 1 shared) - (func (result ${type}) (${operator} (${addr}))) - (export "" 0))`; - checkRoundTrip(text, [addr, operator]); - } - { - let operator = `${type}.atomic.store${view}`; - let text = `(module (memory 1 1 shared) - (func (${operator} (${addr}) (${value}))) - (export "" 0))`; - checkRoundTrip(text, [addr, value, operator]); - } -} - -for (let type of ['i32', 'i64']) { - let addr = "i32.const 48"; - let operator = `${type}.atomic.wait` - let value = `${type}.const 1`; - let timeout = "i64.const 314159"; - let text = `(module (memory 1 1 shared) - (func (result i32) (${operator} (${addr}) (${value}) (${timeout}))) - (export "" 0))`; - checkRoundTrip(text, [addr, value, timeout, operator]); -} - -{ - let addr = "i32.const 48"; - let operator = "atomic.wake" - let count = "i32.const 1"; - let text = `(module (memory 1 1 shared) - (func (result i32) (${operator} (${addr}) (${count}))) - (export "" 0))`; - checkRoundTrip(text, [addr, count, operator]); -} - function valText(text) { return WebAssembly.validate(wasmTextToBinary(text)); } -function checkRoundTrip(text, ss) { - let input = wasmTextToBinary(text); - let output = wasmBinaryToText(input).split("\n").map(String.trim); - for (let s of output) { - if (ss.length == 0) - break; - if (s.match(ss[0])) - ss.shift(); - } - assertEq(ss.length, 0); -} - // Test that atomic operations work. function I64(hi, lo) { diff --git a/js/src/jit-test/tests/wasm/gc/structs.js b/js/src/jit-test/tests/wasm/gc/structs.js index dca23258c018..75044c94057c 100644 --- a/js/src/jit-test/tests/wasm/gc/structs.js +++ b/js/src/jit-test/tests/wasm/gc/structs.js @@ -69,15 +69,6 @@ assertEq(ins.hello(4.0, 1), 16.0) assertEq(ins.x1(12), 36) assertEq(ins.x2(8), Math.PI) -// Crude but at least checks that we have *something*. - -var txt = wasmBinaryToText(bin); -var re = /\(type\s+\$[a-z0-9]+\s+\(struct/gm; -assertEq(Array.isArray(re.exec(txt)), true); -assertEq(Array.isArray(re.exec(txt)), true); -assertEq(Array.isArray(re.exec(txt)), true); -assertEq(Array.isArray(re.exec(txt)), false); - // The field name is optional, so this should work. wasmEvalText(` diff --git a/js/src/jit-test/tests/wasm/to-text.js b/js/src/jit-test/tests/wasm/to-text.js deleted file mode 100644 index e89ce615b018..000000000000 --- a/js/src/jit-test/tests/wasm/to-text.js +++ /dev/null @@ -1,293 +0,0 @@ -var caught = false; -try { - wasmBinaryToText(new Int8Array(1)); -} catch (e) { - caught = true; -} -assertEq(caught, true); - -assertErrorMessage(() => wasmBinaryToText(wasmTextToBinary(`(module (func (result i32) (f32.const 13.37)))`)), WebAssembly.CompileError, /type mismatch/); - -function runTest(code) { - var expected = wasmTextToBinary(code); - var s = wasmBinaryToText(expected); - print("TEXT: " + s); - var roundtrip = wasmTextToBinary(s); - assertDeepEq(expected, roundtrip); -} - -// Smoke test -runTest(` -(module - (func (param i32) (result f64) - (local $l f32) - (block - (set_local $l (f32.const 0.0)) - (loop $exit $cont - (br_if $exit (get_local 0)) - (br 2) - ) - (drop (if f64 (i32.const 1) - (f64.min (f64.neg (f64.const 1)) (f64.const 0)) - (f64.add (f64.const 0.5) (f64.load offset=0 (i32.const 0)) ) - )) - ) - (i32.store16 (i32.const 8) (i32.const 128)) - - (return (f64.const 0)) - ) - (export "test" 0) - (memory 1 10) -)`); - -// Constants, stores and loads -runTest(` -(module (func - (local i32) (local f32) (local f64) - (drop (i32.const 0)) - (drop (i32.const 100002)) - (drop (f32.const 0.0)) - (drop (f32.const 1.5)) - (drop (f64.const 0.0)) - (drop (f64.const -10.25)) - (i32.store (i32.const 0) (i32.load (i32.const 0))) - (i32.store8 (i32.const 1) (i32.load8_s (i32.const 2))) - (i32.store8 (i32.const 3) (i32.load8_u (i32.const 4))) - (i32.store16 (i32.const 2) (i32.load16_s (i32.const 0))) - (i32.store16 (i32.const 1) (i32.load16_u (i32.const 0))) - (f32.store (i32.const 5) (f32.load (i32.const 6))) - (f64.store (i32.const 5) (f64.load (i32.const 6))) - (set_local 0 (get_local 0)) - (set_local 2 (get_local 2)) -)(memory 100))`); - -// Branching -runTest(` -(module -(func - (block (block (block (nop)))) - (block (loop )) - (if (i32.const 0) (block $label (nop))) - (if (i32.const 1) (nop) (loop $exit $cont (block ))) - (block $l (br $l)) - (block $m (block (block (br $m)))) - (block $k (br_if 0 (i32.const 0)) (return)) - (block $n (block (block (br_if 2 (i32.const 1)) (nop)))) - (block $1 (block $2 (block $3 (br_table $2 $3 $1 (i32.const 1)) )) (nop)) - (loop $exit $cont (br_if $cont (i32.const 0)) (nop)) - (return) -) -(func (result f32) (return (f32.const -0.5))) -(memory 0) -)`); - -// i32, f32 and f64 operations -runTest(` -(module - (func $iadd (param $x i32) (param $y i32) (result i32) (i32.add (get_local $x) (get_local $y))) - (func $isub (param $x i32) (param $y i32) (result i32) (i32.sub (get_local $x) (get_local $y))) - (func $imul (param $x i32) (param $y i32) (result i32) (i32.mul (get_local $x) (get_local $y))) - (func $idiv_s (param $x i32) (param $y i32) (result i32) (i32.div_s (get_local $x) (get_local $y))) - (func $idiv_u (param $x i32) (param $y i32) (result i32) (i32.div_u (get_local $x) (get_local $y))) - (func $irem_s (param $x i32) (param $y i32) (result i32) (i32.rem_s (get_local $x) (get_local $y))) - (func $irem_u (param $x i32) (param $y i32) (result i32) (i32.rem_u (get_local $x) (get_local $y))) - (func $iand (param $x i32) (param $y i32) (result i32) (i32.and (get_local $x) (get_local $y))) - (func $ior (param $x i32) (param $y i32) (result i32) (i32.or (get_local $x) (get_local $y))) - (func $ixor (param $x i32) (param $y i32) (result i32) (i32.xor (get_local $x) (get_local $y))) - (func $ishl (param $x i32) (param $y i32) (result i32) (i32.shl (get_local $x) (get_local $y))) - (func $ishr_s (param $x i32) (param $y i32) (result i32) (i32.shr_s (get_local $x) (get_local $y))) - (func $ishr_u (param $x i32) (param $y i32) (result i32) (i32.shr_u (get_local $x) (get_local $y))) - (func $iclz (param $x i32) (result i32) (i32.clz (get_local $x))) - (func $ictz (param $x i32) (result i32) (i32.ctz (get_local $x))) - (func $ipopcnt (param $x i32) (result i32) (i32.popcnt (get_local $x))) - (func $ieq (param $x i32) (param $y i32) (result i32) (i32.eq (get_local $x) (get_local $y))) - (func $ine (param $x i32) (param $y i32) (result i32) (i32.ne (get_local $x) (get_local $y))) - (func $ilt_s (param $x i32) (param $y i32) (result i32) (i32.lt_s (get_local $x) (get_local $y))) - (func $ilt_u (param $x i32) (param $y i32) (result i32) (i32.lt_u (get_local $x) (get_local $y))) - (func $ile_s (param $x i32) (param $y i32) (result i32) (i32.le_s (get_local $x) (get_local $y))) - (func $ile_u (param $x i32) (param $y i32) (result i32) (i32.le_u (get_local $x) (get_local $y))) - (func $igt_s (param $x i32) (param $y i32) (result i32) (i32.gt_s (get_local $x) (get_local $y))) - (func $igt_u (param $x i32) (param $y i32) (result i32) (i32.gt_u (get_local $x) (get_local $y))) - (func $ige_s (param $x i32) (param $y i32) (result i32) (i32.ge_s (get_local $x) (get_local $y))) - (func $ige_u (param $x i32) (param $y i32) (result i32) (i32.ge_u (get_local $x) (get_local $y))) - - (func $fadd (param $x f32) (param $y f32) (result f32) (f32.add (get_local $x) (get_local $y))) - (func $fsub (param $x f32) (param $y f32) (result f32) (f32.sub (get_local $x) (get_local $y))) - (func $fmul (param $x f32) (param $y f32) (result f32) (f32.mul (get_local $x) (get_local $y))) - (func $fdiv (param $x f32) (param $y f32) (result f32) (f32.div (get_local $x) (get_local $y))) - (func $fsqrt (param $x f32) (result f32) (f32.sqrt (get_local $x))) - (func $fmin (param $x f32) (param $y f32) (result f32) (f32.min (get_local $x) (get_local $y))) - (func $fmax (param $x f32) (param $y f32) (result f32) (f32.max (get_local $x) (get_local $y))) - (func $fceil (param $x f32) (result f32) (f32.ceil (get_local $x))) - (func $ffloor (param $x f32) (result f32) (f32.floor (get_local $x))) - (func $fabs (param $x f32) (result f32) (f32.abs (get_local $x))) - (func $fneg (param $x f32) (result f32) (f32.neg (get_local $x))) - - (func $dadd (param $x f64) (param $y f64) (result f64) (f64.add (get_local $x) (get_local $y))) - (func $dsub (param $x f64) (param $y f64) (result f64) (f64.sub (get_local $x) (get_local $y))) - (func $dmul (param $x f64) (param $y f64) (result f64) (f64.mul (get_local $x) (get_local $y))) - (func $ddiv (param $x f64) (param $y f64) (result f64) (f64.div (get_local $x) (get_local $y))) - (func $dceil (param $x f64) (result f64) (f64.ceil (get_local $x))) - (func $dfloor (param $x f64) (result f64) (f64.floor (get_local $x))) - (func $dabs (param $x f64) (result f64) (f64.abs (get_local $x))) - (func $dneg (param $x f64) (result f64) (f64.neg (get_local $x))) -(memory 0))`); - -// conversions -runTest(` -(module - (func $itrunc_s_f32 (param $x f32) (result i32) (i32.trunc_s/f32 (get_local $x))) - (func $itrunc_u_f32 (param $x f32) (result i32) (i32.trunc_u/f32 (get_local $x))) - (func $itrunc_s_f64 (param $x f64) (result i32) (i32.trunc_s/f64 (get_local $x))) - (func $itrunc_u_f64 (param $x f64) (result i32) (i32.trunc_u/f64 (get_local $x))) - (func $fconvert_s_i32 (param $x i32) (result f32) (f32.convert_s/i32 (get_local $x))) - (func $dconvert_s_i32 (param $x i32) (result f64) (f64.convert_s/i32 (get_local $x))) - (func $fconvert_u_i32 (param $x i32) (result f32) (f32.convert_u/i32 (get_local $x))) - (func $dconvert_u_i32 (param $x i32) (result f64) (f64.convert_u/i32 (get_local $x))) - (func $dpromote_f32 (param $x f32) (result f64) (f64.promote/f32 (get_local $x))) - (func $fdemote_f64 (param $x f64) (result f32) (f32.demote/f64 (get_local $x))) -(memory 0))`); - -// function calls -runTest(` -(module - (type $type1 (func (param i32) (result i32))) - (import $import1 "mod" "test" (param f32) (result f32)) - (table anyfunc (elem $func1 $func2)) - (func $func1 (param i32) (param f32) (nop)) - (func $func2 (param i32) (result i32) (get_local 0)) - (func $test - (call $func1 - (call_indirect $type1 (i32.const 2) (i32.const 1)) - (call $import1 (f32.const 1.0)) - ) - ) - (export "test" $test) - (memory 1) -)`); - -// default memory export from binaryen -runTest(`(module (func (nop)) (memory 0 65535))`); - -// stack-machine code that isn't directly representable as an AST -runTest(` -(module - (func (result i32) - (local $x i32) - i32.const 100 - set_local $x - i32.const 200 - set_local $x - i32.const 400 - set_local $x - i32.const 2 - i32.const 16 - nop - set_local $x - i32.const 3 - i32.const 17 - set_local $x - i32.const 18 - set_local $x - i32.lt_s - if i32 - i32.const 101 - set_local $x - i32.const 8 - i32.const 102 - set_local $x - else - i32.const 103 - set_local $x - i32.const 900 - i32.const 104 - set_local $x - i32.const 105 - set_local $x - end - i32.const 107 - set_local $x - get_local $x - i32.add - i32.const 106 - set_local $x - ) - (export "" 0) -)`); - -// more stack-machine code that isn't directly representable as an AST -runTest(` -(module - (func $return_void) - - (func (result i32) - (local $x i32) - i32.const 0 - block - i32.const 1 - set_local $x - end - i32.const 2 - set_local $x - i32.const 3 - loop - i32.const 4 - set_local $x - end - i32.const 5 - set_local $x - i32.add - call $return_void - ) - (export "" 0) -)`); - -runTest(` - (module - (func $func - block $block - i32.const 0 - if - i32.const 0 - if - end - else - end - end - ) - (export "" 0) -)`); - -// Branch table. -runTest(`(module - (func (export "run") (param $p i32) (local $n i32) - i32.const 0 - set_local $n - loop $outer - block $c block $b block $a - loop $inner - get_local $p - br_table $b $a $c $inner $outer - end $inner - end $a - get_local $n - i32.const 1 - i32.add - set_local $n - end $b - block - get_local $n - i32.const 2 - i32.add - set_local $n - end - end $c - end $outer - ) -)`); - -// Import as a start function. -runTest(`(module - (import "env" "test" (func)) - (start 0) -)`); diff --git a/js/src/moz.build b/js/src/moz.build index f9b803b400b8..043e49ede68c 100755 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -401,8 +401,6 @@ UNIFIED_SOURCES += [ 'vm/Xdr.cpp', 'wasm/AsmJS.cpp', 'wasm/WasmBaselineCompile.cpp', - 'wasm/WasmBinaryToAST.cpp', - 'wasm/WasmBinaryToText.cpp', 'wasm/WasmBuiltins.cpp', 'wasm/WasmCode.cpp', 'wasm/WasmCompile.cpp', @@ -420,7 +418,6 @@ UNIFIED_SOURCES += [ 'wasm/WasmStubs.cpp', 'wasm/WasmTable.cpp', 'wasm/WasmTextToBinary.cpp', - 'wasm/WasmTextUtils.cpp', 'wasm/WasmTypes.cpp', 'wasm/WasmValidate.cpp' ] diff --git a/js/src/wasm/WasmAST.h b/js/src/wasm/WasmAST.h index f0302d9d4ec0..da52a778dd3b 100644 --- a/js/src/wasm/WasmAST.h +++ b/js/src/wasm/WasmAST.h @@ -126,10 +126,17 @@ struct AstBase } }; +struct AstNode +{ + void* operator new(size_t numBytes, LifoAlloc& astLifo) throw() { + return astLifo.alloc(numBytes); + } +}; + class AstFuncType; class AstStructType; -class AstTypeDef : public AstBase +class AstTypeDef : public AstNode { protected: enum class Which { IsFuncType, IsStructType }; @@ -258,19 +265,6 @@ AstTypeDef::asStructType() const return *static_cast(this); } -const uint32_t AstNodeUnknownOffset = 0; - -class AstNode : public AstBase -{ - uint32_t offset_; // if applicable, offset in the binary format file - - public: - AstNode() : offset_(AstNodeUnknownOffset) {} - - uint32_t offset() const { return offset_; } - void setOffset(uint32_t offset) { offset_ = offset; } -}; - enum class AstExprKind { AtomicCmpXchg, @@ -862,7 +856,6 @@ class AstFunc : public AstNode AstValTypeVector vars_; AstNameVector localNames_; AstExprVector body_; - uint32_t endOffset_; // if applicable, offset in the binary format file public: AstFunc(AstName name, AstRef ft, AstValTypeVector&& vars, @@ -871,16 +864,13 @@ class AstFunc : public AstNode funcType_(ft), vars_(std::move(vars)), localNames_(std::move(locals)), - body_(std::move(body)), - endOffset_(AstNodeUnknownOffset) + body_(std::move(body)) {} AstRef& funcType() { return funcType_; } const AstValTypeVector& vars() const { return vars_; } const AstNameVector& locals() const { return localNames_; } const AstExprVector& body() const { return body_; } AstName name() const { return name_; } - uint32_t endOffset() const { return endOffset_; } - void setEndOffset(uint32_t offset) { endOffset_ = offset; } }; class AstGlobal : public AstNode diff --git a/js/src/wasm/WasmBinaryToAST.cpp b/js/src/wasm/WasmBinaryToAST.cpp deleted file mode 100644 index da72508716d2..000000000000 --- a/js/src/wasm/WasmBinaryToAST.cpp +++ /dev/null @@ -1,2390 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * - * Copyright 2016 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "wasm/WasmBinaryToAST.h" - -#include "mozilla/MathAlgorithms.h" -#include "mozilla/Sprintf.h" - -#include "vm/JSContext.h" -#include "vm/Realm.h" -#include "wasm/WasmOpIter.h" -#include "wasm/WasmValidate.h" - -using namespace js; -using namespace js::wasm; - -using mozilla::FloorLog2; - -enum AstDecodeTerminationKind -{ - Unknown, - End, - Else -}; - -struct AstDecodeStackItem -{ - AstExpr* expr; - AstDecodeTerminationKind terminationKind; - ExprType type; - - explicit AstDecodeStackItem() - : expr(nullptr), - terminationKind(AstDecodeTerminationKind::Unknown), - type(ExprType::Limit) - {} - explicit AstDecodeStackItem(AstDecodeTerminationKind terminationKind, ExprType type) - : expr(nullptr), - terminationKind(terminationKind), - type(type) - {} - explicit AstDecodeStackItem(AstExpr* expr) - : expr(expr), - terminationKind(AstDecodeTerminationKind::Unknown), - type(ExprType::Limit) - {} -}; - -// We don't define a Value type because OpIter doesn't push void values, which -// we actually need here because we're building an AST, so we maintain our own -// stack. -struct AstDecodePolicy -{ - typedef Nothing Value; - typedef Nothing ControlItem; -}; - -typedef OpIter AstDecodeOpIter; - -class AstDecodeContext -{ - public: - typedef AstVector AstDecodeStack; - typedef AstVector DepthStack; - - JSContext* cx; - LifoAlloc& lifo; - Decoder& d; - bool generateNames; - - private: - ModuleEnvironment env_; - - AstModule& module_; - AstDecodeOpIter *iter_; - AstDecodeStack exprs_; - DepthStack depths_; - const ValTypeVector* locals_; - AstNameVector blockLabels_; - uint32_t currentLabelIndex_; - ExprType retType_; - - public: - AstDecodeContext(JSContext* cx, LifoAlloc& lifo, Decoder& d, AstModule& module, - bool generateNames, HasGcTypes hasGcTypes) - : cx(cx), - lifo(lifo), - d(d), - generateNames(generateNames), - env_(CompileMode::Once, Tier::Ion, DebugEnabled::False, hasGcTypes, - cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled() - ? Shareable::True - : Shareable::False), - module_(module), - iter_(nullptr), - exprs_(lifo), - depths_(lifo), - locals_(nullptr), - blockLabels_(lifo), - currentLabelIndex_(0), - retType_(ExprType::Limit) - {} - - ModuleEnvironment& env() { return env_; } - - AstModule& module() { return module_; } - AstDecodeOpIter& iter() { return *iter_; } - AstDecodeStack& exprs() { return exprs_; } - DepthStack& depths() { return depths_; } - - AstNameVector& blockLabels() { return blockLabels_; } - - ExprType retType() const { return retType_; } - const ValTypeVector& locals() const { return *locals_; } - - void popBack() { return exprs().popBack(); } - AstDecodeStackItem popCopy() { return exprs().popCopy(); } - AstDecodeStackItem& top() { return exprs().back(); } - MOZ_MUST_USE bool push(AstDecodeStackItem item) { return exprs().append(item); } - - bool needFirst() { - for (size_t i = depths().back(); i < exprs().length(); ++i) { - if (!exprs()[i].expr->isVoid()) - return true; - } - return false; - } - - AstExpr* handleVoidExpr(AstExpr* voidNode) - { - MOZ_ASSERT(voidNode->isVoid()); - - // To attach a node that "returns void" to the middle of an AST, wrap it - // in a first node next to the node it should accompany. - if (needFirst()) { - AstExpr *prev = popCopy().expr; - - // If the previous/A node is already a First, reuse it. - if (prev->kind() == AstExprKind::First) { - if (!prev->as().exprs().append(voidNode)) - return nullptr; - return prev; - } - - AstExprVector exprs(lifo); - if (!exprs.append(prev)) - return nullptr; - if (!exprs.append(voidNode)) - return nullptr; - - return new(lifo) AstFirst(std::move(exprs)); - } - - return voidNode; - } - - void startFunction(AstDecodeOpIter* iter, const ValTypeVector* locals, ExprType retType) - { - iter_ = iter; - locals_ = locals; - currentLabelIndex_ = 0; - retType_ = retType; - } - void endFunction() - { - iter_ = nullptr; - locals_ = nullptr; - retType_ = ExprType::Limit; - MOZ_ASSERT(blockLabels_.length() == 0); - } - uint32_t nextLabelIndex() - { - return currentLabelIndex_++; - } -}; - -static bool -GenerateName(AstDecodeContext& c, const AstName& prefix, uint32_t index, AstName* name) -{ - if (!c.generateNames) { - *name = AstName(); - return true; - } - - AstVector result(c.lifo); - if (!result.append(u'$')) - return false; - if (!result.append(prefix.begin(), prefix.length())) - return false; - - uint32_t tmp = index; - do { - if (!result.append(u'0')) - return false; - tmp /= 10; - } while (tmp); - - if (index) { - char16_t* p = result.end(); - for (tmp = index; tmp; tmp /= 10) - *(--p) = u'0' + (tmp % 10); - } - - size_t length = result.length(); - char16_t* begin = result.extractOrCopyRawBuffer(); - if (!begin) - return false; - - *name = AstName(begin, length); - return true; -} - -static bool -GenerateRef(AstDecodeContext& c, const AstName& prefix, uint32_t index, AstRef* ref) -{ - MOZ_ASSERT(index != AstNoIndex); - - if (!c.generateNames) { - *ref = AstRef(index); - return true; - } - - AstName name; - if (!GenerateName(c, prefix, index, &name)) - return false; - MOZ_ASSERT(!name.empty()); - - *ref = AstRef(name); - ref->setIndex(index); - return true; -} - -static bool -GenerateFuncRef(AstDecodeContext& c, uint32_t funcIndex, AstRef* ref) -{ - if (funcIndex < c.module().numFuncImports()) { - *ref = AstRef(c.module().funcImportNames()[funcIndex]); - } else { - if (!GenerateRef(c, AstName(u"func"), funcIndex, ref)) - return false; - } - return true; -} - -static bool -AstDecodeCallArgs(AstDecodeContext& c, const FuncTypeWithId& funcType, AstExprVector* funcArgs) -{ - MOZ_ASSERT(!c.iter().currentBlockHasPolymorphicBase()); - - uint32_t numArgs = funcType.args().length(); - if (!funcArgs->resize(numArgs)) - return false; - - for (size_t i = 0; i < numArgs; ++i) - (*funcArgs)[i] = c.exprs()[c.exprs().length() - numArgs + i].expr; - - c.exprs().shrinkBy(numArgs); - - return true; -} - -static bool -AstDecodeExpr(AstDecodeContext& c); - -static bool -AstDecodeDrop(AstDecodeContext& c) -{ - if (!c.iter().readDrop()) - return false; - - AstDecodeStackItem value = c.popCopy(); - - AstExpr* tmp = new(c.lifo) AstDrop(*value.expr); - if (!tmp) - return false; - - tmp = c.handleVoidExpr(tmp); - if (!tmp) - return false; - - if (!c.push(AstDecodeStackItem(tmp))) - return false; - - return true; -} - -static bool -AstDecodeCall(AstDecodeContext& c) -{ - uint32_t funcIndex; - AstDecodeOpIter::ValueVector unusedArgs; - if (!c.iter().readCall(&funcIndex, &unusedArgs)) - return false; - - if (c.iter().currentBlockHasPolymorphicBase()) - return true; - - AstRef funcRef; - if (!GenerateFuncRef(c, funcIndex, &funcRef)) - return false; - - const FuncTypeWithId* funcType = c.env().funcTypes[funcIndex]; - - AstExprVector args(c.lifo); - if (!AstDecodeCallArgs(c, *funcType, &args)) - return false; - - AstCall* call = new(c.lifo) AstCall(Op::Call, funcType->ret(), funcRef, std::move(args)); - if (!call) - return false; - - AstExpr* result = call; - if (IsVoid(funcType->ret())) - result = c.handleVoidExpr(call); - - if (!c.push(AstDecodeStackItem(result))) - return false; - - return true; -} - -static bool -AstDecodeCallIndirect(AstDecodeContext& c) -{ - uint32_t funcTypeIndex; - AstDecodeOpIter::ValueVector unusedArgs; - if (!c.iter().readCallIndirect(&funcTypeIndex, nullptr, &unusedArgs)) - return false; - - if (c.iter().currentBlockHasPolymorphicBase()) - return true; - - AstDecodeStackItem index = c.popCopy(); - - AstRef funcTypeRef; - if (!GenerateRef(c, AstName(u"type"), funcTypeIndex, &funcTypeRef)) - return false; - - const FuncTypeWithId& funcType = c.env().types[funcTypeIndex].funcType(); - AstExprVector args(c.lifo); - if (!AstDecodeCallArgs(c, funcType, &args)) - return false; - - AstCallIndirect* call = - new(c.lifo) AstCallIndirect(funcTypeRef, funcType.ret(), std::move(args), index.expr); - if (!call) - return false; - - AstExpr* result = call; - if (IsVoid(funcType.ret())) - result = c.handleVoidExpr(call); - - if (!c.push(AstDecodeStackItem(result))) - return false; - - return true; -} - -static bool -AstDecodeGetBlockRef(AstDecodeContext& c, uint32_t depth, AstRef* ref) -{ - if (!c.generateNames || depth >= c.blockLabels().length()) { - // Also ignoring if it's a function body label. - *ref = AstRef(depth); - return true; - } - - uint32_t index = c.blockLabels().length() - depth - 1; - if (c.blockLabels()[index].empty()) { - if (!GenerateName(c, AstName(u"label"), c.nextLabelIndex(), &c.blockLabels()[index])) - return false; - } - *ref = AstRef(c.blockLabels()[index]); - ref->setIndex(depth); - return true; -} - -static bool -AstDecodeBrTable(AstDecodeContext& c) -{ - bool unreachable = c.iter().currentBlockHasPolymorphicBase(); - - Uint32Vector depths; - uint32_t defaultDepth; - ExprType type; - if (!c.iter().readBrTable(&depths, &defaultDepth, &type, nullptr, nullptr)) - return false; - - if (unreachable) - return true; - - AstRefVector table(c.lifo); - if (!table.resize(depths.length())) - return false; - - for (size_t i = 0; i < depths.length(); ++i) { - if (!AstDecodeGetBlockRef(c, depths[i], &table[i])) - return false; - } - - AstDecodeStackItem index = c.popCopy(); - AstDecodeStackItem value; - if (!IsVoid(type)) - value = c.popCopy(); - - AstRef def; - if (!AstDecodeGetBlockRef(c, defaultDepth, &def)) - return false; - - auto branchTable = new(c.lifo) AstBranchTable(*index.expr, def, std::move(table), value.expr); - if (!branchTable) - return false; - - if (!c.push(AstDecodeStackItem(branchTable))) - return false; - - return true; -} - -static bool -AstDecodeBlock(AstDecodeContext& c, Op op) -{ - MOZ_ASSERT(op == Op::Block || op == Op::Loop); - - if (!c.blockLabels().append(AstName())) - return false; - - if (op == Op::Loop) { - if (!c.iter().readLoop()) - return false; - } else { - if (!c.iter().readBlock()) - return false; - } - - if (!c.depths().append(c.exprs().length())) - return false; - - ExprType type; - while (true) { - if (!AstDecodeExpr(c)) - return false; - - const AstDecodeStackItem& item = c.top(); - if (!item.expr) { // Op::End was found - type = item.type; - c.popBack(); - break; - } - } - - AstExprVector exprs(c.lifo); - for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end(); - i != e; ++i) { - if (!exprs.append(i->expr)) - return false; - } - c.exprs().shrinkTo(c.depths().popCopy()); - - AstName name = c.blockLabels().popCopy(); - AstBlock* block = new(c.lifo) AstBlock(op, type, name, std::move(exprs)); - if (!block) - return false; - - AstExpr* result = block; - if (IsVoid(type)) - result = c.handleVoidExpr(block); - - if (!c.push(AstDecodeStackItem(result))) - return false; - - return true; -} - -static bool -AstDecodeIf(AstDecodeContext& c) -{ - if (!c.iter().readIf(nullptr)) - return false; - - AstDecodeStackItem cond = c.popCopy(); - - bool hasElse = false; - - if (!c.depths().append(c.exprs().length())) - return false; - - if (!c.blockLabels().append(AstName())) - return false; - - ExprType type; - while (true) { - if (!AstDecodeExpr(c)) - return false; - - const AstDecodeStackItem& item = c.top(); - if (!item.expr) { // Op::End was found - hasElse = item.terminationKind == AstDecodeTerminationKind::Else; - type = item.type; - c.popBack(); - break; - } - } - - AstExprVector thenExprs(c.lifo); - for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end(); - i != e; ++i) { - if (!thenExprs.append(i->expr)) - return false; - } - c.exprs().shrinkTo(c.depths().back()); - - AstExprVector elseExprs(c.lifo); - if (hasElse) { - while (true) { - if (!AstDecodeExpr(c)) - return false; - - const AstDecodeStackItem& item = c.top(); - if (!item.expr) { // Op::End was found - c.popBack(); - break; - } - } - - for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end(); - i != e; ++i) { - if (!elseExprs.append(i->expr)) - return false; - } - c.exprs().shrinkTo(c.depths().back()); - } - - c.depths().popBack(); - - AstName name = c.blockLabels().popCopy(); - - AstIf* if_ = new(c.lifo) AstIf(type, cond.expr, name, std::move(thenExprs), std::move(elseExprs)); - if (!if_) - return false; - - AstExpr* result = if_; - if (IsVoid(type)) - result = c.handleVoidExpr(if_); - - if (!c.push(AstDecodeStackItem(result))) - return false; - - return true; -} - -static bool -AstDecodeEnd(AstDecodeContext& c) -{ - LabelKind kind; - ExprType type; - if (!c.iter().readEnd(&kind, &type, nullptr)) - return false; - - c.iter().popEnd(); - - if (!c.push(AstDecodeStackItem(AstDecodeTerminationKind::End, type))) - return false; - - return true; -} - -static bool -AstDecodeElse(AstDecodeContext& c) -{ - ExprType type; - - if (!c.iter().readElse(&type, nullptr)) - return false; - - if (!c.push(AstDecodeStackItem(AstDecodeTerminationKind::Else, type))) - return false; - - return true; -} - -static bool -AstDecodeNop(AstDecodeContext& c) -{ - if (!c.iter().readNop()) - return false; - - AstExpr* tmp = new(c.lifo) AstNop(); - if (!tmp) - return false; - - tmp = c.handleVoidExpr(tmp); - if (!tmp) - return false; - - if (!c.push(AstDecodeStackItem(tmp))) - return false; - - return true; -} - -static bool -AstDecodeUnary(AstDecodeContext& c, ValType type, Op op) -{ - if (!c.iter().readUnary(type, nullptr)) - return false; - - AstDecodeStackItem operand = c.popCopy(); - - AstUnaryOperator* unary = new(c.lifo) AstUnaryOperator(op, operand.expr); - if (!unary) - return false; - - if (!c.push(AstDecodeStackItem(unary))) - return false; - - return true; -} - -static bool -AstDecodeBinary(AstDecodeContext& c, ValType type, Op op) -{ - if (!c.iter().readBinary(type, nullptr, nullptr)) - return false; - - AstDecodeStackItem rhs = c.popCopy(); - AstDecodeStackItem lhs = c.popCopy(); - - AstBinaryOperator* binary = new(c.lifo) AstBinaryOperator(op, lhs.expr, rhs.expr); - if (!binary) - return false; - - if (!c.push(AstDecodeStackItem(binary))) - return false; - - return true; -} - -static bool -AstDecodeSelect(AstDecodeContext& c) -{ - StackType type; - if (!c.iter().readSelect(&type, nullptr, nullptr, nullptr)) - return false; - - if (c.iter().currentBlockHasPolymorphicBase()) - return true; - - AstDecodeStackItem selectFalse = c.popCopy(); - AstDecodeStackItem selectTrue = c.popCopy(); - AstDecodeStackItem cond = c.popCopy(); - - auto* select = new(c.lifo) AstTernaryOperator(Op::Select, cond.expr, selectTrue.expr, - selectFalse.expr); - if (!select) - return false; - - if (!c.push(AstDecodeStackItem(select))) - return false; - - return true; -} - -static bool -AstDecodeComparison(AstDecodeContext& c, ValType type, Op op) -{ - if (!c.iter().readComparison(type, nullptr, nullptr)) - return false; - - AstDecodeStackItem rhs = c.popCopy(); - AstDecodeStackItem lhs = c.popCopy(); - - AstComparisonOperator* comparison = new(c.lifo) AstComparisonOperator(op, lhs.expr, rhs.expr); - if (!comparison) - return false; - - if (!c.push(AstDecodeStackItem(comparison))) - return false; - - return true; -} - -static bool -AstDecodeConversion(AstDecodeContext& c, ValType fromType, ValType toType, Op op) -{ - if (!c.iter().readConversion(fromType, toType, nullptr)) - return false; - - AstDecodeStackItem operand = c.popCopy(); - - AstConversionOperator* conversion = new(c.lifo) AstConversionOperator(op, operand.expr); - if (!conversion) - return false; - - if (!c.push(AstDecodeStackItem(conversion))) - return false; - - return true; -} - -#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS -static bool -AstDecodeExtraConversion(AstDecodeContext& c, ValType fromType, ValType toType, MiscOp op) -{ - if (!c.iter().readConversion(fromType, toType, nullptr)) - return false; - - AstDecodeStackItem operand = c.popCopy(); - - AstExtraConversionOperator* conversion = - new(c.lifo) AstExtraConversionOperator(op, operand.expr); - if (!conversion) - return false; - - if (!c.push(AstDecodeStackItem(conversion))) - return false; - - return true; -} -#endif - -static AstLoadStoreAddress -AstDecodeLoadStoreAddress(const LinearMemoryAddress& addr, const AstDecodeStackItem& item) -{ - uint32_t flags = FloorLog2(addr.align); - return AstLoadStoreAddress(item.expr, flags, addr.offset); -} - -static bool -AstDecodeLoad(AstDecodeContext& c, ValType type, uint32_t byteSize, Op op) -{ - LinearMemoryAddress addr; - if (!c.iter().readLoad(type, byteSize, &addr)) - return false; - - AstDecodeStackItem item = c.popCopy(); - - AstLoad* load = new(c.lifo) AstLoad(op, AstDecodeLoadStoreAddress(addr, item)); - if (!load) - return false; - - if (!c.push(AstDecodeStackItem(load))) - return false; - - return true; -} - -static bool -AstDecodeStore(AstDecodeContext& c, ValType type, uint32_t byteSize, Op op) -{ - LinearMemoryAddress addr; - if (!c.iter().readStore(type, byteSize, &addr, nullptr)) - return false; - - AstDecodeStackItem value = c.popCopy(); - AstDecodeStackItem item = c.popCopy(); - - AstStore* store = new(c.lifo) AstStore(op, AstDecodeLoadStoreAddress(addr, item), value.expr); - if (!store) - return false; - - AstExpr* wrapped = c.handleVoidExpr(store); - if (!wrapped) - return false; - - if (!c.push(AstDecodeStackItem(wrapped))) - return false; - - return true; -} - -static bool -AstDecodeCurrentMemory(AstDecodeContext& c) -{ - if (!c.iter().readCurrentMemory()) - return false; - - AstCurrentMemory* gm = new(c.lifo) AstCurrentMemory(); - if (!gm) - return false; - - if (!c.push(AstDecodeStackItem(gm))) - return false; - - return true; -} - -static bool -AstDecodeGrowMemory(AstDecodeContext& c) -{ - if (!c.iter().readGrowMemory(nullptr)) - return false; - - AstDecodeStackItem operand = c.popCopy(); - - AstGrowMemory* gm = new(c.lifo) AstGrowMemory(operand.expr); - if (!gm) - return false; - - if (!c.push(AstDecodeStackItem(gm))) - return false; - - return true; -} - -static bool -AstDecodeBranch(AstDecodeContext& c, Op op) -{ - MOZ_ASSERT(op == Op::Br || op == Op::BrIf); - - uint32_t depth; - ExprType type; - AstDecodeStackItem value; - AstDecodeStackItem cond; - if (op == Op::Br) { - if (!c.iter().readBr(&depth, &type, nullptr)) - return false; - if (!IsVoid(type)) - value = c.popCopy(); - } else { - if (!c.iter().readBrIf(&depth, &type, nullptr, nullptr)) - return false; - if (!IsVoid(type)) - value = c.popCopy(); - cond = c.popCopy(); - } - - AstRef depthRef; - if (!AstDecodeGetBlockRef(c, depth, &depthRef)) - return false; - - if (op == Op::Br || !value.expr) - type = ExprType::Void; - AstBranch* branch = new(c.lifo) AstBranch(op, type, cond.expr, depthRef, value.expr); - if (!branch) - return false; - - if (!c.push(AstDecodeStackItem(branch))) - return false; - - return true; -} - -static bool -AstDecodeGetLocal(AstDecodeContext& c) -{ - uint32_t getLocalId; - if (!c.iter().readGetLocal(c.locals(), &getLocalId)) - return false; - - AstRef localRef; - if (!GenerateRef(c, AstName(u"var"), getLocalId, &localRef)) - return false; - - AstGetLocal* getLocal = new(c.lifo) AstGetLocal(localRef); - if (!getLocal) - return false; - - if (!c.push(AstDecodeStackItem(getLocal))) - return false; - - return true; -} - -static bool -AstDecodeSetLocal(AstDecodeContext& c) -{ - uint32_t setLocalId; - if (!c.iter().readSetLocal(c.locals(), &setLocalId, nullptr)) - return false; - - AstDecodeStackItem setLocalValue = c.popCopy(); - - AstRef localRef; - if (!GenerateRef(c, AstName(u"var"), setLocalId, &localRef)) - return false; - - AstSetLocal* setLocal = new(c.lifo) AstSetLocal(localRef, *setLocalValue.expr); - if (!setLocal) - return false; - - AstExpr* expr = c.handleVoidExpr(setLocal); - if (!expr) - return false; - - if (!c.push(AstDecodeStackItem(expr))) - return false; - - return true; -} - -static bool -AstDecodeTeeLocal(AstDecodeContext& c) -{ - uint32_t teeLocalId; - if (!c.iter().readTeeLocal(c.locals(), &teeLocalId, nullptr)) - return false; - - AstDecodeStackItem teeLocalValue = c.popCopy(); - - AstRef localRef; - if (!GenerateRef(c, AstName(u"var"), teeLocalId, &localRef)) - return false; - - AstTeeLocal* teeLocal = new(c.lifo) AstTeeLocal(localRef, *teeLocalValue.expr); - if (!teeLocal) - return false; - - if (!c.push(AstDecodeStackItem(teeLocal))) - return false; - - return true; -} - -static bool -AstDecodeGetGlobal(AstDecodeContext& c) -{ - uint32_t globalId; - if (!c.iter().readGetGlobal(&globalId)) - return false; - - AstRef globalRef; - if (!GenerateRef(c, AstName(u"global"), globalId, &globalRef)) - return false; - - auto* getGlobal = new(c.lifo) AstGetGlobal(globalRef); - if (!getGlobal) - return false; - - if (!c.push(AstDecodeStackItem(getGlobal))) - return false; - - return true; -} - -static bool -AstDecodeSetGlobal(AstDecodeContext& c) -{ - uint32_t globalId; - if (!c.iter().readSetGlobal(&globalId, nullptr)) - return false; - - AstDecodeStackItem value = c.popCopy(); - - AstRef globalRef; - if (!GenerateRef(c, AstName(u"global"), globalId, &globalRef)) - return false; - - auto* setGlobal = new(c.lifo) AstSetGlobal(globalRef, *value.expr); - if (!setGlobal) - return false; - - AstExpr* expr = c.handleVoidExpr(setGlobal); - if (!expr) - return false; - - if (!c.push(AstDecodeStackItem(expr))) - return false; - - return true; -} - -static bool -AstDecodeReturn(AstDecodeContext& c) -{ - if (!c.iter().readReturn(nullptr)) - return false; - - AstDecodeStackItem result; - if (!IsVoid(c.retType())) - result = c.popCopy(); - - AstReturn* ret = new(c.lifo) AstReturn(result.expr); - if (!ret) - return false; - - if (!c.push(AstDecodeStackItem(ret))) - return false; - - return true; -} - -static bool -AstDecodeAtomicLoad(AstDecodeContext& c, ThreadOp op) -{ - ValType type; - uint32_t byteSize; - switch (op) { - case ThreadOp::I32AtomicLoad: type = ValType::I32; byteSize = 4; break; - case ThreadOp::I64AtomicLoad: type = ValType::I64; byteSize = 8; break; - case ThreadOp::I32AtomicLoad8U: type = ValType::I32; byteSize = 1; break; - case ThreadOp::I32AtomicLoad16U: type = ValType::I32; byteSize = 2; break; - case ThreadOp::I64AtomicLoad8U: type = ValType::I64; byteSize = 1; break; - case ThreadOp::I64AtomicLoad16U: type = ValType::I64; byteSize = 2; break; - case ThreadOp::I64AtomicLoad32U: type = ValType::I64; byteSize = 4; break; - default: - MOZ_CRASH("Should not happen"); - } - - LinearMemoryAddress addr; - if (!c.iter().readAtomicLoad(&addr, type, byteSize)) - return false; - - AstDecodeStackItem item = c.popCopy(); - - AstAtomicLoad* load = new(c.lifo) AstAtomicLoad(op, AstDecodeLoadStoreAddress(addr, item)); - if (!load) - return false; - - if (!c.push(AstDecodeStackItem(load))) - return false; - - return true; -} - -static bool -AstDecodeAtomicStore(AstDecodeContext& c, ThreadOp op) -{ - ValType type; - uint32_t byteSize; - switch (op) { - case ThreadOp::I32AtomicStore: type = ValType::I32; byteSize = 4; break; - case ThreadOp::I64AtomicStore: type = ValType::I64; byteSize = 8; break; - case ThreadOp::I32AtomicStore8U: type = ValType::I32; byteSize = 1; break; - case ThreadOp::I32AtomicStore16U: type = ValType::I32; byteSize = 2; break; - case ThreadOp::I64AtomicStore8U: type = ValType::I64; byteSize = 1; break; - case ThreadOp::I64AtomicStore16U: type = ValType::I64; byteSize = 2; break; - case ThreadOp::I64AtomicStore32U: type = ValType::I64; byteSize = 4; break; - default: - MOZ_CRASH("Should not happen"); - } - - Nothing nothing; - LinearMemoryAddress addr; - if (!c.iter().readAtomicStore(&addr, type, byteSize, ¬hing)) - return false; - - AstDecodeStackItem value = c.popCopy(); - AstDecodeStackItem item = c.popCopy(); - - AstAtomicStore* store = new(c.lifo) AstAtomicStore(op, AstDecodeLoadStoreAddress(addr, item), value.expr); - if (!store) - return false; - - AstExpr* wrapped = c.handleVoidExpr(store); - if (!wrapped) - return false; - - if (!c.push(AstDecodeStackItem(wrapped))) - return false; - - return true; -} - -static bool -AstDecodeAtomicRMW(AstDecodeContext& c, ThreadOp op) -{ - ValType type; - uint32_t byteSize; - switch (op) { - case ThreadOp::I32AtomicAdd: - case ThreadOp::I32AtomicSub: - case ThreadOp::I32AtomicAnd: - case ThreadOp::I32AtomicOr: - case ThreadOp::I32AtomicXor: - case ThreadOp::I32AtomicXchg: - type = ValType::I32; - byteSize = 4; - break; - case ThreadOp::I64AtomicAdd: - case ThreadOp::I64AtomicSub: - case ThreadOp::I64AtomicAnd: - case ThreadOp::I64AtomicOr: - case ThreadOp::I64AtomicXor: - case ThreadOp::I64AtomicXchg: - type = ValType::I64; - byteSize = 8; - break; - case ThreadOp::I32AtomicAdd8U: - case ThreadOp::I32AtomicSub8U: - case ThreadOp::I32AtomicOr8U: - case ThreadOp::I32AtomicXor8U: - case ThreadOp::I32AtomicXchg8U: - case ThreadOp::I32AtomicAnd8U: - type = ValType::I32; - byteSize = 1; - break; - case ThreadOp::I32AtomicAdd16U: - case ThreadOp::I32AtomicSub16U: - case ThreadOp::I32AtomicAnd16U: - case ThreadOp::I32AtomicOr16U: - case ThreadOp::I32AtomicXor16U: - case ThreadOp::I32AtomicXchg16U: - type = ValType::I32; - byteSize = 2; - break; - case ThreadOp::I64AtomicAdd8U: - case ThreadOp::I64AtomicSub8U: - case ThreadOp::I64AtomicAnd8U: - case ThreadOp::I64AtomicOr8U: - case ThreadOp::I64AtomicXor8U: - case ThreadOp::I64AtomicXchg8U: - type = ValType::I64; - byteSize = 1; - break; - case ThreadOp::I64AtomicAdd16U: - case ThreadOp::I64AtomicSub16U: - case ThreadOp::I64AtomicAnd16U: - case ThreadOp::I64AtomicOr16U: - case ThreadOp::I64AtomicXor16U: - case ThreadOp::I64AtomicXchg16U: - type = ValType::I64; - byteSize = 2; - break; - case ThreadOp::I64AtomicAdd32U: - case ThreadOp::I64AtomicSub32U: - case ThreadOp::I64AtomicAnd32U: - case ThreadOp::I64AtomicOr32U: - case ThreadOp::I64AtomicXor32U: - case ThreadOp::I64AtomicXchg32U: - type = ValType::I64; - byteSize = 4; - break; - default: - MOZ_CRASH("Should not happen"); - } - - Nothing nothing; - LinearMemoryAddress addr; - if (!c.iter().readAtomicRMW(&addr, type, byteSize, ¬hing)) - return false; - - AstDecodeStackItem value = c.popCopy(); - AstDecodeStackItem item = c.popCopy(); - - AstAtomicRMW* rmw = new(c.lifo) AstAtomicRMW(op, AstDecodeLoadStoreAddress(addr, item), - value.expr); - if (!rmw) - return false; - - if (!c.push(AstDecodeStackItem(rmw))) - return false; - - return true; -} - -static bool -AstDecodeAtomicCmpXchg(AstDecodeContext& c, ThreadOp op) -{ - ValType type; - uint32_t byteSize; - switch (op) { - case ThreadOp::I32AtomicCmpXchg: type = ValType::I32; byteSize = 4; break; - case ThreadOp::I64AtomicCmpXchg: type = ValType::I64; byteSize = 8; break; - case ThreadOp::I32AtomicCmpXchg8U: type = ValType::I32; byteSize = 1; break; - case ThreadOp::I32AtomicCmpXchg16U: type = ValType::I32; byteSize = 2; break; - case ThreadOp::I64AtomicCmpXchg8U: type = ValType::I64; byteSize = 1; break; - case ThreadOp::I64AtomicCmpXchg16U: type = ValType::I64; byteSize = 2; break; - case ThreadOp::I64AtomicCmpXchg32U: type = ValType::I64; byteSize = 4; break; - default: - MOZ_CRASH("Should not happen"); - } - - Nothing nothing; - LinearMemoryAddress addr; - if (!c.iter().readAtomicCmpXchg(&addr, type, byteSize, ¬hing, ¬hing)) - return false; - - AstDecodeStackItem replacement = c.popCopy(); - AstDecodeStackItem expected = c.popCopy(); - AstDecodeStackItem item = c.popCopy(); - - AstAtomicCmpXchg* cmpxchg = - new(c.lifo) AstAtomicCmpXchg(op, AstDecodeLoadStoreAddress(addr, item), expected.expr, - replacement.expr); - if (!cmpxchg) - return false; - - if (!c.push(AstDecodeStackItem(cmpxchg))) - return false; - - return true; -} - -static bool -AstDecodeWait(AstDecodeContext& c, ThreadOp op) -{ - ValType type; - uint32_t byteSize; - switch (op) { - case ThreadOp::I32Wait: type = ValType::I32; byteSize = 4; break; - case ThreadOp::I64Wait: type = ValType::I64; byteSize = 8; break; - default: - MOZ_CRASH("Should not happen"); - } - - Nothing nothing; - LinearMemoryAddress addr; - if (!c.iter().readWait(&addr, type, byteSize, ¬hing, ¬hing)) - return false; - - AstDecodeStackItem timeout = c.popCopy(); - AstDecodeStackItem value = c.popCopy(); - AstDecodeStackItem item = c.popCopy(); - - AstWait* wait = new(c.lifo) AstWait(op, AstDecodeLoadStoreAddress(addr, item), value.expr, - timeout.expr); - if (!wait) - return false; - - if (!c.push(AstDecodeStackItem(wait))) - return false; - - return true; -} - -static bool -AstDecodeWake(AstDecodeContext& c) -{ - Nothing nothing; - LinearMemoryAddress addr; - if (!c.iter().readWake(&addr, ¬hing)) - return false; - - AstDecodeStackItem count = c.popCopy(); - AstDecodeStackItem item = c.popCopy(); - - AstWake* wake = new(c.lifo) AstWake(AstDecodeLoadStoreAddress(addr, item), count.expr); - if (!wake) - return false; - - if (!c.push(AstDecodeStackItem(wake))) - return false; - - return true; -} - -#ifdef ENABLE_WASM_BULKMEM_OPS -static bool -AstDecodeMemCopy(AstDecodeContext& c) -{ - if (!c.iter().readMemCopy(nullptr, nullptr, nullptr)) - return false; - - AstDecodeStackItem dest = c.popCopy(); - AstDecodeStackItem src = c.popCopy(); - AstDecodeStackItem len = c.popCopy(); - - AstMemCopy* mc = new(c.lifo) AstMemCopy(dest.expr, src.expr, len.expr); - - if (!mc) - return false; - - if (!c.push(AstDecodeStackItem(mc))) - return false; - - return true; -} - -static bool -AstDecodeMemFill(AstDecodeContext& c) -{ - if (!c.iter().readMemFill(nullptr, nullptr, nullptr)) - return false; - - AstDecodeStackItem len = c.popCopy(); - AstDecodeStackItem val = c.popCopy(); - AstDecodeStackItem start = c.popCopy(); - - AstMemFill* mf = new(c.lifo) AstMemFill(start.expr, val.expr, len.expr); - - if (!mf) - return false; - - if (!c.push(AstDecodeStackItem(mf))) - return false; - - return true; -} -#endif - -static bool -AstDecodeExpr(AstDecodeContext& c) -{ - uint32_t exprOffset = c.iter().currentOffset(); - OpBytes op; - if (!c.iter().readOp(&op)) - return false; - - AstExpr* tmp; - switch (op.b0) { - case uint16_t(Op::Nop): - if (!AstDecodeNop(c)) - return false; - break; - case uint16_t(Op::Drop): - if (!AstDecodeDrop(c)) - return false; - break; - case uint16_t(Op::Call): - if (!AstDecodeCall(c)) - return false; - break; - case uint16_t(Op::CallIndirect): - if (!AstDecodeCallIndirect(c)) - return false; - break; - case uint16_t(Op::I32Const): - int32_t i32; - if (!c.iter().readI32Const(&i32)) - return false; - tmp = new(c.lifo) AstConst(Val((uint32_t)i32)); - if (!tmp || !c.push(AstDecodeStackItem(tmp))) - return false; - break; - case uint16_t(Op::I64Const): - int64_t i64; - if (!c.iter().readI64Const(&i64)) - return false; - tmp = new(c.lifo) AstConst(Val((uint64_t)i64)); - if (!tmp || !c.push(AstDecodeStackItem(tmp))) - return false; - break; - case uint16_t(Op::F32Const): { - float f32; - if (!c.iter().readF32Const(&f32)) - return false; - tmp = new(c.lifo) AstConst(Val(f32)); - if (!tmp || !c.push(AstDecodeStackItem(tmp))) - return false; - break; - } - case uint16_t(Op::F64Const): { - double f64; - if (!c.iter().readF64Const(&f64)) - return false; - tmp = new(c.lifo) AstConst(Val(f64)); - if (!tmp || !c.push(AstDecodeStackItem(tmp))) - return false; - break; - } - case uint16_t(Op::GetLocal): - if (!AstDecodeGetLocal(c)) - return false; - break; - case uint16_t(Op::SetLocal): - if (!AstDecodeSetLocal(c)) - return false; - break; - case uint16_t(Op::TeeLocal): - if (!AstDecodeTeeLocal(c)) - return false; - break; - case uint16_t(Op::Select): - if (!AstDecodeSelect(c)) - return false; - break; - case uint16_t(Op::Block): - case uint16_t(Op::Loop): - if (!AstDecodeBlock(c, Op(op.b0))) - return false; - break; - case uint16_t(Op::If): - if (!AstDecodeIf(c)) - return false; - break; - case uint16_t(Op::Else): - if (!AstDecodeElse(c)) - return false; - break; - case uint16_t(Op::End): - if (!AstDecodeEnd(c)) - return false; - break; - case uint16_t(Op::I32Clz): - case uint16_t(Op::I32Ctz): - case uint16_t(Op::I32Popcnt): - if (!AstDecodeUnary(c, ValType::I32, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64Clz): - case uint16_t(Op::I64Ctz): - case uint16_t(Op::I64Popcnt): - if (!AstDecodeUnary(c, ValType::I64, Op(op.b0))) - return false; - break; - case uint16_t(Op::F32Abs): - case uint16_t(Op::F32Neg): - case uint16_t(Op::F32Ceil): - case uint16_t(Op::F32Floor): - case uint16_t(Op::F32Sqrt): - case uint16_t(Op::F32Trunc): - case uint16_t(Op::F32Nearest): - if (!AstDecodeUnary(c, ValType::F32, Op(op.b0))) - return false; - break; - case uint16_t(Op::F64Abs): - case uint16_t(Op::F64Neg): - case uint16_t(Op::F64Ceil): - case uint16_t(Op::F64Floor): - case uint16_t(Op::F64Sqrt): - case uint16_t(Op::F64Trunc): - case uint16_t(Op::F64Nearest): - if (!AstDecodeUnary(c, ValType::F64, Op(op.b0))) - return false; - break; - case uint16_t(Op::I32Add): - case uint16_t(Op::I32Sub): - case uint16_t(Op::I32Mul): - case uint16_t(Op::I32DivS): - case uint16_t(Op::I32DivU): - case uint16_t(Op::I32RemS): - case uint16_t(Op::I32RemU): - case uint16_t(Op::I32And): - case uint16_t(Op::I32Or): - case uint16_t(Op::I32Xor): - case uint16_t(Op::I32Shl): - case uint16_t(Op::I32ShrS): - case uint16_t(Op::I32ShrU): - case uint16_t(Op::I32Rotl): - case uint16_t(Op::I32Rotr): - if (!AstDecodeBinary(c, ValType::I32, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64Add): - case uint16_t(Op::I64Sub): - case uint16_t(Op::I64Mul): - case uint16_t(Op::I64DivS): - case uint16_t(Op::I64DivU): - case uint16_t(Op::I64RemS): - case uint16_t(Op::I64RemU): - case uint16_t(Op::I64And): - case uint16_t(Op::I64Or): - case uint16_t(Op::I64Xor): - case uint16_t(Op::I64Shl): - case uint16_t(Op::I64ShrS): - case uint16_t(Op::I64ShrU): - case uint16_t(Op::I64Rotl): - case uint16_t(Op::I64Rotr): - if (!AstDecodeBinary(c, ValType::I64, Op(op.b0))) - return false; - break; - case uint16_t(Op::F32Add): - case uint16_t(Op::F32Sub): - case uint16_t(Op::F32Mul): - case uint16_t(Op::F32Div): - case uint16_t(Op::F32Min): - case uint16_t(Op::F32Max): - case uint16_t(Op::F32CopySign): - if (!AstDecodeBinary(c, ValType::F32, Op(op.b0))) - return false; - break; - case uint16_t(Op::F64Add): - case uint16_t(Op::F64Sub): - case uint16_t(Op::F64Mul): - case uint16_t(Op::F64Div): - case uint16_t(Op::F64Min): - case uint16_t(Op::F64Max): - case uint16_t(Op::F64CopySign): - if (!AstDecodeBinary(c, ValType::F64, Op(op.b0))) - return false; - break; - case uint16_t(Op::I32Eq): - case uint16_t(Op::I32Ne): - case uint16_t(Op::I32LtS): - case uint16_t(Op::I32LtU): - case uint16_t(Op::I32LeS): - case uint16_t(Op::I32LeU): - case uint16_t(Op::I32GtS): - case uint16_t(Op::I32GtU): - case uint16_t(Op::I32GeS): - case uint16_t(Op::I32GeU): - if (!AstDecodeComparison(c, ValType::I32, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64Eq): - case uint16_t(Op::I64Ne): - case uint16_t(Op::I64LtS): - case uint16_t(Op::I64LtU): - case uint16_t(Op::I64LeS): - case uint16_t(Op::I64LeU): - case uint16_t(Op::I64GtS): - case uint16_t(Op::I64GtU): - case uint16_t(Op::I64GeS): - case uint16_t(Op::I64GeU): - if (!AstDecodeComparison(c, ValType::I64, Op(op.b0))) - return false; - break; - case uint16_t(Op::F32Eq): - case uint16_t(Op::F32Ne): - case uint16_t(Op::F32Lt): - case uint16_t(Op::F32Le): - case uint16_t(Op::F32Gt): - case uint16_t(Op::F32Ge): - if (!AstDecodeComparison(c, ValType::F32, Op(op.b0))) - return false; - break; - case uint16_t(Op::F64Eq): - case uint16_t(Op::F64Ne): - case uint16_t(Op::F64Lt): - case uint16_t(Op::F64Le): - case uint16_t(Op::F64Gt): - case uint16_t(Op::F64Ge): - if (!AstDecodeComparison(c, ValType::F64, Op(op.b0))) - return false; - break; - case uint16_t(Op::I32Eqz): - if (!AstDecodeConversion(c, ValType::I32, ValType::I32, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64Eqz): - case uint16_t(Op::I32WrapI64): - if (!AstDecodeConversion(c, ValType::I64, ValType::I32, Op(op.b0))) - return false; - break; - case uint16_t(Op::I32TruncSF32): - case uint16_t(Op::I32TruncUF32): - case uint16_t(Op::I32ReinterpretF32): - if (!AstDecodeConversion(c, ValType::F32, ValType::I32, Op(op.b0))) - return false; - break; - case uint16_t(Op::I32TruncSF64): - case uint16_t(Op::I32TruncUF64): - if (!AstDecodeConversion(c, ValType::F64, ValType::I32, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64ExtendSI32): - case uint16_t(Op::I64ExtendUI32): - if (!AstDecodeConversion(c, ValType::I32, ValType::I64, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64TruncSF32): - case uint16_t(Op::I64TruncUF32): - if (!AstDecodeConversion(c, ValType::F32, ValType::I64, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64TruncSF64): - case uint16_t(Op::I64TruncUF64): - case uint16_t(Op::I64ReinterpretF64): - if (!AstDecodeConversion(c, ValType::F64, ValType::I64, Op(op.b0))) - return false; - break; - case uint16_t(Op::F32ConvertSI32): - case uint16_t(Op::F32ConvertUI32): - case uint16_t(Op::F32ReinterpretI32): - if (!AstDecodeConversion(c, ValType::I32, ValType::F32, Op(op.b0))) - return false; - break; - case uint16_t(Op::F32ConvertSI64): - case uint16_t(Op::F32ConvertUI64): - if (!AstDecodeConversion(c, ValType::I64, ValType::F32, Op(op.b0))) - return false; - break; - case uint16_t(Op::F32DemoteF64): - if (!AstDecodeConversion(c, ValType::F64, ValType::F32, Op(op.b0))) - return false; - break; - case uint16_t(Op::F64ConvertSI32): - case uint16_t(Op::F64ConvertUI32): - if (!AstDecodeConversion(c, ValType::I32, ValType::F64, Op(op.b0))) - return false; - break; - case uint16_t(Op::F64ConvertSI64): - case uint16_t(Op::F64ConvertUI64): - case uint16_t(Op::F64ReinterpretI64): - if (!AstDecodeConversion(c, ValType::I64, ValType::F64, Op(op.b0))) - return false; - break; - case uint16_t(Op::F64PromoteF32): - if (!AstDecodeConversion(c, ValType::F32, ValType::F64, Op(op.b0))) - return false; - break; - case uint16_t(Op::I32Extend8S): - case uint16_t(Op::I32Extend16S): - if (!AstDecodeConversion(c, ValType::I32, ValType::I32, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64Extend8S): - case uint16_t(Op::I64Extend16S): - case uint16_t(Op::I64Extend32S): - if (!AstDecodeConversion(c, ValType::I64, ValType::I64, Op(op.b0))) - return false; - break; - case uint16_t(Op::I32Load8S): - case uint16_t(Op::I32Load8U): - if (!AstDecodeLoad(c, ValType::I32, 1, Op(op.b0))) - return false; - break; - case uint16_t(Op::I32Load16S): - case uint16_t(Op::I32Load16U): - if (!AstDecodeLoad(c, ValType::I32, 2, Op(op.b0))) - return false; - break; - case uint16_t(Op::I32Load): - if (!AstDecodeLoad(c, ValType::I32, 4, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64Load8S): - case uint16_t(Op::I64Load8U): - if (!AstDecodeLoad(c, ValType::I64, 1, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64Load16S): - case uint16_t(Op::I64Load16U): - if (!AstDecodeLoad(c, ValType::I64, 2, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64Load32S): - case uint16_t(Op::I64Load32U): - if (!AstDecodeLoad(c, ValType::I64, 4, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64Load): - if (!AstDecodeLoad(c, ValType::I64, 8, Op(op.b0))) - return false; - break; - case uint16_t(Op::F32Load): - if (!AstDecodeLoad(c, ValType::F32, 4, Op(op.b0))) - return false; - break; - case uint16_t(Op::F64Load): - if (!AstDecodeLoad(c, ValType::F64, 8, Op(op.b0))) - return false; - break; - case uint16_t(Op::I32Store8): - if (!AstDecodeStore(c, ValType::I32, 1, Op(op.b0))) - return false; - break; - case uint16_t(Op::I32Store16): - if (!AstDecodeStore(c, ValType::I32, 2, Op(op.b0))) - return false; - break; - case uint16_t(Op::I32Store): - if (!AstDecodeStore(c, ValType::I32, 4, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64Store8): - if (!AstDecodeStore(c, ValType::I64, 1, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64Store16): - if (!AstDecodeStore(c, ValType::I64, 2, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64Store32): - if (!AstDecodeStore(c, ValType::I64, 4, Op(op.b0))) - return false; - break; - case uint16_t(Op::I64Store): - if (!AstDecodeStore(c, ValType::I64, 8, Op(op.b0))) - return false; - break; - case uint16_t(Op::F32Store): - if (!AstDecodeStore(c, ValType::F32, 4, Op(op.b0))) - return false; - break; - case uint16_t(Op::F64Store): - if (!AstDecodeStore(c, ValType::F64, 8, Op(op.b0))) - return false; - break; - case uint16_t(Op::CurrentMemory): - if (!AstDecodeCurrentMemory(c)) - return false; - break; - case uint16_t(Op::GrowMemory): - if (!AstDecodeGrowMemory(c)) - return false; - break; - case uint16_t(Op::SetGlobal): - if (!AstDecodeSetGlobal(c)) - return false; - break; - case uint16_t(Op::GetGlobal): - if (!AstDecodeGetGlobal(c)) - return false; - break; - case uint16_t(Op::Br): - case uint16_t(Op::BrIf): - if (!AstDecodeBranch(c, Op(op.b0))) - return false; - break; - case uint16_t(Op::BrTable): - if (!AstDecodeBrTable(c)) - return false; - break; - case uint16_t(Op::Return): - if (!AstDecodeReturn(c)) - return false; - break; - case uint16_t(Op::Unreachable): - if (!c.iter().readUnreachable()) - return false; - tmp = new(c.lifo) AstUnreachable(); - if (!tmp) - return false; - if (!c.push(AstDecodeStackItem(tmp))) - return false; - break; - case uint16_t(Op::MiscPrefix): - switch (op.b1) { -#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS - case uint16_t(MiscOp::I32TruncSSatF32): - case uint16_t(MiscOp::I32TruncUSatF32): - if (!AstDecodeExtraConversion(c, ValType::F32, ValType::I32, MiscOp(op.b1))) - return false; - break; - case uint16_t(MiscOp::I32TruncSSatF64): - case uint16_t(MiscOp::I32TruncUSatF64): - if (!AstDecodeExtraConversion(c, ValType::F64, ValType::I32, MiscOp(op.b1))) - return false; - break; - case uint16_t(MiscOp::I64TruncSSatF32): - case uint16_t(MiscOp::I64TruncUSatF32): - if (!AstDecodeExtraConversion(c, ValType::F32, ValType::I64, MiscOp(op.b1))) - return false; - break; - case uint16_t(MiscOp::I64TruncSSatF64): - case uint16_t(MiscOp::I64TruncUSatF64): - if (!AstDecodeExtraConversion(c, ValType::F64, ValType::I64, MiscOp(op.b1))) - return false; - break; -#endif -#ifdef ENABLE_WASM_BULKMEM_OPS - case uint16_t(MiscOp::MemCopy): - if (!AstDecodeMemCopy(c)) - return false; - break; - case uint16_t(MiscOp::MemFill): - if (!AstDecodeMemFill(c)) - return false; - break; -#endif - default: - return c.iter().unrecognizedOpcode(&op); - } - break; - case uint16_t(Op::ThreadPrefix): - switch (op.b1) { - case uint16_t(ThreadOp::Wake): - if (!AstDecodeWake(c)) - return false; - break; - case uint16_t(ThreadOp::I32Wait): - case uint16_t(ThreadOp::I64Wait): - if (!AstDecodeWait(c, ThreadOp(op.b1))) - return false; - break; - case uint16_t(ThreadOp::I32AtomicLoad): - case uint16_t(ThreadOp::I64AtomicLoad): - case uint16_t(ThreadOp::I32AtomicLoad8U): - case uint16_t(ThreadOp::I32AtomicLoad16U): - case uint16_t(ThreadOp::I64AtomicLoad8U): - case uint16_t(ThreadOp::I64AtomicLoad16U): - case uint16_t(ThreadOp::I64AtomicLoad32U): - if (!AstDecodeAtomicLoad(c, ThreadOp(op.b1))) - return false; - break; - case uint16_t(ThreadOp::I32AtomicStore): - case uint16_t(ThreadOp::I64AtomicStore): - case uint16_t(ThreadOp::I32AtomicStore8U): - case uint16_t(ThreadOp::I32AtomicStore16U): - case uint16_t(ThreadOp::I64AtomicStore8U): - case uint16_t(ThreadOp::I64AtomicStore16U): - case uint16_t(ThreadOp::I64AtomicStore32U): - if (!AstDecodeAtomicStore(c, ThreadOp(op.b1))) - return false; - break; - case uint16_t(ThreadOp::I32AtomicAdd): - case uint16_t(ThreadOp::I64AtomicAdd): - case uint16_t(ThreadOp::I32AtomicAdd8U): - case uint16_t(ThreadOp::I32AtomicAdd16U): - case uint16_t(ThreadOp::I64AtomicAdd8U): - case uint16_t(ThreadOp::I64AtomicAdd16U): - case uint16_t(ThreadOp::I64AtomicAdd32U): - case uint16_t(ThreadOp::I32AtomicSub): - case uint16_t(ThreadOp::I64AtomicSub): - case uint16_t(ThreadOp::I32AtomicSub8U): - case uint16_t(ThreadOp::I32AtomicSub16U): - case uint16_t(ThreadOp::I64AtomicSub8U): - case uint16_t(ThreadOp::I64AtomicSub16U): - case uint16_t(ThreadOp::I64AtomicSub32U): - case uint16_t(ThreadOp::I32AtomicAnd): - case uint16_t(ThreadOp::I64AtomicAnd): - case uint16_t(ThreadOp::I32AtomicAnd8U): - case uint16_t(ThreadOp::I32AtomicAnd16U): - case uint16_t(ThreadOp::I64AtomicAnd8U): - case uint16_t(ThreadOp::I64AtomicAnd16U): - case uint16_t(ThreadOp::I64AtomicAnd32U): - case uint16_t(ThreadOp::I32AtomicOr): - case uint16_t(ThreadOp::I64AtomicOr): - case uint16_t(ThreadOp::I32AtomicOr8U): - case uint16_t(ThreadOp::I32AtomicOr16U): - case uint16_t(ThreadOp::I64AtomicOr8U): - case uint16_t(ThreadOp::I64AtomicOr16U): - case uint16_t(ThreadOp::I64AtomicOr32U): - case uint16_t(ThreadOp::I32AtomicXor): - case uint16_t(ThreadOp::I64AtomicXor): - case uint16_t(ThreadOp::I32AtomicXor8U): - case uint16_t(ThreadOp::I32AtomicXor16U): - case uint16_t(ThreadOp::I64AtomicXor8U): - case uint16_t(ThreadOp::I64AtomicXor16U): - case uint16_t(ThreadOp::I64AtomicXor32U): - case uint16_t(ThreadOp::I32AtomicXchg): - case uint16_t(ThreadOp::I64AtomicXchg): - case uint16_t(ThreadOp::I32AtomicXchg8U): - case uint16_t(ThreadOp::I32AtomicXchg16U): - case uint16_t(ThreadOp::I64AtomicXchg8U): - case uint16_t(ThreadOp::I64AtomicXchg16U): - case uint16_t(ThreadOp::I64AtomicXchg32U): - if (!AstDecodeAtomicRMW(c, ThreadOp(op.b1))) - return false; - break; - case uint16_t(ThreadOp::I32AtomicCmpXchg): - case uint16_t(ThreadOp::I64AtomicCmpXchg): - case uint16_t(ThreadOp::I32AtomicCmpXchg8U): - case uint16_t(ThreadOp::I32AtomicCmpXchg16U): - case uint16_t(ThreadOp::I64AtomicCmpXchg8U): - case uint16_t(ThreadOp::I64AtomicCmpXchg16U): - case uint16_t(ThreadOp::I64AtomicCmpXchg32U): - if (!AstDecodeAtomicCmpXchg(c, ThreadOp(op.b1))) - return false; - break; - default: - return c.iter().unrecognizedOpcode(&op); - } - break; - case uint16_t(Op::MozPrefix): - return c.iter().unrecognizedOpcode(&op); - default: - return c.iter().unrecognizedOpcode(&op); - } - - AstExpr* lastExpr = c.top().expr; - if (lastExpr) { - // If last node is a 'first' node, the offset must assigned to it - // last child. - if (lastExpr->kind() == AstExprKind::First) - lastExpr->as().exprs().back()->setOffset(exprOffset); - else - lastExpr->setOffset(exprOffset); - } - return true; -} - -static bool -AstDecodeFunctionBody(AstDecodeContext &c, uint32_t funcIndex, AstFunc** func) -{ - uint32_t offset = c.d.currentOffset(); - uint32_t bodySize; - if (!c.d.readVarU32(&bodySize)) - return c.d.fail("expected number of function body bytes"); - - if (c.d.bytesRemain() < bodySize) - return c.d.fail("function body length too big"); - - const uint8_t* bodyBegin = c.d.currentPosition(); - const uint8_t* bodyEnd = bodyBegin + bodySize; - - const FuncTypeWithId* funcType = c.env().funcTypes[funcIndex]; - - ValTypeVector locals; - if (!locals.appendAll(funcType->args())) - return false; - - if (!DecodeLocalEntries(c.d, ModuleKind::Wasm, c.env().gcTypesEnabled, &locals)) - return false; - - AstDecodeOpIter iter(c.env(), c.d); - c.startFunction(&iter, &locals, funcType->ret()); - - AstName funcName; - if (!GenerateName(c, AstName(u"func"), funcIndex, &funcName)) - return false; - - uint32_t numParams = funcType->args().length(); - uint32_t numLocals = locals.length(); - - AstValTypeVector vars(c.lifo); - for (uint32_t i = numParams; i < numLocals; i++) { - if (!vars.append(locals[i])) - return false; - } - - AstNameVector localsNames(c.lifo); - for (uint32_t i = 0; i < numLocals; i++) { - AstName varName; - if (!GenerateName(c, AstName(u"var"), i, &varName)) - return false; - if (!localsNames.append(varName)) - return false; - } - - if (!c.iter().readFunctionStart(funcType->ret())) - return false; - - if (!c.depths().append(c.exprs().length())) - return false; - - uint32_t endOffset = offset; - while (c.d.currentPosition() < bodyEnd) { - if (!AstDecodeExpr(c)) - return false; - - const AstDecodeStackItem& item = c.top(); - if (!item.expr) { // Op::End was found - c.popBack(); - break; - } - - endOffset = c.d.currentOffset(); - } - - AstExprVector body(c.lifo); - for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end(); i != e; ++i) { - if (!body.append(i->expr)) - return false; - } - c.exprs().shrinkTo(c.depths().popCopy()); - - if (!c.iter().readFunctionEnd(bodyEnd)) - return false; - - c.endFunction(); - - if (c.d.currentPosition() != bodyEnd) - return c.d.fail("function body length mismatch"); - - size_t funcTypeIndex = c.env().funcIndexToFuncTypeIndex(funcIndex); - - AstRef funcTypeRef; - if (!GenerateRef(c, AstName(u"type"), funcTypeIndex, &funcTypeRef)) - return false; - - *func = new(c.lifo) AstFunc(funcName, funcTypeRef, std::move(vars), std::move(localsNames), - std::move(body)); - if (!*func) - return false; - (*func)->setOffset(offset); - (*func)->setEndOffset(endOffset); - - return true; -} - -/*****************************************************************************/ -// wasm decoding and generation - -static bool -AstCreateTypes(AstDecodeContext& c) -{ - uint32_t typeIndexForNames = 0; - for (const TypeDef& td : c.env().types) { - if (td.isFuncType()) { - const FuncType& funcType = td.funcType(); - - AstValTypeVector args(c.lifo); - if (!args.appendAll(funcType.args())) - return false; - - AstFuncType ftNoName(std::move(args), funcType.ret()); - - AstName ftName; - if (!GenerateName(c, AstName(u"type"), typeIndexForNames, &ftName)) - return false; - - AstFuncType* astFuncType = new(c.lifo) AstFuncType(ftName, std::move(ftNoName)); - if (!astFuncType || !c.module().append(astFuncType)) - return false; - } else if (td.isStructType()) { - const StructType& st = td.structType(); - - AstValTypeVector fieldTypes(c.lifo); - if (!fieldTypes.appendAll(st.fields_)) - return false; - - AstNameVector fieldNames(c.lifo); - if (!fieldNames.resize(fieldTypes.length())) - return false; - - // The multiplication ensures that generated field names are unique - // within the module, though the resulting namespace is very sparse. - - for (size_t fieldIndex = 0; fieldIndex < fieldTypes.length(); fieldIndex++) { - size_t idx = (typeIndexForNames * MaxStructFields) + fieldIndex; - if (!GenerateName(c, AstName(u"f"), idx, &fieldNames[fieldIndex])) - return false; - } - - AstStructType stNoName(std::move(fieldNames), std::move(fieldTypes)); - - AstName stName; - if (!GenerateName(c, AstName(u"type"), typeIndexForNames, &stName)) - return false; - - AstStructType* astStruct = new(c.lifo) AstStructType(stName, std::move(stNoName)); - if (!astStruct || !c.module().append(astStruct)) - return false; - } else { - MOZ_CRASH(); - } - typeIndexForNames++; - } - - return true; -} - -static bool -ToAstName(AstDecodeContext& c, const char* name, AstName* out) -{ - size_t len = strlen(name); - char16_t* buffer = static_cast(c.lifo.alloc(len * sizeof(char16_t))); - if (!buffer) - return false; - - for (size_t i = 0; i < len; i++) - buffer[i] = name[i]; - - *out = AstName(buffer, len); - return true; -} - -static bool -AstCreateImports(AstDecodeContext& c) -{ - size_t lastFunc = 0; - size_t lastGlobal = 0; - size_t lastTable = 0; - size_t lastMemory = 0; - - Maybe memory; - if (c.env().usesMemory()) { - memory = Some(Limits(c.env().minMemoryLength, - c.env().maxMemoryLength, - c.env().memoryUsage == MemoryUsage::Shared - ? Shareable::True - : Shareable::False)); - } - - for (size_t importIndex = 0; importIndex < c.env().imports.length(); importIndex++) { - const Import& import = c.env().imports[importIndex]; - - AstName moduleName; - if (!ToAstName(c, import.module.get(), &moduleName)) - return false; - - AstName fieldName; - if (!ToAstName(c, import.field.get(), &fieldName)) - return false; - - AstImport* ast = nullptr; - switch (import.kind) { - case DefinitionKind::Function: { - AstName importName; - if (!GenerateName(c, AstName(u"import"), lastFunc, &importName)) - return false; - - size_t funcTypeIndex = c.env().funcIndexToFuncTypeIndex(lastFunc); - - AstRef funcTypeRef; - if (!GenerateRef(c, AstName(u"type"), funcTypeIndex, &funcTypeRef)) - return false; - - ast = new(c.lifo) AstImport(importName, moduleName, fieldName, funcTypeRef); - lastFunc++; - break; - } - case DefinitionKind::Global: { - AstName importName; - if (!GenerateName(c, AstName(u"global"), lastGlobal, &importName)) - return false; - - const GlobalDesc& global = c.env().globals[lastGlobal]; - ValType type = global.type(); - bool isMutable = global.isMutable(); - - ast = new(c.lifo) AstImport(importName, moduleName, fieldName, - AstGlobal(importName, type, isMutable)); - lastGlobal++; - break; - } - case DefinitionKind::Table: { - AstName importName; - if (!GenerateName(c, AstName(u"table"), lastTable, &importName)) - return false; - - ast = new(c.lifo) AstImport(importName, moduleName, fieldName, DefinitionKind::Table, - c.env().tables[lastTable].limits); - lastTable++; - break; - } - case DefinitionKind::Memory: { - AstName importName; - if (!GenerateName(c, AstName(u"memory"), lastMemory, &importName)) - return false; - - ast = new(c.lifo) AstImport(importName, moduleName, fieldName, DefinitionKind::Memory, - *memory); - lastMemory++; - break; - } - } - - if (!ast || !c.module().append(ast)) - return false; - } - - return true; -} - -static bool -AstCreateTables(AstDecodeContext& c) -{ - size_t numImported = c.module().tables().length(); - - for (size_t i = numImported; i < c.env().tables.length(); i++) { - AstName name; - if (!GenerateName(c, AstName(u"table"), i, &name)) - return false; - if (!c.module().addTable(name, c.env().tables[i].limits)) - return false; - } - - return true; -} - -static bool -AstCreateMemory(AstDecodeContext& c) -{ - bool importedMemory = !!c.module().memories().length(); - if (!c.env().usesMemory() || importedMemory) - return true; - - AstName name; - if (!GenerateName(c, AstName(u"memory"), c.module().memories().length(), &name)) - return false; - - return c.module().addMemory(name, Limits(c.env().minMemoryLength, - c.env().maxMemoryLength, - c.env().memoryUsage == MemoryUsage::Shared - ? Shareable::True - : Shareable::False)); -} - -static AstExpr* -ToAstExpr(AstDecodeContext& c, const InitExpr& initExpr) -{ - switch (initExpr.kind()) { - case InitExpr::Kind::Constant: { - return new(c.lifo) AstConst(Val(initExpr.val())); - } - case InitExpr::Kind::GetGlobal: { - AstRef globalRef; - if (!GenerateRef(c, AstName(u"global"), initExpr.globalIndex(), &globalRef)) - return nullptr; - return new(c.lifo) AstGetGlobal(globalRef); - } - } - return nullptr; -} - -static bool -AstCreateGlobals(AstDecodeContext& c) -{ - for (uint32_t i = 0; i < c.env().globals.length(); i++) { - const GlobalDesc& global = c.env().globals[i]; - if (global.isImport()) - continue; - - AstName name; - if (!GenerateName(c, AstName(u"global"), i, &name)) - return false; - - AstExpr* init = global.isConstant() - ? new(c.lifo) AstConst(global.constantValue()) - : ToAstExpr(c, global.initExpr()); - if (!init) - return false; - - auto* g = new(c.lifo) AstGlobal(name, global.type(), global.isMutable(), Some(init)); - if (!g || !c.module().append(g)) - return false; - } - - return true; -} - -static bool -AstCreateExports(AstDecodeContext& c) -{ - for (const Export& exp : c.env().exports) { - size_t index; - switch (exp.kind()) { - case DefinitionKind::Function: index = exp.funcIndex(); break; - case DefinitionKind::Global: index = exp.globalIndex(); break; - case DefinitionKind::Memory: index = 0; break; - case DefinitionKind::Table: index = 0; break; - } - - AstName name; - if (!ToAstName(c, exp.fieldName(), &name)) - return false; - - AstExport* e = new(c.lifo) AstExport(name, exp.kind(), AstRef(index)); - if (!e || !c.module().append(e)) - return false; - } - - return true; -} - -static bool -AstCreateStartFunc(AstDecodeContext &c) -{ - if (!c.env().startFuncIndex) - return true; - - AstRef funcRef; - if (!GenerateFuncRef(c, *c.env().startFuncIndex, &funcRef)) - return false; - - c.module().setStartFunc(AstStartFunc(funcRef)); - return true; -} - -static bool -AstCreateElems(AstDecodeContext &c) -{ - for (const ElemSegment& seg : c.env().elemSegments) { - AstRefVector elems(c.lifo); - if (!elems.reserve(seg.elemFuncIndices.length())) - return false; - - for (uint32_t i : seg.elemFuncIndices) - elems.infallibleAppend(AstRef(i)); - - AstExpr* offset = ToAstExpr(c, seg.offset); - if (!offset) - return false; - - AstElemSegment* segment = new(c.lifo) AstElemSegment(offset, std::move(elems)); - if (!segment || !c.module().append(segment)) - return false; - } - - return true; -} - -static bool -AstDecodeEnvironment(AstDecodeContext& c) -{ - if (!DecodeModuleEnvironment(c.d, &c.env())) - return false; - - if (!AstCreateTypes(c)) - return false; - - if (!AstCreateImports(c)) - return false; - - if (!AstCreateTables(c)) - return false; - - if (!AstCreateMemory(c)) - return false; - - if (!AstCreateGlobals(c)) - return false; - - if (!AstCreateExports(c)) - return false; - - if (!AstCreateStartFunc(c)) - return false; - - if (!AstCreateElems(c)) - return false; - - return true; -} - -static bool -AstDecodeCodeSection(AstDecodeContext& c) -{ - if (!c.env().codeSection) { - if (c.env().numFuncDefs() != 0) - return c.d.fail("expected function bodies"); - return true; - } - - uint32_t numFuncBodies; - if (!c.d.readVarU32(&numFuncBodies)) - return c.d.fail("expected function body count"); - - if (numFuncBodies != c.env().numFuncDefs()) - return c.d.fail("function body count does not match function signature count"); - - for (uint32_t funcDefIndex = 0; funcDefIndex < numFuncBodies; funcDefIndex++) { - AstFunc* func; - if (!AstDecodeFunctionBody(c, c.module().numFuncImports() + funcDefIndex, &func)) - return false; - if (!c.module().append(func)) - return false; - } - - return c.d.finishSection(*c.env().codeSection, "code"); -} - -// Number of bytes to display in a single fragment of a data section (per line). -static const size_t WRAP_DATA_BYTES = 30; - -static bool -AstDecodeModuleTail(AstDecodeContext& c) -{ - MOZ_ASSERT(c.module().memories().length() <= 1, "at most one memory in MVP"); - - if (!DecodeModuleTail(c.d, &c.env())) - return false; - - for (DataSegment& s : c.env().dataSegments) { - char16_t* buffer = static_cast(c.lifo.alloc(s.length * sizeof(char16_t))); - if (!buffer) - return false; - - const uint8_t* src = c.d.begin() + s.bytecodeOffset; - for (size_t i = 0; i < s.length; i++) - buffer[i] = src[i]; - - AstExpr* offset = ToAstExpr(c, s.offset); - if (!offset) - return false; - - AstNameVector fragments(c.lifo); - for (size_t start = 0; start < s.length; start += WRAP_DATA_BYTES) { - AstName name(buffer + start, Min(WRAP_DATA_BYTES, s.length - start)); - if (!fragments.append(name)) - return false; - } - - AstDataSegment* segment = new(c.lifo) AstDataSegment(offset, std::move(fragments)); - if (!segment || !c.module().append(segment)) - return false; - } - - return true; -} - -bool -wasm::BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length, LifoAlloc& lifo, - AstModule** module) -{ - AstModule* result = new(lifo) AstModule(lifo); - if (!result || !result->init()) - return false; - - UniqueChars error; - Decoder d(bytes, bytes + length, 0, &error, nullptr, /* resilient */ true); - AstDecodeContext c(cx, lifo, d, *result, /* generateNames */ true, HasGcTypes::True); - - if (!AstDecodeEnvironment(c) || - !AstDecodeCodeSection(c) || - !AstDecodeModuleTail(c)) - { - if (error) { - JS_ReportErrorNumberUTF8(c.cx, GetErrorMessage, nullptr, JSMSG_WASM_COMPILE_ERROR, - error.get()); - return false; - } - ReportOutOfMemory(c.cx); - return false; - } - - MOZ_ASSERT(!error, "unreported error in decoding"); - - *module = result; - return true; -} diff --git a/js/src/wasm/WasmBinaryToAST.h b/js/src/wasm/WasmBinaryToAST.h deleted file mode 100644 index 320862dbbc7c..000000000000 --- a/js/src/wasm/WasmBinaryToAST.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * - * Copyright 2015 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef wasmbinarytoast_h -#define wasmbinarytoast_h - -#include "ds/LifoAlloc.h" - -#include "wasm/WasmAST.h" -#include "wasm/WasmTypes.h" - -namespace js { -namespace wasm { - -bool -BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length, LifoAlloc& lifo, - AstModule** module); - -} // end wasm namespace -} // end js namespace - -#endif // namespace wasmbinarytoast_h diff --git a/js/src/wasm/WasmBinaryToText.cpp b/js/src/wasm/WasmBinaryToText.cpp deleted file mode 100644 index 344cc12a62a7..000000000000 --- a/js/src/wasm/WasmBinaryToText.cpp +++ /dev/null @@ -1,2087 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * - * Copyright 2015 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "wasm/WasmBinaryToText.h" - -#include "jsnum.h" - -#include "util/StringBuffer.h" -#include "vm/ArrayBufferObject.h" -#include "wasm/WasmAST.h" -#include "wasm/WasmBinaryToAST.h" -#include "wasm/WasmDebug.h" -#include "wasm/WasmTextUtils.h" -#include "wasm/WasmTypes.h" - -using namespace js; -using namespace js::wasm; - -using mozilla::IsInfinite; -using mozilla::IsNaN; -using mozilla::IsNegativeZero; - -struct WasmRenderContext -{ - JSContext* cx; - AstModule* module; - WasmPrintBuffer& buffer; - uint32_t indent; - uint32_t currentFuncIndex; - - WasmRenderContext(JSContext* cx, AstModule* module, WasmPrintBuffer& buffer) - : cx(cx), - module(module), - buffer(buffer), - indent(0), - currentFuncIndex(0) - {} - - StringBuffer& sb() { return buffer.stringBuffer(); } -}; - -/*****************************************************************************/ -// utilities - -// Return true on purpose, so that we have a useful error message to provide to -// the user. -static bool -Fail(WasmRenderContext& c, const char* msg) -{ - c.buffer.stringBuffer().clear(); - - return c.buffer.append("There was a problem when rendering the wasm text format: ") && - c.buffer.append(msg, strlen(msg)) && - c.buffer.append("\nYou should consider file a bug on Bugzilla in the " - "Core:::JavaScript Engine::JIT component at " - "https://bugzilla.mozilla.org/enter_bug.cgi."); -} - -static bool -RenderIndent(WasmRenderContext& c) -{ - for (uint32_t i = 0; i < c.indent; i++) { - if (!c.buffer.append(" ")) - return false; - } - return true; -} - -static bool -RenderInt32(WasmRenderContext& c, int32_t num) -{ - return NumberValueToStringBuffer(c.cx, Int32Value(num), c.sb()); -} - -static bool -RenderInt64(WasmRenderContext& c, int64_t num) -{ - if (num < 0 && !c.buffer.append("-")) - return false; - if (!num) - return c.buffer.append("0"); - return RenderInBase<10>(c.sb(), mozilla::Abs(num)); -} - -static bool -RenderDouble(WasmRenderContext& c, double d) -{ - if (IsNaN(d)) - return RenderNaN(c.sb(), d); - if (IsNegativeZero(d)) - return c.buffer.append("-0"); - if (IsInfinite(d)) { - if (d > 0) - return c.buffer.append("infinity"); - return c.buffer.append("-infinity"); - } - return NumberValueToStringBuffer(c.cx, DoubleValue(d), c.sb()); -} - -static bool -RenderFloat32(WasmRenderContext& c, float f) -{ - if (IsNaN(f)) - return RenderNaN(c.sb(), f); - return RenderDouble(c, double(f)); -} - -static bool -RenderEscapedString(WasmRenderContext& c, const AstName& s) -{ - size_t length = s.length(); - const char16_t* p = s.begin(); - for (size_t i = 0; i < length; i++) { - char16_t byte = p[i]; - switch (byte) { - case '\n': - if (!c.buffer.append("\\n")) - return false; - break; - case '\r': - if (!c.buffer.append("\\0d")) - return false; - break; - case '\t': - if (!c.buffer.append("\\t")) - return false; - break; - case '\f': - if (!c.buffer.append("\\0c")) - return false; - break; - case '\b': - if (!c.buffer.append("\\08")) - return false; - break; - case '\\': - if (!c.buffer.append("\\\\")) - return false; - break; - case '"' : - if (!c.buffer.append("\\\"")) - return false; - break; - case '\'': - if (!c.buffer.append("\\'")) - return false; - break; - default: - if (byte >= 32 && byte < 127) { - if (!c.buffer.append((char)byte)) - return false; - } else { - char digit1 = byte / 16, digit2 = byte % 16; - if (!c.buffer.append("\\")) - return false; - if (!c.buffer.append((char)(digit1 < 10 ? digit1 + '0' : digit1 + 'a' - 10))) - return false; - if (!c.buffer.append((char)(digit2 < 10 ? digit2 + '0' : digit2 + 'a' - 10))) - return false; - } - break; - } - } - return true; -} - -static bool -RenderExprType(WasmRenderContext& c, ExprType type) -{ - switch (type) { - case ExprType::Void: return true; // ignoring void - case ExprType::I32: return c.buffer.append("i32"); - case ExprType::I64: return c.buffer.append("i64"); - case ExprType::F32: return c.buffer.append("f32"); - case ExprType::F64: return c.buffer.append("f64"); - case ExprType::AnyRef: return c.buffer.append("anyref"); - default:; - } - - MOZ_CRASH("bad type"); -} - -static bool -RenderValType(WasmRenderContext& c, ValType type) -{ - return RenderExprType(c, ToExprType(type)); -} - -static bool -RenderName(WasmRenderContext& c, const AstName& name) -{ - return c.buffer.append(name.begin(), name.end()); -} - -static bool -RenderNonemptyName(WasmRenderContext& c, const AstName& name) -{ - return name.empty() || (RenderName(c, name) && c.buffer.append(' ')); -} - -static bool -RenderRef(WasmRenderContext& c, const AstRef& ref) -{ - if (ref.name().empty()) - return RenderInt32(c, ref.index()); - - return RenderName(c, ref.name()); -} - -static bool -RenderBlockNameAndSignature(WasmRenderContext& c, const AstName& name, ExprType type) -{ - if (!name.empty()) { - if (!c.buffer.append(' ')) - return false; - - if (!RenderName(c, name)) - return false; - } - - if (!IsVoid(type)) { - if (!c.buffer.append(' ')) - return false; - - if (!RenderExprType(c, type)) - return false; - } - - return true; -} - -static bool -RenderExpr(WasmRenderContext& c, AstExpr& expr, bool newLine = true); - -/*****************************************************************************/ -// binary format parsing and rendering - -static bool -RenderNop(WasmRenderContext& c, AstNop& nop) -{ - if (!RenderIndent(c)) - return false; - return c.buffer.append("nop"); -} - -static bool -RenderDrop(WasmRenderContext& c, AstDrop& drop) -{ - if (!RenderExpr(c, drop.value())) - return false; - - if (!RenderIndent(c)) - return false; - return c.buffer.append("drop"); -} - -static bool -RenderUnreachable(WasmRenderContext& c, AstUnreachable& unreachable) -{ - if (!RenderIndent(c)) - return false; - return c.buffer.append("unreachable"); -} - -static bool -RenderCallArgs(WasmRenderContext& c, const AstExprVector& args) -{ - for (uint32_t i = 0; i < args.length(); i++) { - if (!RenderExpr(c, *args[i])) - return false; - } - - return true; -} - -static bool -RenderCall(WasmRenderContext& c, AstCall& call) -{ - if (!RenderCallArgs(c, call.args())) - return false; - - if (!RenderIndent(c)) - return false; - - if (call.op() == Op::Call) { - if (!c.buffer.append("call ")) - return false; - } else { - return Fail(c, "unexpected operator"); - } - - return RenderRef(c, call.func()); -} - -static bool -RenderCallIndirect(WasmRenderContext& c, AstCallIndirect& call) -{ - if (!RenderCallArgs(c, call.args())) - return false; - - if (!RenderExpr(c, *call.index())) - return false; - - if (!RenderIndent(c)) - return false; - - if (!c.buffer.append("call_indirect ")) - return false; - return RenderRef(c, call.funcType()); -} - -static bool -RenderConst(WasmRenderContext& c, AstConst& cst) -{ - if (!RenderIndent(c)) - return false; - - if (!RenderValType(c, cst.val().type())) - return false; - if (!c.buffer.append(".const ")) - return false; - - switch (ToExprType(cst.val().type())) { - case ExprType::I32: - return RenderInt32(c, (int32_t)cst.val().i32()); - case ExprType::I64: - return RenderInt64(c, (int64_t)cst.val().i64()); - case ExprType::F32: - return RenderFloat32(c, cst.val().f32()); - case ExprType::F64: - return RenderDouble(c, cst.val().f64()); - default: - break; - } - - return false; -} - -static bool -RenderGetLocal(WasmRenderContext& c, AstGetLocal& gl) -{ - if (!RenderIndent(c)) - return false; - - if (!c.buffer.append("get_local ")) - return false; - return RenderRef(c, gl.local()); -} - -static bool -RenderSetLocal(WasmRenderContext& c, AstSetLocal& sl) -{ - if (!RenderExpr(c, sl.value())) - return false; - - if (!RenderIndent(c)) - return false; - - if (!c.buffer.append("set_local ")) - return false; - return RenderRef(c, sl.local()); -} - -static bool -RenderTeeLocal(WasmRenderContext& c, AstTeeLocal& tl) -{ - if (!RenderExpr(c, tl.value())) - return false; - - if (!RenderIndent(c)) - return false; - - if (!c.buffer.append("tee_local ")) - return false; - return RenderRef(c, tl.local()); -} - -static bool -RenderGetGlobal(WasmRenderContext& c, AstGetGlobal& gg) -{ - if (!RenderIndent(c)) - return false; - - if (!c.buffer.append("get_global ")) - return false; - return RenderRef(c, gg.global()); -} - -static bool -RenderSetGlobal(WasmRenderContext& c, AstSetGlobal& sg) -{ - if (!RenderExpr(c, sg.value())) - return false; - - if (!RenderIndent(c)) - return false; - - if (!c.buffer.append("set_global ")) - return false; - return RenderRef(c, sg.global()); -} - -static bool -RenderExprList(WasmRenderContext& c, const AstExprVector& exprs, uint32_t startAt = 0) -{ - for (uint32_t i = startAt; i < exprs.length(); i++) { - if (!RenderExpr(c, *exprs[i])) - return false; - } - return true; -} - -static bool -RenderBlock(WasmRenderContext& c, AstBlock& block, bool isInline = false) -{ - if (!isInline && !RenderIndent(c)) - return false; - - if (block.op() == Op::Block) { - if (!c.buffer.append("block")) - return false; - } else if (block.op() == Op::Loop) { - if (!c.buffer.append("loop")) - return false; - } else { - return Fail(c, "unexpected block kind"); - } - - if (!RenderBlockNameAndSignature(c, block.name(), block.type())) - return false; - - uint32_t startAtSubExpr = 0; - - // If there is a stack of blocks, print them all inline. - if (block.op() == Op::Block && - block.exprs().length() && - block.exprs()[0]->kind() == AstExprKind::Block && - block.exprs()[0]->as().op() == Op::Block) - { - if (!c.buffer.append(' ')) - return false; - - // Render the first inner expr (block) at the same indent level, but - // next instructions one level further. - if (!RenderBlock(c, block.exprs()[0]->as(), /* isInline */ true)) - return false; - - startAtSubExpr = 1; - } - - if (!c.buffer.append('\n')) - return false; - - c.indent++; - if (!RenderExprList(c, block.exprs(), startAtSubExpr)) - return false; - c.indent--; - - return RenderIndent(c) && - c.buffer.append("end ") && - RenderName(c, block.name()); -} - -static bool -RenderFirst(WasmRenderContext& c, AstFirst& first) -{ - return RenderExprList(c, first.exprs()); -} - -static bool -RenderCurrentMemory(WasmRenderContext& c, AstCurrentMemory& cm) -{ - if (!RenderIndent(c)) - return false; - - return c.buffer.append("current_memory\n"); -} - -static bool -RenderGrowMemory(WasmRenderContext& c, AstGrowMemory& gm) -{ - if (!RenderExpr(c, *gm.operand())) - return false; - - if (!RenderIndent(c)) - return false; - - return c.buffer.append("grow_memory\n"); -} - -static bool -RenderUnaryOperator(WasmRenderContext& c, AstUnaryOperator& unary) -{ - if (!RenderExpr(c, *unary.operand())) - return false; - - if (!RenderIndent(c)) - return false; - - const char* opStr; - switch (unary.op()) { - case Op::I32Eqz: opStr = "i32.eqz"; break; - case Op::I32Clz: opStr = "i32.clz"; break; - case Op::I32Ctz: opStr = "i32.ctz"; break; - case Op::I32Popcnt: opStr = "i32.popcnt"; break; - case Op::I64Clz: opStr = "i64.clz"; break; - case Op::I64Ctz: opStr = "i64.ctz"; break; - case Op::I64Popcnt: opStr = "i64.popcnt"; break; - case Op::F32Abs: opStr = "f32.abs"; break; - case Op::F32Neg: opStr = "f32.neg"; break; - case Op::F32Ceil: opStr = "f32.ceil"; break; - case Op::F32Floor: opStr = "f32.floor"; break; - case Op::F32Sqrt: opStr = "f32.sqrt"; break; - case Op::F32Trunc: opStr = "f32.trunc"; break; - case Op::F32Nearest: opStr = "f32.nearest"; break; - case Op::F64Abs: opStr = "f64.abs"; break; - case Op::F64Neg: opStr = "f64.neg"; break; - case Op::F64Ceil: opStr = "f64.ceil"; break; - case Op::F64Floor: opStr = "f64.floor"; break; - case Op::F64Nearest: opStr = "f64.nearest"; break; - case Op::F64Sqrt: opStr = "f64.sqrt"; break; - case Op::F64Trunc: opStr = "f64.trunc"; break; - default: return Fail(c, "unexpected unary operator"); - } - - return c.buffer.append(opStr, strlen(opStr)); -} - -static bool -RenderBinaryOperator(WasmRenderContext& c, AstBinaryOperator& binary) -{ - if (!RenderExpr(c, *binary.lhs())) - return false; - if (!RenderExpr(c, *binary.rhs())) - return false; - - if (!RenderIndent(c)) - return false; - - const char* opStr; - switch (binary.op()) { - case Op::I32Add: opStr = "i32.add"; break; - case Op::I32Sub: opStr = "i32.sub"; break; - case Op::I32Mul: opStr = "i32.mul"; break; - case Op::I32DivS: opStr = "i32.div_s"; break; - case Op::I32DivU: opStr = "i32.div_u"; break; - case Op::I32RemS: opStr = "i32.rem_s"; break; - case Op::I32RemU: opStr = "i32.rem_u"; break; - case Op::I32And: opStr = "i32.and"; break; - case Op::I32Or: opStr = "i32.or"; break; - case Op::I32Xor: opStr = "i32.xor"; break; - case Op::I32Shl: opStr = "i32.shl"; break; - case Op::I32ShrS: opStr = "i32.shr_s"; break; - case Op::I32ShrU: opStr = "i32.shr_u"; break; - case Op::I32Rotl: opStr = "i32.rotl"; break; - case Op::I32Rotr: opStr = "i32.rotr"; break; - case Op::I64Add: opStr = "i64.add"; break; - case Op::I64Sub: opStr = "i64.sub"; break; - case Op::I64Mul: opStr = "i64.mul"; break; - case Op::I64DivS: opStr = "i64.div_s"; break; - case Op::I64DivU: opStr = "i64.div_u"; break; - case Op::I64RemS: opStr = "i64.rem_s"; break; - case Op::I64RemU: opStr = "i64.rem_u"; break; - case Op::I64And: opStr = "i64.and"; break; - case Op::I64Or: opStr = "i64.or"; break; - case Op::I64Xor: opStr = "i64.xor"; break; - case Op::I64Shl: opStr = "i64.shl"; break; - case Op::I64ShrS: opStr = "i64.shr_s"; break; - case Op::I64ShrU: opStr = "i64.shr_u"; break; - case Op::I64Rotl: opStr = "i64.rotl"; break; - case Op::I64Rotr: opStr = "i64.rotr"; break; - case Op::F32Add: opStr = "f32.add"; break; - case Op::F32Sub: opStr = "f32.sub"; break; - case Op::F32Mul: opStr = "f32.mul"; break; - case Op::F32Div: opStr = "f32.div"; break; - case Op::F32Min: opStr = "f32.min"; break; - case Op::F32Max: opStr = "f32.max"; break; - case Op::F32CopySign: opStr = "f32.copysign"; break; - case Op::F64Add: opStr = "f64.add"; break; - case Op::F64Sub: opStr = "f64.sub"; break; - case Op::F64Mul: opStr = "f64.mul"; break; - case Op::F64Div: opStr = "f64.div"; break; - case Op::F64Min: opStr = "f64.min"; break; - case Op::F64Max: opStr = "f64.max"; break; - case Op::F64CopySign: opStr = "f64.copysign"; break; - default: return Fail(c, "unexpected binary operator"); - } - - return c.buffer.append(opStr, strlen(opStr)); -} - -static bool -RenderTernaryOperator(WasmRenderContext& c, AstTernaryOperator& ternary) -{ - if (!RenderExpr(c, *ternary.op0())) - return false; - if (!RenderExpr(c, *ternary.op1())) - return false; - if (!RenderExpr(c, *ternary.op2())) - return false; - - if (!RenderIndent(c)) - return false; - - const char* opStr; - switch (ternary.op()) { - case Op::Select: opStr = "select"; break; - default: return Fail(c, "unexpected ternary operator"); - } - - return c.buffer.append(opStr, strlen(opStr)); -} - -static bool -RenderComparisonOperator(WasmRenderContext& c, AstComparisonOperator& comp) -{ - if (!RenderExpr(c, *comp.lhs())) - return false; - if (!RenderExpr(c, *comp.rhs())) - return false; - - if (!RenderIndent(c)) - return false; - - const char* opStr; - switch (comp.op()) { - case Op::I32Eq: opStr = "i32.eq"; break; - case Op::I32Ne: opStr = "i32.ne"; break; - case Op::I32LtS: opStr = "i32.lt_s"; break; - case Op::I32LtU: opStr = "i32.lt_u"; break; - case Op::I32LeS: opStr = "i32.le_s"; break; - case Op::I32LeU: opStr = "i32.le_u"; break; - case Op::I32GtS: opStr = "i32.gt_s"; break; - case Op::I32GtU: opStr = "i32.gt_u"; break; - case Op::I32GeS: opStr = "i32.ge_s"; break; - case Op::I32GeU: opStr = "i32.ge_u"; break; - case Op::I64Eq: opStr = "i64.eq"; break; - case Op::I64Ne: opStr = "i64.ne"; break; - case Op::I64LtS: opStr = "i64.lt_s"; break; - case Op::I64LtU: opStr = "i64.lt_u"; break; - case Op::I64LeS: opStr = "i64.le_s"; break; - case Op::I64LeU: opStr = "i64.le_u"; break; - case Op::I64GtS: opStr = "i64.gt_s"; break; - case Op::I64GtU: opStr = "i64.gt_u"; break; - case Op::I64GeS: opStr = "i64.ge_s"; break; - case Op::I64GeU: opStr = "i64.ge_u"; break; - case Op::F32Eq: opStr = "f32.eq"; break; - case Op::F32Ne: opStr = "f32.ne"; break; - case Op::F32Lt: opStr = "f32.lt"; break; - case Op::F32Le: opStr = "f32.le"; break; - case Op::F32Gt: opStr = "f32.gt"; break; - case Op::F32Ge: opStr = "f32.ge"; break; - case Op::F64Eq: opStr = "f64.eq"; break; - case Op::F64Ne: opStr = "f64.ne"; break; - case Op::F64Lt: opStr = "f64.lt"; break; - case Op::F64Le: opStr = "f64.le"; break; - case Op::F64Gt: opStr = "f64.gt"; break; - case Op::F64Ge: opStr = "f64.ge"; break; - default: return Fail(c, "unexpected comparison operator"); - } - - return c.buffer.append(opStr, strlen(opStr)); -} - -static bool -RenderConversionOperator(WasmRenderContext& c, AstConversionOperator& conv) -{ - if (!RenderExpr(c, *conv.operand())) - return false; - - if (!RenderIndent(c)) - return false; - - const char* opStr; - switch (conv.op()) { - case Op::I32WrapI64: opStr = "i32.wrap/i64"; break; - case Op::I32TruncSF32: opStr = "i32.trunc_s/f32"; break; - case Op::I32TruncUF32: opStr = "i32.trunc_u/f32"; break; - case Op::I32ReinterpretF32: opStr = "i32.reinterpret/f32"; break; - case Op::I32TruncSF64: opStr = "i32.trunc_s/f64"; break; - case Op::I32TruncUF64: opStr = "i32.trunc_u/f64"; break; - case Op::I64ExtendSI32: opStr = "i64.extend_s/i32"; break; - case Op::I64ExtendUI32: opStr = "i64.extend_u/i32"; break; - case Op::I64TruncSF32: opStr = "i64.trunc_s/f32"; break; - case Op::I64TruncUF32: opStr = "i64.trunc_u/f32"; break; - case Op::I64TruncSF64: opStr = "i64.trunc_s/f64"; break; - case Op::I64TruncUF64: opStr = "i64.trunc_u/f64"; break; - case Op::I64ReinterpretF64: opStr = "i64.reinterpret/f64"; break; - case Op::F32ConvertSI32: opStr = "f32.convert_s/i32"; break; - case Op::F32ConvertUI32: opStr = "f32.convert_u/i32"; break; - case Op::F32ReinterpretI32: opStr = "f32.reinterpret/i32"; break; - case Op::F32ConvertSI64: opStr = "f32.convert_s/i64"; break; - case Op::F32ConvertUI64: opStr = "f32.convert_u/i64"; break; - case Op::F32DemoteF64: opStr = "f32.demote/f64"; break; - case Op::F64ConvertSI32: opStr = "f64.convert_s/i32"; break; - case Op::F64ConvertUI32: opStr = "f64.convert_u/i32"; break; - case Op::F64ConvertSI64: opStr = "f64.convert_s/i64"; break; - case Op::F64ConvertUI64: opStr = "f64.convert_u/i64"; break; - case Op::F64ReinterpretI64: opStr = "f64.reinterpret/i64"; break; - case Op::F64PromoteF32: opStr = "f64.promote/f32"; break; - case Op::I32Extend8S: opStr = "i32.extend8_s"; break; - case Op::I32Extend16S: opStr = "i32.extend16_s"; break; - case Op::I64Extend8S: opStr = "i64.extend8_s"; break; - case Op::I64Extend16S: opStr = "i64.extend16_s"; break; - case Op::I64Extend32S: opStr = "i64.extend32_s"; break; - case Op::I32Eqz: opStr = "i32.eqz"; break; - case Op::I64Eqz: opStr = "i64.eqz"; break; - default: return Fail(c, "unexpected conversion operator"); - } - return c.buffer.append(opStr, strlen(opStr)); -} - -#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS -static bool -RenderExtraConversionOperator(WasmRenderContext& c, AstExtraConversionOperator& conv) -{ - if (!RenderExpr(c, *conv.operand())) - return false; - - if (!RenderIndent(c)) - return false; - - const char* opStr; - switch (conv.op()) { - case MiscOp::I32TruncSSatF32: opStr = "i32.trunc_s:sat/f32"; break; - case MiscOp::I32TruncUSatF32: opStr = "i32.trunc_u:sat/f32"; break; - case MiscOp::I32TruncSSatF64: opStr = "i32.trunc_s:sat/f64"; break; - case MiscOp::I32TruncUSatF64: opStr = "i32.trunc_u:sat/f64"; break; - case MiscOp::I64TruncSSatF32: opStr = "i64.trunc_s:sat/f32"; break; - case MiscOp::I64TruncUSatF32: opStr = "i64.trunc_u:sat/f32"; break; - case MiscOp::I64TruncSSatF64: opStr = "i64.trunc_s:sat/f64"; break; - case MiscOp::I64TruncUSatF64: opStr = "i64.trunc_u:sat/f64"; break; - default: return Fail(c, "unexpected extra conversion operator"); - } - return c.buffer.append(opStr, strlen(opStr)); -} -#endif - -static bool -RenderIf(WasmRenderContext& c, AstIf& if_) -{ - if (!RenderExpr(c, if_.cond())) - return false; - - if (!RenderIndent(c)) - return false; - - if (!c.buffer.append("if")) - return false; - if (!RenderBlockNameAndSignature(c, if_.name(), if_.type())) - return false; - if (!c.buffer.append('\n')) - return false; - - c.indent++; - if (!RenderExprList(c, if_.thenExprs())) - return false; - c.indent--; - - if (if_.hasElse()) { - if (!RenderIndent(c)) - return false; - - if (!c.buffer.append("else\n")) - return false; - - c.indent++; - if (!RenderExprList(c, if_.elseExprs())) - return false; - c.indent--; - } - - if (!RenderIndent(c)) - return false; - - return c.buffer.append("end"); -} - -static bool -RenderLoadStoreBase(WasmRenderContext& c, const AstLoadStoreAddress& lsa) -{ - return RenderExpr(c, lsa.base()); -} - -static bool -RenderLoadStoreAddress(WasmRenderContext& c, const AstLoadStoreAddress& lsa, uint32_t defaultAlignLog2) -{ - if (lsa.offset() != 0) { - if (!c.buffer.append(" offset=")) - return false; - if (!RenderInt32(c, lsa.offset())) - return false; - } - - uint32_t alignLog2 = lsa.flags(); - if (defaultAlignLog2 != alignLog2) { - if (!c.buffer.append(" align=")) - return false; - if (!RenderInt32(c, 1 << alignLog2)) - return false; - } - - return true; -} - -static bool -RenderLoad(WasmRenderContext& c, AstLoad& load) -{ - if (!RenderLoadStoreBase(c, load.address())) - return false; - - if (!RenderIndent(c)) - return false; - - uint32_t defaultAlignLog2; - switch (load.op()) { - case Op::I32Load8S: - if (!c.buffer.append("i32.load8_s")) - return false; - defaultAlignLog2 = 0; - break; - case Op::I64Load8S: - if (!c.buffer.append("i64.load8_s")) - return false; - defaultAlignLog2 = 0; - break; - case Op::I32Load8U: - if (!c.buffer.append("i32.load8_u")) - return false; - defaultAlignLog2 = 0; - break; - case Op::I64Load8U: - if (!c.buffer.append("i64.load8_u")) - return false; - defaultAlignLog2 = 0; - break; - case Op::I32Load16S: - if (!c.buffer.append("i32.load16_s")) - return false; - defaultAlignLog2 = 1; - break; - case Op::I64Load16S: - if (!c.buffer.append("i64.load16_s")) - return false; - defaultAlignLog2 = 1; - break; - case Op::I32Load16U: - if (!c.buffer.append("i32.load16_u")) - return false; - defaultAlignLog2 = 1; - break; - case Op::I64Load16U: - if (!c.buffer.append("i64.load16_u")) - return false; - defaultAlignLog2 = 1; - break; - case Op::I64Load32S: - if (!c.buffer.append("i64.load32_s")) - return false; - defaultAlignLog2 = 2; - break; - case Op::I64Load32U: - if (!c.buffer.append("i64.load32_u")) - return false; - defaultAlignLog2 = 2; - break; - case Op::I32Load: - if (!c.buffer.append("i32.load")) - return false; - defaultAlignLog2 = 2; - break; - case Op::I64Load: - if (!c.buffer.append("i64.load")) - return false; - defaultAlignLog2 = 3; - break; - case Op::F32Load: - if (!c.buffer.append("f32.load")) - return false; - defaultAlignLog2 = 2; - break; - case Op::F64Load: - if (!c.buffer.append("f64.load")) - return false; - defaultAlignLog2 = 3; - break; - default: - return Fail(c, "unexpected load operator"); - } - - return RenderLoadStoreAddress(c, load.address(), defaultAlignLog2); -} - -static bool -RenderStore(WasmRenderContext& c, AstStore& store) -{ - if (!RenderLoadStoreBase(c, store.address())) - return false; - - if (!RenderExpr(c, store.value())) - return false; - - if (!RenderIndent(c)) - return false; - - uint32_t defaultAlignLog2; - switch (store.op()) { - case Op::I32Store8: - if (!c.buffer.append("i32.store8")) - return false; - defaultAlignLog2 = 0; - break; - case Op::I64Store8: - if (!c.buffer.append("i64.store8")) - return false; - defaultAlignLog2 = 0; - break; - case Op::I32Store16: - if (!c.buffer.append("i32.store16")) - return false; - defaultAlignLog2 = 1; - break; - case Op::I64Store16: - if (!c.buffer.append("i64.store16")) - return false; - defaultAlignLog2 = 1; - break; - case Op::I64Store32: - if (!c.buffer.append("i64.store32")) - return false; - defaultAlignLog2 = 2; - break; - case Op::I32Store: - if (!c.buffer.append("i32.store")) - return false; - defaultAlignLog2 = 2; - break; - case Op::I64Store: - if (!c.buffer.append("i64.store")) - return false; - defaultAlignLog2 = 3; - break; - case Op::F32Store: - if (!c.buffer.append("f32.store")) - return false; - defaultAlignLog2 = 2; - break; - case Op::F64Store: - if (!c.buffer.append("f64.store")) - return false; - defaultAlignLog2 = 3; - break; - default: - return Fail(c, "unexpected store operator"); - } - - return RenderLoadStoreAddress(c, store.address(), defaultAlignLog2); -} - -static bool -RenderBranch(WasmRenderContext& c, AstBranch& branch) -{ - Op op = branch.op(); - MOZ_ASSERT(op == Op::BrIf || op == Op::Br); - - if (op == Op::BrIf) { - if (!RenderExpr(c, branch.cond())) - return false; - } - - if (branch.maybeValue()) { - if (!RenderExpr(c, *(branch.maybeValue()))) - return false; - } - - if (!RenderIndent(c)) - return false; - - if (op == Op::BrIf ? !c.buffer.append("br_if ") : !c.buffer.append("br ")) - return false; - - return RenderRef(c, branch.target()); -} - -static bool -RenderBrTable(WasmRenderContext& c, AstBranchTable& table) -{ - if (table.maybeValue()) { - if (!RenderExpr(c, *(table.maybeValue()))) - return false; - } - - // Index - if (!RenderExpr(c, table.index())) - return false; - - if (!RenderIndent(c)) - return false; - - if (!c.buffer.append("br_table ")) - return false; - - uint32_t tableLength = table.table().length(); - for (uint32_t i = 0; i < tableLength; i++) { - if (!RenderRef(c, table.table()[i])) - return false; - - if (!c.buffer.append(" ")) - return false; - } - - return RenderRef(c, table.def()); -} - -static bool -RenderReturn(WasmRenderContext& c, AstReturn& ret) -{ - if (ret.maybeExpr()) { - if (!RenderExpr(c, *(ret.maybeExpr()))) - return false; - } - - if (!RenderIndent(c)) - return false; - - return c.buffer.append("return"); -} - -static bool -RenderAtomicCmpXchg(WasmRenderContext& c, AstAtomicCmpXchg& cmpxchg) -{ - if (!RenderLoadStoreBase(c, cmpxchg.address())) - return false; - - if (!RenderExpr(c, cmpxchg.expected())) - return false; - if (!RenderExpr(c, cmpxchg.replacement())) - return false; - - if (!RenderIndent(c)) - return false; - - const char* opname; - switch (cmpxchg.op()) { - case ThreadOp::I32AtomicCmpXchg8U: opname = "i32.atomic.rmw8_u.cmpxchg"; break; - case ThreadOp::I64AtomicCmpXchg8U: opname = "i64.atomic.rmw8_u.cmpxchg"; break; - case ThreadOp::I32AtomicCmpXchg16U: opname = "i32.atomic.rmw16_u.cmpxchg"; break; - case ThreadOp::I64AtomicCmpXchg16U: opname = "i64.atomic.rmw16_u.cmpxchg"; break; - case ThreadOp::I64AtomicCmpXchg32U: opname = "i64.atomic.rmw32_u.cmpxchg"; break; - case ThreadOp::I32AtomicCmpXchg: opname = "i32.atomic.rmw.cmpxchg"; break; - case ThreadOp::I64AtomicCmpXchg: opname = "i64.atomic.rmw.cmpxchg"; break; - default: return Fail(c, "unexpected cmpxchg operator"); - } - - if (!c.buffer.append(opname, strlen(opname))) - return false; - - return RenderLoadStoreAddress(c, cmpxchg.address(), 0); -} - -static bool -RenderAtomicLoad(WasmRenderContext& c, AstAtomicLoad& load) -{ - if (!RenderLoadStoreBase(c, load.address())) - return false; - - if (!RenderIndent(c)) - return false; - - const char* opname; - switch (load.op()) { - case ThreadOp::I32AtomicLoad8U: opname = "i32.atomic.load8_u"; break; - case ThreadOp::I64AtomicLoad8U: opname = "i64.atomic.load8_u"; break; - case ThreadOp::I32AtomicLoad16U: opname = "i32.atomic.load16_u"; break; - case ThreadOp::I64AtomicLoad16U: opname = "i64.atomic.load16_u"; break; - case ThreadOp::I64AtomicLoad32U: opname = "i64.atomic.load32_u"; break; - case ThreadOp::I32AtomicLoad: opname = "i32.atomic.load"; break; - case ThreadOp::I64AtomicLoad: opname = "i64.atomic.load"; break; - default: return Fail(c, "unexpected load operator"); - } - - if (!c.buffer.append(opname, strlen(opname))) - return false; - - return RenderLoadStoreAddress(c, load.address(), 0); -} - -static bool -RenderAtomicRMW(WasmRenderContext& c, AstAtomicRMW& rmw) -{ - if (!RenderLoadStoreBase(c, rmw.address())) - return false; - - if (!RenderExpr(c, rmw.value())) - return false; - - if (!RenderIndent(c)) - return false; - - const char* opname; - switch (rmw.op()) { - case ThreadOp::I32AtomicAdd: opname = "i32.atomic.rmw.add"; break; - case ThreadOp::I64AtomicAdd: opname = "i64.atomic.rmw.add"; break; - case ThreadOp::I32AtomicAdd8U: opname = "i32.atomic.rmw8_u.add"; break; - case ThreadOp::I32AtomicAdd16U: opname = "i32.atomic.rmw16_u.add"; break; - case ThreadOp::I64AtomicAdd8U: opname = "i64.atomic.rmw8_u.add"; break; - case ThreadOp::I64AtomicAdd16U: opname = "i64.atomic.rmw16_u.add"; break; - case ThreadOp::I64AtomicAdd32U: opname = "i64.atomic.rmw32_u.add"; break; - case ThreadOp::I32AtomicSub: opname = "i32.atomic.rmw.sub"; break; - case ThreadOp::I64AtomicSub: opname = "i64.atomic.rmw.sub"; break; - case ThreadOp::I32AtomicSub8U: opname = "i32.atomic.rmw8_u.sub"; break; - case ThreadOp::I32AtomicSub16U: opname = "i32.atomic.rmw16_u.sub"; break; - case ThreadOp::I64AtomicSub8U: opname = "i64.atomic.rmw8_u.sub"; break; - case ThreadOp::I64AtomicSub16U: opname = "i64.atomic.rmw16_u.sub"; break; - case ThreadOp::I64AtomicSub32U: opname = "i64.atomic.rmw32_u.sub"; break; - case ThreadOp::I32AtomicAnd: opname = "i32.atomic.rmw.and"; break; - case ThreadOp::I64AtomicAnd: opname = "i64.atomic.rmw.and"; break; - case ThreadOp::I32AtomicAnd8U: opname = "i32.atomic.rmw8_u.and"; break; - case ThreadOp::I32AtomicAnd16U: opname = "i32.atomic.rmw16_u.and"; break; - case ThreadOp::I64AtomicAnd8U: opname = "i64.atomic.rmw8_u.and"; break; - case ThreadOp::I64AtomicAnd16U: opname = "i64.atomic.rmw16_u.and"; break; - case ThreadOp::I64AtomicAnd32U: opname = "i64.atomic.rmw32_u.and"; break; - case ThreadOp::I32AtomicOr: opname = "i32.atomic.rmw.or"; break; - case ThreadOp::I64AtomicOr: opname = "i64.atomic.rmw.or"; break; - case ThreadOp::I32AtomicOr8U: opname = "i32.atomic.rmw8_u.or"; break; - case ThreadOp::I32AtomicOr16U: opname = "i32.atomic.rmw16_u.or"; break; - case ThreadOp::I64AtomicOr8U: opname = "i64.atomic.rmw8_u.or"; break; - case ThreadOp::I64AtomicOr16U: opname = "i64.atomic.rmw16_u.or"; break; - case ThreadOp::I64AtomicOr32U: opname = "i64.atomic.rmw32_u.or"; break; - case ThreadOp::I32AtomicXor: opname = "i32.atomic.rmw.xor"; break; - case ThreadOp::I64AtomicXor: opname = "i64.atomic.rmw.xor"; break; - case ThreadOp::I32AtomicXor8U: opname = "i32.atomic.rmw8_u.xor"; break; - case ThreadOp::I32AtomicXor16U: opname = "i32.atomic.rmw16_u.xor"; break; - case ThreadOp::I64AtomicXor8U: opname = "i64.atomic.rmw8_u.xor"; break; - case ThreadOp::I64AtomicXor16U: opname = "i64.atomic.rmw16_u.xor"; break; - case ThreadOp::I64AtomicXor32U: opname = "i64.atomic.rmw32_u.xor"; break; - case ThreadOp::I32AtomicXchg: opname = "i32.atomic.rmw.xchg"; break; - case ThreadOp::I64AtomicXchg: opname = "i64.atomic.rmw.xchg"; break; - case ThreadOp::I32AtomicXchg8U: opname = "i32.atomic.rmw8_u.xchg"; break; - case ThreadOp::I32AtomicXchg16U: opname = "i32.atomic.rmw16_u.xchg"; break; - case ThreadOp::I64AtomicXchg8U: opname = "i64.atomic.rmw8_u.xchg"; break; - case ThreadOp::I64AtomicXchg16U: opname = "i64.atomic.rmw16_u.xchg"; break; - case ThreadOp::I64AtomicXchg32U: opname = "i64.atomic.rmw32_u.xchg"; break; - default: return Fail(c, "unexpected rmw operator"); - } - - if (!c.buffer.append(opname, strlen(opname))) - return false; - - return RenderLoadStoreAddress(c, rmw.address(), 0); -} - -static bool -RenderAtomicStore(WasmRenderContext& c, AstAtomicStore& store) -{ - if (!RenderLoadStoreBase(c, store.address())) - return false; - - if (!RenderExpr(c, store.value())) - return false; - - if (!RenderIndent(c)) - return false; - - const char* opname; - switch (store.op()) { - case ThreadOp::I32AtomicStore8U: opname = "i32.atomic.store8_u"; break; - case ThreadOp::I64AtomicStore8U: opname = "i64.atomic.store8_u"; break; - case ThreadOp::I32AtomicStore16U: opname = "i32.atomic.store16_u"; break; - case ThreadOp::I64AtomicStore16U: opname = "i64.atomic.store16_u"; break; - case ThreadOp::I64AtomicStore32U: opname = "i64.atomic.store32_u"; break; - case ThreadOp::I32AtomicStore: opname = "i32.atomic.store"; break; - case ThreadOp::I64AtomicStore: opname = "i64.atomic.store"; break; - default: return Fail(c, "unexpected store operator"); - } - - if (!c.buffer.append(opname, strlen(opname))) - return false; - - return RenderLoadStoreAddress(c, store.address(), 0); -} - -static bool -RenderWait(WasmRenderContext& c, AstWait& wait) -{ - if (!RenderLoadStoreBase(c, wait.address())) - return false; - - if (!RenderExpr(c, wait.expected())) - return false; - - if (!RenderExpr(c, wait.timeout())) - return false; - - if (!RenderIndent(c)) - return false; - - const char* opname; - switch (wait.op()) { - case ThreadOp::I32Wait: opname = "i32.atomic.wait"; break; - case ThreadOp::I64Wait: opname = "i64.atomic.wait"; break; - default: return Fail(c, "unexpected wait operator"); - } - - if (!c.buffer.append(opname, strlen(opname))) - return false; - - return RenderLoadStoreAddress(c, wait.address(), 0); -} - -static bool -RenderWake(WasmRenderContext& c, AstWake& wake) -{ - if (!RenderLoadStoreBase(c, wake.address())) - return false; - - if (!RenderExpr(c, wake.count())) - return false; - - if (!RenderIndent(c)) - return false; - - if (!c.buffer.append("atomic.wake", strlen("atomic.wake"))) - return false; - - return RenderLoadStoreAddress(c, wake.address(), 0); -} - -#ifdef ENABLE_WASM_BULKMEM_OPS -static bool -RenderMemCopy(WasmRenderContext& c, AstMemCopy& mc) -{ - if (!RenderExpr(c, mc.dest())) - return false; - if (!RenderExpr(c, mc.src())) - return false; - if (!RenderExpr(c, mc.len())) - return false; - - if (!RenderIndent(c)) - return false; - - const char* opStr = "memory.copy"; - - return c.buffer.append(opStr, strlen(opStr)); -} - -static bool -RenderMemFill(WasmRenderContext& c, AstMemFill& mf) -{ - if (!RenderExpr(c, mf.start())) - return false; - if (!RenderExpr(c, mf.val())) - return false; - if (!RenderExpr(c, mf.len())) - return false; - - if (!RenderIndent(c)) - return false; - - const char* opStr = "memory.fill"; - - return c.buffer.append(opStr, strlen(opStr)); -} -#endif - -static bool -RenderExpr(WasmRenderContext& c, AstExpr& expr, bool newLine /* = true */) -{ - switch (expr.kind()) { - case AstExprKind::Drop: - if (!RenderDrop(c, expr.as())) - return false; - break; - case AstExprKind::Nop: - if (!RenderNop(c, expr.as())) - return false; - break; - case AstExprKind::Unreachable: - if (!RenderUnreachable(c, expr.as())) - return false; - break; - case AstExprKind::Call: - if (!RenderCall(c, expr.as())) - return false; - break; - case AstExprKind::CallIndirect: - if (!RenderCallIndirect(c, expr.as())) - return false; - break; - case AstExprKind::Const: - if (!RenderConst(c, expr.as())) - return false; - break; - case AstExprKind::GetLocal: - if (!RenderGetLocal(c, expr.as())) - return false; - break; - case AstExprKind::SetLocal: - if (!RenderSetLocal(c, expr.as())) - return false; - break; - case AstExprKind::GetGlobal: - if (!RenderGetGlobal(c, expr.as())) - return false; - break; - case AstExprKind::SetGlobal: - if (!RenderSetGlobal(c, expr.as())) - return false; - break; - case AstExprKind::TeeLocal: - if (!RenderTeeLocal(c, expr.as())) - return false; - break; - case AstExprKind::Block: - if (!RenderBlock(c, expr.as())) - return false; - break; - case AstExprKind::If: - if (!RenderIf(c, expr.as())) - return false; - break; - case AstExprKind::UnaryOperator: - if (!RenderUnaryOperator(c, expr.as())) - return false; - break; - case AstExprKind::BinaryOperator: - if (!RenderBinaryOperator(c, expr.as())) - return false; - break; - case AstExprKind::TernaryOperator: - if (!RenderTernaryOperator(c, expr.as())) - return false; - break; - case AstExprKind::ComparisonOperator: - if (!RenderComparisonOperator(c, expr.as())) - return false; - break; - case AstExprKind::ConversionOperator: - if (!RenderConversionOperator(c, expr.as())) - return false; - break; -#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS - case AstExprKind::ExtraConversionOperator: - if (!RenderExtraConversionOperator(c, expr.as())) - return false; - break; -#endif - case AstExprKind::Load: - if (!RenderLoad(c, expr.as())) - return false; - break; - case AstExprKind::Store: - if (!RenderStore(c, expr.as())) - return false; - break; - case AstExprKind::Branch: - if (!RenderBranch(c, expr.as())) - return false; - break; - case AstExprKind::BranchTable: - if (!RenderBrTable(c, expr.as())) - return false; - break; - case AstExprKind::Return: - if (!RenderReturn(c, expr.as())) - return false; - break; - case AstExprKind::First: - newLine = false; - if (!RenderFirst(c, expr.as())) - return false; - break; - case AstExprKind::CurrentMemory: - if (!RenderCurrentMemory(c, expr.as())) - return false; - break; - case AstExprKind::GrowMemory: - if (!RenderGrowMemory(c, expr.as())) - return false; - break; - case AstExprKind::AtomicCmpXchg: - if (!RenderAtomicCmpXchg(c, expr.as())) - return false; - break; - case AstExprKind::AtomicLoad: - if (!RenderAtomicLoad(c, expr.as())) - return false; - break; - case AstExprKind::AtomicRMW: - if (!RenderAtomicRMW(c, expr.as())) - return false; - break; - case AstExprKind::AtomicStore: - if (!RenderAtomicStore(c, expr.as())) - return false; - break; - case AstExprKind::Wait: - if (!RenderWait(c, expr.as())) - return false; - break; - case AstExprKind::Wake: - if (!RenderWake(c, expr.as())) - return false; - break; -#ifdef ENABLE_WASM_BULKMEM_OPS - case AstExprKind::MemCopy: - if (!RenderMemCopy(c, expr.as())) - return false; - break; - case AstExprKind::MemFill: - if (!RenderMemFill(c, expr.as())) - return false; - break; -#endif - default: - MOZ_CRASH("Bad AstExprKind"); - } - - return !newLine || c.buffer.append("\n"); -} - -static bool -RenderSignature(WasmRenderContext& c, const AstFuncType& funcType, - const AstNameVector* maybeLocals = nullptr) -{ - uint32_t paramsNum = funcType.args().length(); - - if (maybeLocals) { - for (uint32_t i = 0; i < paramsNum; i++) { - if (!c.buffer.append(" (param ")) - return false; - const AstName& name = (*maybeLocals)[i]; - if (!RenderNonemptyName(c, name)) - return false; - ValType arg = funcType.args()[i]; - if (!RenderValType(c, arg)) - return false; - if (!c.buffer.append(")")) - return false; - } - } else if (paramsNum > 0) { - if (!c.buffer.append(" (param")) - return false; - for (uint32_t i = 0; i < paramsNum; i++) { - if (!c.buffer.append(" ")) - return false; - ValType arg = funcType.args()[i]; - if (!RenderValType(c, arg)) - return false; - } - if (!c.buffer.append(")")) - return false; - } - if (funcType.ret() != ExprType::Void) { - if (!c.buffer.append(" (result ")) - return false; - if (!RenderExprType(c, funcType.ret())) - return false; - if (!c.buffer.append(")")) - return false; - } - return true; -} - -static bool -RenderFields(WasmRenderContext& c, const AstStructType& st) -{ - const AstNameVector& fieldNames = st.fieldNames(); - const AstValTypeVector& fieldTypes = st.fieldTypes(); - - for (uint32_t fieldIndex = 0; fieldIndex < fieldTypes.length(); fieldIndex++) { - if (!c.buffer.append("\n")) - return false; - if (!RenderIndent(c)) - return false; - if (!c.buffer.append("(field ")) - return false; - if (!RenderNonemptyName(c, fieldNames[fieldIndex])) - return false; - if (!RenderValType(c, fieldTypes[fieldIndex])) - return false; - if (!c.buffer.append(')')) - return false; - } - return true; -} - -template -static bool -RenderTypeStart(WasmRenderContext& c, const AstName& name, const char (&keyword)[ArrayLength]) -{ - if (!RenderIndent(c)) - return false; - if (!c.buffer.append("(type ")) - return false; - if (!RenderNonemptyName(c, name)) - return false; - if (!c.buffer.append("(")) - return false; - return c.buffer.append(keyword); -} - -static bool -RenderTypeEnd(WasmRenderContext& c) -{ - return c.buffer.append("))\n"); -} - -static bool -RenderTypeSection(WasmRenderContext& c, const AstModule::TypeDefVector& types) -{ - for (uint32_t typeIndex = 0; typeIndex < types.length(); typeIndex++) { - const AstTypeDef* type = types[typeIndex]; - if (type->isFuncType()) { - const AstFuncType* funcType = &type->asFuncType(); - if (!RenderTypeStart(c, funcType->name(), "func")) - return false; - if (!RenderSignature(c, *funcType)) - return false; - } else { - const AstStructType* st = &type->asStructType(); - if (!RenderTypeStart(c, st->name(), "struct")) - return false; - c.indent++; - if (!RenderFields(c, *st)) - return false; - c.indent--; - } - if (!RenderTypeEnd(c)) - return false; - } - - return true; -} - -static bool -RenderLimits(WasmRenderContext& c, const Limits& limits) -{ - if (!RenderInt32(c, limits.initial)) - return false; - if (limits.maximum) { - if (!c.buffer.append(" ")) - return false; - if (!RenderInt32(c, *limits.maximum)) - return false; - } - if (limits.shared == Shareable::True) { - if (!c.buffer.append(" shared")) - return false; - } - return true; -} - -static bool -RenderResizableTable(WasmRenderContext& c, const Limits& table) -{ - if (!c.buffer.append("(table ")) - return false; - if (!RenderLimits(c, table)) - return false; - MOZ_ASSERT(table.shared == Shareable::False); - return c.buffer.append(" anyfunc)"); -} - -static bool -RenderTableSection(WasmRenderContext& c, const AstModule& module) -{ - if (!module.hasTable()) - return true; - for (const AstResizable& table : module.tables()) { - if (table.imported) - continue; - if (!RenderIndent(c)) - return false; - if (!RenderResizableTable(c, table.limits)) - return false; - if (!c.buffer.append("\n")) - return false; - } - return true; -} - -static bool -RenderInlineExpr(WasmRenderContext& c, AstExpr& expr) -{ - if (!c.buffer.append("(")) - return false; - - uint32_t prevIndent = c.indent; - c.indent = 0; - if (!RenderExpr(c, expr, /* newLine */ false)) - return false; - c.indent = prevIndent; - - return c.buffer.append(")"); -} - -static bool -RenderElemSection(WasmRenderContext& c, const AstModule& module) -{ - for (const AstElemSegment* segment : module.elemSegments()) { - if (!RenderIndent(c)) - return false; - if (!c.buffer.append("(elem ")) - return false; - if (!RenderInlineExpr(c, *segment->offset())) - return false; - - for (const AstRef& elem : segment->elems()) { - if (!c.buffer.append(" ")) - return false; - - uint32_t index = elem.index(); - AstName name = index < module.funcImportNames().length() - ? module.funcImportNames()[index] - : module.funcs()[index - module.funcImportNames().length()]->name(); - - if (name.empty()) { - if (!RenderInt32(c, index)) - return false; - } else { - if (!RenderName(c, name)) - return false; - } - } - - if (!c.buffer.append(")\n")) - return false; - } - - return true; -} - -static bool -RenderGlobal(WasmRenderContext& c, const AstGlobal& glob, bool inImport = false) -{ - if (!c.buffer.append("(global ")) - return false; - - if (!inImport) { - if (!RenderName(c, glob.name())) - return false; - if (!c.buffer.append(" ")) - return false; - } - - if (glob.isMutable()) { - if (!c.buffer.append("(mut ")) - return false; - if (!RenderValType(c, glob.type())) - return false; - if (!c.buffer.append(")")) - return false; - } else { - if (!RenderValType(c, glob.type())) - return false; - } - - if (glob.hasInit()) { - if (!c.buffer.append(" ")) - return false; - if (!RenderInlineExpr(c, glob.init())) - return false; - } - - if (!c.buffer.append(")")) - return false; - - return inImport || c.buffer.append("\n"); -} - -static bool -RenderGlobalSection(WasmRenderContext& c, const AstModule& module) -{ - if (module.globals().empty()) - return true; - - for (const AstGlobal* global : module.globals()) { - if (!RenderIndent(c)) - return false; - if (!RenderGlobal(c, *global)) - return false; - } - - return true; -} - -static bool -RenderResizableMemory(WasmRenderContext& c, const Limits& memory) -{ - if (!c.buffer.append("(memory ")) - return false; - - Limits resizedMemory = memory; - - MOZ_ASSERT(resizedMemory.initial % PageSize == 0); - resizedMemory.initial /= PageSize; - - if (resizedMemory.maximum) { - if (*resizedMemory.maximum == UINT32_MAX) { - // See special casing in DecodeMemoryLimits. - *resizedMemory.maximum = MaxMemoryMaximumPages; - } else { - MOZ_ASSERT(*resizedMemory.maximum % PageSize == 0); - *resizedMemory.maximum /= PageSize; - } - } - - if (!RenderLimits(c, resizedMemory)) - return false; - - return c.buffer.append(")"); -} - -static bool -RenderImport(WasmRenderContext& c, AstImport& import, const AstModule& module) -{ - if (!RenderIndent(c)) - return false; - if (!c.buffer.append("(import ")) - return false; - if (!RenderName(c, import.name())) - return false; - if (!c.buffer.append(" \"")) - return false; - - const AstName& moduleName = import.module(); - if (!RenderEscapedString(c, moduleName)) - return false; - - if (!c.buffer.append("\" \"")) - return false; - - const AstName& fieldName = import.field(); - if (!RenderEscapedString(c, fieldName)) - return false; - - if (!c.buffer.append("\" ")) - return false; - - switch (import.kind()) { - case DefinitionKind::Function: { - if (!c.buffer.append("(func")) - return false; - const AstFuncType* funcType = &module.types()[import.funcType().index()]->asFuncType(); - if (!RenderSignature(c, *funcType)) - return false; - if (!c.buffer.append(")")) - return false; - break; - } - case DefinitionKind::Table: { - if (!RenderResizableTable(c, import.limits())) - return false; - break; - } - case DefinitionKind::Memory: { - if (!RenderResizableMemory(c, import.limits())) - return false; - break; - } - case DefinitionKind::Global: { - const AstGlobal& glob = import.global(); - if (!RenderGlobal(c, glob, /* inImport */ true)) - return false; - break; - } - } - - return c.buffer.append(")\n"); -} - -static bool -RenderImportSection(WasmRenderContext& c, const AstModule& module) -{ - for (AstImport* import : module.imports()) { - if (!RenderImport(c, *import, module)) - return false; - } - return true; -} - -static bool -RenderExport(WasmRenderContext& c, AstExport& export_, - const AstModule::NameVector& funcImportNames, - const AstModule::FuncVector& funcs) -{ - if (!RenderIndent(c)) - return false; - if (!c.buffer.append("(export \"")) - return false; - if (!RenderEscapedString(c, export_.name())) - return false; - if (!c.buffer.append("\" ")) - return false; - - switch (export_.kind()) { - case DefinitionKind::Function: { - uint32_t index = export_.ref().index(); - AstName name = index < funcImportNames.length() - ? funcImportNames[index] - : funcs[index - funcImportNames.length()]->name(); - if (name.empty()) { - if (!RenderInt32(c, index)) - return false; - } else { - if (!RenderName(c, name)) - return false; - } - break; - } - case DefinitionKind::Table: { - if (!c.buffer.append("table")) - return false; - break; - } - case DefinitionKind::Memory: { - if (!c.buffer.append("memory")) - return false; - break; - } - case DefinitionKind::Global: { - if (!c.buffer.append("global ")) - return false; - if (!RenderRef(c, export_.ref())) - return false; - break; - } - } - - return c.buffer.append(")\n"); -} - -static bool -RenderExportSection(WasmRenderContext& c, const AstModule::ExportVector& exports, - const AstModule::NameVector& funcImportNames, - const AstModule::FuncVector& funcs) -{ - uint32_t numExports = exports.length(); - for (uint32_t i = 0; i < numExports; i++) { - if (!RenderExport(c, *exports[i], funcImportNames, funcs)) - return false; - } - return true; -} - -static bool -RenderFunctionBody(WasmRenderContext& c, AstFunc& func, const AstModule::TypeDefVector& types) -{ - const AstFuncType* funcType = &types[func.funcType().index()]->asFuncType(); - - uint32_t argsNum = funcType->args().length(); - uint32_t localsNum = func.vars().length(); - if (localsNum > 0) { - if (!RenderIndent(c)) - return false; - for (uint32_t i = 0; i < localsNum; i++) { - if (!c.buffer.append("(local ")) - return false; - const AstName& name = func.locals()[argsNum + i]; - if (!name.empty()) { - if (!RenderName(c, name)) - return false; - if (!c.buffer.append(" ")) - return false; - } - ValType local = func.vars()[i]; - if (!RenderValType(c, local)) - return false; - if (!c.buffer.append(") ")) - return false; - } - if (!c.buffer.append("\n")) - return false; - } - - - uint32_t exprsNum = func.body().length(); - for (uint32_t i = 0; i < exprsNum; i++) { - if (!RenderExpr(c, *func.body()[i])) - return false; - } - - return true; -} - -static bool -RenderCodeSection(WasmRenderContext& c, const AstModule::FuncVector& funcs, - const AstModule::TypeDefVector& types) -{ - uint32_t numFuncBodies = funcs.length(); - for (uint32_t funcIndex = 0; funcIndex < numFuncBodies; funcIndex++) { - AstFunc* func = funcs[funcIndex]; - uint32_t funcTypeIndex = func->funcType().index(); - AstFuncType* funcType = &types[funcTypeIndex]->asFuncType(); - - if (!RenderIndent(c)) - return false; - if (!c.buffer.append("(func ")) - return false; - if (!func->name().empty()) { - if (!RenderName(c, func->name())) - return false; - } - - if (!RenderSignature(c, *funcType, &(func->locals()))) - return false; - if (!c.buffer.append("\n")) - return false; - - c.currentFuncIndex = funcIndex; - - c.indent++; - if (!RenderFunctionBody(c, *func, types)) - return false; - c.indent--; - if (!RenderIndent(c)) - return false; - if (!c.buffer.append(")\n")) - return false; - } - - return true; -} - -static bool -RenderMemorySection(WasmRenderContext& c, const AstModule& module) -{ - if (!module.hasMemory()) - return true; - - for (const AstResizable& memory : module.memories()) { - if (memory.imported) - continue; - if (!RenderIndent(c)) - return false; - if (!RenderResizableMemory(c, memory.limits)) - return false; - if (!c.buffer.append("\n")) - return false; - } - - return true; -} - -static bool -RenderDataSection(WasmRenderContext& c, const AstModule& module) -{ - uint32_t numSegments = module.dataSegments().length(); - if (!numSegments) - return true; - - for (const AstDataSegment* seg : module.dataSegments()) { - if (!RenderIndent(c)) - return false; - if (!c.buffer.append("(data ")) - return false; - if (!RenderInlineExpr(c, *seg->offset())) - return false; - if (!c.buffer.append("\n")) - return false; - - c.indent++; - for (const AstName& fragment : seg->fragments()) { - if (!RenderIndent(c)) - return false; - if (!c.buffer.append("\"")) - return false; - if (!RenderEscapedString(c, fragment)) - return false; - if (!c.buffer.append("\"\n")) - return false; - } - c.indent--; - - if (!RenderIndent(c)) - return false; - if (!c.buffer.append(")\n")) - return false; - } - - return true; -} - -static bool -RenderStartSection(WasmRenderContext& c, AstModule& module) -{ - if (!module.hasStartFunc()) - return true; - - if (!RenderIndent(c)) - return false; - if (!c.buffer.append("(start ")) - return false; - if (!RenderRef(c, module.startFunc().func())) - return false; - if (!c.buffer.append(")\n")) - return false; - - return true; -} - -static bool -RenderModule(WasmRenderContext& c, AstModule& module) -{ - if (!c.buffer.append("(module\n")) - return false; - - c.indent++; - - if (!RenderTypeSection(c, module.types())) - return false; - - if (!RenderImportSection(c, module)) - return false; - - if (!RenderTableSection(c, module)) - return false; - - if (!RenderMemorySection(c, module)) - return false; - - if (!RenderGlobalSection(c, module)) - return false; - - if (!RenderExportSection(c, module.exports(), module.funcImportNames(), module.funcs())) - return false; - - if (!RenderStartSection(c, module)) - return false; - - if (!RenderElemSection(c, module)) - return false; - - if (!RenderCodeSection(c, module.funcs(), module.types())) - return false; - - if (!RenderDataSection(c, module)) - return false; - - c.indent--; - - if (!c.buffer.append(")")) - return false; - - return true; -} - -/*****************************************************************************/ -// Top-level functions - -bool -wasm::BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer) -{ - LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE); - - AstModule* module; - if (!BinaryToAst(cx, bytes, length, lifo, &module)) - return false; - - WasmPrintBuffer buf(buffer); - WasmRenderContext c(cx, module, buf); - - if (!RenderModule(c, *module)) { - if (!cx->isExceptionPending()) - ReportOutOfMemory(cx); - return false; - } - - return true; -} diff --git a/js/src/wasm/WasmBinaryToText.h b/js/src/wasm/WasmBinaryToText.h deleted file mode 100644 index b07ce2a29c24..000000000000 --- a/js/src/wasm/WasmBinaryToText.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * - * Copyright 2015 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef wasm_binary_to_text_h -#define wasm_binary_to_text_h - -#include "NamespaceImports.h" - -#include "gc/Rooting.h" -#include "js/Class.h" -#include "wasm/WasmCode.h" - -namespace js { - -class StringBuffer; - -namespace wasm { - -// Translate the given binary representation of a wasm module into the module's textual -// representation. - -MOZ_MUST_USE bool -BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer); - -} // namespace wasm - -} // namespace js - -#endif // namespace wasm_binary_to_text_h diff --git a/js/src/wasm/WasmDebug.cpp b/js/src/wasm/WasmDebug.cpp index c65d2db67ef9..b9597baa23b7 100644 --- a/js/src/wasm/WasmDebug.cpp +++ b/js/src/wasm/WasmDebug.cpp @@ -27,7 +27,6 @@ #include "util/StringBuffer.h" #include "util/Text.h" #include "vm/Debugger.h" -#include "wasm/WasmBinaryToText.h" #include "wasm/WasmInstance.h" #include "wasm/WasmValidate.h" diff --git a/js/src/wasm/WasmTextUtils.cpp b/js/src/wasm/WasmTextUtils.cpp deleted file mode 100644 index 6582ae9ce89d..000000000000 --- a/js/src/wasm/WasmTextUtils.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * - * Copyright 2016 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "wasm/WasmTextUtils.h" - -#include "util/StringBuffer.h" -#include "wasm/WasmTypes.h" - -using namespace js; -using namespace jit; -using namespace wasm; - -using mozilla::IsNaN; - -template -bool -wasm::RenderInBase(StringBuffer& sb, uint64_t num) -{ - uint64_t n = num; - uint64_t pow = 1; - while (n) { - pow *= base; - n /= base; - } - pow /= base; - - n = num; - while (pow) { - if (!sb.append("0123456789abcdef"[n / pow])) - return false; - n -= (n / pow) * pow; - pow /= base; - } - - return true; -} - -template bool wasm::RenderInBase<10>(StringBuffer& sb, uint64_t num); - -template -bool -wasm::RenderNaN(StringBuffer& sb, T num) -{ - typedef typename mozilla::SelectTrait Traits; - typedef typename Traits::Bits Bits; - - MOZ_ASSERT(IsNaN(num)); - - Bits bits = mozilla::BitwiseCast(num); - if ((bits & Traits::kSignBit) && !sb.append("-")) - return false; - if (!sb.append("nan")) - return false; - - Bits payload = bits & Traits::kSignificandBits; - // Only render the payload if it's not the spec's default NaN. - if (payload == ((Traits::kSignificandBits + 1) >> 1)) - return true; - - return sb.append(":0x") && - RenderInBase<16>(sb, payload); -} - -template MOZ_MUST_USE bool wasm::RenderNaN(StringBuffer& b, float num); -template MOZ_MUST_USE bool wasm::RenderNaN(StringBuffer& b, double num); diff --git a/js/src/wasm/WasmTextUtils.h b/js/src/wasm/WasmTextUtils.h deleted file mode 100644 index 386ba03a032a..000000000000 --- a/js/src/wasm/WasmTextUtils.h +++ /dev/null @@ -1,105 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * - * Copyright 2016 Mozilla Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef wasm_text_utils -#define wasm_text_utils - -#include "NamespaceImports.h" - -#include "util/StringBuffer.h" - -namespace js { -namespace wasm { - -template -MOZ_MUST_USE bool -RenderInBase(StringBuffer& sb, uint64_t num); - -template -MOZ_MUST_USE bool -RenderNaN(StringBuffer& sb, T num); - -// Helper class, StringBuffer wrapper, to track the position (line and column) -// within the generated source. - -class WasmPrintBuffer -{ - StringBuffer& stringBuffer_; - uint32_t lineno_; - uint32_t column_; - - public: - explicit WasmPrintBuffer(StringBuffer& stringBuffer) - : stringBuffer_(stringBuffer), - lineno_(1), - column_(1) - {} - inline char processChar(char ch) { - if (ch == '\n') { - lineno_++; column_ = 1; - } else - column_++; - return ch; - } - inline char16_t processChar(char16_t ch) { - if (ch == '\n') { - lineno_++; column_ = 1; - } else - column_++; - return ch; - } - bool append(const char ch) { - return stringBuffer_.append(processChar(ch)); - } - bool append(const char16_t ch) { - return stringBuffer_.append(processChar(ch)); - } - bool append(const char* str, size_t length) { - for (size_t i = 0; i < length; i++) - processChar(str[i]); - return stringBuffer_.append(str, length); - } - bool append(const char16_t* begin, const char16_t* end) { - for (const char16_t* p = begin; p != end; p++) - processChar(*p); - return stringBuffer_.append(begin, end); - } - bool append(const char16_t* str, size_t length) { - return append(str, str + length); - } - template - bool append(const char (&array)[ArrayLength]) { - static_assert(ArrayLength > 0, "null-terminated"); - MOZ_ASSERT(array[ArrayLength - 1] == '\0'); - return append(array, ArrayLength - 1); - } - char16_t getChar(size_t index) { - return stringBuffer_.getChar(index); - } - size_t length() { - return stringBuffer_.length(); - } - StringBuffer& stringBuffer() { return stringBuffer_; } - uint32_t lineno() { return lineno_; } - uint32_t column() { return column_; } -}; - -} // namespace wasm -} // namespace js - -#endif // namespace wasm_text_utils diff --git a/js/src/wasm/WasmValidate.h b/js/src/wasm/WasmValidate.h index ed6bdbd0ec98..6f19cb6fbe0b 100644 --- a/js/src/wasm/WasmValidate.h +++ b/js/src/wasm/WasmValidate.h @@ -132,9 +132,6 @@ struct ModuleEnvironment bool funcIsImport(uint32_t funcIndex) const { return funcIndex < funcImportGlobalDataOffsets.length(); } - uint32_t funcIndexToFuncTypeIndex(uint32_t funcIndex) const { - return TypeDef::fromFuncTypeWithIdPtr(funcTypes[funcIndex]) - types.begin(); - } }; // The Encoder class appends bytes to the Bytes object it is given during From 89fd549c61948a41d1202bc8471145eb8e7e0066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 26 Jun 2018 22:19:19 +0200 Subject: [PATCH 03/43] Bug 1471013: Make MozAutoplayMediaBlocked chrome-only. r=smaug Summary: MozReview-Commit-ID: JVLMpCeMkAs Reviewers: smaug Tags: #secure-revision Bug #: 1471013 Differential Revision: https://phabricator.services.mozilla.com/D1840 MozReview-Commit-ID: 2he7tHFbZ8t --- dom/html/HTMLMediaElement.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 51e20a6ee528..5bb316475cf9 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -6958,8 +6958,7 @@ HTMLMediaElement::IsAllowedToPlay() { if (!AutoplayPolicy::IsMediaElementAllowedToPlay(WrapNotNull(this))) { #if defined(MOZ_WIDGET_ANDROID) - // FIXME: This should be chrome-only. - nsContentUtils::DispatchTrustedEvent( + nsContentUtils::DispatchChromeEvent( OwnerDoc(), static_cast(this), NS_LITERAL_STRING("MozAutoplayMediaBlocked"), From a24ebbee4e79bfaa683527e7b45a73553abfad8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 22 Jun 2018 21:47:29 +0200 Subject: [PATCH 04/43] Bug 1470545: Add chromeonly "shadowrootattached" event for devtools. r=smaug Summary: document.addEventListener("shadowrootattached", e => { // Do stuff with composedTarget. }); I didn't bother to add tests for the event itself since this is going to get tested in bug 1449333, but I can look into writing a chrome mochitest if you want. Test Plan: See above. Reviewers: smaug Bug #: 1470545 Differential Revision: https://phabricator.services.mozilla.com/D1777 MozReview-Commit-ID: 55cVMSsznMS --- dom/base/Element.cpp | 11 +++++++++++ dom/events/AsyncEventDispatcher.cpp | 5 +++++ dom/events/AsyncEventDispatcher.h | 5 ++++- widget/EventForwards.h | 7 +++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 249ad7b3d621..b4b28088fc9c 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -1263,6 +1263,17 @@ Element::AttachShadowWithoutNameChecks(ShadowRootMode aMode) */ SetShadowRoot(shadowRoot); + // Dispatch a "shadowrootattached" event for devtools. + { + AsyncEventDispatcher* dispatcher = + new AsyncEventDispatcher(this, + NS_LITERAL_STRING("shadowrootattached"), + CanBubble::eYes, + ChromeOnlyDispatch::eYes, + Composed::eYes); + dispatcher->PostDOMEvent(); + } + /** * 6. Return shadow. */ diff --git a/dom/events/AsyncEventDispatcher.cpp b/dom/events/AsyncEventDispatcher.cpp index cdf33d38091e..63e3560ee7f0 100644 --- a/dom/events/AsyncEventDispatcher.cpp +++ b/dom/events/AsyncEventDispatcher.cpp @@ -50,6 +50,7 @@ AsyncEventDispatcher::Run() } mTarget->AsyncEventRunning(this); if (mEventMessage != eUnidentifiedEvent) { + MOZ_ASSERT(mComposed == Composed::eDefault); return nsContentUtils::DispatchTrustedEvent (node->OwnerDoc(), mTarget, mEventMessage, mCanBubble, Cancelable::eNo, nullptr /* aDefaultAction */, mOnlyChromeDispatch); @@ -60,6 +61,10 @@ AsyncEventDispatcher::Run() event->InitEvent(mEventType, mCanBubble, Cancelable::eNo); event->SetTrusted(true); } + if (mComposed != Composed::eDefault) { + event->WidgetEventPtr()->mFlags.mComposed = + mComposed == Composed::eYes; + } if (mOnlyChromeDispatch == ChromeOnlyDispatch::eYes) { MOZ_ASSERT(event->IsTrusted()); event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true; diff --git a/dom/events/AsyncEventDispatcher.h b/dom/events/AsyncEventDispatcher.h index c8ae0ccf2cce..7901f12ed6a3 100644 --- a/dom/events/AsyncEventDispatcher.h +++ b/dom/events/AsyncEventDispatcher.h @@ -39,13 +39,15 @@ public: AsyncEventDispatcher(nsINode* aTarget, const nsAString& aEventType, CanBubble aCanBubble, - ChromeOnlyDispatch aOnlyChromeDispatch) + ChromeOnlyDispatch aOnlyChromeDispatch, + Composed aComposed = Composed::eDefault) : CancelableRunnable("AsyncEventDispatcher") , mTarget(aTarget) , mEventType(aEventType) , mEventMessage(eUnidentifiedEvent) , mCanBubble(aCanBubble) , mOnlyChromeDispatch(aOnlyChromeDispatch) + , mComposed(aComposed) { } @@ -121,6 +123,7 @@ public: EventMessage mEventMessage; CanBubble mCanBubble = CanBubble::eNo; ChromeOnlyDispatch mOnlyChromeDispatch = ChromeOnlyDispatch::eNo; + Composed mComposed = Composed::eDefault; bool mCanceled = false; bool mCheckStillInDoc = false; }; diff --git a/widget/EventForwards.h b/widget/EventForwards.h index b7338c93bca1..114c194b22a9 100644 --- a/widget/EventForwards.h +++ b/widget/EventForwards.h @@ -57,6 +57,13 @@ enum class Trusted eNo }; +enum class Composed +{ + eYes, + eNo, + eDefault +}; + /** * Event messages */ From 0004f1c6ae8ab6199965bffff4702a1346b6fad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Wed, 27 Jun 2018 11:50:13 +0200 Subject: [PATCH 05/43] No bug - Remove some useless intermediate variables. r=me MozReview-Commit-ID: LFGpL7JImwK --- layout/generic/nsImageFrame.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 0343ad2d8b36..34897a979a29 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -575,8 +575,7 @@ nsImageFrame::OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage) * one frame = 1 * one loop = 2 */ - nsPresContext *presContext = PresContext(); - aImage->SetAnimationMode(presContext->ImageAnimationMode()); + aImage->SetAnimationMode(PresContext()->ImageAnimationMode()); if (IsPendingLoad(aRequest)) { // We don't care @@ -1869,8 +1868,7 @@ nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, bool nsImageFrame::ShouldDisplaySelection() { - nsPresContext* presContext = PresContext(); - int16_t displaySelection = presContext->PresShell()->GetSelectionFlags(); + int16_t displaySelection = PresShell()->GetSelectionFlags(); if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES)) return false;//no need to check the blue border, we cannot be drawn selected From e555bb0ae41820ccd6e3f787171c953827875bef Mon Sep 17 00:00:00 2001 From: Coroiu Cristina Date: Wed, 27 Jun 2018 13:09:31 +0300 Subject: [PATCH 06/43] Backed out 2 changesets (bug 1447591) for mochitest failures at dom/base/test/test_postMessages.html Backed out changeset 0800fdb509d2 (bug 1447591) Backed out changeset 9b5347dee1f2 (bug 1447591) --- devtools/server/actors/source.js | 4 +- .../tests/unit/test_frameactor_wasm-01.js | 5 +- js/src/builtin/TestingFunctions.cpp | 100 +- js/src/jit-test/lib/wasm.js | 27 +- js/src/jit-test/tests/debug/wasm-05.js | 115 + js/src/jit-test/tests/debug/wasm-07.js | 28 +- .../tests/debug/wasm-binary-sources.js | 6 +- .../jit-test/tests/debug/wasm-breakpoint.js | 105 +- .../tests/debug/wasm-getAllColumnOffsets.js | 8 +- js/src/jit-test/tests/wasm/atomic.js | 69 + js/src/jit-test/tests/wasm/gc/structs.js | 9 + js/src/jit-test/tests/wasm/to-text.js | 293 ++ js/src/moz.build | 3 + js/src/wasm/WasmAST.h | 28 +- js/src/wasm/WasmBinaryToAST.cpp | 2390 +++++++++++++++++ js/src/wasm/WasmBinaryToAST.h | 37 + js/src/wasm/WasmBinaryToText.cpp | 2138 +++++++++++++++ js/src/wasm/WasmBinaryToText.h | 45 + js/src/wasm/WasmDebug.cpp | 210 +- js/src/wasm/WasmDebug.h | 38 +- js/src/wasm/WasmTextToBinary.cpp | 19 +- js/src/wasm/WasmTextToBinary.h | 3 +- js/src/wasm/WasmTextUtils.cpp | 80 + js/src/wasm/WasmTextUtils.h | 105 + js/src/wasm/WasmValidate.h | 3 + 25 files changed, 5689 insertions(+), 179 deletions(-) create mode 100644 js/src/jit-test/tests/debug/wasm-05.js create mode 100644 js/src/jit-test/tests/wasm/to-text.js create mode 100644 js/src/wasm/WasmBinaryToAST.cpp create mode 100644 js/src/wasm/WasmBinaryToAST.h create mode 100644 js/src/wasm/WasmBinaryToText.cpp create mode 100644 js/src/wasm/WasmBinaryToText.h create mode 100644 js/src/wasm/WasmTextUtils.cpp create mode 100644 js/src/wasm/WasmTextUtils.h diff --git a/devtools/server/actors/source.js b/devtools/server/actors/source.js index 511701e4024d..677f017babc8 100644 --- a/devtools/server/actors/source.js +++ b/devtools/server/actors/source.js @@ -792,10 +792,8 @@ const SourceActor = ActorClassWithSpec(sourceSpec, { if (!this.isSourceMapped) { const generatedLocation = GeneratedLocation.fromOriginalLocation(originalLocation); - const isWasm = this.source && this.source.introductionType === "wasm"; if (!this._setBreakpointAtGeneratedLocation(actor, generatedLocation) && - !noSliding && - !isWasm) { + !noSliding) { const query = { line: originalLine }; // For most cases, we have a real source to query for. The // only time we don't is for HTML pages. In that case we want diff --git a/devtools/server/tests/unit/test_frameactor_wasm-01.js b/devtools/server/tests/unit/test_frameactor_wasm-01.js index 4f0f43566a25..883170eb26ae 100644 --- a/devtools/server/tests/unit/test_frameactor_wasm-01.js +++ b/devtools/server/tests/unit/test_frameactor_wasm-01.js @@ -26,10 +26,7 @@ function run_test() { gClient, "test-stack", function(response, tabClient, threadClient) { gThreadClient = threadClient; - gThreadClient.reconfigure({ - observeAsmJS: true, - wasmBinarySource: true - }, function(response) { + gThreadClient.reconfigure({ observeAsmJS: true }, function(response) { Assert.equal(!!response.error, false); test_pause_frame(); }); diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index fff29feaacc9..ffb3179696c9 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -63,6 +63,7 @@ #include "vm/StringType.h" #include "vm/TraceLogging.h" #include "wasm/AsmJS.h" +#include "wasm/WasmBinaryToText.h" #include "wasm/WasmJS.h" #include "wasm/WasmModule.h" #include "wasm/WasmSignalHandlers.h" @@ -658,61 +659,90 @@ WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp) if (!twoByteChars.initTwoByte(cx, args[0].toString())) return false; - bool withOffsets = false; if (args.hasDefined(1)) { - if (!args[1].isBoolean()) { - ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a boolean"); + if (!args[1].isString()) { + ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a String"); return false; } - withOffsets = ToBoolean(args[1]); } uintptr_t stackLimit = GetNativeStackLimit(cx); wasm::Bytes bytes; UniqueChars error; - wasm::Uint32Vector offsets; - if (!wasm::TextToBinary(twoByteChars.twoByteChars(), stackLimit, &bytes, &offsets, &error)) { + if (!wasm::TextToBinary(twoByteChars.twoByteChars(), stackLimit, &bytes, &error)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL, error.get() ? error.get() : "out of memory"); return false; } - RootedObject binary(cx, JS_NewUint8Array(cx, bytes.length())); - if (!binary) - return false; - - memcpy(binary->as().viewDataUnshared(), bytes.begin(), bytes.length()); - - if (!withOffsets) { - args.rval().setObject(*binary); - return true; - } - - RootedObject obj(cx, JS_NewPlainObject(cx)); + RootedObject obj(cx, JS_NewUint8Array(cx, bytes.length())); if (!obj) return false; - constexpr unsigned propAttrs = JSPROP_ENUMERATE; - if (!JS_DefineProperty(cx, obj, "binary", binary, propAttrs)) - return false; - - RootedObject jsOffsets(cx, JS_NewArrayObject(cx, offsets.length())); - if (!jsOffsets) - return false; - for (size_t i = 0; i < offsets.length(); i++) { - uint32_t offset = offsets[i]; - RootedValue offsetVal(cx, NumberValue(offset)); - if (!JS_SetElement(cx, jsOffsets, i, offsetVal)) - return false; - } - if (!JS_DefineProperty(cx, obj, "offsets", jsOffsets, propAttrs)) - return false; + memcpy(obj->as().viewDataUnshared(), bytes.begin(), bytes.length()); args.rval().setObject(*obj); return true; } +static bool +WasmBinaryToText(JSContext* cx, unsigned argc, Value* vp) +{ + if (!cx->options().wasm()) { + JS_ReportErrorASCII(cx, "wasm support unavailable"); + return false; + } + + CallArgs args = CallArgsFromVp(argc, vp); + + if (!args.get(0).isObject() || !args.get(0).toObject().is()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG); + return false; + } + + Rooted code(cx, &args[0].toObject().as()); + + if (!TypedArrayObject::ensureHasBuffer(cx, code)) + return false; + + if (code->isSharedMemory()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG); + return false; + } + + const uint8_t* bufferStart = code->bufferUnshared()->dataPointer(); + const uint8_t* bytes = bufferStart + code->byteOffset(); + uint32_t length = code->byteLength(); + + Vector copy(cx); + if (code->bufferUnshared()->hasInlineData()) { + if (!copy.append(bytes, length)) + return false; + bytes = copy.begin(); + } + + if (args.length() > 1) { + JS_ReportErrorASCII(cx, "wasm text format selection is not supported"); + return false; + } + + StringBuffer buffer(cx); + bool ok = wasm::BinaryToText(cx, bytes, length, buffer); + if (!ok) { + if (!cx->isExceptionPending()) + JS_ReportErrorASCII(cx, "wasm binary to text print error"); + return false; + } + + JSString* result = buffer.finishString(); + if (!result) + return false; + + args.rval().setString(result); + return true; +} + static bool WasmExtractCode(JSContext* cx, unsigned argc, Value* vp) { @@ -5648,6 +5678,10 @@ gc::ZealModeHelpText), "wasmTextToBinary(str)", " Translates the given text wasm module into its binary encoding."), + JS_FN_HELP("wasmBinaryToText", WasmBinaryToText, 1, 0, +"wasmBinaryToText(bin)", +" Translates binary encoding to text format"), + JS_FN_HELP("wasmExtractCode", WasmExtractCode, 1, 0, "wasmExtractCode(module[, tier])", " Extracts generated machine code from WebAssembly.Module. The tier is a string,\n" diff --git a/js/src/jit-test/lib/wasm.js b/js/src/jit-test/lib/wasm.js index 92ca6f18bea8..537bf5a2b245 100644 --- a/js/src/jit-test/lib/wasm.js +++ b/js/src/jit-test/lib/wasm.js @@ -116,6 +116,14 @@ function wasmFullPass(text, expected, maybeImports, ...args) { let instance = new WebAssembly.Instance(module, maybeImports); assertEq(typeof instance.exports.run, 'function', "A 'run' function must be exported."); assertEq(instance.exports.run(...args), expected, "Initial module must return the expected result."); + + let retext = wasmBinaryToText(binary); + let rebinary = wasmTextToBinary(retext); + + assertEq(WebAssembly.validate(rebinary), true, "Recreated binary must validate."); + let remodule = new WebAssembly.Module(rebinary); + let reinstance = new WebAssembly.Instance(remodule, maybeImports); + assertEq(reinstance.exports.run(...args), expected, "Reformed module must return the expected result"); } // Ditto, but expects a function named '$run' instead of exported with this name. @@ -126,15 +134,15 @@ function wasmFullPassI64(text, expected, maybeImports, ...args) { let augmentedSrc = _augmentSrc(text, [ { type: 'i64', func: '$run', args, expected } ]); let augmentedBinary = wasmTextToBinary(augmentedSrc); new WebAssembly.Instance(new WebAssembly.Module(augmentedBinary), maybeImports).exports.assert_0(); + + let retext = wasmBinaryToText(augmentedBinary); + new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(retext)), maybeImports).exports.assert_0(); } function wasmRunWithDebugger(wast, lib, init, done) { let g = newGlobal(''); let dbg = new Debugger(g); - // Enable binary source mode. - dbg.allowWasmBinarySource = true; - g.eval(` var wasm = wasmTextToBinary('${wast}'); var lib = ${lib || 'undefined'}; @@ -152,6 +160,19 @@ var m = new WebAssembly.Instance(new WebAssembly.Module(wasm), lib);`); done({dbg, result, error, wasmScript, g,}); } +function wasmGetScriptBreakpoints(wasmScript) { + var result = []; + var sourceText = wasmScript.source.text; + sourceText.split('\n').forEach(function (line, i) { + var lineOffsets = wasmScript.getLineOffsets(i + 1); + if (lineOffsets.length === 0) + return; + assertEq(lineOffsets.length, 1); + result.push({str: line.trim(), line: i + 1, offset: lineOffsets[0]}); + }); + return result; +} + const WasmHelpers = {}; (function() { diff --git a/js/src/jit-test/tests/debug/wasm-05.js b/js/src/jit-test/tests/debug/wasm-05.js new file mode 100644 index 000000000000..9332baa275e3 --- /dev/null +++ b/js/src/jit-test/tests/debug/wasm-05.js @@ -0,0 +1,115 @@ +// |jit-test| test-also-no-wasm-baseline +// Tests that wasm module scripts have text line to bytecode offset information +// when source text is generated. + +load(libdir + "asserts.js"); + +if (!wasmDebuggingIsSupported()) + quit(); + +// Checking if experimental format generates internal source map to binary file +// by querying debugger scripts getLineOffsets. +// (Notice that the source map will not be produced by wasmBinaryToText) +function getAllOffsets(wast) { + var sandbox = newGlobal(''); + var dbg = new Debugger(); + dbg.addDebuggee(sandbox); + sandbox.eval(` + var wasm = wasmTextToBinary('${wast}'); + var m = new WebAssembly.Instance(new WebAssembly.Module(wasm)); + `); + var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0]; + var lines = wasmScript.source.text.split('\n'); + return lines.map((l, n) => { return { str: l, offsets: wasmScript.getLineOffsets(n + 1) }; }); +} + +var result1 = getAllOffsets('(module \ + (func (nop)) \ + (func (drop (f32.sqrt (f32.add (f32.const 1.0) (f32.const 2.0))))) \ +)'); + +var nopLine = result1.filter(i => i.str.indexOf('nop') >= 0); +assertEq(nopLine.length, 1); +// The nopLine shall have single offset. +assertEq(nopLine[0].offsets.length, 1); +assertEq(nopLine[0].offsets[0] > 0, true); + +var singleOffsetLines = result1.filter(i => i.offsets.length === 1); +// There shall be total 8 lines with single offset. +assertEq(singleOffsetLines.length, 8); + +// Checking if all reported offsets can be resolved back to the corresponding +// line number. +function checkOffsetLineNumberMapping(wast, offsetsExpected) { + var sandbox = newGlobal(''); + var dbg = new Debugger(); + dbg.addDebuggee(sandbox); + sandbox.eval(` +var wasm = wasmTextToBinary('${wast}'); +var module = new WebAssembly.Module(wasm); +imports = {} +for (let descriptor of WebAssembly.Module.imports(module)) { + imports[descriptor.module] = {} + switch(descriptor.kind) { + case "function": imports[descriptor.module][descriptor.name] = new Function(''); break; + } +} +var instance = new WebAssembly.Instance(module, imports); +`); + var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0]; + assertEq(wasmScript.startLine, 1); + assertEq(wasmScript.lineCount >= 0, true); + var lines = wasmScript.source.text.split('\n'); + var offsetsFound = 0; + lines.forEach(function (l, n) { + var offsets = wasmScript.getLineOffsets(n + 1); + if (offsets.length < 1) return; + assertEq(offsets.length, 1); + assertEq(offsets[0] > 0, true); + offsetsFound++; + var loc = wasmScript.getOffsetLocation(offsets[0]); + assertEq(loc instanceof Object, true); + assertEq(loc.isEntryPoint, true); + assertEq(loc.lineNumber, n + 1); + assertEq(loc.columnNumber > 0, true); + }); + assertEq(offsetsFound, offsetsExpected); +} + +checkOffsetLineNumberMapping('(module (func))', 1); +checkOffsetLineNumberMapping('(module (func (nop)))', 2); +checkOffsetLineNumberMapping('(module (import "a" "b"))', 0); +checkOffsetLineNumberMapping('(module \ + (func (nop)) \ + (func (drop (f32.sqrt (f32.add (f32.const 1.0) (f32.const 2.0))))) \ +)', 8); +checkOffsetLineNumberMapping('(module \ + (func (local i32) i32.const 0 i32.const 1 set_local 0 get_local 0 call 0 i32.add nop drop) \ +)', 9); + +// Checking that there are no offsets are present in a wasm instance script for +// which debug mode was not enabled. +function getWasmScriptAfterDebuggerAttached(wast) { + var sandbox = newGlobal(''); + var dbg = new Debugger(); + sandbox.eval(` + var wasm = wasmTextToBinary('${wast}'); + var m = new WebAssembly.Instance(new WebAssembly.Module(wasm)); + `); + // Attaching after wasm instance is created. + dbg.addDebuggee(sandbox); + var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0]; + return wasmScript; +} + +var wasmScript1 = getWasmScriptAfterDebuggerAttached('(module (func (nop)))'); +var wasmLines1 = wasmScript1.source.text.split('\n'); +assertEq(wasmScript1.startLine, 1); +assertEq(wasmScript1.lineCount, 0); +assertEq(wasmLines1.every((l, n) => wasmScript1.getLineOffsets(n + 1).length == 0), true); + +// Checking that we must not resolve any location for any offset in a wasm +// instance which debug mode was not enabled. +var wasmScript2 = getWasmScriptAfterDebuggerAttached('(module (func (nop)))'); +for (var i = wasmTextToBinary('(module (func (nop)))').length - 1; i >= 0; i--) + assertThrowsInstanceOf(() => wasmScript2.getOffsetLocation(i), Error); diff --git a/js/src/jit-test/tests/debug/wasm-07.js b/js/src/jit-test/tests/debug/wasm-07.js index 890b6168390e..b045597c8ba0 100644 --- a/js/src/jit-test/tests/debug/wasm-07.js +++ b/js/src/jit-test/tests/debug/wasm-07.js @@ -15,9 +15,7 @@ wasmRunWithDebugger( function ({dbg}) { offsets = []; dbg.onEnterFrame = function (frame) { - if (frame.type != 'wasmcall') { - return; - } + if (frame.type != 'wasmcall') return; offsets.push(frame.offset); frame.onStep = function () { offsets.push(frame.offset); @@ -26,16 +24,16 @@ wasmRunWithDebugger( offsets.push(frame.offset); }; }; - }, - function ({wasmScript, error}) { - assertEq(error, undefined); - assertEq(offsets.length, 5); - offsets.forEach(offset => { - var loc = wasmScript.getOffsetLocation(offset); - assertEq(loc.isEntryPoint, true); - assertEq(loc.lineNumber > 0, true); - assertEq(loc.columnNumber > 0, true); - assertEq(wasmScript.getLineOffsets(loc.lineNumber).length, 1); - }); - } + }, + function ({wasmScript, error}) { + assertEq(error, undefined); + assertEq(offsets.length, 5); + offsets.forEach(offset => { + var loc = wasmScript.getOffsetLocation(offset); + assertEq(loc.isEntryPoint, true); + assertEq(loc.lineNumber > 0, true); + assertEq(loc.columnNumber > 0, true); + assertEq(wasmScript.getLineOffsets(loc.lineNumber).length, 1); + }); + } ); diff --git a/js/src/jit-test/tests/debug/wasm-binary-sources.js b/js/src/jit-test/tests/debug/wasm-binary-sources.js index a0b9545829b7..d352eef6543a 100644 --- a/js/src/jit-test/tests/debug/wasm-binary-sources.js +++ b/js/src/jit-test/tests/debug/wasm-binary-sources.js @@ -19,8 +19,8 @@ assertEq(s.format, "wasm"); var source = s.source; -// The text is never generated with the native Debugger API. -assertEq(source.text.includes('module'), false); +// The text is generated if wasm binary sources are disabled. +assertEq(source.text.includes('module'), true); assertThrowsInstanceOf(() => source.binary, Error); // Enable binary sources. @@ -31,7 +31,7 @@ assertEq(s.format, "wasm"); var source2 = s.source; -// The text is predefined if wasm binary sources are enabled. +// The text is '[wasm]' if wasm binary sources are enabled. assertEq(source2.text, '[wasm]'); // The binary contains Uint8Array which is equal to wasm bytecode; arraysEqual(source2.binary, wasmTextToBinary('(module (func) (export "" 0))')); diff --git a/js/src/jit-test/tests/debug/wasm-breakpoint.js b/js/src/jit-test/tests/debug/wasm-breakpoint.js index 039cd5e1c63a..249342907b7e 100644 --- a/js/src/jit-test/tests/debug/wasm-breakpoint.js +++ b/js/src/jit-test/tests/debug/wasm-breakpoint.js @@ -6,55 +6,21 @@ load(libdir + "wasm.js"); if (!wasmDebuggingIsSupported()) quit(); -function runTest(wast, initFunc, doneFunc) { - let g = newGlobal(''); - let dbg = new Debugger(g); - - g.eval(` -var { binary, offsets } = wasmTextToBinary('${wast}', /* offsets */ true); -var m = new WebAssembly.Instance(new WebAssembly.Module(binary));`); - - var { offsets } = g; - - var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0]; - - initFunc({ - dbg, - wasmScript, - g, - breakpoints: offsets - }); - - let result, error; - try { - result = g.eval("m.exports.test()"); - } catch (ex) { - error = ex; - } - - doneFunc({ - dbg, - result, - error, - wasmScript, - g - }); -} - - -var onBreakpointCalled; - // Checking if we can stop at specified breakpoint. -runTest( +var onBreakpointCalled; +wasmRunWithDebugger( '(module (func (nop) (nop)) (export "test" 0))', - function ({wasmScript, breakpoints}) { + undefined, + function ({wasmScript}) { + var breakpoints = wasmGetScriptBreakpoints(wasmScript); assertEq(breakpoints.length, 3); - assertEq(breakpoints[0] > 0, true); + assertEq(breakpoints[0].offset > 0, true); // Checking if breakpoints offsets are in ascending order. - assertEq(breakpoints[0] < breakpoints[1], true); - assertEq(breakpoints[1] < breakpoints[2], true); + assertEq(breakpoints[0].offset < breakpoints[1].offset, true); + assertEq(breakpoints[1].offset < breakpoints[2].offset, true); onBreakpointCalled = 0; - breakpoints.forEach(function (offset) { + breakpoints.forEach(function (bp) { + var offset = bp.offset; wasmScript.setBreakpoint(offset, { hit: (frame) => { assertEq(frame.offset, offset); @@ -70,15 +36,18 @@ runTest( ); // Checking if we can remove breakpoint one by one. -runTest( +wasmRunWithDebugger( '(module (func (nop) (nop)) (export "test" 0))', - function ({wasmScript, breakpoints}) { + undefined, + function ({wasmScript}) { + var breakpoints = wasmGetScriptBreakpoints(wasmScript); onBreakpointCalled = 0; var handlers = []; - breakpoints.forEach(function (offset, i) { + breakpoints.forEach(function (bp, i) { + var offset = bp.offset; wasmScript.setBreakpoint(offset, handlers[i] = { hit: (frame) => { - assertEq(frame.offset, breakpoints[0]); + assertEq(frame.offset, breakpoints[0].offset); onBreakpointCalled++; // Removing all handlers. handlers.forEach(h => wasmScript.clearBreakpoint(h)); @@ -93,15 +62,18 @@ runTest( ); // Checking if we can remove breakpoint one by one from a breakpoint handler. -runTest( +wasmRunWithDebugger( '(module (func (nop) (nop)) (export "test" 0))', - function ({wasmScript, breakpoints}) { + undefined, + function ({wasmScript}) { + var breakpoints = wasmGetScriptBreakpoints(wasmScript); onBreakpointCalled = 0; var handlers = []; - breakpoints.forEach(function (offset, i) { + breakpoints.forEach(function (bp, i) { + var offset = bp.offset; wasmScript.setBreakpoint(offset, handlers[i] = { hit: (frame) => { - assertEq(frame.offset, breakpoints[0]); + assertEq(frame.offset, breakpoints[0].offset); onBreakpointCalled++; // Removing all handlers. handlers.forEach(h => wasmScript.clearBreakpoint(h)); @@ -118,13 +90,16 @@ runTest( // Checking if we can remove breakpoint one by one from onEnterFrame, // but onStep will still work. var onStepCalled; -runTest( +wasmRunWithDebugger( '(module (func (nop) (nop)) (export "test" 0))', - function ({dbg, wasmScript, breakpoints}) { + undefined, + function ({dbg, wasmScript}) { + var breakpoints = wasmGetScriptBreakpoints(wasmScript); onBreakpointCalled = 0; onStepCalled = []; var handlers = []; - breakpoints.forEach(function (offset, i) { + breakpoints.forEach(function (bp, i) { + var offset = bp.offset; wasmScript.setBreakpoint(offset, handlers[i] = { hit: (frame) => { assertEq(false, true); @@ -150,14 +125,17 @@ runTest( ); // Checking if we can remove all breakpoints. -runTest( +wasmRunWithDebugger( '(module (func (nop) (nop)) (export "test" 0))', - function ({wasmScript, breakpoints}) { + undefined, + function ({wasmScript}) { + var breakpoints = wasmGetScriptBreakpoints(wasmScript); onBreakpointCalled = 0; - breakpoints.forEach(function (offset, i) { + breakpoints.forEach(function (bp, i) { + var offset = bp.offset; wasmScript.setBreakpoint(offset, { hit: (frame) => { - assertEq(frame.offset, breakpoints[0]); + assertEq(frame.offset, breakpoints[0].offset); onBreakpointCalled++; // Removing all handlers. wasmScript.clearAllBreakpoints(); @@ -172,11 +150,14 @@ runTest( ); // Checking if breakpoints are removed after debugger has been detached. -runTest( +wasmRunWithDebugger( '(module (func (nop) (nop)) (export "test" 0))', - function ({dbg, wasmScript, g, breakpoints}) { + undefined, + function ({dbg, wasmScript, g}) { + var breakpoints = wasmGetScriptBreakpoints(wasmScript); onBreakpointCalled = 0; - breakpoints.forEach(function (offset, i) { + breakpoints.forEach(function (bp, i) { + var offset = bp.offset; wasmScript.setBreakpoint(offset, { hit: (frame) => { onBreakpointCalled++; diff --git a/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js b/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js index c47f2ccd17b8..70e6ab1f3b92 100644 --- a/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js +++ b/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js @@ -5,15 +5,15 @@ load(libdir + "asserts.js"); if (!wasmDebuggingIsSupported()) - quit(); + quit(); // Checking if experimental format generates internal source map to binary file // by querying debugger scripts getAllColumnOffsets. +// (Notice that the source map will not be produced by wasmBinaryToText) function getAllOffsets(wast) { var sandbox = newGlobal(''); var dbg = new Debugger(); dbg.addDebuggee(sandbox); - dbg.allowWasmBinarySource = true; sandbox.eval(` var wasm = wasmTextToBinary('${wast}'); var m = new WebAssembly.Instance(new WebAssembly.Module(wasm)); @@ -28,10 +28,8 @@ var offsets1 = getAllOffsets('(module \ )'); // There shall be total 8 lines with single and unique offset per line. -var usedOffsets = Object.create(null), - usedLines = Object.create(null); +var usedOffsets = Object.create(null), usedLines = Object.create(null); assertEq(offsets1.length, 8); - offsets1.forEach(({offset, lineNumber, columnNumber}) => { assertEq(offset > 0, true); assertEq(lineNumber > 0, true); diff --git a/js/src/jit-test/tests/wasm/atomic.js b/js/src/jit-test/tests/wasm/atomic.js index 9dfb8d1e767d..23be9da9dae5 100644 --- a/js/src/jit-test/tests/wasm/atomic.js +++ b/js/src/jit-test/tests/wasm/atomic.js @@ -84,10 +84,79 @@ for (let align of [1, 2, 4, 8]) { assertEq(valText(text), align == 4); } +// Check that the text output is sane. + +for (let [type,view] of [['i32','8_u'],['i32','16_u'],['i32',''],['i64','8_u'],['i64','16_u'],['i64','32_u'],['i64','']]) { + let addr = "i32.const 48"; + let value = `${type}.const 1`; + let value2 = `${type}.const 2`; + for (let op of ["add", "and", "or", "xor", "xchg"]) { + let operator = `${type}.atomic.rmw${view}.${op}`; + let text = `(module (memory 1 1 shared) + (func (result ${type}) (${operator} (${addr}) (${value}))) + (export "" 0))`; + checkRoundTrip(text, [addr, value, operator]); + } + { + let operator = `${type}.atomic.rmw${view}.cmpxchg`; + let text = `(module (memory 1 1 shared) + (func (result ${type}) (${operator} (${addr}) (${value}) (${value2}))) + (export "" 0))`; + checkRoundTrip(text, [addr, value, value2, operator]); + } + { + let operator = `${type}.atomic.load${view}`; + let text = `(module (memory 1 1 shared) + (func (result ${type}) (${operator} (${addr}))) + (export "" 0))`; + checkRoundTrip(text, [addr, operator]); + } + { + let operator = `${type}.atomic.store${view}`; + let text = `(module (memory 1 1 shared) + (func (${operator} (${addr}) (${value}))) + (export "" 0))`; + checkRoundTrip(text, [addr, value, operator]); + } +} + +for (let type of ['i32', 'i64']) { + let addr = "i32.const 48"; + let operator = `${type}.atomic.wait` + let value = `${type}.const 1`; + let timeout = "i64.const 314159"; + let text = `(module (memory 1 1 shared) + (func (result i32) (${operator} (${addr}) (${value}) (${timeout}))) + (export "" 0))`; + checkRoundTrip(text, [addr, value, timeout, operator]); +} + +{ + let addr = "i32.const 48"; + let operator = "atomic.wake" + let count = "i32.const 1"; + let text = `(module (memory 1 1 shared) + (func (result i32) (${operator} (${addr}) (${count}))) + (export "" 0))`; + checkRoundTrip(text, [addr, count, operator]); +} + function valText(text) { return WebAssembly.validate(wasmTextToBinary(text)); } +function checkRoundTrip(text, ss) { + let input = wasmTextToBinary(text); + let output = wasmBinaryToText(input).split("\n").map(String.trim); + for (let s of output) { + if (ss.length == 0) + break; + if (s.match(ss[0])) + ss.shift(); + } + assertEq(ss.length, 0); +} + // Test that atomic operations work. function I64(hi, lo) { diff --git a/js/src/jit-test/tests/wasm/gc/structs.js b/js/src/jit-test/tests/wasm/gc/structs.js index 75044c94057c..dca23258c018 100644 --- a/js/src/jit-test/tests/wasm/gc/structs.js +++ b/js/src/jit-test/tests/wasm/gc/structs.js @@ -69,6 +69,15 @@ assertEq(ins.hello(4.0, 1), 16.0) assertEq(ins.x1(12), 36) assertEq(ins.x2(8), Math.PI) +// Crude but at least checks that we have *something*. + +var txt = wasmBinaryToText(bin); +var re = /\(type\s+\$[a-z0-9]+\s+\(struct/gm; +assertEq(Array.isArray(re.exec(txt)), true); +assertEq(Array.isArray(re.exec(txt)), true); +assertEq(Array.isArray(re.exec(txt)), true); +assertEq(Array.isArray(re.exec(txt)), false); + // The field name is optional, so this should work. wasmEvalText(` diff --git a/js/src/jit-test/tests/wasm/to-text.js b/js/src/jit-test/tests/wasm/to-text.js new file mode 100644 index 000000000000..e89ce615b018 --- /dev/null +++ b/js/src/jit-test/tests/wasm/to-text.js @@ -0,0 +1,293 @@ +var caught = false; +try { + wasmBinaryToText(new Int8Array(1)); +} catch (e) { + caught = true; +} +assertEq(caught, true); + +assertErrorMessage(() => wasmBinaryToText(wasmTextToBinary(`(module (func (result i32) (f32.const 13.37)))`)), WebAssembly.CompileError, /type mismatch/); + +function runTest(code) { + var expected = wasmTextToBinary(code); + var s = wasmBinaryToText(expected); + print("TEXT: " + s); + var roundtrip = wasmTextToBinary(s); + assertDeepEq(expected, roundtrip); +} + +// Smoke test +runTest(` +(module + (func (param i32) (result f64) + (local $l f32) + (block + (set_local $l (f32.const 0.0)) + (loop $exit $cont + (br_if $exit (get_local 0)) + (br 2) + ) + (drop (if f64 (i32.const 1) + (f64.min (f64.neg (f64.const 1)) (f64.const 0)) + (f64.add (f64.const 0.5) (f64.load offset=0 (i32.const 0)) ) + )) + ) + (i32.store16 (i32.const 8) (i32.const 128)) + + (return (f64.const 0)) + ) + (export "test" 0) + (memory 1 10) +)`); + +// Constants, stores and loads +runTest(` +(module (func + (local i32) (local f32) (local f64) + (drop (i32.const 0)) + (drop (i32.const 100002)) + (drop (f32.const 0.0)) + (drop (f32.const 1.5)) + (drop (f64.const 0.0)) + (drop (f64.const -10.25)) + (i32.store (i32.const 0) (i32.load (i32.const 0))) + (i32.store8 (i32.const 1) (i32.load8_s (i32.const 2))) + (i32.store8 (i32.const 3) (i32.load8_u (i32.const 4))) + (i32.store16 (i32.const 2) (i32.load16_s (i32.const 0))) + (i32.store16 (i32.const 1) (i32.load16_u (i32.const 0))) + (f32.store (i32.const 5) (f32.load (i32.const 6))) + (f64.store (i32.const 5) (f64.load (i32.const 6))) + (set_local 0 (get_local 0)) + (set_local 2 (get_local 2)) +)(memory 100))`); + +// Branching +runTest(` +(module +(func + (block (block (block (nop)))) + (block (loop )) + (if (i32.const 0) (block $label (nop))) + (if (i32.const 1) (nop) (loop $exit $cont (block ))) + (block $l (br $l)) + (block $m (block (block (br $m)))) + (block $k (br_if 0 (i32.const 0)) (return)) + (block $n (block (block (br_if 2 (i32.const 1)) (nop)))) + (block $1 (block $2 (block $3 (br_table $2 $3 $1 (i32.const 1)) )) (nop)) + (loop $exit $cont (br_if $cont (i32.const 0)) (nop)) + (return) +) +(func (result f32) (return (f32.const -0.5))) +(memory 0) +)`); + +// i32, f32 and f64 operations +runTest(` +(module + (func $iadd (param $x i32) (param $y i32) (result i32) (i32.add (get_local $x) (get_local $y))) + (func $isub (param $x i32) (param $y i32) (result i32) (i32.sub (get_local $x) (get_local $y))) + (func $imul (param $x i32) (param $y i32) (result i32) (i32.mul (get_local $x) (get_local $y))) + (func $idiv_s (param $x i32) (param $y i32) (result i32) (i32.div_s (get_local $x) (get_local $y))) + (func $idiv_u (param $x i32) (param $y i32) (result i32) (i32.div_u (get_local $x) (get_local $y))) + (func $irem_s (param $x i32) (param $y i32) (result i32) (i32.rem_s (get_local $x) (get_local $y))) + (func $irem_u (param $x i32) (param $y i32) (result i32) (i32.rem_u (get_local $x) (get_local $y))) + (func $iand (param $x i32) (param $y i32) (result i32) (i32.and (get_local $x) (get_local $y))) + (func $ior (param $x i32) (param $y i32) (result i32) (i32.or (get_local $x) (get_local $y))) + (func $ixor (param $x i32) (param $y i32) (result i32) (i32.xor (get_local $x) (get_local $y))) + (func $ishl (param $x i32) (param $y i32) (result i32) (i32.shl (get_local $x) (get_local $y))) + (func $ishr_s (param $x i32) (param $y i32) (result i32) (i32.shr_s (get_local $x) (get_local $y))) + (func $ishr_u (param $x i32) (param $y i32) (result i32) (i32.shr_u (get_local $x) (get_local $y))) + (func $iclz (param $x i32) (result i32) (i32.clz (get_local $x))) + (func $ictz (param $x i32) (result i32) (i32.ctz (get_local $x))) + (func $ipopcnt (param $x i32) (result i32) (i32.popcnt (get_local $x))) + (func $ieq (param $x i32) (param $y i32) (result i32) (i32.eq (get_local $x) (get_local $y))) + (func $ine (param $x i32) (param $y i32) (result i32) (i32.ne (get_local $x) (get_local $y))) + (func $ilt_s (param $x i32) (param $y i32) (result i32) (i32.lt_s (get_local $x) (get_local $y))) + (func $ilt_u (param $x i32) (param $y i32) (result i32) (i32.lt_u (get_local $x) (get_local $y))) + (func $ile_s (param $x i32) (param $y i32) (result i32) (i32.le_s (get_local $x) (get_local $y))) + (func $ile_u (param $x i32) (param $y i32) (result i32) (i32.le_u (get_local $x) (get_local $y))) + (func $igt_s (param $x i32) (param $y i32) (result i32) (i32.gt_s (get_local $x) (get_local $y))) + (func $igt_u (param $x i32) (param $y i32) (result i32) (i32.gt_u (get_local $x) (get_local $y))) + (func $ige_s (param $x i32) (param $y i32) (result i32) (i32.ge_s (get_local $x) (get_local $y))) + (func $ige_u (param $x i32) (param $y i32) (result i32) (i32.ge_u (get_local $x) (get_local $y))) + + (func $fadd (param $x f32) (param $y f32) (result f32) (f32.add (get_local $x) (get_local $y))) + (func $fsub (param $x f32) (param $y f32) (result f32) (f32.sub (get_local $x) (get_local $y))) + (func $fmul (param $x f32) (param $y f32) (result f32) (f32.mul (get_local $x) (get_local $y))) + (func $fdiv (param $x f32) (param $y f32) (result f32) (f32.div (get_local $x) (get_local $y))) + (func $fsqrt (param $x f32) (result f32) (f32.sqrt (get_local $x))) + (func $fmin (param $x f32) (param $y f32) (result f32) (f32.min (get_local $x) (get_local $y))) + (func $fmax (param $x f32) (param $y f32) (result f32) (f32.max (get_local $x) (get_local $y))) + (func $fceil (param $x f32) (result f32) (f32.ceil (get_local $x))) + (func $ffloor (param $x f32) (result f32) (f32.floor (get_local $x))) + (func $fabs (param $x f32) (result f32) (f32.abs (get_local $x))) + (func $fneg (param $x f32) (result f32) (f32.neg (get_local $x))) + + (func $dadd (param $x f64) (param $y f64) (result f64) (f64.add (get_local $x) (get_local $y))) + (func $dsub (param $x f64) (param $y f64) (result f64) (f64.sub (get_local $x) (get_local $y))) + (func $dmul (param $x f64) (param $y f64) (result f64) (f64.mul (get_local $x) (get_local $y))) + (func $ddiv (param $x f64) (param $y f64) (result f64) (f64.div (get_local $x) (get_local $y))) + (func $dceil (param $x f64) (result f64) (f64.ceil (get_local $x))) + (func $dfloor (param $x f64) (result f64) (f64.floor (get_local $x))) + (func $dabs (param $x f64) (result f64) (f64.abs (get_local $x))) + (func $dneg (param $x f64) (result f64) (f64.neg (get_local $x))) +(memory 0))`); + +// conversions +runTest(` +(module + (func $itrunc_s_f32 (param $x f32) (result i32) (i32.trunc_s/f32 (get_local $x))) + (func $itrunc_u_f32 (param $x f32) (result i32) (i32.trunc_u/f32 (get_local $x))) + (func $itrunc_s_f64 (param $x f64) (result i32) (i32.trunc_s/f64 (get_local $x))) + (func $itrunc_u_f64 (param $x f64) (result i32) (i32.trunc_u/f64 (get_local $x))) + (func $fconvert_s_i32 (param $x i32) (result f32) (f32.convert_s/i32 (get_local $x))) + (func $dconvert_s_i32 (param $x i32) (result f64) (f64.convert_s/i32 (get_local $x))) + (func $fconvert_u_i32 (param $x i32) (result f32) (f32.convert_u/i32 (get_local $x))) + (func $dconvert_u_i32 (param $x i32) (result f64) (f64.convert_u/i32 (get_local $x))) + (func $dpromote_f32 (param $x f32) (result f64) (f64.promote/f32 (get_local $x))) + (func $fdemote_f64 (param $x f64) (result f32) (f32.demote/f64 (get_local $x))) +(memory 0))`); + +// function calls +runTest(` +(module + (type $type1 (func (param i32) (result i32))) + (import $import1 "mod" "test" (param f32) (result f32)) + (table anyfunc (elem $func1 $func2)) + (func $func1 (param i32) (param f32) (nop)) + (func $func2 (param i32) (result i32) (get_local 0)) + (func $test + (call $func1 + (call_indirect $type1 (i32.const 2) (i32.const 1)) + (call $import1 (f32.const 1.0)) + ) + ) + (export "test" $test) + (memory 1) +)`); + +// default memory export from binaryen +runTest(`(module (func (nop)) (memory 0 65535))`); + +// stack-machine code that isn't directly representable as an AST +runTest(` +(module + (func (result i32) + (local $x i32) + i32.const 100 + set_local $x + i32.const 200 + set_local $x + i32.const 400 + set_local $x + i32.const 2 + i32.const 16 + nop + set_local $x + i32.const 3 + i32.const 17 + set_local $x + i32.const 18 + set_local $x + i32.lt_s + if i32 + i32.const 101 + set_local $x + i32.const 8 + i32.const 102 + set_local $x + else + i32.const 103 + set_local $x + i32.const 900 + i32.const 104 + set_local $x + i32.const 105 + set_local $x + end + i32.const 107 + set_local $x + get_local $x + i32.add + i32.const 106 + set_local $x + ) + (export "" 0) +)`); + +// more stack-machine code that isn't directly representable as an AST +runTest(` +(module + (func $return_void) + + (func (result i32) + (local $x i32) + i32.const 0 + block + i32.const 1 + set_local $x + end + i32.const 2 + set_local $x + i32.const 3 + loop + i32.const 4 + set_local $x + end + i32.const 5 + set_local $x + i32.add + call $return_void + ) + (export "" 0) +)`); + +runTest(` + (module + (func $func + block $block + i32.const 0 + if + i32.const 0 + if + end + else + end + end + ) + (export "" 0) +)`); + +// Branch table. +runTest(`(module + (func (export "run") (param $p i32) (local $n i32) + i32.const 0 + set_local $n + loop $outer + block $c block $b block $a + loop $inner + get_local $p + br_table $b $a $c $inner $outer + end $inner + end $a + get_local $n + i32.const 1 + i32.add + set_local $n + end $b + block + get_local $n + i32.const 2 + i32.add + set_local $n + end + end $c + end $outer + ) +)`); + +// Import as a start function. +runTest(`(module + (import "env" "test" (func)) + (start 0) +)`); diff --git a/js/src/moz.build b/js/src/moz.build index 043e49ede68c..f9b803b400b8 100755 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -401,6 +401,8 @@ UNIFIED_SOURCES += [ 'vm/Xdr.cpp', 'wasm/AsmJS.cpp', 'wasm/WasmBaselineCompile.cpp', + 'wasm/WasmBinaryToAST.cpp', + 'wasm/WasmBinaryToText.cpp', 'wasm/WasmBuiltins.cpp', 'wasm/WasmCode.cpp', 'wasm/WasmCompile.cpp', @@ -418,6 +420,7 @@ UNIFIED_SOURCES += [ 'wasm/WasmStubs.cpp', 'wasm/WasmTable.cpp', 'wasm/WasmTextToBinary.cpp', + 'wasm/WasmTextUtils.cpp', 'wasm/WasmTypes.cpp', 'wasm/WasmValidate.cpp' ] diff --git a/js/src/wasm/WasmAST.h b/js/src/wasm/WasmAST.h index da52a778dd3b..f0302d9d4ec0 100644 --- a/js/src/wasm/WasmAST.h +++ b/js/src/wasm/WasmAST.h @@ -126,17 +126,10 @@ struct AstBase } }; -struct AstNode -{ - void* operator new(size_t numBytes, LifoAlloc& astLifo) throw() { - return astLifo.alloc(numBytes); - } -}; - class AstFuncType; class AstStructType; -class AstTypeDef : public AstNode +class AstTypeDef : public AstBase { protected: enum class Which { IsFuncType, IsStructType }; @@ -265,6 +258,19 @@ AstTypeDef::asStructType() const return *static_cast(this); } +const uint32_t AstNodeUnknownOffset = 0; + +class AstNode : public AstBase +{ + uint32_t offset_; // if applicable, offset in the binary format file + + public: + AstNode() : offset_(AstNodeUnknownOffset) {} + + uint32_t offset() const { return offset_; } + void setOffset(uint32_t offset) { offset_ = offset; } +}; + enum class AstExprKind { AtomicCmpXchg, @@ -856,6 +862,7 @@ class AstFunc : public AstNode AstValTypeVector vars_; AstNameVector localNames_; AstExprVector body_; + uint32_t endOffset_; // if applicable, offset in the binary format file public: AstFunc(AstName name, AstRef ft, AstValTypeVector&& vars, @@ -864,13 +871,16 @@ class AstFunc : public AstNode funcType_(ft), vars_(std::move(vars)), localNames_(std::move(locals)), - body_(std::move(body)) + body_(std::move(body)), + endOffset_(AstNodeUnknownOffset) {} AstRef& funcType() { return funcType_; } const AstValTypeVector& vars() const { return vars_; } const AstNameVector& locals() const { return localNames_; } const AstExprVector& body() const { return body_; } AstName name() const { return name_; } + uint32_t endOffset() const { return endOffset_; } + void setEndOffset(uint32_t offset) { endOffset_ = offset; } }; class AstGlobal : public AstNode diff --git a/js/src/wasm/WasmBinaryToAST.cpp b/js/src/wasm/WasmBinaryToAST.cpp new file mode 100644 index 000000000000..da72508716d2 --- /dev/null +++ b/js/src/wasm/WasmBinaryToAST.cpp @@ -0,0 +1,2390 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * Copyright 2016 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wasm/WasmBinaryToAST.h" + +#include "mozilla/MathAlgorithms.h" +#include "mozilla/Sprintf.h" + +#include "vm/JSContext.h" +#include "vm/Realm.h" +#include "wasm/WasmOpIter.h" +#include "wasm/WasmValidate.h" + +using namespace js; +using namespace js::wasm; + +using mozilla::FloorLog2; + +enum AstDecodeTerminationKind +{ + Unknown, + End, + Else +}; + +struct AstDecodeStackItem +{ + AstExpr* expr; + AstDecodeTerminationKind terminationKind; + ExprType type; + + explicit AstDecodeStackItem() + : expr(nullptr), + terminationKind(AstDecodeTerminationKind::Unknown), + type(ExprType::Limit) + {} + explicit AstDecodeStackItem(AstDecodeTerminationKind terminationKind, ExprType type) + : expr(nullptr), + terminationKind(terminationKind), + type(type) + {} + explicit AstDecodeStackItem(AstExpr* expr) + : expr(expr), + terminationKind(AstDecodeTerminationKind::Unknown), + type(ExprType::Limit) + {} +}; + +// We don't define a Value type because OpIter doesn't push void values, which +// we actually need here because we're building an AST, so we maintain our own +// stack. +struct AstDecodePolicy +{ + typedef Nothing Value; + typedef Nothing ControlItem; +}; + +typedef OpIter AstDecodeOpIter; + +class AstDecodeContext +{ + public: + typedef AstVector AstDecodeStack; + typedef AstVector DepthStack; + + JSContext* cx; + LifoAlloc& lifo; + Decoder& d; + bool generateNames; + + private: + ModuleEnvironment env_; + + AstModule& module_; + AstDecodeOpIter *iter_; + AstDecodeStack exprs_; + DepthStack depths_; + const ValTypeVector* locals_; + AstNameVector blockLabels_; + uint32_t currentLabelIndex_; + ExprType retType_; + + public: + AstDecodeContext(JSContext* cx, LifoAlloc& lifo, Decoder& d, AstModule& module, + bool generateNames, HasGcTypes hasGcTypes) + : cx(cx), + lifo(lifo), + d(d), + generateNames(generateNames), + env_(CompileMode::Once, Tier::Ion, DebugEnabled::False, hasGcTypes, + cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled() + ? Shareable::True + : Shareable::False), + module_(module), + iter_(nullptr), + exprs_(lifo), + depths_(lifo), + locals_(nullptr), + blockLabels_(lifo), + currentLabelIndex_(0), + retType_(ExprType::Limit) + {} + + ModuleEnvironment& env() { return env_; } + + AstModule& module() { return module_; } + AstDecodeOpIter& iter() { return *iter_; } + AstDecodeStack& exprs() { return exprs_; } + DepthStack& depths() { return depths_; } + + AstNameVector& blockLabels() { return blockLabels_; } + + ExprType retType() const { return retType_; } + const ValTypeVector& locals() const { return *locals_; } + + void popBack() { return exprs().popBack(); } + AstDecodeStackItem popCopy() { return exprs().popCopy(); } + AstDecodeStackItem& top() { return exprs().back(); } + MOZ_MUST_USE bool push(AstDecodeStackItem item) { return exprs().append(item); } + + bool needFirst() { + for (size_t i = depths().back(); i < exprs().length(); ++i) { + if (!exprs()[i].expr->isVoid()) + return true; + } + return false; + } + + AstExpr* handleVoidExpr(AstExpr* voidNode) + { + MOZ_ASSERT(voidNode->isVoid()); + + // To attach a node that "returns void" to the middle of an AST, wrap it + // in a first node next to the node it should accompany. + if (needFirst()) { + AstExpr *prev = popCopy().expr; + + // If the previous/A node is already a First, reuse it. + if (prev->kind() == AstExprKind::First) { + if (!prev->as().exprs().append(voidNode)) + return nullptr; + return prev; + } + + AstExprVector exprs(lifo); + if (!exprs.append(prev)) + return nullptr; + if (!exprs.append(voidNode)) + return nullptr; + + return new(lifo) AstFirst(std::move(exprs)); + } + + return voidNode; + } + + void startFunction(AstDecodeOpIter* iter, const ValTypeVector* locals, ExprType retType) + { + iter_ = iter; + locals_ = locals; + currentLabelIndex_ = 0; + retType_ = retType; + } + void endFunction() + { + iter_ = nullptr; + locals_ = nullptr; + retType_ = ExprType::Limit; + MOZ_ASSERT(blockLabels_.length() == 0); + } + uint32_t nextLabelIndex() + { + return currentLabelIndex_++; + } +}; + +static bool +GenerateName(AstDecodeContext& c, const AstName& prefix, uint32_t index, AstName* name) +{ + if (!c.generateNames) { + *name = AstName(); + return true; + } + + AstVector result(c.lifo); + if (!result.append(u'$')) + return false; + if (!result.append(prefix.begin(), prefix.length())) + return false; + + uint32_t tmp = index; + do { + if (!result.append(u'0')) + return false; + tmp /= 10; + } while (tmp); + + if (index) { + char16_t* p = result.end(); + for (tmp = index; tmp; tmp /= 10) + *(--p) = u'0' + (tmp % 10); + } + + size_t length = result.length(); + char16_t* begin = result.extractOrCopyRawBuffer(); + if (!begin) + return false; + + *name = AstName(begin, length); + return true; +} + +static bool +GenerateRef(AstDecodeContext& c, const AstName& prefix, uint32_t index, AstRef* ref) +{ + MOZ_ASSERT(index != AstNoIndex); + + if (!c.generateNames) { + *ref = AstRef(index); + return true; + } + + AstName name; + if (!GenerateName(c, prefix, index, &name)) + return false; + MOZ_ASSERT(!name.empty()); + + *ref = AstRef(name); + ref->setIndex(index); + return true; +} + +static bool +GenerateFuncRef(AstDecodeContext& c, uint32_t funcIndex, AstRef* ref) +{ + if (funcIndex < c.module().numFuncImports()) { + *ref = AstRef(c.module().funcImportNames()[funcIndex]); + } else { + if (!GenerateRef(c, AstName(u"func"), funcIndex, ref)) + return false; + } + return true; +} + +static bool +AstDecodeCallArgs(AstDecodeContext& c, const FuncTypeWithId& funcType, AstExprVector* funcArgs) +{ + MOZ_ASSERT(!c.iter().currentBlockHasPolymorphicBase()); + + uint32_t numArgs = funcType.args().length(); + if (!funcArgs->resize(numArgs)) + return false; + + for (size_t i = 0; i < numArgs; ++i) + (*funcArgs)[i] = c.exprs()[c.exprs().length() - numArgs + i].expr; + + c.exprs().shrinkBy(numArgs); + + return true; +} + +static bool +AstDecodeExpr(AstDecodeContext& c); + +static bool +AstDecodeDrop(AstDecodeContext& c) +{ + if (!c.iter().readDrop()) + return false; + + AstDecodeStackItem value = c.popCopy(); + + AstExpr* tmp = new(c.lifo) AstDrop(*value.expr); + if (!tmp) + return false; + + tmp = c.handleVoidExpr(tmp); + if (!tmp) + return false; + + if (!c.push(AstDecodeStackItem(tmp))) + return false; + + return true; +} + +static bool +AstDecodeCall(AstDecodeContext& c) +{ + uint32_t funcIndex; + AstDecodeOpIter::ValueVector unusedArgs; + if (!c.iter().readCall(&funcIndex, &unusedArgs)) + return false; + + if (c.iter().currentBlockHasPolymorphicBase()) + return true; + + AstRef funcRef; + if (!GenerateFuncRef(c, funcIndex, &funcRef)) + return false; + + const FuncTypeWithId* funcType = c.env().funcTypes[funcIndex]; + + AstExprVector args(c.lifo); + if (!AstDecodeCallArgs(c, *funcType, &args)) + return false; + + AstCall* call = new(c.lifo) AstCall(Op::Call, funcType->ret(), funcRef, std::move(args)); + if (!call) + return false; + + AstExpr* result = call; + if (IsVoid(funcType->ret())) + result = c.handleVoidExpr(call); + + if (!c.push(AstDecodeStackItem(result))) + return false; + + return true; +} + +static bool +AstDecodeCallIndirect(AstDecodeContext& c) +{ + uint32_t funcTypeIndex; + AstDecodeOpIter::ValueVector unusedArgs; + if (!c.iter().readCallIndirect(&funcTypeIndex, nullptr, &unusedArgs)) + return false; + + if (c.iter().currentBlockHasPolymorphicBase()) + return true; + + AstDecodeStackItem index = c.popCopy(); + + AstRef funcTypeRef; + if (!GenerateRef(c, AstName(u"type"), funcTypeIndex, &funcTypeRef)) + return false; + + const FuncTypeWithId& funcType = c.env().types[funcTypeIndex].funcType(); + AstExprVector args(c.lifo); + if (!AstDecodeCallArgs(c, funcType, &args)) + return false; + + AstCallIndirect* call = + new(c.lifo) AstCallIndirect(funcTypeRef, funcType.ret(), std::move(args), index.expr); + if (!call) + return false; + + AstExpr* result = call; + if (IsVoid(funcType.ret())) + result = c.handleVoidExpr(call); + + if (!c.push(AstDecodeStackItem(result))) + return false; + + return true; +} + +static bool +AstDecodeGetBlockRef(AstDecodeContext& c, uint32_t depth, AstRef* ref) +{ + if (!c.generateNames || depth >= c.blockLabels().length()) { + // Also ignoring if it's a function body label. + *ref = AstRef(depth); + return true; + } + + uint32_t index = c.blockLabels().length() - depth - 1; + if (c.blockLabels()[index].empty()) { + if (!GenerateName(c, AstName(u"label"), c.nextLabelIndex(), &c.blockLabels()[index])) + return false; + } + *ref = AstRef(c.blockLabels()[index]); + ref->setIndex(depth); + return true; +} + +static bool +AstDecodeBrTable(AstDecodeContext& c) +{ + bool unreachable = c.iter().currentBlockHasPolymorphicBase(); + + Uint32Vector depths; + uint32_t defaultDepth; + ExprType type; + if (!c.iter().readBrTable(&depths, &defaultDepth, &type, nullptr, nullptr)) + return false; + + if (unreachable) + return true; + + AstRefVector table(c.lifo); + if (!table.resize(depths.length())) + return false; + + for (size_t i = 0; i < depths.length(); ++i) { + if (!AstDecodeGetBlockRef(c, depths[i], &table[i])) + return false; + } + + AstDecodeStackItem index = c.popCopy(); + AstDecodeStackItem value; + if (!IsVoid(type)) + value = c.popCopy(); + + AstRef def; + if (!AstDecodeGetBlockRef(c, defaultDepth, &def)) + return false; + + auto branchTable = new(c.lifo) AstBranchTable(*index.expr, def, std::move(table), value.expr); + if (!branchTable) + return false; + + if (!c.push(AstDecodeStackItem(branchTable))) + return false; + + return true; +} + +static bool +AstDecodeBlock(AstDecodeContext& c, Op op) +{ + MOZ_ASSERT(op == Op::Block || op == Op::Loop); + + if (!c.blockLabels().append(AstName())) + return false; + + if (op == Op::Loop) { + if (!c.iter().readLoop()) + return false; + } else { + if (!c.iter().readBlock()) + return false; + } + + if (!c.depths().append(c.exprs().length())) + return false; + + ExprType type; + while (true) { + if (!AstDecodeExpr(c)) + return false; + + const AstDecodeStackItem& item = c.top(); + if (!item.expr) { // Op::End was found + type = item.type; + c.popBack(); + break; + } + } + + AstExprVector exprs(c.lifo); + for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end(); + i != e; ++i) { + if (!exprs.append(i->expr)) + return false; + } + c.exprs().shrinkTo(c.depths().popCopy()); + + AstName name = c.blockLabels().popCopy(); + AstBlock* block = new(c.lifo) AstBlock(op, type, name, std::move(exprs)); + if (!block) + return false; + + AstExpr* result = block; + if (IsVoid(type)) + result = c.handleVoidExpr(block); + + if (!c.push(AstDecodeStackItem(result))) + return false; + + return true; +} + +static bool +AstDecodeIf(AstDecodeContext& c) +{ + if (!c.iter().readIf(nullptr)) + return false; + + AstDecodeStackItem cond = c.popCopy(); + + bool hasElse = false; + + if (!c.depths().append(c.exprs().length())) + return false; + + if (!c.blockLabels().append(AstName())) + return false; + + ExprType type; + while (true) { + if (!AstDecodeExpr(c)) + return false; + + const AstDecodeStackItem& item = c.top(); + if (!item.expr) { // Op::End was found + hasElse = item.terminationKind == AstDecodeTerminationKind::Else; + type = item.type; + c.popBack(); + break; + } + } + + AstExprVector thenExprs(c.lifo); + for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end(); + i != e; ++i) { + if (!thenExprs.append(i->expr)) + return false; + } + c.exprs().shrinkTo(c.depths().back()); + + AstExprVector elseExprs(c.lifo); + if (hasElse) { + while (true) { + if (!AstDecodeExpr(c)) + return false; + + const AstDecodeStackItem& item = c.top(); + if (!item.expr) { // Op::End was found + c.popBack(); + break; + } + } + + for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end(); + i != e; ++i) { + if (!elseExprs.append(i->expr)) + return false; + } + c.exprs().shrinkTo(c.depths().back()); + } + + c.depths().popBack(); + + AstName name = c.blockLabels().popCopy(); + + AstIf* if_ = new(c.lifo) AstIf(type, cond.expr, name, std::move(thenExprs), std::move(elseExprs)); + if (!if_) + return false; + + AstExpr* result = if_; + if (IsVoid(type)) + result = c.handleVoidExpr(if_); + + if (!c.push(AstDecodeStackItem(result))) + return false; + + return true; +} + +static bool +AstDecodeEnd(AstDecodeContext& c) +{ + LabelKind kind; + ExprType type; + if (!c.iter().readEnd(&kind, &type, nullptr)) + return false; + + c.iter().popEnd(); + + if (!c.push(AstDecodeStackItem(AstDecodeTerminationKind::End, type))) + return false; + + return true; +} + +static bool +AstDecodeElse(AstDecodeContext& c) +{ + ExprType type; + + if (!c.iter().readElse(&type, nullptr)) + return false; + + if (!c.push(AstDecodeStackItem(AstDecodeTerminationKind::Else, type))) + return false; + + return true; +} + +static bool +AstDecodeNop(AstDecodeContext& c) +{ + if (!c.iter().readNop()) + return false; + + AstExpr* tmp = new(c.lifo) AstNop(); + if (!tmp) + return false; + + tmp = c.handleVoidExpr(tmp); + if (!tmp) + return false; + + if (!c.push(AstDecodeStackItem(tmp))) + return false; + + return true; +} + +static bool +AstDecodeUnary(AstDecodeContext& c, ValType type, Op op) +{ + if (!c.iter().readUnary(type, nullptr)) + return false; + + AstDecodeStackItem operand = c.popCopy(); + + AstUnaryOperator* unary = new(c.lifo) AstUnaryOperator(op, operand.expr); + if (!unary) + return false; + + if (!c.push(AstDecodeStackItem(unary))) + return false; + + return true; +} + +static bool +AstDecodeBinary(AstDecodeContext& c, ValType type, Op op) +{ + if (!c.iter().readBinary(type, nullptr, nullptr)) + return false; + + AstDecodeStackItem rhs = c.popCopy(); + AstDecodeStackItem lhs = c.popCopy(); + + AstBinaryOperator* binary = new(c.lifo) AstBinaryOperator(op, lhs.expr, rhs.expr); + if (!binary) + return false; + + if (!c.push(AstDecodeStackItem(binary))) + return false; + + return true; +} + +static bool +AstDecodeSelect(AstDecodeContext& c) +{ + StackType type; + if (!c.iter().readSelect(&type, nullptr, nullptr, nullptr)) + return false; + + if (c.iter().currentBlockHasPolymorphicBase()) + return true; + + AstDecodeStackItem selectFalse = c.popCopy(); + AstDecodeStackItem selectTrue = c.popCopy(); + AstDecodeStackItem cond = c.popCopy(); + + auto* select = new(c.lifo) AstTernaryOperator(Op::Select, cond.expr, selectTrue.expr, + selectFalse.expr); + if (!select) + return false; + + if (!c.push(AstDecodeStackItem(select))) + return false; + + return true; +} + +static bool +AstDecodeComparison(AstDecodeContext& c, ValType type, Op op) +{ + if (!c.iter().readComparison(type, nullptr, nullptr)) + return false; + + AstDecodeStackItem rhs = c.popCopy(); + AstDecodeStackItem lhs = c.popCopy(); + + AstComparisonOperator* comparison = new(c.lifo) AstComparisonOperator(op, lhs.expr, rhs.expr); + if (!comparison) + return false; + + if (!c.push(AstDecodeStackItem(comparison))) + return false; + + return true; +} + +static bool +AstDecodeConversion(AstDecodeContext& c, ValType fromType, ValType toType, Op op) +{ + if (!c.iter().readConversion(fromType, toType, nullptr)) + return false; + + AstDecodeStackItem operand = c.popCopy(); + + AstConversionOperator* conversion = new(c.lifo) AstConversionOperator(op, operand.expr); + if (!conversion) + return false; + + if (!c.push(AstDecodeStackItem(conversion))) + return false; + + return true; +} + +#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS +static bool +AstDecodeExtraConversion(AstDecodeContext& c, ValType fromType, ValType toType, MiscOp op) +{ + if (!c.iter().readConversion(fromType, toType, nullptr)) + return false; + + AstDecodeStackItem operand = c.popCopy(); + + AstExtraConversionOperator* conversion = + new(c.lifo) AstExtraConversionOperator(op, operand.expr); + if (!conversion) + return false; + + if (!c.push(AstDecodeStackItem(conversion))) + return false; + + return true; +} +#endif + +static AstLoadStoreAddress +AstDecodeLoadStoreAddress(const LinearMemoryAddress& addr, const AstDecodeStackItem& item) +{ + uint32_t flags = FloorLog2(addr.align); + return AstLoadStoreAddress(item.expr, flags, addr.offset); +} + +static bool +AstDecodeLoad(AstDecodeContext& c, ValType type, uint32_t byteSize, Op op) +{ + LinearMemoryAddress addr; + if (!c.iter().readLoad(type, byteSize, &addr)) + return false; + + AstDecodeStackItem item = c.popCopy(); + + AstLoad* load = new(c.lifo) AstLoad(op, AstDecodeLoadStoreAddress(addr, item)); + if (!load) + return false; + + if (!c.push(AstDecodeStackItem(load))) + return false; + + return true; +} + +static bool +AstDecodeStore(AstDecodeContext& c, ValType type, uint32_t byteSize, Op op) +{ + LinearMemoryAddress addr; + if (!c.iter().readStore(type, byteSize, &addr, nullptr)) + return false; + + AstDecodeStackItem value = c.popCopy(); + AstDecodeStackItem item = c.popCopy(); + + AstStore* store = new(c.lifo) AstStore(op, AstDecodeLoadStoreAddress(addr, item), value.expr); + if (!store) + return false; + + AstExpr* wrapped = c.handleVoidExpr(store); + if (!wrapped) + return false; + + if (!c.push(AstDecodeStackItem(wrapped))) + return false; + + return true; +} + +static bool +AstDecodeCurrentMemory(AstDecodeContext& c) +{ + if (!c.iter().readCurrentMemory()) + return false; + + AstCurrentMemory* gm = new(c.lifo) AstCurrentMemory(); + if (!gm) + return false; + + if (!c.push(AstDecodeStackItem(gm))) + return false; + + return true; +} + +static bool +AstDecodeGrowMemory(AstDecodeContext& c) +{ + if (!c.iter().readGrowMemory(nullptr)) + return false; + + AstDecodeStackItem operand = c.popCopy(); + + AstGrowMemory* gm = new(c.lifo) AstGrowMemory(operand.expr); + if (!gm) + return false; + + if (!c.push(AstDecodeStackItem(gm))) + return false; + + return true; +} + +static bool +AstDecodeBranch(AstDecodeContext& c, Op op) +{ + MOZ_ASSERT(op == Op::Br || op == Op::BrIf); + + uint32_t depth; + ExprType type; + AstDecodeStackItem value; + AstDecodeStackItem cond; + if (op == Op::Br) { + if (!c.iter().readBr(&depth, &type, nullptr)) + return false; + if (!IsVoid(type)) + value = c.popCopy(); + } else { + if (!c.iter().readBrIf(&depth, &type, nullptr, nullptr)) + return false; + if (!IsVoid(type)) + value = c.popCopy(); + cond = c.popCopy(); + } + + AstRef depthRef; + if (!AstDecodeGetBlockRef(c, depth, &depthRef)) + return false; + + if (op == Op::Br || !value.expr) + type = ExprType::Void; + AstBranch* branch = new(c.lifo) AstBranch(op, type, cond.expr, depthRef, value.expr); + if (!branch) + return false; + + if (!c.push(AstDecodeStackItem(branch))) + return false; + + return true; +} + +static bool +AstDecodeGetLocal(AstDecodeContext& c) +{ + uint32_t getLocalId; + if (!c.iter().readGetLocal(c.locals(), &getLocalId)) + return false; + + AstRef localRef; + if (!GenerateRef(c, AstName(u"var"), getLocalId, &localRef)) + return false; + + AstGetLocal* getLocal = new(c.lifo) AstGetLocal(localRef); + if (!getLocal) + return false; + + if (!c.push(AstDecodeStackItem(getLocal))) + return false; + + return true; +} + +static bool +AstDecodeSetLocal(AstDecodeContext& c) +{ + uint32_t setLocalId; + if (!c.iter().readSetLocal(c.locals(), &setLocalId, nullptr)) + return false; + + AstDecodeStackItem setLocalValue = c.popCopy(); + + AstRef localRef; + if (!GenerateRef(c, AstName(u"var"), setLocalId, &localRef)) + return false; + + AstSetLocal* setLocal = new(c.lifo) AstSetLocal(localRef, *setLocalValue.expr); + if (!setLocal) + return false; + + AstExpr* expr = c.handleVoidExpr(setLocal); + if (!expr) + return false; + + if (!c.push(AstDecodeStackItem(expr))) + return false; + + return true; +} + +static bool +AstDecodeTeeLocal(AstDecodeContext& c) +{ + uint32_t teeLocalId; + if (!c.iter().readTeeLocal(c.locals(), &teeLocalId, nullptr)) + return false; + + AstDecodeStackItem teeLocalValue = c.popCopy(); + + AstRef localRef; + if (!GenerateRef(c, AstName(u"var"), teeLocalId, &localRef)) + return false; + + AstTeeLocal* teeLocal = new(c.lifo) AstTeeLocal(localRef, *teeLocalValue.expr); + if (!teeLocal) + return false; + + if (!c.push(AstDecodeStackItem(teeLocal))) + return false; + + return true; +} + +static bool +AstDecodeGetGlobal(AstDecodeContext& c) +{ + uint32_t globalId; + if (!c.iter().readGetGlobal(&globalId)) + return false; + + AstRef globalRef; + if (!GenerateRef(c, AstName(u"global"), globalId, &globalRef)) + return false; + + auto* getGlobal = new(c.lifo) AstGetGlobal(globalRef); + if (!getGlobal) + return false; + + if (!c.push(AstDecodeStackItem(getGlobal))) + return false; + + return true; +} + +static bool +AstDecodeSetGlobal(AstDecodeContext& c) +{ + uint32_t globalId; + if (!c.iter().readSetGlobal(&globalId, nullptr)) + return false; + + AstDecodeStackItem value = c.popCopy(); + + AstRef globalRef; + if (!GenerateRef(c, AstName(u"global"), globalId, &globalRef)) + return false; + + auto* setGlobal = new(c.lifo) AstSetGlobal(globalRef, *value.expr); + if (!setGlobal) + return false; + + AstExpr* expr = c.handleVoidExpr(setGlobal); + if (!expr) + return false; + + if (!c.push(AstDecodeStackItem(expr))) + return false; + + return true; +} + +static bool +AstDecodeReturn(AstDecodeContext& c) +{ + if (!c.iter().readReturn(nullptr)) + return false; + + AstDecodeStackItem result; + if (!IsVoid(c.retType())) + result = c.popCopy(); + + AstReturn* ret = new(c.lifo) AstReturn(result.expr); + if (!ret) + return false; + + if (!c.push(AstDecodeStackItem(ret))) + return false; + + return true; +} + +static bool +AstDecodeAtomicLoad(AstDecodeContext& c, ThreadOp op) +{ + ValType type; + uint32_t byteSize; + switch (op) { + case ThreadOp::I32AtomicLoad: type = ValType::I32; byteSize = 4; break; + case ThreadOp::I64AtomicLoad: type = ValType::I64; byteSize = 8; break; + case ThreadOp::I32AtomicLoad8U: type = ValType::I32; byteSize = 1; break; + case ThreadOp::I32AtomicLoad16U: type = ValType::I32; byteSize = 2; break; + case ThreadOp::I64AtomicLoad8U: type = ValType::I64; byteSize = 1; break; + case ThreadOp::I64AtomicLoad16U: type = ValType::I64; byteSize = 2; break; + case ThreadOp::I64AtomicLoad32U: type = ValType::I64; byteSize = 4; break; + default: + MOZ_CRASH("Should not happen"); + } + + LinearMemoryAddress addr; + if (!c.iter().readAtomicLoad(&addr, type, byteSize)) + return false; + + AstDecodeStackItem item = c.popCopy(); + + AstAtomicLoad* load = new(c.lifo) AstAtomicLoad(op, AstDecodeLoadStoreAddress(addr, item)); + if (!load) + return false; + + if (!c.push(AstDecodeStackItem(load))) + return false; + + return true; +} + +static bool +AstDecodeAtomicStore(AstDecodeContext& c, ThreadOp op) +{ + ValType type; + uint32_t byteSize; + switch (op) { + case ThreadOp::I32AtomicStore: type = ValType::I32; byteSize = 4; break; + case ThreadOp::I64AtomicStore: type = ValType::I64; byteSize = 8; break; + case ThreadOp::I32AtomicStore8U: type = ValType::I32; byteSize = 1; break; + case ThreadOp::I32AtomicStore16U: type = ValType::I32; byteSize = 2; break; + case ThreadOp::I64AtomicStore8U: type = ValType::I64; byteSize = 1; break; + case ThreadOp::I64AtomicStore16U: type = ValType::I64; byteSize = 2; break; + case ThreadOp::I64AtomicStore32U: type = ValType::I64; byteSize = 4; break; + default: + MOZ_CRASH("Should not happen"); + } + + Nothing nothing; + LinearMemoryAddress addr; + if (!c.iter().readAtomicStore(&addr, type, byteSize, ¬hing)) + return false; + + AstDecodeStackItem value = c.popCopy(); + AstDecodeStackItem item = c.popCopy(); + + AstAtomicStore* store = new(c.lifo) AstAtomicStore(op, AstDecodeLoadStoreAddress(addr, item), value.expr); + if (!store) + return false; + + AstExpr* wrapped = c.handleVoidExpr(store); + if (!wrapped) + return false; + + if (!c.push(AstDecodeStackItem(wrapped))) + return false; + + return true; +} + +static bool +AstDecodeAtomicRMW(AstDecodeContext& c, ThreadOp op) +{ + ValType type; + uint32_t byteSize; + switch (op) { + case ThreadOp::I32AtomicAdd: + case ThreadOp::I32AtomicSub: + case ThreadOp::I32AtomicAnd: + case ThreadOp::I32AtomicOr: + case ThreadOp::I32AtomicXor: + case ThreadOp::I32AtomicXchg: + type = ValType::I32; + byteSize = 4; + break; + case ThreadOp::I64AtomicAdd: + case ThreadOp::I64AtomicSub: + case ThreadOp::I64AtomicAnd: + case ThreadOp::I64AtomicOr: + case ThreadOp::I64AtomicXor: + case ThreadOp::I64AtomicXchg: + type = ValType::I64; + byteSize = 8; + break; + case ThreadOp::I32AtomicAdd8U: + case ThreadOp::I32AtomicSub8U: + case ThreadOp::I32AtomicOr8U: + case ThreadOp::I32AtomicXor8U: + case ThreadOp::I32AtomicXchg8U: + case ThreadOp::I32AtomicAnd8U: + type = ValType::I32; + byteSize = 1; + break; + case ThreadOp::I32AtomicAdd16U: + case ThreadOp::I32AtomicSub16U: + case ThreadOp::I32AtomicAnd16U: + case ThreadOp::I32AtomicOr16U: + case ThreadOp::I32AtomicXor16U: + case ThreadOp::I32AtomicXchg16U: + type = ValType::I32; + byteSize = 2; + break; + case ThreadOp::I64AtomicAdd8U: + case ThreadOp::I64AtomicSub8U: + case ThreadOp::I64AtomicAnd8U: + case ThreadOp::I64AtomicOr8U: + case ThreadOp::I64AtomicXor8U: + case ThreadOp::I64AtomicXchg8U: + type = ValType::I64; + byteSize = 1; + break; + case ThreadOp::I64AtomicAdd16U: + case ThreadOp::I64AtomicSub16U: + case ThreadOp::I64AtomicAnd16U: + case ThreadOp::I64AtomicOr16U: + case ThreadOp::I64AtomicXor16U: + case ThreadOp::I64AtomicXchg16U: + type = ValType::I64; + byteSize = 2; + break; + case ThreadOp::I64AtomicAdd32U: + case ThreadOp::I64AtomicSub32U: + case ThreadOp::I64AtomicAnd32U: + case ThreadOp::I64AtomicOr32U: + case ThreadOp::I64AtomicXor32U: + case ThreadOp::I64AtomicXchg32U: + type = ValType::I64; + byteSize = 4; + break; + default: + MOZ_CRASH("Should not happen"); + } + + Nothing nothing; + LinearMemoryAddress addr; + if (!c.iter().readAtomicRMW(&addr, type, byteSize, ¬hing)) + return false; + + AstDecodeStackItem value = c.popCopy(); + AstDecodeStackItem item = c.popCopy(); + + AstAtomicRMW* rmw = new(c.lifo) AstAtomicRMW(op, AstDecodeLoadStoreAddress(addr, item), + value.expr); + if (!rmw) + return false; + + if (!c.push(AstDecodeStackItem(rmw))) + return false; + + return true; +} + +static bool +AstDecodeAtomicCmpXchg(AstDecodeContext& c, ThreadOp op) +{ + ValType type; + uint32_t byteSize; + switch (op) { + case ThreadOp::I32AtomicCmpXchg: type = ValType::I32; byteSize = 4; break; + case ThreadOp::I64AtomicCmpXchg: type = ValType::I64; byteSize = 8; break; + case ThreadOp::I32AtomicCmpXchg8U: type = ValType::I32; byteSize = 1; break; + case ThreadOp::I32AtomicCmpXchg16U: type = ValType::I32; byteSize = 2; break; + case ThreadOp::I64AtomicCmpXchg8U: type = ValType::I64; byteSize = 1; break; + case ThreadOp::I64AtomicCmpXchg16U: type = ValType::I64; byteSize = 2; break; + case ThreadOp::I64AtomicCmpXchg32U: type = ValType::I64; byteSize = 4; break; + default: + MOZ_CRASH("Should not happen"); + } + + Nothing nothing; + LinearMemoryAddress addr; + if (!c.iter().readAtomicCmpXchg(&addr, type, byteSize, ¬hing, ¬hing)) + return false; + + AstDecodeStackItem replacement = c.popCopy(); + AstDecodeStackItem expected = c.popCopy(); + AstDecodeStackItem item = c.popCopy(); + + AstAtomicCmpXchg* cmpxchg = + new(c.lifo) AstAtomicCmpXchg(op, AstDecodeLoadStoreAddress(addr, item), expected.expr, + replacement.expr); + if (!cmpxchg) + return false; + + if (!c.push(AstDecodeStackItem(cmpxchg))) + return false; + + return true; +} + +static bool +AstDecodeWait(AstDecodeContext& c, ThreadOp op) +{ + ValType type; + uint32_t byteSize; + switch (op) { + case ThreadOp::I32Wait: type = ValType::I32; byteSize = 4; break; + case ThreadOp::I64Wait: type = ValType::I64; byteSize = 8; break; + default: + MOZ_CRASH("Should not happen"); + } + + Nothing nothing; + LinearMemoryAddress addr; + if (!c.iter().readWait(&addr, type, byteSize, ¬hing, ¬hing)) + return false; + + AstDecodeStackItem timeout = c.popCopy(); + AstDecodeStackItem value = c.popCopy(); + AstDecodeStackItem item = c.popCopy(); + + AstWait* wait = new(c.lifo) AstWait(op, AstDecodeLoadStoreAddress(addr, item), value.expr, + timeout.expr); + if (!wait) + return false; + + if (!c.push(AstDecodeStackItem(wait))) + return false; + + return true; +} + +static bool +AstDecodeWake(AstDecodeContext& c) +{ + Nothing nothing; + LinearMemoryAddress addr; + if (!c.iter().readWake(&addr, ¬hing)) + return false; + + AstDecodeStackItem count = c.popCopy(); + AstDecodeStackItem item = c.popCopy(); + + AstWake* wake = new(c.lifo) AstWake(AstDecodeLoadStoreAddress(addr, item), count.expr); + if (!wake) + return false; + + if (!c.push(AstDecodeStackItem(wake))) + return false; + + return true; +} + +#ifdef ENABLE_WASM_BULKMEM_OPS +static bool +AstDecodeMemCopy(AstDecodeContext& c) +{ + if (!c.iter().readMemCopy(nullptr, nullptr, nullptr)) + return false; + + AstDecodeStackItem dest = c.popCopy(); + AstDecodeStackItem src = c.popCopy(); + AstDecodeStackItem len = c.popCopy(); + + AstMemCopy* mc = new(c.lifo) AstMemCopy(dest.expr, src.expr, len.expr); + + if (!mc) + return false; + + if (!c.push(AstDecodeStackItem(mc))) + return false; + + return true; +} + +static bool +AstDecodeMemFill(AstDecodeContext& c) +{ + if (!c.iter().readMemFill(nullptr, nullptr, nullptr)) + return false; + + AstDecodeStackItem len = c.popCopy(); + AstDecodeStackItem val = c.popCopy(); + AstDecodeStackItem start = c.popCopy(); + + AstMemFill* mf = new(c.lifo) AstMemFill(start.expr, val.expr, len.expr); + + if (!mf) + return false; + + if (!c.push(AstDecodeStackItem(mf))) + return false; + + return true; +} +#endif + +static bool +AstDecodeExpr(AstDecodeContext& c) +{ + uint32_t exprOffset = c.iter().currentOffset(); + OpBytes op; + if (!c.iter().readOp(&op)) + return false; + + AstExpr* tmp; + switch (op.b0) { + case uint16_t(Op::Nop): + if (!AstDecodeNop(c)) + return false; + break; + case uint16_t(Op::Drop): + if (!AstDecodeDrop(c)) + return false; + break; + case uint16_t(Op::Call): + if (!AstDecodeCall(c)) + return false; + break; + case uint16_t(Op::CallIndirect): + if (!AstDecodeCallIndirect(c)) + return false; + break; + case uint16_t(Op::I32Const): + int32_t i32; + if (!c.iter().readI32Const(&i32)) + return false; + tmp = new(c.lifo) AstConst(Val((uint32_t)i32)); + if (!tmp || !c.push(AstDecodeStackItem(tmp))) + return false; + break; + case uint16_t(Op::I64Const): + int64_t i64; + if (!c.iter().readI64Const(&i64)) + return false; + tmp = new(c.lifo) AstConst(Val((uint64_t)i64)); + if (!tmp || !c.push(AstDecodeStackItem(tmp))) + return false; + break; + case uint16_t(Op::F32Const): { + float f32; + if (!c.iter().readF32Const(&f32)) + return false; + tmp = new(c.lifo) AstConst(Val(f32)); + if (!tmp || !c.push(AstDecodeStackItem(tmp))) + return false; + break; + } + case uint16_t(Op::F64Const): { + double f64; + if (!c.iter().readF64Const(&f64)) + return false; + tmp = new(c.lifo) AstConst(Val(f64)); + if (!tmp || !c.push(AstDecodeStackItem(tmp))) + return false; + break; + } + case uint16_t(Op::GetLocal): + if (!AstDecodeGetLocal(c)) + return false; + break; + case uint16_t(Op::SetLocal): + if (!AstDecodeSetLocal(c)) + return false; + break; + case uint16_t(Op::TeeLocal): + if (!AstDecodeTeeLocal(c)) + return false; + break; + case uint16_t(Op::Select): + if (!AstDecodeSelect(c)) + return false; + break; + case uint16_t(Op::Block): + case uint16_t(Op::Loop): + if (!AstDecodeBlock(c, Op(op.b0))) + return false; + break; + case uint16_t(Op::If): + if (!AstDecodeIf(c)) + return false; + break; + case uint16_t(Op::Else): + if (!AstDecodeElse(c)) + return false; + break; + case uint16_t(Op::End): + if (!AstDecodeEnd(c)) + return false; + break; + case uint16_t(Op::I32Clz): + case uint16_t(Op::I32Ctz): + case uint16_t(Op::I32Popcnt): + if (!AstDecodeUnary(c, ValType::I32, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64Clz): + case uint16_t(Op::I64Ctz): + case uint16_t(Op::I64Popcnt): + if (!AstDecodeUnary(c, ValType::I64, Op(op.b0))) + return false; + break; + case uint16_t(Op::F32Abs): + case uint16_t(Op::F32Neg): + case uint16_t(Op::F32Ceil): + case uint16_t(Op::F32Floor): + case uint16_t(Op::F32Sqrt): + case uint16_t(Op::F32Trunc): + case uint16_t(Op::F32Nearest): + if (!AstDecodeUnary(c, ValType::F32, Op(op.b0))) + return false; + break; + case uint16_t(Op::F64Abs): + case uint16_t(Op::F64Neg): + case uint16_t(Op::F64Ceil): + case uint16_t(Op::F64Floor): + case uint16_t(Op::F64Sqrt): + case uint16_t(Op::F64Trunc): + case uint16_t(Op::F64Nearest): + if (!AstDecodeUnary(c, ValType::F64, Op(op.b0))) + return false; + break; + case uint16_t(Op::I32Add): + case uint16_t(Op::I32Sub): + case uint16_t(Op::I32Mul): + case uint16_t(Op::I32DivS): + case uint16_t(Op::I32DivU): + case uint16_t(Op::I32RemS): + case uint16_t(Op::I32RemU): + case uint16_t(Op::I32And): + case uint16_t(Op::I32Or): + case uint16_t(Op::I32Xor): + case uint16_t(Op::I32Shl): + case uint16_t(Op::I32ShrS): + case uint16_t(Op::I32ShrU): + case uint16_t(Op::I32Rotl): + case uint16_t(Op::I32Rotr): + if (!AstDecodeBinary(c, ValType::I32, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64Add): + case uint16_t(Op::I64Sub): + case uint16_t(Op::I64Mul): + case uint16_t(Op::I64DivS): + case uint16_t(Op::I64DivU): + case uint16_t(Op::I64RemS): + case uint16_t(Op::I64RemU): + case uint16_t(Op::I64And): + case uint16_t(Op::I64Or): + case uint16_t(Op::I64Xor): + case uint16_t(Op::I64Shl): + case uint16_t(Op::I64ShrS): + case uint16_t(Op::I64ShrU): + case uint16_t(Op::I64Rotl): + case uint16_t(Op::I64Rotr): + if (!AstDecodeBinary(c, ValType::I64, Op(op.b0))) + return false; + break; + case uint16_t(Op::F32Add): + case uint16_t(Op::F32Sub): + case uint16_t(Op::F32Mul): + case uint16_t(Op::F32Div): + case uint16_t(Op::F32Min): + case uint16_t(Op::F32Max): + case uint16_t(Op::F32CopySign): + if (!AstDecodeBinary(c, ValType::F32, Op(op.b0))) + return false; + break; + case uint16_t(Op::F64Add): + case uint16_t(Op::F64Sub): + case uint16_t(Op::F64Mul): + case uint16_t(Op::F64Div): + case uint16_t(Op::F64Min): + case uint16_t(Op::F64Max): + case uint16_t(Op::F64CopySign): + if (!AstDecodeBinary(c, ValType::F64, Op(op.b0))) + return false; + break; + case uint16_t(Op::I32Eq): + case uint16_t(Op::I32Ne): + case uint16_t(Op::I32LtS): + case uint16_t(Op::I32LtU): + case uint16_t(Op::I32LeS): + case uint16_t(Op::I32LeU): + case uint16_t(Op::I32GtS): + case uint16_t(Op::I32GtU): + case uint16_t(Op::I32GeS): + case uint16_t(Op::I32GeU): + if (!AstDecodeComparison(c, ValType::I32, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64Eq): + case uint16_t(Op::I64Ne): + case uint16_t(Op::I64LtS): + case uint16_t(Op::I64LtU): + case uint16_t(Op::I64LeS): + case uint16_t(Op::I64LeU): + case uint16_t(Op::I64GtS): + case uint16_t(Op::I64GtU): + case uint16_t(Op::I64GeS): + case uint16_t(Op::I64GeU): + if (!AstDecodeComparison(c, ValType::I64, Op(op.b0))) + return false; + break; + case uint16_t(Op::F32Eq): + case uint16_t(Op::F32Ne): + case uint16_t(Op::F32Lt): + case uint16_t(Op::F32Le): + case uint16_t(Op::F32Gt): + case uint16_t(Op::F32Ge): + if (!AstDecodeComparison(c, ValType::F32, Op(op.b0))) + return false; + break; + case uint16_t(Op::F64Eq): + case uint16_t(Op::F64Ne): + case uint16_t(Op::F64Lt): + case uint16_t(Op::F64Le): + case uint16_t(Op::F64Gt): + case uint16_t(Op::F64Ge): + if (!AstDecodeComparison(c, ValType::F64, Op(op.b0))) + return false; + break; + case uint16_t(Op::I32Eqz): + if (!AstDecodeConversion(c, ValType::I32, ValType::I32, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64Eqz): + case uint16_t(Op::I32WrapI64): + if (!AstDecodeConversion(c, ValType::I64, ValType::I32, Op(op.b0))) + return false; + break; + case uint16_t(Op::I32TruncSF32): + case uint16_t(Op::I32TruncUF32): + case uint16_t(Op::I32ReinterpretF32): + if (!AstDecodeConversion(c, ValType::F32, ValType::I32, Op(op.b0))) + return false; + break; + case uint16_t(Op::I32TruncSF64): + case uint16_t(Op::I32TruncUF64): + if (!AstDecodeConversion(c, ValType::F64, ValType::I32, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64ExtendSI32): + case uint16_t(Op::I64ExtendUI32): + if (!AstDecodeConversion(c, ValType::I32, ValType::I64, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64TruncSF32): + case uint16_t(Op::I64TruncUF32): + if (!AstDecodeConversion(c, ValType::F32, ValType::I64, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64TruncSF64): + case uint16_t(Op::I64TruncUF64): + case uint16_t(Op::I64ReinterpretF64): + if (!AstDecodeConversion(c, ValType::F64, ValType::I64, Op(op.b0))) + return false; + break; + case uint16_t(Op::F32ConvertSI32): + case uint16_t(Op::F32ConvertUI32): + case uint16_t(Op::F32ReinterpretI32): + if (!AstDecodeConversion(c, ValType::I32, ValType::F32, Op(op.b0))) + return false; + break; + case uint16_t(Op::F32ConvertSI64): + case uint16_t(Op::F32ConvertUI64): + if (!AstDecodeConversion(c, ValType::I64, ValType::F32, Op(op.b0))) + return false; + break; + case uint16_t(Op::F32DemoteF64): + if (!AstDecodeConversion(c, ValType::F64, ValType::F32, Op(op.b0))) + return false; + break; + case uint16_t(Op::F64ConvertSI32): + case uint16_t(Op::F64ConvertUI32): + if (!AstDecodeConversion(c, ValType::I32, ValType::F64, Op(op.b0))) + return false; + break; + case uint16_t(Op::F64ConvertSI64): + case uint16_t(Op::F64ConvertUI64): + case uint16_t(Op::F64ReinterpretI64): + if (!AstDecodeConversion(c, ValType::I64, ValType::F64, Op(op.b0))) + return false; + break; + case uint16_t(Op::F64PromoteF32): + if (!AstDecodeConversion(c, ValType::F32, ValType::F64, Op(op.b0))) + return false; + break; + case uint16_t(Op::I32Extend8S): + case uint16_t(Op::I32Extend16S): + if (!AstDecodeConversion(c, ValType::I32, ValType::I32, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64Extend8S): + case uint16_t(Op::I64Extend16S): + case uint16_t(Op::I64Extend32S): + if (!AstDecodeConversion(c, ValType::I64, ValType::I64, Op(op.b0))) + return false; + break; + case uint16_t(Op::I32Load8S): + case uint16_t(Op::I32Load8U): + if (!AstDecodeLoad(c, ValType::I32, 1, Op(op.b0))) + return false; + break; + case uint16_t(Op::I32Load16S): + case uint16_t(Op::I32Load16U): + if (!AstDecodeLoad(c, ValType::I32, 2, Op(op.b0))) + return false; + break; + case uint16_t(Op::I32Load): + if (!AstDecodeLoad(c, ValType::I32, 4, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64Load8S): + case uint16_t(Op::I64Load8U): + if (!AstDecodeLoad(c, ValType::I64, 1, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64Load16S): + case uint16_t(Op::I64Load16U): + if (!AstDecodeLoad(c, ValType::I64, 2, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64Load32S): + case uint16_t(Op::I64Load32U): + if (!AstDecodeLoad(c, ValType::I64, 4, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64Load): + if (!AstDecodeLoad(c, ValType::I64, 8, Op(op.b0))) + return false; + break; + case uint16_t(Op::F32Load): + if (!AstDecodeLoad(c, ValType::F32, 4, Op(op.b0))) + return false; + break; + case uint16_t(Op::F64Load): + if (!AstDecodeLoad(c, ValType::F64, 8, Op(op.b0))) + return false; + break; + case uint16_t(Op::I32Store8): + if (!AstDecodeStore(c, ValType::I32, 1, Op(op.b0))) + return false; + break; + case uint16_t(Op::I32Store16): + if (!AstDecodeStore(c, ValType::I32, 2, Op(op.b0))) + return false; + break; + case uint16_t(Op::I32Store): + if (!AstDecodeStore(c, ValType::I32, 4, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64Store8): + if (!AstDecodeStore(c, ValType::I64, 1, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64Store16): + if (!AstDecodeStore(c, ValType::I64, 2, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64Store32): + if (!AstDecodeStore(c, ValType::I64, 4, Op(op.b0))) + return false; + break; + case uint16_t(Op::I64Store): + if (!AstDecodeStore(c, ValType::I64, 8, Op(op.b0))) + return false; + break; + case uint16_t(Op::F32Store): + if (!AstDecodeStore(c, ValType::F32, 4, Op(op.b0))) + return false; + break; + case uint16_t(Op::F64Store): + if (!AstDecodeStore(c, ValType::F64, 8, Op(op.b0))) + return false; + break; + case uint16_t(Op::CurrentMemory): + if (!AstDecodeCurrentMemory(c)) + return false; + break; + case uint16_t(Op::GrowMemory): + if (!AstDecodeGrowMemory(c)) + return false; + break; + case uint16_t(Op::SetGlobal): + if (!AstDecodeSetGlobal(c)) + return false; + break; + case uint16_t(Op::GetGlobal): + if (!AstDecodeGetGlobal(c)) + return false; + break; + case uint16_t(Op::Br): + case uint16_t(Op::BrIf): + if (!AstDecodeBranch(c, Op(op.b0))) + return false; + break; + case uint16_t(Op::BrTable): + if (!AstDecodeBrTable(c)) + return false; + break; + case uint16_t(Op::Return): + if (!AstDecodeReturn(c)) + return false; + break; + case uint16_t(Op::Unreachable): + if (!c.iter().readUnreachable()) + return false; + tmp = new(c.lifo) AstUnreachable(); + if (!tmp) + return false; + if (!c.push(AstDecodeStackItem(tmp))) + return false; + break; + case uint16_t(Op::MiscPrefix): + switch (op.b1) { +#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS + case uint16_t(MiscOp::I32TruncSSatF32): + case uint16_t(MiscOp::I32TruncUSatF32): + if (!AstDecodeExtraConversion(c, ValType::F32, ValType::I32, MiscOp(op.b1))) + return false; + break; + case uint16_t(MiscOp::I32TruncSSatF64): + case uint16_t(MiscOp::I32TruncUSatF64): + if (!AstDecodeExtraConversion(c, ValType::F64, ValType::I32, MiscOp(op.b1))) + return false; + break; + case uint16_t(MiscOp::I64TruncSSatF32): + case uint16_t(MiscOp::I64TruncUSatF32): + if (!AstDecodeExtraConversion(c, ValType::F32, ValType::I64, MiscOp(op.b1))) + return false; + break; + case uint16_t(MiscOp::I64TruncSSatF64): + case uint16_t(MiscOp::I64TruncUSatF64): + if (!AstDecodeExtraConversion(c, ValType::F64, ValType::I64, MiscOp(op.b1))) + return false; + break; +#endif +#ifdef ENABLE_WASM_BULKMEM_OPS + case uint16_t(MiscOp::MemCopy): + if (!AstDecodeMemCopy(c)) + return false; + break; + case uint16_t(MiscOp::MemFill): + if (!AstDecodeMemFill(c)) + return false; + break; +#endif + default: + return c.iter().unrecognizedOpcode(&op); + } + break; + case uint16_t(Op::ThreadPrefix): + switch (op.b1) { + case uint16_t(ThreadOp::Wake): + if (!AstDecodeWake(c)) + return false; + break; + case uint16_t(ThreadOp::I32Wait): + case uint16_t(ThreadOp::I64Wait): + if (!AstDecodeWait(c, ThreadOp(op.b1))) + return false; + break; + case uint16_t(ThreadOp::I32AtomicLoad): + case uint16_t(ThreadOp::I64AtomicLoad): + case uint16_t(ThreadOp::I32AtomicLoad8U): + case uint16_t(ThreadOp::I32AtomicLoad16U): + case uint16_t(ThreadOp::I64AtomicLoad8U): + case uint16_t(ThreadOp::I64AtomicLoad16U): + case uint16_t(ThreadOp::I64AtomicLoad32U): + if (!AstDecodeAtomicLoad(c, ThreadOp(op.b1))) + return false; + break; + case uint16_t(ThreadOp::I32AtomicStore): + case uint16_t(ThreadOp::I64AtomicStore): + case uint16_t(ThreadOp::I32AtomicStore8U): + case uint16_t(ThreadOp::I32AtomicStore16U): + case uint16_t(ThreadOp::I64AtomicStore8U): + case uint16_t(ThreadOp::I64AtomicStore16U): + case uint16_t(ThreadOp::I64AtomicStore32U): + if (!AstDecodeAtomicStore(c, ThreadOp(op.b1))) + return false; + break; + case uint16_t(ThreadOp::I32AtomicAdd): + case uint16_t(ThreadOp::I64AtomicAdd): + case uint16_t(ThreadOp::I32AtomicAdd8U): + case uint16_t(ThreadOp::I32AtomicAdd16U): + case uint16_t(ThreadOp::I64AtomicAdd8U): + case uint16_t(ThreadOp::I64AtomicAdd16U): + case uint16_t(ThreadOp::I64AtomicAdd32U): + case uint16_t(ThreadOp::I32AtomicSub): + case uint16_t(ThreadOp::I64AtomicSub): + case uint16_t(ThreadOp::I32AtomicSub8U): + case uint16_t(ThreadOp::I32AtomicSub16U): + case uint16_t(ThreadOp::I64AtomicSub8U): + case uint16_t(ThreadOp::I64AtomicSub16U): + case uint16_t(ThreadOp::I64AtomicSub32U): + case uint16_t(ThreadOp::I32AtomicAnd): + case uint16_t(ThreadOp::I64AtomicAnd): + case uint16_t(ThreadOp::I32AtomicAnd8U): + case uint16_t(ThreadOp::I32AtomicAnd16U): + case uint16_t(ThreadOp::I64AtomicAnd8U): + case uint16_t(ThreadOp::I64AtomicAnd16U): + case uint16_t(ThreadOp::I64AtomicAnd32U): + case uint16_t(ThreadOp::I32AtomicOr): + case uint16_t(ThreadOp::I64AtomicOr): + case uint16_t(ThreadOp::I32AtomicOr8U): + case uint16_t(ThreadOp::I32AtomicOr16U): + case uint16_t(ThreadOp::I64AtomicOr8U): + case uint16_t(ThreadOp::I64AtomicOr16U): + case uint16_t(ThreadOp::I64AtomicOr32U): + case uint16_t(ThreadOp::I32AtomicXor): + case uint16_t(ThreadOp::I64AtomicXor): + case uint16_t(ThreadOp::I32AtomicXor8U): + case uint16_t(ThreadOp::I32AtomicXor16U): + case uint16_t(ThreadOp::I64AtomicXor8U): + case uint16_t(ThreadOp::I64AtomicXor16U): + case uint16_t(ThreadOp::I64AtomicXor32U): + case uint16_t(ThreadOp::I32AtomicXchg): + case uint16_t(ThreadOp::I64AtomicXchg): + case uint16_t(ThreadOp::I32AtomicXchg8U): + case uint16_t(ThreadOp::I32AtomicXchg16U): + case uint16_t(ThreadOp::I64AtomicXchg8U): + case uint16_t(ThreadOp::I64AtomicXchg16U): + case uint16_t(ThreadOp::I64AtomicXchg32U): + if (!AstDecodeAtomicRMW(c, ThreadOp(op.b1))) + return false; + break; + case uint16_t(ThreadOp::I32AtomicCmpXchg): + case uint16_t(ThreadOp::I64AtomicCmpXchg): + case uint16_t(ThreadOp::I32AtomicCmpXchg8U): + case uint16_t(ThreadOp::I32AtomicCmpXchg16U): + case uint16_t(ThreadOp::I64AtomicCmpXchg8U): + case uint16_t(ThreadOp::I64AtomicCmpXchg16U): + case uint16_t(ThreadOp::I64AtomicCmpXchg32U): + if (!AstDecodeAtomicCmpXchg(c, ThreadOp(op.b1))) + return false; + break; + default: + return c.iter().unrecognizedOpcode(&op); + } + break; + case uint16_t(Op::MozPrefix): + return c.iter().unrecognizedOpcode(&op); + default: + return c.iter().unrecognizedOpcode(&op); + } + + AstExpr* lastExpr = c.top().expr; + if (lastExpr) { + // If last node is a 'first' node, the offset must assigned to it + // last child. + if (lastExpr->kind() == AstExprKind::First) + lastExpr->as().exprs().back()->setOffset(exprOffset); + else + lastExpr->setOffset(exprOffset); + } + return true; +} + +static bool +AstDecodeFunctionBody(AstDecodeContext &c, uint32_t funcIndex, AstFunc** func) +{ + uint32_t offset = c.d.currentOffset(); + uint32_t bodySize; + if (!c.d.readVarU32(&bodySize)) + return c.d.fail("expected number of function body bytes"); + + if (c.d.bytesRemain() < bodySize) + return c.d.fail("function body length too big"); + + const uint8_t* bodyBegin = c.d.currentPosition(); + const uint8_t* bodyEnd = bodyBegin + bodySize; + + const FuncTypeWithId* funcType = c.env().funcTypes[funcIndex]; + + ValTypeVector locals; + if (!locals.appendAll(funcType->args())) + return false; + + if (!DecodeLocalEntries(c.d, ModuleKind::Wasm, c.env().gcTypesEnabled, &locals)) + return false; + + AstDecodeOpIter iter(c.env(), c.d); + c.startFunction(&iter, &locals, funcType->ret()); + + AstName funcName; + if (!GenerateName(c, AstName(u"func"), funcIndex, &funcName)) + return false; + + uint32_t numParams = funcType->args().length(); + uint32_t numLocals = locals.length(); + + AstValTypeVector vars(c.lifo); + for (uint32_t i = numParams; i < numLocals; i++) { + if (!vars.append(locals[i])) + return false; + } + + AstNameVector localsNames(c.lifo); + for (uint32_t i = 0; i < numLocals; i++) { + AstName varName; + if (!GenerateName(c, AstName(u"var"), i, &varName)) + return false; + if (!localsNames.append(varName)) + return false; + } + + if (!c.iter().readFunctionStart(funcType->ret())) + return false; + + if (!c.depths().append(c.exprs().length())) + return false; + + uint32_t endOffset = offset; + while (c.d.currentPosition() < bodyEnd) { + if (!AstDecodeExpr(c)) + return false; + + const AstDecodeStackItem& item = c.top(); + if (!item.expr) { // Op::End was found + c.popBack(); + break; + } + + endOffset = c.d.currentOffset(); + } + + AstExprVector body(c.lifo); + for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end(); i != e; ++i) { + if (!body.append(i->expr)) + return false; + } + c.exprs().shrinkTo(c.depths().popCopy()); + + if (!c.iter().readFunctionEnd(bodyEnd)) + return false; + + c.endFunction(); + + if (c.d.currentPosition() != bodyEnd) + return c.d.fail("function body length mismatch"); + + size_t funcTypeIndex = c.env().funcIndexToFuncTypeIndex(funcIndex); + + AstRef funcTypeRef; + if (!GenerateRef(c, AstName(u"type"), funcTypeIndex, &funcTypeRef)) + return false; + + *func = new(c.lifo) AstFunc(funcName, funcTypeRef, std::move(vars), std::move(localsNames), + std::move(body)); + if (!*func) + return false; + (*func)->setOffset(offset); + (*func)->setEndOffset(endOffset); + + return true; +} + +/*****************************************************************************/ +// wasm decoding and generation + +static bool +AstCreateTypes(AstDecodeContext& c) +{ + uint32_t typeIndexForNames = 0; + for (const TypeDef& td : c.env().types) { + if (td.isFuncType()) { + const FuncType& funcType = td.funcType(); + + AstValTypeVector args(c.lifo); + if (!args.appendAll(funcType.args())) + return false; + + AstFuncType ftNoName(std::move(args), funcType.ret()); + + AstName ftName; + if (!GenerateName(c, AstName(u"type"), typeIndexForNames, &ftName)) + return false; + + AstFuncType* astFuncType = new(c.lifo) AstFuncType(ftName, std::move(ftNoName)); + if (!astFuncType || !c.module().append(astFuncType)) + return false; + } else if (td.isStructType()) { + const StructType& st = td.structType(); + + AstValTypeVector fieldTypes(c.lifo); + if (!fieldTypes.appendAll(st.fields_)) + return false; + + AstNameVector fieldNames(c.lifo); + if (!fieldNames.resize(fieldTypes.length())) + return false; + + // The multiplication ensures that generated field names are unique + // within the module, though the resulting namespace is very sparse. + + for (size_t fieldIndex = 0; fieldIndex < fieldTypes.length(); fieldIndex++) { + size_t idx = (typeIndexForNames * MaxStructFields) + fieldIndex; + if (!GenerateName(c, AstName(u"f"), idx, &fieldNames[fieldIndex])) + return false; + } + + AstStructType stNoName(std::move(fieldNames), std::move(fieldTypes)); + + AstName stName; + if (!GenerateName(c, AstName(u"type"), typeIndexForNames, &stName)) + return false; + + AstStructType* astStruct = new(c.lifo) AstStructType(stName, std::move(stNoName)); + if (!astStruct || !c.module().append(astStruct)) + return false; + } else { + MOZ_CRASH(); + } + typeIndexForNames++; + } + + return true; +} + +static bool +ToAstName(AstDecodeContext& c, const char* name, AstName* out) +{ + size_t len = strlen(name); + char16_t* buffer = static_cast(c.lifo.alloc(len * sizeof(char16_t))); + if (!buffer) + return false; + + for (size_t i = 0; i < len; i++) + buffer[i] = name[i]; + + *out = AstName(buffer, len); + return true; +} + +static bool +AstCreateImports(AstDecodeContext& c) +{ + size_t lastFunc = 0; + size_t lastGlobal = 0; + size_t lastTable = 0; + size_t lastMemory = 0; + + Maybe memory; + if (c.env().usesMemory()) { + memory = Some(Limits(c.env().minMemoryLength, + c.env().maxMemoryLength, + c.env().memoryUsage == MemoryUsage::Shared + ? Shareable::True + : Shareable::False)); + } + + for (size_t importIndex = 0; importIndex < c.env().imports.length(); importIndex++) { + const Import& import = c.env().imports[importIndex]; + + AstName moduleName; + if (!ToAstName(c, import.module.get(), &moduleName)) + return false; + + AstName fieldName; + if (!ToAstName(c, import.field.get(), &fieldName)) + return false; + + AstImport* ast = nullptr; + switch (import.kind) { + case DefinitionKind::Function: { + AstName importName; + if (!GenerateName(c, AstName(u"import"), lastFunc, &importName)) + return false; + + size_t funcTypeIndex = c.env().funcIndexToFuncTypeIndex(lastFunc); + + AstRef funcTypeRef; + if (!GenerateRef(c, AstName(u"type"), funcTypeIndex, &funcTypeRef)) + return false; + + ast = new(c.lifo) AstImport(importName, moduleName, fieldName, funcTypeRef); + lastFunc++; + break; + } + case DefinitionKind::Global: { + AstName importName; + if (!GenerateName(c, AstName(u"global"), lastGlobal, &importName)) + return false; + + const GlobalDesc& global = c.env().globals[lastGlobal]; + ValType type = global.type(); + bool isMutable = global.isMutable(); + + ast = new(c.lifo) AstImport(importName, moduleName, fieldName, + AstGlobal(importName, type, isMutable)); + lastGlobal++; + break; + } + case DefinitionKind::Table: { + AstName importName; + if (!GenerateName(c, AstName(u"table"), lastTable, &importName)) + return false; + + ast = new(c.lifo) AstImport(importName, moduleName, fieldName, DefinitionKind::Table, + c.env().tables[lastTable].limits); + lastTable++; + break; + } + case DefinitionKind::Memory: { + AstName importName; + if (!GenerateName(c, AstName(u"memory"), lastMemory, &importName)) + return false; + + ast = new(c.lifo) AstImport(importName, moduleName, fieldName, DefinitionKind::Memory, + *memory); + lastMemory++; + break; + } + } + + if (!ast || !c.module().append(ast)) + return false; + } + + return true; +} + +static bool +AstCreateTables(AstDecodeContext& c) +{ + size_t numImported = c.module().tables().length(); + + for (size_t i = numImported; i < c.env().tables.length(); i++) { + AstName name; + if (!GenerateName(c, AstName(u"table"), i, &name)) + return false; + if (!c.module().addTable(name, c.env().tables[i].limits)) + return false; + } + + return true; +} + +static bool +AstCreateMemory(AstDecodeContext& c) +{ + bool importedMemory = !!c.module().memories().length(); + if (!c.env().usesMemory() || importedMemory) + return true; + + AstName name; + if (!GenerateName(c, AstName(u"memory"), c.module().memories().length(), &name)) + return false; + + return c.module().addMemory(name, Limits(c.env().minMemoryLength, + c.env().maxMemoryLength, + c.env().memoryUsage == MemoryUsage::Shared + ? Shareable::True + : Shareable::False)); +} + +static AstExpr* +ToAstExpr(AstDecodeContext& c, const InitExpr& initExpr) +{ + switch (initExpr.kind()) { + case InitExpr::Kind::Constant: { + return new(c.lifo) AstConst(Val(initExpr.val())); + } + case InitExpr::Kind::GetGlobal: { + AstRef globalRef; + if (!GenerateRef(c, AstName(u"global"), initExpr.globalIndex(), &globalRef)) + return nullptr; + return new(c.lifo) AstGetGlobal(globalRef); + } + } + return nullptr; +} + +static bool +AstCreateGlobals(AstDecodeContext& c) +{ + for (uint32_t i = 0; i < c.env().globals.length(); i++) { + const GlobalDesc& global = c.env().globals[i]; + if (global.isImport()) + continue; + + AstName name; + if (!GenerateName(c, AstName(u"global"), i, &name)) + return false; + + AstExpr* init = global.isConstant() + ? new(c.lifo) AstConst(global.constantValue()) + : ToAstExpr(c, global.initExpr()); + if (!init) + return false; + + auto* g = new(c.lifo) AstGlobal(name, global.type(), global.isMutable(), Some(init)); + if (!g || !c.module().append(g)) + return false; + } + + return true; +} + +static bool +AstCreateExports(AstDecodeContext& c) +{ + for (const Export& exp : c.env().exports) { + size_t index; + switch (exp.kind()) { + case DefinitionKind::Function: index = exp.funcIndex(); break; + case DefinitionKind::Global: index = exp.globalIndex(); break; + case DefinitionKind::Memory: index = 0; break; + case DefinitionKind::Table: index = 0; break; + } + + AstName name; + if (!ToAstName(c, exp.fieldName(), &name)) + return false; + + AstExport* e = new(c.lifo) AstExport(name, exp.kind(), AstRef(index)); + if (!e || !c.module().append(e)) + return false; + } + + return true; +} + +static bool +AstCreateStartFunc(AstDecodeContext &c) +{ + if (!c.env().startFuncIndex) + return true; + + AstRef funcRef; + if (!GenerateFuncRef(c, *c.env().startFuncIndex, &funcRef)) + return false; + + c.module().setStartFunc(AstStartFunc(funcRef)); + return true; +} + +static bool +AstCreateElems(AstDecodeContext &c) +{ + for (const ElemSegment& seg : c.env().elemSegments) { + AstRefVector elems(c.lifo); + if (!elems.reserve(seg.elemFuncIndices.length())) + return false; + + for (uint32_t i : seg.elemFuncIndices) + elems.infallibleAppend(AstRef(i)); + + AstExpr* offset = ToAstExpr(c, seg.offset); + if (!offset) + return false; + + AstElemSegment* segment = new(c.lifo) AstElemSegment(offset, std::move(elems)); + if (!segment || !c.module().append(segment)) + return false; + } + + return true; +} + +static bool +AstDecodeEnvironment(AstDecodeContext& c) +{ + if (!DecodeModuleEnvironment(c.d, &c.env())) + return false; + + if (!AstCreateTypes(c)) + return false; + + if (!AstCreateImports(c)) + return false; + + if (!AstCreateTables(c)) + return false; + + if (!AstCreateMemory(c)) + return false; + + if (!AstCreateGlobals(c)) + return false; + + if (!AstCreateExports(c)) + return false; + + if (!AstCreateStartFunc(c)) + return false; + + if (!AstCreateElems(c)) + return false; + + return true; +} + +static bool +AstDecodeCodeSection(AstDecodeContext& c) +{ + if (!c.env().codeSection) { + if (c.env().numFuncDefs() != 0) + return c.d.fail("expected function bodies"); + return true; + } + + uint32_t numFuncBodies; + if (!c.d.readVarU32(&numFuncBodies)) + return c.d.fail("expected function body count"); + + if (numFuncBodies != c.env().numFuncDefs()) + return c.d.fail("function body count does not match function signature count"); + + for (uint32_t funcDefIndex = 0; funcDefIndex < numFuncBodies; funcDefIndex++) { + AstFunc* func; + if (!AstDecodeFunctionBody(c, c.module().numFuncImports() + funcDefIndex, &func)) + return false; + if (!c.module().append(func)) + return false; + } + + return c.d.finishSection(*c.env().codeSection, "code"); +} + +// Number of bytes to display in a single fragment of a data section (per line). +static const size_t WRAP_DATA_BYTES = 30; + +static bool +AstDecodeModuleTail(AstDecodeContext& c) +{ + MOZ_ASSERT(c.module().memories().length() <= 1, "at most one memory in MVP"); + + if (!DecodeModuleTail(c.d, &c.env())) + return false; + + for (DataSegment& s : c.env().dataSegments) { + char16_t* buffer = static_cast(c.lifo.alloc(s.length * sizeof(char16_t))); + if (!buffer) + return false; + + const uint8_t* src = c.d.begin() + s.bytecodeOffset; + for (size_t i = 0; i < s.length; i++) + buffer[i] = src[i]; + + AstExpr* offset = ToAstExpr(c, s.offset); + if (!offset) + return false; + + AstNameVector fragments(c.lifo); + for (size_t start = 0; start < s.length; start += WRAP_DATA_BYTES) { + AstName name(buffer + start, Min(WRAP_DATA_BYTES, s.length - start)); + if (!fragments.append(name)) + return false; + } + + AstDataSegment* segment = new(c.lifo) AstDataSegment(offset, std::move(fragments)); + if (!segment || !c.module().append(segment)) + return false; + } + + return true; +} + +bool +wasm::BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length, LifoAlloc& lifo, + AstModule** module) +{ + AstModule* result = new(lifo) AstModule(lifo); + if (!result || !result->init()) + return false; + + UniqueChars error; + Decoder d(bytes, bytes + length, 0, &error, nullptr, /* resilient */ true); + AstDecodeContext c(cx, lifo, d, *result, /* generateNames */ true, HasGcTypes::True); + + if (!AstDecodeEnvironment(c) || + !AstDecodeCodeSection(c) || + !AstDecodeModuleTail(c)) + { + if (error) { + JS_ReportErrorNumberUTF8(c.cx, GetErrorMessage, nullptr, JSMSG_WASM_COMPILE_ERROR, + error.get()); + return false; + } + ReportOutOfMemory(c.cx); + return false; + } + + MOZ_ASSERT(!error, "unreported error in decoding"); + + *module = result; + return true; +} diff --git a/js/src/wasm/WasmBinaryToAST.h b/js/src/wasm/WasmBinaryToAST.h new file mode 100644 index 000000000000..320862dbbc7c --- /dev/null +++ b/js/src/wasm/WasmBinaryToAST.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * Copyright 2015 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wasmbinarytoast_h +#define wasmbinarytoast_h + +#include "ds/LifoAlloc.h" + +#include "wasm/WasmAST.h" +#include "wasm/WasmTypes.h" + +namespace js { +namespace wasm { + +bool +BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length, LifoAlloc& lifo, + AstModule** module); + +} // end wasm namespace +} // end js namespace + +#endif // namespace wasmbinarytoast_h diff --git a/js/src/wasm/WasmBinaryToText.cpp b/js/src/wasm/WasmBinaryToText.cpp new file mode 100644 index 000000000000..c7f5110d7ef0 --- /dev/null +++ b/js/src/wasm/WasmBinaryToText.cpp @@ -0,0 +1,2138 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * Copyright 2015 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wasm/WasmBinaryToText.h" + +#include "jsnum.h" + +#include "util/StringBuffer.h" +#include "vm/ArrayBufferObject.h" +#include "wasm/WasmAST.h" +#include "wasm/WasmBinaryToAST.h" +#include "wasm/WasmDebug.h" +#include "wasm/WasmTextUtils.h" +#include "wasm/WasmTypes.h" + +using namespace js; +using namespace js::wasm; + +using mozilla::IsInfinite; +using mozilla::IsNaN; +using mozilla::IsNegativeZero; + +struct WasmRenderContext +{ + JSContext* cx; + AstModule* module; + WasmPrintBuffer& buffer; + GeneratedSourceMap* maybeSourceMap; + uint32_t indent; + uint32_t currentFuncIndex; + + WasmRenderContext(JSContext* cx, AstModule* module, WasmPrintBuffer& buffer, + GeneratedSourceMap* sourceMap) + : cx(cx), + module(module), + buffer(buffer), + maybeSourceMap(sourceMap), + indent(0), + currentFuncIndex(0) + {} + + StringBuffer& sb() { return buffer.stringBuffer(); } +}; + +/*****************************************************************************/ +// utilities + +// Return true on purpose, so that we have a useful error message to provide to +// the user. +static bool +Fail(WasmRenderContext& c, const char* msg) +{ + c.buffer.stringBuffer().clear(); + + return c.buffer.append("There was a problem when rendering the wasm text format: ") && + c.buffer.append(msg, strlen(msg)) && + c.buffer.append("\nYou should consider file a bug on Bugzilla in the " + "Core:::JavaScript Engine::JIT component at " + "https://bugzilla.mozilla.org/enter_bug.cgi."); +} + +static bool +RenderIndent(WasmRenderContext& c) +{ + for (uint32_t i = 0; i < c.indent; i++) { + if (!c.buffer.append(" ")) + return false; + } + return true; +} + +static bool +RenderInt32(WasmRenderContext& c, int32_t num) +{ + return NumberValueToStringBuffer(c.cx, Int32Value(num), c.sb()); +} + +static bool +RenderInt64(WasmRenderContext& c, int64_t num) +{ + if (num < 0 && !c.buffer.append("-")) + return false; + if (!num) + return c.buffer.append("0"); + return RenderInBase<10>(c.sb(), mozilla::Abs(num)); +} + +static bool +RenderDouble(WasmRenderContext& c, double d) +{ + if (IsNaN(d)) + return RenderNaN(c.sb(), d); + if (IsNegativeZero(d)) + return c.buffer.append("-0"); + if (IsInfinite(d)) { + if (d > 0) + return c.buffer.append("infinity"); + return c.buffer.append("-infinity"); + } + return NumberValueToStringBuffer(c.cx, DoubleValue(d), c.sb()); +} + +static bool +RenderFloat32(WasmRenderContext& c, float f) +{ + if (IsNaN(f)) + return RenderNaN(c.sb(), f); + return RenderDouble(c, double(f)); +} + +static bool +RenderEscapedString(WasmRenderContext& c, const AstName& s) +{ + size_t length = s.length(); + const char16_t* p = s.begin(); + for (size_t i = 0; i < length; i++) { + char16_t byte = p[i]; + switch (byte) { + case '\n': + if (!c.buffer.append("\\n")) + return false; + break; + case '\r': + if (!c.buffer.append("\\0d")) + return false; + break; + case '\t': + if (!c.buffer.append("\\t")) + return false; + break; + case '\f': + if (!c.buffer.append("\\0c")) + return false; + break; + case '\b': + if (!c.buffer.append("\\08")) + return false; + break; + case '\\': + if (!c.buffer.append("\\\\")) + return false; + break; + case '"' : + if (!c.buffer.append("\\\"")) + return false; + break; + case '\'': + if (!c.buffer.append("\\'")) + return false; + break; + default: + if (byte >= 32 && byte < 127) { + if (!c.buffer.append((char)byte)) + return false; + } else { + char digit1 = byte / 16, digit2 = byte % 16; + if (!c.buffer.append("\\")) + return false; + if (!c.buffer.append((char)(digit1 < 10 ? digit1 + '0' : digit1 + 'a' - 10))) + return false; + if (!c.buffer.append((char)(digit2 < 10 ? digit2 + '0' : digit2 + 'a' - 10))) + return false; + } + break; + } + } + return true; +} + +static bool +RenderExprType(WasmRenderContext& c, ExprType type) +{ + switch (type) { + case ExprType::Void: return true; // ignoring void + case ExprType::I32: return c.buffer.append("i32"); + case ExprType::I64: return c.buffer.append("i64"); + case ExprType::F32: return c.buffer.append("f32"); + case ExprType::F64: return c.buffer.append("f64"); + case ExprType::AnyRef: return c.buffer.append("anyref"); + default:; + } + + MOZ_CRASH("bad type"); +} + +static bool +RenderValType(WasmRenderContext& c, ValType type) +{ + return RenderExprType(c, ToExprType(type)); +} + +static bool +RenderName(WasmRenderContext& c, const AstName& name) +{ + return c.buffer.append(name.begin(), name.end()); +} + +static bool +RenderNonemptyName(WasmRenderContext& c, const AstName& name) +{ + return name.empty() || (RenderName(c, name) && c.buffer.append(' ')); +} + +static bool +RenderRef(WasmRenderContext& c, const AstRef& ref) +{ + if (ref.name().empty()) + return RenderInt32(c, ref.index()); + + return RenderName(c, ref.name()); +} + +static bool +RenderBlockNameAndSignature(WasmRenderContext& c, const AstName& name, ExprType type) +{ + if (!name.empty()) { + if (!c.buffer.append(' ')) + return false; + + if (!RenderName(c, name)) + return false; + } + + if (!IsVoid(type)) { + if (!c.buffer.append(' ')) + return false; + + if (!RenderExprType(c, type)) + return false; + } + + return true; +} + +static bool +RenderExpr(WasmRenderContext& c, AstExpr& expr, bool newLine = true); + +#define MAP_AST_EXPR(c, expr) \ + if (c.maybeSourceMap) { \ + uint32_t lineno = c.buffer.lineno(); \ + uint32_t column = c.buffer.column(); \ + if (!c.maybeSourceMap->exprlocs().emplaceBack(lineno, column, expr.offset())) \ + return false; \ + } + +/*****************************************************************************/ +// binary format parsing and rendering + +static bool +RenderNop(WasmRenderContext& c, AstNop& nop) +{ + if (!RenderIndent(c)) + return false; + MAP_AST_EXPR(c, nop); + return c.buffer.append("nop"); +} + +static bool +RenderDrop(WasmRenderContext& c, AstDrop& drop) +{ + if (!RenderExpr(c, drop.value())) + return false; + + if (!RenderIndent(c)) + return false; + MAP_AST_EXPR(c, drop); + return c.buffer.append("drop"); +} + +static bool +RenderUnreachable(WasmRenderContext& c, AstUnreachable& unreachable) +{ + if (!RenderIndent(c)) + return false; + MAP_AST_EXPR(c, unreachable); + return c.buffer.append("unreachable"); +} + +static bool +RenderCallArgs(WasmRenderContext& c, const AstExprVector& args) +{ + for (uint32_t i = 0; i < args.length(); i++) { + if (!RenderExpr(c, *args[i])) + return false; + } + + return true; +} + +static bool +RenderCall(WasmRenderContext& c, AstCall& call) +{ + if (!RenderCallArgs(c, call.args())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, call); + if (call.op() == Op::Call) { + if (!c.buffer.append("call ")) + return false; + } else { + return Fail(c, "unexpected operator"); + } + + return RenderRef(c, call.func()); +} + +static bool +RenderCallIndirect(WasmRenderContext& c, AstCallIndirect& call) +{ + if (!RenderCallArgs(c, call.args())) + return false; + + if (!RenderExpr(c, *call.index())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, call); + if (!c.buffer.append("call_indirect ")) + return false; + return RenderRef(c, call.funcType()); +} + +static bool +RenderConst(WasmRenderContext& c, AstConst& cst) +{ + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, cst); + if (!RenderValType(c, cst.val().type())) + return false; + if (!c.buffer.append(".const ")) + return false; + + switch (ToExprType(cst.val().type())) { + case ExprType::I32: + return RenderInt32(c, (int32_t)cst.val().i32()); + case ExprType::I64: + return RenderInt64(c, (int64_t)cst.val().i64()); + case ExprType::F32: + return RenderFloat32(c, cst.val().f32()); + case ExprType::F64: + return RenderDouble(c, cst.val().f64()); + default: + break; + } + + return false; +} + +static bool +RenderGetLocal(WasmRenderContext& c, AstGetLocal& gl) +{ + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, gl); + if (!c.buffer.append("get_local ")) + return false; + return RenderRef(c, gl.local()); +} + +static bool +RenderSetLocal(WasmRenderContext& c, AstSetLocal& sl) +{ + if (!RenderExpr(c, sl.value())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, sl); + if (!c.buffer.append("set_local ")) + return false; + return RenderRef(c, sl.local()); +} + +static bool +RenderTeeLocal(WasmRenderContext& c, AstTeeLocal& tl) +{ + if (!RenderExpr(c, tl.value())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, tl); + if (!c.buffer.append("tee_local ")) + return false; + return RenderRef(c, tl.local()); +} + +static bool +RenderGetGlobal(WasmRenderContext& c, AstGetGlobal& gg) +{ + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, gg); + if (!c.buffer.append("get_global ")) + return false; + return RenderRef(c, gg.global()); +} + +static bool +RenderSetGlobal(WasmRenderContext& c, AstSetGlobal& sg) +{ + if (!RenderExpr(c, sg.value())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, sg); + if (!c.buffer.append("set_global ")) + return false; + return RenderRef(c, sg.global()); +} + +static bool +RenderExprList(WasmRenderContext& c, const AstExprVector& exprs, uint32_t startAt = 0) +{ + for (uint32_t i = startAt; i < exprs.length(); i++) { + if (!RenderExpr(c, *exprs[i])) + return false; + } + return true; +} + +static bool +RenderBlock(WasmRenderContext& c, AstBlock& block, bool isInline = false) +{ + if (!isInline && !RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, block); + if (block.op() == Op::Block) { + if (!c.buffer.append("block")) + return false; + } else if (block.op() == Op::Loop) { + if (!c.buffer.append("loop")) + return false; + } else { + return Fail(c, "unexpected block kind"); + } + + if (!RenderBlockNameAndSignature(c, block.name(), block.type())) + return false; + + uint32_t startAtSubExpr = 0; + + // If there is a stack of blocks, print them all inline. + if (block.op() == Op::Block && + block.exprs().length() && + block.exprs()[0]->kind() == AstExprKind::Block && + block.exprs()[0]->as().op() == Op::Block) + { + if (!c.buffer.append(' ')) + return false; + + // Render the first inner expr (block) at the same indent level, but + // next instructions one level further. + if (!RenderBlock(c, block.exprs()[0]->as(), /* isInline */ true)) + return false; + + startAtSubExpr = 1; + } + + if (!c.buffer.append('\n')) + return false; + + c.indent++; + if (!RenderExprList(c, block.exprs(), startAtSubExpr)) + return false; + c.indent--; + + return RenderIndent(c) && + c.buffer.append("end ") && + RenderName(c, block.name()); +} + +static bool +RenderFirst(WasmRenderContext& c, AstFirst& first) +{ + return RenderExprList(c, first.exprs()); +} + +static bool +RenderCurrentMemory(WasmRenderContext& c, AstCurrentMemory& cm) +{ + if (!RenderIndent(c)) + return false; + + return c.buffer.append("current_memory\n"); +} + +static bool +RenderGrowMemory(WasmRenderContext& c, AstGrowMemory& gm) +{ + if (!RenderExpr(c, *gm.operand())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, gm); + return c.buffer.append("grow_memory\n"); +} + +static bool +RenderUnaryOperator(WasmRenderContext& c, AstUnaryOperator& unary) +{ + if (!RenderExpr(c, *unary.operand())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, unary); + const char* opStr; + switch (unary.op()) { + case Op::I32Eqz: opStr = "i32.eqz"; break; + case Op::I32Clz: opStr = "i32.clz"; break; + case Op::I32Ctz: opStr = "i32.ctz"; break; + case Op::I32Popcnt: opStr = "i32.popcnt"; break; + case Op::I64Clz: opStr = "i64.clz"; break; + case Op::I64Ctz: opStr = "i64.ctz"; break; + case Op::I64Popcnt: opStr = "i64.popcnt"; break; + case Op::F32Abs: opStr = "f32.abs"; break; + case Op::F32Neg: opStr = "f32.neg"; break; + case Op::F32Ceil: opStr = "f32.ceil"; break; + case Op::F32Floor: opStr = "f32.floor"; break; + case Op::F32Sqrt: opStr = "f32.sqrt"; break; + case Op::F32Trunc: opStr = "f32.trunc"; break; + case Op::F32Nearest: opStr = "f32.nearest"; break; + case Op::F64Abs: opStr = "f64.abs"; break; + case Op::F64Neg: opStr = "f64.neg"; break; + case Op::F64Ceil: opStr = "f64.ceil"; break; + case Op::F64Floor: opStr = "f64.floor"; break; + case Op::F64Nearest: opStr = "f64.nearest"; break; + case Op::F64Sqrt: opStr = "f64.sqrt"; break; + case Op::F64Trunc: opStr = "f64.trunc"; break; + default: return Fail(c, "unexpected unary operator"); + } + + return c.buffer.append(opStr, strlen(opStr)); +} + +static bool +RenderBinaryOperator(WasmRenderContext& c, AstBinaryOperator& binary) +{ + if (!RenderExpr(c, *binary.lhs())) + return false; + if (!RenderExpr(c, *binary.rhs())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, binary); + const char* opStr; + switch (binary.op()) { + case Op::I32Add: opStr = "i32.add"; break; + case Op::I32Sub: opStr = "i32.sub"; break; + case Op::I32Mul: opStr = "i32.mul"; break; + case Op::I32DivS: opStr = "i32.div_s"; break; + case Op::I32DivU: opStr = "i32.div_u"; break; + case Op::I32RemS: opStr = "i32.rem_s"; break; + case Op::I32RemU: opStr = "i32.rem_u"; break; + case Op::I32And: opStr = "i32.and"; break; + case Op::I32Or: opStr = "i32.or"; break; + case Op::I32Xor: opStr = "i32.xor"; break; + case Op::I32Shl: opStr = "i32.shl"; break; + case Op::I32ShrS: opStr = "i32.shr_s"; break; + case Op::I32ShrU: opStr = "i32.shr_u"; break; + case Op::I32Rotl: opStr = "i32.rotl"; break; + case Op::I32Rotr: opStr = "i32.rotr"; break; + case Op::I64Add: opStr = "i64.add"; break; + case Op::I64Sub: opStr = "i64.sub"; break; + case Op::I64Mul: opStr = "i64.mul"; break; + case Op::I64DivS: opStr = "i64.div_s"; break; + case Op::I64DivU: opStr = "i64.div_u"; break; + case Op::I64RemS: opStr = "i64.rem_s"; break; + case Op::I64RemU: opStr = "i64.rem_u"; break; + case Op::I64And: opStr = "i64.and"; break; + case Op::I64Or: opStr = "i64.or"; break; + case Op::I64Xor: opStr = "i64.xor"; break; + case Op::I64Shl: opStr = "i64.shl"; break; + case Op::I64ShrS: opStr = "i64.shr_s"; break; + case Op::I64ShrU: opStr = "i64.shr_u"; break; + case Op::I64Rotl: opStr = "i64.rotl"; break; + case Op::I64Rotr: opStr = "i64.rotr"; break; + case Op::F32Add: opStr = "f32.add"; break; + case Op::F32Sub: opStr = "f32.sub"; break; + case Op::F32Mul: opStr = "f32.mul"; break; + case Op::F32Div: opStr = "f32.div"; break; + case Op::F32Min: opStr = "f32.min"; break; + case Op::F32Max: opStr = "f32.max"; break; + case Op::F32CopySign: opStr = "f32.copysign"; break; + case Op::F64Add: opStr = "f64.add"; break; + case Op::F64Sub: opStr = "f64.sub"; break; + case Op::F64Mul: opStr = "f64.mul"; break; + case Op::F64Div: opStr = "f64.div"; break; + case Op::F64Min: opStr = "f64.min"; break; + case Op::F64Max: opStr = "f64.max"; break; + case Op::F64CopySign: opStr = "f64.copysign"; break; + default: return Fail(c, "unexpected binary operator"); + } + + return c.buffer.append(opStr, strlen(opStr)); +} + +static bool +RenderTernaryOperator(WasmRenderContext& c, AstTernaryOperator& ternary) +{ + if (!RenderExpr(c, *ternary.op0())) + return false; + if (!RenderExpr(c, *ternary.op1())) + return false; + if (!RenderExpr(c, *ternary.op2())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, ternary); + const char* opStr; + switch (ternary.op()) { + case Op::Select: opStr = "select"; break; + default: return Fail(c, "unexpected ternary operator"); + } + + return c.buffer.append(opStr, strlen(opStr)); +} + +static bool +RenderComparisonOperator(WasmRenderContext& c, AstComparisonOperator& comp) +{ + if (!RenderExpr(c, *comp.lhs())) + return false; + if (!RenderExpr(c, *comp.rhs())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, comp); + const char* opStr; + switch (comp.op()) { + case Op::I32Eq: opStr = "i32.eq"; break; + case Op::I32Ne: opStr = "i32.ne"; break; + case Op::I32LtS: opStr = "i32.lt_s"; break; + case Op::I32LtU: opStr = "i32.lt_u"; break; + case Op::I32LeS: opStr = "i32.le_s"; break; + case Op::I32LeU: opStr = "i32.le_u"; break; + case Op::I32GtS: opStr = "i32.gt_s"; break; + case Op::I32GtU: opStr = "i32.gt_u"; break; + case Op::I32GeS: opStr = "i32.ge_s"; break; + case Op::I32GeU: opStr = "i32.ge_u"; break; + case Op::I64Eq: opStr = "i64.eq"; break; + case Op::I64Ne: opStr = "i64.ne"; break; + case Op::I64LtS: opStr = "i64.lt_s"; break; + case Op::I64LtU: opStr = "i64.lt_u"; break; + case Op::I64LeS: opStr = "i64.le_s"; break; + case Op::I64LeU: opStr = "i64.le_u"; break; + case Op::I64GtS: opStr = "i64.gt_s"; break; + case Op::I64GtU: opStr = "i64.gt_u"; break; + case Op::I64GeS: opStr = "i64.ge_s"; break; + case Op::I64GeU: opStr = "i64.ge_u"; break; + case Op::F32Eq: opStr = "f32.eq"; break; + case Op::F32Ne: opStr = "f32.ne"; break; + case Op::F32Lt: opStr = "f32.lt"; break; + case Op::F32Le: opStr = "f32.le"; break; + case Op::F32Gt: opStr = "f32.gt"; break; + case Op::F32Ge: opStr = "f32.ge"; break; + case Op::F64Eq: opStr = "f64.eq"; break; + case Op::F64Ne: opStr = "f64.ne"; break; + case Op::F64Lt: opStr = "f64.lt"; break; + case Op::F64Le: opStr = "f64.le"; break; + case Op::F64Gt: opStr = "f64.gt"; break; + case Op::F64Ge: opStr = "f64.ge"; break; + default: return Fail(c, "unexpected comparison operator"); + } + + return c.buffer.append(opStr, strlen(opStr)); +} + +static bool +RenderConversionOperator(WasmRenderContext& c, AstConversionOperator& conv) +{ + if (!RenderExpr(c, *conv.operand())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, conv); + const char* opStr; + switch (conv.op()) { + case Op::I32WrapI64: opStr = "i32.wrap/i64"; break; + case Op::I32TruncSF32: opStr = "i32.trunc_s/f32"; break; + case Op::I32TruncUF32: opStr = "i32.trunc_u/f32"; break; + case Op::I32ReinterpretF32: opStr = "i32.reinterpret/f32"; break; + case Op::I32TruncSF64: opStr = "i32.trunc_s/f64"; break; + case Op::I32TruncUF64: opStr = "i32.trunc_u/f64"; break; + case Op::I64ExtendSI32: opStr = "i64.extend_s/i32"; break; + case Op::I64ExtendUI32: opStr = "i64.extend_u/i32"; break; + case Op::I64TruncSF32: opStr = "i64.trunc_s/f32"; break; + case Op::I64TruncUF32: opStr = "i64.trunc_u/f32"; break; + case Op::I64TruncSF64: opStr = "i64.trunc_s/f64"; break; + case Op::I64TruncUF64: opStr = "i64.trunc_u/f64"; break; + case Op::I64ReinterpretF64: opStr = "i64.reinterpret/f64"; break; + case Op::F32ConvertSI32: opStr = "f32.convert_s/i32"; break; + case Op::F32ConvertUI32: opStr = "f32.convert_u/i32"; break; + case Op::F32ReinterpretI32: opStr = "f32.reinterpret/i32"; break; + case Op::F32ConvertSI64: opStr = "f32.convert_s/i64"; break; + case Op::F32ConvertUI64: opStr = "f32.convert_u/i64"; break; + case Op::F32DemoteF64: opStr = "f32.demote/f64"; break; + case Op::F64ConvertSI32: opStr = "f64.convert_s/i32"; break; + case Op::F64ConvertUI32: opStr = "f64.convert_u/i32"; break; + case Op::F64ConvertSI64: opStr = "f64.convert_s/i64"; break; + case Op::F64ConvertUI64: opStr = "f64.convert_u/i64"; break; + case Op::F64ReinterpretI64: opStr = "f64.reinterpret/i64"; break; + case Op::F64PromoteF32: opStr = "f64.promote/f32"; break; + case Op::I32Extend8S: opStr = "i32.extend8_s"; break; + case Op::I32Extend16S: opStr = "i32.extend16_s"; break; + case Op::I64Extend8S: opStr = "i64.extend8_s"; break; + case Op::I64Extend16S: opStr = "i64.extend16_s"; break; + case Op::I64Extend32S: opStr = "i64.extend32_s"; break; + case Op::I32Eqz: opStr = "i32.eqz"; break; + case Op::I64Eqz: opStr = "i64.eqz"; break; + default: return Fail(c, "unexpected conversion operator"); + } + return c.buffer.append(opStr, strlen(opStr)); +} + +#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS +static bool +RenderExtraConversionOperator(WasmRenderContext& c, AstExtraConversionOperator& conv) +{ + if (!RenderExpr(c, *conv.operand())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, conv); + const char* opStr; + switch (conv.op()) { + case MiscOp::I32TruncSSatF32: opStr = "i32.trunc_s:sat/f32"; break; + case MiscOp::I32TruncUSatF32: opStr = "i32.trunc_u:sat/f32"; break; + case MiscOp::I32TruncSSatF64: opStr = "i32.trunc_s:sat/f64"; break; + case MiscOp::I32TruncUSatF64: opStr = "i32.trunc_u:sat/f64"; break; + case MiscOp::I64TruncSSatF32: opStr = "i64.trunc_s:sat/f32"; break; + case MiscOp::I64TruncUSatF32: opStr = "i64.trunc_u:sat/f32"; break; + case MiscOp::I64TruncSSatF64: opStr = "i64.trunc_s:sat/f64"; break; + case MiscOp::I64TruncUSatF64: opStr = "i64.trunc_u:sat/f64"; break; + default: return Fail(c, "unexpected extra conversion operator"); + } + return c.buffer.append(opStr, strlen(opStr)); +} +#endif + +static bool +RenderIf(WasmRenderContext& c, AstIf& if_) +{ + if (!RenderExpr(c, if_.cond())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, if_); + if (!c.buffer.append("if")) + return false; + if (!RenderBlockNameAndSignature(c, if_.name(), if_.type())) + return false; + if (!c.buffer.append('\n')) + return false; + + c.indent++; + if (!RenderExprList(c, if_.thenExprs())) + return false; + c.indent--; + + if (if_.hasElse()) { + if (!RenderIndent(c)) + return false; + + if (!c.buffer.append("else\n")) + return false; + + c.indent++; + if (!RenderExprList(c, if_.elseExprs())) + return false; + c.indent--; + } + + if (!RenderIndent(c)) + return false; + + return c.buffer.append("end"); +} + +static bool +RenderLoadStoreBase(WasmRenderContext& c, const AstLoadStoreAddress& lsa) +{ + return RenderExpr(c, lsa.base()); +} + +static bool +RenderLoadStoreAddress(WasmRenderContext& c, const AstLoadStoreAddress& lsa, uint32_t defaultAlignLog2) +{ + if (lsa.offset() != 0) { + if (!c.buffer.append(" offset=")) + return false; + if (!RenderInt32(c, lsa.offset())) + return false; + } + + uint32_t alignLog2 = lsa.flags(); + if (defaultAlignLog2 != alignLog2) { + if (!c.buffer.append(" align=")) + return false; + if (!RenderInt32(c, 1 << alignLog2)) + return false; + } + + return true; +} + +static bool +RenderLoad(WasmRenderContext& c, AstLoad& load) +{ + if (!RenderLoadStoreBase(c, load.address())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, load); + uint32_t defaultAlignLog2; + switch (load.op()) { + case Op::I32Load8S: + if (!c.buffer.append("i32.load8_s")) + return false; + defaultAlignLog2 = 0; + break; + case Op::I64Load8S: + if (!c.buffer.append("i64.load8_s")) + return false; + defaultAlignLog2 = 0; + break; + case Op::I32Load8U: + if (!c.buffer.append("i32.load8_u")) + return false; + defaultAlignLog2 = 0; + break; + case Op::I64Load8U: + if (!c.buffer.append("i64.load8_u")) + return false; + defaultAlignLog2 = 0; + break; + case Op::I32Load16S: + if (!c.buffer.append("i32.load16_s")) + return false; + defaultAlignLog2 = 1; + break; + case Op::I64Load16S: + if (!c.buffer.append("i64.load16_s")) + return false; + defaultAlignLog2 = 1; + break; + case Op::I32Load16U: + if (!c.buffer.append("i32.load16_u")) + return false; + defaultAlignLog2 = 1; + break; + case Op::I64Load16U: + if (!c.buffer.append("i64.load16_u")) + return false; + defaultAlignLog2 = 1; + break; + case Op::I64Load32S: + if (!c.buffer.append("i64.load32_s")) + return false; + defaultAlignLog2 = 2; + break; + case Op::I64Load32U: + if (!c.buffer.append("i64.load32_u")) + return false; + defaultAlignLog2 = 2; + break; + case Op::I32Load: + if (!c.buffer.append("i32.load")) + return false; + defaultAlignLog2 = 2; + break; + case Op::I64Load: + if (!c.buffer.append("i64.load")) + return false; + defaultAlignLog2 = 3; + break; + case Op::F32Load: + if (!c.buffer.append("f32.load")) + return false; + defaultAlignLog2 = 2; + break; + case Op::F64Load: + if (!c.buffer.append("f64.load")) + return false; + defaultAlignLog2 = 3; + break; + default: + return Fail(c, "unexpected load operator"); + } + + return RenderLoadStoreAddress(c, load.address(), defaultAlignLog2); +} + +static bool +RenderStore(WasmRenderContext& c, AstStore& store) +{ + if (!RenderLoadStoreBase(c, store.address())) + return false; + + if (!RenderExpr(c, store.value())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, store); + uint32_t defaultAlignLog2; + switch (store.op()) { + case Op::I32Store8: + if (!c.buffer.append("i32.store8")) + return false; + defaultAlignLog2 = 0; + break; + case Op::I64Store8: + if (!c.buffer.append("i64.store8")) + return false; + defaultAlignLog2 = 0; + break; + case Op::I32Store16: + if (!c.buffer.append("i32.store16")) + return false; + defaultAlignLog2 = 1; + break; + case Op::I64Store16: + if (!c.buffer.append("i64.store16")) + return false; + defaultAlignLog2 = 1; + break; + case Op::I64Store32: + if (!c.buffer.append("i64.store32")) + return false; + defaultAlignLog2 = 2; + break; + case Op::I32Store: + if (!c.buffer.append("i32.store")) + return false; + defaultAlignLog2 = 2; + break; + case Op::I64Store: + if (!c.buffer.append("i64.store")) + return false; + defaultAlignLog2 = 3; + break; + case Op::F32Store: + if (!c.buffer.append("f32.store")) + return false; + defaultAlignLog2 = 2; + break; + case Op::F64Store: + if (!c.buffer.append("f64.store")) + return false; + defaultAlignLog2 = 3; + break; + default: + return Fail(c, "unexpected store operator"); + } + + return RenderLoadStoreAddress(c, store.address(), defaultAlignLog2); +} + +static bool +RenderBranch(WasmRenderContext& c, AstBranch& branch) +{ + Op op = branch.op(); + MOZ_ASSERT(op == Op::BrIf || op == Op::Br); + + if (op == Op::BrIf) { + if (!RenderExpr(c, branch.cond())) + return false; + } + + if (branch.maybeValue()) { + if (!RenderExpr(c, *(branch.maybeValue()))) + return false; + } + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, branch); + if (op == Op::BrIf ? !c.buffer.append("br_if ") : !c.buffer.append("br ")) + return false; + + return RenderRef(c, branch.target()); +} + +static bool +RenderBrTable(WasmRenderContext& c, AstBranchTable& table) +{ + if (table.maybeValue()) { + if (!RenderExpr(c, *(table.maybeValue()))) + return false; + } + + // Index + if (!RenderExpr(c, table.index())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, table); + if (!c.buffer.append("br_table ")) + return false; + + uint32_t tableLength = table.table().length(); + for (uint32_t i = 0; i < tableLength; i++) { + if (!RenderRef(c, table.table()[i])) + return false; + + if (!c.buffer.append(" ")) + return false; + } + + return RenderRef(c, table.def()); +} + +static bool +RenderReturn(WasmRenderContext& c, AstReturn& ret) +{ + if (ret.maybeExpr()) { + if (!RenderExpr(c, *(ret.maybeExpr()))) + return false; + } + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, ret); + return c.buffer.append("return"); +} + +static bool +RenderAtomicCmpXchg(WasmRenderContext& c, AstAtomicCmpXchg& cmpxchg) +{ + if (!RenderLoadStoreBase(c, cmpxchg.address())) + return false; + + if (!RenderExpr(c, cmpxchg.expected())) + return false; + if (!RenderExpr(c, cmpxchg.replacement())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, cmpxchg); + const char* opname; + switch (cmpxchg.op()) { + case ThreadOp::I32AtomicCmpXchg8U: opname = "i32.atomic.rmw8_u.cmpxchg"; break; + case ThreadOp::I64AtomicCmpXchg8U: opname = "i64.atomic.rmw8_u.cmpxchg"; break; + case ThreadOp::I32AtomicCmpXchg16U: opname = "i32.atomic.rmw16_u.cmpxchg"; break; + case ThreadOp::I64AtomicCmpXchg16U: opname = "i64.atomic.rmw16_u.cmpxchg"; break; + case ThreadOp::I64AtomicCmpXchg32U: opname = "i64.atomic.rmw32_u.cmpxchg"; break; + case ThreadOp::I32AtomicCmpXchg: opname = "i32.atomic.rmw.cmpxchg"; break; + case ThreadOp::I64AtomicCmpXchg: opname = "i64.atomic.rmw.cmpxchg"; break; + default: return Fail(c, "unexpected cmpxchg operator"); + } + + if (!c.buffer.append(opname, strlen(opname))) + return false; + + return RenderLoadStoreAddress(c, cmpxchg.address(), 0); +} + +static bool +RenderAtomicLoad(WasmRenderContext& c, AstAtomicLoad& load) +{ + if (!RenderLoadStoreBase(c, load.address())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, load); + const char* opname; + switch (load.op()) { + case ThreadOp::I32AtomicLoad8U: opname = "i32.atomic.load8_u"; break; + case ThreadOp::I64AtomicLoad8U: opname = "i64.atomic.load8_u"; break; + case ThreadOp::I32AtomicLoad16U: opname = "i32.atomic.load16_u"; break; + case ThreadOp::I64AtomicLoad16U: opname = "i64.atomic.load16_u"; break; + case ThreadOp::I64AtomicLoad32U: opname = "i64.atomic.load32_u"; break; + case ThreadOp::I32AtomicLoad: opname = "i32.atomic.load"; break; + case ThreadOp::I64AtomicLoad: opname = "i64.atomic.load"; break; + default: return Fail(c, "unexpected load operator"); + } + + if (!c.buffer.append(opname, strlen(opname))) + return false; + + return RenderLoadStoreAddress(c, load.address(), 0); +} + +static bool +RenderAtomicRMW(WasmRenderContext& c, AstAtomicRMW& rmw) +{ + if (!RenderLoadStoreBase(c, rmw.address())) + return false; + + if (!RenderExpr(c, rmw.value())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, rmw); + const char* opname; + switch (rmw.op()) { + case ThreadOp::I32AtomicAdd: opname = "i32.atomic.rmw.add"; break; + case ThreadOp::I64AtomicAdd: opname = "i64.atomic.rmw.add"; break; + case ThreadOp::I32AtomicAdd8U: opname = "i32.atomic.rmw8_u.add"; break; + case ThreadOp::I32AtomicAdd16U: opname = "i32.atomic.rmw16_u.add"; break; + case ThreadOp::I64AtomicAdd8U: opname = "i64.atomic.rmw8_u.add"; break; + case ThreadOp::I64AtomicAdd16U: opname = "i64.atomic.rmw16_u.add"; break; + case ThreadOp::I64AtomicAdd32U: opname = "i64.atomic.rmw32_u.add"; break; + case ThreadOp::I32AtomicSub: opname = "i32.atomic.rmw.sub"; break; + case ThreadOp::I64AtomicSub: opname = "i64.atomic.rmw.sub"; break; + case ThreadOp::I32AtomicSub8U: opname = "i32.atomic.rmw8_u.sub"; break; + case ThreadOp::I32AtomicSub16U: opname = "i32.atomic.rmw16_u.sub"; break; + case ThreadOp::I64AtomicSub8U: opname = "i64.atomic.rmw8_u.sub"; break; + case ThreadOp::I64AtomicSub16U: opname = "i64.atomic.rmw16_u.sub"; break; + case ThreadOp::I64AtomicSub32U: opname = "i64.atomic.rmw32_u.sub"; break; + case ThreadOp::I32AtomicAnd: opname = "i32.atomic.rmw.and"; break; + case ThreadOp::I64AtomicAnd: opname = "i64.atomic.rmw.and"; break; + case ThreadOp::I32AtomicAnd8U: opname = "i32.atomic.rmw8_u.and"; break; + case ThreadOp::I32AtomicAnd16U: opname = "i32.atomic.rmw16_u.and"; break; + case ThreadOp::I64AtomicAnd8U: opname = "i64.atomic.rmw8_u.and"; break; + case ThreadOp::I64AtomicAnd16U: opname = "i64.atomic.rmw16_u.and"; break; + case ThreadOp::I64AtomicAnd32U: opname = "i64.atomic.rmw32_u.and"; break; + case ThreadOp::I32AtomicOr: opname = "i32.atomic.rmw.or"; break; + case ThreadOp::I64AtomicOr: opname = "i64.atomic.rmw.or"; break; + case ThreadOp::I32AtomicOr8U: opname = "i32.atomic.rmw8_u.or"; break; + case ThreadOp::I32AtomicOr16U: opname = "i32.atomic.rmw16_u.or"; break; + case ThreadOp::I64AtomicOr8U: opname = "i64.atomic.rmw8_u.or"; break; + case ThreadOp::I64AtomicOr16U: opname = "i64.atomic.rmw16_u.or"; break; + case ThreadOp::I64AtomicOr32U: opname = "i64.atomic.rmw32_u.or"; break; + case ThreadOp::I32AtomicXor: opname = "i32.atomic.rmw.xor"; break; + case ThreadOp::I64AtomicXor: opname = "i64.atomic.rmw.xor"; break; + case ThreadOp::I32AtomicXor8U: opname = "i32.atomic.rmw8_u.xor"; break; + case ThreadOp::I32AtomicXor16U: opname = "i32.atomic.rmw16_u.xor"; break; + case ThreadOp::I64AtomicXor8U: opname = "i64.atomic.rmw8_u.xor"; break; + case ThreadOp::I64AtomicXor16U: opname = "i64.atomic.rmw16_u.xor"; break; + case ThreadOp::I64AtomicXor32U: opname = "i64.atomic.rmw32_u.xor"; break; + case ThreadOp::I32AtomicXchg: opname = "i32.atomic.rmw.xchg"; break; + case ThreadOp::I64AtomicXchg: opname = "i64.atomic.rmw.xchg"; break; + case ThreadOp::I32AtomicXchg8U: opname = "i32.atomic.rmw8_u.xchg"; break; + case ThreadOp::I32AtomicXchg16U: opname = "i32.atomic.rmw16_u.xchg"; break; + case ThreadOp::I64AtomicXchg8U: opname = "i64.atomic.rmw8_u.xchg"; break; + case ThreadOp::I64AtomicXchg16U: opname = "i64.atomic.rmw16_u.xchg"; break; + case ThreadOp::I64AtomicXchg32U: opname = "i64.atomic.rmw32_u.xchg"; break; + default: return Fail(c, "unexpected rmw operator"); + } + + if (!c.buffer.append(opname, strlen(opname))) + return false; + + return RenderLoadStoreAddress(c, rmw.address(), 0); +} + +static bool +RenderAtomicStore(WasmRenderContext& c, AstAtomicStore& store) +{ + if (!RenderLoadStoreBase(c, store.address())) + return false; + + if (!RenderExpr(c, store.value())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, store); + const char* opname; + switch (store.op()) { + case ThreadOp::I32AtomicStore8U: opname = "i32.atomic.store8_u"; break; + case ThreadOp::I64AtomicStore8U: opname = "i64.atomic.store8_u"; break; + case ThreadOp::I32AtomicStore16U: opname = "i32.atomic.store16_u"; break; + case ThreadOp::I64AtomicStore16U: opname = "i64.atomic.store16_u"; break; + case ThreadOp::I64AtomicStore32U: opname = "i64.atomic.store32_u"; break; + case ThreadOp::I32AtomicStore: opname = "i32.atomic.store"; break; + case ThreadOp::I64AtomicStore: opname = "i64.atomic.store"; break; + default: return Fail(c, "unexpected store operator"); + } + + if (!c.buffer.append(opname, strlen(opname))) + return false; + + return RenderLoadStoreAddress(c, store.address(), 0); +} + +static bool +RenderWait(WasmRenderContext& c, AstWait& wait) +{ + if (!RenderLoadStoreBase(c, wait.address())) + return false; + + if (!RenderExpr(c, wait.expected())) + return false; + + if (!RenderExpr(c, wait.timeout())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, wait); + const char* opname; + switch (wait.op()) { + case ThreadOp::I32Wait: opname = "i32.atomic.wait"; break; + case ThreadOp::I64Wait: opname = "i64.atomic.wait"; break; + default: return Fail(c, "unexpected wait operator"); + } + + if (!c.buffer.append(opname, strlen(opname))) + return false; + + return RenderLoadStoreAddress(c, wait.address(), 0); +} + +static bool +RenderWake(WasmRenderContext& c, AstWake& wake) +{ + if (!RenderLoadStoreBase(c, wake.address())) + return false; + + if (!RenderExpr(c, wake.count())) + return false; + + if (!RenderIndent(c)) + return false; + + if (!c.buffer.append("atomic.wake", strlen("atomic.wake"))) + return false; + + return RenderLoadStoreAddress(c, wake.address(), 0); +} + +#ifdef ENABLE_WASM_BULKMEM_OPS +static bool +RenderMemCopy(WasmRenderContext& c, AstMemCopy& mc) +{ + if (!RenderExpr(c, mc.dest())) + return false; + if (!RenderExpr(c, mc.src())) + return false; + if (!RenderExpr(c, mc.len())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, mc); + const char* opStr = "memory.copy"; + + return c.buffer.append(opStr, strlen(opStr)); +} + +static bool +RenderMemFill(WasmRenderContext& c, AstMemFill& mf) +{ + if (!RenderExpr(c, mf.start())) + return false; + if (!RenderExpr(c, mf.val())) + return false; + if (!RenderExpr(c, mf.len())) + return false; + + if (!RenderIndent(c)) + return false; + + MAP_AST_EXPR(c, mf); + const char* opStr = "memory.fill"; + + return c.buffer.append(opStr, strlen(opStr)); +} +#endif + +static bool +RenderExpr(WasmRenderContext& c, AstExpr& expr, bool newLine /* = true */) +{ + switch (expr.kind()) { + case AstExprKind::Drop: + if (!RenderDrop(c, expr.as())) + return false; + break; + case AstExprKind::Nop: + if (!RenderNop(c, expr.as())) + return false; + break; + case AstExprKind::Unreachable: + if (!RenderUnreachable(c, expr.as())) + return false; + break; + case AstExprKind::Call: + if (!RenderCall(c, expr.as())) + return false; + break; + case AstExprKind::CallIndirect: + if (!RenderCallIndirect(c, expr.as())) + return false; + break; + case AstExprKind::Const: + if (!RenderConst(c, expr.as())) + return false; + break; + case AstExprKind::GetLocal: + if (!RenderGetLocal(c, expr.as())) + return false; + break; + case AstExprKind::SetLocal: + if (!RenderSetLocal(c, expr.as())) + return false; + break; + case AstExprKind::GetGlobal: + if (!RenderGetGlobal(c, expr.as())) + return false; + break; + case AstExprKind::SetGlobal: + if (!RenderSetGlobal(c, expr.as())) + return false; + break; + case AstExprKind::TeeLocal: + if (!RenderTeeLocal(c, expr.as())) + return false; + break; + case AstExprKind::Block: + if (!RenderBlock(c, expr.as())) + return false; + break; + case AstExprKind::If: + if (!RenderIf(c, expr.as())) + return false; + break; + case AstExprKind::UnaryOperator: + if (!RenderUnaryOperator(c, expr.as())) + return false; + break; + case AstExprKind::BinaryOperator: + if (!RenderBinaryOperator(c, expr.as())) + return false; + break; + case AstExprKind::TernaryOperator: + if (!RenderTernaryOperator(c, expr.as())) + return false; + break; + case AstExprKind::ComparisonOperator: + if (!RenderComparisonOperator(c, expr.as())) + return false; + break; + case AstExprKind::ConversionOperator: + if (!RenderConversionOperator(c, expr.as())) + return false; + break; +#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS + case AstExprKind::ExtraConversionOperator: + if (!RenderExtraConversionOperator(c, expr.as())) + return false; + break; +#endif + case AstExprKind::Load: + if (!RenderLoad(c, expr.as())) + return false; + break; + case AstExprKind::Store: + if (!RenderStore(c, expr.as())) + return false; + break; + case AstExprKind::Branch: + if (!RenderBranch(c, expr.as())) + return false; + break; + case AstExprKind::BranchTable: + if (!RenderBrTable(c, expr.as())) + return false; + break; + case AstExprKind::Return: + if (!RenderReturn(c, expr.as())) + return false; + break; + case AstExprKind::First: + newLine = false; + if (!RenderFirst(c, expr.as())) + return false; + break; + case AstExprKind::CurrentMemory: + if (!RenderCurrentMemory(c, expr.as())) + return false; + break; + case AstExprKind::GrowMemory: + if (!RenderGrowMemory(c, expr.as())) + return false; + break; + case AstExprKind::AtomicCmpXchg: + if (!RenderAtomicCmpXchg(c, expr.as())) + return false; + break; + case AstExprKind::AtomicLoad: + if (!RenderAtomicLoad(c, expr.as())) + return false; + break; + case AstExprKind::AtomicRMW: + if (!RenderAtomicRMW(c, expr.as())) + return false; + break; + case AstExprKind::AtomicStore: + if (!RenderAtomicStore(c, expr.as())) + return false; + break; + case AstExprKind::Wait: + if (!RenderWait(c, expr.as())) + return false; + break; + case AstExprKind::Wake: + if (!RenderWake(c, expr.as())) + return false; + break; +#ifdef ENABLE_WASM_BULKMEM_OPS + case AstExprKind::MemCopy: + if (!RenderMemCopy(c, expr.as())) + return false; + break; + case AstExprKind::MemFill: + if (!RenderMemFill(c, expr.as())) + return false; + break; +#endif + default: + MOZ_CRASH("Bad AstExprKind"); + } + + return !newLine || c.buffer.append("\n"); +} + +static bool +RenderSignature(WasmRenderContext& c, const AstFuncType& funcType, + const AstNameVector* maybeLocals = nullptr) +{ + uint32_t paramsNum = funcType.args().length(); + + if (maybeLocals) { + for (uint32_t i = 0; i < paramsNum; i++) { + if (!c.buffer.append(" (param ")) + return false; + const AstName& name = (*maybeLocals)[i]; + if (!RenderNonemptyName(c, name)) + return false; + ValType arg = funcType.args()[i]; + if (!RenderValType(c, arg)) + return false; + if (!c.buffer.append(")")) + return false; + } + } else if (paramsNum > 0) { + if (!c.buffer.append(" (param")) + return false; + for (uint32_t i = 0; i < paramsNum; i++) { + if (!c.buffer.append(" ")) + return false; + ValType arg = funcType.args()[i]; + if (!RenderValType(c, arg)) + return false; + } + if (!c.buffer.append(")")) + return false; + } + if (funcType.ret() != ExprType::Void) { + if (!c.buffer.append(" (result ")) + return false; + if (!RenderExprType(c, funcType.ret())) + return false; + if (!c.buffer.append(")")) + return false; + } + return true; +} + +static bool +RenderFields(WasmRenderContext& c, const AstStructType& st) +{ + const AstNameVector& fieldNames = st.fieldNames(); + const AstValTypeVector& fieldTypes = st.fieldTypes(); + + for (uint32_t fieldIndex = 0; fieldIndex < fieldTypes.length(); fieldIndex++) { + if (!c.buffer.append("\n")) + return false; + if (!RenderIndent(c)) + return false; + if (!c.buffer.append("(field ")) + return false; + if (!RenderNonemptyName(c, fieldNames[fieldIndex])) + return false; + if (!RenderValType(c, fieldTypes[fieldIndex])) + return false; + if (!c.buffer.append(')')) + return false; + } + return true; +} + +template +static bool +RenderTypeStart(WasmRenderContext& c, const AstName& name, const char (&keyword)[ArrayLength]) +{ + if (!RenderIndent(c)) + return false; + if (!c.buffer.append("(type ")) + return false; + if (!RenderNonemptyName(c, name)) + return false; + if (!c.buffer.append("(")) + return false; + return c.buffer.append(keyword); +} + +static bool +RenderTypeEnd(WasmRenderContext& c) +{ + return c.buffer.append("))\n"); +} + +static bool +RenderTypeSection(WasmRenderContext& c, const AstModule::TypeDefVector& types) +{ + for (uint32_t typeIndex = 0; typeIndex < types.length(); typeIndex++) { + const AstTypeDef* type = types[typeIndex]; + if (type->isFuncType()) { + const AstFuncType* funcType = &type->asFuncType(); + if (!RenderTypeStart(c, funcType->name(), "func")) + return false; + if (!RenderSignature(c, *funcType)) + return false; + } else { + const AstStructType* st = &type->asStructType(); + if (!RenderTypeStart(c, st->name(), "struct")) + return false; + c.indent++; + if (!RenderFields(c, *st)) + return false; + c.indent--; + } + if (!RenderTypeEnd(c)) + return false; + } + + return true; +} + +static bool +RenderLimits(WasmRenderContext& c, const Limits& limits) +{ + if (!RenderInt32(c, limits.initial)) + return false; + if (limits.maximum) { + if (!c.buffer.append(" ")) + return false; + if (!RenderInt32(c, *limits.maximum)) + return false; + } + if (limits.shared == Shareable::True) { + if (!c.buffer.append(" shared")) + return false; + } + return true; +} + +static bool +RenderResizableTable(WasmRenderContext& c, const Limits& table) +{ + if (!c.buffer.append("(table ")) + return false; + if (!RenderLimits(c, table)) + return false; + MOZ_ASSERT(table.shared == Shareable::False); + return c.buffer.append(" anyfunc)"); +} + +static bool +RenderTableSection(WasmRenderContext& c, const AstModule& module) +{ + if (!module.hasTable()) + return true; + for (const AstResizable& table : module.tables()) { + if (table.imported) + continue; + if (!RenderIndent(c)) + return false; + if (!RenderResizableTable(c, table.limits)) + return false; + if (!c.buffer.append("\n")) + return false; + } + return true; +} + +static bool +RenderInlineExpr(WasmRenderContext& c, AstExpr& expr) +{ + if (!c.buffer.append("(")) + return false; + + uint32_t prevIndent = c.indent; + c.indent = 0; + if (!RenderExpr(c, expr, /* newLine */ false)) + return false; + c.indent = prevIndent; + + return c.buffer.append(")"); +} + +static bool +RenderElemSection(WasmRenderContext& c, const AstModule& module) +{ + for (const AstElemSegment* segment : module.elemSegments()) { + if (!RenderIndent(c)) + return false; + if (!c.buffer.append("(elem ")) + return false; + if (!RenderInlineExpr(c, *segment->offset())) + return false; + + for (const AstRef& elem : segment->elems()) { + if (!c.buffer.append(" ")) + return false; + + uint32_t index = elem.index(); + AstName name = index < module.funcImportNames().length() + ? module.funcImportNames()[index] + : module.funcs()[index - module.funcImportNames().length()]->name(); + + if (name.empty()) { + if (!RenderInt32(c, index)) + return false; + } else { + if (!RenderName(c, name)) + return false; + } + } + + if (!c.buffer.append(")\n")) + return false; + } + + return true; +} + +static bool +RenderGlobal(WasmRenderContext& c, const AstGlobal& glob, bool inImport = false) +{ + if (!c.buffer.append("(global ")) + return false; + + if (!inImport) { + if (!RenderName(c, glob.name())) + return false; + if (!c.buffer.append(" ")) + return false; + } + + if (glob.isMutable()) { + if (!c.buffer.append("(mut ")) + return false; + if (!RenderValType(c, glob.type())) + return false; + if (!c.buffer.append(")")) + return false; + } else { + if (!RenderValType(c, glob.type())) + return false; + } + + if (glob.hasInit()) { + if (!c.buffer.append(" ")) + return false; + if (!RenderInlineExpr(c, glob.init())) + return false; + } + + if (!c.buffer.append(")")) + return false; + + return inImport || c.buffer.append("\n"); +} + +static bool +RenderGlobalSection(WasmRenderContext& c, const AstModule& module) +{ + if (module.globals().empty()) + return true; + + for (const AstGlobal* global : module.globals()) { + if (!RenderIndent(c)) + return false; + if (!RenderGlobal(c, *global)) + return false; + } + + return true; +} + +static bool +RenderResizableMemory(WasmRenderContext& c, const Limits& memory) +{ + if (!c.buffer.append("(memory ")) + return false; + + Limits resizedMemory = memory; + + MOZ_ASSERT(resizedMemory.initial % PageSize == 0); + resizedMemory.initial /= PageSize; + + if (resizedMemory.maximum) { + if (*resizedMemory.maximum == UINT32_MAX) { + // See special casing in DecodeMemoryLimits. + *resizedMemory.maximum = MaxMemoryMaximumPages; + } else { + MOZ_ASSERT(*resizedMemory.maximum % PageSize == 0); + *resizedMemory.maximum /= PageSize; + } + } + + if (!RenderLimits(c, resizedMemory)) + return false; + + return c.buffer.append(")"); +} + +static bool +RenderImport(WasmRenderContext& c, AstImport& import, const AstModule& module) +{ + if (!RenderIndent(c)) + return false; + if (!c.buffer.append("(import ")) + return false; + if (!RenderName(c, import.name())) + return false; + if (!c.buffer.append(" \"")) + return false; + + const AstName& moduleName = import.module(); + if (!RenderEscapedString(c, moduleName)) + return false; + + if (!c.buffer.append("\" \"")) + return false; + + const AstName& fieldName = import.field(); + if (!RenderEscapedString(c, fieldName)) + return false; + + if (!c.buffer.append("\" ")) + return false; + + switch (import.kind()) { + case DefinitionKind::Function: { + if (!c.buffer.append("(func")) + return false; + const AstFuncType* funcType = &module.types()[import.funcType().index()]->asFuncType(); + if (!RenderSignature(c, *funcType)) + return false; + if (!c.buffer.append(")")) + return false; + break; + } + case DefinitionKind::Table: { + if (!RenderResizableTable(c, import.limits())) + return false; + break; + } + case DefinitionKind::Memory: { + if (!RenderResizableMemory(c, import.limits())) + return false; + break; + } + case DefinitionKind::Global: { + const AstGlobal& glob = import.global(); + if (!RenderGlobal(c, glob, /* inImport */ true)) + return false; + break; + } + } + + return c.buffer.append(")\n"); +} + +static bool +RenderImportSection(WasmRenderContext& c, const AstModule& module) +{ + for (AstImport* import : module.imports()) { + if (!RenderImport(c, *import, module)) + return false; + } + return true; +} + +static bool +RenderExport(WasmRenderContext& c, AstExport& export_, + const AstModule::NameVector& funcImportNames, + const AstModule::FuncVector& funcs) +{ + if (!RenderIndent(c)) + return false; + if (!c.buffer.append("(export \"")) + return false; + if (!RenderEscapedString(c, export_.name())) + return false; + if (!c.buffer.append("\" ")) + return false; + + switch (export_.kind()) { + case DefinitionKind::Function: { + uint32_t index = export_.ref().index(); + AstName name = index < funcImportNames.length() + ? funcImportNames[index] + : funcs[index - funcImportNames.length()]->name(); + if (name.empty()) { + if (!RenderInt32(c, index)) + return false; + } else { + if (!RenderName(c, name)) + return false; + } + break; + } + case DefinitionKind::Table: { + if (!c.buffer.append("table")) + return false; + break; + } + case DefinitionKind::Memory: { + if (!c.buffer.append("memory")) + return false; + break; + } + case DefinitionKind::Global: { + if (!c.buffer.append("global ")) + return false; + if (!RenderRef(c, export_.ref())) + return false; + break; + } + } + + return c.buffer.append(")\n"); +} + +static bool +RenderExportSection(WasmRenderContext& c, const AstModule::ExportVector& exports, + const AstModule::NameVector& funcImportNames, + const AstModule::FuncVector& funcs) +{ + uint32_t numExports = exports.length(); + for (uint32_t i = 0; i < numExports; i++) { + if (!RenderExport(c, *exports[i], funcImportNames, funcs)) + return false; + } + return true; +} + +static bool +RenderFunctionBody(WasmRenderContext& c, AstFunc& func, const AstModule::TypeDefVector& types) +{ + const AstFuncType* funcType = &types[func.funcType().index()]->asFuncType(); + + uint32_t argsNum = funcType->args().length(); + uint32_t localsNum = func.vars().length(); + if (localsNum > 0) { + if (!RenderIndent(c)) + return false; + for (uint32_t i = 0; i < localsNum; i++) { + if (!c.buffer.append("(local ")) + return false; + const AstName& name = func.locals()[argsNum + i]; + if (!name.empty()) { + if (!RenderName(c, name)) + return false; + if (!c.buffer.append(" ")) + return false; + } + ValType local = func.vars()[i]; + if (!RenderValType(c, local)) + return false; + if (!c.buffer.append(") ")) + return false; + } + if (!c.buffer.append("\n")) + return false; + } + + + uint32_t exprsNum = func.body().length(); + for (uint32_t i = 0; i < exprsNum; i++) { + if (!RenderExpr(c, *func.body()[i])) + return false; + } + + if (c.maybeSourceMap) { + if (!c.maybeSourceMap->exprlocs().emplaceBack(c.buffer.lineno(), c.buffer.column(), func.endOffset())) + return false; + } + + return true; +} + +static bool +RenderCodeSection(WasmRenderContext& c, const AstModule::FuncVector& funcs, + const AstModule::TypeDefVector& types) +{ + uint32_t numFuncBodies = funcs.length(); + for (uint32_t funcIndex = 0; funcIndex < numFuncBodies; funcIndex++) { + AstFunc* func = funcs[funcIndex]; + uint32_t funcTypeIndex = func->funcType().index(); + AstFuncType* funcType = &types[funcTypeIndex]->asFuncType(); + + if (!RenderIndent(c)) + return false; + if (!c.buffer.append("(func ")) + return false; + if (!func->name().empty()) { + if (!RenderName(c, func->name())) + return false; + } + + if (!RenderSignature(c, *funcType, &(func->locals()))) + return false; + if (!c.buffer.append("\n")) + return false; + + c.currentFuncIndex = funcIndex; + + c.indent++; + if (!RenderFunctionBody(c, *func, types)) + return false; + c.indent--; + if (!RenderIndent(c)) + return false; + if (!c.buffer.append(")\n")) + return false; + } + + return true; +} + +static bool +RenderMemorySection(WasmRenderContext& c, const AstModule& module) +{ + if (!module.hasMemory()) + return true; + + for (const AstResizable& memory : module.memories()) { + if (memory.imported) + continue; + if (!RenderIndent(c)) + return false; + if (!RenderResizableMemory(c, memory.limits)) + return false; + if (!c.buffer.append("\n")) + return false; + } + + return true; +} + +static bool +RenderDataSection(WasmRenderContext& c, const AstModule& module) +{ + uint32_t numSegments = module.dataSegments().length(); + if (!numSegments) + return true; + + for (const AstDataSegment* seg : module.dataSegments()) { + if (!RenderIndent(c)) + return false; + if (!c.buffer.append("(data ")) + return false; + if (!RenderInlineExpr(c, *seg->offset())) + return false; + if (!c.buffer.append("\n")) + return false; + + c.indent++; + for (const AstName& fragment : seg->fragments()) { + if (!RenderIndent(c)) + return false; + if (!c.buffer.append("\"")) + return false; + if (!RenderEscapedString(c, fragment)) + return false; + if (!c.buffer.append("\"\n")) + return false; + } + c.indent--; + + if (!RenderIndent(c)) + return false; + if (!c.buffer.append(")\n")) + return false; + } + + return true; +} + +static bool +RenderStartSection(WasmRenderContext& c, AstModule& module) +{ + if (!module.hasStartFunc()) + return true; + + if (!RenderIndent(c)) + return false; + if (!c.buffer.append("(start ")) + return false; + if (!RenderRef(c, module.startFunc().func())) + return false; + if (!c.buffer.append(")\n")) + return false; + + return true; +} + +static bool +RenderModule(WasmRenderContext& c, AstModule& module) +{ + if (!c.buffer.append("(module\n")) + return false; + + c.indent++; + + if (!RenderTypeSection(c, module.types())) + return false; + + if (!RenderImportSection(c, module)) + return false; + + if (!RenderTableSection(c, module)) + return false; + + if (!RenderMemorySection(c, module)) + return false; + + if (!RenderGlobalSection(c, module)) + return false; + + if (!RenderExportSection(c, module.exports(), module.funcImportNames(), module.funcs())) + return false; + + if (!RenderStartSection(c, module)) + return false; + + if (!RenderElemSection(c, module)) + return false; + + if (!RenderCodeSection(c, module.funcs(), module.types())) + return false; + + if (!RenderDataSection(c, module)) + return false; + + c.indent--; + + if (!c.buffer.append(")")) + return false; + + return true; +} + +#undef MAP_AST_EXPR + +/*****************************************************************************/ +// Top-level functions + +bool +wasm::BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer, + GeneratedSourceMap* sourceMap /* = nullptr */) +{ + LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE); + + AstModule* module; + if (!BinaryToAst(cx, bytes, length, lifo, &module)) + return false; + + WasmPrintBuffer buf(buffer); + WasmRenderContext c(cx, module, buf, sourceMap); + + if (!RenderModule(c, *module)) { + if (!cx->isExceptionPending()) + ReportOutOfMemory(cx); + return false; + } + + return true; +} diff --git a/js/src/wasm/WasmBinaryToText.h b/js/src/wasm/WasmBinaryToText.h new file mode 100644 index 000000000000..2aad45917e14 --- /dev/null +++ b/js/src/wasm/WasmBinaryToText.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * Copyright 2015 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wasm_binary_to_text_h +#define wasm_binary_to_text_h + +#include "NamespaceImports.h" + +#include "gc/Rooting.h" +#include "js/Class.h" +#include "wasm/WasmCode.h" + +namespace js { + +class StringBuffer; + +namespace wasm { + +// Translate the given binary representation of a wasm module into the module's textual +// representation. + +MOZ_MUST_USE bool +BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer, + GeneratedSourceMap* sourceMap = nullptr); + +} // namespace wasm + +} // namespace js + +#endif // namespace wasm_binary_to_text_h diff --git a/js/src/wasm/WasmDebug.cpp b/js/src/wasm/WasmDebug.cpp index b9597baa23b7..7ce1a7eee015 100644 --- a/js/src/wasm/WasmDebug.cpp +++ b/js/src/wasm/WasmDebug.cpp @@ -27,6 +27,7 @@ #include "util/StringBuffer.h" #include "util/Text.h" #include "vm/Debugger.h" +#include "wasm/WasmBinaryToText.h" #include "wasm/WasmInstance.h" #include "wasm/WasmValidate.h" @@ -36,6 +37,56 @@ using namespace js::wasm; using mozilla::BinarySearchIf; +bool +GeneratedSourceMap::searchLineByOffset(JSContext* cx, uint32_t offset, size_t* exprlocIndex) +{ + MOZ_ASSERT(!exprlocs_.empty()); + size_t exprlocsLength = exprlocs_.length(); + + // Lazily build sorted array for fast log(n) lookup. + if (!sortedByOffsetExprLocIndices_) { + ExprLocIndexVector scratch; + auto indices = MakeUnique(); + if (!indices || !indices->resize(exprlocsLength) || !scratch.resize(exprlocsLength)) { + ReportOutOfMemory(cx); + return false; + } + sortedByOffsetExprLocIndices_ = std::move(indices); + + for (size_t i = 0; i < exprlocsLength; i++) + (*sortedByOffsetExprLocIndices_)[i] = i; + + auto compareExprLocViaIndex = [&](uint32_t i, uint32_t j, bool* lessOrEqualp) -> bool { + *lessOrEqualp = exprlocs_[i].offset <= exprlocs_[j].offset; + return true; + }; + MOZ_ALWAYS_TRUE(MergeSort(sortedByOffsetExprLocIndices_->begin(), exprlocsLength, + scratch.begin(), compareExprLocViaIndex)); + } + + // Allowing non-exact search and if BinarySearchIf returns out-of-bound + // index, moving the index to the last index. + auto lookupFn = [&](uint32_t i) -> int { + const ExprLoc& loc = exprlocs_[i]; + return offset == loc.offset ? 0 : offset < loc.offset ? -1 : 1; + }; + size_t match; + Unused << BinarySearchIf(sortedByOffsetExprLocIndices_->begin(), 0, exprlocsLength, lookupFn, &match); + if (match >= exprlocsLength) + match = exprlocsLength - 1; + *exprlocIndex = (*sortedByOffsetExprLocIndices_)[match]; + return true; +} + +size_t +GeneratedSourceMap::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const +{ + size_t size = exprlocs_.sizeOfExcludingThis(mallocSizeOf); + if (sortedByOffsetExprLocIndices_) + size += sortedByOffsetExprLocIndices_->sizeOfIncludingThis(mallocSizeOf); + return size; +} + DebugState::DebugState(SharedCode code, const ShareableBytes* maybeBytecode, bool binarySource) @@ -48,14 +99,17 @@ DebugState::DebugState(SharedCode code, } const char enabledMessage[] = - "Restart with developer tools open to view WebAssembly source."; + "Restart with developer tools open to view WebAssembly source"; -const char noBinarySource[] = - "Configure the debugger to display WebAssembly bytecode."; +const char tooBigMessage[] = + "Unfortunately, this WebAssembly module is too big to view as text.\n" + "We are working hard to remove this limitation."; const char notGeneratedMessage[] = "WebAssembly text generation was disabled."; +static const unsigned TooBig = 1000000; + static const uint32_t DefaultBinarySourceColumnNumber = 1; static const CallSite* @@ -75,26 +129,98 @@ DebugState::createText(JSContext* cx) if (!maybeBytecode_) { if (!buffer.append(enabledMessage)) return nullptr; + + MOZ_ASSERT(!maybeSourceMap_); } else if (binarySource_) { if (!buffer.append(notGeneratedMessage)) return nullptr; - } else { - if (!buffer.append(noBinarySource)) + return buffer.finishString(); + } else if (maybeBytecode_->bytes.length() > TooBig) { + if (!buffer.append(tooBigMessage)) return nullptr; + + MOZ_ASSERT(!maybeSourceMap_); + } else { + const Bytes& bytes = maybeBytecode_->bytes; + auto sourceMap = MakeUnique(); + if (!sourceMap) { + ReportOutOfMemory(cx); + return nullptr; + } + maybeSourceMap_ = std::move(sourceMap); + + if (!BinaryToText(cx, bytes.begin(), bytes.length(), buffer, maybeSourceMap_.get())) + return nullptr; + +#if DEBUG + // Check that expression locations are sorted by line number. + uint32_t lastLineno = 0; + for (const ExprLoc& loc : maybeSourceMap_->exprlocs()) { + MOZ_ASSERT(lastLineno <= loc.lineno); + lastLineno = loc.lineno; + } +#endif } + return buffer.finishString(); } +bool +DebugState::ensureSourceMap(JSContext* cx) +{ + if (maybeSourceMap_ || !maybeBytecode_) + return true; + + // We just need to cache maybeSourceMap_, ignoring the text result. + return createText(cx); +} + +struct LineComparator +{ + const uint32_t lineno; + explicit LineComparator(uint32_t lineno) : lineno(lineno) {} + + int operator()(const ExprLoc& loc) const { + return lineno == loc.lineno ? 0 : lineno < loc.lineno ? -1 : 1; + } +}; + bool DebugState::getLineOffsets(JSContext* cx, size_t lineno, Vector* offsets) { if (!debugEnabled()) return true; - if (!binarySource_) + + if (binarySource_) { + const CallSite* callsite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), lineno); + if (callsite && !offsets->append(lineno)) + return false; return true; - const CallSite* callsite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), lineno); - if (callsite && !offsets->append(lineno)) + } + + if (!ensureSourceMap(cx)) return false; + + if (!maybeSourceMap_) + return true; // no source text available, keep offsets empty. + + ExprLocVector& exprlocs = maybeSourceMap_->exprlocs(); + + // Binary search for the expression with the specified line number and + // rewind to the first expression, if more than one expression on the same line. + size_t match; + if (!BinarySearchIf(exprlocs, 0, exprlocs.length(), LineComparator(lineno), &match)) + return true; + + while (match > 0 && exprlocs[match - 1].lineno == lineno) + match--; + + // Return all expression offsets that were printed on the specified line. + for (size_t i = match; i < exprlocs.length() && exprlocs[i].lineno == lineno; i++) { + if (!offsets->append(exprlocs[i].offset)) + return false; + } + return true; } @@ -103,16 +229,25 @@ DebugState::getAllColumnOffsets(JSContext* cx, Vector* offsets) { if (!metadata().debugEnabled) return true; - if (!binarySource_) + + if (binarySource_) { + for (const CallSite& callSite : metadata(Tier::Debug).callSites) { + if (callSite.kind() != CallSite::Breakpoint) + continue; + uint32_t offset = callSite.lineOrBytecode(); + if (!offsets->emplaceBack(offset, DefaultBinarySourceColumnNumber, offset)) + return false; + } return true; - for (const CallSite& callSite : metadata(Tier::Debug).callSites) { - if (callSite.kind() != CallSite::Breakpoint) - continue; - uint32_t offset = callSite.lineOrBytecode(); - if (!offsets->emplaceBack(offset, DefaultBinarySourceColumnNumber, offset)) - return false; } - return true; + + if (!ensureSourceMap(cx)) + return false; + + if (!maybeSourceMap_) + return true; // no source text available, keep offsets empty. + + return offsets->appendAll(maybeSourceMap_->exprlocs()); } bool @@ -121,13 +256,30 @@ DebugState::getOffsetLocation(JSContext* cx, uint32_t offset, bool* found, size_ *found = false; if (!debugEnabled()) return true; - if (!binarySource_) + + if (binarySource_) { + if (!SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset)) + return true; // offset was not found + *found = true; + *lineno = offset; + *column = DefaultBinarySourceColumnNumber; return true; - if (!SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset)) - return true; // offset was not found + } + + if (!ensureSourceMap(cx)) + return false; + + if (!maybeSourceMap_ || maybeSourceMap_->exprlocs().empty()) + return true; // no source text available + + size_t foundAt; + if (!maybeSourceMap_->searchLineByOffset(cx, offset, &foundAt)) + return false; + + const ExprLoc& loc = maybeSourceMap_->exprlocs()[foundAt]; *found = true; - *lineno = offset; - *column = DefaultBinarySourceColumnNumber; + *lineno = loc.lineno; + *column = loc.column; return true; } @@ -137,10 +289,18 @@ DebugState::totalSourceLines(JSContext* cx, uint32_t* count) *count = 0; if (!debugEnabled()) return true; - if (!binarySource_) + + if (binarySource_) { + if (maybeBytecode_) + *count = maybeBytecode_->length(); return true; - if (maybeBytecode_) - *count = maybeBytecode_->length(); + } + + if (!ensureSourceMap(cx)) + return false; + + if (maybeSourceMap_) + *count = maybeSourceMap_->totalLines(); return true; } @@ -548,6 +708,8 @@ DebugState::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* data) const { code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code, data); + if (maybeSourceMap_) + *data += maybeSourceMap_->sizeOfExcludingThis(mallocSizeOf); if (maybeBytecode_) *data += maybeBytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes); } diff --git a/js/src/wasm/WasmDebug.h b/js/src/wasm/WasmDebug.h index d2b458f311b1..ab901bb1e1a9 100644 --- a/js/src/wasm/WasmDebug.h +++ b/js/src/wasm/WasmDebug.h @@ -34,8 +34,8 @@ namespace wasm { struct MetadataTier; -// The generated source location for the AST node/expression. The offset field -// refers an offset in an binary format file. +// The generated source location for the AST node/expression. The offset field refers +// an offset in an binary format file. struct ExprLoc { @@ -48,14 +48,39 @@ struct ExprLoc {} }; +typedef Vector ExprLocVector; +typedef Vector ExprLocIndexVector; + +// The generated source map for WebAssembly binary file. This map is generated during +// building the text buffer (see BinaryToExperimentalText). + +class GeneratedSourceMap +{ + ExprLocVector exprlocs_; + UniquePtr sortedByOffsetExprLocIndices_; + uint32_t totalLines_; + + public: + explicit GeneratedSourceMap() : totalLines_(0) {} + ExprLocVector& exprlocs() { return exprlocs_; } + + uint32_t totalLines() { return totalLines_; } + void setTotalLines(uint32_t val) { totalLines_ = val; } + + bool searchLineByOffset(JSContext* cx, uint32_t offset, size_t* exprlocIndex); + + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; +}; + +typedef UniquePtr UniqueGeneratedSourceMap; typedef HashMap, SystemAllocPolicy> StepModeCounters; -typedef HashMap, SystemAllocPolicy> - WasmBreakpointSiteMap; +typedef HashMap, SystemAllocPolicy> WasmBreakpointSiteMap; class DebugState { const SharedCode code_; const SharedBytes maybeBytecode_; + UniqueGeneratedSourceMap maybeSourceMap_; bool binarySource_; // State maintained when debugging is enabled. In this case, the Code is @@ -67,6 +92,7 @@ class DebugState StepModeCounters stepModeCounters_; void toggleDebugTrap(uint32_t offset, bool enabled); + bool ensureSourceMap(JSContext* cx); public: DebugState(SharedCode code, @@ -76,6 +102,10 @@ class DebugState const Bytes* maybeBytecode() const { return maybeBytecode_ ? &maybeBytecode_->bytes : nullptr; } bool binarySource() const { return binarySource_; } + // If the source bytecode was saved when this Code was constructed, this + // method will render the binary as text. Otherwise, a diagnostic string + // will be returned. + JSString* createText(JSContext* cx); bool getLineOffsets(JSContext* cx, size_t lineno, Vector* offsets); bool getAllColumnOffsets(JSContext* cx, Vector* offsets); diff --git a/js/src/wasm/WasmTextToBinary.cpp b/js/src/wasm/WasmTextToBinary.cpp index df066677be6d..b307b3117ea5 100644 --- a/js/src/wasm/WasmTextToBinary.cpp +++ b/js/src/wasm/WasmTextToBinary.cpp @@ -5544,7 +5544,7 @@ EncodeTableSection(Encoder& e, AstModule& module) } static bool -EncodeFunctionBody(Encoder& e, Uint32Vector* offsets, AstFunc& func) +EncodeFunctionBody(Encoder& e, AstFunc& func) { size_t bodySizeAt; if (!e.writePatchableVarU32(&bodySizeAt)) @@ -5559,14 +5559,10 @@ EncodeFunctionBody(Encoder& e, Uint32Vector* offsets, AstFunc& func) return false; for (AstExpr* expr : func.body()) { - if (!offsets->append(e.currentOffset())) - return false; if (!EncodeExpr(e, *expr)) return false; } - if (!offsets->append(e.currentOffset())) - return false; if (!e.writeOp(Op::End)) return false; @@ -5592,7 +5588,7 @@ EncodeStartSection(Encoder& e, AstModule& module) } static bool -EncodeCodeSection(Encoder& e, Uint32Vector* offsets, AstModule& module) +EncodeCodeSection(Encoder& e, AstModule& module) { if (module.funcs().empty()) return true; @@ -5605,7 +5601,7 @@ EncodeCodeSection(Encoder& e, Uint32Vector* offsets, AstModule& module) return false; for (AstFunc* func : module.funcs()) { - if (!EncodeFunctionBody(e, offsets, *func)) + if (!EncodeFunctionBody(e, *func)) return false; } @@ -5712,7 +5708,7 @@ EncodeElemSection(Encoder& e, AstModule& module) } static bool -EncodeModule(AstModule& module, Uint32Vector* offsets, Bytes* bytes) +EncodeModule(AstModule& module, Bytes* bytes) { Encoder e(*bytes); @@ -5749,7 +5745,7 @@ EncodeModule(AstModule& module, Uint32Vector* offsets, Bytes* bytes) if (!EncodeElemSection(e, module)) return false; - if (!EncodeCodeSection(e, offsets, module)) + if (!EncodeCodeSection(e, module)) return false; if (!EncodeDataSection(e, module)) @@ -5783,8 +5779,7 @@ EncodeBinaryModule(const AstModule& module, Bytes* bytes) /*****************************************************************************/ bool -wasm::TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, Uint32Vector* offsets, - UniqueChars* error) +wasm::TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, UniqueChars* error) { LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE); @@ -5799,5 +5794,5 @@ wasm::TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, Uin if (!ResolveModule(lifo, module, error)) return false; - return EncodeModule(*module, offsets, bytes); + return EncodeModule(*module, bytes); } diff --git a/js/src/wasm/WasmTextToBinary.h b/js/src/wasm/WasmTextToBinary.h index 0e525bb4aea6..a67e14c2a1f1 100644 --- a/js/src/wasm/WasmTextToBinary.h +++ b/js/src/wasm/WasmTextToBinary.h @@ -29,8 +29,7 @@ namespace wasm { // other than out-of-memory an error message string will be stored in 'error'. extern MOZ_MUST_USE bool -TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, Uint32Vector* offsets, - UniqueChars* error); +TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, UniqueChars* error); } // namespace wasm } // namespace js diff --git a/js/src/wasm/WasmTextUtils.cpp b/js/src/wasm/WasmTextUtils.cpp new file mode 100644 index 000000000000..6582ae9ce89d --- /dev/null +++ b/js/src/wasm/WasmTextUtils.cpp @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * Copyright 2016 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wasm/WasmTextUtils.h" + +#include "util/StringBuffer.h" +#include "wasm/WasmTypes.h" + +using namespace js; +using namespace jit; +using namespace wasm; + +using mozilla::IsNaN; + +template +bool +wasm::RenderInBase(StringBuffer& sb, uint64_t num) +{ + uint64_t n = num; + uint64_t pow = 1; + while (n) { + pow *= base; + n /= base; + } + pow /= base; + + n = num; + while (pow) { + if (!sb.append("0123456789abcdef"[n / pow])) + return false; + n -= (n / pow) * pow; + pow /= base; + } + + return true; +} + +template bool wasm::RenderInBase<10>(StringBuffer& sb, uint64_t num); + +template +bool +wasm::RenderNaN(StringBuffer& sb, T num) +{ + typedef typename mozilla::SelectTrait Traits; + typedef typename Traits::Bits Bits; + + MOZ_ASSERT(IsNaN(num)); + + Bits bits = mozilla::BitwiseCast(num); + if ((bits & Traits::kSignBit) && !sb.append("-")) + return false; + if (!sb.append("nan")) + return false; + + Bits payload = bits & Traits::kSignificandBits; + // Only render the payload if it's not the spec's default NaN. + if (payload == ((Traits::kSignificandBits + 1) >> 1)) + return true; + + return sb.append(":0x") && + RenderInBase<16>(sb, payload); +} + +template MOZ_MUST_USE bool wasm::RenderNaN(StringBuffer& b, float num); +template MOZ_MUST_USE bool wasm::RenderNaN(StringBuffer& b, double num); diff --git a/js/src/wasm/WasmTextUtils.h b/js/src/wasm/WasmTextUtils.h new file mode 100644 index 000000000000..386ba03a032a --- /dev/null +++ b/js/src/wasm/WasmTextUtils.h @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * Copyright 2016 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wasm_text_utils +#define wasm_text_utils + +#include "NamespaceImports.h" + +#include "util/StringBuffer.h" + +namespace js { +namespace wasm { + +template +MOZ_MUST_USE bool +RenderInBase(StringBuffer& sb, uint64_t num); + +template +MOZ_MUST_USE bool +RenderNaN(StringBuffer& sb, T num); + +// Helper class, StringBuffer wrapper, to track the position (line and column) +// within the generated source. + +class WasmPrintBuffer +{ + StringBuffer& stringBuffer_; + uint32_t lineno_; + uint32_t column_; + + public: + explicit WasmPrintBuffer(StringBuffer& stringBuffer) + : stringBuffer_(stringBuffer), + lineno_(1), + column_(1) + {} + inline char processChar(char ch) { + if (ch == '\n') { + lineno_++; column_ = 1; + } else + column_++; + return ch; + } + inline char16_t processChar(char16_t ch) { + if (ch == '\n') { + lineno_++; column_ = 1; + } else + column_++; + return ch; + } + bool append(const char ch) { + return stringBuffer_.append(processChar(ch)); + } + bool append(const char16_t ch) { + return stringBuffer_.append(processChar(ch)); + } + bool append(const char* str, size_t length) { + for (size_t i = 0; i < length; i++) + processChar(str[i]); + return stringBuffer_.append(str, length); + } + bool append(const char16_t* begin, const char16_t* end) { + for (const char16_t* p = begin; p != end; p++) + processChar(*p); + return stringBuffer_.append(begin, end); + } + bool append(const char16_t* str, size_t length) { + return append(str, str + length); + } + template + bool append(const char (&array)[ArrayLength]) { + static_assert(ArrayLength > 0, "null-terminated"); + MOZ_ASSERT(array[ArrayLength - 1] == '\0'); + return append(array, ArrayLength - 1); + } + char16_t getChar(size_t index) { + return stringBuffer_.getChar(index); + } + size_t length() { + return stringBuffer_.length(); + } + StringBuffer& stringBuffer() { return stringBuffer_; } + uint32_t lineno() { return lineno_; } + uint32_t column() { return column_; } +}; + +} // namespace wasm +} // namespace js + +#endif // namespace wasm_text_utils diff --git a/js/src/wasm/WasmValidate.h b/js/src/wasm/WasmValidate.h index 6f19cb6fbe0b..ed6bdbd0ec98 100644 --- a/js/src/wasm/WasmValidate.h +++ b/js/src/wasm/WasmValidate.h @@ -132,6 +132,9 @@ struct ModuleEnvironment bool funcIsImport(uint32_t funcIndex) const { return funcIndex < funcImportGlobalDataOffsets.length(); } + uint32_t funcIndexToFuncTypeIndex(uint32_t funcIndex) const { + return TypeDef::fromFuncTypeWithIdPtr(funcTypes[funcIndex]) - types.begin(); + } }; // The Encoder class appends bytes to the Bytes object it is given during From db3005b5b39bb3e76c61547a7fbffe3cb6411be1 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Wed, 27 Jun 2018 11:23:15 +0100 Subject: [PATCH 07/43] Bug 1470992 - Don't call though public APIs when converting atoms to strings in the parser r=jandem --- js/src/jit-test/tests/parser/bug-1470992.js | 5 +++++ js/src/jsapi.cpp | 23 +-------------------- js/src/vm/JSAtom.cpp | 3 ++- js/src/vm/StringType.cpp | 23 +++++++++++++++++++++ js/src/vm/StringType.h | 6 ++++++ 5 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 js/src/jit-test/tests/parser/bug-1470992.js diff --git a/js/src/jit-test/tests/parser/bug-1470992.js b/js/src/jit-test/tests/parser/bug-1470992.js new file mode 100644 index 000000000000..e03828418ed7 --- /dev/null +++ b/js/src/jit-test/tests/parser/bug-1470992.js @@ -0,0 +1,5 @@ +if (helperThreadCount() === 0) + quit(); + +offThreadCompileModule("export { x };"); +gcslice(10); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index a478caf2bcbe..66a76ec626e0 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -6102,34 +6102,13 @@ JS_DecodeBytes(JSContext* cx, const char* src, size_t srclen, char16_t* dst, siz return true; } -static char* -EncodeLatin1(JSContext* cx, JSString* str) -{ - JSLinearString* linear = str->ensureLinear(cx); - if (!linear) - return nullptr; - - JS::AutoCheckCannotGC nogc; - if (linear->hasTwoByteChars()) - return JS::LossyTwoByteCharsToNewLatin1CharsZ(cx, linear->twoByteRange(nogc)).c_str(); - - size_t len = str->length(); - Latin1Char* buf = cx->pod_malloc(len + 1); - if (!buf) - return nullptr; - - mozilla::PodCopy(buf, linear->latin1Chars(nogc), len); - buf[len] = '\0'; - return reinterpret_cast(buf); -} - JS_PUBLIC_API(char*) JS_EncodeString(JSContext* cx, JSString* str) { AssertHeapIsIdle(); CHECK_REQUEST(cx); - return EncodeLatin1(cx, str); + return js::EncodeLatin1(cx, str).release(); } JS_PUBLIC_API(char*) diff --git a/js/src/vm/JSAtom.cpp b/js/src/vm/JSAtom.cpp index 0677621a9817..648c07ea8141 100644 --- a/js/src/vm/JSAtom.cpp +++ b/js/src/vm/JSAtom.cpp @@ -121,7 +121,8 @@ js::AtomToPrintableString(JSContext* cx, JSAtom* atom, JSAutoByteString* bytes) JSString* str = QuoteString(cx, atom, 0); if (!str) return nullptr; - return bytes->encodeLatin1(cx, str); + bytes->initBytes(EncodeLatin1(cx, str)); + return bytes->ptr(); } #define DEFINE_PROTO_STRING(name,init,clasp) const char js_##name##_str[] = #name; diff --git a/js/src/vm/StringType.cpp b/js/src/vm/StringType.cpp index 8b8d49d4f7d4..12a4b3db33f0 100644 --- a/js/src/vm/StringType.cpp +++ b/js/src/vm/StringType.cpp @@ -2007,6 +2007,29 @@ JSString::fillWithRepresentatives(JSContext* cx, HandleArrayObject array) /*** Conversions *********************************************************************************/ +UniqueChars +js::EncodeLatin1(JSContext* cx, JSString* str) +{ + JSLinearString* linear = str->ensureLinear(cx); + if (!linear) + return nullptr; + + JS::AutoCheckCannotGC nogc; + if (linear->hasTwoByteChars()) { + Latin1CharsZ chars = JS::LossyTwoByteCharsToNewLatin1CharsZ(cx, linear->twoByteRange(nogc)); + return UniqueChars(chars.c_str()); + } + + size_t len = str->length(); + Latin1Char* buf = cx->pod_malloc(len + 1); + if (!buf) + return nullptr; + + mozilla::PodCopy(buf, linear->latin1Chars(nogc), len); + buf[len] = '\0'; + return UniqueChars(reinterpret_cast(buf)); +} + const char* js::ValueToPrintableLatin1(JSContext* cx, const Value& vArg, JSAutoByteString* bytes, bool asSource) diff --git a/js/src/vm/StringType.h b/js/src/vm/StringType.h index 3e39e4224dc6..8638fe7ff8b3 100644 --- a/js/src/vm/StringType.h +++ b/js/src/vm/StringType.h @@ -1589,6 +1589,12 @@ SubstringKernel(JSContext* cx, HandleString str, int32_t beginInt, int32_t lengt /*** Conversions *********************************************************************************/ +/* + * Convert a string to a printable C string. + */ +UniqueChars +EncodeLatin1(JSContext* cx, JSString* str); + /* * Convert a value to a printable C string. * From 5f96c00e81d1c8f8d9aa0116e1c374c7311bc141 Mon Sep 17 00:00:00 2001 From: "Thomas P." Date: Tue, 26 Jun 2018 02:20:22 +0000 Subject: [PATCH 08/43] Bug 1464257: fix lut8Type tag implementation. r=bas --- gfx/qcms/iccread.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gfx/qcms/iccread.c b/gfx/qcms/iccread.c index 2b4b51412204..89e8d29193c4 100644 --- a/gfx/qcms/iccread.c +++ b/gfx/qcms/iccread.c @@ -691,18 +691,17 @@ static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index uint16_t num_input_table_entries; uint16_t num_output_table_entries; uint8_t in_chan, grid_points, out_chan; - uint32_t clut_offset, output_offset; + uint32_t input_offset, clut_offset, output_offset; uint32_t clut_size; size_t entry_size; struct lutType *lut; uint32_t i; - /* I'm not sure why the spec specifies a fixed number of entries for LUT8 tables even though - * they have room for the num_entries fields */ if (type == LUT8_TYPE) { num_input_table_entries = 256; num_output_table_entries = 256; entry_size = 1; + input_offset = 48; } else if (type == LUT16_TYPE) { num_input_table_entries = read_u16(src, offset + 48); num_output_table_entries = read_u16(src, offset + 50); @@ -711,6 +710,7 @@ static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index return NULL; } entry_size = 2; + input_offset = 52; } else { assert(0); // the caller checks that this doesn't happen invalid_source(src, "Unexpected lut type"); @@ -765,13 +765,13 @@ static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index for (i = 0; i < (uint32_t)(lut->num_input_table_entries * in_chan); i++) { if (type == LUT8_TYPE) { - lut->input_table[i] = uInt8Number_to_float(read_uInt8Number(src, offset + 52 + i * entry_size)); + lut->input_table[i] = uInt8Number_to_float(read_uInt8Number(src, offset + input_offset + i * entry_size)); } else { - lut->input_table[i] = uInt16Number_to_float(read_uInt16Number(src, offset + 52 + i * entry_size)); + lut->input_table[i] = uInt16Number_to_float(read_uInt16Number(src, offset + input_offset + i * entry_size)); } } - clut_offset = offset + 52 + lut->num_input_table_entries * in_chan * entry_size; + clut_offset = offset + input_offset + lut->num_input_table_entries * in_chan * entry_size; for (i = 0; i < clut_size * out_chan; i+=3) { if (type == LUT8_TYPE) { lut->clut_table[i+0] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 0)); From 81a7e68e791507d42ce7313a75285ddcf49c3960 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Wed, 27 Jun 2018 14:51:43 +0200 Subject: [PATCH 09/43] Bug 1394102 - FetchEvent Request.signal should be aborted when the corrisponding nsIChannel is canceled r=bkelly --- dom/serviceworkers/ServiceWorkerPrivate.cpp | 250 +++++++++++++++++++- 1 file changed, 247 insertions(+), 3 deletions(-) diff --git a/dom/serviceworkers/ServiceWorkerPrivate.cpp b/dom/serviceworkers/ServiceWorkerPrivate.cpp index 7f906ce21d03..a6309c7e9369 100644 --- a/dom/serviceworkers/ServiceWorkerPrivate.cpp +++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp @@ -11,6 +11,7 @@ #include "nsContentUtils.h" #include "nsICacheInfoChannel.h" #include "nsIHttpChannelInternal.h" +#include "nsIHttpProtocolHandler.h" #include "nsIHttpHeaderVisitor.h" #include "nsINamed.h" #include "nsINetworkInterceptController.h" @@ -116,6 +117,232 @@ ServiceWorkerPrivate::~ServiceWorkerPrivate() namespace { +class AbortChannelObserver; + +// Simple runnable that calls AbortChannelObserver::MaybeAbortAndReleaseSignal() +// on the worker thread. +class AbortSignalRunnable : public WorkerRunnable +{ + RefPtr mObserver; + bool mSignalToAbort; + +public: + AbortSignalRunnable(WorkerPrivate* aWorkerPrivate, + AbortChannelObserver* aObserver, + bool aSignalToAbort) + : WorkerRunnable(aWorkerPrivate) + , mObserver(aObserver) + , mSignalToAbort(aSignalToAbort) + { + MOZ_ASSERT(mObserver); + } + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override; +}; + +// This object observes the terminations of nsIChannel objects. When |mChannel| +// is terminated, the AbortSignal object, owned by AbortSignalWorkerHolder, is +// aborted on the worker thread. This object is kept alive by the observer and +// by FetchEventRunnable until the AbortSignal is created on the the worker +// thread. +class AbortChannelObserver final : public nsIObserver +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + static already_AddRefed + Create(nsIChannel* aChannel) + { + MOZ_ASSERT(aChannel); + AssertIsOnMainThread(); + + RefPtr observer = + new AbortChannelObserver(aChannel); + + // Let's use NS_HTTP_ON_STOP_REQUEST_TOPIC to know when the channel is + // released: maybe we have to abort the AbortSignal object. + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (NS_WARN_IF(!obs)) { + return nullptr; + } + + if (NS_FAILED(obs->AddObserver(observer, NS_HTTP_ON_STOP_REQUEST_TOPIC, + false))) { + return nullptr; + } + + return observer.forget(); + } + + NS_IMETHOD + Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) override + { + AssertIsOnMainThread(); + + // This is not our channel. + if (!SameCOMIdentity(aSubject, mChannel)) { + return NS_OK; + } + + // Maybe the observer is the only reason why this object is still alive. + // Let's keep it alive until the operation is completed. + RefPtr kungFuDeathGrip = this; + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + obs->RemoveObserver(this, NS_HTTP_ON_STOP_REQUEST_TOPIC); + } + + nsCOMPtr internalChannel = + do_QueryInterface(mChannel); + NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE); + mChannel = nullptr; + + bool canceled = false; + Unused << NS_WARN_IF(NS_FAILED(internalChannel->GetCanceled(&canceled))); + + // From now on, we touch cross-process things. We need to protect them with + // the locking of a mutex. + RefPtr r; + { + MutexAutoLock lock(mMutex); + + mState = canceled ? eChannelAborted : eChannelSucceeded; + + if (!mWorkerRef) { + // CreateSignal() has not been called yet. + return NS_OK; + } + + // This will release AbortSignal on the correct thread. + r = new AbortSignalRunnable(mWorkerRef->GetUnsafePrivate(), this, canceled); + } + + if (NS_WARN_IF(!r->Dispatch())) { + // If this happens, it's not a big deal. |mWorkerRef| will be notified + // and AbortSignal will be released. + return NS_ERROR_FAILURE; + } + + return NS_OK; + } + + already_AddRefed + CreateSignal(WorkerPrivate* aWorkerPrivate) + { + MOZ_ASSERT(aWorkerPrivate); + MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); + + RefPtr abortSignal; + { + MutexAutoLock lock(mMutex); + MOZ_DIAGNOSTIC_ASSERT(!mWorkerRef); + + abortSignal = new AbortSignal(mState == eChannelAborted); + + if (mState != eChannelActive) { + return abortSignal.forget(); + } + } + + // This is needed to keep AbortSignal alive. + RefPtr self = this; + RefPtr workerRef = + WeakWorkerRef::Create(aWorkerPrivate, + [self]() { + self->MaybeAbortAndReleaseSignal(false); + } + ); + if (NS_WARN_IF(!workerRef)) { + return nullptr; + } + + { + MutexAutoLock lock(mMutex); + if (mState == eChannelActive) { + mSignal = abortSignal; + mWorkerRef.swap(workerRef); + } + } + + return abortSignal.forget(); + } + + void + Reset() + { + MOZ_ASSERT(IsCurrentThreadRunningWorker()); + + RefPtr workerRef; + { + MutexAutoLock lock(mMutex); + mSignal = nullptr; + workerRef.swap(mWorkerRef); + } + } + + void + MaybeAbortAndReleaseSignal(bool aToAbort) + { + MOZ_ASSERT(IsCurrentThreadRunningWorker()); + + RefPtr workerRef; + { + MutexAutoLock lock(mMutex); + MOZ_ASSERT(mWorkerRef); + + if (aToAbort && mSignal) { + mSignal->Abort(); + } + + workerRef.swap(mWorkerRef); + mSignal = nullptr; + } + } + +private: + explicit AbortChannelObserver(nsIChannel* aChannel) + : mChannel(aChannel) + , mMutex("AbortChannelObserver::mMutex") + , mState(eChannelActive) + {} + + ~AbortChannelObserver() + {} + + nsCOMPtr mChannel; + + // This object is touched in main-thread and workers but it's always released + // on the worker thread. When touched, the mutex is locked. + RefPtr mWorkerRef; + + RefPtr mSignal; + + mozilla::Mutex mMutex; + + // The following is protected by mutex. + enum { + // The channel is active. + eChannelActive, + + // the channel has been canceled. + eChannelAborted, + + // the cannel has been terminated successfully. + eChannelSucceeded, + } mState; +}; + +NS_IMPL_ISUPPORTS(AbortChannelObserver, nsIObserver) + +bool +AbortSignalRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) +{ + mObserver->MaybeAbortAndReleaseSignal(mSignalToAbort); + return true; +} + class CheckScriptEvaluationWithCallback final : public WorkerRunnable { nsMainThreadPtrHandle mServiceWorkerPrivate; @@ -1343,6 +1570,8 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable nsCString mReferrer; ReferrerPolicy mReferrerPolicy; nsString mIntegrity; + RefPtr mAbortObserver; + public: FetchEventRunnable(WorkerPrivate* aWorkerPrivate, KeepAliveToken* aKeepAliveToken, @@ -1508,6 +1737,11 @@ public: mUploadStream = uploadStream; } + mAbortObserver = AbortChannelObserver::Create(channel); + if (NS_WARN_IF(!mAbortObserver)) { + return NS_ERROR_FAILURE; + } + return NS_OK; } @@ -1540,6 +1774,9 @@ public: if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable))) { NS_WARNING("Failed to resume channel on FetchEventRunnable::Cancel()!\n"); } + + mAbortObserver = nullptr; + WorkerRunnable::Cancel(); return NS_OK; } @@ -1595,6 +1832,14 @@ private: } } + RefPtr abortSignal = + mAbortObserver->CreateSignal(aWorkerPrivate); + + RefPtr observer = mAbortObserver.forget(); + auto abortObserverRAII = mozilla::MakeScopeExit([observer] { + observer->Reset(); + }); + ErrorResult result; internalHeaders->SetGuard(HeadersGuardEnum::Immutable, result); if (NS_WARN_IF(result.Failed())) { @@ -1634,9 +1879,7 @@ private: return false; } - // TODO This request object should be created with a AbortSignal object - // which should be aborted if the loading is aborted. See bug 1394102. - RefPtr request = new Request(global, internalReq, nullptr); + RefPtr request = new Request(global, internalReq, abortSignal); MOZ_ASSERT_IF(internalReq->IsNavigationRequest(), request->Redirect() == RequestRedirect::Manual); @@ -1684,6 +1927,7 @@ private: MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(runnable.forget())); } + abortObserverRAII.release(); return true; } }; From 473293997d9b590213c1002ffeeeb01ec0c3d1b3 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Wed, 27 Jun 2018 14:51:57 +0200 Subject: [PATCH 10/43] Bug 1394102 - FetchEvent Request.signal should be aborted when the corrisponding nsIChannel is canceled - test, r=bkelly --- .../serviceworker-intercepted.https.html | 30 +++++++++++++++++++ .../tests/fetch/api/resources/sw-intercept.js | 14 +++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html b/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html index 623036d46639..a6b28e827b8b 100644 --- a/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html +++ b/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html @@ -94,6 +94,36 @@ await promise_rejects(t, "AbortError", reader.read()); await promise_rejects(t, "AbortError", reader.closed); }, "Stream errors once aborted."); + + promise_test(async t => { + const scope = SCOPE + "?q=aborted-signal"; + await setupRegistration(t, scope); + const iframe = await with_iframe(scope); + const w = iframe.contentWindow; + + const testReady = new Promise(resolve => { + w.navigator.serviceWorker.addEventListener('message', event => { + resolve(event.data); + }, { once: true }); + }); + + const controller = new w.AbortController(); + const signal = controller.signal; + + w.fetch('data.json?signal', { signal }); + + assert_true((await testReady) == "ready", ""); + controller.abort(); + + const aborted = new Promise(resolve => { + w.navigator.serviceWorker.addEventListener('message', event => { + resolve(event.data); + }, { once: true }); + }); + + assert_true((await aborted) == "aborted", ""); + }, "FetchEvent.request.signal must be aborted."); + diff --git a/testing/web-platform/tests/fetch/api/resources/sw-intercept.js b/testing/web-platform/tests/fetch/api/resources/sw-intercept.js index b8166b62a5c9..2804e12a9cef 100644 --- a/testing/web-platform/tests/fetch/api/resources/sw-intercept.js +++ b/testing/web-platform/tests/fetch/api/resources/sw-intercept.js @@ -5,6 +5,16 @@ async function broadcast(msg) { } addEventListener('fetch', event => { - event.waitUntil(broadcast(event.request.url)); - event.respondWith(fetch(event.request)); + if (event.request.url.endsWith('?signal')) { + event.waitUntil(broadcast("ready")); + event.respondWith(new Promise((resolve, reject) => { + event.request.signal.onabort = () => { + broadcast("aborted"); + reject(); + }; + })); + } else { + event.waitUntil(broadcast(event.request.url)); + event.respondWith(fetch(event.request)); + } }); From 1d376fe5d72eef49ca2ffd0c292a0914b1d8b4fe Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Mon, 25 Jun 2018 16:06:08 +0000 Subject: [PATCH 11/43] Bug 1470732 - Unprotect memory before moving to the list of finished/cancelled compilations. r=tcampbell --- js/src/vm/HelperThreads.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index 9a3e3506b077..d1897cbcfc90 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -297,6 +297,11 @@ CancelOffThreadIonCompileLocked(const CompilationSelector& selector, bool discar for (size_t i = 0; i < worklist.length(); i++) { jit::IonBuilder* builder = worklist[i]; if (IonBuilderMatches(selector, builder)) { + // Once finished, builders are handled by a Linked list which is + // allocated with the IonBuilder class which is contained in the + // LifoAlloc-ated structure. Thus we need it to be mutable. + worklist[i]->alloc().lifoAlloc()->setReadWrite(); + FinishOffThreadIonCompile(builder, lock); HelperThreadState().remove(worklist, &i); } From 683ad5a8dea4e260b55c5925835285608a255d06 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Mon, 25 Jun 2018 16:10:55 +0000 Subject: [PATCH 12/43] Bug 1470732 - Reprotect memory of pending Ion compilations in case of moving GCs. r=jonco --- js/src/vm/HelperThreads.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index d1897cbcfc90..f804a0c80f71 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -2267,8 +2267,11 @@ void GlobalHelperThreadState::trace(JSTracer* trc) { AutoLockHelperThreadState lock; - for (auto builder : ionWorklist(lock)) + for (auto builder : ionWorklist(lock)) { + builder->alloc().lifoAlloc()->setReadWrite(); builder->trace(trc); + builder->alloc().lifoAlloc()->setReadOnly(); + } for (auto builder : ionFinishedList(lock)) builder->trace(trc); From 3b7262f482cd0be92129179c96792652a1989470 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Wed, 27 Jun 2018 12:51:23 +0000 Subject: [PATCH 13/43] Bug 1470732 - Add test case. r=tcampbell --- js/src/jit-test/tests/basic/bug1470732.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 js/src/jit-test/tests/basic/bug1470732.js diff --git a/js/src/jit-test/tests/basic/bug1470732.js b/js/src/jit-test/tests/basic/bug1470732.js new file mode 100644 index 000000000000..05fdbd1e87c8 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1470732.js @@ -0,0 +1,12 @@ +if (helperThreadCount() === 0) + quit(); + +var i = 0; +while(i++ < 500) { + evalInWorker(` + assertFloat32(0x23456789 | 0, false); + `); + let m = parseModule(""); + m.declarationInstantiation(); +} + From ac56b410a9dc4b464893d727791d30f9c10abdfb Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 27 Jun 2018 16:05:48 +0300 Subject: [PATCH 14/43] Bug 1470994 - remove unused accuracy field from SensorData; r=gsvelto Reviewers: gsvelto Reviewed By: gsvelto Bug #: 1470994 Differential Revision: https://phabricator.services.mozilla.com/D1817 --HG-- extra : rebase_source : 03e1aee4934bcba96968578ba92d6c2ec6275747 extra : amend_source : 6f6e69fb6e18d092090e99db765e12c16021f265 --- dom/system/nsDeviceSensors.cpp | 1 - hal/HalSensor.h | 21 ------------------- hal/cocoa/CocoaSensor.mm | 3 +-- hal/sandbox/PHal.ipdl | 2 -- hal/windows/WindowsSensor.cpp | 3 +-- .../java/org/mozilla/gecko/GeckoAppShell.java | 19 ++--------------- .../org/mozilla/gecko/GeckoHalDefines.java | 6 ------ widget/android/nsAppShell.cpp | 5 ++--- 8 files changed, 6 insertions(+), 54 deletions(-) diff --git a/dom/system/nsDeviceSensors.cpp b/dom/system/nsDeviceSensors.cpp index a77eb39ab201..6a825b5c4968 100644 --- a/dom/system/nsDeviceSensors.cpp +++ b/dom/system/nsDeviceSensors.cpp @@ -176,7 +176,6 @@ public: sensorData.values().AppendElement(0.5f); sensorData.values().AppendElement(0.5f); sensorData.values().AppendElement(0.5f); - sensorData.accuracy() = SENSOR_ACCURACY_UNRELIABLE; mTarget->Notify(sensorData); return NS_OK; } diff --git a/hal/HalSensor.h b/hal/HalSensor.h index 551c4271d539..f6032219cc22 100644 --- a/hal/HalSensor.h +++ b/hal/HalSensor.h @@ -34,18 +34,6 @@ class SensorData; typedef Observer ISensorObserver; -/** - * Enumeration of sensor accuracy types. - */ -enum SensorAccuracyType { - SENSOR_ACCURACY_UNKNOWN = -1, - SENSOR_ACCURACY_UNRELIABLE, - SENSOR_ACCURACY_LOW, - SENSOR_ACCURACY_MED, - SENSOR_ACCURACY_HIGH, - NUM_SENSOR_ACCURACY_TYPE -}; - class SensorAccuracy; typedef Observer ISensorAccuracyObserver; @@ -66,15 +54,6 @@ namespace IPC { mozilla::hal::SENSOR_UNKNOWN, mozilla::hal::NUM_SENSOR_TYPE> { }; - - template <> - struct ParamTraits: - public ContiguousEnumSerializer< - mozilla::hal::SensorAccuracyType, - mozilla::hal::SENSOR_ACCURACY_UNKNOWN, - mozilla::hal::NUM_SENSOR_ACCURACY_TYPE> { - - }; } // namespace IPC #endif /* __HAL_SENSOR_H_ */ diff --git a/hal/cocoa/CocoaSensor.mm b/hal/cocoa/CocoaSensor.mm index a9823489f97b..8992c24768bc 100644 --- a/hal/cocoa/CocoaSensor.mm +++ b/hal/cocoa/CocoaSensor.mm @@ -73,8 +73,7 @@ UpdateHandler(nsITimer *aTimer, void *aClosure) hal::SensorData sdata(sensor, PR_Now(), - values, - hal::SENSOR_ACCURACY_UNKNOWN); + values); hal::NotifySensorChange(sdata); } } diff --git a/hal/sandbox/PHal.ipdl b/hal/sandbox/PHal.ipdl index d621dbbe2c09..b97d34c25b7d 100644 --- a/hal/sandbox/PHal.ipdl +++ b/hal/sandbox/PHal.ipdl @@ -11,7 +11,6 @@ include "mozilla/GfxMessageUtils.h"; using mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h"; using mozilla::hal::SensorType from "mozilla/HalSensor.h"; -using mozilla::hal::SensorAccuracyType from "mozilla/HalSensor.h"; using mozilla::hal::WakeLockControl from "mozilla/HalTypes.h"; using mozilla::hal::ProcessPriority from "mozilla/HalTypes.h"; using nsIntRect from "nsRect.h"; @@ -30,7 +29,6 @@ struct SensorData { SensorType sensor; PRTime timestamp; float[] values; - SensorAccuracyType accuracy; }; struct NetworkInformation { diff --git a/hal/windows/WindowsSensor.cpp b/hal/windows/WindowsSensor.cpp index 7d6040c6aa82..32987428dc57 100644 --- a/hal/windows/WindowsSensor.cpp +++ b/hal/windows/WindowsSensor.cpp @@ -92,8 +92,7 @@ public: hal::SensorData sdata(hal::SENSOR_ACCELERATION, PR_Now(), - values, - hal::SENSOR_ACCURACY_UNKNOWN); + values); hal::NotifySensorChange(sdata); return S_OK; diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java index ffed6b95f621..78ff627f3fd0 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java @@ -397,7 +397,7 @@ public class GeckoAppShell @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko") /* package */ static native void onSensorChanged(int hal_type, float x, float y, float z, - float w, int accuracy, long time); + float w, long time); @WrapForJNI(calledFrom = "any", dispatchTo = "gecko") /* package */ static native void onLocationChanged(double latitude, double longitude, @@ -415,26 +415,11 @@ public class GeckoAppShell public void onAccuracyChanged(Sensor sensor, int accuracy) { } - private static int HalSensorAccuracyFor(int androidAccuracy) { - switch (androidAccuracy) { - case SensorManager.SENSOR_STATUS_UNRELIABLE: - return GeckoHalDefines.SENSOR_ACCURACY_UNRELIABLE; - case SensorManager.SENSOR_STATUS_ACCURACY_LOW: - return GeckoHalDefines.SENSOR_ACCURACY_LOW; - case SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM: - return GeckoHalDefines.SENSOR_ACCURACY_MED; - case SensorManager.SENSOR_STATUS_ACCURACY_HIGH: - return GeckoHalDefines.SENSOR_ACCURACY_HIGH; - } - return GeckoHalDefines.SENSOR_ACCURACY_UNKNOWN; - } - @Override public void onSensorChanged(SensorEvent s) { int sensor_type = s.sensor.getType(); int hal_type = 0; float x = 0.0f, y = 0.0f, z = 0.0f, w = 0.0f; - final int accuracy = HalSensorAccuracyFor(s.accuracy); // SensorEvent timestamp is in nanoseconds, Gecko expects microseconds. final long time = s.timestamp / 1000; @@ -493,7 +478,7 @@ public class GeckoAppShell break; } - GeckoAppShell.onSensorChanged(hal_type, x, y, z, w, accuracy, time); + GeckoAppShell.onSensorChanged(hal_type, x, y, z, w, time); } // Geolocation. diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoHalDefines.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoHalDefines.java index 3d9b97427981..9e22109ec473 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoHalDefines.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoHalDefines.java @@ -18,10 +18,4 @@ public class GeckoHalDefines public static final int SENSOR_LIGHT = 5; public static final int SENSOR_ROTATION_VECTOR = 6; public static final int SENSOR_GAME_ROTATION_VECTOR = 7; - - public static final int SENSOR_ACCURACY_UNKNOWN = -1; - public static final int SENSOR_ACCURACY_UNRELIABLE = 0; - public static final int SENSOR_ACCURACY_LOW = 1; - public static final int SENSOR_ACCURACY_MED = 2; - public static final int SENSOR_ACCURACY_HIGH = 3; }; diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index 466b47b50070..1ef41ac6c7e1 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -296,7 +296,7 @@ public: } static void OnSensorChanged(int32_t aType, float aX, float aY, float aZ, - float aW, int32_t aAccuracy, int64_t aTime) + float aW, int64_t aTime) { AutoTArray values; @@ -336,8 +336,7 @@ public: "Unknown sensor type %d", aType); } - hal::SensorData sdata(hal::SensorType(aType), aTime, values, - hal::SensorAccuracyType(aAccuracy)); + hal::SensorData sdata(hal::SensorType(aType), aTime, values); hal::NotifySensorChange(sdata); } From eac6fdd4b00874eb942e8d0c9cfb213bf2bcb1d6 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 27 Jun 2018 16:10:41 +0300 Subject: [PATCH 15/43] Bug 1470890 - handle deserialization failure gracefully in JS IPC; r=evilpie Summary: This primarily affects the efficiency of fuzzing. Reviewers: evilpie Reviewed By: evilpie Bug #: 1470890 Differential Revision: https://phabricator.services.mozilla.com/D1797 --HG-- extra : rebase_source : 43f92c8989262259bb9ec604d2be60b1195ee802 extra : amend_source : 5899b2672fb8602b0b1fb806cb3807677493daa7 --- js/ipc/WrapperOwner.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/ipc/WrapperOwner.cpp b/js/ipc/WrapperOwner.cpp index 6f533acf9abe..6f1d8e1fbf6d 100644 --- a/js/ipc/WrapperOwner.cpp +++ b/js/ipc/WrapperOwner.cpp @@ -1233,7 +1233,9 @@ JSObject* WrapperOwner::fromLocalObjectVariant(JSContext* cx, const LocalObject& objVar) { Maybe id(ObjectId::deserialize(objVar.serializedId())); - MOZ_RELEASE_ASSERT(id.isSome()); + if (id.isNothing()) { + return nullptr; + } Rooted obj(cx, findObjectById(cx, id.value())); if (!obj) return nullptr; From 0f2235111d79220375979537f16cfecd1285be35 Mon Sep 17 00:00:00 2001 From: Tom Ritter Date: Wed, 27 Jun 2018 16:16:57 +0300 Subject: [PATCH 16/43] Bug 1469933 When using ./mach run --debugger=windbg, use the x64 version of WinDBG r=ted Summary: Bug 1469933 When using ./mach run --debugger=windbg, use the x64 version of WinDBG r?ted Reviewers: ted Reviewed By: ted Bug #: 1469933 Differential Revision: https://phabricator.services.mozilla.com/D1730 --HG-- extra : amend_source : 67de4dae3a129df77976da82005acb47ad64b5ed --- testing/mozbase/mozdebug/mozdebug/mozdebug.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/mozbase/mozdebug/mozdebug/mozdebug.py b/testing/mozbase/mozdebug/mozdebug/mozdebug.py index 7f28b481c6b0..9de794ebb947 100755 --- a/testing/mozbase/mozdebug/mozdebug/mozdebug.py +++ b/testing/mozbase/mozdebug/mozdebug/mozdebug.py @@ -82,7 +82,7 @@ def _windbg_installation_paths(): 'Windows Kits') for version in windowsKitsVersions: yield os.path.join(windowsKitsPrefix, version, - 'Debuggers', 'x86', 'windbg.exe') + 'Debuggers', 'x64', 'windbg.exe') def get_debugger_path(debugger): From a2d5894efe206273c491c9b55f49148347735421 Mon Sep 17 00:00:00 2001 From: Matthew Gaudet Date: Tue, 26 Jun 2018 18:09:34 -0400 Subject: [PATCH 17/43] Bug 1471361: Keep stack height constant when fastpathing truncation r=tcampbell --HG-- extra : rebase_source : b2f2238e73a1c24ce3b926dd76d147feef5a0ddf --- js/src/jit/CacheIRCompiler.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp index 82ad7d543df3..77f30b5d1b0f 100644 --- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -1932,13 +1932,13 @@ CacheIRCompiler::emitTruncateDoubleToUInt32() ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId()); Register res = allocator.defineRegister(masm, reader.int32OperandId()); + Label int32, done; + masm.branchTestInt32(Assembler::Equal, val, &int32); + Label doneTruncate, truncateABICall; if (mode_ != Mode::Baseline) masm.push(FloatReg0); - Label int32, done; - masm.branchTestInt32(Assembler::Equal, val, &int32); - masm.unboxDouble(val, FloatReg0); masm.branchTruncateDoubleMaybeModUint32(FloatReg0, res, &truncateABICall); masm.jump(&doneTruncate); From ef998f1b548447849c20daa3c4e3047d29d2ed96 Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Tue, 26 Jun 2018 14:37:51 +0000 Subject: [PATCH 18/43] Bug 1471234 - Assert LifoAlloc::steal does not trash BumpChunks. r=tcampbell --- js/src/ds/LifoAlloc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h index 8e0fc874287d..f9e28c963e45 100644 --- a/js/src/ds/LifoAlloc.h +++ b/js/src/ds/LifoAlloc.h @@ -615,7 +615,8 @@ class LifoAlloc // Steal allocated chunks from |other|. void steal(LifoAlloc* other) { MOZ_ASSERT(!other->markCount); - MOZ_ASSERT(chunks_.empty()); + MOZ_DIAGNOSTIC_ASSERT(unused_.empty()); + MOZ_DIAGNOSTIC_ASSERT(chunks_.empty()); // Copy everything from |other| to |this| except for |peakSize_|, which // requires some care. From 2a4568f6f1a4561dae6a6a1799747be8151ca793 Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Fri, 22 Jun 2018 14:31:59 +0100 Subject: [PATCH 19/43] Bug 1470151 - Make dumpCoverage and resetCoverage return a Promise that is resolved when the parent process and all content processes are done with dumping or resetting coverage counters. r=froydnj --HG-- extra : rebase_source : 1c6e668346b49a409b6271b9b51b731784a57cf5 --- dom/ipc/ContentChild.cpp | 10 ++- dom/ipc/ContentChild.h | 4 +- dom/ipc/PContent.ipdl | 4 +- tools/code-coverage/CodeCoverageHandler.cpp | 18 ++++- tools/code-coverage/CodeCoverageHandler.h | 6 +- tools/code-coverage/nsCodeCoverage.cpp | 84 +++++++++++++++++---- tools/code-coverage/nsICodeCoverage.idl | 6 +- 7 files changed, 102 insertions(+), 30 deletions(-) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 61f0ac058a19..ed1c09f724d4 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -3696,10 +3696,11 @@ ContentChild::RecvShareCodeCoverageMutex(const CrossProcessMutexHandle& aHandle) } mozilla::ipc::IPCResult -ContentChild::RecvDumpCodeCoverageCounters() +ContentChild::RecvDumpCodeCoverageCounters(DumpCodeCoverageCountersResolver&& aResolver) { #ifdef MOZ_CODE_COVERAGE - CodeCoverageHandler::DumpCounters(0); + CodeCoverageHandler::DumpCounters(); + aResolver(/* unused */ true); return IPC_OK(); #else MOZ_CRASH("Shouldn't receive this message in non-code coverage builds!"); @@ -3707,10 +3708,11 @@ ContentChild::RecvDumpCodeCoverageCounters() } mozilla::ipc::IPCResult -ContentChild::RecvResetCodeCoverageCounters() +ContentChild::RecvResetCodeCoverageCounters(ResetCodeCoverageCountersResolver&& aResolver) { #ifdef MOZ_CODE_COVERAGE - CodeCoverageHandler::ResetCounters(0); + CodeCoverageHandler::ResetCounters(); + aResolver(/* unused */ true); return IPC_OK(); #else MOZ_CRASH("Shouldn't receive this message in non-code coverage builds!"); diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 5870194b0b41..9695d53c389e 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -624,10 +624,10 @@ public: RecvShareCodeCoverageMutex(const CrossProcessMutexHandle& aHandle) override; virtual mozilla::ipc::IPCResult - RecvDumpCodeCoverageCounters() override; + RecvDumpCodeCoverageCounters(DumpCodeCoverageCountersResolver&& aResolver) override; virtual mozilla::ipc::IPCResult - RecvResetCodeCoverageCounters() override; + RecvResetCodeCoverageCounters(ResetCodeCoverageCountersResolver&& aResolver) override; virtual mozilla::ipc::IPCResult RecvSetInputEventQueueEnabled() override; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 7e93b3fc4ae3..2fd9d327535c 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -633,8 +633,8 @@ child: async SetPluginList(uint32_t pluginEpoch, PluginTag[] plugins, FakePluginTag[] fakePlugins); async ShareCodeCoverageMutex(CrossProcessMutexHandle handle); - async DumpCodeCoverageCounters(); - async ResetCodeCoverageCounters(); + async DumpCodeCoverageCounters() returns (bool unused); + async ResetCodeCoverageCounters() returns (bool unused); /* * IPC message to enable the input event queue on the main thread of the diff --git a/tools/code-coverage/CodeCoverageHandler.cpp b/tools/code-coverage/CodeCoverageHandler.cpp index 76dd399759a4..be687a590417 100644 --- a/tools/code-coverage/CodeCoverageHandler.cpp +++ b/tools/code-coverage/CodeCoverageHandler.cpp @@ -44,7 +44,7 @@ void counters_reset() { StaticAutoPtr CodeCoverageHandler::instance; -void CodeCoverageHandler::DumpCounters(int) +void CodeCoverageHandler::DumpCounters() { CrossProcessMutexAutoLock lock(*CodeCoverageHandler::Get()->GetMutex()); @@ -53,7 +53,12 @@ void CodeCoverageHandler::DumpCounters(int) printf_stderr("[CodeCoverage] Dump completed.\n"); } -void CodeCoverageHandler::ResetCounters(int) +void CodeCoverageHandler::DumpCountersSignalHandler(int) +{ + DumpCounters(); +} + +void CodeCoverageHandler::ResetCounters() { CrossProcessMutexAutoLock lock(*CodeCoverageHandler::Get()->GetMutex()); @@ -62,20 +67,25 @@ void CodeCoverageHandler::ResetCounters(int) printf_stderr("[CodeCoverage] Reset completed.\n"); } +void CodeCoverageHandler::ResetCountersSignalHandler(int) +{ + ResetCounters(); +} + void CodeCoverageHandler::SetSignalHandlers() { #ifndef XP_WIN printf_stderr("[CodeCoverage] Setting handlers for process %d.\n", getpid()); struct sigaction dump_sa; - dump_sa.sa_handler = CodeCoverageHandler::DumpCounters; + dump_sa.sa_handler = CodeCoverageHandler::DumpCountersSignalHandler; dump_sa.sa_flags = SA_RESTART; sigemptyset(&dump_sa.sa_mask); DebugOnly r1 = sigaction(SIGUSR1, &dump_sa, nullptr); MOZ_ASSERT(r1 == 0, "Failed to install GCOV SIGUSR1 handler"); struct sigaction reset_sa; - reset_sa.sa_handler = CodeCoverageHandler::ResetCounters; + reset_sa.sa_handler = CodeCoverageHandler::ResetCountersSignalHandler; reset_sa.sa_flags = SA_RESTART; sigemptyset(&reset_sa.sa_mask); DebugOnly r2 = sigaction(SIGUSR2, &reset_sa, nullptr); diff --git a/tools/code-coverage/CodeCoverageHandler.h b/tools/code-coverage/CodeCoverageHandler.h index 42e4725e46b1..0a29fabe15f1 100644 --- a/tools/code-coverage/CodeCoverageHandler.h +++ b/tools/code-coverage/CodeCoverageHandler.h @@ -18,8 +18,10 @@ public: static CodeCoverageHandler* Get(); CrossProcessMutex* GetMutex(); CrossProcessMutexHandle GetMutexHandle(int aProcId); - static void DumpCounters(int); - static void ResetCounters(int); + static void DumpCounters(); + static void DumpCountersSignalHandler(int); + static void ResetCounters(); + static void ResetCountersSignalHandler(int); private: CodeCoverageHandler(); diff --git a/tools/code-coverage/nsCodeCoverage.cpp b/tools/code-coverage/nsCodeCoverage.cpp index 904778508c8d..d6e1b7aeb529 100644 --- a/tools/code-coverage/nsCodeCoverage.cpp +++ b/tools/code-coverage/nsCodeCoverage.cpp @@ -7,9 +7,11 @@ #include "mozilla/CodeCoverageHandler.h" #include "mozilla/Unused.h" #include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/Promise.h" using namespace mozilla; using namespace mozilla::dom; +using namespace mozilla::ipc; NS_IMPL_ISUPPORTS(nsCodeCoverage, nsICodeCoverage) @@ -22,28 +24,82 @@ nsCodeCoverage::~nsCodeCoverage() { } -NS_IMETHODIMP nsCodeCoverage::DumpCounters() +enum RequestType { Dump, Reset }; + +class ProcessCount final { + NS_INLINE_DECL_REFCOUNTING(ProcessCount); + +public: + ProcessCount(uint32_t c) : mCount(c) {} + operator uint32_t() const { return mCount; } + ProcessCount& operator--() { mCount--; return *this; } + +private: + ~ProcessCount() {} + uint32_t mCount; +}; + +nsresult Request(JSContext* cx, Promise** aPromise, RequestType requestType) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); - CodeCoverageHandler::DumpCounters(0); - for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { - Unused << cp->SendDumpCodeCoverageCounters(); + nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx)); + if (NS_WARN_IF(!global)) { + return NS_ERROR_FAILURE; } + ErrorResult result; + RefPtr promise = Promise::Create(global, result); + if (NS_WARN_IF(result.Failed())) { + return result.StealNSResult(); + } + + uint32_t processCount = 0; + for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { + ++processCount; + } + + if (requestType == RequestType::Dump) { + CodeCoverageHandler::DumpCounters(); + } else if (requestType == RequestType::Reset) { + CodeCoverageHandler::ResetCounters(); + } + + if (processCount == 0) { + promise->MaybeResolveWithUndefined(); + } else { + RefPtr processCountHolder(new ProcessCount(processCount)); + + auto resolve = [processCountHolder, promise](bool unused) { + if (--(*processCountHolder) == 0) { + promise->MaybeResolveWithUndefined(); + } + }; + + auto reject = [promise](ResponseRejectReason aReason) { + promise->MaybeReject(NS_ERROR_FAILURE); + }; + + for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { + if (requestType == RequestType::Dump) { + cp->SendDumpCodeCoverageCounters(resolve, reject); + } else if (requestType == RequestType::Reset) { + cp->SendResetCodeCoverageCounters(resolve, reject); + } + } + } + + promise.forget(aPromise); return NS_OK; } -NS_IMETHODIMP nsCodeCoverage::ResetCounters() +NS_IMETHODIMP nsCodeCoverage::DumpCounters(JSContext *cx, Promise** aPromise) { - MOZ_ASSERT(XRE_IsParentProcess()); - MOZ_ASSERT(NS_IsMainThread()); - - CodeCoverageHandler::ResetCounters(0); - for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { - Unused << cp->SendResetCodeCoverageCounters(); - } - - return NS_OK; + return Request(cx, aPromise, RequestType::Dump); +} + +NS_IMETHODIMP nsCodeCoverage::ResetCounters(JSContext *cx, Promise** aPromise) +{ + return Request(cx, aPromise, RequestType::Reset); } diff --git a/tools/code-coverage/nsICodeCoverage.idl b/tools/code-coverage/nsICodeCoverage.idl index 4b4216572d16..e1f1a21316c6 100644 --- a/tools/code-coverage/nsICodeCoverage.idl +++ b/tools/code-coverage/nsICodeCoverage.idl @@ -19,10 +19,12 @@ interface nsICodeCoverage : nsISupports /** * Write the coverage counters to disk. */ - void dumpCounters(); + [implicit_jscontext] + Promise dumpCounters(); /** * Reset the coverage counters to 0 (as if nothing was executed). */ - void resetCounters(); + [implicit_jscontext] + Promise resetCounters(); }; From a5007cf99472e919d721b91b9ed5b1a2b9bf4c8c Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Thu, 21 Jun 2018 14:40:20 +0100 Subject: [PATCH 20/43] Bug 1470151 - Add a PerTestCoverageUtils JavaScript module to manage resetting/dumping coverage counters for tests. r=jmaher --HG-- extra : rebase_source : cab07616038e535e8bc5ba2cf268a4f292b384aa --- tools/code-coverage/PerTestCoverageUtils.jsm | 64 ++++++++++++++++++++ tools/code-coverage/moz.build | 2 + 2 files changed, 66 insertions(+) create mode 100644 tools/code-coverage/PerTestCoverageUtils.jsm diff --git a/tools/code-coverage/PerTestCoverageUtils.jsm b/tools/code-coverage/PerTestCoverageUtils.jsm new file mode 100644 index 000000000000..c804aaa89b00 --- /dev/null +++ b/tools/code-coverage/PerTestCoverageUtils.jsm @@ -0,0 +1,64 @@ +/* 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/. */ + +/* exported PerTestCoverageUtils */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["PerTestCoverageUtils"]; + +ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); + +class PerTestCoverageUtilsClass { + constructor(tmp_gcov_dir, gcov_dir) { + this.tmp_gcov_dir = tmp_gcov_dir; + this.gcov_dir = gcov_dir; + + this.codeCoverageService = Cc["@mozilla.org/tools/code-coverage;1"].getService(Ci.nsICodeCoverage); + } + + _awaitPromise(promise) { + let ret; + let complete = false; + let error = null; + promise.catch(e => error = e).then(v => { + ret = v; + complete = true; + }); + Services.tm.spinEventLoopUntil(() => complete); + if (error) { + throw new Error(error); + } + return ret; + } + + // Resets the counters to 0. + beforeTest() { + this._awaitPromise(this.codeCoverageService.resetCounters()); + } + + // Dumps counters and moves the gcda files in the directory expected by codecoverage.py. + afterTest() { + this._awaitPromise(this.codeCoverageService.dumpCounters()); + + let srcDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + srcDir.initWithPath(this.tmp_gcov_dir); + + let destDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + destDir.initWithPath(this.gcov_dir); + + let srcDirEntries = srcDir.directoryEntries; + while (srcDirEntries.hasMoreElements()) { + srcDirEntries.nextFile.moveTo(destDir, null); + } + } +} + +const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); +// This is the directory where gcov is emitting the gcda files. +const tmp_gcov_dir = env.get("GCOV_PREFIX"); +// This is the directory where codecoverage.py is expecting to see the gcda files. +const gcov_dir = env.get("GCOV_RESULTS_DIR"); + +const PerTestCoverageUtils = gcov_dir ? new PerTestCoverageUtilsClass(tmp_gcov_dir, gcov_dir) : null; diff --git a/tools/code-coverage/moz.build b/tools/code-coverage/moz.build index c3bad16b9fa6..d66699a740a1 100644 --- a/tools/code-coverage/moz.build +++ b/tools/code-coverage/moz.build @@ -31,4 +31,6 @@ if CONFIG['MOZ_CODE_COVERAGE']: XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini'] MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini'] + TESTING_JS_MODULES += ['PerTestCoverageUtils.jsm'] + FINAL_LIBRARY = 'xul' From 09c7d31a96cd1ae00b490d3e39d2147b1c0903df Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Thu, 21 Jun 2018 14:41:32 +0100 Subject: [PATCH 21/43] Bug 1470151 - Support resetting/dumping coverage counters in per-test mode for xpcshell on Linux. r=jmaher --HG-- extra : rebase_source : 92336abb5c9e97dd42d322b4e683c9f617ea4cc2 --- testing/mozharness/scripts/desktop_unittest.py | 5 +++++ testing/xpcshell/head.js | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/testing/mozharness/scripts/desktop_unittest.py b/testing/mozharness/scripts/desktop_unittest.py index d923158042b4..ea8ff30d625b 100755 --- a/testing/mozharness/scripts/desktop_unittest.py +++ b/testing/mozharness/scripts/desktop_unittest.py @@ -15,6 +15,7 @@ import re import sys import copy import shutil +import tempfile import glob import imp @@ -890,6 +891,10 @@ class DesktopUnittest(TestingMixin, MercurialScript, MozbaseMixin, if self.per_test_coverage: gcov_dir, jsvm_dir = self.set_coverage_env(env) + # Per-test reset/dump is only supported for xpcshell and + # Linux for the time being. + if not is_baseline_test and suite == 'xpcshell' and self._is_linux(): + env['GCOV_RESULTS_DIR'] = gcov_dir = tempfile.mkdtemp() return_code = self.run_command(final_cmd, cwd=dirs['abs_work_dir'], output_timeout=cmd_timeout, diff --git a/testing/xpcshell/head.js b/testing/xpcshell/head.js index 952649302db3..97a8f3a01789 100644 --- a/testing/xpcshell/head.js +++ b/testing/xpcshell/head.js @@ -511,6 +511,20 @@ function _execute_test() { this[func] = Assert[func].bind(Assert); } + try { + ChromeUtils.import("resource://testing-common/PerTestCoverageUtils.jsm"); + } catch (e) { + // If the module doesn't exist, code coverage is disabled. + // Otherwise, rethrow the exception. + if (e.result != Cr.NS_ERROR_FILE_NOT_FOUND) { + throw e; + } + } + + if (PerTestCoverageUtils) { + PerTestCoverageUtils.beforeTest(); + } + try { do_test_pending("MAIN run_test"); // Check if run_test() is defined. If defined, run it. @@ -529,6 +543,10 @@ function _execute_test() { if (coverageCollector != null) { coverageCollector.recordTestCoverage(_TEST_FILE[0]); } + + if (PerTestCoverageUtils) { + PerTestCoverageUtils.afterTest(); + } } catch (e) { _passed = false; // do_check failures are already logged and set _quit to true and throw From befc08424bd818c8962de8fc545a6f5691e2950e Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Wed, 27 Jun 2018 12:29:17 +0000 Subject: [PATCH 22/43] Bug 1471589 - Ensure that ranges do not overlap in diagnostic builds. r=tcampbell --- js/src/ds/MemoryProtectionExceptionHandler.cpp | 4 ++++ js/src/ds/SplayTree.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/js/src/ds/MemoryProtectionExceptionHandler.cpp b/js/src/ds/MemoryProtectionExceptionHandler.cpp index e6e72fa648ef..96fed3df68d1 100644 --- a/js/src/ds/MemoryProtectionExceptionHandler.cpp +++ b/js/src/ds/MemoryProtectionExceptionHandler.cpp @@ -49,6 +49,10 @@ class ProtectedRegionTree Region(uintptr_t addr, size_t size) : first(addr), last(addr + (size - 1)) {} + // This function compares 2 memory regions. If they overlap they are + // considered as identical. This is used for querying if an address is + // included in a range, or if an address is already registered as a + // protected region. static int compare(const Region& A, const Region& B) { if (A.last < B.first) return -1; diff --git a/js/src/ds/SplayTree.h b/js/src/ds/SplayTree.h index 55dab4f1f1b4..d2503ef57568 100644 --- a/js/src/ds/SplayTree.h +++ b/js/src/ds/SplayTree.h @@ -104,7 +104,7 @@ class SplayTree int cmp = C::compare(v, last->item); // Don't tolerate duplicate elements. - MOZ_ASSERT(cmp); + MOZ_DIAGNOSTIC_ASSERT(cmp); Node*& parentPointer = (cmp < 0) ? last->left : last->right; MOZ_ASSERT(!parentPointer); From 8f2baafcb6fb6dbe55d1f36dc02c2efdeefd3ea4 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 27 Jun 2018 09:46:33 -0400 Subject: [PATCH 23/43] Bug 1471434 - micro-optimize refcounting for directory enumerators; r=erahm We were not being as efficient as we could be with passing ownership. --- xpcom/io/nsLocalFileUnix.cpp | 2 +- xpcom/io/nsLocalFileWin.cpp | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/xpcom/io/nsLocalFileUnix.cpp b/xpcom/io/nsLocalFileUnix.cpp index 021ad6749f1d..f0d9f6d72bbd 100644 --- a/xpcom/io/nsLocalFileUnix.cpp +++ b/xpcom/io/nsLocalFileUnix.cpp @@ -168,7 +168,7 @@ nsDirEnumeratorUnix::GetNext(nsISupports** aResult) if (NS_FAILED(rv)) { return rv; } - NS_IF_ADDREF(*aResult = file); + file.forget(aResult); return NS_OK; } diff --git a/xpcom/io/nsLocalFileWin.cpp b/xpcom/io/nsLocalFileWin.cpp index cfc63b15fc59..c667a92c08b8 100644 --- a/xpcom/io/nsLocalFileWin.cpp +++ b/xpcom/io/nsLocalFileWin.cpp @@ -750,7 +750,7 @@ public: return rv; } - mNext = do_QueryInterface(file); + mNext = file.forget(); } *aResult = mNext != nullptr; if (!*aResult) { @@ -768,10 +768,7 @@ public: return rv; } - *aResult = mNext; // might return nullptr - NS_IF_ADDREF(*aResult); - - mNext = nullptr; + mNext.forget(aResult); return NS_OK; } @@ -783,9 +780,7 @@ public: if (NS_FAILED(rv) || !hasMore) { return rv; } - *aResult = mNext; - NS_IF_ADDREF(*aResult); - mNext = nullptr; + mNext.forget(aResult); return NS_OK; } From 818897b5a1404adfd0ea8afcf4eedb691542763d Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Wed, 27 Jun 2018 06:51:24 -0700 Subject: [PATCH 24/43] Bug 1471303 Fix CopyableErrorResult::operator==() to actually compile with ipdl types. r=bz --- dom/bindings/BindingUtils.cpp | 30 ++++++++++++++++++++++++++++-- dom/bindings/ErrorResult.h | 26 -------------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 51b2a53c6ed1..4d2479aaeeb3 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -184,7 +184,7 @@ struct TErrorResult::Message { return GetErrorArgCount(mErrorNumber) == mArgs.Length(); } - bool operator==(const TErrorResult::Message& aRight) + bool operator==(const TErrorResult::Message& aRight) const { return mErrorNumber == aRight.mErrorNumber && mArgs == aRight.mArgs; @@ -341,7 +341,7 @@ struct TErrorResult::DOMExceptionInfo { nsCString mMessage; nsresult mRv; - bool operator==(const TErrorResult::DOMExceptionInfo& aRight) + bool operator==(const TErrorResult::DOMExceptionInfo& aRight) const { return mRv == aRight.mRv && mMessage == aRight.mMessage; @@ -509,6 +509,32 @@ TErrorResult::operator=(TErrorResult&& aRHS) return *this; } +template +bool +TErrorResult::operator==(const ErrorResult& aRight) const +{ + auto right = reinterpret_cast*>(&aRight); + + if (mResult != right->mResult) { + return false; + } + + if (IsJSException()) { + // js exceptions are always non-equal + return false; + } + + if (IsErrorWithMessage()) { + return *mExtra.mMessage == *right->mExtra.mMessage; + } + + if (IsDOMException()) { + return *mExtra.mDOMExceptionInfo == *right->mExtra.mDOMExceptionInfo; + } + + return true; +} + template void TErrorResult::CloneTo(TErrorResult& aRv) const diff --git a/dom/bindings/ErrorResult.h b/dom/bindings/ErrorResult.h index b258a8371c82..22a82a92874c 100644 --- a/dom/bindings/ErrorResult.h +++ b/dom/bindings/ErrorResult.h @@ -646,32 +646,6 @@ binding_danger::TErrorResult::operator const ErrorResult&() const reinterpret_cast*>(this)); } -template -bool -binding_danger::TErrorResult::operator==(const ErrorResult& aRight) const -{ - auto right = reinterpret_cast*>(&aRight); - - if (mResult != right->mResult) { - return false; - } - - if (IsJSException()) { - // js exceptions are always non-equal - return false; - } - - if (IsErrorWithMessage()) { - return *mExtra.mMessage == *right->mExtra.mMessage; - } - - if (IsDOMException()) { - return *mExtra.mDOMExceptionInfo == *right->mExtra.mDOMExceptionInfo; - } - - return true; -} - // A class for use when an ErrorResult should just automatically be ignored. // This doesn't inherit from ErrorResult so we don't make two separate calls to // SuppressException. From e923cd7f53bd5de28ed50da276e16469345884f2 Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Wed, 27 Jun 2018 09:52:19 -0400 Subject: [PATCH 25/43] Bug 1468198 - remove tests that only run on b2g. r=smaug,mcmanus,whimboo --- dom/events/test/marionette/head.js | 139 ----- dom/events/test/marionette/manifest.ini | 4 - .../marionette/test_sensor_orientation.js | 53 -- dom/network/tests/marionette/head.js | 551 ------------------ dom/network/tests/marionette/manifest.ini | 13 - .../marionette/test_ethernet_add_interface.js | 16 - .../test_ethernet_connect_with_dhcp.js | 26 - .../test_ethernet_connect_with_static_ip.js | 33 -- .../tests/marionette/test_ethernet_disable.js | 17 - .../marionette/test_ethernet_disconnect.js | 25 - .../tests/marionette/test_ethernet_enable.js | 17 - .../test_ethernet_ip_mode_change.js | 43 -- .../test_ethernet_reconnect_with_dhcp.js | 29 - .../test_ethernet_reconnect_with_static_ip.js | 36 -- .../test_ethernet_remove_interface.js | 16 - .../mozbuild/mozbuild/action/test_archive.py | 1 - python/mozbuild/mozbuild/frontend/context.py | 4 - .../marionette_harness/tests/webapi-tests.ini | 3 - testing/marionette/moz.build | 1 - 19 files changed, 1027 deletions(-) delete mode 100644 dom/events/test/marionette/head.js delete mode 100644 dom/events/test/marionette/manifest.ini delete mode 100644 dom/events/test/marionette/test_sensor_orientation.js delete mode 100644 dom/network/tests/marionette/head.js delete mode 100644 dom/network/tests/marionette/manifest.ini delete mode 100644 dom/network/tests/marionette/test_ethernet_add_interface.js delete mode 100644 dom/network/tests/marionette/test_ethernet_connect_with_dhcp.js delete mode 100644 dom/network/tests/marionette/test_ethernet_connect_with_static_ip.js delete mode 100644 dom/network/tests/marionette/test_ethernet_disable.js delete mode 100644 dom/network/tests/marionette/test_ethernet_disconnect.js delete mode 100644 dom/network/tests/marionette/test_ethernet_enable.js delete mode 100644 dom/network/tests/marionette/test_ethernet_ip_mode_change.js delete mode 100644 dom/network/tests/marionette/test_ethernet_reconnect_with_dhcp.js delete mode 100644 dom/network/tests/marionette/test_ethernet_reconnect_with_static_ip.js delete mode 100644 dom/network/tests/marionette/test_ethernet_remove_interface.js delete mode 100644 testing/marionette/harness/marionette_harness/tests/webapi-tests.ini diff --git a/dom/events/test/marionette/head.js b/dom/events/test/marionette/head.js deleted file mode 100644 index 770f3d14888e..000000000000 --- a/dom/events/test/marionette/head.js +++ /dev/null @@ -1,139 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers; - - -var _pendingEmulatorCmdCount = 0; - -/** - * Send emulator command with safe guard. - * - * We should only call |finish()| after all emulator command transactions - * end, so here comes with the pending counter. Resolve when the emulator - * gives positive response, and reject otherwise. - * - * Fulfill params: - * result -- an array of emulator response lines. - * Reject params: - * result -- an array of emulator response lines. - * - * @return A deferred promise. - */ -function runEmulatorCmdSafe(aCommand) { - return new Promise((resolve, reject) => { - - ++_pendingEmulatorCmdCount; - runEmulatorCmd(aCommand, function(aResult) { - --_pendingEmulatorCmdCount; - - ok(true, "Emulator response: " + JSON.stringify(aResult)); - if (Array.isArray(aResult) && - aResult[aResult.length - 1] === "OK") { - resolve(aResult); - } else { - reject(aResult); - } - }); - - }); -} - -/** - * Get emulator sensor values of a named sensor. - * - * Fulfill params: - * result -- an array of emulator sensor values. - * Reject params: (none) - * - * @param aSensorName - * A string name of the sensor. Availables are: "acceleration" - * "magnetic-field", "orientation", "temperature", "proximity". - * - * @return A deferred promise. - */ -function getEmulatorSensorValues(aSensorName) { - return runEmulatorCmdSafe("sensor get " + aSensorName) - .then(function(aResult) { - // aResult = ["orientation = 0:0:0", "OK"] - return aResult[0].split(" ")[2].split(":").map(function(aElement) { - return parseInt(aElement, 10); - }); - }); -} - -/** - * Convenient alias function for getting orientation sensor values. - */ -function getEmulatorOrientationValues() { - return getEmulatorSensorValues("orientation"); -} - -/** - * Set emulator orientation sensor values. - * - * Fulfill params: (none) - * Reject params: (none) - * - * @param aAzimuth - * @param aPitch - * @param aRoll - * - * @return A deferred promise. - */ -function setEmulatorOrientationValues(aAzimuth, aPitch, aRoll) { - let cmd = "sensor set orientation " + aAzimuth + ":" + aPitch + ":" + aRoll; - return runEmulatorCmdSafe(cmd); -} - -/** - * Wait for a named window event. - * - * Resolve if that named event occurs. Never reject. - * - * Forfill params: the DOMEvent passed. - * - * @param aEventName - * A string event name. - * - * @return A deferred promise. - */ -function waitForWindowEvent(aEventName) { - return new Promise(resolve => { - - window.addEventListener(aEventName, function(aEvent) { - ok(true, "Window event '" + aEventName + "' got."); - resolve(aEvent); - }, {once: true}); - - }); -} - -/** - * Wait for pending emulator transactions and call |finish()|. - */ -function cleanUp() { - // Use ok here so that we have at least one test run. - ok(true, ":: CLEANING UP ::"); - - waitFor(finish, function() { - return _pendingEmulatorCmdCount === 0; - }); -} - -/** - * Basic test routine helper. - * - * This helper does nothing but clean-ups. - * - * @param aTestCaseMain - * A function that takes no parameter. - */ -function startTestBase(aTestCaseMain) { - Promise.resolve() - .then(aTestCaseMain) - .then(cleanUp, function() { - ok(false, 'promise rejects during test.'); - cleanUp(); - }); -} diff --git a/dom/events/test/marionette/manifest.ini b/dom/events/test/marionette/manifest.ini deleted file mode 100644 index b0ec98d99dea..000000000000 --- a/dom/events/test/marionette/manifest.ini +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -run-if = buildapp == 'b2g' - -[test_sensor_orientation.js] diff --git a/dom/events/test/marionette/test_sensor_orientation.js b/dom/events/test/marionette/test_sensor_orientation.js deleted file mode 100644 index 70df0ccd3150..000000000000 --- a/dom/events/test/marionette/test_sensor_orientation.js +++ /dev/null @@ -1,53 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 120000; -MARIONETTE_HEAD_JS = 'head.js'; - -function doTest(aAzimuth, aPitch, aRoll) { - log("Testing [azimuth, pitch, roll] = " + Array.slice(arguments)); - - return setEmulatorOrientationValues(aAzimuth, aPitch, aRoll) - .then(() => waitForWindowEvent("deviceorientation")) - .then(function(aEvent) { - is(aEvent.alpha, aAzimuth, "azimuth"); - is(aEvent.beta, aPitch, "pitch"); - is(aEvent.gamma, aRoll, "roll"); - }); -} - -function testAllPermutations() { - const angles = [-180, -90, 0, 90, 180]; - let promise = Promise.resolve(); - for (let i = 0; i < angles.length; i++) { - for (let j = 0; j < angles.length; j++) { - for (let k = 0; k < angles.length; k++) { - promise = - promise.then(doTest.bind(null, angles[i], angles[j], angles[k])); - } - } - } - return promise; -} - -startTestBase(function() { - let origValues; - - return Promise.resolve() - - // Retrieve original status. - .then(() => getEmulatorOrientationValues()) - .then(function(aValues) { - origValues = aValues; - is(typeof origValues, "object", "typeof origValues"); - is(origValues.length, 3, "origValues.length"); - }) - - // Test original status - .then(() => doTest.apply(null, origValues)) - - .then(testAllPermutations) - - // Restore original status. - .then(() => setEmulatorOrientationValues.apply(null, origValues)); -}); diff --git a/dom/network/tests/marionette/head.js b/dom/network/tests/marionette/head.js deleted file mode 100644 index d0e976b3b7f9..000000000000 --- a/dom/network/tests/marionette/head.js +++ /dev/null @@ -1,551 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - - -const ETHERNET_MANAGER_CONTRACT_ID = "@mozilla.org/ethernetManager;1"; - -const INTERFACE_UP = "UP"; -const INTERFACE_DOWN = "DOWN"; - -let gTestSuite = (function() { - let suite = {}; - - // Private member variables of the returned object |suite|. - let ethernetManager = SpecialPowers.Cc[ETHERNET_MANAGER_CONTRACT_ID] - .getService(SpecialPowers.Ci.nsIEthernetManager); - let pendingEmulatorShellCount = 0; - - /** - * Send emulator shell command with safe guard. - * - * We should only call |finish()| after all emulator command transactions - * end, so here comes with the pending counter. Resolve when the emulator - * gives positive response, and reject otherwise. - * - * Fulfill params: an array of emulator response lines. - * Reject params: an array of emulator response lines. - * - * @param command - * A string command to be passed to emulator through its telnet console. - * - * @return A deferred promise. - */ - function runEmulatorShellSafe(command) { - return new Promise((resolve, reject) => { - - ++pendingEmulatorShellCount; - runEmulatorShell(command, function(aResult) { - --pendingEmulatorShellCount; - - ok(true, "Emulator shell response: " + JSON.stringify(aResult)); - if (Array.isArray(aResult)) { - resolve(aResult); - } else { - reject(aResult); - } - }); - - }); - } - - /** - * Get the system network conifg by the given interface name. - * - * Use shell command 'netcfg' to get the list of network cofig. - * - * Fulfill params: An object of { name, flag, ip } - * - * @parm ifname - * Interface name. - * - * @return A deferred promise. - */ - function getNetworkConfig(ifname) { - return runEmulatorShellSafe(['netcfg']) - .then(result => { - // Sample 'netcfg' output: - // - // lo UP 127.0.0.1/8 0x00000049 00:00:00:00:00:00 - // eth0 UP 10.0.2.15/24 0x00001043 52:54:00:12:34:56 - // eth1 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:57 - // rmnet1 DOWN 0.0.0.0/0 0x00001002 52:54:00:12:34:59 - - let config; - - for (let i = 0; i < result.length; i++) { - let tokens = result[i].split(/\s+/); - let name = tokens[0]; - let flag = tokens[1]; - let ip = tokens[2].split(/\/+/)[0]; - if (name == ifname) { - config = { name: name, flag: flag, ip: ip }; - break; - } - } - - return config; - }); - } - - /** - * Get the ip assigned by dhcp server of a given interface name. - * - * Get the ip from android property 'dhcp.[ifname].ipaddress'. - * - * Fulfill params: A string of ip address. - * - * @parm ifname - * Interface name. - * - * @return A deferred promise. - */ - function getDhcpIpAddr(ifname) { - return runEmulatorShellSafe(['getprop', 'dhcp.' + ifname + '.ipaddress']) - .then(function(ipAddr) { - return ipAddr[0]; - }); - } - - /** - * Get the gateway assigned by dhcp server of a given interface name. - * - * Get the ip from android property 'dhcp.[ifname].gateway'. - * - * Fulfill params: A string of gateway. - * - * @parm ifname - * Interface name. - * - * @return A deferred promise. - */ - function getDhcpGateway(ifname) { - return runEmulatorShellSafe(['getprop', 'dhcp.' + ifname + '.gateway']) - .then(function(gateway) { - return gateway[0]; - }); - } - - /** - * Get the default route. - * - * Use shell command 'ip route' to get the default of device. - * - * Fulfill params: An array of { name, gateway } - * - * @return A deferred promise. - */ - function getDefaultRoute() { - return runEmulatorShellSafe(['ip', 'route']) - .then(result => { - // Sample 'ip route' output: - // - // 10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 - // default via 10.0.2.2 dev eth0 metric 2 - - let routeInfo = []; - - for (let i = 0; i < result.length; i++) { - if (!result[i].match('default')) { - continue; - } - - let tokens = result[i].split(/\s+/); - let name = tokens[4]; - let gateway = tokens[2]; - routeInfo.push({ name: name, gateway: gateway }); - } - - return routeInfo; - }); - } - - /** - * Check a specific interface is enabled or not. - * - * @parm ifname - * Interface name. - * @parm enabled - * A boolean value used to check interface is disable or not. - * - * @return A deferred promise. - */ - function checkInterfaceIsEnabled(ifname, enabled) { - return getNetworkConfig(ifname) - .then(function(config) { - if (enabled) { - is(config.flag, INTERFACE_UP, "Interface is enabled as expected."); - } else { - is(config.flag, INTERFACE_DOWN, "Interface is disabled as expected."); - } - }); - } - - /** - * Check the ip of a specific interface is equal to given ip or not. - * - * @parm ifname - * Interface name. - * @parm ip - * Given ip address. - * - * @return A deferred promise. - */ - function checkInterfaceIpAddr(ifname, ip) { - return getNetworkConfig(ifname) - .then(function(config) { - is(config.ip, ip, "IP is right as expected."); - }); - } - - /** - * Check the default gateway of a specific interface is equal to given gateway - * or not. - * - * @parm ifname - * Interface name. - * @parm gateway - * Given gateway. - * - * @return A deferred promise. - */ - function checkDefaultRoute(ifname, gateway) { - return getDefaultRoute() - .then(function(routeInfo) { - for (let i = 0; i < routeInfo.length; i++) { - if (routeInfo[i].name == ifname) { - is(routeInfo[i].gateway, gateway, - "Default gateway is right as expected."); - return true; - } - } - - if (!gateway) { - ok(true, "Default route is cleared."); - return true; - } - - // TODO: should we ok(false, ......) here? - return false; - }); - } - - /** - * Check the length of interface list in EthernetManager is equal to given - * length or not. - * - * @parm length - * Given length. - */ - function checkInterfaceListLength(length) { - let list = ethernetManager.interfaceList; - is(length, list.length, "List length is equal as expected."); - } - - /** - * Check the given interface exists on device or not. - * - * @parm ifname - * Interface name. - * - * @return A deferred promise. - */ - function checkInterfaceExist(ifname) { - return scanInterfaces() - .then(list => { - let index = list.indexOf(ifname); - if (index < 0) { - throw "Interface " + ifname + " not found."; - } - - ok(true, ifname + " exists."); - }); - } - - /** - * Scan for available ethernet interfaces. - * - * Fulfill params: A list of available interfaces found in device. - * - * @return A deferred promise. - */ - function scanInterfaces() { - return new Promise(resolve => { - - ethernetManager.scan(function onScan(list) { - resolve(list); - }); - - }); - } - - /** - * Add an interface into interface list. - * - * Fulfill params: A boolean value indicates success or not. - * - * @param ifname - * Interface name. - * - * @return A deferred promise. - */ - function addInterface(ifname) { - return new Promise(resolve => { - - ethernetManager.addInterface(ifname, function onAdd(success, message) { - ok(success, "Add interface " + ifname + " succeeded."); - is(message, "ok", "Message is as expected."); - - resolve(success); - }); - - }); - } - - /** - * Remove an interface form the interface list. - * - * Fulfill params: A boolean value indicates success or not. - * - * @param ifname - * Interface name. - * - * @return A deferred promise. - */ - function removeInterface(ifname) { - return new Promise(resolve => { - - ethernetManager.removeInterface(ifname, function onRemove(success, message) { - ok(success, "Remove interface " + ifname + " succeeded."); - is(message, "ok", "Message is as expected."); - - resolve(success); - }); - - }); - } - - /** - * Enable networking of an interface in the interface list. - * - * Fulfill params: A boolean value indicates success or not. - * - * @param ifname - * Interface name. - * - * @return A deferred promise. - */ - function enableInterface(ifname) { - return new Promise(resolve => { - - ethernetManager.enable(ifname, function onEnable(success, message) { - ok(success, "Enable interface " + ifname + " succeeded."); - is(message, "ok", "Message is as expected."); - - resolve(success); - }); - - }); - } - - /** - * Disable networking of an interface in the interface list. - * - * Fulfill params: A boolean value indicates success or not. - * - * @param ifname - * Interface name. - * - * @return A deferred promise. - */ - function disableInterface(ifname) { - return new Promise(resolve => { - - ethernetManager.disable(ifname, function onDisable(success, message) { - ok(success, "Disable interface " + ifname + " succeeded."); - is(message, "ok", "Message is as expected."); - - resolve(success); - }); - - }); - } - - /** - * Make an interface connect to network. - * - * Fulfill params: A boolean value indicates success or not. - * - * @param ifname - * Interface name. - * - * @return A deferred promise. - */ - function makeInterfaceConnect(ifname) { - return new Promise(resolve => { - - ethernetManager.connect(ifname, function onConnect(success, message) { - ok(success, "Interface " + ifname + " is connected successfully."); - is(message, "ok", "Message is as expected."); - - resolve(success); - }); - - }); - } - - /** - * Make an interface disconnect to network. - * - * Fulfill params: A boolean value indicates success or not. - * - * @param ifname - * Interface name. - * - * @return A deferred promise. - */ - function makeInterfaceDisconnect(ifname) { - return new Promise(resolve => { - - ethernetManager.disconnect(ifname, function onDisconnect(success, message) { - ok(success, "Interface " + ifname + " is disconnected successfully."); - is(message, "ok", "Message is as expected."); - - resolve(success); - }); - - }); - } - - /** - * Update the config the an interface in the interface list. - * - * @param ifname - * Interface name. - * @param config - * .ip: ip address. - * .prefixLength: mask length. - * .gateway: gateway. - * .dnses: dnses. - * .httpProxyHost: http proxy host. - * .httpProxyPort: http porxy port. - * .usingDhcp: an boolean value indicates using dhcp or not. - * - * @return A deferred promise. - */ - function updateInterfaceConfig(ifname, config) { - return new Promise(resolve => { - - ethernetManager.updateInterfaceConfig(ifname, config, - function onUpdated(success, message) { - ok(success, "Interface " + ifname + " config is updated successfully " + - "with " + JSON.stringify(config)); - is(message, "ok", "Message is as expected."); - - resolve(success); - }); - - }); - } - - /** - * Wait for timeout. - * - * @param timeout - * Time in ms. - * - * @return A deferred promise. - */ - function waitForTimeout(timeout) { - return new Promise(resolve => { - - setTimeout(function() { - ok(true, "waitForTimeout " + timeout); - resolve(); - }, timeout); - - }); - } - - /** - * Wait for default route of a specific interface being set and - * check. - * - * @param ifname - * Interface name. - * @param gateway - * Target gateway. - * - * @return A deferred promise. - */ - function waitForDefaultRouteSet(ifname, gateway) { - return gTestSuite.waitForTimeout(500) - .then(() => gTestSuite.checkDefaultRoute(ifname, gateway)) - .then(success => { - if (success) { - ok(true, "Default route is set as expected." + gateway); - return; - } - - ok(true, "Default route is not set yet, check again. " + success); - return waitForDefaultRouteSet(ifname, gateway); - }); - } - - //--------------------------------------------------- - // Public test suite functions - //--------------------------------------------------- - suite.scanInterfaces = scanInterfaces; - suite.addInterface = addInterface; - suite.removeInterface = removeInterface; - suite.enableInterface = enableInterface; - suite.disableInterface = disableInterface; - suite.makeInterfaceConnect = makeInterfaceConnect; - suite.makeInterfaceDisconnect = makeInterfaceDisconnect; - suite.updateInterfaceConfig = updateInterfaceConfig; - suite.getDhcpIpAddr = getDhcpIpAddr; - suite.getDhcpGateway = getDhcpGateway; - suite.checkInterfaceExist = checkInterfaceExist; - suite.checkInterfaceIsEnabled = checkInterfaceIsEnabled; - suite.checkInterfaceIpAddr = checkInterfaceIpAddr; - suite.checkDefaultRoute = checkDefaultRoute; - suite.checkInterfaceListLength = checkInterfaceListLength; - suite.waitForTimeout = waitForTimeout; - suite.waitForDefaultRouteSet = waitForDefaultRouteSet; - - /** - * End up the test run. - * - * Wait until all pending emulator shell commands are done and then |finish| - * will be called in the end. - */ - function cleanUp() { - waitFor(finish, function() { - return pendingEmulatorShellCount === 0; - }); - } - - /** - * Common test routine. - * - * Start a test with the given test case chain. The test environment will be - * settled down before the test. After the test, all the affected things will - * be restored. - * - * @param aTestCaseChain - * The test case entry point, which can be a function or a promise. - * - * @return A deferred promise. - */ - suite.doTest = function(aTestCaseChain) { - return Promise.resolve() - .then(aTestCaseChain) - .then(function onresolve() { - cleanUp(); - }, function onreject(aReason) { - ok(false, 'Promise rejects during test' + (aReason ? '(' + aReason + ')' : '')); - cleanUp(); - }); - }; - - return suite; -})(); diff --git a/dom/network/tests/marionette/manifest.ini b/dom/network/tests/marionette/manifest.ini deleted file mode 100644 index 23f184bae636..000000000000 --- a/dom/network/tests/marionette/manifest.ini +++ /dev/null @@ -1,13 +0,0 @@ -[DEFAULT] -run-if = buildapp == 'b2g' - -[test_ethernet_add_interface.js] -[test_ethernet_remove_interface.js] -[test_ethernet_enable.js] -[test_ethernet_disable.js] -[test_ethernet_connect_with_dhcp.js] -[test_ethernet_connect_with_static_ip.js] -[test_ethernet_reconnect_with_dhcp.js] -[test_ethernet_reconnect_with_static_ip.js] -[test_ethernet_ip_mode_change.js] -[test_ethernet_disconnect.js] diff --git a/dom/network/tests/marionette/test_ethernet_add_interface.js b/dom/network/tests/marionette/test_ethernet_add_interface.js deleted file mode 100644 index d628e77054b1..000000000000 --- a/dom/network/tests/marionette/test_ethernet_add_interface.js +++ /dev/null @@ -1,16 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = 'head.js'; - -const ETHERNET_INTERFACE_NAME = "eth1"; - -gTestSuite.doTest(function() { - return Promise.resolve() - .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.checkInterfaceListLength(0)) - .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.checkInterfaceListLength(1)) - .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME)); -}); \ No newline at end of file diff --git a/dom/network/tests/marionette/test_ethernet_connect_with_dhcp.js b/dom/network/tests/marionette/test_ethernet_connect_with_dhcp.js deleted file mode 100644 index 57c2df9c2ec3..000000000000 --- a/dom/network/tests/marionette/test_ethernet_connect_with_dhcp.js +++ /dev/null @@ -1,26 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = 'head.js'; - -const ETHERNET_INTERFACE_NAME = "eth1"; - -function checkDhcpResult(ifname) { - return gTestSuite.getDhcpIpAddr(ifname) - .then(ip => gTestSuite.checkInterfaceIpAddr(ifname, ip)) - .then(() => gTestSuite.getDhcpGateway(ifname)) - .then(gateway => gTestSuite.waitForDefaultRouteSet(ifname, gateway)); -} - -gTestSuite.doTest(function() { - return Promise.resolve() - .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME)) - .then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME)); -}); \ No newline at end of file diff --git a/dom/network/tests/marionette/test_ethernet_connect_with_static_ip.js b/dom/network/tests/marionette/test_ethernet_connect_with_static_ip.js deleted file mode 100644 index 3adc37b23caa..000000000000 --- a/dom/network/tests/marionette/test_ethernet_connect_with_static_ip.js +++ /dev/null @@ -1,33 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = 'head.js'; - -const ETHERNET_INTERFACE_NAME = "eth1"; - -let staticConfig = { - ip: "1.2.3.4", - gateway: "1.2.3.5", - prefixLength: 24, - dnses: ["1.2.3.6"], - ipMode: "static" -}; - -function checkStaticResult(ifname) { - return gTestSuite.checkInterfaceIpAddr(ifname, staticConfig.ip) - .then(() => gTestSuite.checkDefaultRoute(ifname, staticConfig.gateway)); -} - -gTestSuite.doTest(function() { - return Promise.resolve() - .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.updateInterfaceConfig(ETHERNET_INTERFACE_NAME, staticConfig)) - .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME)) - .then(() => checkStaticResult(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME)); -}); \ No newline at end of file diff --git a/dom/network/tests/marionette/test_ethernet_disable.js b/dom/network/tests/marionette/test_ethernet_disable.js deleted file mode 100644 index 9c3525faa341..000000000000 --- a/dom/network/tests/marionette/test_ethernet_disable.js +++ /dev/null @@ -1,17 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = 'head.js'; - -const ETHERNET_INTERFACE_NAME = "eth1"; - -gTestSuite.doTest(function() { - return Promise.resolve() - .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.checkInterfaceIsEnabled(ETHERNET_INTERFACE_NAME, false)) - .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME)); -}); \ No newline at end of file diff --git a/dom/network/tests/marionette/test_ethernet_disconnect.js b/dom/network/tests/marionette/test_ethernet_disconnect.js deleted file mode 100644 index 73f6aa3c5e83..000000000000 --- a/dom/network/tests/marionette/test_ethernet_disconnect.js +++ /dev/null @@ -1,25 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = 'head.js'; - -const ETHERNET_INTERFACE_NAME = "eth1"; -const INTERFACE_IP_NONE = "0.0.0.0"; - -function checkIpAddrIsReset(ifname) { - return gTestSuite.checkInterfaceIpAddr(ifname, INTERFACE_IP_NONE) - .then(() => gTestSuite.checkDefaultRoute(ifname)); -} - -gTestSuite.doTest(function() { - return Promise.resolve() - .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME)) - .then(() => checkIpAddrIsReset(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME)); -}); \ No newline at end of file diff --git a/dom/network/tests/marionette/test_ethernet_enable.js b/dom/network/tests/marionette/test_ethernet_enable.js deleted file mode 100644 index f5578a44f80b..000000000000 --- a/dom/network/tests/marionette/test_ethernet_enable.js +++ /dev/null @@ -1,17 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = 'head.js'; - -const ETHERNET_INTERFACE_NAME = "eth1"; - -gTestSuite.doTest(function() { - return Promise.resolve() - .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.checkInterfaceIsEnabled(ETHERNET_INTERFACE_NAME, true)) - .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME)); -}); \ No newline at end of file diff --git a/dom/network/tests/marionette/test_ethernet_ip_mode_change.js b/dom/network/tests/marionette/test_ethernet_ip_mode_change.js deleted file mode 100644 index 5db2049bef7d..000000000000 --- a/dom/network/tests/marionette/test_ethernet_ip_mode_change.js +++ /dev/null @@ -1,43 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = 'head.js'; - -const ETHERNET_INTERFACE_NAME = "eth1"; - -let staticConfig = { - ip: "1.2.3.4", - gateway: "1.2.3.5", - prefixLength: 24, - dnses: ["1.2.3.6"], - ipMode: "static" -}; - -function checkStaticResult(ifname) { - return gTestSuite.checkInterfaceIpAddr(ifname, staticConfig.ip) - .then(() => gTestSuite.waitForDefaultRouteSet(ifname, staticConfig.gateway)); -} - -function checkDhcpResult(ifname) { - return gTestSuite.getDhcpIpAddr(ifname) - .then(ip => gTestSuite.checkInterfaceIpAddr(ifname, ip)) - .then(() => gTestSuite.getDhcpGateway(ifname)) - .then(gateway => gTestSuite.waitForDefaultRouteSet(ifname, gateway)); -} - -gTestSuite.doTest(function() { - return Promise.resolve() - .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME)) - .then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.updateInterfaceConfig(ETHERNET_INTERFACE_NAME, staticConfig)) - .then(() => checkStaticResult(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.updateInterfaceConfig(ETHERNET_INTERFACE_NAME, { ipMode: "dhcp"})) - .then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME)); -}); \ No newline at end of file diff --git a/dom/network/tests/marionette/test_ethernet_reconnect_with_dhcp.js b/dom/network/tests/marionette/test_ethernet_reconnect_with_dhcp.js deleted file mode 100644 index 96719c15264c..000000000000 --- a/dom/network/tests/marionette/test_ethernet_reconnect_with_dhcp.js +++ /dev/null @@ -1,29 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = 'head.js'; - -const ETHERNET_INTERFACE_NAME = "eth1"; - -function checkDhcpResult(ifname) { - return gTestSuite.getDhcpIpAddr(ifname) - .then(ip => gTestSuite.checkInterfaceIpAddr(ifname, ip)) - .then(() => gTestSuite.getDhcpGateway(ifname)) - .then(gateway => gTestSuite.waitForDefaultRouteSet(ifname, gateway)); -} - -gTestSuite.doTest(function() { - return Promise.resolve() - .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME)) - .then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME)) - .then(() => checkDhcpResult(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME)); -}); diff --git a/dom/network/tests/marionette/test_ethernet_reconnect_with_static_ip.js b/dom/network/tests/marionette/test_ethernet_reconnect_with_static_ip.js deleted file mode 100644 index 91f25a4710d1..000000000000 --- a/dom/network/tests/marionette/test_ethernet_reconnect_with_static_ip.js +++ /dev/null @@ -1,36 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = 'head.js'; - -const ETHERNET_INTERFACE_NAME = "eth1"; - -let staticConfig = { - ip: "1.2.3.4", - gateway: "1.2.3.5", - prefixLength: 24, - dnses: ["1.2.3.6"], - ipMode: "static" -}; - -function checkStaticResult(ifname) { - return gTestSuite.checkInterfaceIpAddr(ifname, staticConfig.ip) - .then(() => gTestSuite.checkDefaultRoute(ifname, staticConfig.gateway)); -} - -gTestSuite.doTest(function() { - return Promise.resolve() - .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.enableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.updateInterfaceConfig(ETHERNET_INTERFACE_NAME, staticConfig)) - .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME)) - .then(() => checkStaticResult(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceConnect(ETHERNET_INTERFACE_NAME)) - .then(() => checkStaticResult(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.makeInterfaceDisconnect(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.disableInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME)); -}); \ No newline at end of file diff --git a/dom/network/tests/marionette/test_ethernet_remove_interface.js b/dom/network/tests/marionette/test_ethernet_remove_interface.js deleted file mode 100644 index c7fb0e81b88b..000000000000 --- a/dom/network/tests/marionette/test_ethernet_remove_interface.js +++ /dev/null @@ -1,16 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = 'head.js'; - -const ETHERNET_INTERFACE_NAME = "eth1"; - -gTestSuite.doTest(function() { - return Promise.resolve() - .then(() => gTestSuite.checkInterfaceExist(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.addInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.checkInterfaceListLength(1)) - .then(() => gTestSuite.removeInterface(ETHERNET_INTERFACE_NAME)) - .then(() => gTestSuite.checkInterfaceListLength(0)); -}); \ No newline at end of file diff --git a/python/mozbuild/mozbuild/action/test_archive.py b/python/mozbuild/mozbuild/action/test_archive.py index 72c3a6fb5dca..f7815e166b8a 100644 --- a/python/mozbuild/mozbuild/action/test_archive.py +++ b/python/mozbuild/mozbuild/action/test_archive.py @@ -138,7 +138,6 @@ ARCHIVE_FILES = { 'base': '', 'manifests': [ 'testing/marionette/harness/marionette_harness/tests/unit-tests.ini', - 'testing/marionette/harness/marionette_harness/tests/webapi-tests.ini', ], # We also need the manifests and harness_unit tests 'pattern': 'testing/marionette/harness/marionette_harness/tests/**', diff --git a/python/mozbuild/mozbuild/frontend/context.py b/python/mozbuild/mozbuild/frontend/context.py index 542e17ae884c..7d9eb3010b51 100644 --- a/python/mozbuild/mozbuild/frontend/context.py +++ b/python/mozbuild/mozbuild/frontend/context.py @@ -1859,10 +1859,6 @@ VARIABLES = { """List of manifest files defining marionette-unit tests. """), - 'MARIONETTE_WEBAPI_MANIFESTS': (ManifestparserManifestList, list, - """List of manifest files defining marionette-webapi tests. - """), - 'METRO_CHROME_MANIFESTS': (ManifestparserManifestList, list, """List of manifest files defining metro browser chrome tests. """), diff --git a/testing/marionette/harness/marionette_harness/tests/webapi-tests.ini b/testing/marionette/harness/marionette_harness/tests/webapi-tests.ini deleted file mode 100644 index 0fa1f0080c14..000000000000 --- a/testing/marionette/harness/marionette_harness/tests/webapi-tests.ini +++ /dev/null @@ -1,3 +0,0 @@ -[include:../../../../../dom/events/test/marionette/manifest.ini] -skip-if = android_version > '15' # Bug 1203075 -[include:../../../../../dom/network/tests/marionette/manifest.ini] diff --git a/testing/marionette/moz.build b/testing/marionette/moz.build index 2d958f0d5d24..471ac4e1e79e 100644 --- a/testing/marionette/moz.build +++ b/testing/marionette/moz.build @@ -8,7 +8,6 @@ JAR_MANIFESTS += ["jar.mn"] JS_PREFERENCE_FILES += ["prefs/marionette.js"] MARIONETTE_UNIT_MANIFESTS += ["harness/marionette_harness/tests/unit/unit-tests.ini"] -MARIONETTE_WEBAPI_MANIFESTS += ["harness/marionette_harness/tests/webapi-tests.ini"] XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.ini"] with Files("**"): From a2ea371c3c67ae931e45015703ae283ecc0dd6e0 Mon Sep 17 00:00:00 2001 From: Coroiu Cristina Date: Wed, 27 Jun 2018 16:57:10 +0300 Subject: [PATCH 26/43] Backed out 3 changesets (bug 1470151) for build bustage at testing/xpcshell/selftest.py on a CLOSED TREE Backed out changeset c73f394a4bef (bug 1470151) Backed out changeset 3579431e03dc (bug 1470151) Backed out changeset ddde7dd347d4 (bug 1470151) --- dom/ipc/ContentChild.cpp | 10 +-- dom/ipc/ContentChild.h | 4 +- dom/ipc/PContent.ipdl | 4 +- .../mozharness/scripts/desktop_unittest.py | 5 -- testing/xpcshell/head.js | 18 ----- tools/code-coverage/CodeCoverageHandler.cpp | 18 +---- tools/code-coverage/CodeCoverageHandler.h | 6 +- tools/code-coverage/PerTestCoverageUtils.jsm | 64 --------------- tools/code-coverage/moz.build | 2 - tools/code-coverage/nsCodeCoverage.cpp | 80 +++---------------- tools/code-coverage/nsICodeCoverage.idl | 6 +- 11 files changed, 28 insertions(+), 189 deletions(-) delete mode 100644 tools/code-coverage/PerTestCoverageUtils.jsm diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index ed1c09f724d4..61f0ac058a19 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -3696,11 +3696,10 @@ ContentChild::RecvShareCodeCoverageMutex(const CrossProcessMutexHandle& aHandle) } mozilla::ipc::IPCResult -ContentChild::RecvDumpCodeCoverageCounters(DumpCodeCoverageCountersResolver&& aResolver) +ContentChild::RecvDumpCodeCoverageCounters() { #ifdef MOZ_CODE_COVERAGE - CodeCoverageHandler::DumpCounters(); - aResolver(/* unused */ true); + CodeCoverageHandler::DumpCounters(0); return IPC_OK(); #else MOZ_CRASH("Shouldn't receive this message in non-code coverage builds!"); @@ -3708,11 +3707,10 @@ ContentChild::RecvDumpCodeCoverageCounters(DumpCodeCoverageCountersResolver&& aR } mozilla::ipc::IPCResult -ContentChild::RecvResetCodeCoverageCounters(ResetCodeCoverageCountersResolver&& aResolver) +ContentChild::RecvResetCodeCoverageCounters() { #ifdef MOZ_CODE_COVERAGE - CodeCoverageHandler::ResetCounters(); - aResolver(/* unused */ true); + CodeCoverageHandler::ResetCounters(0); return IPC_OK(); #else MOZ_CRASH("Shouldn't receive this message in non-code coverage builds!"); diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 9695d53c389e..5870194b0b41 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -624,10 +624,10 @@ public: RecvShareCodeCoverageMutex(const CrossProcessMutexHandle& aHandle) override; virtual mozilla::ipc::IPCResult - RecvDumpCodeCoverageCounters(DumpCodeCoverageCountersResolver&& aResolver) override; + RecvDumpCodeCoverageCounters() override; virtual mozilla::ipc::IPCResult - RecvResetCodeCoverageCounters(ResetCodeCoverageCountersResolver&& aResolver) override; + RecvResetCodeCoverageCounters() override; virtual mozilla::ipc::IPCResult RecvSetInputEventQueueEnabled() override; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 2fd9d327535c..7e93b3fc4ae3 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -633,8 +633,8 @@ child: async SetPluginList(uint32_t pluginEpoch, PluginTag[] plugins, FakePluginTag[] fakePlugins); async ShareCodeCoverageMutex(CrossProcessMutexHandle handle); - async DumpCodeCoverageCounters() returns (bool unused); - async ResetCodeCoverageCounters() returns (bool unused); + async DumpCodeCoverageCounters(); + async ResetCodeCoverageCounters(); /* * IPC message to enable the input event queue on the main thread of the diff --git a/testing/mozharness/scripts/desktop_unittest.py b/testing/mozharness/scripts/desktop_unittest.py index ea8ff30d625b..d923158042b4 100755 --- a/testing/mozharness/scripts/desktop_unittest.py +++ b/testing/mozharness/scripts/desktop_unittest.py @@ -15,7 +15,6 @@ import re import sys import copy import shutil -import tempfile import glob import imp @@ -891,10 +890,6 @@ class DesktopUnittest(TestingMixin, MercurialScript, MozbaseMixin, if self.per_test_coverage: gcov_dir, jsvm_dir = self.set_coverage_env(env) - # Per-test reset/dump is only supported for xpcshell and - # Linux for the time being. - if not is_baseline_test and suite == 'xpcshell' and self._is_linux(): - env['GCOV_RESULTS_DIR'] = gcov_dir = tempfile.mkdtemp() return_code = self.run_command(final_cmd, cwd=dirs['abs_work_dir'], output_timeout=cmd_timeout, diff --git a/testing/xpcshell/head.js b/testing/xpcshell/head.js index 97a8f3a01789..952649302db3 100644 --- a/testing/xpcshell/head.js +++ b/testing/xpcshell/head.js @@ -511,20 +511,6 @@ function _execute_test() { this[func] = Assert[func].bind(Assert); } - try { - ChromeUtils.import("resource://testing-common/PerTestCoverageUtils.jsm"); - } catch (e) { - // If the module doesn't exist, code coverage is disabled. - // Otherwise, rethrow the exception. - if (e.result != Cr.NS_ERROR_FILE_NOT_FOUND) { - throw e; - } - } - - if (PerTestCoverageUtils) { - PerTestCoverageUtils.beforeTest(); - } - try { do_test_pending("MAIN run_test"); // Check if run_test() is defined. If defined, run it. @@ -543,10 +529,6 @@ function _execute_test() { if (coverageCollector != null) { coverageCollector.recordTestCoverage(_TEST_FILE[0]); } - - if (PerTestCoverageUtils) { - PerTestCoverageUtils.afterTest(); - } } catch (e) { _passed = false; // do_check failures are already logged and set _quit to true and throw diff --git a/tools/code-coverage/CodeCoverageHandler.cpp b/tools/code-coverage/CodeCoverageHandler.cpp index be687a590417..76dd399759a4 100644 --- a/tools/code-coverage/CodeCoverageHandler.cpp +++ b/tools/code-coverage/CodeCoverageHandler.cpp @@ -44,7 +44,7 @@ void counters_reset() { StaticAutoPtr CodeCoverageHandler::instance; -void CodeCoverageHandler::DumpCounters() +void CodeCoverageHandler::DumpCounters(int) { CrossProcessMutexAutoLock lock(*CodeCoverageHandler::Get()->GetMutex()); @@ -53,12 +53,7 @@ void CodeCoverageHandler::DumpCounters() printf_stderr("[CodeCoverage] Dump completed.\n"); } -void CodeCoverageHandler::DumpCountersSignalHandler(int) -{ - DumpCounters(); -} - -void CodeCoverageHandler::ResetCounters() +void CodeCoverageHandler::ResetCounters(int) { CrossProcessMutexAutoLock lock(*CodeCoverageHandler::Get()->GetMutex()); @@ -67,25 +62,20 @@ void CodeCoverageHandler::ResetCounters() printf_stderr("[CodeCoverage] Reset completed.\n"); } -void CodeCoverageHandler::ResetCountersSignalHandler(int) -{ - ResetCounters(); -} - void CodeCoverageHandler::SetSignalHandlers() { #ifndef XP_WIN printf_stderr("[CodeCoverage] Setting handlers for process %d.\n", getpid()); struct sigaction dump_sa; - dump_sa.sa_handler = CodeCoverageHandler::DumpCountersSignalHandler; + dump_sa.sa_handler = CodeCoverageHandler::DumpCounters; dump_sa.sa_flags = SA_RESTART; sigemptyset(&dump_sa.sa_mask); DebugOnly r1 = sigaction(SIGUSR1, &dump_sa, nullptr); MOZ_ASSERT(r1 == 0, "Failed to install GCOV SIGUSR1 handler"); struct sigaction reset_sa; - reset_sa.sa_handler = CodeCoverageHandler::ResetCountersSignalHandler; + reset_sa.sa_handler = CodeCoverageHandler::ResetCounters; reset_sa.sa_flags = SA_RESTART; sigemptyset(&reset_sa.sa_mask); DebugOnly r2 = sigaction(SIGUSR2, &reset_sa, nullptr); diff --git a/tools/code-coverage/CodeCoverageHandler.h b/tools/code-coverage/CodeCoverageHandler.h index 0a29fabe15f1..42e4725e46b1 100644 --- a/tools/code-coverage/CodeCoverageHandler.h +++ b/tools/code-coverage/CodeCoverageHandler.h @@ -18,10 +18,8 @@ public: static CodeCoverageHandler* Get(); CrossProcessMutex* GetMutex(); CrossProcessMutexHandle GetMutexHandle(int aProcId); - static void DumpCounters(); - static void DumpCountersSignalHandler(int); - static void ResetCounters(); - static void ResetCountersSignalHandler(int); + static void DumpCounters(int); + static void ResetCounters(int); private: CodeCoverageHandler(); diff --git a/tools/code-coverage/PerTestCoverageUtils.jsm b/tools/code-coverage/PerTestCoverageUtils.jsm deleted file mode 100644 index c804aaa89b00..000000000000 --- a/tools/code-coverage/PerTestCoverageUtils.jsm +++ /dev/null @@ -1,64 +0,0 @@ -/* 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/. */ - -/* exported PerTestCoverageUtils */ - -"use strict"; - -var EXPORTED_SYMBOLS = ["PerTestCoverageUtils"]; - -ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); - -class PerTestCoverageUtilsClass { - constructor(tmp_gcov_dir, gcov_dir) { - this.tmp_gcov_dir = tmp_gcov_dir; - this.gcov_dir = gcov_dir; - - this.codeCoverageService = Cc["@mozilla.org/tools/code-coverage;1"].getService(Ci.nsICodeCoverage); - } - - _awaitPromise(promise) { - let ret; - let complete = false; - let error = null; - promise.catch(e => error = e).then(v => { - ret = v; - complete = true; - }); - Services.tm.spinEventLoopUntil(() => complete); - if (error) { - throw new Error(error); - } - return ret; - } - - // Resets the counters to 0. - beforeTest() { - this._awaitPromise(this.codeCoverageService.resetCounters()); - } - - // Dumps counters and moves the gcda files in the directory expected by codecoverage.py. - afterTest() { - this._awaitPromise(this.codeCoverageService.dumpCounters()); - - let srcDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); - srcDir.initWithPath(this.tmp_gcov_dir); - - let destDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); - destDir.initWithPath(this.gcov_dir); - - let srcDirEntries = srcDir.directoryEntries; - while (srcDirEntries.hasMoreElements()) { - srcDirEntries.nextFile.moveTo(destDir, null); - } - } -} - -const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); -// This is the directory where gcov is emitting the gcda files. -const tmp_gcov_dir = env.get("GCOV_PREFIX"); -// This is the directory where codecoverage.py is expecting to see the gcda files. -const gcov_dir = env.get("GCOV_RESULTS_DIR"); - -const PerTestCoverageUtils = gcov_dir ? new PerTestCoverageUtilsClass(tmp_gcov_dir, gcov_dir) : null; diff --git a/tools/code-coverage/moz.build b/tools/code-coverage/moz.build index d66699a740a1..c3bad16b9fa6 100644 --- a/tools/code-coverage/moz.build +++ b/tools/code-coverage/moz.build @@ -31,6 +31,4 @@ if CONFIG['MOZ_CODE_COVERAGE']: XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini'] MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini'] - TESTING_JS_MODULES += ['PerTestCoverageUtils.jsm'] - FINAL_LIBRARY = 'xul' diff --git a/tools/code-coverage/nsCodeCoverage.cpp b/tools/code-coverage/nsCodeCoverage.cpp index d6e1b7aeb529..904778508c8d 100644 --- a/tools/code-coverage/nsCodeCoverage.cpp +++ b/tools/code-coverage/nsCodeCoverage.cpp @@ -7,11 +7,9 @@ #include "mozilla/CodeCoverageHandler.h" #include "mozilla/Unused.h" #include "mozilla/dom/ContentParent.h" -#include "mozilla/dom/Promise.h" using namespace mozilla; using namespace mozilla::dom; -using namespace mozilla::ipc; NS_IMPL_ISUPPORTS(nsCodeCoverage, nsICodeCoverage) @@ -24,82 +22,28 @@ nsCodeCoverage::~nsCodeCoverage() { } -enum RequestType { Dump, Reset }; - -class ProcessCount final { - NS_INLINE_DECL_REFCOUNTING(ProcessCount); - -public: - ProcessCount(uint32_t c) : mCount(c) {} - operator uint32_t() const { return mCount; } - ProcessCount& operator--() { mCount--; return *this; } - -private: - ~ProcessCount() {} - uint32_t mCount; -}; - -nsresult Request(JSContext* cx, Promise** aPromise, RequestType requestType) +NS_IMETHODIMP nsCodeCoverage::DumpCounters() { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); - nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx)); - if (NS_WARN_IF(!global)) { - return NS_ERROR_FAILURE; - } - - ErrorResult result; - RefPtr promise = Promise::Create(global, result); - if (NS_WARN_IF(result.Failed())) { - return result.StealNSResult(); - } - - uint32_t processCount = 0; + CodeCoverageHandler::DumpCounters(0); for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { - ++processCount; + Unused << cp->SendDumpCodeCoverageCounters(); } - if (requestType == RequestType::Dump) { - CodeCoverageHandler::DumpCounters(); - } else if (requestType == RequestType::Reset) { - CodeCoverageHandler::ResetCounters(); - } - - if (processCount == 0) { - promise->MaybeResolveWithUndefined(); - } else { - RefPtr processCountHolder(new ProcessCount(processCount)); - - auto resolve = [processCountHolder, promise](bool unused) { - if (--(*processCountHolder) == 0) { - promise->MaybeResolveWithUndefined(); - } - }; - - auto reject = [promise](ResponseRejectReason aReason) { - promise->MaybeReject(NS_ERROR_FAILURE); - }; - - for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { - if (requestType == RequestType::Dump) { - cp->SendDumpCodeCoverageCounters(resolve, reject); - } else if (requestType == RequestType::Reset) { - cp->SendResetCodeCoverageCounters(resolve, reject); - } - } - } - - promise.forget(aPromise); return NS_OK; } -NS_IMETHODIMP nsCodeCoverage::DumpCounters(JSContext *cx, Promise** aPromise) +NS_IMETHODIMP nsCodeCoverage::ResetCounters() { - return Request(cx, aPromise, RequestType::Dump); -} + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(NS_IsMainThread()); -NS_IMETHODIMP nsCodeCoverage::ResetCounters(JSContext *cx, Promise** aPromise) -{ - return Request(cx, aPromise, RequestType::Reset); + CodeCoverageHandler::ResetCounters(0); + for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { + Unused << cp->SendResetCodeCoverageCounters(); + } + + return NS_OK; } diff --git a/tools/code-coverage/nsICodeCoverage.idl b/tools/code-coverage/nsICodeCoverage.idl index e1f1a21316c6..4b4216572d16 100644 --- a/tools/code-coverage/nsICodeCoverage.idl +++ b/tools/code-coverage/nsICodeCoverage.idl @@ -19,12 +19,10 @@ interface nsICodeCoverage : nsISupports /** * Write the coverage counters to disk. */ - [implicit_jscontext] - Promise dumpCounters(); + void dumpCounters(); /** * Reset the coverage counters to 0 (as if nothing was executed). */ - [implicit_jscontext] - Promise resetCounters(); + void resetCounters(); }; From d73b4b06790daaed588910a027db1f5aa25ebbe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 26 Jun 2018 20:37:00 +0200 Subject: [PATCH 27/43] Bug 1464782: Rename offset-* logical properties to inset-*. r=xidorn MozReview-Commit-ID: BW44sru99RF --- layout/base/tests/bug1354478-1-ref.html | 2 +- layout/base/tests/bug1354478-1.html | 2 +- layout/base/tests/bug1354478-2-ref.html | 2 +- layout/base/tests/bug1354478-2.html | 2 +- layout/base/tests/bug1354478-3-ref.html | 2 +- layout/base/tests/bug1354478-3.html | 2 +- layout/base/tests/bug1354478-4-ref.html | 2 +- layout/base/tests/bug1354478-4.html | 2 +- layout/base/tests/bug1354478-5-ref.html | 2 +- layout/base/tests/bug1354478-5.html | 2 +- layout/base/tests/bug1354478-6-ref.html | 2 +- layout/base/tests/bug1354478-6.html | 2 +- .../css-grid/grid-align-content-001-ref.html | 20 +-- .../css-grid/grid-item-align-001-ref.html | 12 +- .../css-grid/grid-item-align-002-ref.html | 2 +- .../css-grid/grid-item-align-003-ref.html | 10 +- .../css-grid/grid-item-justify-001-ref.html | 2 +- .../css-grid/grid-item-justify-002-ref.html | 2 +- .../grid-justify-content-001-ref.html | 24 ++-- .../css-grid/grid-row-gap-001-ref.html | 20 +-- ...rid-placement-auto-row-sparse-001-ref.html | 98 ++++++------- .../rtl-grid-placement-definite-001-ref.html | 16 +-- ...rid-placement-auto-row-sparse-001-ref.html | 98 ++++++------- ...rid-placement-auto-row-sparse-001-ref.html | 114 +++++++-------- ...1376231-vertical-gpos-adjustments-ref.html | 2 +- .../1376231-vertical-gpos-adjustments.html | 2 +- ...side-border-box-border-radius-005-ref.html | 12 +- ...side-border-box-border-radius-006-ref.html | 12 +- ...side-border-box-border-radius-007-ref.html | 12 +- ...side-border-box-border-radius-008-ref.html | 12 +- ...side-border-box-border-radius-009-ref.html | 12 +- ...side-border-box-border-radius-010-ref.html | 12 +- ...side-border-box-border-radius-011-ref.html | 12 +- ...side-border-box-border-radius-012-ref.html | 12 +- .../shapes1/shape-outside-circle-048-ref.html | 14 +- .../shapes1/shape-outside-circle-049-ref.html | 14 +- .../shapes1/shape-outside-circle-050-ref.html | 14 +- .../shapes1/shape-outside-circle-051-ref.html | 14 +- .../shapes1/shape-outside-circle-052-ref.html | 14 +- .../shapes1/shape-outside-circle-053-ref.html | 14 +- .../shapes1/shape-outside-circle-054-ref.html | 14 +- .../shapes1/shape-outside-circle-055-ref.html | 14 +- .../shape-outside-ellipse-046-ref.html | 8 +- .../shape-outside-ellipse-047-ref.html | 8 +- .../shape-outside-ellipse-048-ref.html | 8 +- .../shape-outside-ellipse-049-ref.html | 8 +- .../shape-outside-ellipse-050-ref.html | 8 +- .../shape-outside-ellipse-051-ref.html | 8 +- .../shapes1/shape-outside-inset-020-ref.html | 12 +- .../shapes1/shape-outside-inset-021-ref.html | 12 +- .../shapes1/shape-outside-inset-022-ref.html | 12 +- .../shapes1/shape-outside-inset-023-ref.html | 12 +- .../shapes1/shape-outside-inset-024-ref.html | 12 +- .../shapes1/shape-outside-inset-025-ref.html | 12 +- .../shapes1/shape-outside-inset-026-ref.html | 12 +- .../shapes1/shape-outside-inset-027-ref.html | 12 +- .../shape-outside-polygon-020-ref.html | 12 +- .../shape-outside-polygon-021-ref.html | 12 +- .../shape-outside-polygon-022-ref.html | 12 +- .../shape-outside-polygon-023-ref.html | 12 +- .../shape-outside-polygon-024-ref.html | 12 +- .../shape-outside-polygon-025-ref.html | 12 +- layout/style/res/html.css | 4 +- layout/style/test/gtest/example.css | 4 +- layout/style/test/property_database.js | 132 +++++++++++------- .../style/test/test_logical_properties.html | 4 +- .../style/properties/helpers.mako.rs | 2 +- .../properties/longhands/position.mako.rs | 7 +- .../animation-types/property-list.js | 16 +-- 69 files changed, 538 insertions(+), 501 deletions(-) diff --git a/layout/base/tests/bug1354478-1-ref.html b/layout/base/tests/bug1354478-1-ref.html index 4a574fbf200e..be2af0e331a4 100644 --- a/layout/base/tests/bug1354478-1-ref.html +++ b/layout/base/tests/bug1354478-1-ref.html @@ -12,7 +12,7 @@ inline-size: 200px; block-size: 20px; position: relative; - offset-block-start: -20px; + inset-block-start: -20px; background: silver; } diff --git a/layout/base/tests/bug1354478-1.html b/layout/base/tests/bug1354478-1.html index 5d6dfab9875d..bfec0df4e8c3 100644 --- a/layout/base/tests/bug1354478-1.html +++ b/layout/base/tests/bug1354478-1.html @@ -12,7 +12,7 @@ inline-size: 200px; block-size: 20px; position: relative; - offset-block-start: -20px; + inset-block-start: -20px; background: silver; } diff --git a/layout/base/tests/bug1354478-2-ref.html b/layout/base/tests/bug1354478-2-ref.html index 256a4c4959a6..ba35b49820f7 100644 --- a/layout/base/tests/bug1354478-2-ref.html +++ b/layout/base/tests/bug1354478-2-ref.html @@ -12,7 +12,7 @@ inline-size: 200px; block-size: 20px; position: relative; - offset-block-start: -20px; + inset-block-start: -20px; background: silver; } diff --git a/layout/base/tests/bug1354478-2.html b/layout/base/tests/bug1354478-2.html index f2b783e9bc3d..fac2a05097eb 100644 --- a/layout/base/tests/bug1354478-2.html +++ b/layout/base/tests/bug1354478-2.html @@ -12,7 +12,7 @@ inline-size: 200px; block-size: 20px; position: relative; - offset-block-start: -20px; + inset-block-start: -20px; background: silver; } diff --git a/layout/base/tests/bug1354478-3-ref.html b/layout/base/tests/bug1354478-3-ref.html index dc58d80f109a..fc3576a3de6b 100644 --- a/layout/base/tests/bug1354478-3-ref.html +++ b/layout/base/tests/bug1354478-3-ref.html @@ -15,7 +15,7 @@ inline-size: 200px; block-size: 20px; position: relative; - offset-block-start: -20px; + inset-block-start: -20px; background: silver; } diff --git a/layout/base/tests/bug1354478-3.html b/layout/base/tests/bug1354478-3.html index cb81e80ca6ef..df36f23ff0d2 100644 --- a/layout/base/tests/bug1354478-3.html +++ b/layout/base/tests/bug1354478-3.html @@ -15,7 +15,7 @@ inline-size: 200px; block-size: 20px; position: relative; - offset-block-start: -20px; + inset-block-start: -20px; background: silver; } diff --git a/layout/base/tests/bug1354478-4-ref.html b/layout/base/tests/bug1354478-4-ref.html index 61c3e6aa563c..64d4a9722f4c 100644 --- a/layout/base/tests/bug1354478-4-ref.html +++ b/layout/base/tests/bug1354478-4-ref.html @@ -15,7 +15,7 @@ inline-size: 200px; block-size: 20px; position: relative; - offset-block-start: -20px; + inset-block-start: -20px; background: silver; } diff --git a/layout/base/tests/bug1354478-4.html b/layout/base/tests/bug1354478-4.html index 937ef28e7cca..4bf4e1ee8db9 100644 --- a/layout/base/tests/bug1354478-4.html +++ b/layout/base/tests/bug1354478-4.html @@ -15,7 +15,7 @@ inline-size: 200px; block-size: 20px; position: relative; - offset-block-start: -20px; + inset-block-start: -20px; background: silver; } diff --git a/layout/base/tests/bug1354478-5-ref.html b/layout/base/tests/bug1354478-5-ref.html index 52e61abfef25..1a49897686b3 100644 --- a/layout/base/tests/bug1354478-5-ref.html +++ b/layout/base/tests/bug1354478-5-ref.html @@ -15,7 +15,7 @@ inline-size: 200px; block-size: 20px; position: relative; - offset-block-start: -20px; + inset-block-start: -20px; background: silver; } diff --git a/layout/base/tests/bug1354478-5.html b/layout/base/tests/bug1354478-5.html index 5a6cb1cde60a..02bde22e8743 100644 --- a/layout/base/tests/bug1354478-5.html +++ b/layout/base/tests/bug1354478-5.html @@ -15,7 +15,7 @@ inline-size: 200px; block-size: 20px; position: relative; - offset-block-start: -20px; + inset-block-start: -20px; background: silver; } diff --git a/layout/base/tests/bug1354478-6-ref.html b/layout/base/tests/bug1354478-6-ref.html index 0b079a87fbcb..3e0e9b162327 100644 --- a/layout/base/tests/bug1354478-6-ref.html +++ b/layout/base/tests/bug1354478-6-ref.html @@ -15,7 +15,7 @@ inline-size: 200px; block-size: 20px; position: relative; - offset-block-start: -20px; + inset-block-start: -20px; background: silver; } diff --git a/layout/base/tests/bug1354478-6.html b/layout/base/tests/bug1354478-6.html index a598b12a3d6b..badf030d67b5 100644 --- a/layout/base/tests/bug1354478-6.html +++ b/layout/base/tests/bug1354478-6.html @@ -15,7 +15,7 @@ inline-size: 200px; block-size: 20px; position: relative; - offset-block-start: -20px; + inset-block-start: -20px; background: silver; } diff --git a/layout/reftests/css-grid/grid-align-content-001-ref.html b/layout/reftests/css-grid/grid-align-content-001-ref.html index f67baf5307f1..ab4ad2d29814 100644 --- a/layout/reftests/css-grid/grid-align-content-001-ref.html +++ b/layout/reftests/css-grid/grid-align-content-001-ref.html @@ -45,20 +45,20 @@ item3 { grid-area: 3 / 3; } .vlr { writing-mode: vertical-lr; direction:rtl; } .vrl { writing-mode: vertical-rl; direction:ltr; } -.aend *, .aflexend * { offset-block-start:25px; } -.acenter * { offset-block-start:12.5px; } +.aend *, .aflexend * { inset-block-start:25px; } +.acenter * { inset-block-start:12.5px; } -.aspace-between item2 { offset-block-start:12.5px; } -.aspace-between item3 { offset-block-start:25px; } +.aspace-between item2 { inset-block-start:12.5px; } +.aspace-between item3 { inset-block-start:25px; } -.aspace-around item1 { offset-block-start:4.1666px; } -.aspace-around item2 { offset-block-start:12.5px; } -.aspace-around item3 { offset-block-start:20.8333px; } +.aspace-around item1 { inset-block-start:4.1666px; } +.aspace-around item2 { inset-block-start:12.5px; } +.aspace-around item3 { inset-block-start:20.8333px; } -.aspace-evenly item1 { offset-block-start:6.25px; } -.aspace-evenly item2 { offset-block-start:12.5px; } -.aspace-evenly item3 { offset-block-start:18.75px; } +.aspace-evenly item1 { inset-block-start:6.25px; } +.aspace-evenly item2 { inset-block-start:12.5px; } +.aspace-evenly item3 { inset-block-start:18.75px; } .astretch2 { grid-template-rows: 1fr 5px 7px; } .astretch3 { grid-template-rows: 15.5px 17.5px 7px; } diff --git a/layout/reftests/css-grid/grid-item-align-001-ref.html b/layout/reftests/css-grid/grid-item-align-001-ref.html index 35bb263ac730..e1d91723047b 100644 --- a/layout/reftests/css-grid/grid-item-align-001-ref.html +++ b/layout/reftests/css-grid/grid-item-align-001-ref.html @@ -34,7 +34,7 @@ span { border-block-start: 2px solid blue; border-inline-start: 2px solid lime; margin: 1px 1px 2px 2px; - offset-inline-start: 1px; + inset-inline-start: 1px; } abs1,abs2,abs3,abs4 { @@ -63,11 +63,11 @@ abs4 { display:none; } .astart,.aflexstart,.aleft,.aright,.astretch1,.astretch2,.astretch2,.astretch3, .astretch4,.astretch5,.astretch6,.astretch7,.aauto { - offset-block-start: 3px; + inset-block-start: 3px; } -.aend,.aflexend { offset-block-start: 9px; } -.acenter { offset-block-start: 5px; margin-block-start:2px; } +.aend,.aflexend { inset-block-start: 9px; } +.acenter { inset-block-start: 5px; margin-block-start:2px; } .hl .astretch2, .hr .astretch2 { height: 15px; } .hl .astretch3, .hr .astretch3 { height: 15px; } @@ -80,7 +80,7 @@ abs4 { display:none; } .astretch7 { width:0; height:9px; } .hl .hr {margin-left:4px;} -.hl .vl {offset-block-start: 1px; offset-inline-start:3px;} +.hl .vl {inset-block-start: 1px; inset-inline-start:3px;} .hl .vl.aend, .hl .vl.aflexend { margin-top: 7px; } .hl .vl.acenter { margin-top:4px; } @@ -115,7 +115,7 @@ abs4 { display:none; } .hr .vrl.aend, .hr .vrl.aflexend {margin-top:9px; margin-right:-7px; } .hr .vrl.acenter {margin-top:6px; margin-right:-3px; } -.vl span { offset-block-start: 1px; offset-inline-start: 3px; } +.vl span { inset-block-start: 1px; inset-inline-start: 3px; } .vl .astretch4 { width:15px; } .vl .astretch5 { width:13px; } diff --git a/layout/reftests/css-grid/grid-item-align-002-ref.html b/layout/reftests/css-grid/grid-item-align-002-ref.html index 73d60d4c2092..de59dea26d04 100644 --- a/layout/reftests/css-grid/grid-item-align-002-ref.html +++ b/layout/reftests/css-grid/grid-item-align-002-ref.html @@ -28,7 +28,7 @@ separator { clear:both; display:block; height:6px; } wrap { display: block; position: relative; - offset-inline-start:1px; + inset-inline-start:1px; background: white; block-size:20px; inline-size:32px; diff --git a/layout/reftests/css-grid/grid-item-align-003-ref.html b/layout/reftests/css-grid/grid-item-align-003-ref.html index f83366cdaa75..a2f14f869eba 100644 --- a/layout/reftests/css-grid/grid-item-align-003-ref.html +++ b/layout/reftests/css-grid/grid-item-align-003-ref.html @@ -29,7 +29,7 @@ separator { clear:both; display:block; height:6px; } wrap { display: block; position: relative; - offset-inline-start:1px; + inset-inline-start:1px; background: white; block-size:20px; inline-size:32px; @@ -65,10 +65,10 @@ abs4 { right: 35px; } .vlr { writing-mode: vertical-lr; direction:rtl; } .vrl { writing-mode: vertical-rl; direction:ltr; } -.unsafe.hl.aend, .unsafe.hl.aflexend { offset-block-start:-15px; } -.unsafe.vrl.aend, .unsafe.vrl.aflexend { offset-inline-start:-15px; } -.unsafe.hl.acenter { offset-block-start:-7px; } -.unsafe.vrl.acenter { offset-inline-start:-7px; } +.unsafe.hl.aend, .unsafe.hl.aflexend { inset-block-start:-15px; } +.unsafe.vrl.aend, .unsafe.vrl.aflexend { inset-inline-start:-15px; } +.unsafe.hl.acenter { inset-block-start:-7px; } +.unsafe.vrl.acenter { inset-inline-start:-7px; } .astretch2 { width:40px; height:15px; } .astretch3 { height:15px; } .astretch4 { width:0; } diff --git a/layout/reftests/css-grid/grid-item-justify-001-ref.html b/layout/reftests/css-grid/grid-item-justify-001-ref.html index 99c8536454e8..cfe109554620 100644 --- a/layout/reftests/css-grid/grid-item-justify-001-ref.html +++ b/layout/reftests/css-grid/grid-item-justify-001-ref.html @@ -29,7 +29,7 @@ separator { clear:both; display:block; height:6px; } wrap { display: block; position: relative; - offset-inline-start: 1px; + inset-inline-start: 1px; background: white; block-size: 20px; inline-size: 32px; diff --git a/layout/reftests/css-grid/grid-item-justify-002-ref.html b/layout/reftests/css-grid/grid-item-justify-002-ref.html index 39e7d26f0e1c..c723bc391508 100644 --- a/layout/reftests/css-grid/grid-item-justify-002-ref.html +++ b/layout/reftests/css-grid/grid-item-justify-002-ref.html @@ -29,7 +29,7 @@ separator { clear:both; display:block; height:6px; } wrap { display: block; position: relative; - offset-inline-start: 1px; + inset-inline-start: 1px; background: white; block-size: 20px; inline-size: 32px; diff --git a/layout/reftests/css-grid/grid-justify-content-001-ref.html b/layout/reftests/css-grid/grid-justify-content-001-ref.html index fa92e6cbf4e5..5e43a86bb499 100644 --- a/layout/reftests/css-grid/grid-justify-content-001-ref.html +++ b/layout/reftests/css-grid/grid-justify-content-001-ref.html @@ -45,21 +45,21 @@ item3 { grid-area: 3 / 3; } .vlr { writing-mode: vertical-lr; direction:rtl; } .vrl { writing-mode: vertical-rl; direction:ltr; } -.jend * , .jflexend * { offset-inline-start:17px; } -.jcenter * { offset-inline-start:8.5px; } -.hr.jleft * , .vlr.jleft * { offset-inline-start:17px; } -.hl.jright * , .vrl.jright * , .vl.jright * , .vr.jright * { offset-inline-start:17px; } +.jend * , .jflexend * { inset-inline-start:17px; } +.jcenter * { inset-inline-start:8.5px; } +.hr.jleft * , .vlr.jleft * { inset-inline-start:17px; } +.hl.jright * , .vrl.jright * , .vl.jright * , .vr.jright * { inset-inline-start:17px; } -.jspace-between item2 { offset-inline-start:8.5px; } -.jspace-between item3 { offset-inline-start:17px; } +.jspace-between item2 { inset-inline-start:8.5px; } +.jspace-between item3 { inset-inline-start:17px; } -.jspace-around item1 { offset-inline-start:2.85px; } -.jspace-around item2 { offset-inline-start:8.5px; } -.jspace-around item3 { offset-inline-start:14.16px; } +.jspace-around item1 { inset-inline-start:2.85px; } +.jspace-around item2 { inset-inline-start:8.5px; } +.jspace-around item3 { inset-inline-start:14.16px; } -.jspace-evenly item1 { offset-inline-start:4.25px; } -.jspace-evenly item2 { offset-inline-start:8.5px; } -.jspace-evenly item3 { offset-inline-start:12.75px; } +.jspace-evenly item1 { inset-inline-start:4.25px; } +.jspace-evenly item2 { inset-inline-start:8.5px; } +.jspace-evenly item3 { inset-inline-start:12.75px; } .jstretch2 { grid-template-columns:1fr 7px 5px; } .jstretch3 { grid-template-columns:19.5px 15.5px 5px; } diff --git a/layout/reftests/css-grid/grid-row-gap-001-ref.html b/layout/reftests/css-grid/grid-row-gap-001-ref.html index cfa815514afc..34b8b1ddb497 100644 --- a/layout/reftests/css-grid/grid-row-gap-001-ref.html +++ b/layout/reftests/css-grid/grid-row-gap-001-ref.html @@ -45,20 +45,20 @@ item3 { grid-area: 5 / 3; } .vlr { writing-mode: vertical-lr; direction:rtl; } .vrl { writing-mode: vertical-rl; direction:ltr; } -.aend *, .aflexend * { offset-block-start:23px; } -.acenter * { offset-block-start:11.5px; } +.aend *, .aflexend * { inset-block-start:23px; } +.acenter * { inset-block-start:11.5px; } -.aspace-between item2 { offset-block-start:11.5px; } -.aspace-between item3 { offset-block-start:23px; } +.aspace-between item2 { inset-block-start:11.5px; } +.aspace-between item3 { inset-block-start:23px; } -.aspace-around item1 { offset-block-start:4.1666px; } -.aspace-around item2 { offset-block-start:11.5px; } -.aspace-around item3 { offset-block-start:18.8333px; } +.aspace-around item1 { inset-block-start:4.1666px; } +.aspace-around item2 { inset-block-start:11.5px; } +.aspace-around item3 { inset-block-start:18.8333px; } -.aspace-evenly item1 { offset-block-start:6.25px; } -.aspace-evenly item2 { offset-block-start:11.5px; } -.aspace-evenly item3 { offset-block-start:16.75px; } +.aspace-evenly item1 { inset-block-start:6.25px; } +.aspace-evenly item2 { inset-block-start:11.5px; } +.aspace-evenly item3 { inset-block-start:16.75px; } .astretch2 { grid-template-rows: 1fr 1px 5px 1px 7px; } .astretch3 { grid-template-rows: 14.5px 1px 16.3333px 1px 7px; } diff --git a/layout/reftests/css-grid/rtl-grid-placement-auto-row-sparse-001-ref.html b/layout/reftests/css-grid/rtl-grid-placement-auto-row-sparse-001-ref.html index 205e718f9502..9200835bfdcf 100644 --- a/layout/reftests/css-grid/rtl-grid-placement-auto-row-sparse-001-ref.html +++ b/layout/reftests/css-grid/rtl-grid-placement-auto-row-sparse-001-ref.html @@ -21,126 +21,126 @@ span { } .test1 .a { - offset-block-start: 0; - offset-inline-start: 20px; + inset-block-start: 0; + inset-inline-start: 20px; inline-size: 20px; block-size: 20px; } .test1 .b { - offset-block-start: 20px; - offset-inline-start: 20px; + inset-block-start: 20px; + inset-inline-start: 20px; inline-size: 60px; block-size: 20px; } .test1 .c { - offset-block-start: 0; - offset-inline-start: 80px; + inset-block-start: 0; + inset-inline-start: 80px; inline-size: 60px; block-size: 40px; } .test1 .e { - offset-block-start: 20px; - offset-inline-start: 0px; + inset-block-start: 20px; + inset-inline-start: 0px; } .test1 .d2 { - offset-block-start: 0px; - offset-inline-start: 40px; + inset-block-start: 0px; + inset-inline-start: 40px; } .test2 .a { - offset-block-start: 0; - offset-inline-start: 20px; + inset-block-start: 0; + inset-inline-start: 20px; inline-size: 60px; block-size: 20px; } .test2 .b { - offset-block-start: 20px; - offset-inline-start: 20px; + inset-block-start: 20px; + inset-inline-start: 20px; inline-size: 20px; block-size: 20px; } .test2 .c { - offset-block-start: 0; - offset-inline-start: 80px; + inset-block-start: 0; + inset-inline-start: 80px; inline-size: 60px; block-size: 40px; } .test2 .e { - offset-block-start: 20px; - offset-inline-start: 0px; + inset-block-start: 20px; + inset-inline-start: 0px; } .test2 .d2 { - offset-block-start: 20px; - offset-inline-start: 40px; + inset-block-start: 20px; + inset-inline-start: 40px; } .test3 .a { - offset-block-start: 0; - offset-inline-start: 0; + inset-block-start: 0; + inset-inline-start: 0; inline-size: 60px; block-size: 40px; } .test3 .b { - offset-block-start: 40px; - offset-inline-start: 20px; + inset-block-start: 40px; + inset-inline-start: 20px; inline-size: 60px; block-size: 20px; } .test3 .c { - offset-block-start: 60px; - offset-inline-start: 0px; + inset-block-start: 60px; + inset-inline-start: 0px; inline-size: 60px; block-size: 40px; } -.test3 .d { offset-block-start: 60px; offset-inline-start:60px; } +.test3 .d { inset-block-start: 60px; inset-inline-start:60px; } .test3 .e { - offset-block-start: 20px; - offset-inline-start: 60px; + inset-block-start: 20px; + inset-inline-start: 60px; } .test3 .d2 { - offset-block-start: 80px; - offset-inline-start: 60px; + inset-block-start: 80px; + inset-inline-start: 60px; } .test4 .c { - offset-block-start: 0; - offset-inline-start: 20px; + inset-block-start: 0; + inset-inline-start: 20px; inline-size: 60px; block-size: 40px; } -.test4 .d { offset-block-start: 40px; } +.test4 .d { inset-block-start: 40px; } .test4 .e { - offset-block-start: 20px; - offset-inline-start: 0px; + inset-block-start: 20px; + inset-inline-start: 0px; } .test4 .d2 { - offset-block-start: 40px; - offset-inline-start: 20px; + inset-block-start: 40px; + inset-inline-start: 20px; } .test5 .c { - offset-block-start: 20px; - offset-inline-start: 0; + inset-block-start: 20px; + inset-inline-start: 0; inline-size: 60px; block-size: 20px; } .test5 .e { - offset-block-start: 20px; - offset-inline-start: 60px; + inset-block-start: 20px; + inset-inline-start: 60px; } .test5 .d2 { - offset-block-start: 0px; - offset-inline-start: 20px; + inset-block-start: 0px; + inset-inline-start: 20px; } -.test6 { offset-inline-start: 0px; offset-block-start: 20px;} +.test6 { inset-inline-start: 0px; inset-block-start: 20px;} .test6d2 { - offset-block-start: 20px; - offset-inline-start: 140px; + inset-block-start: 20px; + inset-inline-start: 140px; } .test6e { - offset-block-start: 20px; - offset-inline-start: 160px; + inset-block-start: 20px; + inset-inline-start: 160px; } diff --git a/layout/reftests/css-grid/rtl-grid-placement-definite-001-ref.html b/layout/reftests/css-grid/rtl-grid-placement-definite-001-ref.html index 743b917efaf7..70287b3ab785 100644 --- a/layout/reftests/css-grid/rtl-grid-placement-definite-001-ref.html +++ b/layout/reftests/css-grid/rtl-grid-placement-definite-001-ref.html @@ -24,26 +24,26 @@ span { } .a { - offset-inline-start: 40px; - offset-block-start: 0px; + inset-inline-start: 40px; + inset-block-start: 0px; inline-size: 60px; block-size: 40px; } .b { - offset-inline-start: 20px; - offset-block-start: 40px; + inset-inline-start: 20px; + inset-block-start: 40px; inline-size: 60px; block-size: 20px; } .c { - offset-inline-start: 80px; - offset-block-start: 40px; + inset-inline-start: 80px; + inset-block-start: 40px; inline-size: 60px; block-size: 20px; } .d { - offset-inline-start: 0px; - offset-block-start: 0px; + inset-inline-start: 0px; + inset-block-start: 0px; inline-size: 20px; block-size: 60px; } diff --git a/layout/reftests/css-grid/vlr-grid-placement-auto-row-sparse-001-ref.html b/layout/reftests/css-grid/vlr-grid-placement-auto-row-sparse-001-ref.html index bc90a09556d6..4a1e6f81e0a1 100644 --- a/layout/reftests/css-grid/vlr-grid-placement-auto-row-sparse-001-ref.html +++ b/layout/reftests/css-grid/vlr-grid-placement-auto-row-sparse-001-ref.html @@ -25,126 +25,126 @@ span { } .test1 .a { - offset-block-start: 0; - offset-inline-start: 20px; + inset-block-start: 0; + inset-inline-start: 20px; inline-size: 20px; block-size: 20px; } .test1 .b { - offset-block-start: 20px; - offset-inline-start: 20px; + inset-block-start: 20px; + inset-inline-start: 20px; inline-size: 60px; block-size: 20px; } .test1 .c { - offset-block-start: 0; - offset-inline-start: 80px; + inset-block-start: 0; + inset-inline-start: 80px; inline-size: 60px; block-size: 40px; } .test1 .e { - offset-block-start: 20px; - offset-inline-start: 0px; + inset-block-start: 20px; + inset-inline-start: 0px; } .test1 .d2 { - offset-block-start: 0px; - offset-inline-start: 40px; + inset-block-start: 0px; + inset-inline-start: 40px; } .test2 .a { - offset-block-start: 0; - offset-inline-start: 20px; + inset-block-start: 0; + inset-inline-start: 20px; inline-size: 60px; block-size: 20px; } .test2 .b { - offset-block-start: 20px; - offset-inline-start: 20px; + inset-block-start: 20px; + inset-inline-start: 20px; inline-size: 20px; block-size: 20px; } .test2 .c { - offset-block-start: 0; - offset-inline-start: 80px; + inset-block-start: 0; + inset-inline-start: 80px; inline-size: 60px; block-size: 40px; } .test2 .e { - offset-block-start: 20px; - offset-inline-start: 0px; + inset-block-start: 20px; + inset-inline-start: 0px; } .test2 .d2 { - offset-block-start: 20px; - offset-inline-start: 40px; + inset-block-start: 20px; + inset-inline-start: 40px; } .test3 .a { - offset-block-start: 0; - offset-inline-start: 0; + inset-block-start: 0; + inset-inline-start: 0; inline-size: 60px; block-size: 40px; } .test3 .b { - offset-block-start: 40px; - offset-inline-start: 20px; + inset-block-start: 40px; + inset-inline-start: 20px; inline-size: 60px; block-size: 20px; } .test3 .c { - offset-block-start: 60px; - offset-inline-start: 0px; + inset-block-start: 60px; + inset-inline-start: 0px; inline-size: 60px; block-size: 40px; } -.test3 .d { offset-block-start: 60px; offset-inline-start:60px; } +.test3 .d { inset-block-start: 60px; inset-inline-start:60px; } .test3 .e { - offset-block-start: 20px; - offset-inline-start: 60px; + inset-block-start: 20px; + inset-inline-start: 60px; } .test3 .d2 { - offset-block-start: 80px; - offset-inline-start: 60px; + inset-block-start: 80px; + inset-inline-start: 60px; } .test4 .c { - offset-block-start: 0; - offset-inline-start: 20px; + inset-block-start: 0; + inset-inline-start: 20px; inline-size: 60px; block-size: 40px; } -.test4 .d { offset-block-start: 40px; } +.test4 .d { inset-block-start: 40px; } .test4 .e { - offset-block-start: 20px; - offset-inline-start: 0px; + inset-block-start: 20px; + inset-inline-start: 0px; } .test4 .d2 { - offset-block-start: 40px; - offset-inline-start: 20px; + inset-block-start: 40px; + inset-inline-start: 20px; } .test5 .c { - offset-block-start: 20px; - offset-inline-start: 0; + inset-block-start: 20px; + inset-inline-start: 0; inline-size: 60px; block-size: 20px; } .test5 .e { - offset-block-start: 20px; - offset-inline-start: 60px; + inset-block-start: 20px; + inset-inline-start: 60px; } .test5 .d2 { - offset-block-start: 0px; - offset-inline-start: 20px; + inset-block-start: 0px; + inset-inline-start: 20px; } -.test6 { offset-inline-start: 0px; offset-block-start: 20px;} +.test6 { inset-inline-start: 0px; inset-block-start: 20px;} .test6d2 { - offset-block-start: 20px; - offset-inline-start: 140px; + inset-block-start: 20px; + inset-inline-start: 140px; } .test6e { - offset-block-start: 20px; - offset-inline-start: 160px; + inset-block-start: 20px; + inset-inline-start: 160px; } diff --git a/layout/reftests/css-grid/vrl-grid-placement-auto-row-sparse-001-ref.html b/layout/reftests/css-grid/vrl-grid-placement-auto-row-sparse-001-ref.html index 7d2fe1e9e70f..a495cceffc48 100644 --- a/layout/reftests/css-grid/vrl-grid-placement-auto-row-sparse-001-ref.html +++ b/layout/reftests/css-grid/vrl-grid-placement-auto-row-sparse-001-ref.html @@ -25,144 +25,144 @@ span { } .test1 .a { - offset-block-start: 0; - offset-inline-start: 20px; + inset-block-start: 0; + inset-inline-start: 20px; inline-size: 20px; block-size: 20px; } .test1 .b { - offset-block-start: 20px; - offset-inline-start: 20px; + inset-block-start: 20px; + inset-inline-start: 20px; inline-size: 60px; block-size: 20px; } .test1 .c { - offset-block-start: 0; - offset-inline-start: 80px; + inset-block-start: 0; + inset-inline-start: 80px; inline-size: 60px; block-size: 40px; } .test1 .d { - offset-block-start: 0; - offset-inline-start: 0; + inset-block-start: 0; + inset-inline-start: 0; } .test1 .e { - offset-block-start: 20px; - offset-inline-start: 0; + inset-block-start: 20px; + inset-inline-start: 0; } .test1 .d2 { - offset-block-start: 0; - offset-inline-start: 40px; + inset-block-start: 0; + inset-inline-start: 40px; } .test2 .a { - offset-block-start: 0; - offset-inline-start: 20px; + inset-block-start: 0; + inset-inline-start: 20px; inline-size: 60px; block-size: 20px; } .test2 .b { - offset-block-start: 20px; - offset-inline-start: 20px; + inset-block-start: 20px; + inset-inline-start: 20px; inline-size: 20px; block-size: 20px; } .test2 .c { - offset-block-start: 0; - offset-inline-start: 80px; + inset-block-start: 0; + inset-inline-start: 80px; inline-size: 60px; block-size: 40px; } .test2 .d { - offset-block-start: 0; - offset-inline-start: 0; + inset-block-start: 0; + inset-inline-start: 0; } .test2 .e { - offset-block-start: 20px; - offset-inline-start: 0; + inset-block-start: 20px; + inset-inline-start: 0; } .test2 .d2 { - offset-block-start: 20px; - offset-inline-start: 40px; + inset-block-start: 20px; + inset-inline-start: 40px; } .test3 .a { - offset-block-start: 0; - offset-inline-start: 0; + inset-block-start: 0; + inset-inline-start: 0; inline-size: 60px; block-size: 40px; } .test3 .b { - offset-block-start: 40px; - offset-inline-start: 20px; + inset-block-start: 40px; + inset-inline-start: 20px; inline-size: 60px; block-size: 20px; } .test3 .c { - offset-block-start: 60px; - offset-inline-start: 0; + inset-block-start: 60px; + inset-inline-start: 0; inline-size: 60px; block-size: 40px; } -.test3 .d { offset-block-start: 60px; offset-inline-start:60px; } +.test3 .d { inset-block-start: 60px; inset-inline-start:60px; } .test3 .e { - offset-block-start: 20px; - offset-inline-start: 60px; + inset-block-start: 20px; + inset-inline-start: 60px; } .test3 .d2 { - offset-block-start: 80px; - offset-inline-start: 60px; + inset-block-start: 80px; + inset-inline-start: 60px; } .test4 .c { - offset-block-start: 0; - offset-inline-start: 20px; + inset-block-start: 0; + inset-inline-start: 20px; inline-size: 60px; block-size: 40px; } .test4 .d { - offset-block-start: 40px; - offset-inline-start: 0; + inset-block-start: 40px; + inset-inline-start: 0; } .test4 .e { - offset-block-start: 20px; - offset-inline-start: 0px; + inset-block-start: 20px; + inset-inline-start: 0px; } .test4 .d2 { - offset-block-start: 40px; - offset-inline-start: 20px; + inset-block-start: 40px; + inset-inline-start: 20px; } .test5 .c { - offset-block-start: 20px; - offset-inline-start: 0; + inset-block-start: 20px; + inset-inline-start: 0; inline-size: 60px; block-size: 20px; } .test5 .d { - offset-block-start: 0; - offset-inline-start: 0; + inset-block-start: 0; + inset-inline-start: 0; } .test5 .e { - offset-block-start: 20px; - offset-inline-start: 60px; + inset-block-start: 20px; + inset-inline-start: 60px; } .test5 .d2 { - offset-block-start: 0; - offset-inline-start: 20px; + inset-block-start: 0; + inset-inline-start: 20px; } .test6 { - offset-inline-start: 0; - offset-block-start: 20px; + inset-inline-start: 0; + inset-block-start: 20px; } .test6d2 { - offset-block-start: 20px; - offset-inline-start: 140px; + inset-block-start: 20px; + inset-inline-start: 140px; } .test6e { - offset-block-start: 20px; - offset-inline-start: 160px; + inset-block-start: 20px; + inset-inline-start: 160px; } diff --git a/layout/reftests/font-features/1376231-vertical-gpos-adjustments-ref.html b/layout/reftests/font-features/1376231-vertical-gpos-adjustments-ref.html index a9f45104650f..7e5108e4807a 100644 --- a/layout/reftests/font-features/1376231-vertical-gpos-adjustments-ref.html +++ b/layout/reftests/font-features/1376231-vertical-gpos-adjustments-ref.html @@ -27,7 +27,7 @@ span { } .overlay { position: relative; - offset-block-start: -3em; + inset-block-start: -3em; } diff --git a/layout/reftests/font-features/1376231-vertical-gpos-adjustments.html b/layout/reftests/font-features/1376231-vertical-gpos-adjustments.html index bb811accbe08..0b0f9b9da21b 100644 --- a/layout/reftests/font-features/1376231-vertical-gpos-adjustments.html +++ b/layout/reftests/font-features/1376231-vertical-gpos-adjustments.html @@ -52,7 +52,7 @@ span { } .overlay { position: relative; - offset-block-start: -3em; + inset-block-start: -3em; } diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-005-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-005-ref.html index fd1d0ff91770..6c56ff4de202 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-005-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-005-ref.html @@ -44,11 +44,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-006-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-006-ref.html index efe1c47cba6f..2f4ab3825798 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-006-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-006-ref.html @@ -45,11 +45,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-007-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-007-ref.html index 9e0e6f04447d..1a1b0e39cf28 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-007-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-007-ref.html @@ -44,11 +44,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-008-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-008-ref.html index 41dbcd228721..bf155edd2a09 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-008-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-008-ref.html @@ -45,11 +45,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-009-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-009-ref.html index 713ce3441a61..0dde92c78b85 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-009-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-009-ref.html @@ -44,11 +44,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-010-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-010-ref.html index 776a3608d455..00ce96cf5b45 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-010-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-010-ref.html @@ -45,11 +45,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-011-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-011-ref.html index f8fa6f2d451b..7097a6452e63 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-011-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-011-ref.html @@ -44,11 +44,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-012-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-012-ref.html index 61d97e7dca69..775d984d1d9c 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-012-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-border-box-border-radius-012-ref.html @@ -45,11 +45,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-048-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-048-ref.html index 5ffdb55b9a63..668f2a8dafb5 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-048-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-048-ref.html @@ -43,12 +43,12 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-049-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-049-ref.html index 2024166b29ef..d38adc31d443 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-049-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-049-ref.html @@ -44,12 +44,12 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-050-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-050-ref.html index 58d33349475f..54359ed03388 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-050-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-050-ref.html @@ -43,12 +43,12 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-051-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-051-ref.html index 21ac48b5e12a..24fdb7fcf5b3 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-051-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-051-ref.html @@ -43,12 +43,12 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-052-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-052-ref.html index 3e80b9422ec7..a035a5b3e07b 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-052-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-052-ref.html @@ -43,12 +43,12 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-053-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-053-ref.html index 843b9ff5e92d..7fac41372816 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-053-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-053-ref.html @@ -44,12 +44,12 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-054-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-054-ref.html index 16a36fc7b071..8dc291eda68c 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-054-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-054-ref.html @@ -43,12 +43,12 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-055-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-055-ref.html index c2cea3d8acfa..cd7f9b282ab9 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-055-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-circle-055-ref.html @@ -44,12 +44,12 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-046-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-046-ref.html index 8af751a26f72..85934258804d 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-046-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-046-ref.html @@ -38,9 +38,9 @@
-
-
-
-
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-047-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-047-ref.html index c6ff6200c5f7..d6c4215b4b19 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-047-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-047-ref.html @@ -39,9 +39,9 @@
-
-
-
-
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-048-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-048-ref.html index 06a9c9b7d55e..0cb81c7d9850 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-048-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-048-ref.html @@ -38,9 +38,9 @@
-
-
-
-
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-049-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-049-ref.html index 373ab729f606..494edc842308 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-049-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-049-ref.html @@ -39,9 +39,9 @@
-
-
-
-
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-050-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-050-ref.html index 6f80fd306641..5acbca772afe 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-050-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-050-ref.html @@ -38,9 +38,9 @@
-
-
-
-
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-051-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-051-ref.html index 71e0e7bd42b3..d3bc6615b438 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-051-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-ellipse-051-ref.html @@ -39,9 +39,9 @@
-
-
-
-
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-020-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-020-ref.html index 3f8ff9842f0e..6d2121ca953b 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-020-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-020-ref.html @@ -41,11 +41,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-021-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-021-ref.html index 1514264eab16..daf161d82cbb 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-021-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-021-ref.html @@ -42,11 +42,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-022-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-022-ref.html index 8601aa51a0ee..4f0378b00f81 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-022-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-022-ref.html @@ -41,11 +41,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-023-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-023-ref.html index 8db2ae935510..f6e655f84bb5 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-023-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-023-ref.html @@ -42,11 +42,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-024-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-024-ref.html index 0b4953462303..8fdfb4b30489 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-024-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-024-ref.html @@ -41,11 +41,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-025-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-025-ref.html index 0b9858a06f0b..66b835f21c7e 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-025-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-025-ref.html @@ -42,11 +42,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-026-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-026-ref.html index 9321e35858a4..fbee45dd14bf 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-026-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-026-ref.html @@ -41,11 +41,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-027-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-027-ref.html index 477e4a5acc90..24ae7ee64607 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-027-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-inset-027-ref.html @@ -42,11 +42,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-020-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-020-ref.html index 41ed8bd22b3b..f7287a1fcaac 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-020-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-020-ref.html @@ -41,11 +41,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-021-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-021-ref.html index 6c827c0265ad..9e0cc4889f39 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-021-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-021-ref.html @@ -42,11 +42,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-022-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-022-ref.html index f29267edcdd6..40cb31fb46bc 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-022-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-022-ref.html @@ -41,11 +41,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-023-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-023-ref.html index 17f97781aa9e..445129dcf7ef 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-023-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-023-ref.html @@ -42,11 +42,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-024-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-024-ref.html index 75ab17f1283d..aba8605f5b9d 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-024-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-024-ref.html @@ -41,11 +41,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-025-ref.html b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-025-ref.html index 35d5d7bf8911..6a334c5d1dac 100644 --- a/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-025-ref.html +++ b/layout/reftests/w3c-css/submitted/shapes1/shape-outside-polygon-025-ref.html @@ -42,11 +42,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/layout/style/res/html.css b/layout/style/res/html.css index e189d5284dcf..9c327252f468 100644 --- a/layout/style/res/html.css +++ b/layout/style/res/html.css @@ -819,8 +819,8 @@ details > summary:first-of-type > *|* { dialog { position: absolute; - offset-inline-start: 0; - offset-inline-end: 0; + inset-inline-start: 0; + inset-inline-end: 0; color: black; margin: auto; border-width: initial; diff --git a/layout/style/test/gtest/example.css b/layout/style/test/gtest/example.css index 0c09dccda729..a124db5a4e5b 100644 --- a/layout/style/test/gtest/example.css +++ b/layout/style/test/gtest/example.css @@ -2278,7 +2278,7 @@ html[dir="rtl"] .editor-mount { .expression-container .close-btn { position: absolute; - offset-inline-end: 6px; + inset-inline-end: 6px; top: 6px; } @@ -2411,7 +2411,7 @@ html .breakpoints-list .breakpoint.paused { .breakpoint .close-btn { position: absolute; - offset-inline-end: 6px; + inset-inline-end: 6px; top: 12px; } diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index ac72e06dfe67..91e3e57c9d2e 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -5957,8 +5957,68 @@ var gCSSProperties = { ], invalid_values: [ "none", "5" ] }, - "offset-block-end": { - domProp: "offsetBlockEnd", + "inset-block-end": { + domProp: "insetBlockEnd", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* FIXME: run tests with multiple prerequisites */ + prerequisites: { "position": "relative" }, + /* XXX 0 may or may not be equal to auto */ + initial_values: [ "auto" ], + other_values: [ "32px", "-3em", "12%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [] + }, + "inset-block-start": { + domProp: "insetBlockStart", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* FIXME: run tests with multiple prerequisites */ + prerequisites: { "position": "relative" }, + /* XXX 0 may or may not be equal to auto */ + initial_values: [ "auto" ], + other_values: [ "32px", "-3em", "12%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [] + }, + "inset-inline-end": { + domProp: "insetInlineEnd", + inherited: false, + type: CSS_TYPE_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + /* FIXME: run tests with multiple prerequisites */ + prerequisites: { "position": "relative" }, + /* XXX 0 may or may not be equal to auto */ + initial_values: [ "auto" ], + other_values: [ "32px", "-3em", "12%", + "calc(2px)", + "calc(-2px)", + "calc(50%)", + "calc(3*25px)", + "calc(25px*3)", + "calc(3*25px + 50%)", + ], + invalid_values: [] + }, + "inset-inline-start": { + domProp: "insetInlineStart", inherited: false, type: CSS_TYPE_LONGHAND, logical: true, @@ -5980,62 +6040,38 @@ var gCSSProperties = { "offset-block-start": { domProp: "offsetBlockStart", inherited: false, - type: CSS_TYPE_LONGHAND, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, logical: true, get_computed: logical_box_prop_get_computed, - /* FIXME: run tests with multiple prerequisites */ - prerequisites: { "position": "relative" }, - /* XXX 0 may or may not be equal to auto */ - initial_values: [ "auto" ], - other_values: [ "32px", "-3em", "12%", - "calc(2px)", - "calc(-2px)", - "calc(50%)", - "calc(3*25px)", - "calc(25px*3)", - "calc(3*25px + 50%)", - ], - invalid_values: [] + alias_for: "inset-block-start", + subproperties: [ "inset-block-start" ], }, - "offset-inline-end": { - domProp: "offsetInlineEnd", + "offset-block-end": { + domProp: "offsetBlockEnd", inherited: false, - type: CSS_TYPE_LONGHAND, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, logical: true, get_computed: logical_box_prop_get_computed, - /* FIXME: run tests with multiple prerequisites */ - prerequisites: { "position": "relative" }, - /* XXX 0 may or may not be equal to auto */ - initial_values: [ "auto" ], - other_values: [ "32px", "-3em", "12%", - "calc(2px)", - "calc(-2px)", - "calc(50%)", - "calc(3*25px)", - "calc(25px*3)", - "calc(3*25px + 50%)", - ], - invalid_values: [] + alias_for: "inset-block-end", + subproperties: [ "inset-block-end" ], }, "offset-inline-start": { domProp: "offsetInlineStart", inherited: false, - type: CSS_TYPE_LONGHAND, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, logical: true, get_computed: logical_box_prop_get_computed, - /* FIXME: run tests with multiple prerequisites */ - prerequisites: { "position": "relative" }, - /* XXX 0 may or may not be equal to auto */ - initial_values: [ "auto" ], - other_values: [ "32px", "-3em", "12%", - "calc(2px)", - "calc(-2px)", - "calc(50%)", - "calc(3*25px)", - "calc(25px*3)", - "calc(3*25px + 50%)", - ], - invalid_values: [] + alias_for: "inset-inline-start", + subproperties: [ "inset-inline-start" ], + }, + "offset-block-end": { + domProp: "offsetInlineEnd", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + alias_for: "inset-inline-end", + subproperties: [ "inset-inline-end" ], }, "padding-block-end": { domProp: "paddingBlockEnd", @@ -6195,8 +6231,8 @@ function logical_box_prop_get_computed(cs, property) if (/^-moz-/.test(property)) { property = physicalize(property.substring(5), inlineMapping, ""); - } else if (/^offset-(block|inline)-(start|end)/.test(property)) { - property = property.substring(7); // we want "top" not "offset-top", e.g. + } else if (/^(offset|inset)-(block|inline)-(start|end)/.test(property)) { + property = property.replace("offset-", "").replace("inset-", ""); // we want "top" not "offset-top", e.g. property = physicalize(property, blockMapping, "block-"); property = physicalize(property, inlineMapping, "inline-"); } else if (/-(block|inline)-(start|end)/.test(property)) { diff --git a/layout/style/test/test_logical_properties.html b/layout/style/test/test_logical_properties.html index f1265496dc1c..a6947791cbe3 100644 --- a/layout/style/test/test_logical_properties.html +++ b/layout/style/test/test_logical_properties.html @@ -94,7 +94,7 @@ function init() { type == CSS_TYPE_LONGHAND && gCSSProperties[p].logical) && /-inline-end/.test(p)) { var valueType; - if (/margin|padding|width|offset/.test(p)) { + if (/margin|padding|width|inset|offset/.test(p)) { valueType = "length"; } else if (/color/.test(p)) { valueType = "color"; @@ -110,7 +110,7 @@ function init() { blockEnd: p.replace("-inline-end", "-block-end"), type: valueType }; - if (/^offset/.test(p)) { + if (/^(offset|inset)/.test(p)) { group.left = "left"; group.right = "right"; group.top = "top"; diff --git a/servo/components/style/properties/helpers.mako.rs b/servo/components/style/properties/helpers.mako.rs index 11a1283f1cc0..e07c9bd57cb7 100644 --- a/servo/components/style/properties/helpers.mako.rs +++ b/servo/components/style/properties/helpers.mako.rs @@ -856,7 +856,7 @@ elif len(maybe_size) == 1: size = maybe_size[0] def phys_ident(side, phy_side): - return to_rust_ident(name.replace(side, phy_side).replace("offset-", "")) + return to_rust_ident(name.replace(side, phy_side).replace("inset-", "")) %> % if side is not None: use logical_geometry::PhysicalSide; diff --git a/servo/components/style/properties/longhands/position.mako.rs b/servo/components/style/properties/longhands/position.mako.rs index a99cebfdba99..d7e4e27a3b74 100644 --- a/servo/components/style/properties/longhands/position.mako.rs +++ b/servo/components/style/properties/longhands/position.mako.rs @@ -21,14 +21,15 @@ servo_restyle_damage="reflow_out_of_flow" )} % endfor -// offset-* logical properties, map to "top" / "left" / "bottom" / "right" +// inset-* logical properties, map to "top" / "left" / "bottom" / "right" % for side in LOGICAL_SIDES: ${helpers.predefined_type( - "offset-%s" % side, + "inset-%s" % side, "LengthOrPercentageOrAuto", "computed::LengthOrPercentageOrAuto::Auto", - spec="https://drafts.csswg.org/css-logical-props/#propdef-offset-%s" % side, + spec="https://drafts.csswg.org/css-logical-props/#propdef-inset-%s" % side, flags="GETCS_NEEDS_LAYOUT_FLUSH", + alias="offset-%s" % side, animation_value_type="ComputedValue", logical=True, )} diff --git a/testing/web-platform/tests/web-animations/animation-model/animation-types/property-list.js b/testing/web-platform/tests/web-animations/animation-model/animation-types/property-list.js index f0b2a640c798..cb6e2c810e8f 100644 --- a/testing/web-platform/tests/web-animations/animation-model/animation-types/property-list.js +++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/property-list.js @@ -997,23 +997,23 @@ const gCSSProperties = { types: [ ] }, - 'offset-block-end': { - // https://drafts.csswg.org/css-logical-props/#propdef-offset-block-end + 'inset-block-end': { + // https://drafts.csswg.org/css-logical-props/#propdef-inset-block-end types: [ ] }, - 'offset-block-start': { - // https://drafts.csswg.org/css-logical-props/#propdef-offset-block-start + 'inset-block-start': { + // https://drafts.csswg.org/css-logical-props/#propdef-inset-block-start types: [ ] }, - 'offset-inline-end': { - // https://drafts.csswg.org/css-logical-props/#propdef-offset-inline-end + 'inset-inline-end': { + // https://drafts.csswg.org/css-logical-props/#propdef-inset-inline-end types: [ ] }, - 'offset-inline-start': { - // https://drafts.csswg.org/css-logical-props/#propdef-offset-inline-start + 'inset-inline-start': { + // https://drafts.csswg.org/css-logical-props/#propdef-inset-inline-start types: [ ] }, From e0ca8be6916f4e94aed2cc42d0ad6128a7723077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 26 Jun 2018 20:48:18 +0200 Subject: [PATCH 28/43] Bug 1464782: Update references to offset-* properties in the rest of the tree. r=xidorn MozReview-Commit-ID: AOMoLSJKySy --- browser/base/content/browser.css | 4 +- .../components/SnippetBase/_SnippetBase.scss | 4 +- .../content-src/components/Base/_Base.scss | 2 +- .../content-src/components/Card/_Card.scss | 6 +- .../_CollapsibleSection.scss | 4 +- .../components/ContextMenu/_ContextMenu.scss | 2 +- .../components/Search/_Search.scss | 2 +- .../components/TopSites/_TopSites.scss | 14 +-- .../content-src/styles/_variables.scss | 10 +- .../css/activity-stream-linux.css | 94 +++++++++---------- .../css/activity-stream-linux.css.map | 16 ++-- .../css/activity-stream-mac.css | 94 +++++++++---------- .../css/activity-stream-mac.css.map | 16 ++-- .../css/activity-stream-windows.css | 94 +++++++++---------- .../css/activity-stream-windows.css.map | 16 ++-- .../onboarding/content/onboarding.css | 12 +-- .../shared/incontentprefs/preferences.inc.css | 4 +- .../privatebrowsing/aboutPrivateBrowsing.css | 2 +- .../client/debugger/new/dist/debugger.css | 10 +- devtools/client/themes/common.css | 4 +- devtools/client/themes/fonts.css | 2 +- devtools/client/themes/toolbox.css | 2 +- devtools/docs/contributing/css.md | 2 +- .../server/actors/animation-type-longhand.js | 8 +- devtools/server/actors/highlighters.css | 4 +- 25 files changed, 214 insertions(+), 214 deletions(-) diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 765b5b8f24c5..f511c90b731a 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -990,8 +990,8 @@ browser[tabmodalPromptShowing] { } #statuspanel[mirror] { - offset-inline-start: auto; - offset-inline-end: 0; + inset-inline-start: auto; + inset-inline-end: 0; } #statuspanel[sizelimit] { diff --git a/browser/extensions/activity-stream/content-src/asrouter/components/SnippetBase/_SnippetBase.scss b/browser/extensions/activity-stream/content-src/asrouter/components/SnippetBase/_SnippetBase.scss index 0a4f08120a91..c737072e47fc 100644 --- a/browser/extensions/activity-stream/content-src/asrouter/components/SnippetBase/_SnippetBase.scss +++ b/browser/extensions/activity-stream/content-src/asrouter/components/SnippetBase/_SnippetBase.scss @@ -36,7 +36,7 @@ border: 0; position: absolute; top: 50%; - offset-inline-end: 12px; + inset-inline-end: 12px; height: 16px; width: 16px; background-image: url('resource://activity-stream/data/content/assets/glyph-dismiss-16.svg'); @@ -48,7 +48,7 @@ cursor: pointer; @media (min-width: 766px) { - offset-inline-end: 24px; + inset-inline-end: 24px; } } diff --git a/browser/extensions/activity-stream/content-src/components/Base/_Base.scss b/browser/extensions/activity-stream/content-src/components/Base/_Base.scss index 756aeb404658..99dc2d105c7b 100644 --- a/browser/extensions/activity-stream/content-src/components/Base/_Base.scss +++ b/browser/extensions/activity-stream/content-src/components/Base/_Base.scss @@ -78,7 +78,7 @@ main { border: 0; cursor: pointer; fill: var(--newtab-icon-primary-color); - offset-inline-end: 15px; + inset-inline-end: 15px; padding: 15px; position: fixed; top: 15px; diff --git a/browser/extensions/activity-stream/content-src/components/Card/_Card.scss b/browser/extensions/activity-stream/content-src/components/Card/_Card.scss index 5ea66485e9f2..16325a0c6c8c 100644 --- a/browser/extensions/activity-stream/content-src/components/Card/_Card.scss +++ b/browser/extensions/activity-stream/content-src/components/Card/_Card.scss @@ -150,7 +150,7 @@ color: var(--newtab-text-secondary-color); display: flex; font-size: 11px; - offset-inline-start: 0; + inset-inline-start: 0; padding: 9px 16px 9px 14px; position: absolute; } @@ -264,8 +264,8 @@ width: $container-size; padding: ($container-size - $icon-size) / 2; top: $card-preview-image-height-compact - $icon-size; - offset-inline-end: 12px; - offset-inline-start: auto; + inset-inline-end: 12px; + inset-inline-start: auto; &::after { border: 1px solid var(--newtab-card-hairline-color); diff --git a/browser/extensions/activity-stream/content-src/components/CollapsibleSection/_CollapsibleSection.scss b/browser/extensions/activity-stream/content-src/components/CollapsibleSection/_CollapsibleSection.scss index 83771992d208..037daf8d926e 100644 --- a/browser/extensions/activity-stream/content-src/components/CollapsibleSection/_CollapsibleSection.scss +++ b/browser/extensions/activity-stream/content-src/components/CollapsibleSection/_CollapsibleSection.scss @@ -40,7 +40,7 @@ cursor: pointer; fill: var(--newtab-section-header-text-color); height: 100%; - offset-inline-end: 0; + inset-inline-end: 0; opacity: 0; position: absolute; top: 0; @@ -116,7 +116,7 @@ margin-top: 2px; max-width: $max-button-width; min-height: $min-button-height; - offset-inline-end: 0; + inset-inline-end: 0; &:hover:not(.dismiss) { box-shadow: $shadow-primary; diff --git a/browser/extensions/activity-stream/content-src/components/ContextMenu/_ContextMenu.scss b/browser/extensions/activity-stream/content-src/components/ContextMenu/_ContextMenu.scss index 41450e008ebf..c441ac009982 100644 --- a/browser/extensions/activity-stream/content-src/components/ContextMenu/_ContextMenu.scss +++ b/browser/extensions/activity-stream/content-src/components/ContextMenu/_ContextMenu.scss @@ -5,7 +5,7 @@ display: block; font-size: $context-menu-font-size; margin-inline-start: 5px; - offset-inline-start: 100%; + inset-inline-start: 100%; position: absolute; top: ($context-menu-button-size / 4); z-index: 10000; diff --git a/browser/extensions/activity-stream/content-src/components/Search/_Search.scss b/browser/extensions/activity-stream/content-src/components/Search/_Search.scss index 76336a086b0e..671596e4424c 100644 --- a/browser/extensions/activity-stream/content-src/components/Search/_Search.scss +++ b/browser/extensions/activity-stream/content-src/components/Search/_Search.scss @@ -49,7 +49,7 @@ -moz-context-properties: fill; fill: var(--newtab-search-icon-color); height: 100%; - offset-inline-end: 0; + inset-inline-end: 0; position: absolute; width: $search-button-width; diff --git a/browser/extensions/activity-stream/content-src/components/TopSites/_TopSites.scss b/browser/extensions/activity-stream/content-src/components/TopSites/_TopSites.scss index 0fd6a7ae23d2..11204c0bc071 100644 --- a/browser/extensions/activity-stream/content-src/components/TopSites/_TopSites.scss +++ b/browser/extensions/activity-stream/content-src/components/TopSites/_TopSites.scss @@ -173,7 +173,7 @@ $half-base-gutter: $base-gutter / 2; .rich-icon { background-size: cover; height: 100%; - offset-inline-start: 0; + inset-inline-start: 0; top: 0; width: 100%; } @@ -182,7 +182,7 @@ $half-base-gutter: $base-gutter / 2; background-size: $default-icon-size; bottom: -$default-icon-offset; height: $default-icon-wrapper-size; - offset-inline-end: -$default-icon-offset; + inset-inline-end: -$default-icon-offset; width: $default-icon-wrapper-size; // for corner letter fallback @@ -207,7 +207,7 @@ $half-base-gutter: $base-gutter / 2; .icon { fill: var(--newtab-icon-tertiary-color); - offset-inline-start: 0; + inset-inline-start: 0; position: absolute; top: 10px; } @@ -323,7 +323,7 @@ $half-base-gutter: $base-gutter / 2; position: absolute; transform: translateY(-50%); top: 50%; - offset-inline-end: 8px; + inset-inline-end: 8px; } } @@ -363,7 +363,7 @@ $half-base-gutter: $base-gutter / 2; position: absolute; transform: translateY(-50%); top: 50%; - offset-inline-end: 8px; + inset-inline-end: 8px; } // This animation is derived from Firefox's tab loading animation @@ -427,7 +427,7 @@ $half-base-gutter: $base-gutter / 2; background: $red-60; border-radius: 2px; color: $white; - offset-inline-start: 3px; + inset-inline-start: 3px; padding: 5px 12px; position: absolute; top: 44px; @@ -439,7 +439,7 @@ $half-base-gutter: $base-gutter / 2; bottom: -8px; content: '.'; height: 16px; - offset-inline-start: 12px; + inset-inline-start: 12px; position: absolute; text-indent: -999px; top: -7px; diff --git a/browser/extensions/activity-stream/content-src/styles/_variables.scss b/browser/extensions/activity-stream/content-src/styles/_variables.scss index 2efa8994b105..6da73d21653c 100644 --- a/browser/extensions/activity-stream/content-src/styles/_variables.scss +++ b/browser/extensions/activity-stream/content-src/styles/_variables.scss @@ -138,7 +138,7 @@ $textbox-shadow-size: 4px; cursor: pointer; fill: var(--newtab-icon-primary-color); height: $context-menu-button-size; - offset-inline-end: -($context-menu-button-size / 2); + inset-inline-end: -($context-menu-button-size / 2); opacity: 0; position: absolute; top: -($context-menu-button-size / 2); @@ -165,8 +165,8 @@ $textbox-shadow-size: 4px; .context-menu { margin-inline-end: auto; margin-inline-start: auto; - offset-inline-end: auto; - offset-inline-start: -$base-gutter; + inset-inline-end: auto; + inset-inline-start: -$base-gutter; } } @@ -174,8 +174,8 @@ $textbox-shadow-size: 4px; .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; + inset-inline-end: 0; + inset-inline-start: auto; } } diff --git a/browser/extensions/activity-stream/css/activity-stream-linux.css b/browser/extensions/activity-stream/css/activity-stream-linux.css index 0f6b56ecea25..d3be009250c5 100644 --- a/browser/extensions/activity-stream/css/activity-stream-linux.css +++ b/browser/extensions/activity-stream/css/activity-stream-linux.css @@ -369,7 +369,7 @@ main { border: 0; cursor: pointer; fill: var(--newtab-icon-primary-color); - offset-inline-end: 15px; + inset-inline-end: 15px; padding: 15px; position: fixed; top: 15px; @@ -405,56 +405,56 @@ main { .top-sites-list :nth-child(2n+1) .context-menu { margin-inline-end: auto; margin-inline-start: auto; - offset-inline-end: auto; - offset-inline-start: -32px; } + inset-inline-end: auto; + inset-inline-start: -32px; } .top-sites-list :nth-child(2n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 482px) and (max-width: 610px) { .top-sites-list :nth-child(3n+2) .context-menu, .top-sites-list :nth-child(3n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 610px) and (max-width: 866px) { .top-sites-list :nth-child(4n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 610px) and (max-width: 834px) { .top-sites-list :nth-child(4n+3) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 866px) and (max-width: 1314px) { .top-sites-list :nth-child(6n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 866px) and (max-width: 1090px) { .top-sites-list :nth-child(6n+5) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 1122px) and (max-width: 1570px) { .top-sites-list :nth-child(8n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 1122px) and (max-width: 1346px) { .top-sites-list :nth-child(8n+7) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media not all and (min-width: 1122px) { .top-sites-list .hide-for-narrow { display: none; } } @@ -490,7 +490,7 @@ main { cursor: pointer; fill: var(--newtab-icon-primary-color); height: 27px; - offset-inline-end: -13.5px; + inset-inline-end: -13.5px; opacity: 0; position: absolute; top: -13.5px; @@ -541,14 +541,14 @@ main { .top-site-outer .rich-icon { background-size: cover; height: 100%; - offset-inline-start: 0; + inset-inline-start: 0; top: 0; width: 100%; } .top-site-outer .default-icon { background-size: 32px; bottom: -6px; height: 42px; - offset-inline-end: -6px; + inset-inline-end: -6px; width: 42px; align-items: center; display: flex; @@ -566,7 +566,7 @@ main { position: relative; } .top-site-outer .title .icon { fill: var(--newtab-icon-tertiary-color); - offset-inline-start: 0; + inset-inline-start: 0; position: absolute; top: 10px; } .top-site-outer .title span { @@ -637,7 +637,7 @@ main { position: absolute; transform: translateY(-50%); top: 50%; - offset-inline-end: 8px; } + inset-inline-end: 8px; } .topsite-form .form-wrapper .url input:dir(ltr) { padding-right: 32px; } .topsite-form .form-wrapper .url input:dir(rtl) { @@ -661,7 +661,7 @@ main { position: absolute; transform: translateY(-50%); top: 50%; - offset-inline-end: 8px; } + inset-inline-end: 8px; } .topsite-form .form-wrapper .custom-image-input-container .loading-animation { width: 960px; height: 16px; @@ -702,7 +702,7 @@ main { background: #D70022; border-radius: 2px; color: #FFF; - offset-inline-start: 3px; + inset-inline-start: 3px; padding: 5px 12px; position: absolute; top: 44px; @@ -712,7 +712,7 @@ main { bottom: -8px; content: '.'; height: 16px; - offset-inline-start: 12px; + inset-inline-start: 12px; position: absolute; text-indent: -999px; top: -7px; @@ -750,26 +750,26 @@ main { .sections-list .section-list .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 610px) and (max-width: 866px) { .sections-list .section-list :nth-child(2n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 866px) and (max-width: 1314px) { .sections-list .section-list :nth-child(3n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 1122px) and (max-width: 1570px) { .sections-list .section-list :nth-child(3n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } .sections-list .section-empty-state { border: 1px solid var(--newtab-border-secondary-color); @@ -1099,7 +1099,7 @@ main { -moz-context-properties: fill; fill: var(--newtab-search-icon-color); height: 100%; - offset-inline-end: 0; + inset-inline-end: 0; position: absolute; width: 36px; } .search-wrapper .search-button:focus, .search-wrapper .search-button:hover { @@ -1158,7 +1158,7 @@ main { display: block; font-size: 14px; margin-inline-start: 5px; - offset-inline-start: 100%; + inset-inline-start: 100%; position: absolute; top: 6.75px; z-index: 10000; } @@ -1261,7 +1261,7 @@ main { cursor: pointer; fill: var(--newtab-icon-primary-color); height: 27px; - offset-inline-end: -13.5px; + inset-inline-end: -13.5px; opacity: 0; position: absolute; top: -13.5px; @@ -1369,7 +1369,7 @@ main { color: var(--newtab-text-secondary-color); display: flex; font-size: 11px; - offset-inline-start: 0; + inset-inline-start: 0; padding: 9px 16px 9px 14px; position: absolute; } .card-outer .card-context-icon { @@ -1434,8 +1434,8 @@ main { width: 32px; padding: 8px; top: 92px; - offset-inline-end: 12px; - offset-inline-start: auto; } + inset-inline-end: 12px; + inset-inline-start: auto; } .compact-cards .card-outer .card-context::after { border: 1px solid var(--newtab-card-hairline-color); border-bottom: 0; @@ -1538,7 +1538,7 @@ main { cursor: pointer; fill: var(--newtab-section-header-text-color); height: 100%; - offset-inline-end: 0; + inset-inline-end: 0; opacity: 0; position: absolute; top: 0; @@ -1554,8 +1554,8 @@ main { .collapsible-section .section-top-bar .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } .collapsible-section:hover .section-top-bar .context-menu-button, .collapsible-section.active .section-top-bar .context-menu-button { opacity: 1; } .collapsible-section.active { @@ -1587,7 +1587,7 @@ main { margin-top: 2px; max-width: 130px; min-height: 26px; - offset-inline-end: 0; } + inset-inline-end: 0; } .collapsible-section .section-disclaimer button:hover:not(.dismiss) { box-shadow: 0 0 0 5px var(--newtab-card-active-outline-color); transition: box-shadow 150ms; } @@ -1698,7 +1698,7 @@ main { border: 0; position: absolute; top: 50%; - offset-inline-end: 12px; + inset-inline-end: 12px; height: 16px; width: 16px; background-image: url("resource://activity-stream/data/content/assets/glyph-dismiss-16.svg"); @@ -1710,7 +1710,7 @@ main { cursor: pointer; } @media (min-width: 766px) { .SnippetBaseContainer .blockButton { - offset-inline-end: 24px; } } + inset-inline-end: 24px; } } .SnippetBaseContainer:hover .blockButton { display: block; } diff --git a/browser/extensions/activity-stream/css/activity-stream-linux.css.map b/browser/extensions/activity-stream/css/activity-stream-linux.css.map index 8c444caa0d32..3902c489654c 100644 --- a/browser/extensions/activity-stream/css/activity-stream-linux.css.map +++ b/browser/extensions/activity-stream/css/activity-stream-linux.css.map @@ -31,24 +31,24 @@ "/* This is the linux variant */ // sass-lint:disable-line no-css-comments\n\n$os-infopanel-arrow-height: 10px;\n$os-infopanel-arrow-offset-end: 6px;\n$os-infopanel-arrow-width: 20px;\n\n@import './activity-stream';\n", "@import './normalize';\n@import './variables';\n@import './theme';\n@import './icons';\n\nhtml {\n height: 100%;\n}\n\nbody,\n#root { // sass-lint:disable-line no-ids\n min-height: 100vh;\n}\n\nbody {\n background-color: var(--newtab-background-color);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Ubuntu', 'Helvetica Neue', sans-serif;\n font-size: 16px;\n overflow-y: scroll;\n}\n\nh1,\nh2 {\n font-weight: normal;\n}\n\na {\n text-decoration: none;\n}\n\n// For screen readers\n.sr-only {\n border: 0;\n clip: rect(0, 0, 0, 0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n position: absolute;\n width: 1px;\n}\n\n.inner-border {\n border: $border-secondary;\n border-radius: $border-radius;\n height: 100%;\n left: 0;\n pointer-events: none;\n position: absolute;\n top: 0;\n width: 100%;\n z-index: 100;\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 1;\n }\n}\n\n.show-on-init {\n opacity: 0;\n transition: opacity 0.2s ease-in;\n\n &.on {\n animation: fadeIn 0.2s;\n opacity: 1;\n }\n}\n\n.actions {\n border-top: $border-secondary;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n margin: 0;\n padding: 15px 25px 0;\n}\n\n// Default button (grey)\n.button,\n.actions button {\n background-color: var(--newtab-button-secondary-color);\n border: $border-primary;\n border-radius: 4px;\n color: inherit;\n cursor: pointer;\n margin-bottom: 15px;\n padding: 10px 30px;\n white-space: nowrap;\n\n &:hover:not(.dismiss) {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n }\n\n &.dismiss {\n background-color: transparent;\n border: 0;\n padding: 0;\n text-decoration: underline;\n }\n\n // Blue button\n &.primary,\n &.done {\n background-color: var(--newtab-button-primary-color);\n border: solid 1px var(--newtab-button-primary-color);\n color: $white;\n margin-inline-start: auto;\n }\n}\n\ninput {\n &[type='text'],\n &[type='search'] {\n border-radius: $border-radius;\n }\n}\n\n// Make sure snippets show up above other UI elements\n#snippets-container { // sass-lint:disable-line no-ids\n z-index: 1;\n}\n\n// Components\n@import '../components/Base/Base';\n@import '../components/ErrorBoundary/ErrorBoundary';\n@import '../components/TopSites/TopSites';\n@import '../components/Sections/Sections';\n@import '../components/StartupOverlay/StartupOverlay';\n@import '../components/Topics/Topics';\n@import '../components/Search/Search';\n@import '../components/ContextMenu/ContextMenu';\n@import '../components/ConfirmDialog/ConfirmDialog';\n@import '../components/Card/Card';\n@import '../components/ManualMigration/ManualMigration';\n@import '../components/CollapsibleSection/CollapsibleSection';\n@import '../components/ASRouterAdmin/ASRouterAdmin';\n\n// AS Router\n@import '../asrouter/components/Button/Button';\n@import '../asrouter/components/SnippetBase/SnippetBase';\n@import '../asrouter/components/ModalOverlay/ModalOverlay';\n@import '../asrouter/templates/SimpleSnippet/SimpleSnippet';\n@import '../asrouter/templates/OnboardingMessage/OnboardingMessage';\n", "html {\n box-sizing: border-box;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\n*::-moz-focus-inner {\n border: 0;\n}\n\nbody {\n margin: 0;\n}\n\nbutton,\ninput {\n background-color: inherit;\n color: inherit;\n font-family: inherit;\n font-size: inherit;\n}\n\n[hidden] {\n display: none !important; // sass-lint:disable-line no-important\n}\n", - "// Photon colors from http://design.firefox.com/photon/visuals/color.html\n$blue-40: #45A1FF;\n$blue-50: #0A84FF;\n$blue-60: #0060DF;\n$blue-70: #003EAA;\n$blue-80: #002275;\n$grey-10: #F9F9FA;\n$grey-20: #EDEDF0;\n$grey-30: #D7D7DB;\n$grey-40: #B1B1B3;\n$grey-50: #737373;\n$grey-60: #4A4A4F;\n$grey-70: #38383D;\n$grey-80: #2A2A2E;\n$grey-90: #0C0C0D;\n$teal-70: #008EA4;\n$red-60: #D70022;\n$yellow-50: #FFE900;\n\n// Photon opacity from http://design.firefox.com/photon/visuals/color.html#opacity\n$grey-10-10: rgba($grey-10, 0.1);\n$grey-10-20: rgba($grey-10, 0.2);\n$grey-10-40: rgba($grey-10, 0.4);\n$grey-10-60: rgba($grey-10, 0.6);\n$grey-10-80: rgba($grey-10, 0.8);\n$grey-20-60: rgba($grey-20, 0.6);\n$grey-20-80: rgba($grey-20, 0.8);\n$grey-30-60: rgba($grey-30, 0.6);\n$grey-90-10: rgba($grey-90, 0.1);\n$grey-90-20: rgba($grey-90, 0.2);\n$grey-90-30: rgba($grey-90, 0.3);\n$grey-90-40: rgba($grey-90, 0.4);\n$grey-90-50: rgba($grey-90, 0.5);\n$grey-90-60: rgba($grey-90, 0.6);\n$grey-90-70: rgba($grey-90, 0.7);\n$grey-90-80: rgba($grey-90, 0.8);\n$grey-90-90: rgba($grey-90, 0.9);\n\n$black: #000;\n$black-5: rgba($black, 0.05);\n$black-10: rgba($black, 0.1);\n$black-15: rgba($black, 0.15);\n$black-20: rgba($black, 0.2);\n$black-25: rgba($black, 0.25);\n$black-30: rgba($black, 0.3);\n\n// Other colors\n$white: #FFF;\n$white-10: rgba($white, 0.1);\n$pocket-teal: #50BCB6;\n$bookmark-icon-fill: #0A84FF;\n$download-icon-fill: #12BC00;\n$pocket-icon-fill: #D70022;\n\n// Photon transitions from http://design.firefox.com/photon/motion/duration-and-easing.html\n$photon-easing: cubic-bezier(0.07, 0.95, 0, 1);\n\n$border-radius: 3px;\n\n// Grid related styles\n$base-gutter: 32px;\n$section-horizontal-padding: 25px;\n$section-vertical-padding: 10px;\n$section-spacing: 40px - $section-vertical-padding * 2;\n$grid-unit: 96px; // 1 top site\n\n$icon-size: 16px;\n$smaller-icon-size: 12px;\n$larger-icon-size: 32px;\n\n$wrapper-default-width: $grid-unit * 2 + $base-gutter * 1 + $section-horizontal-padding * 2; // 2 top sites\n$wrapper-max-width-small: $grid-unit * 3 + $base-gutter * 2 + $section-horizontal-padding * 2; // 3 top sites\n$wrapper-max-width-medium: $grid-unit * 4 + $base-gutter * 3 + $section-horizontal-padding * 2; // 4 top sites\n$wrapper-max-width-large: $grid-unit * 6 + $base-gutter * 5 + $section-horizontal-padding * 2; // 6 top sites\n$wrapper-max-width-widest: $grid-unit * 8 + $base-gutter * 7 + $section-horizontal-padding * 2; // 8 top sites\n// For the breakpoints, we need to add space for the scrollbar to avoid weird\n// layout issues when the scrollbar is visible. 16px is wide enough to cover all\n// OSes and keeps it simpler than a per-OS value.\n$scrollbar-width: 16px;\n$break-point-small: $wrapper-max-width-small + $base-gutter * 2 + $scrollbar-width;\n$break-point-medium: $wrapper-max-width-medium + $base-gutter * 2 + $scrollbar-width;\n$break-point-large: $wrapper-max-width-large + $base-gutter * 2 + $scrollbar-width;\n$break-point-widest: $wrapper-max-width-widest + $base-gutter * 2 + $scrollbar-width;\n\n$section-title-font-size: 13px;\n\n$card-width: $grid-unit * 2 + $base-gutter;\n$card-height: 266px;\n$card-preview-image-height: 122px;\n$card-title-margin: 2px;\n$card-text-line-height: 19px;\n// Larger cards for wider screens:\n$card-width-large: 309px;\n$card-height-large: 370px;\n$card-preview-image-height-large: 155px;\n// Compact cards for Highlights\n$card-height-compact: 160px;\n$card-preview-image-height-compact: 108px;\n\n$topic-margin-top: 12px;\n\n$context-menu-button-size: 27px;\n$context-menu-button-boxshadow: 0 2px $grey-90-10;\n$context-menu-shadow: 0 5px 10px $black-30, 0 0 0 1px $black-20;\n$context-menu-font-size: 14px;\n$context-menu-border-radius: 5px;\n$context-menu-outer-padding: 5px;\n$context-menu-item-padding: 3px 12px;\n\n$error-fallback-font-size: 12px;\n$error-fallback-line-height: 1.5;\n\n$image-path: '../data/content/assets/';\n\n$snippets-container-height: 120px;\n\n$textbox-shadow-size: 4px;\n\n@mixin fade-in {\n box-shadow: inset $inner-box-shadow, $shadow-primary;\n transition: box-shadow 150ms;\n}\n\n@mixin fade-in-card {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n}\n\n@mixin context-menu-button {\n .context-menu-button {\n background-clip: padding-box;\n background-color: var(--newtab-contextmenu-button-color);\n background-image: url('chrome://browser/skin/page-action.svg');\n background-position: 55%;\n border: $border-primary;\n border-radius: 100%;\n box-shadow: $context-menu-button-boxshadow;\n cursor: pointer;\n fill: var(--newtab-icon-primary-color);\n height: $context-menu-button-size;\n offset-inline-end: -($context-menu-button-size / 2);\n opacity: 0;\n position: absolute;\n top: -($context-menu-button-size / 2);\n transform: scale(0.25);\n transition-duration: 200ms;\n transition-property: transform, opacity;\n width: $context-menu-button-size;\n\n &:-moz-any(:active, :focus) {\n opacity: 1;\n transform: scale(1);\n }\n }\n}\n\n@mixin context-menu-button-hover {\n .context-menu-button {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@mixin context-menu-open-middle {\n .context-menu {\n margin-inline-end: auto;\n margin-inline-start: auto;\n offset-inline-end: auto;\n offset-inline-start: -$base-gutter;\n }\n}\n\n@mixin context-menu-open-left {\n .context-menu {\n margin-inline-end: 5px;\n margin-inline-start: auto;\n offset-inline-end: 0;\n offset-inline-start: auto;\n }\n}\n\n@mixin flip-icon {\n &:dir(rtl) {\n transform: scaleX(-1);\n }\n}\n", + "// Photon colors from http://design.firefox.com/photon/visuals/color.html\n$blue-40: #45A1FF;\n$blue-50: #0A84FF;\n$blue-60: #0060DF;\n$blue-70: #003EAA;\n$blue-80: #002275;\n$grey-10: #F9F9FA;\n$grey-20: #EDEDF0;\n$grey-30: #D7D7DB;\n$grey-40: #B1B1B3;\n$grey-50: #737373;\n$grey-60: #4A4A4F;\n$grey-70: #38383D;\n$grey-80: #2A2A2E;\n$grey-90: #0C0C0D;\n$teal-70: #008EA4;\n$red-60: #D70022;\n$yellow-50: #FFE900;\n\n// Photon opacity from http://design.firefox.com/photon/visuals/color.html#opacity\n$grey-10-10: rgba($grey-10, 0.1);\n$grey-10-20: rgba($grey-10, 0.2);\n$grey-10-40: rgba($grey-10, 0.4);\n$grey-10-60: rgba($grey-10, 0.6);\n$grey-10-80: rgba($grey-10, 0.8);\n$grey-20-60: rgba($grey-20, 0.6);\n$grey-20-80: rgba($grey-20, 0.8);\n$grey-30-60: rgba($grey-30, 0.6);\n$grey-90-10: rgba($grey-90, 0.1);\n$grey-90-20: rgba($grey-90, 0.2);\n$grey-90-30: rgba($grey-90, 0.3);\n$grey-90-40: rgba($grey-90, 0.4);\n$grey-90-50: rgba($grey-90, 0.5);\n$grey-90-60: rgba($grey-90, 0.6);\n$grey-90-70: rgba($grey-90, 0.7);\n$grey-90-80: rgba($grey-90, 0.8);\n$grey-90-90: rgba($grey-90, 0.9);\n\n$black: #000;\n$black-5: rgba($black, 0.05);\n$black-10: rgba($black, 0.1);\n$black-15: rgba($black, 0.15);\n$black-20: rgba($black, 0.2);\n$black-25: rgba($black, 0.25);\n$black-30: rgba($black, 0.3);\n\n// Other colors\n$white: #FFF;\n$white-10: rgba($white, 0.1);\n$pocket-teal: #50BCB6;\n$bookmark-icon-fill: #0A84FF;\n$download-icon-fill: #12BC00;\n$pocket-icon-fill: #D70022;\n\n// Photon transitions from http://design.firefox.com/photon/motion/duration-and-easing.html\n$photon-easing: cubic-bezier(0.07, 0.95, 0, 1);\n\n$border-radius: 3px;\n\n// Grid related styles\n$base-gutter: 32px;\n$section-horizontal-padding: 25px;\n$section-vertical-padding: 10px;\n$section-spacing: 40px - $section-vertical-padding * 2;\n$grid-unit: 96px; // 1 top site\n\n$icon-size: 16px;\n$smaller-icon-size: 12px;\n$larger-icon-size: 32px;\n\n$wrapper-default-width: $grid-unit * 2 + $base-gutter * 1 + $section-horizontal-padding * 2; // 2 top sites\n$wrapper-max-width-small: $grid-unit * 3 + $base-gutter * 2 + $section-horizontal-padding * 2; // 3 top sites\n$wrapper-max-width-medium: $grid-unit * 4 + $base-gutter * 3 + $section-horizontal-padding * 2; // 4 top sites\n$wrapper-max-width-large: $grid-unit * 6 + $base-gutter * 5 + $section-horizontal-padding * 2; // 6 top sites\n$wrapper-max-width-widest: $grid-unit * 8 + $base-gutter * 7 + $section-horizontal-padding * 2; // 8 top sites\n// For the breakpoints, we need to add space for the scrollbar to avoid weird\n// layout issues when the scrollbar is visible. 16px is wide enough to cover all\n// OSes and keeps it simpler than a per-OS value.\n$scrollbar-width: 16px;\n$break-point-small: $wrapper-max-width-small + $base-gutter * 2 + $scrollbar-width;\n$break-point-medium: $wrapper-max-width-medium + $base-gutter * 2 + $scrollbar-width;\n$break-point-large: $wrapper-max-width-large + $base-gutter * 2 + $scrollbar-width;\n$break-point-widest: $wrapper-max-width-widest + $base-gutter * 2 + $scrollbar-width;\n\n$section-title-font-size: 13px;\n\n$card-width: $grid-unit * 2 + $base-gutter;\n$card-height: 266px;\n$card-preview-image-height: 122px;\n$card-title-margin: 2px;\n$card-text-line-height: 19px;\n// Larger cards for wider screens:\n$card-width-large: 309px;\n$card-height-large: 370px;\n$card-preview-image-height-large: 155px;\n// Compact cards for Highlights\n$card-height-compact: 160px;\n$card-preview-image-height-compact: 108px;\n\n$topic-margin-top: 12px;\n\n$context-menu-button-size: 27px;\n$context-menu-button-boxshadow: 0 2px $grey-90-10;\n$context-menu-shadow: 0 5px 10px $black-30, 0 0 0 1px $black-20;\n$context-menu-font-size: 14px;\n$context-menu-border-radius: 5px;\n$context-menu-outer-padding: 5px;\n$context-menu-item-padding: 3px 12px;\n\n$error-fallback-font-size: 12px;\n$error-fallback-line-height: 1.5;\n\n$image-path: '../data/content/assets/';\n\n$snippets-container-height: 120px;\n\n$textbox-shadow-size: 4px;\n\n@mixin fade-in {\n box-shadow: inset $inner-box-shadow, $shadow-primary;\n transition: box-shadow 150ms;\n}\n\n@mixin fade-in-card {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n}\n\n@mixin context-menu-button {\n .context-menu-button {\n background-clip: padding-box;\n background-color: var(--newtab-contextmenu-button-color);\n background-image: url('chrome://browser/skin/page-action.svg');\n background-position: 55%;\n border: $border-primary;\n border-radius: 100%;\n box-shadow: $context-menu-button-boxshadow;\n cursor: pointer;\n fill: var(--newtab-icon-primary-color);\n height: $context-menu-button-size;\n inset-inline-end: -($context-menu-button-size / 2);\n opacity: 0;\n position: absolute;\n top: -($context-menu-button-size / 2);\n transform: scale(0.25);\n transition-duration: 200ms;\n transition-property: transform, opacity;\n width: $context-menu-button-size;\n\n &:-moz-any(:active, :focus) {\n opacity: 1;\n transform: scale(1);\n }\n }\n}\n\n@mixin context-menu-button-hover {\n .context-menu-button {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@mixin context-menu-open-middle {\n .context-menu {\n margin-inline-end: auto;\n margin-inline-start: auto;\n inset-inline-end: auto;\n inset-inline-start: -$base-gutter;\n }\n}\n\n@mixin context-menu-open-left {\n .context-menu {\n margin-inline-end: 5px;\n margin-inline-start: auto;\n inset-inline-end: 0;\n inset-inline-start: auto;\n }\n}\n\n@mixin flip-icon {\n &:dir(rtl) {\n transform: scaleX(-1);\n }\n}\n", "@function textbox-shadow($color) {\n @return 0 0 0 1px $color, 0 0 0 $textbox-shadow-size rgba($color, 0.3);\n}\n\n@mixin textbox-focus($color) {\n --newtab-textbox-focus-color: $color;\n --newtab-textbox-focus-boxshadow: textbox-shadow($color);\n}\n\n// scss variables related to the theme.\n$border-primary: 1px solid var(--newtab-border-primary-color);\n$border-secondary: 1px solid var(--newtab-border-secondary-color);\n$inner-box-shadow: 0 0 0 1px var(--newtab-inner-box-shadow-color);\n$input-border: 1px solid var(--newtab-textbox-border);\n$input-border-active: 1px solid var(--newtab-textbox-focus-color);\n$input-error-border: 1px solid $red-60;\n$input-error-boxshadow: textbox-shadow($red-60);\n$shadow-primary: 0 0 0 5px var(--newtab-card-active-outline-color);\n$shadow-secondary: 0 1px 4px 0 $grey-90-20;\n\n// Default theme\nbody {\n // General styles\n --newtab-background-color: $grey-10;\n --newtab-border-primary-color: $grey-40;\n --newtab-border-secondary-color: $grey-30;\n --newtab-button-primary-color: $blue-60;\n --newtab-button-secondary-color: inherit;\n --newtab-element-active-color: $grey-30-60;\n --newtab-element-hover-color: $grey-20;\n --newtab-icon-primary-color: $grey-90-80;\n --newtab-icon-secondary-color: $grey-90-60;\n --newtab-icon-tertiary-color: $grey-30;\n --newtab-inner-box-shadow-color: $black-10;\n --newtab-link-primary-color: $blue-60;\n --newtab-link-secondary-color: $teal-70;\n --newtab-text-conditional-color: $grey-60;\n --newtab-text-primary-color: $grey-90;\n --newtab-text-secondary-color: $grey-50;\n --newtab-textbox-background-color: $white;\n --newtab-textbox-border: $grey-90-20;\n @include textbox-focus($blue-60); // sass-lint:disable-line mixins-before-declarations\n\n // Context menu\n --newtab-contextmenu-background-color: $grey-10;\n --newtab-contextmenu-button-color: $white;\n\n // Modal + overlay\n --newtab-modal-color: $white;\n --newtab-overlay-color: $grey-20-80;\n\n // Sections\n --newtab-section-header-text-color: $grey-50;\n --newtab-section-navigation-text-color: $grey-50;\n --newtab-section-active-contextmenu-color: $grey-90;\n\n // Search\n --newtab-search-border-color: transparent;\n --newtab-search-dropdown-color: $white;\n --newtab-search-dropdown-header-color: $grey-10;\n --newtab-search-icon-color: $grey-90-40;\n\n // Top Sites\n --newtab-topsites-background-color: $white;\n --newtab-topsites-icon-shadow: inset $inner-box-shadow;\n --newtab-topsites-label-color: inherit;\n\n // Cards\n --newtab-card-active-outline-color: $grey-30;\n --newtab-card-background-color: $white;\n --newtab-card-hairline-color: $black-10;\n --newtab-card-shadow: 0 1px 4px 0 $grey-90-10;\n\n // Snippets\n --newtab-snippets-background-color: $white;\n --newtab-snippets-hairline-color: transparent;\n}\n\n// Dark theme\n.dark-theme {\n // General styles\n --newtab-background-color: $grey-80;\n --newtab-border-primary-color: $grey-10-80;\n --newtab-border-secondary-color: $grey-10-10;\n --newtab-button-primary-color: $blue-60;\n --newtab-button-secondary-color: $grey-70;\n --newtab-element-active-color: $grey-10-20;\n --newtab-element-hover-color: $grey-10-10;\n --newtab-icon-primary-color: $grey-10-80;\n --newtab-icon-secondary-color: $grey-10-40;\n --newtab-icon-tertiary-color: $grey-10-40;\n --newtab-inner-box-shadow-color: $grey-10-20;\n --newtab-link-primary-color: $blue-40;\n --newtab-link-secondary-color: $pocket-teal;\n --newtab-text-conditional-color: $grey-10;\n --newtab-text-primary-color: $grey-10;\n --newtab-text-secondary-color: $grey-10-80;\n --newtab-textbox-background-color: $grey-70;\n --newtab-textbox-border: $grey-10-20;\n @include textbox-focus($blue-40); // sass-lint:disable-line mixins-before-declarations\n\n // Context menu\n --newtab-contextmenu-background-color: $grey-60;\n --newtab-contextmenu-button-color: $grey-80;\n\n // Modal + overlay\n --newtab-modal-color: $grey-80;\n --newtab-overlay-color: $grey-90-80;\n\n // Sections\n --newtab-section-header-text-color: $grey-10-80;\n --newtab-section-navigation-text-color: $grey-10-80;\n --newtab-section-active-contextmenu-color: $white;\n\n // Search\n --newtab-search-border-color: $grey-10-20;\n --newtab-search-dropdown-color: $grey-70;\n --newtab-search-dropdown-header-color: $grey-60;\n --newtab-search-icon-color: $grey-10-60;\n\n // Top Sites\n --newtab-topsites-background-color: $grey-70;\n --newtab-topsites-icon-shadow: none;\n --newtab-topsites-label-color: $grey-10-80;\n\n // Cards\n --newtab-card-active-outline-color: $grey-60;\n --newtab-card-background-color: $grey-70;\n --newtab-card-hairline-color: $grey-10-10;\n --newtab-card-shadow: 0 1px 8px 0 $grey-90-20;\n\n // Snippets\n --newtab-snippets-background-color: $grey-70;\n --newtab-snippets-hairline-color: $white-10;\n}\n", ".icon {\n background-position: center center;\n background-repeat: no-repeat;\n background-size: $icon-size;\n -moz-context-properties: fill;\n display: inline-block;\n fill: var(--newtab-icon-primary-color);\n height: $icon-size;\n vertical-align: middle;\n width: $icon-size;\n\n &.icon-spacer {\n margin-inline-end: 8px;\n }\n\n &.icon-small-spacer {\n margin-inline-end: 6px;\n }\n\n &.icon-bookmark-added {\n background-image: url('chrome://browser/skin/bookmark.svg');\n }\n\n &.icon-bookmark-hollow {\n background-image: url('chrome://browser/skin/bookmark-hollow.svg');\n }\n\n &.icon-clear-input {\n fill: var(--newtab-icon-secondary-color);\n background-image: url('#{$image-path}glyph-cancel-16.svg');\n }\n\n &.icon-delete {\n background-image: url('#{$image-path}glyph-delete-16.svg');\n }\n\n &.icon-search {\n background-image: url('chrome://browser/skin/search-glass.svg');\n }\n\n &.icon-modal-delete {\n flex-shrink: 0;\n background-image: url('#{$image-path}glyph-modal-delete-32.svg');\n background-size: $larger-icon-size;\n height: $larger-icon-size;\n width: $larger-icon-size;\n }\n\n &.icon-dismiss {\n background-image: url('#{$image-path}glyph-dismiss-16.svg');\n }\n\n &.icon-info {\n background-image: url('#{$image-path}glyph-info-16.svg');\n }\n\n &.icon-import {\n background-image: url('#{$image-path}glyph-import-16.svg');\n }\n\n &.icon-new-window {\n @include flip-icon;\n background-image: url('#{$image-path}glyph-newWindow-16.svg');\n }\n\n &.icon-new-window-private {\n background-image: url('chrome://browser/skin/privateBrowsing.svg');\n }\n\n &.icon-settings {\n background-image: url('chrome://browser/skin/settings.svg');\n }\n\n &.icon-pin {\n @include flip-icon;\n background-image: url('#{$image-path}glyph-pin-16.svg');\n }\n\n &.icon-unpin {\n @include flip-icon;\n background-image: url('#{$image-path}glyph-unpin-16.svg');\n }\n\n &.icon-edit {\n background-image: url('#{$image-path}glyph-edit-16.svg');\n }\n\n &.icon-pocket {\n background-image: url('#{$image-path}glyph-pocket-16.svg');\n }\n\n &.icon-history-item {\n background-image: url('chrome://browser/skin/history.svg');\n }\n\n &.icon-trending {\n background-image: url('#{$image-path}glyph-trending-16.svg');\n transform: translateY(2px); // trending bolt is visually top heavy\n }\n\n &.icon-now {\n background-image: url('chrome://browser/skin/history.svg');\n }\n\n &.icon-topsites {\n background-image: url('#{$image-path}glyph-topsites-16.svg');\n }\n\n &.icon-pin-small {\n @include flip-icon;\n background-image: url('#{$image-path}glyph-pin-12.svg');\n background-size: $smaller-icon-size;\n height: $smaller-icon-size;\n width: $smaller-icon-size;\n }\n\n &.icon-check {\n background-image: url('chrome://browser/skin/check.svg');\n }\n\n &.icon-download {\n background-image: url('chrome://browser/skin/downloads/download-icons.svg#arrow-with-bar');\n }\n\n &.icon-copy {\n background-image: url('chrome://browser/skin/edit-copy.svg');\n }\n\n &.icon-open-file {\n background-image: url('#{$image-path}glyph-open-file-16.svg');\n }\n\n &.icon-webextension {\n background-image: url('#{$image-path}glyph-webextension-16.svg');\n }\n\n &.icon-highlights {\n background-image: url('#{$image-path}glyph-highlights-16.svg');\n }\n\n &.icon-arrowhead-down {\n background-image: url('#{$image-path}glyph-arrowhead-down-16.svg');\n }\n\n &.icon-arrowhead-down-small {\n background-image: url('#{$image-path}glyph-arrowhead-down-12.svg');\n background-size: $smaller-icon-size;\n height: $smaller-icon-size;\n width: $smaller-icon-size;\n }\n\n &.icon-arrowhead-forward-small {\n background-image: url('#{$image-path}glyph-arrowhead-down-12.svg');\n background-size: $smaller-icon-size;\n height: $smaller-icon-size;\n transform: rotate(-90deg);\n width: $smaller-icon-size;\n\n &:dir(rtl) {\n transform: rotate(90deg);\n }\n }\n\n &.icon-arrowhead-up {\n background-image: url('#{$image-path}glyph-arrowhead-down-16.svg');\n transform: rotate(180deg);\n }\n\n &.icon-add {\n background-image: url('#{$image-path}glyph-add-16.svg');\n }\n\n &.icon-minimize {\n background-image: url('#{$image-path}glyph-minimize-16.svg');\n }\n\n &.icon-maximize {\n background-image: url('#{$image-path}glyph-maximize-16.svg');\n }\n}\n", - ".outer-wrapper {\n color: var(--newtab-text-primary-color);\n display: flex;\n flex-grow: 1;\n min-height: 100vh;\n padding: ($section-spacing + $section-vertical-padding) $base-gutter $base-gutter;\n\n &.fixed-to-top {\n display: block;\n }\n\n a {\n color: var(--newtab-link-primary-color);\n }\n}\n\nmain {\n margin: auto;\n // Offset the snippets container so things at the bottom of the page are still\n // visible when snippets / onboarding are visible. Adjust for other spacing.\n padding-bottom: $snippets-container-height - $section-spacing - $base-gutter;\n width: $wrapper-default-width;\n\n @media (min-width: $break-point-small) {\n width: $wrapper-max-width-small;\n }\n\n @media (min-width: $break-point-medium) {\n width: $wrapper-max-width-medium;\n }\n\n @media (min-width: $break-point-large) {\n width: $wrapper-max-width-large;\n }\n\n @media (min-width: $break-point-widest) {\n width: $wrapper-max-width-widest;\n }\n\n section {\n margin-bottom: $section-spacing;\n position: relative;\n }\n}\n\n.base-content-fallback {\n // Make the error message be centered against the viewport\n height: 100vh;\n}\n\n.body-wrapper {\n // Hide certain elements so the page structure is fixed, e.g., placeholders,\n // while avoiding flashes of changing content, e.g., icons and text\n $selectors-to-hide: '\n .section-title,\n .sections-list .section:last-of-type,\n .topic\n ';\n\n #{$selectors-to-hide} {\n opacity: 0;\n }\n\n &.on {\n #{$selectors-to-hide} {\n opacity: 1;\n }\n }\n}\n\n.non-collapsible-section {\n padding: 0 $section-horizontal-padding;\n}\n\n.prefs-button {\n button {\n background-color: transparent;\n border: 0;\n cursor: pointer;\n fill: var(--newtab-icon-primary-color);\n offset-inline-end: 15px;\n padding: 15px;\n position: fixed;\n top: 15px;\n z-index: 1000;\n\n &:hover,\n &:focus {\n background-color: var(--newtab-element-hover-color);\n }\n\n &:active {\n background-color: var(--newtab-element-active-color);\n }\n }\n}\n", + ".outer-wrapper {\n color: var(--newtab-text-primary-color);\n display: flex;\n flex-grow: 1;\n min-height: 100vh;\n padding: ($section-spacing + $section-vertical-padding) $base-gutter $base-gutter;\n\n &.fixed-to-top {\n display: block;\n }\n\n a {\n color: var(--newtab-link-primary-color);\n }\n}\n\nmain {\n margin: auto;\n // Offset the snippets container so things at the bottom of the page are still\n // visible when snippets / onboarding are visible. Adjust for other spacing.\n padding-bottom: $snippets-container-height - $section-spacing - $base-gutter;\n width: $wrapper-default-width;\n\n @media (min-width: $break-point-small) {\n width: $wrapper-max-width-small;\n }\n\n @media (min-width: $break-point-medium) {\n width: $wrapper-max-width-medium;\n }\n\n @media (min-width: $break-point-large) {\n width: $wrapper-max-width-large;\n }\n\n @media (min-width: $break-point-widest) {\n width: $wrapper-max-width-widest;\n }\n\n section {\n margin-bottom: $section-spacing;\n position: relative;\n }\n}\n\n.base-content-fallback {\n // Make the error message be centered against the viewport\n height: 100vh;\n}\n\n.body-wrapper {\n // Hide certain elements so the page structure is fixed, e.g., placeholders,\n // while avoiding flashes of changing content, e.g., icons and text\n $selectors-to-hide: '\n .section-title,\n .sections-list .section:last-of-type,\n .topic\n ';\n\n #{$selectors-to-hide} {\n opacity: 0;\n }\n\n &.on {\n #{$selectors-to-hide} {\n opacity: 1;\n }\n }\n}\n\n.non-collapsible-section {\n padding: 0 $section-horizontal-padding;\n}\n\n.prefs-button {\n button {\n background-color: transparent;\n border: 0;\n cursor: pointer;\n fill: var(--newtab-icon-primary-color);\n inset-inline-end: 15px;\n padding: 15px;\n position: fixed;\n top: 15px;\n z-index: 1000;\n\n &:hover,\n &:focus {\n background-color: var(--newtab-element-hover-color);\n }\n\n &:active {\n background-color: var(--newtab-element-active-color);\n }\n }\n}\n", ".as-error-fallback {\n align-items: center;\n border-radius: $border-radius;\n box-shadow: inset $inner-box-shadow;\n color: var(--newtab-text-conditional-color);\n display: flex;\n flex-direction: column;\n font-size: $error-fallback-font-size;\n justify-content: center;\n justify-items: center;\n line-height: $error-fallback-line-height;\n\n a {\n color: var(--newtab-text-conditional-color);\n text-decoration: underline;\n }\n}\n", - "$top-sites-size: $grid-unit;\n$top-sites-border-radius: 6px;\n$top-sites-title-height: 30px;\n$top-sites-vertical-space: 8px;\n$screenshot-size: cover;\n$rich-icon-size: 96px;\n$default-icon-wrapper-size: 42px;\n$default-icon-size: 32px;\n$default-icon-offset: 6px;\n$half-base-gutter: $base-gutter / 2;\n\n.top-sites {\n // Take back the margin from the bottom row of vertical spacing as well as the\n // extra whitespace below the title text as it's vertically centered.\n margin-bottom: $section-spacing - ($top-sites-vertical-space + $top-sites-title-height / 3);\n}\n\n.top-sites-list {\n list-style: none;\n margin: 0 (-$half-base-gutter);\n padding: 0;\n\n // Two columns\n @media (max-width: $break-point-small) {\n :nth-child(2n+1) {\n @include context-menu-open-middle;\n }\n\n :nth-child(2n) {\n @include context-menu-open-left;\n }\n }\n\n // Three columns\n @media (min-width: $break-point-small) and (max-width: $break-point-medium) {\n :nth-child(3n+2),\n :nth-child(3n) {\n @include context-menu-open-left;\n }\n }\n\n // Four columns\n @media (min-width: $break-point-medium) and (max-width: $break-point-large) {\n :nth-child(4n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-medium) and (max-width: $break-point-medium + $card-width) {\n :nth-child(4n+3) {\n @include context-menu-open-left;\n }\n }\n\n // Six columns\n @media (min-width: $break-point-large) and (max-width: $break-point-large + 2 * $card-width) {\n :nth-child(6n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-large) and (max-width: $break-point-large + $card-width) {\n :nth-child(6n+5) {\n @include context-menu-open-left;\n }\n }\n\n // Eight columns\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + 2 * $card-width) {\n :nth-child(8n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + $card-width) {\n :nth-child(8n+7) {\n @include context-menu-open-left;\n }\n }\n\n @media not all and (min-width: $break-point-widest) {\n .hide-for-narrow {\n display: none;\n }\n }\n\n li {\n margin: 0 0 $top-sites-vertical-space;\n }\n\n &:not(.dnd-active) {\n .top-site-outer:-moz-any(.active, :focus, :hover) {\n .tile {\n @include fade-in;\n }\n\n @include context-menu-button-hover;\n }\n }\n}\n\n// container for drop zone\n.top-site-outer {\n padding: 0 $half-base-gutter;\n display: inline-block;\n\n // container for context menu\n .top-site-inner {\n position: relative;\n\n > a {\n color: inherit;\n display: block;\n outline: none;\n\n &:-moz-any(.active, :focus) {\n .tile {\n @include fade-in;\n }\n }\n }\n }\n\n @include context-menu-button;\n\n .tile { // sass-lint:disable-block property-sort-order\n border-radius: $top-sites-border-radius;\n box-shadow: inset $inner-box-shadow, var(--newtab-card-shadow);\n height: $top-sites-size;\n position: relative;\n width: $top-sites-size;\n\n // For letter fallback\n align-items: center;\n color: var(--newtab-text-secondary-color);\n display: flex;\n font-size: 32px;\n font-weight: 200;\n justify-content: center;\n text-transform: uppercase;\n\n &::before {\n content: attr(data-fallback);\n }\n }\n\n .screenshot {\n background-color: $white;\n background-position: top left;\n background-size: $screenshot-size;\n border-radius: $top-sites-border-radius;\n box-shadow: inset $inner-box-shadow;\n height: 100%;\n left: 0;\n opacity: 0;\n position: absolute;\n top: 0;\n transition: opacity 1s;\n width: 100%;\n\n &.active {\n opacity: 1;\n }\n }\n\n // Some common styles for all icons (rich and default) in top sites\n .top-site-icon {\n background-color: var(--newtab-topsites-background-color);\n background-position: center center;\n background-repeat: no-repeat;\n border-radius: $top-sites-border-radius;\n box-shadow: var(--newtab-topsites-icon-shadow);\n position: absolute;\n }\n\n .rich-icon {\n background-size: cover;\n height: 100%;\n offset-inline-start: 0;\n top: 0;\n width: 100%;\n }\n\n .default-icon { // sass-lint:disable block property-sort-order\n background-size: $default-icon-size;\n bottom: -$default-icon-offset;\n height: $default-icon-wrapper-size;\n offset-inline-end: -$default-icon-offset;\n width: $default-icon-wrapper-size;\n\n // for corner letter fallback\n align-items: center;\n display: flex;\n font-size: 20px;\n justify-content: center;\n\n &[data-fallback]::before {\n content: attr(data-fallback);\n }\n }\n\n .title {\n color: var(--newtab-topsites-label-color);\n font: message-box;\n height: $top-sites-title-height;\n line-height: $top-sites-title-height;\n text-align: center;\n width: $top-sites-size;\n position: relative;\n\n .icon {\n fill: var(--newtab-icon-tertiary-color);\n offset-inline-start: 0;\n position: absolute;\n top: 10px;\n }\n\n span {\n height: $top-sites-title-height;\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &.pinned {\n span {\n padding: 0 13px;\n }\n }\n }\n\n .edit-button {\n background-image: url('#{$image-path}glyph-edit-16.svg');\n }\n\n &.placeholder {\n .tile {\n box-shadow: inset $inner-box-shadow;\n }\n\n .screenshot {\n display: none;\n }\n }\n\n &.dragged {\n .tile {\n background: $grey-20;\n box-shadow: none;\n\n *,\n &::before {\n display: none;\n }\n }\n\n .title {\n visibility: hidden;\n }\n }\n}\n\n.edit-topsites-wrapper {\n .modal {\n box-shadow: $shadow-secondary;\n left: 0;\n margin: 0 auto;\n position: fixed;\n right: 0;\n top: 40px;\n width: $wrapper-default-width;\n\n @media (min-width: $break-point-small) {\n width: $wrapper-max-width-small;\n }\n\n @media (min-width: $break-point-medium) {\n width: $wrapper-max-width-medium;\n }\n\n @media (min-width: $break-point-large) {\n width: $wrapper-max-width-large;\n }\n }\n}\n\n.topsite-form {\n $form-width: 300px;\n $form-spacing: 32px;\n\n .form-input-container {\n max-width: $form-width + 3 * $form-spacing + $rich-icon-size;\n margin: 0 auto;\n padding: $form-spacing;\n\n .top-site-outer {\n padding: 0;\n margin: 24px 0 0;\n margin-inline-start: $form-spacing;\n pointer-events: none;\n }\n\n .section-title {\n text-transform: none;\n font-size: 16px;\n margin: 0 0 16px;\n }\n }\n\n .fields-and-preview {\n display: flex;\n }\n\n label {\n font-size: $section-title-font-size;\n }\n\n .form-wrapper {\n width: 100%;\n\n .field {\n position: relative;\n\n .icon-clear-input {\n position: absolute;\n transform: translateY(-50%);\n top: 50%;\n offset-inline-end: 8px;\n }\n }\n\n .url {\n input:dir(ltr) {\n padding-right: 32px;\n }\n\n input:dir(rtl) {\n padding-left: 32px;\n\n &:not(:placeholder-shown) {\n direction: ltr;\n text-align: right;\n }\n }\n }\n\n .enable-custom-image-input {\n display: inline-block;\n font-size: 13px;\n margin-top: 4px;\n cursor: pointer;\n\n &:hover {\n text-decoration: underline;\n }\n }\n\n .custom-image-input-container {\n margin-top: 4px;\n\n .loading-container {\n width: 16px;\n height: 16px;\n overflow: hidden;\n position: absolute;\n transform: translateY(-50%);\n top: 50%;\n offset-inline-end: 8px;\n }\n\n // This animation is derived from Firefox's tab loading animation\n // See https://searchfox.org/mozilla-central/rev/b29daa46443b30612415c35be0a3c9c13b9dc5f6/browser/themes/shared/tabs.inc.css#208-216\n .loading-animation {\n @keyframes tab-throbber-animation {\n 100% { transform: translateX(-960px); }\n }\n\n @keyframes tab-throbber-animation-rtl {\n 100% { transform: translateX(960px); }\n }\n\n width: 960px;\n height: 16px;\n -moz-context-properties: fill;\n fill: $blue-50;\n background-image: url('chrome://browser/skin/tabbrowser/loading.svg');\n animation: tab-throbber-animation 1.05s steps(60) infinite;\n\n &:dir(rtl) {\n animation-name: tab-throbber-animation-rtl;\n }\n }\n }\n\n input {\n &[type='text'] {\n background-color: var(--newtab-textbox-background-color);\n border: $input-border;\n margin: 8px 0;\n padding: 0 8px;\n height: 32px;\n width: 100%;\n font-size: 15px;\n\n &:focus {\n border: $input-border-active;\n box-shadow: var(--newtab-textbox-focus-boxshadow);\n }\n\n &[disabled] {\n border: $input-border;\n box-shadow: none;\n opacity: 0.4;\n }\n }\n }\n\n .invalid {\n input {\n &[type='text'] {\n border: $input-error-border;\n box-shadow: $input-error-boxshadow;\n }\n }\n }\n\n .error-tooltip {\n animation: fade-up-tt 450ms;\n background: $red-60;\n border-radius: 2px;\n color: $white;\n offset-inline-start: 3px;\n padding: 5px 12px;\n position: absolute;\n top: 44px;\n z-index: 1;\n\n // tooltip caret\n &::before {\n background: $red-60;\n bottom: -8px;\n content: '.';\n height: 16px;\n offset-inline-start: 12px;\n position: absolute;\n text-indent: -999px;\n top: -7px;\n transform: rotate(45deg);\n white-space: nowrap;\n width: 16px;\n z-index: -1;\n }\n }\n }\n\n .actions {\n justify-content: flex-end;\n\n button {\n margin-inline-start: 10px;\n margin-inline-end: 0;\n }\n }\n\n @media (max-width: $break-point-small) {\n .fields-and-preview {\n flex-direction: column;\n\n .top-site-outer {\n margin-inline-start: 0;\n }\n }\n }\n}\n\n//used for tooltips below form element\n@keyframes fade-up-tt {\n 0% {\n opacity: 0;\n transform: translateY(15px);\n }\n\n 100% {\n opacity: 1;\n transform: translateY(0);\n }\n}\n", + "$top-sites-size: $grid-unit;\n$top-sites-border-radius: 6px;\n$top-sites-title-height: 30px;\n$top-sites-vertical-space: 8px;\n$screenshot-size: cover;\n$rich-icon-size: 96px;\n$default-icon-wrapper-size: 42px;\n$default-icon-size: 32px;\n$default-icon-offset: 6px;\n$half-base-gutter: $base-gutter / 2;\n\n.top-sites {\n // Take back the margin from the bottom row of vertical spacing as well as the\n // extra whitespace below the title text as it's vertically centered.\n margin-bottom: $section-spacing - ($top-sites-vertical-space + $top-sites-title-height / 3);\n}\n\n.top-sites-list {\n list-style: none;\n margin: 0 (-$half-base-gutter);\n padding: 0;\n\n // Two columns\n @media (max-width: $break-point-small) {\n :nth-child(2n+1) {\n @include context-menu-open-middle;\n }\n\n :nth-child(2n) {\n @include context-menu-open-left;\n }\n }\n\n // Three columns\n @media (min-width: $break-point-small) and (max-width: $break-point-medium) {\n :nth-child(3n+2),\n :nth-child(3n) {\n @include context-menu-open-left;\n }\n }\n\n // Four columns\n @media (min-width: $break-point-medium) and (max-width: $break-point-large) {\n :nth-child(4n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-medium) and (max-width: $break-point-medium + $card-width) {\n :nth-child(4n+3) {\n @include context-menu-open-left;\n }\n }\n\n // Six columns\n @media (min-width: $break-point-large) and (max-width: $break-point-large + 2 * $card-width) {\n :nth-child(6n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-large) and (max-width: $break-point-large + $card-width) {\n :nth-child(6n+5) {\n @include context-menu-open-left;\n }\n }\n\n // Eight columns\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + 2 * $card-width) {\n :nth-child(8n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + $card-width) {\n :nth-child(8n+7) {\n @include context-menu-open-left;\n }\n }\n\n @media not all and (min-width: $break-point-widest) {\n .hide-for-narrow {\n display: none;\n }\n }\n\n li {\n margin: 0 0 $top-sites-vertical-space;\n }\n\n &:not(.dnd-active) {\n .top-site-outer:-moz-any(.active, :focus, :hover) {\n .tile {\n @include fade-in;\n }\n\n @include context-menu-button-hover;\n }\n }\n}\n\n// container for drop zone\n.top-site-outer {\n padding: 0 $half-base-gutter;\n display: inline-block;\n\n // container for context menu\n .top-site-inner {\n position: relative;\n\n > a {\n color: inherit;\n display: block;\n outline: none;\n\n &:-moz-any(.active, :focus) {\n .tile {\n @include fade-in;\n }\n }\n }\n }\n\n @include context-menu-button;\n\n .tile { // sass-lint:disable-block property-sort-order\n border-radius: $top-sites-border-radius;\n box-shadow: inset $inner-box-shadow, var(--newtab-card-shadow);\n height: $top-sites-size;\n position: relative;\n width: $top-sites-size;\n\n // For letter fallback\n align-items: center;\n color: var(--newtab-text-secondary-color);\n display: flex;\n font-size: 32px;\n font-weight: 200;\n justify-content: center;\n text-transform: uppercase;\n\n &::before {\n content: attr(data-fallback);\n }\n }\n\n .screenshot {\n background-color: $white;\n background-position: top left;\n background-size: $screenshot-size;\n border-radius: $top-sites-border-radius;\n box-shadow: inset $inner-box-shadow;\n height: 100%;\n left: 0;\n opacity: 0;\n position: absolute;\n top: 0;\n transition: opacity 1s;\n width: 100%;\n\n &.active {\n opacity: 1;\n }\n }\n\n // Some common styles for all icons (rich and default) in top sites\n .top-site-icon {\n background-color: var(--newtab-topsites-background-color);\n background-position: center center;\n background-repeat: no-repeat;\n border-radius: $top-sites-border-radius;\n box-shadow: var(--newtab-topsites-icon-shadow);\n position: absolute;\n }\n\n .rich-icon {\n background-size: cover;\n height: 100%;\n inset-inline-start: 0;\n top: 0;\n width: 100%;\n }\n\n .default-icon { // sass-lint:disable block property-sort-order\n background-size: $default-icon-size;\n bottom: -$default-icon-offset;\n height: $default-icon-wrapper-size;\n inset-inline-end: -$default-icon-offset;\n width: $default-icon-wrapper-size;\n\n // for corner letter fallback\n align-items: center;\n display: flex;\n font-size: 20px;\n justify-content: center;\n\n &[data-fallback]::before {\n content: attr(data-fallback);\n }\n }\n\n .title {\n color: var(--newtab-topsites-label-color);\n font: message-box;\n height: $top-sites-title-height;\n line-height: $top-sites-title-height;\n text-align: center;\n width: $top-sites-size;\n position: relative;\n\n .icon {\n fill: var(--newtab-icon-tertiary-color);\n inset-inline-start: 0;\n position: absolute;\n top: 10px;\n }\n\n span {\n height: $top-sites-title-height;\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &.pinned {\n span {\n padding: 0 13px;\n }\n }\n }\n\n .edit-button {\n background-image: url('#{$image-path}glyph-edit-16.svg');\n }\n\n &.placeholder {\n .tile {\n box-shadow: inset $inner-box-shadow;\n }\n\n .screenshot {\n display: none;\n }\n }\n\n &.dragged {\n .tile {\n background: $grey-20;\n box-shadow: none;\n\n *,\n &::before {\n display: none;\n }\n }\n\n .title {\n visibility: hidden;\n }\n }\n}\n\n.edit-topsites-wrapper {\n .modal {\n box-shadow: $shadow-secondary;\n left: 0;\n margin: 0 auto;\n position: fixed;\n right: 0;\n top: 40px;\n width: $wrapper-default-width;\n\n @media (min-width: $break-point-small) {\n width: $wrapper-max-width-small;\n }\n\n @media (min-width: $break-point-medium) {\n width: $wrapper-max-width-medium;\n }\n\n @media (min-width: $break-point-large) {\n width: $wrapper-max-width-large;\n }\n }\n}\n\n.topsite-form {\n $form-width: 300px;\n $form-spacing: 32px;\n\n .form-input-container {\n max-width: $form-width + 3 * $form-spacing + $rich-icon-size;\n margin: 0 auto;\n padding: $form-spacing;\n\n .top-site-outer {\n padding: 0;\n margin: 24px 0 0;\n margin-inline-start: $form-spacing;\n pointer-events: none;\n }\n\n .section-title {\n text-transform: none;\n font-size: 16px;\n margin: 0 0 16px;\n }\n }\n\n .fields-and-preview {\n display: flex;\n }\n\n label {\n font-size: $section-title-font-size;\n }\n\n .form-wrapper {\n width: 100%;\n\n .field {\n position: relative;\n\n .icon-clear-input {\n position: absolute;\n transform: translateY(-50%);\n top: 50%;\n inset-inline-end: 8px;\n }\n }\n\n .url {\n input:dir(ltr) {\n padding-right: 32px;\n }\n\n input:dir(rtl) {\n padding-left: 32px;\n\n &:not(:placeholder-shown) {\n direction: ltr;\n text-align: right;\n }\n }\n }\n\n .enable-custom-image-input {\n display: inline-block;\n font-size: 13px;\n margin-top: 4px;\n cursor: pointer;\n\n &:hover {\n text-decoration: underline;\n }\n }\n\n .custom-image-input-container {\n margin-top: 4px;\n\n .loading-container {\n width: 16px;\n height: 16px;\n overflow: hidden;\n position: absolute;\n transform: translateY(-50%);\n top: 50%;\n inset-inline-end: 8px;\n }\n\n // This animation is derived from Firefox's tab loading animation\n // See https://searchfox.org/mozilla-central/rev/b29daa46443b30612415c35be0a3c9c13b9dc5f6/browser/themes/shared/tabs.inc.css#208-216\n .loading-animation {\n @keyframes tab-throbber-animation {\n 100% { transform: translateX(-960px); }\n }\n\n @keyframes tab-throbber-animation-rtl {\n 100% { transform: translateX(960px); }\n }\n\n width: 960px;\n height: 16px;\n -moz-context-properties: fill;\n fill: $blue-50;\n background-image: url('chrome://browser/skin/tabbrowser/loading.svg');\n animation: tab-throbber-animation 1.05s steps(60) infinite;\n\n &:dir(rtl) {\n animation-name: tab-throbber-animation-rtl;\n }\n }\n }\n\n input {\n &[type='text'] {\n background-color: var(--newtab-textbox-background-color);\n border: $input-border;\n margin: 8px 0;\n padding: 0 8px;\n height: 32px;\n width: 100%;\n font-size: 15px;\n\n &:focus {\n border: $input-border-active;\n box-shadow: var(--newtab-textbox-focus-boxshadow);\n }\n\n &[disabled] {\n border: $input-border;\n box-shadow: none;\n opacity: 0.4;\n }\n }\n }\n\n .invalid {\n input {\n &[type='text'] {\n border: $input-error-border;\n box-shadow: $input-error-boxshadow;\n }\n }\n }\n\n .error-tooltip {\n animation: fade-up-tt 450ms;\n background: $red-60;\n border-radius: 2px;\n color: $white;\n inset-inline-start: 3px;\n padding: 5px 12px;\n position: absolute;\n top: 44px;\n z-index: 1;\n\n // tooltip caret\n &::before {\n background: $red-60;\n bottom: -8px;\n content: '.';\n height: 16px;\n inset-inline-start: 12px;\n position: absolute;\n text-indent: -999px;\n top: -7px;\n transform: rotate(45deg);\n white-space: nowrap;\n width: 16px;\n z-index: -1;\n }\n }\n }\n\n .actions {\n justify-content: flex-end;\n\n button {\n margin-inline-start: 10px;\n margin-inline-end: 0;\n }\n }\n\n @media (max-width: $break-point-small) {\n .fields-and-preview {\n flex-direction: column;\n\n .top-site-outer {\n margin-inline-start: 0;\n }\n }\n }\n}\n\n//used for tooltips below form element\n@keyframes fade-up-tt {\n 0% {\n opacity: 0;\n transform: translateY(15px);\n }\n\n 100% {\n opacity: 1;\n transform: translateY(0);\n }\n}\n", ".sections-list {\n .section-list {\n display: grid;\n grid-gap: $base-gutter;\n grid-template-columns: repeat(auto-fit, $card-width);\n margin: 0;\n\n @media (max-width: $break-point-medium) {\n @include context-menu-open-left;\n }\n\n @media (min-width: $break-point-medium) and (max-width: $break-point-large) {\n :nth-child(2n) {\n @include context-menu-open-left;\n }\n }\n\n @media (min-width: $break-point-large) and (max-width: $break-point-large + 2 * $card-width) {\n :nth-child(3n) {\n @include context-menu-open-left;\n }\n }\n\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + 2 * $card-width) {\n :nth-child(3n) {\n @include context-menu-open-left;\n }\n }\n }\n\n .section-empty-state {\n border: $border-secondary;\n border-radius: $border-radius;\n display: flex;\n height: $card-height;\n width: 100%;\n\n .empty-state {\n margin: auto;\n max-width: 350px;\n\n .empty-state-icon {\n background-position: center;\n background-repeat: no-repeat;\n background-size: 50px 50px;\n -moz-context-properties: fill;\n display: block;\n fill: var(--newtab-icon-secondary-color);\n height: 50px;\n margin: 0 auto;\n width: 50px;\n }\n\n .empty-state-message {\n color: var(--newtab-text-primary-color);\n font-size: 13px;\n margin-bottom: 0;\n text-align: center;\n }\n }\n\n @media (min-width: $break-point-widest) {\n height: $card-height-large;\n }\n }\n}\n\n@media (min-width: $break-point-widest) {\n .sections-list {\n // Compact cards stay the same size but normal cards get bigger.\n .normal-cards {\n .section-list {\n grid-template-columns: repeat(auto-fit, $card-width-large);\n }\n }\n }\n}\n", ".activity-stream {\n &.welcome {\n overflow: hidden;\n }\n\n &:not(.welcome) {\n .overlay-wrapper {\n display: none;\n }\n }\n}\n\n.overlay-wrapper {\n position: absolute;\n top: 0;\n width: 100vw;\n height: 100vh;\n z-index: 21000;\n transition: opacity 0.4s;\n opacity: 0;\n overflow-x: auto;\n\n &.show {\n transition: none;\n opacity: 1;\n\n .firstrun-sign-in {\n transition: opacity 1.5s, transform 1.5s;\n transition-delay: 0.2s;\n transform: translateY(-50%) scale(1);\n opacity: 1;\n\n @media screen and (max-width: 790px) {\n float: none;\n margin: auto;\n top: 190px;\n margin-bottom: 100px;\n }\n }\n\n .firstrun-firefox-logo {\n transition: opacity 2.3s;\n opacity: 1;\n }\n\n .firstrun-title,\n .firstrun-content,\n .firstrun-link {\n transition: transform 0.5s, opacity 0.8s;\n transform: translateY(0);\n opacity: 1;\n }\n\n .firstrun-title {\n transition-delay: 0.2s;\n }\n\n .firstrun-content {\n transition-delay: 0.4s;\n }\n\n .firstrun-link {\n transition-delay: 0.6s;\n }\n\n .fxaccounts-container {\n transition: none;\n opacity: 1;\n }\n }\n}\n\n.background {\n width: 100%;\n height: 100%;\n display: block;\n background: url('#{$image-path}fox-tail.png') top -200px center no-repeat,\n linear-gradient(to bottom, $blue-70 40%, #004EC2 60%, $blue-60 80%, #0080FF 90%, #00C7FF 100%) top center no-repeat,\n $blue-70;\n background-size: cover;\n position: fixed;\n}\n\n.firstrun-sign-in {\n transform: translateY(-50%) scale(0.8);\n position: relative;\n top: 50%;\n width: 358px;\n opacity: 0;\n background-color: $white;\n float: inline-end;\n color: $grey-90;\n text-align: center;\n padding: 10px;\n\n .extra-links {\n font-size: 12px;\n max-width: 340px;\n margin: 14px 50px;\n color: #676F7E;\n cursor: default;\n\n a {\n color: $grey-50;\n cursor: pointer;\n text-decoration: underline;\n }\n\n a:hover,\n a:active,\n a:focus {\n color: $blue-50;\n }\n }\n\n .email-input {\n box-shadow: none;\n margin: auto;\n width: 244px;\n display: block;\n height: 40px;\n padding-inline-start: 20px;\n border: 1px solid $grey-50;\n border-radius: 2px;\n font-size: 16px;\n transition: border-color 150ms, box-shadow 150ms;\n\n &:hover {\n border-color: $grey-90;\n }\n\n &:focus {\n border-color: $blue-50;\n box-shadow: 0 0 0 3px rgba(10, 132, 255, 0.3);\n }\n }\n\n .form-header {\n font-size: 22px;\n margin: 15px auto;\n }\n\n .form-header .sub-header {\n font-size: 14px;\n margin-top: 4px;\n display: block;\n }\n\n button {\n display: block;\n cursor: pointer;\n margin: 10px auto 0;\n }\n\n .continue-button {\n font-size: 18px;\n height: 43px;\n width: 250px;\n padding: 8px 0;\n border: 0;\n border-radius: 4px;\n color: $white;\n background-color: $blue-60;\n transition: background-color 150ms;\n\n &:not([disabled]):active,\n &:not([disabled]):hover {\n background: $blue-70;\n border-color: $blue-80;\n }\n }\n\n .skip-button {\n font-size: 13px;\n margin-top: 35px;\n margin-bottom: 20px;\n background-color: #FCFCFC;\n color: $blue-50;\n border: 1px solid $blue-50;\n border-radius: 2px;\n min-height: 24px;\n padding: 5px 10px;\n transition: background-color 150ms, color 150ms, border-color 150ms;\n\n &[disabled] {\n background-color: #EBEBEB;\n border-color: #B1B1B1;\n color: #6A6A6A;\n cursor: default;\n opacity: 0.5;\n }\n\n &:not([disabled]):hover {\n background-color: $blue-50;\n border-color: $blue-60;\n color: $white;\n }\n }\n}\n\n.firstrun-left-divider {\n position: relative;\n float: inline-start;\n clear: both;\n width: 435px;\n\n @media screen and (max-width: 825px) {\n width: 400px;\n }\n\n @media screen and (max-width: 790px) {\n margin: auto;\n float: none;\n width: 352px;\n text-align: center;\n }\n}\n\n.firstrun-content {\n line-height: 1.5;\n margin-bottom: 48px;\n max-width: 352px;\n background: url('#{$image-path}sync-devices.svg') bottom center no-repeat;\n padding-bottom: 210px;\n}\n\n.firstrun-link {\n color: $white;\n display: block;\n text-decoration: underline;\n\n &:hover,\n &:active,\n &:focus {\n color: $white;\n }\n}\n\n.firstrun-title {\n background: url('chrome://branding/content/about-logo.png') top left no-repeat;\n background-size: 90px 90px;\n margin: 40px 0 10px;\n padding-top: 110px;\n\n @media screen and (max-width: 790px) {\n background: url('chrome://branding/content/about-logo.png') top center no-repeat;\n background-size: 90px 90px;\n }\n}\n\n[dir='rtl'] {\n .firstrun-title {\n background-position: top right;\n }\n}\n\n.fxaccounts-container {\n position: absolute;\n bottom: 0;\n right: 0;\n top: 0;\n left: 0;\n color: $white;\n height: 515px;\n margin: auto;\n width: 819px;\n z-index: 10;\n transition: opacity 0.3s;\n opacity: 0;\n\n @media screen and (max-width: 825px) {\n width: 784px;\n }\n\n @media screen and (max-width: 790px) {\n width: auto;\n height: 100%;\n }\n}\n\n.firstrun-title,\n.firstrun-content,\n.firstrun-link {\n opacity: 0;\n transform: translateY(-5px);\n}\n", ".topic {\n color: var(--newtab-section-navigation-text-color);\n font-size: 12px;\n line-height: 1.6;\n margin-top: $topic-margin-top;\n\n @media (min-width: $break-point-large) {\n line-height: 16px;\n }\n\n ul {\n margin: 0;\n padding: 0;\n @media (min-width: $break-point-large) {\n display: inline;\n padding-inline-start: 12px;\n }\n }\n\n\n ul li {\n display: inline-block;\n\n &::after {\n content: '•';\n padding: 8px;\n }\n\n &:last-child::after {\n content: none;\n }\n }\n\n .topic-link {\n color: var(--newtab-link-secondary-color);\n font-weight: bold;\n }\n\n .topic-read-more {\n color: var(--newtab-link-secondary-color);\n font-weight: bold;\n\n @media (min-width: $break-point-large) {\n // This is floating to accomodate a very large number of topics and/or\n // very long topic names due to l10n.\n float: right;\n\n &:dir(rtl) {\n float: left;\n }\n }\n\n &::after {\n background: url('#{$image-path}topic-show-more-12.svg') no-repeat center center;\n content: '';\n -moz-context-properties: fill;\n display: inline-block;\n fill: var(--newtab-link-secondary-color);\n height: 16px;\n margin-inline-start: 5px;\n vertical-align: top;\n width: 12px;\n }\n\n &:dir(rtl)::after {\n transform: scaleX(-1);\n }\n }\n\n // This is a clearfix to for the topics-read-more link which is floating and causes\n // some jank when we set overflow:hidden for the animation.\n &::after {\n clear: both;\n content: '';\n display: table;\n }\n}\n", - ".search-wrapper {\n $search-height: 35px;\n $search-icon-size: 18px;\n $search-icon-padding: 8px;\n $search-icon-width: 2 * $search-icon-padding + $search-icon-size;\n $search-input-left-label-width: 35px;\n $search-button-width: 36px;\n $glyph-forward: url('chrome://browser/skin/forward.svg');\n\n cursor: default;\n display: flex;\n height: $search-height;\n margin-bottom: $section-spacing;\n position: relative;\n width: 100%;\n\n input {\n background: var(--newtab-textbox-background-color) var(--newtab-search-icon) $search-icon-padding center / $search-icon-size no-repeat;\n border: solid 1px var(--newtab-search-border-color);\n box-shadow: $shadow-secondary, 0 0 0 1px $black-15;\n font-size: 15px;\n -moz-context-properties: fill;\n fill: var(--newtab-search-icon-color);\n padding: 0;\n padding-inline-end: $search-button-width;\n padding-inline-start: $search-icon-width;\n width: 100%;\n\n &:dir(rtl) {\n background-position-x: right $search-icon-padding;\n }\n }\n\n &:hover input {\n box-shadow: $shadow-secondary, 0 0 0 1px $black-25;\n }\n\n &:active input,\n input:focus {\n border: $input-border-active;\n box-shadow: var(--newtab-textbox-focus-boxshadow);\n }\n\n .search-button {\n background: $glyph-forward no-repeat center center;\n background-size: 16px 16px;\n border: 0;\n border-radius: 0 $border-radius $border-radius 0;\n -moz-context-properties: fill;\n fill: var(--newtab-search-icon-color);\n height: 100%;\n offset-inline-end: 0;\n position: absolute;\n width: $search-button-width;\n\n &:focus,\n &:hover {\n background-color: $grey-90-10;\n cursor: pointer;\n }\n\n &:active {\n background-color: $grey-90-20;\n }\n\n &:dir(rtl) {\n transform: scaleX(-1);\n }\n }\n}\n\n@at-root {\n // Adjust the style of the contentSearchUI-generated table\n .contentSearchSuggestionTable {\n background-color: var(--newtab-search-dropdown-color);\n border: 0;\n box-shadow: $context-menu-shadow;\n transform: translateY($textbox-shadow-size);\n\n .contentSearchHeader {\n background-color: var(--newtab-search-dropdown-header-color);\n color: var(--newtab-text-secondary-color);\n }\n\n .contentSearchHeader,\n .contentSearchSettingsButton {\n border-color: var(--newtab-border-secondary-color);\n }\n\n .contentSearchSuggestionsList {\n border: 0;\n }\n\n .contentSearchOneOffsTable {\n background-color: var(--newtab-search-dropdown-header-color);\n border-top: solid 1px var(--newtab-border-secondary-color);\n }\n\n .contentSearchSearchWithHeaderSearchText {\n color: var(--newtab-text-primary-color);\n }\n\n .contentSearchSuggestionsContainer {\n background-color: var(--newtab-search-dropdown-color);\n }\n\n .contentSearchSuggestionRow {\n &.selected {\n background: var(--newtab-element-hover-color);\n color: var(--newtab-text-primary-color);\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n\n .historyIcon {\n fill: var(--newtab-icon-secondary-color);\n }\n }\n }\n\n .contentSearchOneOffsTable {\n .contentSearchSuggestionsContainer {\n background-color: var(--newtab-search-dropdown-header-color);\n }\n }\n\n .contentSearchOneOffItem {\n // Make the border slightly shorter by offsetting from the top and bottom\n $border-offset: 18%;\n\n background-image: none;\n border-image: linear-gradient(transparent $border-offset, var(--newtab-border-secondary-color) $border-offset, var(--newtab-border-secondary-color) 100% - $border-offset, transparent 100% - $border-offset) 1;\n border-inline-end: 1px solid;\n position: relative;\n\n &.selected {\n background: var(--newtab-element-hover-color);\n }\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n }\n\n .contentSearchSettingsButton {\n &:hover {\n background: var(--newtab-element-hover-color);\n color: var(--newtab-text-primary-color);\n }\n }\n }\n}\n", - ".context-menu {\n background: var(--newtab-contextmenu-background-color);\n border-radius: $context-menu-border-radius;\n box-shadow: $context-menu-shadow;\n display: block;\n font-size: $context-menu-font-size;\n margin-inline-start: 5px;\n offset-inline-start: 100%;\n position: absolute;\n top: ($context-menu-button-size / 4);\n z-index: 10000;\n\n > ul {\n list-style: none;\n margin: 0;\n padding: $context-menu-outer-padding 0;\n\n > li {\n margin: 0;\n width: 100%;\n\n &.separator {\n border-bottom: $border-secondary;\n margin: $context-menu-outer-padding 0;\n }\n\n > a {\n align-items: center;\n color: inherit;\n cursor: pointer;\n display: flex;\n line-height: 16px;\n outline: none;\n padding: $context-menu-item-padding;\n white-space: nowrap;\n\n &:-moz-any(:focus, :hover) {\n background: var(--newtab-element-hover-color);\n }\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n\n &.disabled {\n opacity: 0.4;\n pointer-events: none;\n }\n }\n }\n }\n}\n", + ".search-wrapper {\n $search-height: 35px;\n $search-icon-size: 18px;\n $search-icon-padding: 8px;\n $search-icon-width: 2 * $search-icon-padding + $search-icon-size;\n $search-input-left-label-width: 35px;\n $search-button-width: 36px;\n $glyph-forward: url('chrome://browser/skin/forward.svg');\n\n cursor: default;\n display: flex;\n height: $search-height;\n margin-bottom: $section-spacing;\n position: relative;\n width: 100%;\n\n input {\n background: var(--newtab-textbox-background-color) var(--newtab-search-icon) $search-icon-padding center / $search-icon-size no-repeat;\n border: solid 1px var(--newtab-search-border-color);\n box-shadow: $shadow-secondary, 0 0 0 1px $black-15;\n font-size: 15px;\n -moz-context-properties: fill;\n fill: var(--newtab-search-icon-color);\n padding: 0;\n padding-inline-end: $search-button-width;\n padding-inline-start: $search-icon-width;\n width: 100%;\n\n &:dir(rtl) {\n background-position-x: right $search-icon-padding;\n }\n }\n\n &:hover input {\n box-shadow: $shadow-secondary, 0 0 0 1px $black-25;\n }\n\n &:active input,\n input:focus {\n border: $input-border-active;\n box-shadow: var(--newtab-textbox-focus-boxshadow);\n }\n\n .search-button {\n background: $glyph-forward no-repeat center center;\n background-size: 16px 16px;\n border: 0;\n border-radius: 0 $border-radius $border-radius 0;\n -moz-context-properties: fill;\n fill: var(--newtab-search-icon-color);\n height: 100%;\n inset-inline-end: 0;\n position: absolute;\n width: $search-button-width;\n\n &:focus,\n &:hover {\n background-color: $grey-90-10;\n cursor: pointer;\n }\n\n &:active {\n background-color: $grey-90-20;\n }\n\n &:dir(rtl) {\n transform: scaleX(-1);\n }\n }\n}\n\n@at-root {\n // Adjust the style of the contentSearchUI-generated table\n .contentSearchSuggestionTable {\n background-color: var(--newtab-search-dropdown-color);\n border: 0;\n box-shadow: $context-menu-shadow;\n transform: translateY($textbox-shadow-size);\n\n .contentSearchHeader {\n background-color: var(--newtab-search-dropdown-header-color);\n color: var(--newtab-text-secondary-color);\n }\n\n .contentSearchHeader,\n .contentSearchSettingsButton {\n border-color: var(--newtab-border-secondary-color);\n }\n\n .contentSearchSuggestionsList {\n border: 0;\n }\n\n .contentSearchOneOffsTable {\n background-color: var(--newtab-search-dropdown-header-color);\n border-top: solid 1px var(--newtab-border-secondary-color);\n }\n\n .contentSearchSearchWithHeaderSearchText {\n color: var(--newtab-text-primary-color);\n }\n\n .contentSearchSuggestionsContainer {\n background-color: var(--newtab-search-dropdown-color);\n }\n\n .contentSearchSuggestionRow {\n &.selected {\n background: var(--newtab-element-hover-color);\n color: var(--newtab-text-primary-color);\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n\n .historyIcon {\n fill: var(--newtab-icon-secondary-color);\n }\n }\n }\n\n .contentSearchOneOffsTable {\n .contentSearchSuggestionsContainer {\n background-color: var(--newtab-search-dropdown-header-color);\n }\n }\n\n .contentSearchOneOffItem {\n // Make the border slightly shorter by offsetting from the top and bottom\n $border-offset: 18%;\n\n background-image: none;\n border-image: linear-gradient(transparent $border-offset, var(--newtab-border-secondary-color) $border-offset, var(--newtab-border-secondary-color) 100% - $border-offset, transparent 100% - $border-offset) 1;\n border-inline-end: 1px solid;\n position: relative;\n\n &.selected {\n background: var(--newtab-element-hover-color);\n }\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n }\n\n .contentSearchSettingsButton {\n &:hover {\n background: var(--newtab-element-hover-color);\n color: var(--newtab-text-primary-color);\n }\n }\n }\n}\n", + ".context-menu {\n background: var(--newtab-contextmenu-background-color);\n border-radius: $context-menu-border-radius;\n box-shadow: $context-menu-shadow;\n display: block;\n font-size: $context-menu-font-size;\n margin-inline-start: 5px;\n inset-inline-start: 100%;\n position: absolute;\n top: ($context-menu-button-size / 4);\n z-index: 10000;\n\n > ul {\n list-style: none;\n margin: 0;\n padding: $context-menu-outer-padding 0;\n\n > li {\n margin: 0;\n width: 100%;\n\n &.separator {\n border-bottom: $border-secondary;\n margin: $context-menu-outer-padding 0;\n }\n\n > a {\n align-items: center;\n color: inherit;\n cursor: pointer;\n display: flex;\n line-height: 16px;\n outline: none;\n padding: $context-menu-item-padding;\n white-space: nowrap;\n\n &:-moz-any(:focus, :hover) {\n background: var(--newtab-element-hover-color);\n }\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n\n &.disabled {\n opacity: 0.4;\n pointer-events: none;\n }\n }\n }\n }\n}\n", ".confirmation-dialog {\n .modal {\n box-shadow: 0 2px 2px 0 $black-10;\n left: 50%;\n margin-left: -200px;\n position: fixed;\n top: 20%;\n width: 400px;\n }\n\n section {\n margin: 0;\n }\n\n .modal-message {\n display: flex;\n padding: 16px;\n padding-bottom: 0;\n\n p {\n margin: 0;\n margin-bottom: 16px;\n }\n }\n\n .actions {\n border: 0;\n display: flex;\n flex-wrap: nowrap;\n padding: 0 16px;\n\n button {\n margin-inline-end: 16px;\n padding-inline-end: 18px;\n padding-inline-start: 18px;\n white-space: normal;\n width: 50%;\n\n &.done {\n margin-inline-end: 0;\n margin-inline-start: 0;\n }\n }\n }\n\n .icon {\n margin-inline-end: 16px;\n }\n}\n\n.modal-overlay {\n background: var(--newtab-overlay-color);\n height: 100%;\n left: 0;\n position: fixed;\n top: 0;\n width: 100%;\n z-index: 11001;\n}\n\n.modal {\n background: var(--newtab-modal-color);\n border: $border-secondary;\n border-radius: 5px;\n font-size: 15px;\n z-index: 11002;\n}\n", - ".card-outer {\n @include context-menu-button;\n background: var(--newtab-card-background-color);\n border-radius: $border-radius;\n display: inline-block;\n height: $card-height;\n margin-inline-end: $base-gutter;\n position: relative;\n width: 100%;\n\n &.placeholder {\n background: transparent;\n\n .card {\n box-shadow: inset $inner-box-shadow;\n }\n\n .card-preview-image-outer,\n .card-context {\n display: none;\n }\n }\n\n .card {\n border-radius: $border-radius;\n box-shadow: var(--newtab-card-shadow);\n height: 100%;\n }\n\n > a {\n color: inherit;\n display: block;\n height: 100%;\n outline: none;\n position: absolute;\n width: 100%;\n\n &:-moz-any(.active, :focus) {\n .card {\n @include fade-in-card;\n }\n\n .card-title {\n color: var(--newtab-link-primary-color);\n }\n }\n }\n\n &:-moz-any(:hover, :focus, .active):not(.placeholder) {\n @include fade-in-card;\n @include context-menu-button-hover;\n outline: none;\n\n .card-title {\n color: var(--newtab-link-primary-color);\n }\n\n .alternate ~ .card-host-name {\n display: none;\n }\n\n .card-host-name.alternate {\n display: block;\n }\n }\n\n .card-preview-image-outer {\n background-color: $grey-30;\n border-radius: $border-radius $border-radius 0 0;\n height: $card-preview-image-height;\n overflow: hidden;\n position: relative;\n\n &::after {\n border-bottom: 1px solid var(--newtab-card-hairline-color);\n bottom: 0;\n content: '';\n position: absolute;\n width: 100%;\n }\n\n .card-preview-image {\n background-position: center;\n background-repeat: no-repeat;\n background-size: cover;\n height: 100%;\n opacity: 0;\n transition: opacity 1s $photon-easing;\n width: 100%;\n\n &.loaded {\n opacity: 1;\n }\n }\n }\n\n .card-details {\n padding: 15px 16px 12px;\n }\n\n .card-text {\n max-height: 4 * $card-text-line-height + $card-title-margin;\n overflow: hidden;\n\n &.no-host-name,\n &.no-context {\n max-height: 5 * $card-text-line-height + $card-title-margin;\n }\n\n &.no-host-name.no-context {\n max-height: 6 * $card-text-line-height + $card-title-margin;\n }\n\n &:not(.no-description) .card-title {\n max-height: 3 * $card-text-line-height;\n overflow: hidden;\n }\n }\n\n .card-host-name {\n color: var(--newtab-text-secondary-color);\n font-size: 10px;\n overflow: hidden;\n padding-bottom: 4px;\n text-overflow: ellipsis;\n text-transform: uppercase;\n white-space: nowrap;\n }\n\n .card-host-name.alternate { display: none; }\n\n .card-title {\n font-size: 14px;\n font-weight: 600;\n line-height: $card-text-line-height;\n margin: 0 0 $card-title-margin;\n word-wrap: break-word;\n }\n\n .card-description {\n font-size: 12px;\n line-height: $card-text-line-height;\n margin: 0;\n overflow: hidden;\n word-wrap: break-word;\n }\n\n .card-context {\n bottom: 0;\n color: var(--newtab-text-secondary-color);\n display: flex;\n font-size: 11px;\n offset-inline-start: 0;\n padding: 9px 16px 9px 14px;\n position: absolute;\n }\n\n .card-context-icon {\n fill: var(--newtab-text-secondary-color);\n height: 22px;\n margin-inline-end: 6px;\n }\n\n .card-context-label {\n flex-grow: 1;\n line-height: 22px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n\n.normal-cards {\n .card-outer {\n // Wide layout styles\n @media (min-width: $break-point-widest) {\n $line-height: 23px;\n height: $card-height-large;\n\n .card-preview-image-outer {\n height: $card-preview-image-height-large;\n }\n\n .card-details {\n padding: 13px 16px 12px;\n }\n\n .card-text {\n max-height: 6 * $line-height + $card-title-margin;\n }\n\n .card-host-name {\n font-size: 12px;\n padding-bottom: 5px;\n }\n\n .card-title {\n font-size: 17px;\n line-height: $line-height;\n margin-bottom: 0;\n }\n\n .card-text:not(.no-description) {\n .card-title {\n max-height: 3 * $line-height;\n }\n }\n\n .card-description {\n font-size: 15px;\n line-height: $line-height;\n }\n\n .card-context {\n bottom: 4px;\n font-size: 14px;\n }\n }\n }\n}\n\n.compact-cards {\n $card-detail-vertical-spacing: 12px;\n $card-title-font-size: 12px;\n\n .card-outer {\n height: $card-height-compact;\n\n .card-preview-image-outer {\n height: $card-preview-image-height-compact;\n }\n\n .card-details {\n padding: $card-detail-vertical-spacing 16px;\n }\n\n .card-host-name {\n line-height: 10px;\n }\n\n .card-text {\n .card-title,\n &:not(.no-description) .card-title {\n font-size: $card-title-font-size;\n line-height: $card-title-font-size + 1;\n max-height: $card-title-font-size + 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n }\n\n .card-description {\n display: none;\n }\n\n .card-context {\n $icon-size: 16px;\n $container-size: 32px;\n background-color: var(--newtab-card-background-color);\n border-radius: $container-size / 2;\n clip-path: inset(-1px -1px $container-size - ($card-height-compact - $card-preview-image-height-compact - 2 * $card-detail-vertical-spacing));\n height: $container-size;\n width: $container-size;\n padding: ($container-size - $icon-size) / 2;\n top: $card-preview-image-height-compact - $icon-size;\n offset-inline-end: 12px;\n offset-inline-start: auto;\n\n &::after {\n border: 1px solid var(--newtab-card-hairline-color);\n border-bottom: 0;\n border-radius: ($container-size / 2) + 1 ($container-size / 2) + 1 0 0;\n content: '';\n position: absolute;\n height: ($container-size + 2) / 2;\n width: $container-size + 2;\n top: -1px;\n left: -1px;\n }\n\n .card-context-icon {\n margin-inline-end: 0;\n height: $icon-size;\n width: $icon-size;\n\n &.icon-bookmark-added {\n fill: $bookmark-icon-fill;\n }\n\n &.icon-download {\n fill: $download-icon-fill;\n }\n\n &.icon-pocket {\n fill: $pocket-icon-fill;\n }\n }\n\n .card-context-label {\n display: none;\n }\n }\n }\n\n @media not all and (min-width: $break-point-widest) {\n .hide-for-narrow {\n display: none;\n }\n }\n}\n", + ".card-outer {\n @include context-menu-button;\n background: var(--newtab-card-background-color);\n border-radius: $border-radius;\n display: inline-block;\n height: $card-height;\n margin-inline-end: $base-gutter;\n position: relative;\n width: 100%;\n\n &.placeholder {\n background: transparent;\n\n .card {\n box-shadow: inset $inner-box-shadow;\n }\n\n .card-preview-image-outer,\n .card-context {\n display: none;\n }\n }\n\n .card {\n border-radius: $border-radius;\n box-shadow: var(--newtab-card-shadow);\n height: 100%;\n }\n\n > a {\n color: inherit;\n display: block;\n height: 100%;\n outline: none;\n position: absolute;\n width: 100%;\n\n &:-moz-any(.active, :focus) {\n .card {\n @include fade-in-card;\n }\n\n .card-title {\n color: var(--newtab-link-primary-color);\n }\n }\n }\n\n &:-moz-any(:hover, :focus, .active):not(.placeholder) {\n @include fade-in-card;\n @include context-menu-button-hover;\n outline: none;\n\n .card-title {\n color: var(--newtab-link-primary-color);\n }\n\n .alternate ~ .card-host-name {\n display: none;\n }\n\n .card-host-name.alternate {\n display: block;\n }\n }\n\n .card-preview-image-outer {\n background-color: $grey-30;\n border-radius: $border-radius $border-radius 0 0;\n height: $card-preview-image-height;\n overflow: hidden;\n position: relative;\n\n &::after {\n border-bottom: 1px solid var(--newtab-card-hairline-color);\n bottom: 0;\n content: '';\n position: absolute;\n width: 100%;\n }\n\n .card-preview-image {\n background-position: center;\n background-repeat: no-repeat;\n background-size: cover;\n height: 100%;\n opacity: 0;\n transition: opacity 1s $photon-easing;\n width: 100%;\n\n &.loaded {\n opacity: 1;\n }\n }\n }\n\n .card-details {\n padding: 15px 16px 12px;\n }\n\n .card-text {\n max-height: 4 * $card-text-line-height + $card-title-margin;\n overflow: hidden;\n\n &.no-host-name,\n &.no-context {\n max-height: 5 * $card-text-line-height + $card-title-margin;\n }\n\n &.no-host-name.no-context {\n max-height: 6 * $card-text-line-height + $card-title-margin;\n }\n\n &:not(.no-description) .card-title {\n max-height: 3 * $card-text-line-height;\n overflow: hidden;\n }\n }\n\n .card-host-name {\n color: var(--newtab-text-secondary-color);\n font-size: 10px;\n overflow: hidden;\n padding-bottom: 4px;\n text-overflow: ellipsis;\n text-transform: uppercase;\n white-space: nowrap;\n }\n\n .card-host-name.alternate { display: none; }\n\n .card-title {\n font-size: 14px;\n font-weight: 600;\n line-height: $card-text-line-height;\n margin: 0 0 $card-title-margin;\n word-wrap: break-word;\n }\n\n .card-description {\n font-size: 12px;\n line-height: $card-text-line-height;\n margin: 0;\n overflow: hidden;\n word-wrap: break-word;\n }\n\n .card-context {\n bottom: 0;\n color: var(--newtab-text-secondary-color);\n display: flex;\n font-size: 11px;\n inset-inline-start: 0;\n padding: 9px 16px 9px 14px;\n position: absolute;\n }\n\n .card-context-icon {\n fill: var(--newtab-text-secondary-color);\n height: 22px;\n margin-inline-end: 6px;\n }\n\n .card-context-label {\n flex-grow: 1;\n line-height: 22px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n\n.normal-cards {\n .card-outer {\n // Wide layout styles\n @media (min-width: $break-point-widest) {\n $line-height: 23px;\n height: $card-height-large;\n\n .card-preview-image-outer {\n height: $card-preview-image-height-large;\n }\n\n .card-details {\n padding: 13px 16px 12px;\n }\n\n .card-text {\n max-height: 6 * $line-height + $card-title-margin;\n }\n\n .card-host-name {\n font-size: 12px;\n padding-bottom: 5px;\n }\n\n .card-title {\n font-size: 17px;\n line-height: $line-height;\n margin-bottom: 0;\n }\n\n .card-text:not(.no-description) {\n .card-title {\n max-height: 3 * $line-height;\n }\n }\n\n .card-description {\n font-size: 15px;\n line-height: $line-height;\n }\n\n .card-context {\n bottom: 4px;\n font-size: 14px;\n }\n }\n }\n}\n\n.compact-cards {\n $card-detail-vertical-spacing: 12px;\n $card-title-font-size: 12px;\n\n .card-outer {\n height: $card-height-compact;\n\n .card-preview-image-outer {\n height: $card-preview-image-height-compact;\n }\n\n .card-details {\n padding: $card-detail-vertical-spacing 16px;\n }\n\n .card-host-name {\n line-height: 10px;\n }\n\n .card-text {\n .card-title,\n &:not(.no-description) .card-title {\n font-size: $card-title-font-size;\n line-height: $card-title-font-size + 1;\n max-height: $card-title-font-size + 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n }\n\n .card-description {\n display: none;\n }\n\n .card-context {\n $icon-size: 16px;\n $container-size: 32px;\n background-color: var(--newtab-card-background-color);\n border-radius: $container-size / 2;\n clip-path: inset(-1px -1px $container-size - ($card-height-compact - $card-preview-image-height-compact - 2 * $card-detail-vertical-spacing));\n height: $container-size;\n width: $container-size;\n padding: ($container-size - $icon-size) / 2;\n top: $card-preview-image-height-compact - $icon-size;\n inset-inline-end: 12px;\n inset-inline-start: auto;\n\n &::after {\n border: 1px solid var(--newtab-card-hairline-color);\n border-bottom: 0;\n border-radius: ($container-size / 2) + 1 ($container-size / 2) + 1 0 0;\n content: '';\n position: absolute;\n height: ($container-size + 2) / 2;\n width: $container-size + 2;\n top: -1px;\n left: -1px;\n }\n\n .card-context-icon {\n margin-inline-end: 0;\n height: $icon-size;\n width: $icon-size;\n\n &.icon-bookmark-added {\n fill: $bookmark-icon-fill;\n }\n\n &.icon-download {\n fill: $download-icon-fill;\n }\n\n &.icon-pocket {\n fill: $pocket-icon-fill;\n }\n }\n\n .card-context-label {\n display: none;\n }\n }\n }\n\n @media not all and (min-width: $break-point-widest) {\n .hide-for-narrow {\n display: none;\n }\n }\n}\n", ".manual-migration-container {\n color: var(--newtab-text-conditional-color);\n font-size: 13px;\n line-height: 15px;\n margin-bottom: $section-spacing;\n text-align: center;\n\n @media (min-width: $break-point-medium) {\n display: flex;\n justify-content: space-between;\n text-align: left;\n }\n\n p {\n margin: 0;\n @media (min-width: $break-point-medium) {\n align-self: center;\n display: flex;\n justify-content: space-between;\n }\n }\n\n .icon {\n display: none;\n @media (min-width: $break-point-medium) {\n align-self: center;\n display: block;\n fill: var(--newtab-icon-secondary-color);\n margin-inline-end: 6px;\n }\n }\n}\n\n.manual-migration-actions {\n border: 0;\n display: block;\n flex-wrap: nowrap;\n\n @media (min-width: $break-point-medium) {\n display: flex;\n justify-content: space-between;\n padding: 0;\n }\n\n button {\n align-self: center;\n height: 26px;\n margin: 0;\n margin-inline-start: 20px;\n padding: 0 12px;\n }\n}\n", - ".collapsible-section {\n padding: $section-vertical-padding $section-horizontal-padding;\n transition-delay: 100ms;\n transition-duration: 100ms;\n transition-property: background-color;\n\n .section-title {\n font-size: $section-title-font-size;\n font-weight: bold;\n margin: 0;\n text-transform: uppercase;\n\n span {\n color: var(--newtab-section-header-text-color);\n display: inline-block;\n fill: var(--newtab-section-header-text-color);\n vertical-align: middle;\n }\n\n .click-target {\n cursor: pointer;\n vertical-align: top;\n white-space: nowrap;\n }\n\n .collapsible-arrow {\n margin-inline-start: 8px;\n margin-top: -1px;\n }\n }\n\n .section-top-bar {\n height: 19px;\n margin-bottom: 13px;\n position: relative;\n\n .context-menu-button {\n background: url('chrome://browser/skin/page-action.svg') no-repeat right center;\n border: 0;\n cursor: pointer;\n fill: var(--newtab-section-header-text-color);\n height: 100%;\n offset-inline-end: 0;\n opacity: 0;\n position: absolute;\n top: 0;\n transition-duration: 200ms;\n transition-property: opacity;\n width: $context-menu-button-size;\n\n &:-moz-any(:active, :focus, :hover) {\n fill: $grey-90;\n opacity: 1;\n }\n }\n\n .context-menu {\n top: 16px;\n }\n\n @media (max-width: $break-point-widest + $card-width * 1.5) {\n @include context-menu-open-left;\n }\n }\n\n &:hover,\n &.active {\n .section-top-bar {\n .context-menu-button {\n opacity: 1;\n }\n }\n }\n\n &.active {\n background: var(--newtab-element-hover-color);\n border-radius: 4px;\n\n .section-top-bar {\n .context-menu-button {\n fill: var(--newtab-section-active-contextmenu-color);\n }\n }\n }\n\n .section-disclaimer {\n $max-button-width: 130px;\n $min-button-height: 26px;\n\n color: var(--newtab-text-conditional-color);\n font-size: 13px;\n margin-bottom: 16px;\n position: relative;\n\n .section-disclaimer-text {\n display: inline-block;\n min-height: $min-button-height;\n width: calc(100% - #{$max-button-width});\n\n @media (max-width: $break-point-medium) {\n width: $card-width;\n }\n }\n\n a {\n color: var(--newtab-link-primary-color);\n font-weight: bold;\n padding-left: 3px;\n }\n\n button {\n background: var(--newtab-button-secondary-color);\n border: 1px solid $grey-40;\n border-radius: 4px;\n cursor: pointer;\n margin-top: 2px;\n max-width: $max-button-width;\n min-height: $min-button-height;\n offset-inline-end: 0;\n\n &:hover:not(.dismiss) {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n }\n\n @media (min-width: $break-point-small) {\n position: absolute;\n }\n }\n }\n\n .section-body-fallback {\n height: $card-height;\n }\n\n .section-body {\n // This is so the top sites favicon and card dropshadows don't get clipped during animation:\n $horizontal-padding: 7px;\n margin: 0 (-$horizontal-padding);\n padding: 0 $horizontal-padding;\n\n &.animating {\n overflow: hidden;\n pointer-events: none;\n }\n }\n\n &.animation-enabled {\n .section-title {\n .collapsible-arrow {\n transition: transform 0.5s $photon-easing;\n }\n }\n\n .section-body {\n transition: max-height 0.5s $photon-easing;\n }\n }\n\n &.collapsed {\n .section-body {\n max-height: 0;\n overflow: hidden;\n }\n }\n}\n", + ".collapsible-section {\n padding: $section-vertical-padding $section-horizontal-padding;\n transition-delay: 100ms;\n transition-duration: 100ms;\n transition-property: background-color;\n\n .section-title {\n font-size: $section-title-font-size;\n font-weight: bold;\n margin: 0;\n text-transform: uppercase;\n\n span {\n color: var(--newtab-section-header-text-color);\n display: inline-block;\n fill: var(--newtab-section-header-text-color);\n vertical-align: middle;\n }\n\n .click-target {\n cursor: pointer;\n vertical-align: top;\n white-space: nowrap;\n }\n\n .collapsible-arrow {\n margin-inline-start: 8px;\n margin-top: -1px;\n }\n }\n\n .section-top-bar {\n height: 19px;\n margin-bottom: 13px;\n position: relative;\n\n .context-menu-button {\n background: url('chrome://browser/skin/page-action.svg') no-repeat right center;\n border: 0;\n cursor: pointer;\n fill: var(--newtab-section-header-text-color);\n height: 100%;\n inset-inline-end: 0;\n opacity: 0;\n position: absolute;\n top: 0;\n transition-duration: 200ms;\n transition-property: opacity;\n width: $context-menu-button-size;\n\n &:-moz-any(:active, :focus, :hover) {\n fill: $grey-90;\n opacity: 1;\n }\n }\n\n .context-menu {\n top: 16px;\n }\n\n @media (max-width: $break-point-widest + $card-width * 1.5) {\n @include context-menu-open-left;\n }\n }\n\n &:hover,\n &.active {\n .section-top-bar {\n .context-menu-button {\n opacity: 1;\n }\n }\n }\n\n &.active {\n background: var(--newtab-element-hover-color);\n border-radius: 4px;\n\n .section-top-bar {\n .context-menu-button {\n fill: var(--newtab-section-active-contextmenu-color);\n }\n }\n }\n\n .section-disclaimer {\n $max-button-width: 130px;\n $min-button-height: 26px;\n\n color: var(--newtab-text-conditional-color);\n font-size: 13px;\n margin-bottom: 16px;\n position: relative;\n\n .section-disclaimer-text {\n display: inline-block;\n min-height: $min-button-height;\n width: calc(100% - #{$max-button-width});\n\n @media (max-width: $break-point-medium) {\n width: $card-width;\n }\n }\n\n a {\n color: var(--newtab-link-primary-color);\n font-weight: bold;\n padding-left: 3px;\n }\n\n button {\n background: var(--newtab-button-secondary-color);\n border: 1px solid $grey-40;\n border-radius: 4px;\n cursor: pointer;\n margin-top: 2px;\n max-width: $max-button-width;\n min-height: $min-button-height;\n inset-inline-end: 0;\n\n &:hover:not(.dismiss) {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n }\n\n @media (min-width: $break-point-small) {\n position: absolute;\n }\n }\n }\n\n .section-body-fallback {\n height: $card-height;\n }\n\n .section-body {\n // This is so the top sites favicon and card dropshadows don't get clipped during animation:\n $horizontal-padding: 7px;\n margin: 0 (-$horizontal-padding);\n padding: 0 $horizontal-padding;\n\n &.animating {\n overflow: hidden;\n pointer-events: none;\n }\n }\n\n &.animation-enabled {\n .section-title {\n .collapsible-arrow {\n transition: transform 0.5s $photon-easing;\n }\n }\n\n .section-body {\n transition: max-height 0.5s $photon-easing;\n }\n }\n\n &.collapsed {\n .section-body {\n max-height: 0;\n overflow: hidden;\n }\n }\n}\n", "\n.asrouter-admin {\n $border-color: var(--newtab-border-secondary-color);\n $monospace: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Mono', 'Droid Sans Mono', 'Source Code Pro', monospace;\n max-width: 996px;\n margin: 0 auto;\n font-size: 14px;\n // Reset .outer-wrapper styles\n display: inherit;\n padding: 0 0 92px;\n\n h1 {\n font-weight: 200;\n font-size: 32px;\n }\n\n table {\n border-collapse: collapse;\n width: 100%;\n }\n\n .message-item {\n &:first-child td {\n border-top: 1px solid $border-color;\n }\n\n td {\n vertical-align: top;\n border-bottom: 1px solid $border-color;\n padding: 8px;\n\n &:first-child {\n border-left: 1px solid $border-color;\n }\n\n &:last-child {\n border-right: 1px solid $border-color;\n }\n }\n\n &.current {\n .message-id span {\n background: $yellow-50;\n padding: 2px 5px;\n\n .dark-theme & {\n color: $black;\n }\n }\n }\n\n &.blocked {\n .message-id,\n .message-summary {\n opacity: 0.5;\n }\n\n .message-id {\n opacity: 0.5;\n }\n }\n\n .message-id {\n font-family: $monospace;\n font-size: 12px;\n }\n }\n\n pre {\n background: var(--newtab-textbox-background-color);\n margin: 0;\n padding: 8px;\n font-size: 12px;\n max-width: 750px;\n overflow: auto;\n font-family: $monospace;\n }\n}\n", ".ASRouterButton {\n white-space: nowrap;\n border-radius: 4px;\n border: 1px solid var(--newtab-border-secondary-color);\n background-color: var(--newtab-button-secondary-color);\n font-family: inherit;\n padding: 8px 15px;\n margin-inline-start: 12px;\n color: inherit;\n .tall & {\n margin-inline-start: 20px;\n }\n}\n", - ".SnippetBaseContainer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: var(--newtab-snippets-background-color);\n color: var(--newtab-text-primary-color);\n font-size: 12px;\n line-height: 16px;\n border-top: 1px solid var(--newtab-snippets-hairline-color);\n box-shadow: $shadow-secondary;\n display: flex;\n align-items: center;\n\n .innerWrapper {\n margin: 0 auto;\n display: flex;\n align-items: center;\n padding: 12px $section-horizontal-padding;\n\n // This is to account for the block button on smaller screens\n padding-inline-end: 36px;\n @media (min-width: $break-point-large) {\n padding-inline-end: $section-horizontal-padding;\n }\n\n max-width: $wrapper-max-width-large;\n @media (min-width: $break-point-widest) {\n max-width: $wrapper-max-width-widest;\n }\n }\n\n .blockButton {\n display: none;\n background: none;\n border: 0;\n position: absolute;\n top: 50%;\n offset-inline-end: 12px;\n height: 16px;\n width: 16px;\n background-image: url('resource://activity-stream/data/content/assets/glyph-dismiss-16.svg');\n -moz-context-properties: fill;\n fill: var(--newtab-icon-primary-color);\n opacity: 0.5;\n margin-top: -8px;\n padding: 0;\n cursor: pointer;\n\n @media (min-width: 766px) {\n offset-inline-end: 24px;\n }\n }\n\n &:hover .blockButton {\n display: block;\n }\n}\n", + ".SnippetBaseContainer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: var(--newtab-snippets-background-color);\n color: var(--newtab-text-primary-color);\n font-size: 12px;\n line-height: 16px;\n border-top: 1px solid var(--newtab-snippets-hairline-color);\n box-shadow: $shadow-secondary;\n display: flex;\n align-items: center;\n\n .innerWrapper {\n margin: 0 auto;\n display: flex;\n align-items: center;\n padding: 12px $section-horizontal-padding;\n\n // This is to account for the block button on smaller screens\n padding-inline-end: 36px;\n @media (min-width: $break-point-large) {\n padding-inline-end: $section-horizontal-padding;\n }\n\n max-width: $wrapper-max-width-large;\n @media (min-width: $break-point-widest) {\n max-width: $wrapper-max-width-widest;\n }\n }\n\n .blockButton {\n display: none;\n background: none;\n border: 0;\n position: absolute;\n top: 50%;\n inset-inline-end: 12px;\n height: 16px;\n width: 16px;\n background-image: url('resource://activity-stream/data/content/assets/glyph-dismiss-16.svg');\n -moz-context-properties: fill;\n fill: var(--newtab-icon-primary-color);\n opacity: 0.5;\n margin-top: -8px;\n padding: 0;\n cursor: pointer;\n\n @media (min-width: 766px) {\n inset-inline-end: 24px;\n }\n }\n\n &:hover .blockButton {\n display: block;\n }\n}\n", ".activity-stream {\n &.modal-open {\n overflow: hidden;\n }\n}\n.modalOverlayOuter {\n background: $white;\n opacity: 0.93;\n height: 100%;\n position: fixed;\n top: 0;\n width: 100%;\n display: none;\n z-index: 1100;\n\n &.active {\n display: block;\n }\n}\n\n.modalOverlayInner {\n width: 960px;\n height: 510px;\n position: fixed;\n top: calc(50% - 255px); // halfway down minus half the height of the modal\n left: calc(50% - 480px); // halfway across minus half the width of the modal\n background: $white;\n box-shadow: 0 1px 15px 0 $black-30;\n border-radius: 4px;\n display: none;\n z-index: 1101;\n\n\n // modal takes over entire screen\n @media(max-width: 960px) {\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n box-shadow: none;\n border-radius: 0;\n }\n\n // if modal is short enough, add a vertical scroll bar\n @media(max-width: 850px) and (max-height: 730px) {\n overflow-y: scroll;\n }\n\n // if modal is narrow enough, add a vertical scroll bar\n @media(max-width: 650px) and (max-height: 600px) {\n overflow-y: scroll;\n }\n\n &.active {\n display: block;\n }\n\n h2 {\n color: $grey-60;\n text-align: center;\n font-weight: 200;\n margin-top: 30px;\n font-size: 28px;\n line-height: 37px;\n letter-spacing: -0.13px;\n\n @media(max-width: 960px) {\n margin-top: 100px;\n }\n\n @media(max-width: 850px) {\n margin-top: 30px;\n }\n }\n\n .footer {\n border-top: 1px solid $grey-30;\n height: 70px;\n width: 100%;\n position: absolute;\n bottom: 0;\n text-align: center;\n background-color: $white;\n\n // if modal is short enough, footer becomes sticky\n @media(max-width: 850px) and (max-height: 730px) {\n position: sticky;\n }\n\n // if modal is narrow enough, footer becomes sticky\n @media(max-width: 650px) and (max-height: 600px) {\n position: sticky;\n }\n\n .modalButton {\n margin-top: 20px;\n width: 150px;\n height: 30px;\n padding: 4px 0 6px 0;\n font-size: 15px;\n }\n }\n}\n", ".SimpleSnippet {\n &.tall {\n padding: 27px 0;\n }\n\n .title {\n display: inline;\n font-size: inherit;\n margin: 0;\n }\n\n .titleIcon {\n background-repeat: no-repeat;\n background-size: 14px;\n height: 16px;\n width: 16px;\n margin-top: 2px;\n margin-inline-end: 2px;\n display: inline-block;\n vertical-align: top;\n }\n\n .body {\n display: inline;\n margin: 0;\n }\n\n .icon {\n height: 42px;\n width: 42px;\n margin-inline-end: 12px;\n flex-shrink: 0;\n }\n &.tall .icon {\n margin-inline-end: 20px;\n }\n\n .ASRouterAnchor {\n color: inherit;\n text-decoration: underline;\n }\n}\n", ".onboardingMessageContainer {\n display: grid;\n grid-column-gap: 21px;\n grid-template-columns: auto auto auto;\n padding-left: 30px;\n padding-right: 30px;\n\n // at 850px, the cards go from vertical layout to horizontal layout\n @media(max-width: 850px) {\n grid-template-columns: none;\n grid-template-rows: auto auto auto;\n padding-left: 110px;\n padding-right: 110px;\n }\n}\n\n.onboardingMessage {\n height: 340px;\n text-align: center;\n padding: 13px;\n font-weight: 200;\n\n // at 850px, img floats left, content floats right next to it\n @media(max-width: 850px) {\n height: 170px;\n text-align: left;\n padding: 10px;\n border-bottom: 1px solid #D8D8D8;\n display: flex;\n margin-bottom: 11px;\n\n &:last-child {\n border: none;\n }\n\n .onboardingContent {\n padding-left: 10px;\n height: 100%;\n\n > span > h3 {\n margin-top: 0;\n margin-bottom: 4px;\n font-weight: 400;\n }\n\n > span > p {\n margin-top: 0;\n line-height: 22px;\n font-size: 15px;\n }\n }\n }\n\n @media(max-width: 650px) {\n height: 250px;\n }\n\n .onboardingMessageImage {\n height: 100px;\n width: 120px;\n background-size: 120px;\n background-position: center center;\n background-repeat: no-repeat;\n display: inline-block;\n vertical-align: middle;\n\n\n @media(max-width: 850px) {\n height: 75px;\n min-width: 80px;\n background-size: 80px;\n }\n\n &.addons {\n background-image: url(\"resource://activity-stream/data/content/assets/illustration-addons@2x.png\");\n }\n\n &.privatebrowsing {\n background-image: url(\"resource://activity-stream/data/content/assets/illustration-privatebrowsing@2x.png\");\n }\n\n &.screenshots {\n background-image: url(\"resource://activity-stream/data/content/assets/illustration-screenshots@2x.png\");\n }\n\n &.gift {\n background-image: url(\"resource://activity-stream/data/content/assets/illustration-gift@2x.png\");\n }\n }\n\n .onboardingContent {\n height: 175px;\n\n > span > h3 {\n color: $grey-90;\n margin-bottom: 8px;\n font-weight: 400;\n }\n\n > span > p {\n color: $grey-60;\n margin-top: 0;\n height: 130px;\n margin-bottom: 12px;\n font-size: 15px;\n line-height: 22px;\n\n @media(max-width: 650px) {\n margin-bottom: 0px;\n }\n }\n }\n\n .onboardingButton {\n background-color: $grey-90-10;\n border: none;\n width: 150px;\n height: 30px;\n margin-bottom: 23px;\n padding: 4px 0 6px 0;\n font-size: 15px;\n\n // at 850px, the button shimmies down and to the right\n @media(max-width: 850px) {\n float: right;\n margin-top: -60px;\n margin-right: -10px;\n }\n\n @media(max-width: 650px) {\n float: none;\n margin-top: 30px;\n }\n }\n\n\n &::before {\n content: '';\n height: 220px;\n width: 1px;\n position: absolute;\n background-color: #D8D8D8;\n margin-top: 40px;\n margin-left: 215px;\n\n // at 850px, the line goes from vertical to horizontal\n @media(max-width: 850px) {\n content: none;\n }\n }\n\n &:last-child::before {\n content: none;\n }\n}\n" diff --git a/browser/extensions/activity-stream/css/activity-stream-mac.css b/browser/extensions/activity-stream/css/activity-stream-mac.css index 3fc623d81bc4..28adb3629bfa 100644 --- a/browser/extensions/activity-stream/css/activity-stream-mac.css +++ b/browser/extensions/activity-stream/css/activity-stream-mac.css @@ -372,7 +372,7 @@ main { border: 0; cursor: pointer; fill: var(--newtab-icon-primary-color); - offset-inline-end: 15px; + inset-inline-end: 15px; padding: 15px; position: fixed; top: 15px; @@ -408,56 +408,56 @@ main { .top-sites-list :nth-child(2n+1) .context-menu { margin-inline-end: auto; margin-inline-start: auto; - offset-inline-end: auto; - offset-inline-start: -32px; } + inset-inline-end: auto; + inset-inline-start: -32px; } .top-sites-list :nth-child(2n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 482px) and (max-width: 610px) { .top-sites-list :nth-child(3n+2) .context-menu, .top-sites-list :nth-child(3n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 610px) and (max-width: 866px) { .top-sites-list :nth-child(4n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 610px) and (max-width: 834px) { .top-sites-list :nth-child(4n+3) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 866px) and (max-width: 1314px) { .top-sites-list :nth-child(6n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 866px) and (max-width: 1090px) { .top-sites-list :nth-child(6n+5) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 1122px) and (max-width: 1570px) { .top-sites-list :nth-child(8n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 1122px) and (max-width: 1346px) { .top-sites-list :nth-child(8n+7) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media not all and (min-width: 1122px) { .top-sites-list .hide-for-narrow { display: none; } } @@ -493,7 +493,7 @@ main { cursor: pointer; fill: var(--newtab-icon-primary-color); height: 27px; - offset-inline-end: -13.5px; + inset-inline-end: -13.5px; opacity: 0; position: absolute; top: -13.5px; @@ -544,14 +544,14 @@ main { .top-site-outer .rich-icon { background-size: cover; height: 100%; - offset-inline-start: 0; + inset-inline-start: 0; top: 0; width: 100%; } .top-site-outer .default-icon { background-size: 32px; bottom: -6px; height: 42px; - offset-inline-end: -6px; + inset-inline-end: -6px; width: 42px; align-items: center; display: flex; @@ -569,7 +569,7 @@ main { position: relative; } .top-site-outer .title .icon { fill: var(--newtab-icon-tertiary-color); - offset-inline-start: 0; + inset-inline-start: 0; position: absolute; top: 10px; } .top-site-outer .title span { @@ -640,7 +640,7 @@ main { position: absolute; transform: translateY(-50%); top: 50%; - offset-inline-end: 8px; } + inset-inline-end: 8px; } .topsite-form .form-wrapper .url input:dir(ltr) { padding-right: 32px; } .topsite-form .form-wrapper .url input:dir(rtl) { @@ -664,7 +664,7 @@ main { position: absolute; transform: translateY(-50%); top: 50%; - offset-inline-end: 8px; } + inset-inline-end: 8px; } .topsite-form .form-wrapper .custom-image-input-container .loading-animation { width: 960px; height: 16px; @@ -705,7 +705,7 @@ main { background: #D70022; border-radius: 2px; color: #FFF; - offset-inline-start: 3px; + inset-inline-start: 3px; padding: 5px 12px; position: absolute; top: 44px; @@ -715,7 +715,7 @@ main { bottom: -8px; content: '.'; height: 16px; - offset-inline-start: 12px; + inset-inline-start: 12px; position: absolute; text-indent: -999px; top: -7px; @@ -753,26 +753,26 @@ main { .sections-list .section-list .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 610px) and (max-width: 866px) { .sections-list .section-list :nth-child(2n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 866px) and (max-width: 1314px) { .sections-list .section-list :nth-child(3n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 1122px) and (max-width: 1570px) { .sections-list .section-list :nth-child(3n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } .sections-list .section-empty-state { border: 1px solid var(--newtab-border-secondary-color); @@ -1102,7 +1102,7 @@ main { -moz-context-properties: fill; fill: var(--newtab-search-icon-color); height: 100%; - offset-inline-end: 0; + inset-inline-end: 0; position: absolute; width: 36px; } .search-wrapper .search-button:focus, .search-wrapper .search-button:hover { @@ -1161,7 +1161,7 @@ main { display: block; font-size: 14px; margin-inline-start: 5px; - offset-inline-start: 100%; + inset-inline-start: 100%; position: absolute; top: 6.75px; z-index: 10000; } @@ -1264,7 +1264,7 @@ main { cursor: pointer; fill: var(--newtab-icon-primary-color); height: 27px; - offset-inline-end: -13.5px; + inset-inline-end: -13.5px; opacity: 0; position: absolute; top: -13.5px; @@ -1372,7 +1372,7 @@ main { color: var(--newtab-text-secondary-color); display: flex; font-size: 11px; - offset-inline-start: 0; + inset-inline-start: 0; padding: 9px 16px 9px 14px; position: absolute; } .card-outer .card-context-icon { @@ -1437,8 +1437,8 @@ main { width: 32px; padding: 8px; top: 92px; - offset-inline-end: 12px; - offset-inline-start: auto; } + inset-inline-end: 12px; + inset-inline-start: auto; } .compact-cards .card-outer .card-context::after { border: 1px solid var(--newtab-card-hairline-color); border-bottom: 0; @@ -1541,7 +1541,7 @@ main { cursor: pointer; fill: var(--newtab-section-header-text-color); height: 100%; - offset-inline-end: 0; + inset-inline-end: 0; opacity: 0; position: absolute; top: 0; @@ -1557,8 +1557,8 @@ main { .collapsible-section .section-top-bar .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } .collapsible-section:hover .section-top-bar .context-menu-button, .collapsible-section.active .section-top-bar .context-menu-button { opacity: 1; } .collapsible-section.active { @@ -1590,7 +1590,7 @@ main { margin-top: 2px; max-width: 130px; min-height: 26px; - offset-inline-end: 0; } + inset-inline-end: 0; } .collapsible-section .section-disclaimer button:hover:not(.dismiss) { box-shadow: 0 0 0 5px var(--newtab-card-active-outline-color); transition: box-shadow 150ms; } @@ -1701,7 +1701,7 @@ main { border: 0; position: absolute; top: 50%; - offset-inline-end: 12px; + inset-inline-end: 12px; height: 16px; width: 16px; background-image: url("resource://activity-stream/data/content/assets/glyph-dismiss-16.svg"); @@ -1713,7 +1713,7 @@ main { cursor: pointer; } @media (min-width: 766px) { .SnippetBaseContainer .blockButton { - offset-inline-end: 24px; } } + inset-inline-end: 24px; } } .SnippetBaseContainer:hover .blockButton { display: block; } diff --git a/browser/extensions/activity-stream/css/activity-stream-mac.css.map b/browser/extensions/activity-stream/css/activity-stream-mac.css.map index ae8fefdcc679..a3a4042ddf80 100644 --- a/browser/extensions/activity-stream/css/activity-stream-mac.css.map +++ b/browser/extensions/activity-stream/css/activity-stream-mac.css.map @@ -31,24 +31,24 @@ "/* This is the mac variant */ // sass-lint:disable-line no-css-comments\n\n$os-infopanel-arrow-height: 10px;\n$os-infopanel-arrow-offset-end: 7px;\n$os-infopanel-arrow-width: 18px;\n\n.dark-theme {\n -moz-osx-font-smoothing: grayscale;\n}\n\n@import './activity-stream';\n", "@import './normalize';\n@import './variables';\n@import './theme';\n@import './icons';\n\nhtml {\n height: 100%;\n}\n\nbody,\n#root { // sass-lint:disable-line no-ids\n min-height: 100vh;\n}\n\nbody {\n background-color: var(--newtab-background-color);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Ubuntu', 'Helvetica Neue', sans-serif;\n font-size: 16px;\n overflow-y: scroll;\n}\n\nh1,\nh2 {\n font-weight: normal;\n}\n\na {\n text-decoration: none;\n}\n\n// For screen readers\n.sr-only {\n border: 0;\n clip: rect(0, 0, 0, 0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n position: absolute;\n width: 1px;\n}\n\n.inner-border {\n border: $border-secondary;\n border-radius: $border-radius;\n height: 100%;\n left: 0;\n pointer-events: none;\n position: absolute;\n top: 0;\n width: 100%;\n z-index: 100;\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 1;\n }\n}\n\n.show-on-init {\n opacity: 0;\n transition: opacity 0.2s ease-in;\n\n &.on {\n animation: fadeIn 0.2s;\n opacity: 1;\n }\n}\n\n.actions {\n border-top: $border-secondary;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n margin: 0;\n padding: 15px 25px 0;\n}\n\n// Default button (grey)\n.button,\n.actions button {\n background-color: var(--newtab-button-secondary-color);\n border: $border-primary;\n border-radius: 4px;\n color: inherit;\n cursor: pointer;\n margin-bottom: 15px;\n padding: 10px 30px;\n white-space: nowrap;\n\n &:hover:not(.dismiss) {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n }\n\n &.dismiss {\n background-color: transparent;\n border: 0;\n padding: 0;\n text-decoration: underline;\n }\n\n // Blue button\n &.primary,\n &.done {\n background-color: var(--newtab-button-primary-color);\n border: solid 1px var(--newtab-button-primary-color);\n color: $white;\n margin-inline-start: auto;\n }\n}\n\ninput {\n &[type='text'],\n &[type='search'] {\n border-radius: $border-radius;\n }\n}\n\n// Make sure snippets show up above other UI elements\n#snippets-container { // sass-lint:disable-line no-ids\n z-index: 1;\n}\n\n// Components\n@import '../components/Base/Base';\n@import '../components/ErrorBoundary/ErrorBoundary';\n@import '../components/TopSites/TopSites';\n@import '../components/Sections/Sections';\n@import '../components/StartupOverlay/StartupOverlay';\n@import '../components/Topics/Topics';\n@import '../components/Search/Search';\n@import '../components/ContextMenu/ContextMenu';\n@import '../components/ConfirmDialog/ConfirmDialog';\n@import '../components/Card/Card';\n@import '../components/ManualMigration/ManualMigration';\n@import '../components/CollapsibleSection/CollapsibleSection';\n@import '../components/ASRouterAdmin/ASRouterAdmin';\n\n// AS Router\n@import '../asrouter/components/Button/Button';\n@import '../asrouter/components/SnippetBase/SnippetBase';\n@import '../asrouter/components/ModalOverlay/ModalOverlay';\n@import '../asrouter/templates/SimpleSnippet/SimpleSnippet';\n@import '../asrouter/templates/OnboardingMessage/OnboardingMessage';\n", "html {\n box-sizing: border-box;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\n*::-moz-focus-inner {\n border: 0;\n}\n\nbody {\n margin: 0;\n}\n\nbutton,\ninput {\n background-color: inherit;\n color: inherit;\n font-family: inherit;\n font-size: inherit;\n}\n\n[hidden] {\n display: none !important; // sass-lint:disable-line no-important\n}\n", - "// Photon colors from http://design.firefox.com/photon/visuals/color.html\n$blue-40: #45A1FF;\n$blue-50: #0A84FF;\n$blue-60: #0060DF;\n$blue-70: #003EAA;\n$blue-80: #002275;\n$grey-10: #F9F9FA;\n$grey-20: #EDEDF0;\n$grey-30: #D7D7DB;\n$grey-40: #B1B1B3;\n$grey-50: #737373;\n$grey-60: #4A4A4F;\n$grey-70: #38383D;\n$grey-80: #2A2A2E;\n$grey-90: #0C0C0D;\n$teal-70: #008EA4;\n$red-60: #D70022;\n$yellow-50: #FFE900;\n\n// Photon opacity from http://design.firefox.com/photon/visuals/color.html#opacity\n$grey-10-10: rgba($grey-10, 0.1);\n$grey-10-20: rgba($grey-10, 0.2);\n$grey-10-40: rgba($grey-10, 0.4);\n$grey-10-60: rgba($grey-10, 0.6);\n$grey-10-80: rgba($grey-10, 0.8);\n$grey-20-60: rgba($grey-20, 0.6);\n$grey-20-80: rgba($grey-20, 0.8);\n$grey-30-60: rgba($grey-30, 0.6);\n$grey-90-10: rgba($grey-90, 0.1);\n$grey-90-20: rgba($grey-90, 0.2);\n$grey-90-30: rgba($grey-90, 0.3);\n$grey-90-40: rgba($grey-90, 0.4);\n$grey-90-50: rgba($grey-90, 0.5);\n$grey-90-60: rgba($grey-90, 0.6);\n$grey-90-70: rgba($grey-90, 0.7);\n$grey-90-80: rgba($grey-90, 0.8);\n$grey-90-90: rgba($grey-90, 0.9);\n\n$black: #000;\n$black-5: rgba($black, 0.05);\n$black-10: rgba($black, 0.1);\n$black-15: rgba($black, 0.15);\n$black-20: rgba($black, 0.2);\n$black-25: rgba($black, 0.25);\n$black-30: rgba($black, 0.3);\n\n// Other colors\n$white: #FFF;\n$white-10: rgba($white, 0.1);\n$pocket-teal: #50BCB6;\n$bookmark-icon-fill: #0A84FF;\n$download-icon-fill: #12BC00;\n$pocket-icon-fill: #D70022;\n\n// Photon transitions from http://design.firefox.com/photon/motion/duration-and-easing.html\n$photon-easing: cubic-bezier(0.07, 0.95, 0, 1);\n\n$border-radius: 3px;\n\n// Grid related styles\n$base-gutter: 32px;\n$section-horizontal-padding: 25px;\n$section-vertical-padding: 10px;\n$section-spacing: 40px - $section-vertical-padding * 2;\n$grid-unit: 96px; // 1 top site\n\n$icon-size: 16px;\n$smaller-icon-size: 12px;\n$larger-icon-size: 32px;\n\n$wrapper-default-width: $grid-unit * 2 + $base-gutter * 1 + $section-horizontal-padding * 2; // 2 top sites\n$wrapper-max-width-small: $grid-unit * 3 + $base-gutter * 2 + $section-horizontal-padding * 2; // 3 top sites\n$wrapper-max-width-medium: $grid-unit * 4 + $base-gutter * 3 + $section-horizontal-padding * 2; // 4 top sites\n$wrapper-max-width-large: $grid-unit * 6 + $base-gutter * 5 + $section-horizontal-padding * 2; // 6 top sites\n$wrapper-max-width-widest: $grid-unit * 8 + $base-gutter * 7 + $section-horizontal-padding * 2; // 8 top sites\n// For the breakpoints, we need to add space for the scrollbar to avoid weird\n// layout issues when the scrollbar is visible. 16px is wide enough to cover all\n// OSes and keeps it simpler than a per-OS value.\n$scrollbar-width: 16px;\n$break-point-small: $wrapper-max-width-small + $base-gutter * 2 + $scrollbar-width;\n$break-point-medium: $wrapper-max-width-medium + $base-gutter * 2 + $scrollbar-width;\n$break-point-large: $wrapper-max-width-large + $base-gutter * 2 + $scrollbar-width;\n$break-point-widest: $wrapper-max-width-widest + $base-gutter * 2 + $scrollbar-width;\n\n$section-title-font-size: 13px;\n\n$card-width: $grid-unit * 2 + $base-gutter;\n$card-height: 266px;\n$card-preview-image-height: 122px;\n$card-title-margin: 2px;\n$card-text-line-height: 19px;\n// Larger cards for wider screens:\n$card-width-large: 309px;\n$card-height-large: 370px;\n$card-preview-image-height-large: 155px;\n// Compact cards for Highlights\n$card-height-compact: 160px;\n$card-preview-image-height-compact: 108px;\n\n$topic-margin-top: 12px;\n\n$context-menu-button-size: 27px;\n$context-menu-button-boxshadow: 0 2px $grey-90-10;\n$context-menu-shadow: 0 5px 10px $black-30, 0 0 0 1px $black-20;\n$context-menu-font-size: 14px;\n$context-menu-border-radius: 5px;\n$context-menu-outer-padding: 5px;\n$context-menu-item-padding: 3px 12px;\n\n$error-fallback-font-size: 12px;\n$error-fallback-line-height: 1.5;\n\n$image-path: '../data/content/assets/';\n\n$snippets-container-height: 120px;\n\n$textbox-shadow-size: 4px;\n\n@mixin fade-in {\n box-shadow: inset $inner-box-shadow, $shadow-primary;\n transition: box-shadow 150ms;\n}\n\n@mixin fade-in-card {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n}\n\n@mixin context-menu-button {\n .context-menu-button {\n background-clip: padding-box;\n background-color: var(--newtab-contextmenu-button-color);\n background-image: url('chrome://browser/skin/page-action.svg');\n background-position: 55%;\n border: $border-primary;\n border-radius: 100%;\n box-shadow: $context-menu-button-boxshadow;\n cursor: pointer;\n fill: var(--newtab-icon-primary-color);\n height: $context-menu-button-size;\n offset-inline-end: -($context-menu-button-size / 2);\n opacity: 0;\n position: absolute;\n top: -($context-menu-button-size / 2);\n transform: scale(0.25);\n transition-duration: 200ms;\n transition-property: transform, opacity;\n width: $context-menu-button-size;\n\n &:-moz-any(:active, :focus) {\n opacity: 1;\n transform: scale(1);\n }\n }\n}\n\n@mixin context-menu-button-hover {\n .context-menu-button {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@mixin context-menu-open-middle {\n .context-menu {\n margin-inline-end: auto;\n margin-inline-start: auto;\n offset-inline-end: auto;\n offset-inline-start: -$base-gutter;\n }\n}\n\n@mixin context-menu-open-left {\n .context-menu {\n margin-inline-end: 5px;\n margin-inline-start: auto;\n offset-inline-end: 0;\n offset-inline-start: auto;\n }\n}\n\n@mixin flip-icon {\n &:dir(rtl) {\n transform: scaleX(-1);\n }\n}\n", + "// Photon colors from http://design.firefox.com/photon/visuals/color.html\n$blue-40: #45A1FF;\n$blue-50: #0A84FF;\n$blue-60: #0060DF;\n$blue-70: #003EAA;\n$blue-80: #002275;\n$grey-10: #F9F9FA;\n$grey-20: #EDEDF0;\n$grey-30: #D7D7DB;\n$grey-40: #B1B1B3;\n$grey-50: #737373;\n$grey-60: #4A4A4F;\n$grey-70: #38383D;\n$grey-80: #2A2A2E;\n$grey-90: #0C0C0D;\n$teal-70: #008EA4;\n$red-60: #D70022;\n$yellow-50: #FFE900;\n\n// Photon opacity from http://design.firefox.com/photon/visuals/color.html#opacity\n$grey-10-10: rgba($grey-10, 0.1);\n$grey-10-20: rgba($grey-10, 0.2);\n$grey-10-40: rgba($grey-10, 0.4);\n$grey-10-60: rgba($grey-10, 0.6);\n$grey-10-80: rgba($grey-10, 0.8);\n$grey-20-60: rgba($grey-20, 0.6);\n$grey-20-80: rgba($grey-20, 0.8);\n$grey-30-60: rgba($grey-30, 0.6);\n$grey-90-10: rgba($grey-90, 0.1);\n$grey-90-20: rgba($grey-90, 0.2);\n$grey-90-30: rgba($grey-90, 0.3);\n$grey-90-40: rgba($grey-90, 0.4);\n$grey-90-50: rgba($grey-90, 0.5);\n$grey-90-60: rgba($grey-90, 0.6);\n$grey-90-70: rgba($grey-90, 0.7);\n$grey-90-80: rgba($grey-90, 0.8);\n$grey-90-90: rgba($grey-90, 0.9);\n\n$black: #000;\n$black-5: rgba($black, 0.05);\n$black-10: rgba($black, 0.1);\n$black-15: rgba($black, 0.15);\n$black-20: rgba($black, 0.2);\n$black-25: rgba($black, 0.25);\n$black-30: rgba($black, 0.3);\n\n// Other colors\n$white: #FFF;\n$white-10: rgba($white, 0.1);\n$pocket-teal: #50BCB6;\n$bookmark-icon-fill: #0A84FF;\n$download-icon-fill: #12BC00;\n$pocket-icon-fill: #D70022;\n\n// Photon transitions from http://design.firefox.com/photon/motion/duration-and-easing.html\n$photon-easing: cubic-bezier(0.07, 0.95, 0, 1);\n\n$border-radius: 3px;\n\n// Grid related styles\n$base-gutter: 32px;\n$section-horizontal-padding: 25px;\n$section-vertical-padding: 10px;\n$section-spacing: 40px - $section-vertical-padding * 2;\n$grid-unit: 96px; // 1 top site\n\n$icon-size: 16px;\n$smaller-icon-size: 12px;\n$larger-icon-size: 32px;\n\n$wrapper-default-width: $grid-unit * 2 + $base-gutter * 1 + $section-horizontal-padding * 2; // 2 top sites\n$wrapper-max-width-small: $grid-unit * 3 + $base-gutter * 2 + $section-horizontal-padding * 2; // 3 top sites\n$wrapper-max-width-medium: $grid-unit * 4 + $base-gutter * 3 + $section-horizontal-padding * 2; // 4 top sites\n$wrapper-max-width-large: $grid-unit * 6 + $base-gutter * 5 + $section-horizontal-padding * 2; // 6 top sites\n$wrapper-max-width-widest: $grid-unit * 8 + $base-gutter * 7 + $section-horizontal-padding * 2; // 8 top sites\n// For the breakpoints, we need to add space for the scrollbar to avoid weird\n// layout issues when the scrollbar is visible. 16px is wide enough to cover all\n// OSes and keeps it simpler than a per-OS value.\n$scrollbar-width: 16px;\n$break-point-small: $wrapper-max-width-small + $base-gutter * 2 + $scrollbar-width;\n$break-point-medium: $wrapper-max-width-medium + $base-gutter * 2 + $scrollbar-width;\n$break-point-large: $wrapper-max-width-large + $base-gutter * 2 + $scrollbar-width;\n$break-point-widest: $wrapper-max-width-widest + $base-gutter * 2 + $scrollbar-width;\n\n$section-title-font-size: 13px;\n\n$card-width: $grid-unit * 2 + $base-gutter;\n$card-height: 266px;\n$card-preview-image-height: 122px;\n$card-title-margin: 2px;\n$card-text-line-height: 19px;\n// Larger cards for wider screens:\n$card-width-large: 309px;\n$card-height-large: 370px;\n$card-preview-image-height-large: 155px;\n// Compact cards for Highlights\n$card-height-compact: 160px;\n$card-preview-image-height-compact: 108px;\n\n$topic-margin-top: 12px;\n\n$context-menu-button-size: 27px;\n$context-menu-button-boxshadow: 0 2px $grey-90-10;\n$context-menu-shadow: 0 5px 10px $black-30, 0 0 0 1px $black-20;\n$context-menu-font-size: 14px;\n$context-menu-border-radius: 5px;\n$context-menu-outer-padding: 5px;\n$context-menu-item-padding: 3px 12px;\n\n$error-fallback-font-size: 12px;\n$error-fallback-line-height: 1.5;\n\n$image-path: '../data/content/assets/';\n\n$snippets-container-height: 120px;\n\n$textbox-shadow-size: 4px;\n\n@mixin fade-in {\n box-shadow: inset $inner-box-shadow, $shadow-primary;\n transition: box-shadow 150ms;\n}\n\n@mixin fade-in-card {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n}\n\n@mixin context-menu-button {\n .context-menu-button {\n background-clip: padding-box;\n background-color: var(--newtab-contextmenu-button-color);\n background-image: url('chrome://browser/skin/page-action.svg');\n background-position: 55%;\n border: $border-primary;\n border-radius: 100%;\n box-shadow: $context-menu-button-boxshadow;\n cursor: pointer;\n fill: var(--newtab-icon-primary-color);\n height: $context-menu-button-size;\n inset-inline-end: -($context-menu-button-size / 2);\n opacity: 0;\n position: absolute;\n top: -($context-menu-button-size / 2);\n transform: scale(0.25);\n transition-duration: 200ms;\n transition-property: transform, opacity;\n width: $context-menu-button-size;\n\n &:-moz-any(:active, :focus) {\n opacity: 1;\n transform: scale(1);\n }\n }\n}\n\n@mixin context-menu-button-hover {\n .context-menu-button {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@mixin context-menu-open-middle {\n .context-menu {\n margin-inline-end: auto;\n margin-inline-start: auto;\n inset-inline-end: auto;\n inset-inline-start: -$base-gutter;\n }\n}\n\n@mixin context-menu-open-left {\n .context-menu {\n margin-inline-end: 5px;\n margin-inline-start: auto;\n inset-inline-end: 0;\n inset-inline-start: auto;\n }\n}\n\n@mixin flip-icon {\n &:dir(rtl) {\n transform: scaleX(-1);\n }\n}\n", "@function textbox-shadow($color) {\n @return 0 0 0 1px $color, 0 0 0 $textbox-shadow-size rgba($color, 0.3);\n}\n\n@mixin textbox-focus($color) {\n --newtab-textbox-focus-color: $color;\n --newtab-textbox-focus-boxshadow: textbox-shadow($color);\n}\n\n// scss variables related to the theme.\n$border-primary: 1px solid var(--newtab-border-primary-color);\n$border-secondary: 1px solid var(--newtab-border-secondary-color);\n$inner-box-shadow: 0 0 0 1px var(--newtab-inner-box-shadow-color);\n$input-border: 1px solid var(--newtab-textbox-border);\n$input-border-active: 1px solid var(--newtab-textbox-focus-color);\n$input-error-border: 1px solid $red-60;\n$input-error-boxshadow: textbox-shadow($red-60);\n$shadow-primary: 0 0 0 5px var(--newtab-card-active-outline-color);\n$shadow-secondary: 0 1px 4px 0 $grey-90-20;\n\n// Default theme\nbody {\n // General styles\n --newtab-background-color: $grey-10;\n --newtab-border-primary-color: $grey-40;\n --newtab-border-secondary-color: $grey-30;\n --newtab-button-primary-color: $blue-60;\n --newtab-button-secondary-color: inherit;\n --newtab-element-active-color: $grey-30-60;\n --newtab-element-hover-color: $grey-20;\n --newtab-icon-primary-color: $grey-90-80;\n --newtab-icon-secondary-color: $grey-90-60;\n --newtab-icon-tertiary-color: $grey-30;\n --newtab-inner-box-shadow-color: $black-10;\n --newtab-link-primary-color: $blue-60;\n --newtab-link-secondary-color: $teal-70;\n --newtab-text-conditional-color: $grey-60;\n --newtab-text-primary-color: $grey-90;\n --newtab-text-secondary-color: $grey-50;\n --newtab-textbox-background-color: $white;\n --newtab-textbox-border: $grey-90-20;\n @include textbox-focus($blue-60); // sass-lint:disable-line mixins-before-declarations\n\n // Context menu\n --newtab-contextmenu-background-color: $grey-10;\n --newtab-contextmenu-button-color: $white;\n\n // Modal + overlay\n --newtab-modal-color: $white;\n --newtab-overlay-color: $grey-20-80;\n\n // Sections\n --newtab-section-header-text-color: $grey-50;\n --newtab-section-navigation-text-color: $grey-50;\n --newtab-section-active-contextmenu-color: $grey-90;\n\n // Search\n --newtab-search-border-color: transparent;\n --newtab-search-dropdown-color: $white;\n --newtab-search-dropdown-header-color: $grey-10;\n --newtab-search-icon-color: $grey-90-40;\n\n // Top Sites\n --newtab-topsites-background-color: $white;\n --newtab-topsites-icon-shadow: inset $inner-box-shadow;\n --newtab-topsites-label-color: inherit;\n\n // Cards\n --newtab-card-active-outline-color: $grey-30;\n --newtab-card-background-color: $white;\n --newtab-card-hairline-color: $black-10;\n --newtab-card-shadow: 0 1px 4px 0 $grey-90-10;\n\n // Snippets\n --newtab-snippets-background-color: $white;\n --newtab-snippets-hairline-color: transparent;\n}\n\n// Dark theme\n.dark-theme {\n // General styles\n --newtab-background-color: $grey-80;\n --newtab-border-primary-color: $grey-10-80;\n --newtab-border-secondary-color: $grey-10-10;\n --newtab-button-primary-color: $blue-60;\n --newtab-button-secondary-color: $grey-70;\n --newtab-element-active-color: $grey-10-20;\n --newtab-element-hover-color: $grey-10-10;\n --newtab-icon-primary-color: $grey-10-80;\n --newtab-icon-secondary-color: $grey-10-40;\n --newtab-icon-tertiary-color: $grey-10-40;\n --newtab-inner-box-shadow-color: $grey-10-20;\n --newtab-link-primary-color: $blue-40;\n --newtab-link-secondary-color: $pocket-teal;\n --newtab-text-conditional-color: $grey-10;\n --newtab-text-primary-color: $grey-10;\n --newtab-text-secondary-color: $grey-10-80;\n --newtab-textbox-background-color: $grey-70;\n --newtab-textbox-border: $grey-10-20;\n @include textbox-focus($blue-40); // sass-lint:disable-line mixins-before-declarations\n\n // Context menu\n --newtab-contextmenu-background-color: $grey-60;\n --newtab-contextmenu-button-color: $grey-80;\n\n // Modal + overlay\n --newtab-modal-color: $grey-80;\n --newtab-overlay-color: $grey-90-80;\n\n // Sections\n --newtab-section-header-text-color: $grey-10-80;\n --newtab-section-navigation-text-color: $grey-10-80;\n --newtab-section-active-contextmenu-color: $white;\n\n // Search\n --newtab-search-border-color: $grey-10-20;\n --newtab-search-dropdown-color: $grey-70;\n --newtab-search-dropdown-header-color: $grey-60;\n --newtab-search-icon-color: $grey-10-60;\n\n // Top Sites\n --newtab-topsites-background-color: $grey-70;\n --newtab-topsites-icon-shadow: none;\n --newtab-topsites-label-color: $grey-10-80;\n\n // Cards\n --newtab-card-active-outline-color: $grey-60;\n --newtab-card-background-color: $grey-70;\n --newtab-card-hairline-color: $grey-10-10;\n --newtab-card-shadow: 0 1px 8px 0 $grey-90-20;\n\n // Snippets\n --newtab-snippets-background-color: $grey-70;\n --newtab-snippets-hairline-color: $white-10;\n}\n", ".icon {\n background-position: center center;\n background-repeat: no-repeat;\n background-size: $icon-size;\n -moz-context-properties: fill;\n display: inline-block;\n fill: var(--newtab-icon-primary-color);\n height: $icon-size;\n vertical-align: middle;\n width: $icon-size;\n\n &.icon-spacer {\n margin-inline-end: 8px;\n }\n\n &.icon-small-spacer {\n margin-inline-end: 6px;\n }\n\n &.icon-bookmark-added {\n background-image: url('chrome://browser/skin/bookmark.svg');\n }\n\n &.icon-bookmark-hollow {\n background-image: url('chrome://browser/skin/bookmark-hollow.svg');\n }\n\n &.icon-clear-input {\n fill: var(--newtab-icon-secondary-color);\n background-image: url('#{$image-path}glyph-cancel-16.svg');\n }\n\n &.icon-delete {\n background-image: url('#{$image-path}glyph-delete-16.svg');\n }\n\n &.icon-search {\n background-image: url('chrome://browser/skin/search-glass.svg');\n }\n\n &.icon-modal-delete {\n flex-shrink: 0;\n background-image: url('#{$image-path}glyph-modal-delete-32.svg');\n background-size: $larger-icon-size;\n height: $larger-icon-size;\n width: $larger-icon-size;\n }\n\n &.icon-dismiss {\n background-image: url('#{$image-path}glyph-dismiss-16.svg');\n }\n\n &.icon-info {\n background-image: url('#{$image-path}glyph-info-16.svg');\n }\n\n &.icon-import {\n background-image: url('#{$image-path}glyph-import-16.svg');\n }\n\n &.icon-new-window {\n @include flip-icon;\n background-image: url('#{$image-path}glyph-newWindow-16.svg');\n }\n\n &.icon-new-window-private {\n background-image: url('chrome://browser/skin/privateBrowsing.svg');\n }\n\n &.icon-settings {\n background-image: url('chrome://browser/skin/settings.svg');\n }\n\n &.icon-pin {\n @include flip-icon;\n background-image: url('#{$image-path}glyph-pin-16.svg');\n }\n\n &.icon-unpin {\n @include flip-icon;\n background-image: url('#{$image-path}glyph-unpin-16.svg');\n }\n\n &.icon-edit {\n background-image: url('#{$image-path}glyph-edit-16.svg');\n }\n\n &.icon-pocket {\n background-image: url('#{$image-path}glyph-pocket-16.svg');\n }\n\n &.icon-history-item {\n background-image: url('chrome://browser/skin/history.svg');\n }\n\n &.icon-trending {\n background-image: url('#{$image-path}glyph-trending-16.svg');\n transform: translateY(2px); // trending bolt is visually top heavy\n }\n\n &.icon-now {\n background-image: url('chrome://browser/skin/history.svg');\n }\n\n &.icon-topsites {\n background-image: url('#{$image-path}glyph-topsites-16.svg');\n }\n\n &.icon-pin-small {\n @include flip-icon;\n background-image: url('#{$image-path}glyph-pin-12.svg');\n background-size: $smaller-icon-size;\n height: $smaller-icon-size;\n width: $smaller-icon-size;\n }\n\n &.icon-check {\n background-image: url('chrome://browser/skin/check.svg');\n }\n\n &.icon-download {\n background-image: url('chrome://browser/skin/downloads/download-icons.svg#arrow-with-bar');\n }\n\n &.icon-copy {\n background-image: url('chrome://browser/skin/edit-copy.svg');\n }\n\n &.icon-open-file {\n background-image: url('#{$image-path}glyph-open-file-16.svg');\n }\n\n &.icon-webextension {\n background-image: url('#{$image-path}glyph-webextension-16.svg');\n }\n\n &.icon-highlights {\n background-image: url('#{$image-path}glyph-highlights-16.svg');\n }\n\n &.icon-arrowhead-down {\n background-image: url('#{$image-path}glyph-arrowhead-down-16.svg');\n }\n\n &.icon-arrowhead-down-small {\n background-image: url('#{$image-path}glyph-arrowhead-down-12.svg');\n background-size: $smaller-icon-size;\n height: $smaller-icon-size;\n width: $smaller-icon-size;\n }\n\n &.icon-arrowhead-forward-small {\n background-image: url('#{$image-path}glyph-arrowhead-down-12.svg');\n background-size: $smaller-icon-size;\n height: $smaller-icon-size;\n transform: rotate(-90deg);\n width: $smaller-icon-size;\n\n &:dir(rtl) {\n transform: rotate(90deg);\n }\n }\n\n &.icon-arrowhead-up {\n background-image: url('#{$image-path}glyph-arrowhead-down-16.svg');\n transform: rotate(180deg);\n }\n\n &.icon-add {\n background-image: url('#{$image-path}glyph-add-16.svg');\n }\n\n &.icon-minimize {\n background-image: url('#{$image-path}glyph-minimize-16.svg');\n }\n\n &.icon-maximize {\n background-image: url('#{$image-path}glyph-maximize-16.svg');\n }\n}\n", - ".outer-wrapper {\n color: var(--newtab-text-primary-color);\n display: flex;\n flex-grow: 1;\n min-height: 100vh;\n padding: ($section-spacing + $section-vertical-padding) $base-gutter $base-gutter;\n\n &.fixed-to-top {\n display: block;\n }\n\n a {\n color: var(--newtab-link-primary-color);\n }\n}\n\nmain {\n margin: auto;\n // Offset the snippets container so things at the bottom of the page are still\n // visible when snippets / onboarding are visible. Adjust for other spacing.\n padding-bottom: $snippets-container-height - $section-spacing - $base-gutter;\n width: $wrapper-default-width;\n\n @media (min-width: $break-point-small) {\n width: $wrapper-max-width-small;\n }\n\n @media (min-width: $break-point-medium) {\n width: $wrapper-max-width-medium;\n }\n\n @media (min-width: $break-point-large) {\n width: $wrapper-max-width-large;\n }\n\n @media (min-width: $break-point-widest) {\n width: $wrapper-max-width-widest;\n }\n\n section {\n margin-bottom: $section-spacing;\n position: relative;\n }\n}\n\n.base-content-fallback {\n // Make the error message be centered against the viewport\n height: 100vh;\n}\n\n.body-wrapper {\n // Hide certain elements so the page structure is fixed, e.g., placeholders,\n // while avoiding flashes of changing content, e.g., icons and text\n $selectors-to-hide: '\n .section-title,\n .sections-list .section:last-of-type,\n .topic\n ';\n\n #{$selectors-to-hide} {\n opacity: 0;\n }\n\n &.on {\n #{$selectors-to-hide} {\n opacity: 1;\n }\n }\n}\n\n.non-collapsible-section {\n padding: 0 $section-horizontal-padding;\n}\n\n.prefs-button {\n button {\n background-color: transparent;\n border: 0;\n cursor: pointer;\n fill: var(--newtab-icon-primary-color);\n offset-inline-end: 15px;\n padding: 15px;\n position: fixed;\n top: 15px;\n z-index: 1000;\n\n &:hover,\n &:focus {\n background-color: var(--newtab-element-hover-color);\n }\n\n &:active {\n background-color: var(--newtab-element-active-color);\n }\n }\n}\n", + ".outer-wrapper {\n color: var(--newtab-text-primary-color);\n display: flex;\n flex-grow: 1;\n min-height: 100vh;\n padding: ($section-spacing + $section-vertical-padding) $base-gutter $base-gutter;\n\n &.fixed-to-top {\n display: block;\n }\n\n a {\n color: var(--newtab-link-primary-color);\n }\n}\n\nmain {\n margin: auto;\n // Offset the snippets container so things at the bottom of the page are still\n // visible when snippets / onboarding are visible. Adjust for other spacing.\n padding-bottom: $snippets-container-height - $section-spacing - $base-gutter;\n width: $wrapper-default-width;\n\n @media (min-width: $break-point-small) {\n width: $wrapper-max-width-small;\n }\n\n @media (min-width: $break-point-medium) {\n width: $wrapper-max-width-medium;\n }\n\n @media (min-width: $break-point-large) {\n width: $wrapper-max-width-large;\n }\n\n @media (min-width: $break-point-widest) {\n width: $wrapper-max-width-widest;\n }\n\n section {\n margin-bottom: $section-spacing;\n position: relative;\n }\n}\n\n.base-content-fallback {\n // Make the error message be centered against the viewport\n height: 100vh;\n}\n\n.body-wrapper {\n // Hide certain elements so the page structure is fixed, e.g., placeholders,\n // while avoiding flashes of changing content, e.g., icons and text\n $selectors-to-hide: '\n .section-title,\n .sections-list .section:last-of-type,\n .topic\n ';\n\n #{$selectors-to-hide} {\n opacity: 0;\n }\n\n &.on {\n #{$selectors-to-hide} {\n opacity: 1;\n }\n }\n}\n\n.non-collapsible-section {\n padding: 0 $section-horizontal-padding;\n}\n\n.prefs-button {\n button {\n background-color: transparent;\n border: 0;\n cursor: pointer;\n fill: var(--newtab-icon-primary-color);\n inset-inline-end: 15px;\n padding: 15px;\n position: fixed;\n top: 15px;\n z-index: 1000;\n\n &:hover,\n &:focus {\n background-color: var(--newtab-element-hover-color);\n }\n\n &:active {\n background-color: var(--newtab-element-active-color);\n }\n }\n}\n", ".as-error-fallback {\n align-items: center;\n border-radius: $border-radius;\n box-shadow: inset $inner-box-shadow;\n color: var(--newtab-text-conditional-color);\n display: flex;\n flex-direction: column;\n font-size: $error-fallback-font-size;\n justify-content: center;\n justify-items: center;\n line-height: $error-fallback-line-height;\n\n a {\n color: var(--newtab-text-conditional-color);\n text-decoration: underline;\n }\n}\n", - "$top-sites-size: $grid-unit;\n$top-sites-border-radius: 6px;\n$top-sites-title-height: 30px;\n$top-sites-vertical-space: 8px;\n$screenshot-size: cover;\n$rich-icon-size: 96px;\n$default-icon-wrapper-size: 42px;\n$default-icon-size: 32px;\n$default-icon-offset: 6px;\n$half-base-gutter: $base-gutter / 2;\n\n.top-sites {\n // Take back the margin from the bottom row of vertical spacing as well as the\n // extra whitespace below the title text as it's vertically centered.\n margin-bottom: $section-spacing - ($top-sites-vertical-space + $top-sites-title-height / 3);\n}\n\n.top-sites-list {\n list-style: none;\n margin: 0 (-$half-base-gutter);\n padding: 0;\n\n // Two columns\n @media (max-width: $break-point-small) {\n :nth-child(2n+1) {\n @include context-menu-open-middle;\n }\n\n :nth-child(2n) {\n @include context-menu-open-left;\n }\n }\n\n // Three columns\n @media (min-width: $break-point-small) and (max-width: $break-point-medium) {\n :nth-child(3n+2),\n :nth-child(3n) {\n @include context-menu-open-left;\n }\n }\n\n // Four columns\n @media (min-width: $break-point-medium) and (max-width: $break-point-large) {\n :nth-child(4n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-medium) and (max-width: $break-point-medium + $card-width) {\n :nth-child(4n+3) {\n @include context-menu-open-left;\n }\n }\n\n // Six columns\n @media (min-width: $break-point-large) and (max-width: $break-point-large + 2 * $card-width) {\n :nth-child(6n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-large) and (max-width: $break-point-large + $card-width) {\n :nth-child(6n+5) {\n @include context-menu-open-left;\n }\n }\n\n // Eight columns\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + 2 * $card-width) {\n :nth-child(8n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + $card-width) {\n :nth-child(8n+7) {\n @include context-menu-open-left;\n }\n }\n\n @media not all and (min-width: $break-point-widest) {\n .hide-for-narrow {\n display: none;\n }\n }\n\n li {\n margin: 0 0 $top-sites-vertical-space;\n }\n\n &:not(.dnd-active) {\n .top-site-outer:-moz-any(.active, :focus, :hover) {\n .tile {\n @include fade-in;\n }\n\n @include context-menu-button-hover;\n }\n }\n}\n\n// container for drop zone\n.top-site-outer {\n padding: 0 $half-base-gutter;\n display: inline-block;\n\n // container for context menu\n .top-site-inner {\n position: relative;\n\n > a {\n color: inherit;\n display: block;\n outline: none;\n\n &:-moz-any(.active, :focus) {\n .tile {\n @include fade-in;\n }\n }\n }\n }\n\n @include context-menu-button;\n\n .tile { // sass-lint:disable-block property-sort-order\n border-radius: $top-sites-border-radius;\n box-shadow: inset $inner-box-shadow, var(--newtab-card-shadow);\n height: $top-sites-size;\n position: relative;\n width: $top-sites-size;\n\n // For letter fallback\n align-items: center;\n color: var(--newtab-text-secondary-color);\n display: flex;\n font-size: 32px;\n font-weight: 200;\n justify-content: center;\n text-transform: uppercase;\n\n &::before {\n content: attr(data-fallback);\n }\n }\n\n .screenshot {\n background-color: $white;\n background-position: top left;\n background-size: $screenshot-size;\n border-radius: $top-sites-border-radius;\n box-shadow: inset $inner-box-shadow;\n height: 100%;\n left: 0;\n opacity: 0;\n position: absolute;\n top: 0;\n transition: opacity 1s;\n width: 100%;\n\n &.active {\n opacity: 1;\n }\n }\n\n // Some common styles for all icons (rich and default) in top sites\n .top-site-icon {\n background-color: var(--newtab-topsites-background-color);\n background-position: center center;\n background-repeat: no-repeat;\n border-radius: $top-sites-border-radius;\n box-shadow: var(--newtab-topsites-icon-shadow);\n position: absolute;\n }\n\n .rich-icon {\n background-size: cover;\n height: 100%;\n offset-inline-start: 0;\n top: 0;\n width: 100%;\n }\n\n .default-icon { // sass-lint:disable block property-sort-order\n background-size: $default-icon-size;\n bottom: -$default-icon-offset;\n height: $default-icon-wrapper-size;\n offset-inline-end: -$default-icon-offset;\n width: $default-icon-wrapper-size;\n\n // for corner letter fallback\n align-items: center;\n display: flex;\n font-size: 20px;\n justify-content: center;\n\n &[data-fallback]::before {\n content: attr(data-fallback);\n }\n }\n\n .title {\n color: var(--newtab-topsites-label-color);\n font: message-box;\n height: $top-sites-title-height;\n line-height: $top-sites-title-height;\n text-align: center;\n width: $top-sites-size;\n position: relative;\n\n .icon {\n fill: var(--newtab-icon-tertiary-color);\n offset-inline-start: 0;\n position: absolute;\n top: 10px;\n }\n\n span {\n height: $top-sites-title-height;\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &.pinned {\n span {\n padding: 0 13px;\n }\n }\n }\n\n .edit-button {\n background-image: url('#{$image-path}glyph-edit-16.svg');\n }\n\n &.placeholder {\n .tile {\n box-shadow: inset $inner-box-shadow;\n }\n\n .screenshot {\n display: none;\n }\n }\n\n &.dragged {\n .tile {\n background: $grey-20;\n box-shadow: none;\n\n *,\n &::before {\n display: none;\n }\n }\n\n .title {\n visibility: hidden;\n }\n }\n}\n\n.edit-topsites-wrapper {\n .modal {\n box-shadow: $shadow-secondary;\n left: 0;\n margin: 0 auto;\n position: fixed;\n right: 0;\n top: 40px;\n width: $wrapper-default-width;\n\n @media (min-width: $break-point-small) {\n width: $wrapper-max-width-small;\n }\n\n @media (min-width: $break-point-medium) {\n width: $wrapper-max-width-medium;\n }\n\n @media (min-width: $break-point-large) {\n width: $wrapper-max-width-large;\n }\n }\n}\n\n.topsite-form {\n $form-width: 300px;\n $form-spacing: 32px;\n\n .form-input-container {\n max-width: $form-width + 3 * $form-spacing + $rich-icon-size;\n margin: 0 auto;\n padding: $form-spacing;\n\n .top-site-outer {\n padding: 0;\n margin: 24px 0 0;\n margin-inline-start: $form-spacing;\n pointer-events: none;\n }\n\n .section-title {\n text-transform: none;\n font-size: 16px;\n margin: 0 0 16px;\n }\n }\n\n .fields-and-preview {\n display: flex;\n }\n\n label {\n font-size: $section-title-font-size;\n }\n\n .form-wrapper {\n width: 100%;\n\n .field {\n position: relative;\n\n .icon-clear-input {\n position: absolute;\n transform: translateY(-50%);\n top: 50%;\n offset-inline-end: 8px;\n }\n }\n\n .url {\n input:dir(ltr) {\n padding-right: 32px;\n }\n\n input:dir(rtl) {\n padding-left: 32px;\n\n &:not(:placeholder-shown) {\n direction: ltr;\n text-align: right;\n }\n }\n }\n\n .enable-custom-image-input {\n display: inline-block;\n font-size: 13px;\n margin-top: 4px;\n cursor: pointer;\n\n &:hover {\n text-decoration: underline;\n }\n }\n\n .custom-image-input-container {\n margin-top: 4px;\n\n .loading-container {\n width: 16px;\n height: 16px;\n overflow: hidden;\n position: absolute;\n transform: translateY(-50%);\n top: 50%;\n offset-inline-end: 8px;\n }\n\n // This animation is derived from Firefox's tab loading animation\n // See https://searchfox.org/mozilla-central/rev/b29daa46443b30612415c35be0a3c9c13b9dc5f6/browser/themes/shared/tabs.inc.css#208-216\n .loading-animation {\n @keyframes tab-throbber-animation {\n 100% { transform: translateX(-960px); }\n }\n\n @keyframes tab-throbber-animation-rtl {\n 100% { transform: translateX(960px); }\n }\n\n width: 960px;\n height: 16px;\n -moz-context-properties: fill;\n fill: $blue-50;\n background-image: url('chrome://browser/skin/tabbrowser/loading.svg');\n animation: tab-throbber-animation 1.05s steps(60) infinite;\n\n &:dir(rtl) {\n animation-name: tab-throbber-animation-rtl;\n }\n }\n }\n\n input {\n &[type='text'] {\n background-color: var(--newtab-textbox-background-color);\n border: $input-border;\n margin: 8px 0;\n padding: 0 8px;\n height: 32px;\n width: 100%;\n font-size: 15px;\n\n &:focus {\n border: $input-border-active;\n box-shadow: var(--newtab-textbox-focus-boxshadow);\n }\n\n &[disabled] {\n border: $input-border;\n box-shadow: none;\n opacity: 0.4;\n }\n }\n }\n\n .invalid {\n input {\n &[type='text'] {\n border: $input-error-border;\n box-shadow: $input-error-boxshadow;\n }\n }\n }\n\n .error-tooltip {\n animation: fade-up-tt 450ms;\n background: $red-60;\n border-radius: 2px;\n color: $white;\n offset-inline-start: 3px;\n padding: 5px 12px;\n position: absolute;\n top: 44px;\n z-index: 1;\n\n // tooltip caret\n &::before {\n background: $red-60;\n bottom: -8px;\n content: '.';\n height: 16px;\n offset-inline-start: 12px;\n position: absolute;\n text-indent: -999px;\n top: -7px;\n transform: rotate(45deg);\n white-space: nowrap;\n width: 16px;\n z-index: -1;\n }\n }\n }\n\n .actions {\n justify-content: flex-end;\n\n button {\n margin-inline-start: 10px;\n margin-inline-end: 0;\n }\n }\n\n @media (max-width: $break-point-small) {\n .fields-and-preview {\n flex-direction: column;\n\n .top-site-outer {\n margin-inline-start: 0;\n }\n }\n }\n}\n\n//used for tooltips below form element\n@keyframes fade-up-tt {\n 0% {\n opacity: 0;\n transform: translateY(15px);\n }\n\n 100% {\n opacity: 1;\n transform: translateY(0);\n }\n}\n", + "$top-sites-size: $grid-unit;\n$top-sites-border-radius: 6px;\n$top-sites-title-height: 30px;\n$top-sites-vertical-space: 8px;\n$screenshot-size: cover;\n$rich-icon-size: 96px;\n$default-icon-wrapper-size: 42px;\n$default-icon-size: 32px;\n$default-icon-offset: 6px;\n$half-base-gutter: $base-gutter / 2;\n\n.top-sites {\n // Take back the margin from the bottom row of vertical spacing as well as the\n // extra whitespace below the title text as it's vertically centered.\n margin-bottom: $section-spacing - ($top-sites-vertical-space + $top-sites-title-height / 3);\n}\n\n.top-sites-list {\n list-style: none;\n margin: 0 (-$half-base-gutter);\n padding: 0;\n\n // Two columns\n @media (max-width: $break-point-small) {\n :nth-child(2n+1) {\n @include context-menu-open-middle;\n }\n\n :nth-child(2n) {\n @include context-menu-open-left;\n }\n }\n\n // Three columns\n @media (min-width: $break-point-small) and (max-width: $break-point-medium) {\n :nth-child(3n+2),\n :nth-child(3n) {\n @include context-menu-open-left;\n }\n }\n\n // Four columns\n @media (min-width: $break-point-medium) and (max-width: $break-point-large) {\n :nth-child(4n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-medium) and (max-width: $break-point-medium + $card-width) {\n :nth-child(4n+3) {\n @include context-menu-open-left;\n }\n }\n\n // Six columns\n @media (min-width: $break-point-large) and (max-width: $break-point-large + 2 * $card-width) {\n :nth-child(6n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-large) and (max-width: $break-point-large + $card-width) {\n :nth-child(6n+5) {\n @include context-menu-open-left;\n }\n }\n\n // Eight columns\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + 2 * $card-width) {\n :nth-child(8n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + $card-width) {\n :nth-child(8n+7) {\n @include context-menu-open-left;\n }\n }\n\n @media not all and (min-width: $break-point-widest) {\n .hide-for-narrow {\n display: none;\n }\n }\n\n li {\n margin: 0 0 $top-sites-vertical-space;\n }\n\n &:not(.dnd-active) {\n .top-site-outer:-moz-any(.active, :focus, :hover) {\n .tile {\n @include fade-in;\n }\n\n @include context-menu-button-hover;\n }\n }\n}\n\n// container for drop zone\n.top-site-outer {\n padding: 0 $half-base-gutter;\n display: inline-block;\n\n // container for context menu\n .top-site-inner {\n position: relative;\n\n > a {\n color: inherit;\n display: block;\n outline: none;\n\n &:-moz-any(.active, :focus) {\n .tile {\n @include fade-in;\n }\n }\n }\n }\n\n @include context-menu-button;\n\n .tile { // sass-lint:disable-block property-sort-order\n border-radius: $top-sites-border-radius;\n box-shadow: inset $inner-box-shadow, var(--newtab-card-shadow);\n height: $top-sites-size;\n position: relative;\n width: $top-sites-size;\n\n // For letter fallback\n align-items: center;\n color: var(--newtab-text-secondary-color);\n display: flex;\n font-size: 32px;\n font-weight: 200;\n justify-content: center;\n text-transform: uppercase;\n\n &::before {\n content: attr(data-fallback);\n }\n }\n\n .screenshot {\n background-color: $white;\n background-position: top left;\n background-size: $screenshot-size;\n border-radius: $top-sites-border-radius;\n box-shadow: inset $inner-box-shadow;\n height: 100%;\n left: 0;\n opacity: 0;\n position: absolute;\n top: 0;\n transition: opacity 1s;\n width: 100%;\n\n &.active {\n opacity: 1;\n }\n }\n\n // Some common styles for all icons (rich and default) in top sites\n .top-site-icon {\n background-color: var(--newtab-topsites-background-color);\n background-position: center center;\n background-repeat: no-repeat;\n border-radius: $top-sites-border-radius;\n box-shadow: var(--newtab-topsites-icon-shadow);\n position: absolute;\n }\n\n .rich-icon {\n background-size: cover;\n height: 100%;\n inset-inline-start: 0;\n top: 0;\n width: 100%;\n }\n\n .default-icon { // sass-lint:disable block property-sort-order\n background-size: $default-icon-size;\n bottom: -$default-icon-offset;\n height: $default-icon-wrapper-size;\n inset-inline-end: -$default-icon-offset;\n width: $default-icon-wrapper-size;\n\n // for corner letter fallback\n align-items: center;\n display: flex;\n font-size: 20px;\n justify-content: center;\n\n &[data-fallback]::before {\n content: attr(data-fallback);\n }\n }\n\n .title {\n color: var(--newtab-topsites-label-color);\n font: message-box;\n height: $top-sites-title-height;\n line-height: $top-sites-title-height;\n text-align: center;\n width: $top-sites-size;\n position: relative;\n\n .icon {\n fill: var(--newtab-icon-tertiary-color);\n inset-inline-start: 0;\n position: absolute;\n top: 10px;\n }\n\n span {\n height: $top-sites-title-height;\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &.pinned {\n span {\n padding: 0 13px;\n }\n }\n }\n\n .edit-button {\n background-image: url('#{$image-path}glyph-edit-16.svg');\n }\n\n &.placeholder {\n .tile {\n box-shadow: inset $inner-box-shadow;\n }\n\n .screenshot {\n display: none;\n }\n }\n\n &.dragged {\n .tile {\n background: $grey-20;\n box-shadow: none;\n\n *,\n &::before {\n display: none;\n }\n }\n\n .title {\n visibility: hidden;\n }\n }\n}\n\n.edit-topsites-wrapper {\n .modal {\n box-shadow: $shadow-secondary;\n left: 0;\n margin: 0 auto;\n position: fixed;\n right: 0;\n top: 40px;\n width: $wrapper-default-width;\n\n @media (min-width: $break-point-small) {\n width: $wrapper-max-width-small;\n }\n\n @media (min-width: $break-point-medium) {\n width: $wrapper-max-width-medium;\n }\n\n @media (min-width: $break-point-large) {\n width: $wrapper-max-width-large;\n }\n }\n}\n\n.topsite-form {\n $form-width: 300px;\n $form-spacing: 32px;\n\n .form-input-container {\n max-width: $form-width + 3 * $form-spacing + $rich-icon-size;\n margin: 0 auto;\n padding: $form-spacing;\n\n .top-site-outer {\n padding: 0;\n margin: 24px 0 0;\n margin-inline-start: $form-spacing;\n pointer-events: none;\n }\n\n .section-title {\n text-transform: none;\n font-size: 16px;\n margin: 0 0 16px;\n }\n }\n\n .fields-and-preview {\n display: flex;\n }\n\n label {\n font-size: $section-title-font-size;\n }\n\n .form-wrapper {\n width: 100%;\n\n .field {\n position: relative;\n\n .icon-clear-input {\n position: absolute;\n transform: translateY(-50%);\n top: 50%;\n inset-inline-end: 8px;\n }\n }\n\n .url {\n input:dir(ltr) {\n padding-right: 32px;\n }\n\n input:dir(rtl) {\n padding-left: 32px;\n\n &:not(:placeholder-shown) {\n direction: ltr;\n text-align: right;\n }\n }\n }\n\n .enable-custom-image-input {\n display: inline-block;\n font-size: 13px;\n margin-top: 4px;\n cursor: pointer;\n\n &:hover {\n text-decoration: underline;\n }\n }\n\n .custom-image-input-container {\n margin-top: 4px;\n\n .loading-container {\n width: 16px;\n height: 16px;\n overflow: hidden;\n position: absolute;\n transform: translateY(-50%);\n top: 50%;\n inset-inline-end: 8px;\n }\n\n // This animation is derived from Firefox's tab loading animation\n // See https://searchfox.org/mozilla-central/rev/b29daa46443b30612415c35be0a3c9c13b9dc5f6/browser/themes/shared/tabs.inc.css#208-216\n .loading-animation {\n @keyframes tab-throbber-animation {\n 100% { transform: translateX(-960px); }\n }\n\n @keyframes tab-throbber-animation-rtl {\n 100% { transform: translateX(960px); }\n }\n\n width: 960px;\n height: 16px;\n -moz-context-properties: fill;\n fill: $blue-50;\n background-image: url('chrome://browser/skin/tabbrowser/loading.svg');\n animation: tab-throbber-animation 1.05s steps(60) infinite;\n\n &:dir(rtl) {\n animation-name: tab-throbber-animation-rtl;\n }\n }\n }\n\n input {\n &[type='text'] {\n background-color: var(--newtab-textbox-background-color);\n border: $input-border;\n margin: 8px 0;\n padding: 0 8px;\n height: 32px;\n width: 100%;\n font-size: 15px;\n\n &:focus {\n border: $input-border-active;\n box-shadow: var(--newtab-textbox-focus-boxshadow);\n }\n\n &[disabled] {\n border: $input-border;\n box-shadow: none;\n opacity: 0.4;\n }\n }\n }\n\n .invalid {\n input {\n &[type='text'] {\n border: $input-error-border;\n box-shadow: $input-error-boxshadow;\n }\n }\n }\n\n .error-tooltip {\n animation: fade-up-tt 450ms;\n background: $red-60;\n border-radius: 2px;\n color: $white;\n inset-inline-start: 3px;\n padding: 5px 12px;\n position: absolute;\n top: 44px;\n z-index: 1;\n\n // tooltip caret\n &::before {\n background: $red-60;\n bottom: -8px;\n content: '.';\n height: 16px;\n inset-inline-start: 12px;\n position: absolute;\n text-indent: -999px;\n top: -7px;\n transform: rotate(45deg);\n white-space: nowrap;\n width: 16px;\n z-index: -1;\n }\n }\n }\n\n .actions {\n justify-content: flex-end;\n\n button {\n margin-inline-start: 10px;\n margin-inline-end: 0;\n }\n }\n\n @media (max-width: $break-point-small) {\n .fields-and-preview {\n flex-direction: column;\n\n .top-site-outer {\n margin-inline-start: 0;\n }\n }\n }\n}\n\n//used for tooltips below form element\n@keyframes fade-up-tt {\n 0% {\n opacity: 0;\n transform: translateY(15px);\n }\n\n 100% {\n opacity: 1;\n transform: translateY(0);\n }\n}\n", ".sections-list {\n .section-list {\n display: grid;\n grid-gap: $base-gutter;\n grid-template-columns: repeat(auto-fit, $card-width);\n margin: 0;\n\n @media (max-width: $break-point-medium) {\n @include context-menu-open-left;\n }\n\n @media (min-width: $break-point-medium) and (max-width: $break-point-large) {\n :nth-child(2n) {\n @include context-menu-open-left;\n }\n }\n\n @media (min-width: $break-point-large) and (max-width: $break-point-large + 2 * $card-width) {\n :nth-child(3n) {\n @include context-menu-open-left;\n }\n }\n\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + 2 * $card-width) {\n :nth-child(3n) {\n @include context-menu-open-left;\n }\n }\n }\n\n .section-empty-state {\n border: $border-secondary;\n border-radius: $border-radius;\n display: flex;\n height: $card-height;\n width: 100%;\n\n .empty-state {\n margin: auto;\n max-width: 350px;\n\n .empty-state-icon {\n background-position: center;\n background-repeat: no-repeat;\n background-size: 50px 50px;\n -moz-context-properties: fill;\n display: block;\n fill: var(--newtab-icon-secondary-color);\n height: 50px;\n margin: 0 auto;\n width: 50px;\n }\n\n .empty-state-message {\n color: var(--newtab-text-primary-color);\n font-size: 13px;\n margin-bottom: 0;\n text-align: center;\n }\n }\n\n @media (min-width: $break-point-widest) {\n height: $card-height-large;\n }\n }\n}\n\n@media (min-width: $break-point-widest) {\n .sections-list {\n // Compact cards stay the same size but normal cards get bigger.\n .normal-cards {\n .section-list {\n grid-template-columns: repeat(auto-fit, $card-width-large);\n }\n }\n }\n}\n", ".activity-stream {\n &.welcome {\n overflow: hidden;\n }\n\n &:not(.welcome) {\n .overlay-wrapper {\n display: none;\n }\n }\n}\n\n.overlay-wrapper {\n position: absolute;\n top: 0;\n width: 100vw;\n height: 100vh;\n z-index: 21000;\n transition: opacity 0.4s;\n opacity: 0;\n overflow-x: auto;\n\n &.show {\n transition: none;\n opacity: 1;\n\n .firstrun-sign-in {\n transition: opacity 1.5s, transform 1.5s;\n transition-delay: 0.2s;\n transform: translateY(-50%) scale(1);\n opacity: 1;\n\n @media screen and (max-width: 790px) {\n float: none;\n margin: auto;\n top: 190px;\n margin-bottom: 100px;\n }\n }\n\n .firstrun-firefox-logo {\n transition: opacity 2.3s;\n opacity: 1;\n }\n\n .firstrun-title,\n .firstrun-content,\n .firstrun-link {\n transition: transform 0.5s, opacity 0.8s;\n transform: translateY(0);\n opacity: 1;\n }\n\n .firstrun-title {\n transition-delay: 0.2s;\n }\n\n .firstrun-content {\n transition-delay: 0.4s;\n }\n\n .firstrun-link {\n transition-delay: 0.6s;\n }\n\n .fxaccounts-container {\n transition: none;\n opacity: 1;\n }\n }\n}\n\n.background {\n width: 100%;\n height: 100%;\n display: block;\n background: url('#{$image-path}fox-tail.png') top -200px center no-repeat,\n linear-gradient(to bottom, $blue-70 40%, #004EC2 60%, $blue-60 80%, #0080FF 90%, #00C7FF 100%) top center no-repeat,\n $blue-70;\n background-size: cover;\n position: fixed;\n}\n\n.firstrun-sign-in {\n transform: translateY(-50%) scale(0.8);\n position: relative;\n top: 50%;\n width: 358px;\n opacity: 0;\n background-color: $white;\n float: inline-end;\n color: $grey-90;\n text-align: center;\n padding: 10px;\n\n .extra-links {\n font-size: 12px;\n max-width: 340px;\n margin: 14px 50px;\n color: #676F7E;\n cursor: default;\n\n a {\n color: $grey-50;\n cursor: pointer;\n text-decoration: underline;\n }\n\n a:hover,\n a:active,\n a:focus {\n color: $blue-50;\n }\n }\n\n .email-input {\n box-shadow: none;\n margin: auto;\n width: 244px;\n display: block;\n height: 40px;\n padding-inline-start: 20px;\n border: 1px solid $grey-50;\n border-radius: 2px;\n font-size: 16px;\n transition: border-color 150ms, box-shadow 150ms;\n\n &:hover {\n border-color: $grey-90;\n }\n\n &:focus {\n border-color: $blue-50;\n box-shadow: 0 0 0 3px rgba(10, 132, 255, 0.3);\n }\n }\n\n .form-header {\n font-size: 22px;\n margin: 15px auto;\n }\n\n .form-header .sub-header {\n font-size: 14px;\n margin-top: 4px;\n display: block;\n }\n\n button {\n display: block;\n cursor: pointer;\n margin: 10px auto 0;\n }\n\n .continue-button {\n font-size: 18px;\n height: 43px;\n width: 250px;\n padding: 8px 0;\n border: 0;\n border-radius: 4px;\n color: $white;\n background-color: $blue-60;\n transition: background-color 150ms;\n\n &:not([disabled]):active,\n &:not([disabled]):hover {\n background: $blue-70;\n border-color: $blue-80;\n }\n }\n\n .skip-button {\n font-size: 13px;\n margin-top: 35px;\n margin-bottom: 20px;\n background-color: #FCFCFC;\n color: $blue-50;\n border: 1px solid $blue-50;\n border-radius: 2px;\n min-height: 24px;\n padding: 5px 10px;\n transition: background-color 150ms, color 150ms, border-color 150ms;\n\n &[disabled] {\n background-color: #EBEBEB;\n border-color: #B1B1B1;\n color: #6A6A6A;\n cursor: default;\n opacity: 0.5;\n }\n\n &:not([disabled]):hover {\n background-color: $blue-50;\n border-color: $blue-60;\n color: $white;\n }\n }\n}\n\n.firstrun-left-divider {\n position: relative;\n float: inline-start;\n clear: both;\n width: 435px;\n\n @media screen and (max-width: 825px) {\n width: 400px;\n }\n\n @media screen and (max-width: 790px) {\n margin: auto;\n float: none;\n width: 352px;\n text-align: center;\n }\n}\n\n.firstrun-content {\n line-height: 1.5;\n margin-bottom: 48px;\n max-width: 352px;\n background: url('#{$image-path}sync-devices.svg') bottom center no-repeat;\n padding-bottom: 210px;\n}\n\n.firstrun-link {\n color: $white;\n display: block;\n text-decoration: underline;\n\n &:hover,\n &:active,\n &:focus {\n color: $white;\n }\n}\n\n.firstrun-title {\n background: url('chrome://branding/content/about-logo.png') top left no-repeat;\n background-size: 90px 90px;\n margin: 40px 0 10px;\n padding-top: 110px;\n\n @media screen and (max-width: 790px) {\n background: url('chrome://branding/content/about-logo.png') top center no-repeat;\n background-size: 90px 90px;\n }\n}\n\n[dir='rtl'] {\n .firstrun-title {\n background-position: top right;\n }\n}\n\n.fxaccounts-container {\n position: absolute;\n bottom: 0;\n right: 0;\n top: 0;\n left: 0;\n color: $white;\n height: 515px;\n margin: auto;\n width: 819px;\n z-index: 10;\n transition: opacity 0.3s;\n opacity: 0;\n\n @media screen and (max-width: 825px) {\n width: 784px;\n }\n\n @media screen and (max-width: 790px) {\n width: auto;\n height: 100%;\n }\n}\n\n.firstrun-title,\n.firstrun-content,\n.firstrun-link {\n opacity: 0;\n transform: translateY(-5px);\n}\n", ".topic {\n color: var(--newtab-section-navigation-text-color);\n font-size: 12px;\n line-height: 1.6;\n margin-top: $topic-margin-top;\n\n @media (min-width: $break-point-large) {\n line-height: 16px;\n }\n\n ul {\n margin: 0;\n padding: 0;\n @media (min-width: $break-point-large) {\n display: inline;\n padding-inline-start: 12px;\n }\n }\n\n\n ul li {\n display: inline-block;\n\n &::after {\n content: '•';\n padding: 8px;\n }\n\n &:last-child::after {\n content: none;\n }\n }\n\n .topic-link {\n color: var(--newtab-link-secondary-color);\n font-weight: bold;\n }\n\n .topic-read-more {\n color: var(--newtab-link-secondary-color);\n font-weight: bold;\n\n @media (min-width: $break-point-large) {\n // This is floating to accomodate a very large number of topics and/or\n // very long topic names due to l10n.\n float: right;\n\n &:dir(rtl) {\n float: left;\n }\n }\n\n &::after {\n background: url('#{$image-path}topic-show-more-12.svg') no-repeat center center;\n content: '';\n -moz-context-properties: fill;\n display: inline-block;\n fill: var(--newtab-link-secondary-color);\n height: 16px;\n margin-inline-start: 5px;\n vertical-align: top;\n width: 12px;\n }\n\n &:dir(rtl)::after {\n transform: scaleX(-1);\n }\n }\n\n // This is a clearfix to for the topics-read-more link which is floating and causes\n // some jank when we set overflow:hidden for the animation.\n &::after {\n clear: both;\n content: '';\n display: table;\n }\n}\n", - ".search-wrapper {\n $search-height: 35px;\n $search-icon-size: 18px;\n $search-icon-padding: 8px;\n $search-icon-width: 2 * $search-icon-padding + $search-icon-size;\n $search-input-left-label-width: 35px;\n $search-button-width: 36px;\n $glyph-forward: url('chrome://browser/skin/forward.svg');\n\n cursor: default;\n display: flex;\n height: $search-height;\n margin-bottom: $section-spacing;\n position: relative;\n width: 100%;\n\n input {\n background: var(--newtab-textbox-background-color) var(--newtab-search-icon) $search-icon-padding center / $search-icon-size no-repeat;\n border: solid 1px var(--newtab-search-border-color);\n box-shadow: $shadow-secondary, 0 0 0 1px $black-15;\n font-size: 15px;\n -moz-context-properties: fill;\n fill: var(--newtab-search-icon-color);\n padding: 0;\n padding-inline-end: $search-button-width;\n padding-inline-start: $search-icon-width;\n width: 100%;\n\n &:dir(rtl) {\n background-position-x: right $search-icon-padding;\n }\n }\n\n &:hover input {\n box-shadow: $shadow-secondary, 0 0 0 1px $black-25;\n }\n\n &:active input,\n input:focus {\n border: $input-border-active;\n box-shadow: var(--newtab-textbox-focus-boxshadow);\n }\n\n .search-button {\n background: $glyph-forward no-repeat center center;\n background-size: 16px 16px;\n border: 0;\n border-radius: 0 $border-radius $border-radius 0;\n -moz-context-properties: fill;\n fill: var(--newtab-search-icon-color);\n height: 100%;\n offset-inline-end: 0;\n position: absolute;\n width: $search-button-width;\n\n &:focus,\n &:hover {\n background-color: $grey-90-10;\n cursor: pointer;\n }\n\n &:active {\n background-color: $grey-90-20;\n }\n\n &:dir(rtl) {\n transform: scaleX(-1);\n }\n }\n}\n\n@at-root {\n // Adjust the style of the contentSearchUI-generated table\n .contentSearchSuggestionTable {\n background-color: var(--newtab-search-dropdown-color);\n border: 0;\n box-shadow: $context-menu-shadow;\n transform: translateY($textbox-shadow-size);\n\n .contentSearchHeader {\n background-color: var(--newtab-search-dropdown-header-color);\n color: var(--newtab-text-secondary-color);\n }\n\n .contentSearchHeader,\n .contentSearchSettingsButton {\n border-color: var(--newtab-border-secondary-color);\n }\n\n .contentSearchSuggestionsList {\n border: 0;\n }\n\n .contentSearchOneOffsTable {\n background-color: var(--newtab-search-dropdown-header-color);\n border-top: solid 1px var(--newtab-border-secondary-color);\n }\n\n .contentSearchSearchWithHeaderSearchText {\n color: var(--newtab-text-primary-color);\n }\n\n .contentSearchSuggestionsContainer {\n background-color: var(--newtab-search-dropdown-color);\n }\n\n .contentSearchSuggestionRow {\n &.selected {\n background: var(--newtab-element-hover-color);\n color: var(--newtab-text-primary-color);\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n\n .historyIcon {\n fill: var(--newtab-icon-secondary-color);\n }\n }\n }\n\n .contentSearchOneOffsTable {\n .contentSearchSuggestionsContainer {\n background-color: var(--newtab-search-dropdown-header-color);\n }\n }\n\n .contentSearchOneOffItem {\n // Make the border slightly shorter by offsetting from the top and bottom\n $border-offset: 18%;\n\n background-image: none;\n border-image: linear-gradient(transparent $border-offset, var(--newtab-border-secondary-color) $border-offset, var(--newtab-border-secondary-color) 100% - $border-offset, transparent 100% - $border-offset) 1;\n border-inline-end: 1px solid;\n position: relative;\n\n &.selected {\n background: var(--newtab-element-hover-color);\n }\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n }\n\n .contentSearchSettingsButton {\n &:hover {\n background: var(--newtab-element-hover-color);\n color: var(--newtab-text-primary-color);\n }\n }\n }\n}\n", - ".context-menu {\n background: var(--newtab-contextmenu-background-color);\n border-radius: $context-menu-border-radius;\n box-shadow: $context-menu-shadow;\n display: block;\n font-size: $context-menu-font-size;\n margin-inline-start: 5px;\n offset-inline-start: 100%;\n position: absolute;\n top: ($context-menu-button-size / 4);\n z-index: 10000;\n\n > ul {\n list-style: none;\n margin: 0;\n padding: $context-menu-outer-padding 0;\n\n > li {\n margin: 0;\n width: 100%;\n\n &.separator {\n border-bottom: $border-secondary;\n margin: $context-menu-outer-padding 0;\n }\n\n > a {\n align-items: center;\n color: inherit;\n cursor: pointer;\n display: flex;\n line-height: 16px;\n outline: none;\n padding: $context-menu-item-padding;\n white-space: nowrap;\n\n &:-moz-any(:focus, :hover) {\n background: var(--newtab-element-hover-color);\n }\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n\n &.disabled {\n opacity: 0.4;\n pointer-events: none;\n }\n }\n }\n }\n}\n", + ".search-wrapper {\n $search-height: 35px;\n $search-icon-size: 18px;\n $search-icon-padding: 8px;\n $search-icon-width: 2 * $search-icon-padding + $search-icon-size;\n $search-input-left-label-width: 35px;\n $search-button-width: 36px;\n $glyph-forward: url('chrome://browser/skin/forward.svg');\n\n cursor: default;\n display: flex;\n height: $search-height;\n margin-bottom: $section-spacing;\n position: relative;\n width: 100%;\n\n input {\n background: var(--newtab-textbox-background-color) var(--newtab-search-icon) $search-icon-padding center / $search-icon-size no-repeat;\n border: solid 1px var(--newtab-search-border-color);\n box-shadow: $shadow-secondary, 0 0 0 1px $black-15;\n font-size: 15px;\n -moz-context-properties: fill;\n fill: var(--newtab-search-icon-color);\n padding: 0;\n padding-inline-end: $search-button-width;\n padding-inline-start: $search-icon-width;\n width: 100%;\n\n &:dir(rtl) {\n background-position-x: right $search-icon-padding;\n }\n }\n\n &:hover input {\n box-shadow: $shadow-secondary, 0 0 0 1px $black-25;\n }\n\n &:active input,\n input:focus {\n border: $input-border-active;\n box-shadow: var(--newtab-textbox-focus-boxshadow);\n }\n\n .search-button {\n background: $glyph-forward no-repeat center center;\n background-size: 16px 16px;\n border: 0;\n border-radius: 0 $border-radius $border-radius 0;\n -moz-context-properties: fill;\n fill: var(--newtab-search-icon-color);\n height: 100%;\n inset-inline-end: 0;\n position: absolute;\n width: $search-button-width;\n\n &:focus,\n &:hover {\n background-color: $grey-90-10;\n cursor: pointer;\n }\n\n &:active {\n background-color: $grey-90-20;\n }\n\n &:dir(rtl) {\n transform: scaleX(-1);\n }\n }\n}\n\n@at-root {\n // Adjust the style of the contentSearchUI-generated table\n .contentSearchSuggestionTable {\n background-color: var(--newtab-search-dropdown-color);\n border: 0;\n box-shadow: $context-menu-shadow;\n transform: translateY($textbox-shadow-size);\n\n .contentSearchHeader {\n background-color: var(--newtab-search-dropdown-header-color);\n color: var(--newtab-text-secondary-color);\n }\n\n .contentSearchHeader,\n .contentSearchSettingsButton {\n border-color: var(--newtab-border-secondary-color);\n }\n\n .contentSearchSuggestionsList {\n border: 0;\n }\n\n .contentSearchOneOffsTable {\n background-color: var(--newtab-search-dropdown-header-color);\n border-top: solid 1px var(--newtab-border-secondary-color);\n }\n\n .contentSearchSearchWithHeaderSearchText {\n color: var(--newtab-text-primary-color);\n }\n\n .contentSearchSuggestionsContainer {\n background-color: var(--newtab-search-dropdown-color);\n }\n\n .contentSearchSuggestionRow {\n &.selected {\n background: var(--newtab-element-hover-color);\n color: var(--newtab-text-primary-color);\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n\n .historyIcon {\n fill: var(--newtab-icon-secondary-color);\n }\n }\n }\n\n .contentSearchOneOffsTable {\n .contentSearchSuggestionsContainer {\n background-color: var(--newtab-search-dropdown-header-color);\n }\n }\n\n .contentSearchOneOffItem {\n // Make the border slightly shorter by offsetting from the top and bottom\n $border-offset: 18%;\n\n background-image: none;\n border-image: linear-gradient(transparent $border-offset, var(--newtab-border-secondary-color) $border-offset, var(--newtab-border-secondary-color) 100% - $border-offset, transparent 100% - $border-offset) 1;\n border-inline-end: 1px solid;\n position: relative;\n\n &.selected {\n background: var(--newtab-element-hover-color);\n }\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n }\n\n .contentSearchSettingsButton {\n &:hover {\n background: var(--newtab-element-hover-color);\n color: var(--newtab-text-primary-color);\n }\n }\n }\n}\n", + ".context-menu {\n background: var(--newtab-contextmenu-background-color);\n border-radius: $context-menu-border-radius;\n box-shadow: $context-menu-shadow;\n display: block;\n font-size: $context-menu-font-size;\n margin-inline-start: 5px;\n inset-inline-start: 100%;\n position: absolute;\n top: ($context-menu-button-size / 4);\n z-index: 10000;\n\n > ul {\n list-style: none;\n margin: 0;\n padding: $context-menu-outer-padding 0;\n\n > li {\n margin: 0;\n width: 100%;\n\n &.separator {\n border-bottom: $border-secondary;\n margin: $context-menu-outer-padding 0;\n }\n\n > a {\n align-items: center;\n color: inherit;\n cursor: pointer;\n display: flex;\n line-height: 16px;\n outline: none;\n padding: $context-menu-item-padding;\n white-space: nowrap;\n\n &:-moz-any(:focus, :hover) {\n background: var(--newtab-element-hover-color);\n }\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n\n &.disabled {\n opacity: 0.4;\n pointer-events: none;\n }\n }\n }\n }\n}\n", ".confirmation-dialog {\n .modal {\n box-shadow: 0 2px 2px 0 $black-10;\n left: 50%;\n margin-left: -200px;\n position: fixed;\n top: 20%;\n width: 400px;\n }\n\n section {\n margin: 0;\n }\n\n .modal-message {\n display: flex;\n padding: 16px;\n padding-bottom: 0;\n\n p {\n margin: 0;\n margin-bottom: 16px;\n }\n }\n\n .actions {\n border: 0;\n display: flex;\n flex-wrap: nowrap;\n padding: 0 16px;\n\n button {\n margin-inline-end: 16px;\n padding-inline-end: 18px;\n padding-inline-start: 18px;\n white-space: normal;\n width: 50%;\n\n &.done {\n margin-inline-end: 0;\n margin-inline-start: 0;\n }\n }\n }\n\n .icon {\n margin-inline-end: 16px;\n }\n}\n\n.modal-overlay {\n background: var(--newtab-overlay-color);\n height: 100%;\n left: 0;\n position: fixed;\n top: 0;\n width: 100%;\n z-index: 11001;\n}\n\n.modal {\n background: var(--newtab-modal-color);\n border: $border-secondary;\n border-radius: 5px;\n font-size: 15px;\n z-index: 11002;\n}\n", - ".card-outer {\n @include context-menu-button;\n background: var(--newtab-card-background-color);\n border-radius: $border-radius;\n display: inline-block;\n height: $card-height;\n margin-inline-end: $base-gutter;\n position: relative;\n width: 100%;\n\n &.placeholder {\n background: transparent;\n\n .card {\n box-shadow: inset $inner-box-shadow;\n }\n\n .card-preview-image-outer,\n .card-context {\n display: none;\n }\n }\n\n .card {\n border-radius: $border-radius;\n box-shadow: var(--newtab-card-shadow);\n height: 100%;\n }\n\n > a {\n color: inherit;\n display: block;\n height: 100%;\n outline: none;\n position: absolute;\n width: 100%;\n\n &:-moz-any(.active, :focus) {\n .card {\n @include fade-in-card;\n }\n\n .card-title {\n color: var(--newtab-link-primary-color);\n }\n }\n }\n\n &:-moz-any(:hover, :focus, .active):not(.placeholder) {\n @include fade-in-card;\n @include context-menu-button-hover;\n outline: none;\n\n .card-title {\n color: var(--newtab-link-primary-color);\n }\n\n .alternate ~ .card-host-name {\n display: none;\n }\n\n .card-host-name.alternate {\n display: block;\n }\n }\n\n .card-preview-image-outer {\n background-color: $grey-30;\n border-radius: $border-radius $border-radius 0 0;\n height: $card-preview-image-height;\n overflow: hidden;\n position: relative;\n\n &::after {\n border-bottom: 1px solid var(--newtab-card-hairline-color);\n bottom: 0;\n content: '';\n position: absolute;\n width: 100%;\n }\n\n .card-preview-image {\n background-position: center;\n background-repeat: no-repeat;\n background-size: cover;\n height: 100%;\n opacity: 0;\n transition: opacity 1s $photon-easing;\n width: 100%;\n\n &.loaded {\n opacity: 1;\n }\n }\n }\n\n .card-details {\n padding: 15px 16px 12px;\n }\n\n .card-text {\n max-height: 4 * $card-text-line-height + $card-title-margin;\n overflow: hidden;\n\n &.no-host-name,\n &.no-context {\n max-height: 5 * $card-text-line-height + $card-title-margin;\n }\n\n &.no-host-name.no-context {\n max-height: 6 * $card-text-line-height + $card-title-margin;\n }\n\n &:not(.no-description) .card-title {\n max-height: 3 * $card-text-line-height;\n overflow: hidden;\n }\n }\n\n .card-host-name {\n color: var(--newtab-text-secondary-color);\n font-size: 10px;\n overflow: hidden;\n padding-bottom: 4px;\n text-overflow: ellipsis;\n text-transform: uppercase;\n white-space: nowrap;\n }\n\n .card-host-name.alternate { display: none; }\n\n .card-title {\n font-size: 14px;\n font-weight: 600;\n line-height: $card-text-line-height;\n margin: 0 0 $card-title-margin;\n word-wrap: break-word;\n }\n\n .card-description {\n font-size: 12px;\n line-height: $card-text-line-height;\n margin: 0;\n overflow: hidden;\n word-wrap: break-word;\n }\n\n .card-context {\n bottom: 0;\n color: var(--newtab-text-secondary-color);\n display: flex;\n font-size: 11px;\n offset-inline-start: 0;\n padding: 9px 16px 9px 14px;\n position: absolute;\n }\n\n .card-context-icon {\n fill: var(--newtab-text-secondary-color);\n height: 22px;\n margin-inline-end: 6px;\n }\n\n .card-context-label {\n flex-grow: 1;\n line-height: 22px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n\n.normal-cards {\n .card-outer {\n // Wide layout styles\n @media (min-width: $break-point-widest) {\n $line-height: 23px;\n height: $card-height-large;\n\n .card-preview-image-outer {\n height: $card-preview-image-height-large;\n }\n\n .card-details {\n padding: 13px 16px 12px;\n }\n\n .card-text {\n max-height: 6 * $line-height + $card-title-margin;\n }\n\n .card-host-name {\n font-size: 12px;\n padding-bottom: 5px;\n }\n\n .card-title {\n font-size: 17px;\n line-height: $line-height;\n margin-bottom: 0;\n }\n\n .card-text:not(.no-description) {\n .card-title {\n max-height: 3 * $line-height;\n }\n }\n\n .card-description {\n font-size: 15px;\n line-height: $line-height;\n }\n\n .card-context {\n bottom: 4px;\n font-size: 14px;\n }\n }\n }\n}\n\n.compact-cards {\n $card-detail-vertical-spacing: 12px;\n $card-title-font-size: 12px;\n\n .card-outer {\n height: $card-height-compact;\n\n .card-preview-image-outer {\n height: $card-preview-image-height-compact;\n }\n\n .card-details {\n padding: $card-detail-vertical-spacing 16px;\n }\n\n .card-host-name {\n line-height: 10px;\n }\n\n .card-text {\n .card-title,\n &:not(.no-description) .card-title {\n font-size: $card-title-font-size;\n line-height: $card-title-font-size + 1;\n max-height: $card-title-font-size + 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n }\n\n .card-description {\n display: none;\n }\n\n .card-context {\n $icon-size: 16px;\n $container-size: 32px;\n background-color: var(--newtab-card-background-color);\n border-radius: $container-size / 2;\n clip-path: inset(-1px -1px $container-size - ($card-height-compact - $card-preview-image-height-compact - 2 * $card-detail-vertical-spacing));\n height: $container-size;\n width: $container-size;\n padding: ($container-size - $icon-size) / 2;\n top: $card-preview-image-height-compact - $icon-size;\n offset-inline-end: 12px;\n offset-inline-start: auto;\n\n &::after {\n border: 1px solid var(--newtab-card-hairline-color);\n border-bottom: 0;\n border-radius: ($container-size / 2) + 1 ($container-size / 2) + 1 0 0;\n content: '';\n position: absolute;\n height: ($container-size + 2) / 2;\n width: $container-size + 2;\n top: -1px;\n left: -1px;\n }\n\n .card-context-icon {\n margin-inline-end: 0;\n height: $icon-size;\n width: $icon-size;\n\n &.icon-bookmark-added {\n fill: $bookmark-icon-fill;\n }\n\n &.icon-download {\n fill: $download-icon-fill;\n }\n\n &.icon-pocket {\n fill: $pocket-icon-fill;\n }\n }\n\n .card-context-label {\n display: none;\n }\n }\n }\n\n @media not all and (min-width: $break-point-widest) {\n .hide-for-narrow {\n display: none;\n }\n }\n}\n", + ".card-outer {\n @include context-menu-button;\n background: var(--newtab-card-background-color);\n border-radius: $border-radius;\n display: inline-block;\n height: $card-height;\n margin-inline-end: $base-gutter;\n position: relative;\n width: 100%;\n\n &.placeholder {\n background: transparent;\n\n .card {\n box-shadow: inset $inner-box-shadow;\n }\n\n .card-preview-image-outer,\n .card-context {\n display: none;\n }\n }\n\n .card {\n border-radius: $border-radius;\n box-shadow: var(--newtab-card-shadow);\n height: 100%;\n }\n\n > a {\n color: inherit;\n display: block;\n height: 100%;\n outline: none;\n position: absolute;\n width: 100%;\n\n &:-moz-any(.active, :focus) {\n .card {\n @include fade-in-card;\n }\n\n .card-title {\n color: var(--newtab-link-primary-color);\n }\n }\n }\n\n &:-moz-any(:hover, :focus, .active):not(.placeholder) {\n @include fade-in-card;\n @include context-menu-button-hover;\n outline: none;\n\n .card-title {\n color: var(--newtab-link-primary-color);\n }\n\n .alternate ~ .card-host-name {\n display: none;\n }\n\n .card-host-name.alternate {\n display: block;\n }\n }\n\n .card-preview-image-outer {\n background-color: $grey-30;\n border-radius: $border-radius $border-radius 0 0;\n height: $card-preview-image-height;\n overflow: hidden;\n position: relative;\n\n &::after {\n border-bottom: 1px solid var(--newtab-card-hairline-color);\n bottom: 0;\n content: '';\n position: absolute;\n width: 100%;\n }\n\n .card-preview-image {\n background-position: center;\n background-repeat: no-repeat;\n background-size: cover;\n height: 100%;\n opacity: 0;\n transition: opacity 1s $photon-easing;\n width: 100%;\n\n &.loaded {\n opacity: 1;\n }\n }\n }\n\n .card-details {\n padding: 15px 16px 12px;\n }\n\n .card-text {\n max-height: 4 * $card-text-line-height + $card-title-margin;\n overflow: hidden;\n\n &.no-host-name,\n &.no-context {\n max-height: 5 * $card-text-line-height + $card-title-margin;\n }\n\n &.no-host-name.no-context {\n max-height: 6 * $card-text-line-height + $card-title-margin;\n }\n\n &:not(.no-description) .card-title {\n max-height: 3 * $card-text-line-height;\n overflow: hidden;\n }\n }\n\n .card-host-name {\n color: var(--newtab-text-secondary-color);\n font-size: 10px;\n overflow: hidden;\n padding-bottom: 4px;\n text-overflow: ellipsis;\n text-transform: uppercase;\n white-space: nowrap;\n }\n\n .card-host-name.alternate { display: none; }\n\n .card-title {\n font-size: 14px;\n font-weight: 600;\n line-height: $card-text-line-height;\n margin: 0 0 $card-title-margin;\n word-wrap: break-word;\n }\n\n .card-description {\n font-size: 12px;\n line-height: $card-text-line-height;\n margin: 0;\n overflow: hidden;\n word-wrap: break-word;\n }\n\n .card-context {\n bottom: 0;\n color: var(--newtab-text-secondary-color);\n display: flex;\n font-size: 11px;\n inset-inline-start: 0;\n padding: 9px 16px 9px 14px;\n position: absolute;\n }\n\n .card-context-icon {\n fill: var(--newtab-text-secondary-color);\n height: 22px;\n margin-inline-end: 6px;\n }\n\n .card-context-label {\n flex-grow: 1;\n line-height: 22px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n\n.normal-cards {\n .card-outer {\n // Wide layout styles\n @media (min-width: $break-point-widest) {\n $line-height: 23px;\n height: $card-height-large;\n\n .card-preview-image-outer {\n height: $card-preview-image-height-large;\n }\n\n .card-details {\n padding: 13px 16px 12px;\n }\n\n .card-text {\n max-height: 6 * $line-height + $card-title-margin;\n }\n\n .card-host-name {\n font-size: 12px;\n padding-bottom: 5px;\n }\n\n .card-title {\n font-size: 17px;\n line-height: $line-height;\n margin-bottom: 0;\n }\n\n .card-text:not(.no-description) {\n .card-title {\n max-height: 3 * $line-height;\n }\n }\n\n .card-description {\n font-size: 15px;\n line-height: $line-height;\n }\n\n .card-context {\n bottom: 4px;\n font-size: 14px;\n }\n }\n }\n}\n\n.compact-cards {\n $card-detail-vertical-spacing: 12px;\n $card-title-font-size: 12px;\n\n .card-outer {\n height: $card-height-compact;\n\n .card-preview-image-outer {\n height: $card-preview-image-height-compact;\n }\n\n .card-details {\n padding: $card-detail-vertical-spacing 16px;\n }\n\n .card-host-name {\n line-height: 10px;\n }\n\n .card-text {\n .card-title,\n &:not(.no-description) .card-title {\n font-size: $card-title-font-size;\n line-height: $card-title-font-size + 1;\n max-height: $card-title-font-size + 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n }\n\n .card-description {\n display: none;\n }\n\n .card-context {\n $icon-size: 16px;\n $container-size: 32px;\n background-color: var(--newtab-card-background-color);\n border-radius: $container-size / 2;\n clip-path: inset(-1px -1px $container-size - ($card-height-compact - $card-preview-image-height-compact - 2 * $card-detail-vertical-spacing));\n height: $container-size;\n width: $container-size;\n padding: ($container-size - $icon-size) / 2;\n top: $card-preview-image-height-compact - $icon-size;\n inset-inline-end: 12px;\n inset-inline-start: auto;\n\n &::after {\n border: 1px solid var(--newtab-card-hairline-color);\n border-bottom: 0;\n border-radius: ($container-size / 2) + 1 ($container-size / 2) + 1 0 0;\n content: '';\n position: absolute;\n height: ($container-size + 2) / 2;\n width: $container-size + 2;\n top: -1px;\n left: -1px;\n }\n\n .card-context-icon {\n margin-inline-end: 0;\n height: $icon-size;\n width: $icon-size;\n\n &.icon-bookmark-added {\n fill: $bookmark-icon-fill;\n }\n\n &.icon-download {\n fill: $download-icon-fill;\n }\n\n &.icon-pocket {\n fill: $pocket-icon-fill;\n }\n }\n\n .card-context-label {\n display: none;\n }\n }\n }\n\n @media not all and (min-width: $break-point-widest) {\n .hide-for-narrow {\n display: none;\n }\n }\n}\n", ".manual-migration-container {\n color: var(--newtab-text-conditional-color);\n font-size: 13px;\n line-height: 15px;\n margin-bottom: $section-spacing;\n text-align: center;\n\n @media (min-width: $break-point-medium) {\n display: flex;\n justify-content: space-between;\n text-align: left;\n }\n\n p {\n margin: 0;\n @media (min-width: $break-point-medium) {\n align-self: center;\n display: flex;\n justify-content: space-between;\n }\n }\n\n .icon {\n display: none;\n @media (min-width: $break-point-medium) {\n align-self: center;\n display: block;\n fill: var(--newtab-icon-secondary-color);\n margin-inline-end: 6px;\n }\n }\n}\n\n.manual-migration-actions {\n border: 0;\n display: block;\n flex-wrap: nowrap;\n\n @media (min-width: $break-point-medium) {\n display: flex;\n justify-content: space-between;\n padding: 0;\n }\n\n button {\n align-self: center;\n height: 26px;\n margin: 0;\n margin-inline-start: 20px;\n padding: 0 12px;\n }\n}\n", - ".collapsible-section {\n padding: $section-vertical-padding $section-horizontal-padding;\n transition-delay: 100ms;\n transition-duration: 100ms;\n transition-property: background-color;\n\n .section-title {\n font-size: $section-title-font-size;\n font-weight: bold;\n margin: 0;\n text-transform: uppercase;\n\n span {\n color: var(--newtab-section-header-text-color);\n display: inline-block;\n fill: var(--newtab-section-header-text-color);\n vertical-align: middle;\n }\n\n .click-target {\n cursor: pointer;\n vertical-align: top;\n white-space: nowrap;\n }\n\n .collapsible-arrow {\n margin-inline-start: 8px;\n margin-top: -1px;\n }\n }\n\n .section-top-bar {\n height: 19px;\n margin-bottom: 13px;\n position: relative;\n\n .context-menu-button {\n background: url('chrome://browser/skin/page-action.svg') no-repeat right center;\n border: 0;\n cursor: pointer;\n fill: var(--newtab-section-header-text-color);\n height: 100%;\n offset-inline-end: 0;\n opacity: 0;\n position: absolute;\n top: 0;\n transition-duration: 200ms;\n transition-property: opacity;\n width: $context-menu-button-size;\n\n &:-moz-any(:active, :focus, :hover) {\n fill: $grey-90;\n opacity: 1;\n }\n }\n\n .context-menu {\n top: 16px;\n }\n\n @media (max-width: $break-point-widest + $card-width * 1.5) {\n @include context-menu-open-left;\n }\n }\n\n &:hover,\n &.active {\n .section-top-bar {\n .context-menu-button {\n opacity: 1;\n }\n }\n }\n\n &.active {\n background: var(--newtab-element-hover-color);\n border-radius: 4px;\n\n .section-top-bar {\n .context-menu-button {\n fill: var(--newtab-section-active-contextmenu-color);\n }\n }\n }\n\n .section-disclaimer {\n $max-button-width: 130px;\n $min-button-height: 26px;\n\n color: var(--newtab-text-conditional-color);\n font-size: 13px;\n margin-bottom: 16px;\n position: relative;\n\n .section-disclaimer-text {\n display: inline-block;\n min-height: $min-button-height;\n width: calc(100% - #{$max-button-width});\n\n @media (max-width: $break-point-medium) {\n width: $card-width;\n }\n }\n\n a {\n color: var(--newtab-link-primary-color);\n font-weight: bold;\n padding-left: 3px;\n }\n\n button {\n background: var(--newtab-button-secondary-color);\n border: 1px solid $grey-40;\n border-radius: 4px;\n cursor: pointer;\n margin-top: 2px;\n max-width: $max-button-width;\n min-height: $min-button-height;\n offset-inline-end: 0;\n\n &:hover:not(.dismiss) {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n }\n\n @media (min-width: $break-point-small) {\n position: absolute;\n }\n }\n }\n\n .section-body-fallback {\n height: $card-height;\n }\n\n .section-body {\n // This is so the top sites favicon and card dropshadows don't get clipped during animation:\n $horizontal-padding: 7px;\n margin: 0 (-$horizontal-padding);\n padding: 0 $horizontal-padding;\n\n &.animating {\n overflow: hidden;\n pointer-events: none;\n }\n }\n\n &.animation-enabled {\n .section-title {\n .collapsible-arrow {\n transition: transform 0.5s $photon-easing;\n }\n }\n\n .section-body {\n transition: max-height 0.5s $photon-easing;\n }\n }\n\n &.collapsed {\n .section-body {\n max-height: 0;\n overflow: hidden;\n }\n }\n}\n", + ".collapsible-section {\n padding: $section-vertical-padding $section-horizontal-padding;\n transition-delay: 100ms;\n transition-duration: 100ms;\n transition-property: background-color;\n\n .section-title {\n font-size: $section-title-font-size;\n font-weight: bold;\n margin: 0;\n text-transform: uppercase;\n\n span {\n color: var(--newtab-section-header-text-color);\n display: inline-block;\n fill: var(--newtab-section-header-text-color);\n vertical-align: middle;\n }\n\n .click-target {\n cursor: pointer;\n vertical-align: top;\n white-space: nowrap;\n }\n\n .collapsible-arrow {\n margin-inline-start: 8px;\n margin-top: -1px;\n }\n }\n\n .section-top-bar {\n height: 19px;\n margin-bottom: 13px;\n position: relative;\n\n .context-menu-button {\n background: url('chrome://browser/skin/page-action.svg') no-repeat right center;\n border: 0;\n cursor: pointer;\n fill: var(--newtab-section-header-text-color);\n height: 100%;\n inset-inline-end: 0;\n opacity: 0;\n position: absolute;\n top: 0;\n transition-duration: 200ms;\n transition-property: opacity;\n width: $context-menu-button-size;\n\n &:-moz-any(:active, :focus, :hover) {\n fill: $grey-90;\n opacity: 1;\n }\n }\n\n .context-menu {\n top: 16px;\n }\n\n @media (max-width: $break-point-widest + $card-width * 1.5) {\n @include context-menu-open-left;\n }\n }\n\n &:hover,\n &.active {\n .section-top-bar {\n .context-menu-button {\n opacity: 1;\n }\n }\n }\n\n &.active {\n background: var(--newtab-element-hover-color);\n border-radius: 4px;\n\n .section-top-bar {\n .context-menu-button {\n fill: var(--newtab-section-active-contextmenu-color);\n }\n }\n }\n\n .section-disclaimer {\n $max-button-width: 130px;\n $min-button-height: 26px;\n\n color: var(--newtab-text-conditional-color);\n font-size: 13px;\n margin-bottom: 16px;\n position: relative;\n\n .section-disclaimer-text {\n display: inline-block;\n min-height: $min-button-height;\n width: calc(100% - #{$max-button-width});\n\n @media (max-width: $break-point-medium) {\n width: $card-width;\n }\n }\n\n a {\n color: var(--newtab-link-primary-color);\n font-weight: bold;\n padding-left: 3px;\n }\n\n button {\n background: var(--newtab-button-secondary-color);\n border: 1px solid $grey-40;\n border-radius: 4px;\n cursor: pointer;\n margin-top: 2px;\n max-width: $max-button-width;\n min-height: $min-button-height;\n inset-inline-end: 0;\n\n &:hover:not(.dismiss) {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n }\n\n @media (min-width: $break-point-small) {\n position: absolute;\n }\n }\n }\n\n .section-body-fallback {\n height: $card-height;\n }\n\n .section-body {\n // This is so the top sites favicon and card dropshadows don't get clipped during animation:\n $horizontal-padding: 7px;\n margin: 0 (-$horizontal-padding);\n padding: 0 $horizontal-padding;\n\n &.animating {\n overflow: hidden;\n pointer-events: none;\n }\n }\n\n &.animation-enabled {\n .section-title {\n .collapsible-arrow {\n transition: transform 0.5s $photon-easing;\n }\n }\n\n .section-body {\n transition: max-height 0.5s $photon-easing;\n }\n }\n\n &.collapsed {\n .section-body {\n max-height: 0;\n overflow: hidden;\n }\n }\n}\n", "\n.asrouter-admin {\n $border-color: var(--newtab-border-secondary-color);\n $monospace: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Mono', 'Droid Sans Mono', 'Source Code Pro', monospace;\n max-width: 996px;\n margin: 0 auto;\n font-size: 14px;\n // Reset .outer-wrapper styles\n display: inherit;\n padding: 0 0 92px;\n\n h1 {\n font-weight: 200;\n font-size: 32px;\n }\n\n table {\n border-collapse: collapse;\n width: 100%;\n }\n\n .message-item {\n &:first-child td {\n border-top: 1px solid $border-color;\n }\n\n td {\n vertical-align: top;\n border-bottom: 1px solid $border-color;\n padding: 8px;\n\n &:first-child {\n border-left: 1px solid $border-color;\n }\n\n &:last-child {\n border-right: 1px solid $border-color;\n }\n }\n\n &.current {\n .message-id span {\n background: $yellow-50;\n padding: 2px 5px;\n\n .dark-theme & {\n color: $black;\n }\n }\n }\n\n &.blocked {\n .message-id,\n .message-summary {\n opacity: 0.5;\n }\n\n .message-id {\n opacity: 0.5;\n }\n }\n\n .message-id {\n font-family: $monospace;\n font-size: 12px;\n }\n }\n\n pre {\n background: var(--newtab-textbox-background-color);\n margin: 0;\n padding: 8px;\n font-size: 12px;\n max-width: 750px;\n overflow: auto;\n font-family: $monospace;\n }\n}\n", ".ASRouterButton {\n white-space: nowrap;\n border-radius: 4px;\n border: 1px solid var(--newtab-border-secondary-color);\n background-color: var(--newtab-button-secondary-color);\n font-family: inherit;\n padding: 8px 15px;\n margin-inline-start: 12px;\n color: inherit;\n .tall & {\n margin-inline-start: 20px;\n }\n}\n", - ".SnippetBaseContainer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: var(--newtab-snippets-background-color);\n color: var(--newtab-text-primary-color);\n font-size: 12px;\n line-height: 16px;\n border-top: 1px solid var(--newtab-snippets-hairline-color);\n box-shadow: $shadow-secondary;\n display: flex;\n align-items: center;\n\n .innerWrapper {\n margin: 0 auto;\n display: flex;\n align-items: center;\n padding: 12px $section-horizontal-padding;\n\n // This is to account for the block button on smaller screens\n padding-inline-end: 36px;\n @media (min-width: $break-point-large) {\n padding-inline-end: $section-horizontal-padding;\n }\n\n max-width: $wrapper-max-width-large;\n @media (min-width: $break-point-widest) {\n max-width: $wrapper-max-width-widest;\n }\n }\n\n .blockButton {\n display: none;\n background: none;\n border: 0;\n position: absolute;\n top: 50%;\n offset-inline-end: 12px;\n height: 16px;\n width: 16px;\n background-image: url('resource://activity-stream/data/content/assets/glyph-dismiss-16.svg');\n -moz-context-properties: fill;\n fill: var(--newtab-icon-primary-color);\n opacity: 0.5;\n margin-top: -8px;\n padding: 0;\n cursor: pointer;\n\n @media (min-width: 766px) {\n offset-inline-end: 24px;\n }\n }\n\n &:hover .blockButton {\n display: block;\n }\n}\n", + ".SnippetBaseContainer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: var(--newtab-snippets-background-color);\n color: var(--newtab-text-primary-color);\n font-size: 12px;\n line-height: 16px;\n border-top: 1px solid var(--newtab-snippets-hairline-color);\n box-shadow: $shadow-secondary;\n display: flex;\n align-items: center;\n\n .innerWrapper {\n margin: 0 auto;\n display: flex;\n align-items: center;\n padding: 12px $section-horizontal-padding;\n\n // This is to account for the block button on smaller screens\n padding-inline-end: 36px;\n @media (min-width: $break-point-large) {\n padding-inline-end: $section-horizontal-padding;\n }\n\n max-width: $wrapper-max-width-large;\n @media (min-width: $break-point-widest) {\n max-width: $wrapper-max-width-widest;\n }\n }\n\n .blockButton {\n display: none;\n background: none;\n border: 0;\n position: absolute;\n top: 50%;\n inset-inline-end: 12px;\n height: 16px;\n width: 16px;\n background-image: url('resource://activity-stream/data/content/assets/glyph-dismiss-16.svg');\n -moz-context-properties: fill;\n fill: var(--newtab-icon-primary-color);\n opacity: 0.5;\n margin-top: -8px;\n padding: 0;\n cursor: pointer;\n\n @media (min-width: 766px) {\n inset-inline-end: 24px;\n }\n }\n\n &:hover .blockButton {\n display: block;\n }\n}\n", ".activity-stream {\n &.modal-open {\n overflow: hidden;\n }\n}\n.modalOverlayOuter {\n background: $white;\n opacity: 0.93;\n height: 100%;\n position: fixed;\n top: 0;\n width: 100%;\n display: none;\n z-index: 1100;\n\n &.active {\n display: block;\n }\n}\n\n.modalOverlayInner {\n width: 960px;\n height: 510px;\n position: fixed;\n top: calc(50% - 255px); // halfway down minus half the height of the modal\n left: calc(50% - 480px); // halfway across minus half the width of the modal\n background: $white;\n box-shadow: 0 1px 15px 0 $black-30;\n border-radius: 4px;\n display: none;\n z-index: 1101;\n\n\n // modal takes over entire screen\n @media(max-width: 960px) {\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n box-shadow: none;\n border-radius: 0;\n }\n\n // if modal is short enough, add a vertical scroll bar\n @media(max-width: 850px) and (max-height: 730px) {\n overflow-y: scroll;\n }\n\n // if modal is narrow enough, add a vertical scroll bar\n @media(max-width: 650px) and (max-height: 600px) {\n overflow-y: scroll;\n }\n\n &.active {\n display: block;\n }\n\n h2 {\n color: $grey-60;\n text-align: center;\n font-weight: 200;\n margin-top: 30px;\n font-size: 28px;\n line-height: 37px;\n letter-spacing: -0.13px;\n\n @media(max-width: 960px) {\n margin-top: 100px;\n }\n\n @media(max-width: 850px) {\n margin-top: 30px;\n }\n }\n\n .footer {\n border-top: 1px solid $grey-30;\n height: 70px;\n width: 100%;\n position: absolute;\n bottom: 0;\n text-align: center;\n background-color: $white;\n\n // if modal is short enough, footer becomes sticky\n @media(max-width: 850px) and (max-height: 730px) {\n position: sticky;\n }\n\n // if modal is narrow enough, footer becomes sticky\n @media(max-width: 650px) and (max-height: 600px) {\n position: sticky;\n }\n\n .modalButton {\n margin-top: 20px;\n width: 150px;\n height: 30px;\n padding: 4px 0 6px 0;\n font-size: 15px;\n }\n }\n}\n", ".SimpleSnippet {\n &.tall {\n padding: 27px 0;\n }\n\n .title {\n display: inline;\n font-size: inherit;\n margin: 0;\n }\n\n .titleIcon {\n background-repeat: no-repeat;\n background-size: 14px;\n height: 16px;\n width: 16px;\n margin-top: 2px;\n margin-inline-end: 2px;\n display: inline-block;\n vertical-align: top;\n }\n\n .body {\n display: inline;\n margin: 0;\n }\n\n .icon {\n height: 42px;\n width: 42px;\n margin-inline-end: 12px;\n flex-shrink: 0;\n }\n &.tall .icon {\n margin-inline-end: 20px;\n }\n\n .ASRouterAnchor {\n color: inherit;\n text-decoration: underline;\n }\n}\n", ".onboardingMessageContainer {\n display: grid;\n grid-column-gap: 21px;\n grid-template-columns: auto auto auto;\n padding-left: 30px;\n padding-right: 30px;\n\n // at 850px, the cards go from vertical layout to horizontal layout\n @media(max-width: 850px) {\n grid-template-columns: none;\n grid-template-rows: auto auto auto;\n padding-left: 110px;\n padding-right: 110px;\n }\n}\n\n.onboardingMessage {\n height: 340px;\n text-align: center;\n padding: 13px;\n font-weight: 200;\n\n // at 850px, img floats left, content floats right next to it\n @media(max-width: 850px) {\n height: 170px;\n text-align: left;\n padding: 10px;\n border-bottom: 1px solid #D8D8D8;\n display: flex;\n margin-bottom: 11px;\n\n &:last-child {\n border: none;\n }\n\n .onboardingContent {\n padding-left: 10px;\n height: 100%;\n\n > span > h3 {\n margin-top: 0;\n margin-bottom: 4px;\n font-weight: 400;\n }\n\n > span > p {\n margin-top: 0;\n line-height: 22px;\n font-size: 15px;\n }\n }\n }\n\n @media(max-width: 650px) {\n height: 250px;\n }\n\n .onboardingMessageImage {\n height: 100px;\n width: 120px;\n background-size: 120px;\n background-position: center center;\n background-repeat: no-repeat;\n display: inline-block;\n vertical-align: middle;\n\n\n @media(max-width: 850px) {\n height: 75px;\n min-width: 80px;\n background-size: 80px;\n }\n\n &.addons {\n background-image: url(\"resource://activity-stream/data/content/assets/illustration-addons@2x.png\");\n }\n\n &.privatebrowsing {\n background-image: url(\"resource://activity-stream/data/content/assets/illustration-privatebrowsing@2x.png\");\n }\n\n &.screenshots {\n background-image: url(\"resource://activity-stream/data/content/assets/illustration-screenshots@2x.png\");\n }\n\n &.gift {\n background-image: url(\"resource://activity-stream/data/content/assets/illustration-gift@2x.png\");\n }\n }\n\n .onboardingContent {\n height: 175px;\n\n > span > h3 {\n color: $grey-90;\n margin-bottom: 8px;\n font-weight: 400;\n }\n\n > span > p {\n color: $grey-60;\n margin-top: 0;\n height: 130px;\n margin-bottom: 12px;\n font-size: 15px;\n line-height: 22px;\n\n @media(max-width: 650px) {\n margin-bottom: 0px;\n }\n }\n }\n\n .onboardingButton {\n background-color: $grey-90-10;\n border: none;\n width: 150px;\n height: 30px;\n margin-bottom: 23px;\n padding: 4px 0 6px 0;\n font-size: 15px;\n\n // at 850px, the button shimmies down and to the right\n @media(max-width: 850px) {\n float: right;\n margin-top: -60px;\n margin-right: -10px;\n }\n\n @media(max-width: 650px) {\n float: none;\n margin-top: 30px;\n }\n }\n\n\n &::before {\n content: '';\n height: 220px;\n width: 1px;\n position: absolute;\n background-color: #D8D8D8;\n margin-top: 40px;\n margin-left: 215px;\n\n // at 850px, the line goes from vertical to horizontal\n @media(max-width: 850px) {\n content: none;\n }\n }\n\n &:last-child::before {\n content: none;\n }\n}\n" diff --git a/browser/extensions/activity-stream/css/activity-stream-windows.css b/browser/extensions/activity-stream/css/activity-stream-windows.css index 02372e3cee0f..3aa19b9854c6 100644 --- a/browser/extensions/activity-stream/css/activity-stream-windows.css +++ b/browser/extensions/activity-stream/css/activity-stream-windows.css @@ -369,7 +369,7 @@ main { border: 0; cursor: pointer; fill: var(--newtab-icon-primary-color); - offset-inline-end: 15px; + inset-inline-end: 15px; padding: 15px; position: fixed; top: 15px; @@ -405,56 +405,56 @@ main { .top-sites-list :nth-child(2n+1) .context-menu { margin-inline-end: auto; margin-inline-start: auto; - offset-inline-end: auto; - offset-inline-start: -32px; } + inset-inline-end: auto; + inset-inline-start: -32px; } .top-sites-list :nth-child(2n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 482px) and (max-width: 610px) { .top-sites-list :nth-child(3n+2) .context-menu, .top-sites-list :nth-child(3n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 610px) and (max-width: 866px) { .top-sites-list :nth-child(4n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 610px) and (max-width: 834px) { .top-sites-list :nth-child(4n+3) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 866px) and (max-width: 1314px) { .top-sites-list :nth-child(6n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 866px) and (max-width: 1090px) { .top-sites-list :nth-child(6n+5) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 1122px) and (max-width: 1570px) { .top-sites-list :nth-child(8n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 1122px) and (max-width: 1346px) { .top-sites-list :nth-child(8n+7) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media not all and (min-width: 1122px) { .top-sites-list .hide-for-narrow { display: none; } } @@ -490,7 +490,7 @@ main { cursor: pointer; fill: var(--newtab-icon-primary-color); height: 27px; - offset-inline-end: -13.5px; + inset-inline-end: -13.5px; opacity: 0; position: absolute; top: -13.5px; @@ -541,14 +541,14 @@ main { .top-site-outer .rich-icon { background-size: cover; height: 100%; - offset-inline-start: 0; + inset-inline-start: 0; top: 0; width: 100%; } .top-site-outer .default-icon { background-size: 32px; bottom: -6px; height: 42px; - offset-inline-end: -6px; + inset-inline-end: -6px; width: 42px; align-items: center; display: flex; @@ -566,7 +566,7 @@ main { position: relative; } .top-site-outer .title .icon { fill: var(--newtab-icon-tertiary-color); - offset-inline-start: 0; + inset-inline-start: 0; position: absolute; top: 10px; } .top-site-outer .title span { @@ -637,7 +637,7 @@ main { position: absolute; transform: translateY(-50%); top: 50%; - offset-inline-end: 8px; } + inset-inline-end: 8px; } .topsite-form .form-wrapper .url input:dir(ltr) { padding-right: 32px; } .topsite-form .form-wrapper .url input:dir(rtl) { @@ -661,7 +661,7 @@ main { position: absolute; transform: translateY(-50%); top: 50%; - offset-inline-end: 8px; } + inset-inline-end: 8px; } .topsite-form .form-wrapper .custom-image-input-container .loading-animation { width: 960px; height: 16px; @@ -702,7 +702,7 @@ main { background: #D70022; border-radius: 2px; color: #FFF; - offset-inline-start: 3px; + inset-inline-start: 3px; padding: 5px 12px; position: absolute; top: 44px; @@ -712,7 +712,7 @@ main { bottom: -8px; content: '.'; height: 16px; - offset-inline-start: 12px; + inset-inline-start: 12px; position: absolute; text-indent: -999px; top: -7px; @@ -750,26 +750,26 @@ main { .sections-list .section-list .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 610px) and (max-width: 866px) { .sections-list .section-list :nth-child(2n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 866px) and (max-width: 1314px) { .sections-list .section-list :nth-child(3n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } @media (min-width: 1122px) and (max-width: 1570px) { .sections-list .section-list :nth-child(3n) .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } .sections-list .section-empty-state { border: 1px solid var(--newtab-border-secondary-color); @@ -1099,7 +1099,7 @@ main { -moz-context-properties: fill; fill: var(--newtab-search-icon-color); height: 100%; - offset-inline-end: 0; + inset-inline-end: 0; position: absolute; width: 36px; } .search-wrapper .search-button:focus, .search-wrapper .search-button:hover { @@ -1158,7 +1158,7 @@ main { display: block; font-size: 14px; margin-inline-start: 5px; - offset-inline-start: 100%; + inset-inline-start: 100%; position: absolute; top: 6.75px; z-index: 10000; } @@ -1261,7 +1261,7 @@ main { cursor: pointer; fill: var(--newtab-icon-primary-color); height: 27px; - offset-inline-end: -13.5px; + inset-inline-end: -13.5px; opacity: 0; position: absolute; top: -13.5px; @@ -1369,7 +1369,7 @@ main { color: var(--newtab-text-secondary-color); display: flex; font-size: 11px; - offset-inline-start: 0; + inset-inline-start: 0; padding: 9px 16px 9px 14px; position: absolute; } .card-outer .card-context-icon { @@ -1434,8 +1434,8 @@ main { width: 32px; padding: 8px; top: 92px; - offset-inline-end: 12px; - offset-inline-start: auto; } + inset-inline-end: 12px; + inset-inline-start: auto; } .compact-cards .card-outer .card-context::after { border: 1px solid var(--newtab-card-hairline-color); border-bottom: 0; @@ -1538,7 +1538,7 @@ main { cursor: pointer; fill: var(--newtab-section-header-text-color); height: 100%; - offset-inline-end: 0; + inset-inline-end: 0; opacity: 0; position: absolute; top: 0; @@ -1554,8 +1554,8 @@ main { .collapsible-section .section-top-bar .context-menu { margin-inline-end: 5px; margin-inline-start: auto; - offset-inline-end: 0; - offset-inline-start: auto; } } + inset-inline-end: 0; + inset-inline-start: auto; } } .collapsible-section:hover .section-top-bar .context-menu-button, .collapsible-section.active .section-top-bar .context-menu-button { opacity: 1; } .collapsible-section.active { @@ -1587,7 +1587,7 @@ main { margin-top: 2px; max-width: 130px; min-height: 26px; - offset-inline-end: 0; } + inset-inline-end: 0; } .collapsible-section .section-disclaimer button:hover:not(.dismiss) { box-shadow: 0 0 0 5px var(--newtab-card-active-outline-color); transition: box-shadow 150ms; } @@ -1698,7 +1698,7 @@ main { border: 0; position: absolute; top: 50%; - offset-inline-end: 12px; + inset-inline-end: 12px; height: 16px; width: 16px; background-image: url("resource://activity-stream/data/content/assets/glyph-dismiss-16.svg"); @@ -1710,7 +1710,7 @@ main { cursor: pointer; } @media (min-width: 766px) { .SnippetBaseContainer .blockButton { - offset-inline-end: 24px; } } + inset-inline-end: 24px; } } .SnippetBaseContainer:hover .blockButton { display: block; } diff --git a/browser/extensions/activity-stream/css/activity-stream-windows.css.map b/browser/extensions/activity-stream/css/activity-stream-windows.css.map index 464c4546af81..d891ff5403fe 100644 --- a/browser/extensions/activity-stream/css/activity-stream-windows.css.map +++ b/browser/extensions/activity-stream/css/activity-stream-windows.css.map @@ -31,24 +31,24 @@ "/* This is the windows variant */ // sass-lint:disable-line no-css-comments\n\n$os-infopanel-arrow-height: 10px;\n$os-infopanel-arrow-offset-end: 6px;\n$os-infopanel-arrow-width: 20px;\n\n@import './activity-stream';\n", "@import './normalize';\n@import './variables';\n@import './theme';\n@import './icons';\n\nhtml {\n height: 100%;\n}\n\nbody,\n#root { // sass-lint:disable-line no-ids\n min-height: 100vh;\n}\n\nbody {\n background-color: var(--newtab-background-color);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Ubuntu', 'Helvetica Neue', sans-serif;\n font-size: 16px;\n overflow-y: scroll;\n}\n\nh1,\nh2 {\n font-weight: normal;\n}\n\na {\n text-decoration: none;\n}\n\n// For screen readers\n.sr-only {\n border: 0;\n clip: rect(0, 0, 0, 0);\n height: 1px;\n margin: -1px;\n overflow: hidden;\n padding: 0;\n position: absolute;\n width: 1px;\n}\n\n.inner-border {\n border: $border-secondary;\n border-radius: $border-radius;\n height: 100%;\n left: 0;\n pointer-events: none;\n position: absolute;\n top: 0;\n width: 100%;\n z-index: 100;\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 1;\n }\n}\n\n.show-on-init {\n opacity: 0;\n transition: opacity 0.2s ease-in;\n\n &.on {\n animation: fadeIn 0.2s;\n opacity: 1;\n }\n}\n\n.actions {\n border-top: $border-secondary;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: flex-start;\n margin: 0;\n padding: 15px 25px 0;\n}\n\n// Default button (grey)\n.button,\n.actions button {\n background-color: var(--newtab-button-secondary-color);\n border: $border-primary;\n border-radius: 4px;\n color: inherit;\n cursor: pointer;\n margin-bottom: 15px;\n padding: 10px 30px;\n white-space: nowrap;\n\n &:hover:not(.dismiss) {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n }\n\n &.dismiss {\n background-color: transparent;\n border: 0;\n padding: 0;\n text-decoration: underline;\n }\n\n // Blue button\n &.primary,\n &.done {\n background-color: var(--newtab-button-primary-color);\n border: solid 1px var(--newtab-button-primary-color);\n color: $white;\n margin-inline-start: auto;\n }\n}\n\ninput {\n &[type='text'],\n &[type='search'] {\n border-radius: $border-radius;\n }\n}\n\n// Make sure snippets show up above other UI elements\n#snippets-container { // sass-lint:disable-line no-ids\n z-index: 1;\n}\n\n// Components\n@import '../components/Base/Base';\n@import '../components/ErrorBoundary/ErrorBoundary';\n@import '../components/TopSites/TopSites';\n@import '../components/Sections/Sections';\n@import '../components/StartupOverlay/StartupOverlay';\n@import '../components/Topics/Topics';\n@import '../components/Search/Search';\n@import '../components/ContextMenu/ContextMenu';\n@import '../components/ConfirmDialog/ConfirmDialog';\n@import '../components/Card/Card';\n@import '../components/ManualMigration/ManualMigration';\n@import '../components/CollapsibleSection/CollapsibleSection';\n@import '../components/ASRouterAdmin/ASRouterAdmin';\n\n// AS Router\n@import '../asrouter/components/Button/Button';\n@import '../asrouter/components/SnippetBase/SnippetBase';\n@import '../asrouter/components/ModalOverlay/ModalOverlay';\n@import '../asrouter/templates/SimpleSnippet/SimpleSnippet';\n@import '../asrouter/templates/OnboardingMessage/OnboardingMessage';\n", "html {\n box-sizing: border-box;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\n*::-moz-focus-inner {\n border: 0;\n}\n\nbody {\n margin: 0;\n}\n\nbutton,\ninput {\n background-color: inherit;\n color: inherit;\n font-family: inherit;\n font-size: inherit;\n}\n\n[hidden] {\n display: none !important; // sass-lint:disable-line no-important\n}\n", - "// Photon colors from http://design.firefox.com/photon/visuals/color.html\n$blue-40: #45A1FF;\n$blue-50: #0A84FF;\n$blue-60: #0060DF;\n$blue-70: #003EAA;\n$blue-80: #002275;\n$grey-10: #F9F9FA;\n$grey-20: #EDEDF0;\n$grey-30: #D7D7DB;\n$grey-40: #B1B1B3;\n$grey-50: #737373;\n$grey-60: #4A4A4F;\n$grey-70: #38383D;\n$grey-80: #2A2A2E;\n$grey-90: #0C0C0D;\n$teal-70: #008EA4;\n$red-60: #D70022;\n$yellow-50: #FFE900;\n\n// Photon opacity from http://design.firefox.com/photon/visuals/color.html#opacity\n$grey-10-10: rgba($grey-10, 0.1);\n$grey-10-20: rgba($grey-10, 0.2);\n$grey-10-40: rgba($grey-10, 0.4);\n$grey-10-60: rgba($grey-10, 0.6);\n$grey-10-80: rgba($grey-10, 0.8);\n$grey-20-60: rgba($grey-20, 0.6);\n$grey-20-80: rgba($grey-20, 0.8);\n$grey-30-60: rgba($grey-30, 0.6);\n$grey-90-10: rgba($grey-90, 0.1);\n$grey-90-20: rgba($grey-90, 0.2);\n$grey-90-30: rgba($grey-90, 0.3);\n$grey-90-40: rgba($grey-90, 0.4);\n$grey-90-50: rgba($grey-90, 0.5);\n$grey-90-60: rgba($grey-90, 0.6);\n$grey-90-70: rgba($grey-90, 0.7);\n$grey-90-80: rgba($grey-90, 0.8);\n$grey-90-90: rgba($grey-90, 0.9);\n\n$black: #000;\n$black-5: rgba($black, 0.05);\n$black-10: rgba($black, 0.1);\n$black-15: rgba($black, 0.15);\n$black-20: rgba($black, 0.2);\n$black-25: rgba($black, 0.25);\n$black-30: rgba($black, 0.3);\n\n// Other colors\n$white: #FFF;\n$white-10: rgba($white, 0.1);\n$pocket-teal: #50BCB6;\n$bookmark-icon-fill: #0A84FF;\n$download-icon-fill: #12BC00;\n$pocket-icon-fill: #D70022;\n\n// Photon transitions from http://design.firefox.com/photon/motion/duration-and-easing.html\n$photon-easing: cubic-bezier(0.07, 0.95, 0, 1);\n\n$border-radius: 3px;\n\n// Grid related styles\n$base-gutter: 32px;\n$section-horizontal-padding: 25px;\n$section-vertical-padding: 10px;\n$section-spacing: 40px - $section-vertical-padding * 2;\n$grid-unit: 96px; // 1 top site\n\n$icon-size: 16px;\n$smaller-icon-size: 12px;\n$larger-icon-size: 32px;\n\n$wrapper-default-width: $grid-unit * 2 + $base-gutter * 1 + $section-horizontal-padding * 2; // 2 top sites\n$wrapper-max-width-small: $grid-unit * 3 + $base-gutter * 2 + $section-horizontal-padding * 2; // 3 top sites\n$wrapper-max-width-medium: $grid-unit * 4 + $base-gutter * 3 + $section-horizontal-padding * 2; // 4 top sites\n$wrapper-max-width-large: $grid-unit * 6 + $base-gutter * 5 + $section-horizontal-padding * 2; // 6 top sites\n$wrapper-max-width-widest: $grid-unit * 8 + $base-gutter * 7 + $section-horizontal-padding * 2; // 8 top sites\n// For the breakpoints, we need to add space for the scrollbar to avoid weird\n// layout issues when the scrollbar is visible. 16px is wide enough to cover all\n// OSes and keeps it simpler than a per-OS value.\n$scrollbar-width: 16px;\n$break-point-small: $wrapper-max-width-small + $base-gutter * 2 + $scrollbar-width;\n$break-point-medium: $wrapper-max-width-medium + $base-gutter * 2 + $scrollbar-width;\n$break-point-large: $wrapper-max-width-large + $base-gutter * 2 + $scrollbar-width;\n$break-point-widest: $wrapper-max-width-widest + $base-gutter * 2 + $scrollbar-width;\n\n$section-title-font-size: 13px;\n\n$card-width: $grid-unit * 2 + $base-gutter;\n$card-height: 266px;\n$card-preview-image-height: 122px;\n$card-title-margin: 2px;\n$card-text-line-height: 19px;\n// Larger cards for wider screens:\n$card-width-large: 309px;\n$card-height-large: 370px;\n$card-preview-image-height-large: 155px;\n// Compact cards for Highlights\n$card-height-compact: 160px;\n$card-preview-image-height-compact: 108px;\n\n$topic-margin-top: 12px;\n\n$context-menu-button-size: 27px;\n$context-menu-button-boxshadow: 0 2px $grey-90-10;\n$context-menu-shadow: 0 5px 10px $black-30, 0 0 0 1px $black-20;\n$context-menu-font-size: 14px;\n$context-menu-border-radius: 5px;\n$context-menu-outer-padding: 5px;\n$context-menu-item-padding: 3px 12px;\n\n$error-fallback-font-size: 12px;\n$error-fallback-line-height: 1.5;\n\n$image-path: '../data/content/assets/';\n\n$snippets-container-height: 120px;\n\n$textbox-shadow-size: 4px;\n\n@mixin fade-in {\n box-shadow: inset $inner-box-shadow, $shadow-primary;\n transition: box-shadow 150ms;\n}\n\n@mixin fade-in-card {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n}\n\n@mixin context-menu-button {\n .context-menu-button {\n background-clip: padding-box;\n background-color: var(--newtab-contextmenu-button-color);\n background-image: url('chrome://browser/skin/page-action.svg');\n background-position: 55%;\n border: $border-primary;\n border-radius: 100%;\n box-shadow: $context-menu-button-boxshadow;\n cursor: pointer;\n fill: var(--newtab-icon-primary-color);\n height: $context-menu-button-size;\n offset-inline-end: -($context-menu-button-size / 2);\n opacity: 0;\n position: absolute;\n top: -($context-menu-button-size / 2);\n transform: scale(0.25);\n transition-duration: 200ms;\n transition-property: transform, opacity;\n width: $context-menu-button-size;\n\n &:-moz-any(:active, :focus) {\n opacity: 1;\n transform: scale(1);\n }\n }\n}\n\n@mixin context-menu-button-hover {\n .context-menu-button {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@mixin context-menu-open-middle {\n .context-menu {\n margin-inline-end: auto;\n margin-inline-start: auto;\n offset-inline-end: auto;\n offset-inline-start: -$base-gutter;\n }\n}\n\n@mixin context-menu-open-left {\n .context-menu {\n margin-inline-end: 5px;\n margin-inline-start: auto;\n offset-inline-end: 0;\n offset-inline-start: auto;\n }\n}\n\n@mixin flip-icon {\n &:dir(rtl) {\n transform: scaleX(-1);\n }\n}\n", + "// Photon colors from http://design.firefox.com/photon/visuals/color.html\n$blue-40: #45A1FF;\n$blue-50: #0A84FF;\n$blue-60: #0060DF;\n$blue-70: #003EAA;\n$blue-80: #002275;\n$grey-10: #F9F9FA;\n$grey-20: #EDEDF0;\n$grey-30: #D7D7DB;\n$grey-40: #B1B1B3;\n$grey-50: #737373;\n$grey-60: #4A4A4F;\n$grey-70: #38383D;\n$grey-80: #2A2A2E;\n$grey-90: #0C0C0D;\n$teal-70: #008EA4;\n$red-60: #D70022;\n$yellow-50: #FFE900;\n\n// Photon opacity from http://design.firefox.com/photon/visuals/color.html#opacity\n$grey-10-10: rgba($grey-10, 0.1);\n$grey-10-20: rgba($grey-10, 0.2);\n$grey-10-40: rgba($grey-10, 0.4);\n$grey-10-60: rgba($grey-10, 0.6);\n$grey-10-80: rgba($grey-10, 0.8);\n$grey-20-60: rgba($grey-20, 0.6);\n$grey-20-80: rgba($grey-20, 0.8);\n$grey-30-60: rgba($grey-30, 0.6);\n$grey-90-10: rgba($grey-90, 0.1);\n$grey-90-20: rgba($grey-90, 0.2);\n$grey-90-30: rgba($grey-90, 0.3);\n$grey-90-40: rgba($grey-90, 0.4);\n$grey-90-50: rgba($grey-90, 0.5);\n$grey-90-60: rgba($grey-90, 0.6);\n$grey-90-70: rgba($grey-90, 0.7);\n$grey-90-80: rgba($grey-90, 0.8);\n$grey-90-90: rgba($grey-90, 0.9);\n\n$black: #000;\n$black-5: rgba($black, 0.05);\n$black-10: rgba($black, 0.1);\n$black-15: rgba($black, 0.15);\n$black-20: rgba($black, 0.2);\n$black-25: rgba($black, 0.25);\n$black-30: rgba($black, 0.3);\n\n// Other colors\n$white: #FFF;\n$white-10: rgba($white, 0.1);\n$pocket-teal: #50BCB6;\n$bookmark-icon-fill: #0A84FF;\n$download-icon-fill: #12BC00;\n$pocket-icon-fill: #D70022;\n\n// Photon transitions from http://design.firefox.com/photon/motion/duration-and-easing.html\n$photon-easing: cubic-bezier(0.07, 0.95, 0, 1);\n\n$border-radius: 3px;\n\n// Grid related styles\n$base-gutter: 32px;\n$section-horizontal-padding: 25px;\n$section-vertical-padding: 10px;\n$section-spacing: 40px - $section-vertical-padding * 2;\n$grid-unit: 96px; // 1 top site\n\n$icon-size: 16px;\n$smaller-icon-size: 12px;\n$larger-icon-size: 32px;\n\n$wrapper-default-width: $grid-unit * 2 + $base-gutter * 1 + $section-horizontal-padding * 2; // 2 top sites\n$wrapper-max-width-small: $grid-unit * 3 + $base-gutter * 2 + $section-horizontal-padding * 2; // 3 top sites\n$wrapper-max-width-medium: $grid-unit * 4 + $base-gutter * 3 + $section-horizontal-padding * 2; // 4 top sites\n$wrapper-max-width-large: $grid-unit * 6 + $base-gutter * 5 + $section-horizontal-padding * 2; // 6 top sites\n$wrapper-max-width-widest: $grid-unit * 8 + $base-gutter * 7 + $section-horizontal-padding * 2; // 8 top sites\n// For the breakpoints, we need to add space for the scrollbar to avoid weird\n// layout issues when the scrollbar is visible. 16px is wide enough to cover all\n// OSes and keeps it simpler than a per-OS value.\n$scrollbar-width: 16px;\n$break-point-small: $wrapper-max-width-small + $base-gutter * 2 + $scrollbar-width;\n$break-point-medium: $wrapper-max-width-medium + $base-gutter * 2 + $scrollbar-width;\n$break-point-large: $wrapper-max-width-large + $base-gutter * 2 + $scrollbar-width;\n$break-point-widest: $wrapper-max-width-widest + $base-gutter * 2 + $scrollbar-width;\n\n$section-title-font-size: 13px;\n\n$card-width: $grid-unit * 2 + $base-gutter;\n$card-height: 266px;\n$card-preview-image-height: 122px;\n$card-title-margin: 2px;\n$card-text-line-height: 19px;\n// Larger cards for wider screens:\n$card-width-large: 309px;\n$card-height-large: 370px;\n$card-preview-image-height-large: 155px;\n// Compact cards for Highlights\n$card-height-compact: 160px;\n$card-preview-image-height-compact: 108px;\n\n$topic-margin-top: 12px;\n\n$context-menu-button-size: 27px;\n$context-menu-button-boxshadow: 0 2px $grey-90-10;\n$context-menu-shadow: 0 5px 10px $black-30, 0 0 0 1px $black-20;\n$context-menu-font-size: 14px;\n$context-menu-border-radius: 5px;\n$context-menu-outer-padding: 5px;\n$context-menu-item-padding: 3px 12px;\n\n$error-fallback-font-size: 12px;\n$error-fallback-line-height: 1.5;\n\n$image-path: '../data/content/assets/';\n\n$snippets-container-height: 120px;\n\n$textbox-shadow-size: 4px;\n\n@mixin fade-in {\n box-shadow: inset $inner-box-shadow, $shadow-primary;\n transition: box-shadow 150ms;\n}\n\n@mixin fade-in-card {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n}\n\n@mixin context-menu-button {\n .context-menu-button {\n background-clip: padding-box;\n background-color: var(--newtab-contextmenu-button-color);\n background-image: url('chrome://browser/skin/page-action.svg');\n background-position: 55%;\n border: $border-primary;\n border-radius: 100%;\n box-shadow: $context-menu-button-boxshadow;\n cursor: pointer;\n fill: var(--newtab-icon-primary-color);\n height: $context-menu-button-size;\n inset-inline-end: -($context-menu-button-size / 2);\n opacity: 0;\n position: absolute;\n top: -($context-menu-button-size / 2);\n transform: scale(0.25);\n transition-duration: 200ms;\n transition-property: transform, opacity;\n width: $context-menu-button-size;\n\n &:-moz-any(:active, :focus) {\n opacity: 1;\n transform: scale(1);\n }\n }\n}\n\n@mixin context-menu-button-hover {\n .context-menu-button {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@mixin context-menu-open-middle {\n .context-menu {\n margin-inline-end: auto;\n margin-inline-start: auto;\n inset-inline-end: auto;\n inset-inline-start: -$base-gutter;\n }\n}\n\n@mixin context-menu-open-left {\n .context-menu {\n margin-inline-end: 5px;\n margin-inline-start: auto;\n inset-inline-end: 0;\n inset-inline-start: auto;\n }\n}\n\n@mixin flip-icon {\n &:dir(rtl) {\n transform: scaleX(-1);\n }\n}\n", "@function textbox-shadow($color) {\n @return 0 0 0 1px $color, 0 0 0 $textbox-shadow-size rgba($color, 0.3);\n}\n\n@mixin textbox-focus($color) {\n --newtab-textbox-focus-color: $color;\n --newtab-textbox-focus-boxshadow: textbox-shadow($color);\n}\n\n// scss variables related to the theme.\n$border-primary: 1px solid var(--newtab-border-primary-color);\n$border-secondary: 1px solid var(--newtab-border-secondary-color);\n$inner-box-shadow: 0 0 0 1px var(--newtab-inner-box-shadow-color);\n$input-border: 1px solid var(--newtab-textbox-border);\n$input-border-active: 1px solid var(--newtab-textbox-focus-color);\n$input-error-border: 1px solid $red-60;\n$input-error-boxshadow: textbox-shadow($red-60);\n$shadow-primary: 0 0 0 5px var(--newtab-card-active-outline-color);\n$shadow-secondary: 0 1px 4px 0 $grey-90-20;\n\n// Default theme\nbody {\n // General styles\n --newtab-background-color: $grey-10;\n --newtab-border-primary-color: $grey-40;\n --newtab-border-secondary-color: $grey-30;\n --newtab-button-primary-color: $blue-60;\n --newtab-button-secondary-color: inherit;\n --newtab-element-active-color: $grey-30-60;\n --newtab-element-hover-color: $grey-20;\n --newtab-icon-primary-color: $grey-90-80;\n --newtab-icon-secondary-color: $grey-90-60;\n --newtab-icon-tertiary-color: $grey-30;\n --newtab-inner-box-shadow-color: $black-10;\n --newtab-link-primary-color: $blue-60;\n --newtab-link-secondary-color: $teal-70;\n --newtab-text-conditional-color: $grey-60;\n --newtab-text-primary-color: $grey-90;\n --newtab-text-secondary-color: $grey-50;\n --newtab-textbox-background-color: $white;\n --newtab-textbox-border: $grey-90-20;\n @include textbox-focus($blue-60); // sass-lint:disable-line mixins-before-declarations\n\n // Context menu\n --newtab-contextmenu-background-color: $grey-10;\n --newtab-contextmenu-button-color: $white;\n\n // Modal + overlay\n --newtab-modal-color: $white;\n --newtab-overlay-color: $grey-20-80;\n\n // Sections\n --newtab-section-header-text-color: $grey-50;\n --newtab-section-navigation-text-color: $grey-50;\n --newtab-section-active-contextmenu-color: $grey-90;\n\n // Search\n --newtab-search-border-color: transparent;\n --newtab-search-dropdown-color: $white;\n --newtab-search-dropdown-header-color: $grey-10;\n --newtab-search-icon-color: $grey-90-40;\n\n // Top Sites\n --newtab-topsites-background-color: $white;\n --newtab-topsites-icon-shadow: inset $inner-box-shadow;\n --newtab-topsites-label-color: inherit;\n\n // Cards\n --newtab-card-active-outline-color: $grey-30;\n --newtab-card-background-color: $white;\n --newtab-card-hairline-color: $black-10;\n --newtab-card-shadow: 0 1px 4px 0 $grey-90-10;\n\n // Snippets\n --newtab-snippets-background-color: $white;\n --newtab-snippets-hairline-color: transparent;\n}\n\n// Dark theme\n.dark-theme {\n // General styles\n --newtab-background-color: $grey-80;\n --newtab-border-primary-color: $grey-10-80;\n --newtab-border-secondary-color: $grey-10-10;\n --newtab-button-primary-color: $blue-60;\n --newtab-button-secondary-color: $grey-70;\n --newtab-element-active-color: $grey-10-20;\n --newtab-element-hover-color: $grey-10-10;\n --newtab-icon-primary-color: $grey-10-80;\n --newtab-icon-secondary-color: $grey-10-40;\n --newtab-icon-tertiary-color: $grey-10-40;\n --newtab-inner-box-shadow-color: $grey-10-20;\n --newtab-link-primary-color: $blue-40;\n --newtab-link-secondary-color: $pocket-teal;\n --newtab-text-conditional-color: $grey-10;\n --newtab-text-primary-color: $grey-10;\n --newtab-text-secondary-color: $grey-10-80;\n --newtab-textbox-background-color: $grey-70;\n --newtab-textbox-border: $grey-10-20;\n @include textbox-focus($blue-40); // sass-lint:disable-line mixins-before-declarations\n\n // Context menu\n --newtab-contextmenu-background-color: $grey-60;\n --newtab-contextmenu-button-color: $grey-80;\n\n // Modal + overlay\n --newtab-modal-color: $grey-80;\n --newtab-overlay-color: $grey-90-80;\n\n // Sections\n --newtab-section-header-text-color: $grey-10-80;\n --newtab-section-navigation-text-color: $grey-10-80;\n --newtab-section-active-contextmenu-color: $white;\n\n // Search\n --newtab-search-border-color: $grey-10-20;\n --newtab-search-dropdown-color: $grey-70;\n --newtab-search-dropdown-header-color: $grey-60;\n --newtab-search-icon-color: $grey-10-60;\n\n // Top Sites\n --newtab-topsites-background-color: $grey-70;\n --newtab-topsites-icon-shadow: none;\n --newtab-topsites-label-color: $grey-10-80;\n\n // Cards\n --newtab-card-active-outline-color: $grey-60;\n --newtab-card-background-color: $grey-70;\n --newtab-card-hairline-color: $grey-10-10;\n --newtab-card-shadow: 0 1px 8px 0 $grey-90-20;\n\n // Snippets\n --newtab-snippets-background-color: $grey-70;\n --newtab-snippets-hairline-color: $white-10;\n}\n", ".icon {\n background-position: center center;\n background-repeat: no-repeat;\n background-size: $icon-size;\n -moz-context-properties: fill;\n display: inline-block;\n fill: var(--newtab-icon-primary-color);\n height: $icon-size;\n vertical-align: middle;\n width: $icon-size;\n\n &.icon-spacer {\n margin-inline-end: 8px;\n }\n\n &.icon-small-spacer {\n margin-inline-end: 6px;\n }\n\n &.icon-bookmark-added {\n background-image: url('chrome://browser/skin/bookmark.svg');\n }\n\n &.icon-bookmark-hollow {\n background-image: url('chrome://browser/skin/bookmark-hollow.svg');\n }\n\n &.icon-clear-input {\n fill: var(--newtab-icon-secondary-color);\n background-image: url('#{$image-path}glyph-cancel-16.svg');\n }\n\n &.icon-delete {\n background-image: url('#{$image-path}glyph-delete-16.svg');\n }\n\n &.icon-search {\n background-image: url('chrome://browser/skin/search-glass.svg');\n }\n\n &.icon-modal-delete {\n flex-shrink: 0;\n background-image: url('#{$image-path}glyph-modal-delete-32.svg');\n background-size: $larger-icon-size;\n height: $larger-icon-size;\n width: $larger-icon-size;\n }\n\n &.icon-dismiss {\n background-image: url('#{$image-path}glyph-dismiss-16.svg');\n }\n\n &.icon-info {\n background-image: url('#{$image-path}glyph-info-16.svg');\n }\n\n &.icon-import {\n background-image: url('#{$image-path}glyph-import-16.svg');\n }\n\n &.icon-new-window {\n @include flip-icon;\n background-image: url('#{$image-path}glyph-newWindow-16.svg');\n }\n\n &.icon-new-window-private {\n background-image: url('chrome://browser/skin/privateBrowsing.svg');\n }\n\n &.icon-settings {\n background-image: url('chrome://browser/skin/settings.svg');\n }\n\n &.icon-pin {\n @include flip-icon;\n background-image: url('#{$image-path}glyph-pin-16.svg');\n }\n\n &.icon-unpin {\n @include flip-icon;\n background-image: url('#{$image-path}glyph-unpin-16.svg');\n }\n\n &.icon-edit {\n background-image: url('#{$image-path}glyph-edit-16.svg');\n }\n\n &.icon-pocket {\n background-image: url('#{$image-path}glyph-pocket-16.svg');\n }\n\n &.icon-history-item {\n background-image: url('chrome://browser/skin/history.svg');\n }\n\n &.icon-trending {\n background-image: url('#{$image-path}glyph-trending-16.svg');\n transform: translateY(2px); // trending bolt is visually top heavy\n }\n\n &.icon-now {\n background-image: url('chrome://browser/skin/history.svg');\n }\n\n &.icon-topsites {\n background-image: url('#{$image-path}glyph-topsites-16.svg');\n }\n\n &.icon-pin-small {\n @include flip-icon;\n background-image: url('#{$image-path}glyph-pin-12.svg');\n background-size: $smaller-icon-size;\n height: $smaller-icon-size;\n width: $smaller-icon-size;\n }\n\n &.icon-check {\n background-image: url('chrome://browser/skin/check.svg');\n }\n\n &.icon-download {\n background-image: url('chrome://browser/skin/downloads/download-icons.svg#arrow-with-bar');\n }\n\n &.icon-copy {\n background-image: url('chrome://browser/skin/edit-copy.svg');\n }\n\n &.icon-open-file {\n background-image: url('#{$image-path}glyph-open-file-16.svg');\n }\n\n &.icon-webextension {\n background-image: url('#{$image-path}glyph-webextension-16.svg');\n }\n\n &.icon-highlights {\n background-image: url('#{$image-path}glyph-highlights-16.svg');\n }\n\n &.icon-arrowhead-down {\n background-image: url('#{$image-path}glyph-arrowhead-down-16.svg');\n }\n\n &.icon-arrowhead-down-small {\n background-image: url('#{$image-path}glyph-arrowhead-down-12.svg');\n background-size: $smaller-icon-size;\n height: $smaller-icon-size;\n width: $smaller-icon-size;\n }\n\n &.icon-arrowhead-forward-small {\n background-image: url('#{$image-path}glyph-arrowhead-down-12.svg');\n background-size: $smaller-icon-size;\n height: $smaller-icon-size;\n transform: rotate(-90deg);\n width: $smaller-icon-size;\n\n &:dir(rtl) {\n transform: rotate(90deg);\n }\n }\n\n &.icon-arrowhead-up {\n background-image: url('#{$image-path}glyph-arrowhead-down-16.svg');\n transform: rotate(180deg);\n }\n\n &.icon-add {\n background-image: url('#{$image-path}glyph-add-16.svg');\n }\n\n &.icon-minimize {\n background-image: url('#{$image-path}glyph-minimize-16.svg');\n }\n\n &.icon-maximize {\n background-image: url('#{$image-path}glyph-maximize-16.svg');\n }\n}\n", - ".outer-wrapper {\n color: var(--newtab-text-primary-color);\n display: flex;\n flex-grow: 1;\n min-height: 100vh;\n padding: ($section-spacing + $section-vertical-padding) $base-gutter $base-gutter;\n\n &.fixed-to-top {\n display: block;\n }\n\n a {\n color: var(--newtab-link-primary-color);\n }\n}\n\nmain {\n margin: auto;\n // Offset the snippets container so things at the bottom of the page are still\n // visible when snippets / onboarding are visible. Adjust for other spacing.\n padding-bottom: $snippets-container-height - $section-spacing - $base-gutter;\n width: $wrapper-default-width;\n\n @media (min-width: $break-point-small) {\n width: $wrapper-max-width-small;\n }\n\n @media (min-width: $break-point-medium) {\n width: $wrapper-max-width-medium;\n }\n\n @media (min-width: $break-point-large) {\n width: $wrapper-max-width-large;\n }\n\n @media (min-width: $break-point-widest) {\n width: $wrapper-max-width-widest;\n }\n\n section {\n margin-bottom: $section-spacing;\n position: relative;\n }\n}\n\n.base-content-fallback {\n // Make the error message be centered against the viewport\n height: 100vh;\n}\n\n.body-wrapper {\n // Hide certain elements so the page structure is fixed, e.g., placeholders,\n // while avoiding flashes of changing content, e.g., icons and text\n $selectors-to-hide: '\n .section-title,\n .sections-list .section:last-of-type,\n .topic\n ';\n\n #{$selectors-to-hide} {\n opacity: 0;\n }\n\n &.on {\n #{$selectors-to-hide} {\n opacity: 1;\n }\n }\n}\n\n.non-collapsible-section {\n padding: 0 $section-horizontal-padding;\n}\n\n.prefs-button {\n button {\n background-color: transparent;\n border: 0;\n cursor: pointer;\n fill: var(--newtab-icon-primary-color);\n offset-inline-end: 15px;\n padding: 15px;\n position: fixed;\n top: 15px;\n z-index: 1000;\n\n &:hover,\n &:focus {\n background-color: var(--newtab-element-hover-color);\n }\n\n &:active {\n background-color: var(--newtab-element-active-color);\n }\n }\n}\n", + ".outer-wrapper {\n color: var(--newtab-text-primary-color);\n display: flex;\n flex-grow: 1;\n min-height: 100vh;\n padding: ($section-spacing + $section-vertical-padding) $base-gutter $base-gutter;\n\n &.fixed-to-top {\n display: block;\n }\n\n a {\n color: var(--newtab-link-primary-color);\n }\n}\n\nmain {\n margin: auto;\n // Offset the snippets container so things at the bottom of the page are still\n // visible when snippets / onboarding are visible. Adjust for other spacing.\n padding-bottom: $snippets-container-height - $section-spacing - $base-gutter;\n width: $wrapper-default-width;\n\n @media (min-width: $break-point-small) {\n width: $wrapper-max-width-small;\n }\n\n @media (min-width: $break-point-medium) {\n width: $wrapper-max-width-medium;\n }\n\n @media (min-width: $break-point-large) {\n width: $wrapper-max-width-large;\n }\n\n @media (min-width: $break-point-widest) {\n width: $wrapper-max-width-widest;\n }\n\n section {\n margin-bottom: $section-spacing;\n position: relative;\n }\n}\n\n.base-content-fallback {\n // Make the error message be centered against the viewport\n height: 100vh;\n}\n\n.body-wrapper {\n // Hide certain elements so the page structure is fixed, e.g., placeholders,\n // while avoiding flashes of changing content, e.g., icons and text\n $selectors-to-hide: '\n .section-title,\n .sections-list .section:last-of-type,\n .topic\n ';\n\n #{$selectors-to-hide} {\n opacity: 0;\n }\n\n &.on {\n #{$selectors-to-hide} {\n opacity: 1;\n }\n }\n}\n\n.non-collapsible-section {\n padding: 0 $section-horizontal-padding;\n}\n\n.prefs-button {\n button {\n background-color: transparent;\n border: 0;\n cursor: pointer;\n fill: var(--newtab-icon-primary-color);\n inset-inline-end: 15px;\n padding: 15px;\n position: fixed;\n top: 15px;\n z-index: 1000;\n\n &:hover,\n &:focus {\n background-color: var(--newtab-element-hover-color);\n }\n\n &:active {\n background-color: var(--newtab-element-active-color);\n }\n }\n}\n", ".as-error-fallback {\n align-items: center;\n border-radius: $border-radius;\n box-shadow: inset $inner-box-shadow;\n color: var(--newtab-text-conditional-color);\n display: flex;\n flex-direction: column;\n font-size: $error-fallback-font-size;\n justify-content: center;\n justify-items: center;\n line-height: $error-fallback-line-height;\n\n a {\n color: var(--newtab-text-conditional-color);\n text-decoration: underline;\n }\n}\n", - "$top-sites-size: $grid-unit;\n$top-sites-border-radius: 6px;\n$top-sites-title-height: 30px;\n$top-sites-vertical-space: 8px;\n$screenshot-size: cover;\n$rich-icon-size: 96px;\n$default-icon-wrapper-size: 42px;\n$default-icon-size: 32px;\n$default-icon-offset: 6px;\n$half-base-gutter: $base-gutter / 2;\n\n.top-sites {\n // Take back the margin from the bottom row of vertical spacing as well as the\n // extra whitespace below the title text as it's vertically centered.\n margin-bottom: $section-spacing - ($top-sites-vertical-space + $top-sites-title-height / 3);\n}\n\n.top-sites-list {\n list-style: none;\n margin: 0 (-$half-base-gutter);\n padding: 0;\n\n // Two columns\n @media (max-width: $break-point-small) {\n :nth-child(2n+1) {\n @include context-menu-open-middle;\n }\n\n :nth-child(2n) {\n @include context-menu-open-left;\n }\n }\n\n // Three columns\n @media (min-width: $break-point-small) and (max-width: $break-point-medium) {\n :nth-child(3n+2),\n :nth-child(3n) {\n @include context-menu-open-left;\n }\n }\n\n // Four columns\n @media (min-width: $break-point-medium) and (max-width: $break-point-large) {\n :nth-child(4n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-medium) and (max-width: $break-point-medium + $card-width) {\n :nth-child(4n+3) {\n @include context-menu-open-left;\n }\n }\n\n // Six columns\n @media (min-width: $break-point-large) and (max-width: $break-point-large + 2 * $card-width) {\n :nth-child(6n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-large) and (max-width: $break-point-large + $card-width) {\n :nth-child(6n+5) {\n @include context-menu-open-left;\n }\n }\n\n // Eight columns\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + 2 * $card-width) {\n :nth-child(8n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + $card-width) {\n :nth-child(8n+7) {\n @include context-menu-open-left;\n }\n }\n\n @media not all and (min-width: $break-point-widest) {\n .hide-for-narrow {\n display: none;\n }\n }\n\n li {\n margin: 0 0 $top-sites-vertical-space;\n }\n\n &:not(.dnd-active) {\n .top-site-outer:-moz-any(.active, :focus, :hover) {\n .tile {\n @include fade-in;\n }\n\n @include context-menu-button-hover;\n }\n }\n}\n\n// container for drop zone\n.top-site-outer {\n padding: 0 $half-base-gutter;\n display: inline-block;\n\n // container for context menu\n .top-site-inner {\n position: relative;\n\n > a {\n color: inherit;\n display: block;\n outline: none;\n\n &:-moz-any(.active, :focus) {\n .tile {\n @include fade-in;\n }\n }\n }\n }\n\n @include context-menu-button;\n\n .tile { // sass-lint:disable-block property-sort-order\n border-radius: $top-sites-border-radius;\n box-shadow: inset $inner-box-shadow, var(--newtab-card-shadow);\n height: $top-sites-size;\n position: relative;\n width: $top-sites-size;\n\n // For letter fallback\n align-items: center;\n color: var(--newtab-text-secondary-color);\n display: flex;\n font-size: 32px;\n font-weight: 200;\n justify-content: center;\n text-transform: uppercase;\n\n &::before {\n content: attr(data-fallback);\n }\n }\n\n .screenshot {\n background-color: $white;\n background-position: top left;\n background-size: $screenshot-size;\n border-radius: $top-sites-border-radius;\n box-shadow: inset $inner-box-shadow;\n height: 100%;\n left: 0;\n opacity: 0;\n position: absolute;\n top: 0;\n transition: opacity 1s;\n width: 100%;\n\n &.active {\n opacity: 1;\n }\n }\n\n // Some common styles for all icons (rich and default) in top sites\n .top-site-icon {\n background-color: var(--newtab-topsites-background-color);\n background-position: center center;\n background-repeat: no-repeat;\n border-radius: $top-sites-border-radius;\n box-shadow: var(--newtab-topsites-icon-shadow);\n position: absolute;\n }\n\n .rich-icon {\n background-size: cover;\n height: 100%;\n offset-inline-start: 0;\n top: 0;\n width: 100%;\n }\n\n .default-icon { // sass-lint:disable block property-sort-order\n background-size: $default-icon-size;\n bottom: -$default-icon-offset;\n height: $default-icon-wrapper-size;\n offset-inline-end: -$default-icon-offset;\n width: $default-icon-wrapper-size;\n\n // for corner letter fallback\n align-items: center;\n display: flex;\n font-size: 20px;\n justify-content: center;\n\n &[data-fallback]::before {\n content: attr(data-fallback);\n }\n }\n\n .title {\n color: var(--newtab-topsites-label-color);\n font: message-box;\n height: $top-sites-title-height;\n line-height: $top-sites-title-height;\n text-align: center;\n width: $top-sites-size;\n position: relative;\n\n .icon {\n fill: var(--newtab-icon-tertiary-color);\n offset-inline-start: 0;\n position: absolute;\n top: 10px;\n }\n\n span {\n height: $top-sites-title-height;\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &.pinned {\n span {\n padding: 0 13px;\n }\n }\n }\n\n .edit-button {\n background-image: url('#{$image-path}glyph-edit-16.svg');\n }\n\n &.placeholder {\n .tile {\n box-shadow: inset $inner-box-shadow;\n }\n\n .screenshot {\n display: none;\n }\n }\n\n &.dragged {\n .tile {\n background: $grey-20;\n box-shadow: none;\n\n *,\n &::before {\n display: none;\n }\n }\n\n .title {\n visibility: hidden;\n }\n }\n}\n\n.edit-topsites-wrapper {\n .modal {\n box-shadow: $shadow-secondary;\n left: 0;\n margin: 0 auto;\n position: fixed;\n right: 0;\n top: 40px;\n width: $wrapper-default-width;\n\n @media (min-width: $break-point-small) {\n width: $wrapper-max-width-small;\n }\n\n @media (min-width: $break-point-medium) {\n width: $wrapper-max-width-medium;\n }\n\n @media (min-width: $break-point-large) {\n width: $wrapper-max-width-large;\n }\n }\n}\n\n.topsite-form {\n $form-width: 300px;\n $form-spacing: 32px;\n\n .form-input-container {\n max-width: $form-width + 3 * $form-spacing + $rich-icon-size;\n margin: 0 auto;\n padding: $form-spacing;\n\n .top-site-outer {\n padding: 0;\n margin: 24px 0 0;\n margin-inline-start: $form-spacing;\n pointer-events: none;\n }\n\n .section-title {\n text-transform: none;\n font-size: 16px;\n margin: 0 0 16px;\n }\n }\n\n .fields-and-preview {\n display: flex;\n }\n\n label {\n font-size: $section-title-font-size;\n }\n\n .form-wrapper {\n width: 100%;\n\n .field {\n position: relative;\n\n .icon-clear-input {\n position: absolute;\n transform: translateY(-50%);\n top: 50%;\n offset-inline-end: 8px;\n }\n }\n\n .url {\n input:dir(ltr) {\n padding-right: 32px;\n }\n\n input:dir(rtl) {\n padding-left: 32px;\n\n &:not(:placeholder-shown) {\n direction: ltr;\n text-align: right;\n }\n }\n }\n\n .enable-custom-image-input {\n display: inline-block;\n font-size: 13px;\n margin-top: 4px;\n cursor: pointer;\n\n &:hover {\n text-decoration: underline;\n }\n }\n\n .custom-image-input-container {\n margin-top: 4px;\n\n .loading-container {\n width: 16px;\n height: 16px;\n overflow: hidden;\n position: absolute;\n transform: translateY(-50%);\n top: 50%;\n offset-inline-end: 8px;\n }\n\n // This animation is derived from Firefox's tab loading animation\n // See https://searchfox.org/mozilla-central/rev/b29daa46443b30612415c35be0a3c9c13b9dc5f6/browser/themes/shared/tabs.inc.css#208-216\n .loading-animation {\n @keyframes tab-throbber-animation {\n 100% { transform: translateX(-960px); }\n }\n\n @keyframes tab-throbber-animation-rtl {\n 100% { transform: translateX(960px); }\n }\n\n width: 960px;\n height: 16px;\n -moz-context-properties: fill;\n fill: $blue-50;\n background-image: url('chrome://browser/skin/tabbrowser/loading.svg');\n animation: tab-throbber-animation 1.05s steps(60) infinite;\n\n &:dir(rtl) {\n animation-name: tab-throbber-animation-rtl;\n }\n }\n }\n\n input {\n &[type='text'] {\n background-color: var(--newtab-textbox-background-color);\n border: $input-border;\n margin: 8px 0;\n padding: 0 8px;\n height: 32px;\n width: 100%;\n font-size: 15px;\n\n &:focus {\n border: $input-border-active;\n box-shadow: var(--newtab-textbox-focus-boxshadow);\n }\n\n &[disabled] {\n border: $input-border;\n box-shadow: none;\n opacity: 0.4;\n }\n }\n }\n\n .invalid {\n input {\n &[type='text'] {\n border: $input-error-border;\n box-shadow: $input-error-boxshadow;\n }\n }\n }\n\n .error-tooltip {\n animation: fade-up-tt 450ms;\n background: $red-60;\n border-radius: 2px;\n color: $white;\n offset-inline-start: 3px;\n padding: 5px 12px;\n position: absolute;\n top: 44px;\n z-index: 1;\n\n // tooltip caret\n &::before {\n background: $red-60;\n bottom: -8px;\n content: '.';\n height: 16px;\n offset-inline-start: 12px;\n position: absolute;\n text-indent: -999px;\n top: -7px;\n transform: rotate(45deg);\n white-space: nowrap;\n width: 16px;\n z-index: -1;\n }\n }\n }\n\n .actions {\n justify-content: flex-end;\n\n button {\n margin-inline-start: 10px;\n margin-inline-end: 0;\n }\n }\n\n @media (max-width: $break-point-small) {\n .fields-and-preview {\n flex-direction: column;\n\n .top-site-outer {\n margin-inline-start: 0;\n }\n }\n }\n}\n\n//used for tooltips below form element\n@keyframes fade-up-tt {\n 0% {\n opacity: 0;\n transform: translateY(15px);\n }\n\n 100% {\n opacity: 1;\n transform: translateY(0);\n }\n}\n", + "$top-sites-size: $grid-unit;\n$top-sites-border-radius: 6px;\n$top-sites-title-height: 30px;\n$top-sites-vertical-space: 8px;\n$screenshot-size: cover;\n$rich-icon-size: 96px;\n$default-icon-wrapper-size: 42px;\n$default-icon-size: 32px;\n$default-icon-offset: 6px;\n$half-base-gutter: $base-gutter / 2;\n\n.top-sites {\n // Take back the margin from the bottom row of vertical spacing as well as the\n // extra whitespace below the title text as it's vertically centered.\n margin-bottom: $section-spacing - ($top-sites-vertical-space + $top-sites-title-height / 3);\n}\n\n.top-sites-list {\n list-style: none;\n margin: 0 (-$half-base-gutter);\n padding: 0;\n\n // Two columns\n @media (max-width: $break-point-small) {\n :nth-child(2n+1) {\n @include context-menu-open-middle;\n }\n\n :nth-child(2n) {\n @include context-menu-open-left;\n }\n }\n\n // Three columns\n @media (min-width: $break-point-small) and (max-width: $break-point-medium) {\n :nth-child(3n+2),\n :nth-child(3n) {\n @include context-menu-open-left;\n }\n }\n\n // Four columns\n @media (min-width: $break-point-medium) and (max-width: $break-point-large) {\n :nth-child(4n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-medium) and (max-width: $break-point-medium + $card-width) {\n :nth-child(4n+3) {\n @include context-menu-open-left;\n }\n }\n\n // Six columns\n @media (min-width: $break-point-large) and (max-width: $break-point-large + 2 * $card-width) {\n :nth-child(6n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-large) and (max-width: $break-point-large + $card-width) {\n :nth-child(6n+5) {\n @include context-menu-open-left;\n }\n }\n\n // Eight columns\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + 2 * $card-width) {\n :nth-child(8n) {\n @include context-menu-open-left;\n }\n }\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + $card-width) {\n :nth-child(8n+7) {\n @include context-menu-open-left;\n }\n }\n\n @media not all and (min-width: $break-point-widest) {\n .hide-for-narrow {\n display: none;\n }\n }\n\n li {\n margin: 0 0 $top-sites-vertical-space;\n }\n\n &:not(.dnd-active) {\n .top-site-outer:-moz-any(.active, :focus, :hover) {\n .tile {\n @include fade-in;\n }\n\n @include context-menu-button-hover;\n }\n }\n}\n\n// container for drop zone\n.top-site-outer {\n padding: 0 $half-base-gutter;\n display: inline-block;\n\n // container for context menu\n .top-site-inner {\n position: relative;\n\n > a {\n color: inherit;\n display: block;\n outline: none;\n\n &:-moz-any(.active, :focus) {\n .tile {\n @include fade-in;\n }\n }\n }\n }\n\n @include context-menu-button;\n\n .tile { // sass-lint:disable-block property-sort-order\n border-radius: $top-sites-border-radius;\n box-shadow: inset $inner-box-shadow, var(--newtab-card-shadow);\n height: $top-sites-size;\n position: relative;\n width: $top-sites-size;\n\n // For letter fallback\n align-items: center;\n color: var(--newtab-text-secondary-color);\n display: flex;\n font-size: 32px;\n font-weight: 200;\n justify-content: center;\n text-transform: uppercase;\n\n &::before {\n content: attr(data-fallback);\n }\n }\n\n .screenshot {\n background-color: $white;\n background-position: top left;\n background-size: $screenshot-size;\n border-radius: $top-sites-border-radius;\n box-shadow: inset $inner-box-shadow;\n height: 100%;\n left: 0;\n opacity: 0;\n position: absolute;\n top: 0;\n transition: opacity 1s;\n width: 100%;\n\n &.active {\n opacity: 1;\n }\n }\n\n // Some common styles for all icons (rich and default) in top sites\n .top-site-icon {\n background-color: var(--newtab-topsites-background-color);\n background-position: center center;\n background-repeat: no-repeat;\n border-radius: $top-sites-border-radius;\n box-shadow: var(--newtab-topsites-icon-shadow);\n position: absolute;\n }\n\n .rich-icon {\n background-size: cover;\n height: 100%;\n inset-inline-start: 0;\n top: 0;\n width: 100%;\n }\n\n .default-icon { // sass-lint:disable block property-sort-order\n background-size: $default-icon-size;\n bottom: -$default-icon-offset;\n height: $default-icon-wrapper-size;\n inset-inline-end: -$default-icon-offset;\n width: $default-icon-wrapper-size;\n\n // for corner letter fallback\n align-items: center;\n display: flex;\n font-size: 20px;\n justify-content: center;\n\n &[data-fallback]::before {\n content: attr(data-fallback);\n }\n }\n\n .title {\n color: var(--newtab-topsites-label-color);\n font: message-box;\n height: $top-sites-title-height;\n line-height: $top-sites-title-height;\n text-align: center;\n width: $top-sites-size;\n position: relative;\n\n .icon {\n fill: var(--newtab-icon-tertiary-color);\n inset-inline-start: 0;\n position: absolute;\n top: 10px;\n }\n\n span {\n height: $top-sites-title-height;\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &.pinned {\n span {\n padding: 0 13px;\n }\n }\n }\n\n .edit-button {\n background-image: url('#{$image-path}glyph-edit-16.svg');\n }\n\n &.placeholder {\n .tile {\n box-shadow: inset $inner-box-shadow;\n }\n\n .screenshot {\n display: none;\n }\n }\n\n &.dragged {\n .tile {\n background: $grey-20;\n box-shadow: none;\n\n *,\n &::before {\n display: none;\n }\n }\n\n .title {\n visibility: hidden;\n }\n }\n}\n\n.edit-topsites-wrapper {\n .modal {\n box-shadow: $shadow-secondary;\n left: 0;\n margin: 0 auto;\n position: fixed;\n right: 0;\n top: 40px;\n width: $wrapper-default-width;\n\n @media (min-width: $break-point-small) {\n width: $wrapper-max-width-small;\n }\n\n @media (min-width: $break-point-medium) {\n width: $wrapper-max-width-medium;\n }\n\n @media (min-width: $break-point-large) {\n width: $wrapper-max-width-large;\n }\n }\n}\n\n.topsite-form {\n $form-width: 300px;\n $form-spacing: 32px;\n\n .form-input-container {\n max-width: $form-width + 3 * $form-spacing + $rich-icon-size;\n margin: 0 auto;\n padding: $form-spacing;\n\n .top-site-outer {\n padding: 0;\n margin: 24px 0 0;\n margin-inline-start: $form-spacing;\n pointer-events: none;\n }\n\n .section-title {\n text-transform: none;\n font-size: 16px;\n margin: 0 0 16px;\n }\n }\n\n .fields-and-preview {\n display: flex;\n }\n\n label {\n font-size: $section-title-font-size;\n }\n\n .form-wrapper {\n width: 100%;\n\n .field {\n position: relative;\n\n .icon-clear-input {\n position: absolute;\n transform: translateY(-50%);\n top: 50%;\n inset-inline-end: 8px;\n }\n }\n\n .url {\n input:dir(ltr) {\n padding-right: 32px;\n }\n\n input:dir(rtl) {\n padding-left: 32px;\n\n &:not(:placeholder-shown) {\n direction: ltr;\n text-align: right;\n }\n }\n }\n\n .enable-custom-image-input {\n display: inline-block;\n font-size: 13px;\n margin-top: 4px;\n cursor: pointer;\n\n &:hover {\n text-decoration: underline;\n }\n }\n\n .custom-image-input-container {\n margin-top: 4px;\n\n .loading-container {\n width: 16px;\n height: 16px;\n overflow: hidden;\n position: absolute;\n transform: translateY(-50%);\n top: 50%;\n inset-inline-end: 8px;\n }\n\n // This animation is derived from Firefox's tab loading animation\n // See https://searchfox.org/mozilla-central/rev/b29daa46443b30612415c35be0a3c9c13b9dc5f6/browser/themes/shared/tabs.inc.css#208-216\n .loading-animation {\n @keyframes tab-throbber-animation {\n 100% { transform: translateX(-960px); }\n }\n\n @keyframes tab-throbber-animation-rtl {\n 100% { transform: translateX(960px); }\n }\n\n width: 960px;\n height: 16px;\n -moz-context-properties: fill;\n fill: $blue-50;\n background-image: url('chrome://browser/skin/tabbrowser/loading.svg');\n animation: tab-throbber-animation 1.05s steps(60) infinite;\n\n &:dir(rtl) {\n animation-name: tab-throbber-animation-rtl;\n }\n }\n }\n\n input {\n &[type='text'] {\n background-color: var(--newtab-textbox-background-color);\n border: $input-border;\n margin: 8px 0;\n padding: 0 8px;\n height: 32px;\n width: 100%;\n font-size: 15px;\n\n &:focus {\n border: $input-border-active;\n box-shadow: var(--newtab-textbox-focus-boxshadow);\n }\n\n &[disabled] {\n border: $input-border;\n box-shadow: none;\n opacity: 0.4;\n }\n }\n }\n\n .invalid {\n input {\n &[type='text'] {\n border: $input-error-border;\n box-shadow: $input-error-boxshadow;\n }\n }\n }\n\n .error-tooltip {\n animation: fade-up-tt 450ms;\n background: $red-60;\n border-radius: 2px;\n color: $white;\n inset-inline-start: 3px;\n padding: 5px 12px;\n position: absolute;\n top: 44px;\n z-index: 1;\n\n // tooltip caret\n &::before {\n background: $red-60;\n bottom: -8px;\n content: '.';\n height: 16px;\n inset-inline-start: 12px;\n position: absolute;\n text-indent: -999px;\n top: -7px;\n transform: rotate(45deg);\n white-space: nowrap;\n width: 16px;\n z-index: -1;\n }\n }\n }\n\n .actions {\n justify-content: flex-end;\n\n button {\n margin-inline-start: 10px;\n margin-inline-end: 0;\n }\n }\n\n @media (max-width: $break-point-small) {\n .fields-and-preview {\n flex-direction: column;\n\n .top-site-outer {\n margin-inline-start: 0;\n }\n }\n }\n}\n\n//used for tooltips below form element\n@keyframes fade-up-tt {\n 0% {\n opacity: 0;\n transform: translateY(15px);\n }\n\n 100% {\n opacity: 1;\n transform: translateY(0);\n }\n}\n", ".sections-list {\n .section-list {\n display: grid;\n grid-gap: $base-gutter;\n grid-template-columns: repeat(auto-fit, $card-width);\n margin: 0;\n\n @media (max-width: $break-point-medium) {\n @include context-menu-open-left;\n }\n\n @media (min-width: $break-point-medium) and (max-width: $break-point-large) {\n :nth-child(2n) {\n @include context-menu-open-left;\n }\n }\n\n @media (min-width: $break-point-large) and (max-width: $break-point-large + 2 * $card-width) {\n :nth-child(3n) {\n @include context-menu-open-left;\n }\n }\n\n @media (min-width: $break-point-widest) and (max-width: $break-point-widest + 2 * $card-width) {\n :nth-child(3n) {\n @include context-menu-open-left;\n }\n }\n }\n\n .section-empty-state {\n border: $border-secondary;\n border-radius: $border-radius;\n display: flex;\n height: $card-height;\n width: 100%;\n\n .empty-state {\n margin: auto;\n max-width: 350px;\n\n .empty-state-icon {\n background-position: center;\n background-repeat: no-repeat;\n background-size: 50px 50px;\n -moz-context-properties: fill;\n display: block;\n fill: var(--newtab-icon-secondary-color);\n height: 50px;\n margin: 0 auto;\n width: 50px;\n }\n\n .empty-state-message {\n color: var(--newtab-text-primary-color);\n font-size: 13px;\n margin-bottom: 0;\n text-align: center;\n }\n }\n\n @media (min-width: $break-point-widest) {\n height: $card-height-large;\n }\n }\n}\n\n@media (min-width: $break-point-widest) {\n .sections-list {\n // Compact cards stay the same size but normal cards get bigger.\n .normal-cards {\n .section-list {\n grid-template-columns: repeat(auto-fit, $card-width-large);\n }\n }\n }\n}\n", ".activity-stream {\n &.welcome {\n overflow: hidden;\n }\n\n &:not(.welcome) {\n .overlay-wrapper {\n display: none;\n }\n }\n}\n\n.overlay-wrapper {\n position: absolute;\n top: 0;\n width: 100vw;\n height: 100vh;\n z-index: 21000;\n transition: opacity 0.4s;\n opacity: 0;\n overflow-x: auto;\n\n &.show {\n transition: none;\n opacity: 1;\n\n .firstrun-sign-in {\n transition: opacity 1.5s, transform 1.5s;\n transition-delay: 0.2s;\n transform: translateY(-50%) scale(1);\n opacity: 1;\n\n @media screen and (max-width: 790px) {\n float: none;\n margin: auto;\n top: 190px;\n margin-bottom: 100px;\n }\n }\n\n .firstrun-firefox-logo {\n transition: opacity 2.3s;\n opacity: 1;\n }\n\n .firstrun-title,\n .firstrun-content,\n .firstrun-link {\n transition: transform 0.5s, opacity 0.8s;\n transform: translateY(0);\n opacity: 1;\n }\n\n .firstrun-title {\n transition-delay: 0.2s;\n }\n\n .firstrun-content {\n transition-delay: 0.4s;\n }\n\n .firstrun-link {\n transition-delay: 0.6s;\n }\n\n .fxaccounts-container {\n transition: none;\n opacity: 1;\n }\n }\n}\n\n.background {\n width: 100%;\n height: 100%;\n display: block;\n background: url('#{$image-path}fox-tail.png') top -200px center no-repeat,\n linear-gradient(to bottom, $blue-70 40%, #004EC2 60%, $blue-60 80%, #0080FF 90%, #00C7FF 100%) top center no-repeat,\n $blue-70;\n background-size: cover;\n position: fixed;\n}\n\n.firstrun-sign-in {\n transform: translateY(-50%) scale(0.8);\n position: relative;\n top: 50%;\n width: 358px;\n opacity: 0;\n background-color: $white;\n float: inline-end;\n color: $grey-90;\n text-align: center;\n padding: 10px;\n\n .extra-links {\n font-size: 12px;\n max-width: 340px;\n margin: 14px 50px;\n color: #676F7E;\n cursor: default;\n\n a {\n color: $grey-50;\n cursor: pointer;\n text-decoration: underline;\n }\n\n a:hover,\n a:active,\n a:focus {\n color: $blue-50;\n }\n }\n\n .email-input {\n box-shadow: none;\n margin: auto;\n width: 244px;\n display: block;\n height: 40px;\n padding-inline-start: 20px;\n border: 1px solid $grey-50;\n border-radius: 2px;\n font-size: 16px;\n transition: border-color 150ms, box-shadow 150ms;\n\n &:hover {\n border-color: $grey-90;\n }\n\n &:focus {\n border-color: $blue-50;\n box-shadow: 0 0 0 3px rgba(10, 132, 255, 0.3);\n }\n }\n\n .form-header {\n font-size: 22px;\n margin: 15px auto;\n }\n\n .form-header .sub-header {\n font-size: 14px;\n margin-top: 4px;\n display: block;\n }\n\n button {\n display: block;\n cursor: pointer;\n margin: 10px auto 0;\n }\n\n .continue-button {\n font-size: 18px;\n height: 43px;\n width: 250px;\n padding: 8px 0;\n border: 0;\n border-radius: 4px;\n color: $white;\n background-color: $blue-60;\n transition: background-color 150ms;\n\n &:not([disabled]):active,\n &:not([disabled]):hover {\n background: $blue-70;\n border-color: $blue-80;\n }\n }\n\n .skip-button {\n font-size: 13px;\n margin-top: 35px;\n margin-bottom: 20px;\n background-color: #FCFCFC;\n color: $blue-50;\n border: 1px solid $blue-50;\n border-radius: 2px;\n min-height: 24px;\n padding: 5px 10px;\n transition: background-color 150ms, color 150ms, border-color 150ms;\n\n &[disabled] {\n background-color: #EBEBEB;\n border-color: #B1B1B1;\n color: #6A6A6A;\n cursor: default;\n opacity: 0.5;\n }\n\n &:not([disabled]):hover {\n background-color: $blue-50;\n border-color: $blue-60;\n color: $white;\n }\n }\n}\n\n.firstrun-left-divider {\n position: relative;\n float: inline-start;\n clear: both;\n width: 435px;\n\n @media screen and (max-width: 825px) {\n width: 400px;\n }\n\n @media screen and (max-width: 790px) {\n margin: auto;\n float: none;\n width: 352px;\n text-align: center;\n }\n}\n\n.firstrun-content {\n line-height: 1.5;\n margin-bottom: 48px;\n max-width: 352px;\n background: url('#{$image-path}sync-devices.svg') bottom center no-repeat;\n padding-bottom: 210px;\n}\n\n.firstrun-link {\n color: $white;\n display: block;\n text-decoration: underline;\n\n &:hover,\n &:active,\n &:focus {\n color: $white;\n }\n}\n\n.firstrun-title {\n background: url('chrome://branding/content/about-logo.png') top left no-repeat;\n background-size: 90px 90px;\n margin: 40px 0 10px;\n padding-top: 110px;\n\n @media screen and (max-width: 790px) {\n background: url('chrome://branding/content/about-logo.png') top center no-repeat;\n background-size: 90px 90px;\n }\n}\n\n[dir='rtl'] {\n .firstrun-title {\n background-position: top right;\n }\n}\n\n.fxaccounts-container {\n position: absolute;\n bottom: 0;\n right: 0;\n top: 0;\n left: 0;\n color: $white;\n height: 515px;\n margin: auto;\n width: 819px;\n z-index: 10;\n transition: opacity 0.3s;\n opacity: 0;\n\n @media screen and (max-width: 825px) {\n width: 784px;\n }\n\n @media screen and (max-width: 790px) {\n width: auto;\n height: 100%;\n }\n}\n\n.firstrun-title,\n.firstrun-content,\n.firstrun-link {\n opacity: 0;\n transform: translateY(-5px);\n}\n", ".topic {\n color: var(--newtab-section-navigation-text-color);\n font-size: 12px;\n line-height: 1.6;\n margin-top: $topic-margin-top;\n\n @media (min-width: $break-point-large) {\n line-height: 16px;\n }\n\n ul {\n margin: 0;\n padding: 0;\n @media (min-width: $break-point-large) {\n display: inline;\n padding-inline-start: 12px;\n }\n }\n\n\n ul li {\n display: inline-block;\n\n &::after {\n content: '•';\n padding: 8px;\n }\n\n &:last-child::after {\n content: none;\n }\n }\n\n .topic-link {\n color: var(--newtab-link-secondary-color);\n font-weight: bold;\n }\n\n .topic-read-more {\n color: var(--newtab-link-secondary-color);\n font-weight: bold;\n\n @media (min-width: $break-point-large) {\n // This is floating to accomodate a very large number of topics and/or\n // very long topic names due to l10n.\n float: right;\n\n &:dir(rtl) {\n float: left;\n }\n }\n\n &::after {\n background: url('#{$image-path}topic-show-more-12.svg') no-repeat center center;\n content: '';\n -moz-context-properties: fill;\n display: inline-block;\n fill: var(--newtab-link-secondary-color);\n height: 16px;\n margin-inline-start: 5px;\n vertical-align: top;\n width: 12px;\n }\n\n &:dir(rtl)::after {\n transform: scaleX(-1);\n }\n }\n\n // This is a clearfix to for the topics-read-more link which is floating and causes\n // some jank when we set overflow:hidden for the animation.\n &::after {\n clear: both;\n content: '';\n display: table;\n }\n}\n", - ".search-wrapper {\n $search-height: 35px;\n $search-icon-size: 18px;\n $search-icon-padding: 8px;\n $search-icon-width: 2 * $search-icon-padding + $search-icon-size;\n $search-input-left-label-width: 35px;\n $search-button-width: 36px;\n $glyph-forward: url('chrome://browser/skin/forward.svg');\n\n cursor: default;\n display: flex;\n height: $search-height;\n margin-bottom: $section-spacing;\n position: relative;\n width: 100%;\n\n input {\n background: var(--newtab-textbox-background-color) var(--newtab-search-icon) $search-icon-padding center / $search-icon-size no-repeat;\n border: solid 1px var(--newtab-search-border-color);\n box-shadow: $shadow-secondary, 0 0 0 1px $black-15;\n font-size: 15px;\n -moz-context-properties: fill;\n fill: var(--newtab-search-icon-color);\n padding: 0;\n padding-inline-end: $search-button-width;\n padding-inline-start: $search-icon-width;\n width: 100%;\n\n &:dir(rtl) {\n background-position-x: right $search-icon-padding;\n }\n }\n\n &:hover input {\n box-shadow: $shadow-secondary, 0 0 0 1px $black-25;\n }\n\n &:active input,\n input:focus {\n border: $input-border-active;\n box-shadow: var(--newtab-textbox-focus-boxshadow);\n }\n\n .search-button {\n background: $glyph-forward no-repeat center center;\n background-size: 16px 16px;\n border: 0;\n border-radius: 0 $border-radius $border-radius 0;\n -moz-context-properties: fill;\n fill: var(--newtab-search-icon-color);\n height: 100%;\n offset-inline-end: 0;\n position: absolute;\n width: $search-button-width;\n\n &:focus,\n &:hover {\n background-color: $grey-90-10;\n cursor: pointer;\n }\n\n &:active {\n background-color: $grey-90-20;\n }\n\n &:dir(rtl) {\n transform: scaleX(-1);\n }\n }\n}\n\n@at-root {\n // Adjust the style of the contentSearchUI-generated table\n .contentSearchSuggestionTable {\n background-color: var(--newtab-search-dropdown-color);\n border: 0;\n box-shadow: $context-menu-shadow;\n transform: translateY($textbox-shadow-size);\n\n .contentSearchHeader {\n background-color: var(--newtab-search-dropdown-header-color);\n color: var(--newtab-text-secondary-color);\n }\n\n .contentSearchHeader,\n .contentSearchSettingsButton {\n border-color: var(--newtab-border-secondary-color);\n }\n\n .contentSearchSuggestionsList {\n border: 0;\n }\n\n .contentSearchOneOffsTable {\n background-color: var(--newtab-search-dropdown-header-color);\n border-top: solid 1px var(--newtab-border-secondary-color);\n }\n\n .contentSearchSearchWithHeaderSearchText {\n color: var(--newtab-text-primary-color);\n }\n\n .contentSearchSuggestionsContainer {\n background-color: var(--newtab-search-dropdown-color);\n }\n\n .contentSearchSuggestionRow {\n &.selected {\n background: var(--newtab-element-hover-color);\n color: var(--newtab-text-primary-color);\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n\n .historyIcon {\n fill: var(--newtab-icon-secondary-color);\n }\n }\n }\n\n .contentSearchOneOffsTable {\n .contentSearchSuggestionsContainer {\n background-color: var(--newtab-search-dropdown-header-color);\n }\n }\n\n .contentSearchOneOffItem {\n // Make the border slightly shorter by offsetting from the top and bottom\n $border-offset: 18%;\n\n background-image: none;\n border-image: linear-gradient(transparent $border-offset, var(--newtab-border-secondary-color) $border-offset, var(--newtab-border-secondary-color) 100% - $border-offset, transparent 100% - $border-offset) 1;\n border-inline-end: 1px solid;\n position: relative;\n\n &.selected {\n background: var(--newtab-element-hover-color);\n }\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n }\n\n .contentSearchSettingsButton {\n &:hover {\n background: var(--newtab-element-hover-color);\n color: var(--newtab-text-primary-color);\n }\n }\n }\n}\n", - ".context-menu {\n background: var(--newtab-contextmenu-background-color);\n border-radius: $context-menu-border-radius;\n box-shadow: $context-menu-shadow;\n display: block;\n font-size: $context-menu-font-size;\n margin-inline-start: 5px;\n offset-inline-start: 100%;\n position: absolute;\n top: ($context-menu-button-size / 4);\n z-index: 10000;\n\n > ul {\n list-style: none;\n margin: 0;\n padding: $context-menu-outer-padding 0;\n\n > li {\n margin: 0;\n width: 100%;\n\n &.separator {\n border-bottom: $border-secondary;\n margin: $context-menu-outer-padding 0;\n }\n\n > a {\n align-items: center;\n color: inherit;\n cursor: pointer;\n display: flex;\n line-height: 16px;\n outline: none;\n padding: $context-menu-item-padding;\n white-space: nowrap;\n\n &:-moz-any(:focus, :hover) {\n background: var(--newtab-element-hover-color);\n }\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n\n &.disabled {\n opacity: 0.4;\n pointer-events: none;\n }\n }\n }\n }\n}\n", + ".search-wrapper {\n $search-height: 35px;\n $search-icon-size: 18px;\n $search-icon-padding: 8px;\n $search-icon-width: 2 * $search-icon-padding + $search-icon-size;\n $search-input-left-label-width: 35px;\n $search-button-width: 36px;\n $glyph-forward: url('chrome://browser/skin/forward.svg');\n\n cursor: default;\n display: flex;\n height: $search-height;\n margin-bottom: $section-spacing;\n position: relative;\n width: 100%;\n\n input {\n background: var(--newtab-textbox-background-color) var(--newtab-search-icon) $search-icon-padding center / $search-icon-size no-repeat;\n border: solid 1px var(--newtab-search-border-color);\n box-shadow: $shadow-secondary, 0 0 0 1px $black-15;\n font-size: 15px;\n -moz-context-properties: fill;\n fill: var(--newtab-search-icon-color);\n padding: 0;\n padding-inline-end: $search-button-width;\n padding-inline-start: $search-icon-width;\n width: 100%;\n\n &:dir(rtl) {\n background-position-x: right $search-icon-padding;\n }\n }\n\n &:hover input {\n box-shadow: $shadow-secondary, 0 0 0 1px $black-25;\n }\n\n &:active input,\n input:focus {\n border: $input-border-active;\n box-shadow: var(--newtab-textbox-focus-boxshadow);\n }\n\n .search-button {\n background: $glyph-forward no-repeat center center;\n background-size: 16px 16px;\n border: 0;\n border-radius: 0 $border-radius $border-radius 0;\n -moz-context-properties: fill;\n fill: var(--newtab-search-icon-color);\n height: 100%;\n inset-inline-end: 0;\n position: absolute;\n width: $search-button-width;\n\n &:focus,\n &:hover {\n background-color: $grey-90-10;\n cursor: pointer;\n }\n\n &:active {\n background-color: $grey-90-20;\n }\n\n &:dir(rtl) {\n transform: scaleX(-1);\n }\n }\n}\n\n@at-root {\n // Adjust the style of the contentSearchUI-generated table\n .contentSearchSuggestionTable {\n background-color: var(--newtab-search-dropdown-color);\n border: 0;\n box-shadow: $context-menu-shadow;\n transform: translateY($textbox-shadow-size);\n\n .contentSearchHeader {\n background-color: var(--newtab-search-dropdown-header-color);\n color: var(--newtab-text-secondary-color);\n }\n\n .contentSearchHeader,\n .contentSearchSettingsButton {\n border-color: var(--newtab-border-secondary-color);\n }\n\n .contentSearchSuggestionsList {\n border: 0;\n }\n\n .contentSearchOneOffsTable {\n background-color: var(--newtab-search-dropdown-header-color);\n border-top: solid 1px var(--newtab-border-secondary-color);\n }\n\n .contentSearchSearchWithHeaderSearchText {\n color: var(--newtab-text-primary-color);\n }\n\n .contentSearchSuggestionsContainer {\n background-color: var(--newtab-search-dropdown-color);\n }\n\n .contentSearchSuggestionRow {\n &.selected {\n background: var(--newtab-element-hover-color);\n color: var(--newtab-text-primary-color);\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n\n .historyIcon {\n fill: var(--newtab-icon-secondary-color);\n }\n }\n }\n\n .contentSearchOneOffsTable {\n .contentSearchSuggestionsContainer {\n background-color: var(--newtab-search-dropdown-header-color);\n }\n }\n\n .contentSearchOneOffItem {\n // Make the border slightly shorter by offsetting from the top and bottom\n $border-offset: 18%;\n\n background-image: none;\n border-image: linear-gradient(transparent $border-offset, var(--newtab-border-secondary-color) $border-offset, var(--newtab-border-secondary-color) 100% - $border-offset, transparent 100% - $border-offset) 1;\n border-inline-end: 1px solid;\n position: relative;\n\n &.selected {\n background: var(--newtab-element-hover-color);\n }\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n }\n\n .contentSearchSettingsButton {\n &:hover {\n background: var(--newtab-element-hover-color);\n color: var(--newtab-text-primary-color);\n }\n }\n }\n}\n", + ".context-menu {\n background: var(--newtab-contextmenu-background-color);\n border-radius: $context-menu-border-radius;\n box-shadow: $context-menu-shadow;\n display: block;\n font-size: $context-menu-font-size;\n margin-inline-start: 5px;\n inset-inline-start: 100%;\n position: absolute;\n top: ($context-menu-button-size / 4);\n z-index: 10000;\n\n > ul {\n list-style: none;\n margin: 0;\n padding: $context-menu-outer-padding 0;\n\n > li {\n margin: 0;\n width: 100%;\n\n &.separator {\n border-bottom: $border-secondary;\n margin: $context-menu-outer-padding 0;\n }\n\n > a {\n align-items: center;\n color: inherit;\n cursor: pointer;\n display: flex;\n line-height: 16px;\n outline: none;\n padding: $context-menu-item-padding;\n white-space: nowrap;\n\n &:-moz-any(:focus, :hover) {\n background: var(--newtab-element-hover-color);\n }\n\n &:active {\n background: var(--newtab-element-active-color);\n }\n\n &.disabled {\n opacity: 0.4;\n pointer-events: none;\n }\n }\n }\n }\n}\n", ".confirmation-dialog {\n .modal {\n box-shadow: 0 2px 2px 0 $black-10;\n left: 50%;\n margin-left: -200px;\n position: fixed;\n top: 20%;\n width: 400px;\n }\n\n section {\n margin: 0;\n }\n\n .modal-message {\n display: flex;\n padding: 16px;\n padding-bottom: 0;\n\n p {\n margin: 0;\n margin-bottom: 16px;\n }\n }\n\n .actions {\n border: 0;\n display: flex;\n flex-wrap: nowrap;\n padding: 0 16px;\n\n button {\n margin-inline-end: 16px;\n padding-inline-end: 18px;\n padding-inline-start: 18px;\n white-space: normal;\n width: 50%;\n\n &.done {\n margin-inline-end: 0;\n margin-inline-start: 0;\n }\n }\n }\n\n .icon {\n margin-inline-end: 16px;\n }\n}\n\n.modal-overlay {\n background: var(--newtab-overlay-color);\n height: 100%;\n left: 0;\n position: fixed;\n top: 0;\n width: 100%;\n z-index: 11001;\n}\n\n.modal {\n background: var(--newtab-modal-color);\n border: $border-secondary;\n border-radius: 5px;\n font-size: 15px;\n z-index: 11002;\n}\n", - ".card-outer {\n @include context-menu-button;\n background: var(--newtab-card-background-color);\n border-radius: $border-radius;\n display: inline-block;\n height: $card-height;\n margin-inline-end: $base-gutter;\n position: relative;\n width: 100%;\n\n &.placeholder {\n background: transparent;\n\n .card {\n box-shadow: inset $inner-box-shadow;\n }\n\n .card-preview-image-outer,\n .card-context {\n display: none;\n }\n }\n\n .card {\n border-radius: $border-radius;\n box-shadow: var(--newtab-card-shadow);\n height: 100%;\n }\n\n > a {\n color: inherit;\n display: block;\n height: 100%;\n outline: none;\n position: absolute;\n width: 100%;\n\n &:-moz-any(.active, :focus) {\n .card {\n @include fade-in-card;\n }\n\n .card-title {\n color: var(--newtab-link-primary-color);\n }\n }\n }\n\n &:-moz-any(:hover, :focus, .active):not(.placeholder) {\n @include fade-in-card;\n @include context-menu-button-hover;\n outline: none;\n\n .card-title {\n color: var(--newtab-link-primary-color);\n }\n\n .alternate ~ .card-host-name {\n display: none;\n }\n\n .card-host-name.alternate {\n display: block;\n }\n }\n\n .card-preview-image-outer {\n background-color: $grey-30;\n border-radius: $border-radius $border-radius 0 0;\n height: $card-preview-image-height;\n overflow: hidden;\n position: relative;\n\n &::after {\n border-bottom: 1px solid var(--newtab-card-hairline-color);\n bottom: 0;\n content: '';\n position: absolute;\n width: 100%;\n }\n\n .card-preview-image {\n background-position: center;\n background-repeat: no-repeat;\n background-size: cover;\n height: 100%;\n opacity: 0;\n transition: opacity 1s $photon-easing;\n width: 100%;\n\n &.loaded {\n opacity: 1;\n }\n }\n }\n\n .card-details {\n padding: 15px 16px 12px;\n }\n\n .card-text {\n max-height: 4 * $card-text-line-height + $card-title-margin;\n overflow: hidden;\n\n &.no-host-name,\n &.no-context {\n max-height: 5 * $card-text-line-height + $card-title-margin;\n }\n\n &.no-host-name.no-context {\n max-height: 6 * $card-text-line-height + $card-title-margin;\n }\n\n &:not(.no-description) .card-title {\n max-height: 3 * $card-text-line-height;\n overflow: hidden;\n }\n }\n\n .card-host-name {\n color: var(--newtab-text-secondary-color);\n font-size: 10px;\n overflow: hidden;\n padding-bottom: 4px;\n text-overflow: ellipsis;\n text-transform: uppercase;\n white-space: nowrap;\n }\n\n .card-host-name.alternate { display: none; }\n\n .card-title {\n font-size: 14px;\n font-weight: 600;\n line-height: $card-text-line-height;\n margin: 0 0 $card-title-margin;\n word-wrap: break-word;\n }\n\n .card-description {\n font-size: 12px;\n line-height: $card-text-line-height;\n margin: 0;\n overflow: hidden;\n word-wrap: break-word;\n }\n\n .card-context {\n bottom: 0;\n color: var(--newtab-text-secondary-color);\n display: flex;\n font-size: 11px;\n offset-inline-start: 0;\n padding: 9px 16px 9px 14px;\n position: absolute;\n }\n\n .card-context-icon {\n fill: var(--newtab-text-secondary-color);\n height: 22px;\n margin-inline-end: 6px;\n }\n\n .card-context-label {\n flex-grow: 1;\n line-height: 22px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n\n.normal-cards {\n .card-outer {\n // Wide layout styles\n @media (min-width: $break-point-widest) {\n $line-height: 23px;\n height: $card-height-large;\n\n .card-preview-image-outer {\n height: $card-preview-image-height-large;\n }\n\n .card-details {\n padding: 13px 16px 12px;\n }\n\n .card-text {\n max-height: 6 * $line-height + $card-title-margin;\n }\n\n .card-host-name {\n font-size: 12px;\n padding-bottom: 5px;\n }\n\n .card-title {\n font-size: 17px;\n line-height: $line-height;\n margin-bottom: 0;\n }\n\n .card-text:not(.no-description) {\n .card-title {\n max-height: 3 * $line-height;\n }\n }\n\n .card-description {\n font-size: 15px;\n line-height: $line-height;\n }\n\n .card-context {\n bottom: 4px;\n font-size: 14px;\n }\n }\n }\n}\n\n.compact-cards {\n $card-detail-vertical-spacing: 12px;\n $card-title-font-size: 12px;\n\n .card-outer {\n height: $card-height-compact;\n\n .card-preview-image-outer {\n height: $card-preview-image-height-compact;\n }\n\n .card-details {\n padding: $card-detail-vertical-spacing 16px;\n }\n\n .card-host-name {\n line-height: 10px;\n }\n\n .card-text {\n .card-title,\n &:not(.no-description) .card-title {\n font-size: $card-title-font-size;\n line-height: $card-title-font-size + 1;\n max-height: $card-title-font-size + 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n }\n\n .card-description {\n display: none;\n }\n\n .card-context {\n $icon-size: 16px;\n $container-size: 32px;\n background-color: var(--newtab-card-background-color);\n border-radius: $container-size / 2;\n clip-path: inset(-1px -1px $container-size - ($card-height-compact - $card-preview-image-height-compact - 2 * $card-detail-vertical-spacing));\n height: $container-size;\n width: $container-size;\n padding: ($container-size - $icon-size) / 2;\n top: $card-preview-image-height-compact - $icon-size;\n offset-inline-end: 12px;\n offset-inline-start: auto;\n\n &::after {\n border: 1px solid var(--newtab-card-hairline-color);\n border-bottom: 0;\n border-radius: ($container-size / 2) + 1 ($container-size / 2) + 1 0 0;\n content: '';\n position: absolute;\n height: ($container-size + 2) / 2;\n width: $container-size + 2;\n top: -1px;\n left: -1px;\n }\n\n .card-context-icon {\n margin-inline-end: 0;\n height: $icon-size;\n width: $icon-size;\n\n &.icon-bookmark-added {\n fill: $bookmark-icon-fill;\n }\n\n &.icon-download {\n fill: $download-icon-fill;\n }\n\n &.icon-pocket {\n fill: $pocket-icon-fill;\n }\n }\n\n .card-context-label {\n display: none;\n }\n }\n }\n\n @media not all and (min-width: $break-point-widest) {\n .hide-for-narrow {\n display: none;\n }\n }\n}\n", + ".card-outer {\n @include context-menu-button;\n background: var(--newtab-card-background-color);\n border-radius: $border-radius;\n display: inline-block;\n height: $card-height;\n margin-inline-end: $base-gutter;\n position: relative;\n width: 100%;\n\n &.placeholder {\n background: transparent;\n\n .card {\n box-shadow: inset $inner-box-shadow;\n }\n\n .card-preview-image-outer,\n .card-context {\n display: none;\n }\n }\n\n .card {\n border-radius: $border-radius;\n box-shadow: var(--newtab-card-shadow);\n height: 100%;\n }\n\n > a {\n color: inherit;\n display: block;\n height: 100%;\n outline: none;\n position: absolute;\n width: 100%;\n\n &:-moz-any(.active, :focus) {\n .card {\n @include fade-in-card;\n }\n\n .card-title {\n color: var(--newtab-link-primary-color);\n }\n }\n }\n\n &:-moz-any(:hover, :focus, .active):not(.placeholder) {\n @include fade-in-card;\n @include context-menu-button-hover;\n outline: none;\n\n .card-title {\n color: var(--newtab-link-primary-color);\n }\n\n .alternate ~ .card-host-name {\n display: none;\n }\n\n .card-host-name.alternate {\n display: block;\n }\n }\n\n .card-preview-image-outer {\n background-color: $grey-30;\n border-radius: $border-radius $border-radius 0 0;\n height: $card-preview-image-height;\n overflow: hidden;\n position: relative;\n\n &::after {\n border-bottom: 1px solid var(--newtab-card-hairline-color);\n bottom: 0;\n content: '';\n position: absolute;\n width: 100%;\n }\n\n .card-preview-image {\n background-position: center;\n background-repeat: no-repeat;\n background-size: cover;\n height: 100%;\n opacity: 0;\n transition: opacity 1s $photon-easing;\n width: 100%;\n\n &.loaded {\n opacity: 1;\n }\n }\n }\n\n .card-details {\n padding: 15px 16px 12px;\n }\n\n .card-text {\n max-height: 4 * $card-text-line-height + $card-title-margin;\n overflow: hidden;\n\n &.no-host-name,\n &.no-context {\n max-height: 5 * $card-text-line-height + $card-title-margin;\n }\n\n &.no-host-name.no-context {\n max-height: 6 * $card-text-line-height + $card-title-margin;\n }\n\n &:not(.no-description) .card-title {\n max-height: 3 * $card-text-line-height;\n overflow: hidden;\n }\n }\n\n .card-host-name {\n color: var(--newtab-text-secondary-color);\n font-size: 10px;\n overflow: hidden;\n padding-bottom: 4px;\n text-overflow: ellipsis;\n text-transform: uppercase;\n white-space: nowrap;\n }\n\n .card-host-name.alternate { display: none; }\n\n .card-title {\n font-size: 14px;\n font-weight: 600;\n line-height: $card-text-line-height;\n margin: 0 0 $card-title-margin;\n word-wrap: break-word;\n }\n\n .card-description {\n font-size: 12px;\n line-height: $card-text-line-height;\n margin: 0;\n overflow: hidden;\n word-wrap: break-word;\n }\n\n .card-context {\n bottom: 0;\n color: var(--newtab-text-secondary-color);\n display: flex;\n font-size: 11px;\n inset-inline-start: 0;\n padding: 9px 16px 9px 14px;\n position: absolute;\n }\n\n .card-context-icon {\n fill: var(--newtab-text-secondary-color);\n height: 22px;\n margin-inline-end: 6px;\n }\n\n .card-context-label {\n flex-grow: 1;\n line-height: 22px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n\n.normal-cards {\n .card-outer {\n // Wide layout styles\n @media (min-width: $break-point-widest) {\n $line-height: 23px;\n height: $card-height-large;\n\n .card-preview-image-outer {\n height: $card-preview-image-height-large;\n }\n\n .card-details {\n padding: 13px 16px 12px;\n }\n\n .card-text {\n max-height: 6 * $line-height + $card-title-margin;\n }\n\n .card-host-name {\n font-size: 12px;\n padding-bottom: 5px;\n }\n\n .card-title {\n font-size: 17px;\n line-height: $line-height;\n margin-bottom: 0;\n }\n\n .card-text:not(.no-description) {\n .card-title {\n max-height: 3 * $line-height;\n }\n }\n\n .card-description {\n font-size: 15px;\n line-height: $line-height;\n }\n\n .card-context {\n bottom: 4px;\n font-size: 14px;\n }\n }\n }\n}\n\n.compact-cards {\n $card-detail-vertical-spacing: 12px;\n $card-title-font-size: 12px;\n\n .card-outer {\n height: $card-height-compact;\n\n .card-preview-image-outer {\n height: $card-preview-image-height-compact;\n }\n\n .card-details {\n padding: $card-detail-vertical-spacing 16px;\n }\n\n .card-host-name {\n line-height: 10px;\n }\n\n .card-text {\n .card-title,\n &:not(.no-description) .card-title {\n font-size: $card-title-font-size;\n line-height: $card-title-font-size + 1;\n max-height: $card-title-font-size + 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n }\n\n .card-description {\n display: none;\n }\n\n .card-context {\n $icon-size: 16px;\n $container-size: 32px;\n background-color: var(--newtab-card-background-color);\n border-radius: $container-size / 2;\n clip-path: inset(-1px -1px $container-size - ($card-height-compact - $card-preview-image-height-compact - 2 * $card-detail-vertical-spacing));\n height: $container-size;\n width: $container-size;\n padding: ($container-size - $icon-size) / 2;\n top: $card-preview-image-height-compact - $icon-size;\n inset-inline-end: 12px;\n inset-inline-start: auto;\n\n &::after {\n border: 1px solid var(--newtab-card-hairline-color);\n border-bottom: 0;\n border-radius: ($container-size / 2) + 1 ($container-size / 2) + 1 0 0;\n content: '';\n position: absolute;\n height: ($container-size + 2) / 2;\n width: $container-size + 2;\n top: -1px;\n left: -1px;\n }\n\n .card-context-icon {\n margin-inline-end: 0;\n height: $icon-size;\n width: $icon-size;\n\n &.icon-bookmark-added {\n fill: $bookmark-icon-fill;\n }\n\n &.icon-download {\n fill: $download-icon-fill;\n }\n\n &.icon-pocket {\n fill: $pocket-icon-fill;\n }\n }\n\n .card-context-label {\n display: none;\n }\n }\n }\n\n @media not all and (min-width: $break-point-widest) {\n .hide-for-narrow {\n display: none;\n }\n }\n}\n", ".manual-migration-container {\n color: var(--newtab-text-conditional-color);\n font-size: 13px;\n line-height: 15px;\n margin-bottom: $section-spacing;\n text-align: center;\n\n @media (min-width: $break-point-medium) {\n display: flex;\n justify-content: space-between;\n text-align: left;\n }\n\n p {\n margin: 0;\n @media (min-width: $break-point-medium) {\n align-self: center;\n display: flex;\n justify-content: space-between;\n }\n }\n\n .icon {\n display: none;\n @media (min-width: $break-point-medium) {\n align-self: center;\n display: block;\n fill: var(--newtab-icon-secondary-color);\n margin-inline-end: 6px;\n }\n }\n}\n\n.manual-migration-actions {\n border: 0;\n display: block;\n flex-wrap: nowrap;\n\n @media (min-width: $break-point-medium) {\n display: flex;\n justify-content: space-between;\n padding: 0;\n }\n\n button {\n align-self: center;\n height: 26px;\n margin: 0;\n margin-inline-start: 20px;\n padding: 0 12px;\n }\n}\n", - ".collapsible-section {\n padding: $section-vertical-padding $section-horizontal-padding;\n transition-delay: 100ms;\n transition-duration: 100ms;\n transition-property: background-color;\n\n .section-title {\n font-size: $section-title-font-size;\n font-weight: bold;\n margin: 0;\n text-transform: uppercase;\n\n span {\n color: var(--newtab-section-header-text-color);\n display: inline-block;\n fill: var(--newtab-section-header-text-color);\n vertical-align: middle;\n }\n\n .click-target {\n cursor: pointer;\n vertical-align: top;\n white-space: nowrap;\n }\n\n .collapsible-arrow {\n margin-inline-start: 8px;\n margin-top: -1px;\n }\n }\n\n .section-top-bar {\n height: 19px;\n margin-bottom: 13px;\n position: relative;\n\n .context-menu-button {\n background: url('chrome://browser/skin/page-action.svg') no-repeat right center;\n border: 0;\n cursor: pointer;\n fill: var(--newtab-section-header-text-color);\n height: 100%;\n offset-inline-end: 0;\n opacity: 0;\n position: absolute;\n top: 0;\n transition-duration: 200ms;\n transition-property: opacity;\n width: $context-menu-button-size;\n\n &:-moz-any(:active, :focus, :hover) {\n fill: $grey-90;\n opacity: 1;\n }\n }\n\n .context-menu {\n top: 16px;\n }\n\n @media (max-width: $break-point-widest + $card-width * 1.5) {\n @include context-menu-open-left;\n }\n }\n\n &:hover,\n &.active {\n .section-top-bar {\n .context-menu-button {\n opacity: 1;\n }\n }\n }\n\n &.active {\n background: var(--newtab-element-hover-color);\n border-radius: 4px;\n\n .section-top-bar {\n .context-menu-button {\n fill: var(--newtab-section-active-contextmenu-color);\n }\n }\n }\n\n .section-disclaimer {\n $max-button-width: 130px;\n $min-button-height: 26px;\n\n color: var(--newtab-text-conditional-color);\n font-size: 13px;\n margin-bottom: 16px;\n position: relative;\n\n .section-disclaimer-text {\n display: inline-block;\n min-height: $min-button-height;\n width: calc(100% - #{$max-button-width});\n\n @media (max-width: $break-point-medium) {\n width: $card-width;\n }\n }\n\n a {\n color: var(--newtab-link-primary-color);\n font-weight: bold;\n padding-left: 3px;\n }\n\n button {\n background: var(--newtab-button-secondary-color);\n border: 1px solid $grey-40;\n border-radius: 4px;\n cursor: pointer;\n margin-top: 2px;\n max-width: $max-button-width;\n min-height: $min-button-height;\n offset-inline-end: 0;\n\n &:hover:not(.dismiss) {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n }\n\n @media (min-width: $break-point-small) {\n position: absolute;\n }\n }\n }\n\n .section-body-fallback {\n height: $card-height;\n }\n\n .section-body {\n // This is so the top sites favicon and card dropshadows don't get clipped during animation:\n $horizontal-padding: 7px;\n margin: 0 (-$horizontal-padding);\n padding: 0 $horizontal-padding;\n\n &.animating {\n overflow: hidden;\n pointer-events: none;\n }\n }\n\n &.animation-enabled {\n .section-title {\n .collapsible-arrow {\n transition: transform 0.5s $photon-easing;\n }\n }\n\n .section-body {\n transition: max-height 0.5s $photon-easing;\n }\n }\n\n &.collapsed {\n .section-body {\n max-height: 0;\n overflow: hidden;\n }\n }\n}\n", + ".collapsible-section {\n padding: $section-vertical-padding $section-horizontal-padding;\n transition-delay: 100ms;\n transition-duration: 100ms;\n transition-property: background-color;\n\n .section-title {\n font-size: $section-title-font-size;\n font-weight: bold;\n margin: 0;\n text-transform: uppercase;\n\n span {\n color: var(--newtab-section-header-text-color);\n display: inline-block;\n fill: var(--newtab-section-header-text-color);\n vertical-align: middle;\n }\n\n .click-target {\n cursor: pointer;\n vertical-align: top;\n white-space: nowrap;\n }\n\n .collapsible-arrow {\n margin-inline-start: 8px;\n margin-top: -1px;\n }\n }\n\n .section-top-bar {\n height: 19px;\n margin-bottom: 13px;\n position: relative;\n\n .context-menu-button {\n background: url('chrome://browser/skin/page-action.svg') no-repeat right center;\n border: 0;\n cursor: pointer;\n fill: var(--newtab-section-header-text-color);\n height: 100%;\n inset-inline-end: 0;\n opacity: 0;\n position: absolute;\n top: 0;\n transition-duration: 200ms;\n transition-property: opacity;\n width: $context-menu-button-size;\n\n &:-moz-any(:active, :focus, :hover) {\n fill: $grey-90;\n opacity: 1;\n }\n }\n\n .context-menu {\n top: 16px;\n }\n\n @media (max-width: $break-point-widest + $card-width * 1.5) {\n @include context-menu-open-left;\n }\n }\n\n &:hover,\n &.active {\n .section-top-bar {\n .context-menu-button {\n opacity: 1;\n }\n }\n }\n\n &.active {\n background: var(--newtab-element-hover-color);\n border-radius: 4px;\n\n .section-top-bar {\n .context-menu-button {\n fill: var(--newtab-section-active-contextmenu-color);\n }\n }\n }\n\n .section-disclaimer {\n $max-button-width: 130px;\n $min-button-height: 26px;\n\n color: var(--newtab-text-conditional-color);\n font-size: 13px;\n margin-bottom: 16px;\n position: relative;\n\n .section-disclaimer-text {\n display: inline-block;\n min-height: $min-button-height;\n width: calc(100% - #{$max-button-width});\n\n @media (max-width: $break-point-medium) {\n width: $card-width;\n }\n }\n\n a {\n color: var(--newtab-link-primary-color);\n font-weight: bold;\n padding-left: 3px;\n }\n\n button {\n background: var(--newtab-button-secondary-color);\n border: 1px solid $grey-40;\n border-radius: 4px;\n cursor: pointer;\n margin-top: 2px;\n max-width: $max-button-width;\n min-height: $min-button-height;\n inset-inline-end: 0;\n\n &:hover:not(.dismiss) {\n box-shadow: $shadow-primary;\n transition: box-shadow 150ms;\n }\n\n @media (min-width: $break-point-small) {\n position: absolute;\n }\n }\n }\n\n .section-body-fallback {\n height: $card-height;\n }\n\n .section-body {\n // This is so the top sites favicon and card dropshadows don't get clipped during animation:\n $horizontal-padding: 7px;\n margin: 0 (-$horizontal-padding);\n padding: 0 $horizontal-padding;\n\n &.animating {\n overflow: hidden;\n pointer-events: none;\n }\n }\n\n &.animation-enabled {\n .section-title {\n .collapsible-arrow {\n transition: transform 0.5s $photon-easing;\n }\n }\n\n .section-body {\n transition: max-height 0.5s $photon-easing;\n }\n }\n\n &.collapsed {\n .section-body {\n max-height: 0;\n overflow: hidden;\n }\n }\n}\n", "\n.asrouter-admin {\n $border-color: var(--newtab-border-secondary-color);\n $monospace: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Mono', 'Droid Sans Mono', 'Source Code Pro', monospace;\n max-width: 996px;\n margin: 0 auto;\n font-size: 14px;\n // Reset .outer-wrapper styles\n display: inherit;\n padding: 0 0 92px;\n\n h1 {\n font-weight: 200;\n font-size: 32px;\n }\n\n table {\n border-collapse: collapse;\n width: 100%;\n }\n\n .message-item {\n &:first-child td {\n border-top: 1px solid $border-color;\n }\n\n td {\n vertical-align: top;\n border-bottom: 1px solid $border-color;\n padding: 8px;\n\n &:first-child {\n border-left: 1px solid $border-color;\n }\n\n &:last-child {\n border-right: 1px solid $border-color;\n }\n }\n\n &.current {\n .message-id span {\n background: $yellow-50;\n padding: 2px 5px;\n\n .dark-theme & {\n color: $black;\n }\n }\n }\n\n &.blocked {\n .message-id,\n .message-summary {\n opacity: 0.5;\n }\n\n .message-id {\n opacity: 0.5;\n }\n }\n\n .message-id {\n font-family: $monospace;\n font-size: 12px;\n }\n }\n\n pre {\n background: var(--newtab-textbox-background-color);\n margin: 0;\n padding: 8px;\n font-size: 12px;\n max-width: 750px;\n overflow: auto;\n font-family: $monospace;\n }\n}\n", ".ASRouterButton {\n white-space: nowrap;\n border-radius: 4px;\n border: 1px solid var(--newtab-border-secondary-color);\n background-color: var(--newtab-button-secondary-color);\n font-family: inherit;\n padding: 8px 15px;\n margin-inline-start: 12px;\n color: inherit;\n .tall & {\n margin-inline-start: 20px;\n }\n}\n", - ".SnippetBaseContainer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: var(--newtab-snippets-background-color);\n color: var(--newtab-text-primary-color);\n font-size: 12px;\n line-height: 16px;\n border-top: 1px solid var(--newtab-snippets-hairline-color);\n box-shadow: $shadow-secondary;\n display: flex;\n align-items: center;\n\n .innerWrapper {\n margin: 0 auto;\n display: flex;\n align-items: center;\n padding: 12px $section-horizontal-padding;\n\n // This is to account for the block button on smaller screens\n padding-inline-end: 36px;\n @media (min-width: $break-point-large) {\n padding-inline-end: $section-horizontal-padding;\n }\n\n max-width: $wrapper-max-width-large;\n @media (min-width: $break-point-widest) {\n max-width: $wrapper-max-width-widest;\n }\n }\n\n .blockButton {\n display: none;\n background: none;\n border: 0;\n position: absolute;\n top: 50%;\n offset-inline-end: 12px;\n height: 16px;\n width: 16px;\n background-image: url('resource://activity-stream/data/content/assets/glyph-dismiss-16.svg');\n -moz-context-properties: fill;\n fill: var(--newtab-icon-primary-color);\n opacity: 0.5;\n margin-top: -8px;\n padding: 0;\n cursor: pointer;\n\n @media (min-width: 766px) {\n offset-inline-end: 24px;\n }\n }\n\n &:hover .blockButton {\n display: block;\n }\n}\n", + ".SnippetBaseContainer {\n position: fixed;\n bottom: 0;\n left: 0;\n right: 0;\n background-color: var(--newtab-snippets-background-color);\n color: var(--newtab-text-primary-color);\n font-size: 12px;\n line-height: 16px;\n border-top: 1px solid var(--newtab-snippets-hairline-color);\n box-shadow: $shadow-secondary;\n display: flex;\n align-items: center;\n\n .innerWrapper {\n margin: 0 auto;\n display: flex;\n align-items: center;\n padding: 12px $section-horizontal-padding;\n\n // This is to account for the block button on smaller screens\n padding-inline-end: 36px;\n @media (min-width: $break-point-large) {\n padding-inline-end: $section-horizontal-padding;\n }\n\n max-width: $wrapper-max-width-large;\n @media (min-width: $break-point-widest) {\n max-width: $wrapper-max-width-widest;\n }\n }\n\n .blockButton {\n display: none;\n background: none;\n border: 0;\n position: absolute;\n top: 50%;\n inset-inline-end: 12px;\n height: 16px;\n width: 16px;\n background-image: url('resource://activity-stream/data/content/assets/glyph-dismiss-16.svg');\n -moz-context-properties: fill;\n fill: var(--newtab-icon-primary-color);\n opacity: 0.5;\n margin-top: -8px;\n padding: 0;\n cursor: pointer;\n\n @media (min-width: 766px) {\n inset-inline-end: 24px;\n }\n }\n\n &:hover .blockButton {\n display: block;\n }\n}\n", ".activity-stream {\n &.modal-open {\n overflow: hidden;\n }\n}\n.modalOverlayOuter {\n background: $white;\n opacity: 0.93;\n height: 100%;\n position: fixed;\n top: 0;\n width: 100%;\n display: none;\n z-index: 1100;\n\n &.active {\n display: block;\n }\n}\n\n.modalOverlayInner {\n width: 960px;\n height: 510px;\n position: fixed;\n top: calc(50% - 255px); // halfway down minus half the height of the modal\n left: calc(50% - 480px); // halfway across minus half the width of the modal\n background: $white;\n box-shadow: 0 1px 15px 0 $black-30;\n border-radius: 4px;\n display: none;\n z-index: 1101;\n\n\n // modal takes over entire screen\n @media(max-width: 960px) {\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n box-shadow: none;\n border-radius: 0;\n }\n\n // if modal is short enough, add a vertical scroll bar\n @media(max-width: 850px) and (max-height: 730px) {\n overflow-y: scroll;\n }\n\n // if modal is narrow enough, add a vertical scroll bar\n @media(max-width: 650px) and (max-height: 600px) {\n overflow-y: scroll;\n }\n\n &.active {\n display: block;\n }\n\n h2 {\n color: $grey-60;\n text-align: center;\n font-weight: 200;\n margin-top: 30px;\n font-size: 28px;\n line-height: 37px;\n letter-spacing: -0.13px;\n\n @media(max-width: 960px) {\n margin-top: 100px;\n }\n\n @media(max-width: 850px) {\n margin-top: 30px;\n }\n }\n\n .footer {\n border-top: 1px solid $grey-30;\n height: 70px;\n width: 100%;\n position: absolute;\n bottom: 0;\n text-align: center;\n background-color: $white;\n\n // if modal is short enough, footer becomes sticky\n @media(max-width: 850px) and (max-height: 730px) {\n position: sticky;\n }\n\n // if modal is narrow enough, footer becomes sticky\n @media(max-width: 650px) and (max-height: 600px) {\n position: sticky;\n }\n\n .modalButton {\n margin-top: 20px;\n width: 150px;\n height: 30px;\n padding: 4px 0 6px 0;\n font-size: 15px;\n }\n }\n}\n", ".SimpleSnippet {\n &.tall {\n padding: 27px 0;\n }\n\n .title {\n display: inline;\n font-size: inherit;\n margin: 0;\n }\n\n .titleIcon {\n background-repeat: no-repeat;\n background-size: 14px;\n height: 16px;\n width: 16px;\n margin-top: 2px;\n margin-inline-end: 2px;\n display: inline-block;\n vertical-align: top;\n }\n\n .body {\n display: inline;\n margin: 0;\n }\n\n .icon {\n height: 42px;\n width: 42px;\n margin-inline-end: 12px;\n flex-shrink: 0;\n }\n &.tall .icon {\n margin-inline-end: 20px;\n }\n\n .ASRouterAnchor {\n color: inherit;\n text-decoration: underline;\n }\n}\n", ".onboardingMessageContainer {\n display: grid;\n grid-column-gap: 21px;\n grid-template-columns: auto auto auto;\n padding-left: 30px;\n padding-right: 30px;\n\n // at 850px, the cards go from vertical layout to horizontal layout\n @media(max-width: 850px) {\n grid-template-columns: none;\n grid-template-rows: auto auto auto;\n padding-left: 110px;\n padding-right: 110px;\n }\n}\n\n.onboardingMessage {\n height: 340px;\n text-align: center;\n padding: 13px;\n font-weight: 200;\n\n // at 850px, img floats left, content floats right next to it\n @media(max-width: 850px) {\n height: 170px;\n text-align: left;\n padding: 10px;\n border-bottom: 1px solid #D8D8D8;\n display: flex;\n margin-bottom: 11px;\n\n &:last-child {\n border: none;\n }\n\n .onboardingContent {\n padding-left: 10px;\n height: 100%;\n\n > span > h3 {\n margin-top: 0;\n margin-bottom: 4px;\n font-weight: 400;\n }\n\n > span > p {\n margin-top: 0;\n line-height: 22px;\n font-size: 15px;\n }\n }\n }\n\n @media(max-width: 650px) {\n height: 250px;\n }\n\n .onboardingMessageImage {\n height: 100px;\n width: 120px;\n background-size: 120px;\n background-position: center center;\n background-repeat: no-repeat;\n display: inline-block;\n vertical-align: middle;\n\n\n @media(max-width: 850px) {\n height: 75px;\n min-width: 80px;\n background-size: 80px;\n }\n\n &.addons {\n background-image: url(\"resource://activity-stream/data/content/assets/illustration-addons@2x.png\");\n }\n\n &.privatebrowsing {\n background-image: url(\"resource://activity-stream/data/content/assets/illustration-privatebrowsing@2x.png\");\n }\n\n &.screenshots {\n background-image: url(\"resource://activity-stream/data/content/assets/illustration-screenshots@2x.png\");\n }\n\n &.gift {\n background-image: url(\"resource://activity-stream/data/content/assets/illustration-gift@2x.png\");\n }\n }\n\n .onboardingContent {\n height: 175px;\n\n > span > h3 {\n color: $grey-90;\n margin-bottom: 8px;\n font-weight: 400;\n }\n\n > span > p {\n color: $grey-60;\n margin-top: 0;\n height: 130px;\n margin-bottom: 12px;\n font-size: 15px;\n line-height: 22px;\n\n @media(max-width: 650px) {\n margin-bottom: 0px;\n }\n }\n }\n\n .onboardingButton {\n background-color: $grey-90-10;\n border: none;\n width: 150px;\n height: 30px;\n margin-bottom: 23px;\n padding: 4px 0 6px 0;\n font-size: 15px;\n\n // at 850px, the button shimmies down and to the right\n @media(max-width: 850px) {\n float: right;\n margin-top: -60px;\n margin-right: -10px;\n }\n\n @media(max-width: 650px) {\n float: none;\n margin-top: 30px;\n }\n }\n\n\n &::before {\n content: '';\n height: 220px;\n width: 1px;\n position: absolute;\n background-color: #D8D8D8;\n margin-top: 40px;\n margin-left: 215px;\n\n // at 850px, the line goes from vertical to horizontal\n @media(max-width: 850px) {\n content: none;\n }\n }\n\n &:last-child::before {\n content: none;\n }\n}\n" diff --git a/browser/extensions/onboarding/content/onboarding.css b/browser/extensions/onboarding/content/onboarding.css index f18e4b65e43d..8f2431477634 100644 --- a/browser/extensions/onboarding/content/onboarding.css +++ b/browser/extensions/onboarding/content/onboarding.css @@ -28,7 +28,7 @@ position: fixed; cursor: pointer; top: 4px; - offset-inline-start: 12px; + inset-inline-start: 12px; border: none; /* Set to none so no grey contrast background in the high-contrast mode */ background: none; @@ -119,7 +119,7 @@ .onboarding-close-btn { position: absolute; top: 15px; - offset-inline-end: 15px; + inset-inline-end: 15px; cursor: pointer; width: 16px; height: 16px; @@ -228,7 +228,7 @@ #onboarding-tour-list .onboarding-tour-item.onboarding-complete::before { content: url("img/icons_tour-complete.svg"); position: relative; - offset-inline-start: 3px; + inset-inline-start: 3px; top: -10px; float: inline-start; } @@ -243,7 +243,7 @@ width: 48px; height: 48px; position: absolute; - offset-inline-start: 0px; + inset-inline-start: 0px; top: 0px; background-color: #3E3D40; mask-repeat: no-repeat; @@ -487,8 +487,8 @@ a#onboarding-tour-screenshots-button:visited { #onboarding-notification-close-btn { position: absolute; - offset-block-start: 50%; - offset-inline-end: 24px; + inset-block-start: 50%; + inset-inline-end: 24px; transform: translateY(-50%); } diff --git a/browser/themes/shared/incontentprefs/preferences.inc.css b/browser/themes/shared/incontentprefs/preferences.inc.css index 97fa47288aa6..32f1e1acc0e9 100644 --- a/browser/themes/shared/incontentprefs/preferences.inc.css +++ b/browser/themes/shared/incontentprefs/preferences.inc.css @@ -811,7 +811,7 @@ button > hbox > label { border: 7px solid transparent; border-top-color: #d7b600; top: 100%; - offset-inline-start: calc(50% - 7px); + inset-inline-start: calc(50% - 7px); } .search-tooltip::after { @@ -820,7 +820,7 @@ button > hbox > label { border: 6px solid transparent; border-top-color: #ffe900; top: 100%; - offset-inline-start: calc(50% - 6px); + inset-inline-start: calc(50% - 6px); } .search-tooltip-parent { diff --git a/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css b/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css index 940355ae9c81..4ebc74d2bd17 100644 --- a/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css +++ b/browser/themes/shared/privatebrowsing/aboutPrivateBrowsing.css @@ -149,7 +149,7 @@ a.button { } .toggle:checked + .toggle-btn::after { - offset-inline-start: 21px; + inset-inline-start: 21px; } .toggle:-moz-focusring + .toggle-btn { diff --git a/devtools/client/debugger/new/dist/debugger.css b/devtools/client/debugger/new/dist/debugger.css index 00d038d12f10..981102efffbb 100644 --- a/devtools/client/debugger/new/dist/debugger.css +++ b/devtools/client/debugger/new/dist/debugger.css @@ -3118,8 +3118,8 @@ html[dir="rtl"] .breakpoints-list .breakpoint .breakpoint-line { } .breakpoint .close-btn { - offset-inline-end: 15px; - offset-inline-start: auto; + inset-inline-end: 15px; + inset-inline-start: auto; position: absolute; top: 8px; display: none; @@ -3268,7 +3268,7 @@ html[dir="rtl"] .breakpoints-list .breakpoint .breakpoint-line { .expression-container__close-btn { position: absolute; - offset-inline-end: 0px; + inset-inline-end: 0px; top: 1px; } @@ -3952,8 +3952,8 @@ html[dir="rtl"] .object-node { .welcomebox .command-bar-button { position: absolute; top: auto; - offset-inline-end: 0; - offset-inline-start: auto; + inset-inline-end: 0; + inset-inline-start: auto; bottom: 0; } diff --git a/devtools/client/themes/common.css b/devtools/client/themes/common.css index bc548e8636e4..803136f7c212 100644 --- a/devtools/client/themes/common.css +++ b/devtools/client/themes/common.css @@ -606,7 +606,7 @@ checkbox:-moz-focusring { .devtools-searchinput-clear { position: absolute; top: 3.5px; - offset-inline-end: 7px; + inset-inline-end: 7px; padding: 0; border: 0; width: 16px; @@ -756,7 +756,7 @@ checkbox:-moz-focusring { position: absolute; top: 0; - offset-inline-end: 0; + inset-inline-end: 0; width: 15px; height: 100%; diff --git a/devtools/client/themes/fonts.css b/devtools/client/themes/fonts.css index ef45b1741d63..630b15560e68 100644 --- a/devtools/client/themes/fonts.css +++ b/devtools/client/themes/fonts.css @@ -102,7 +102,7 @@ color: var(--theme-toolbar-color); grid-column: span 2; position: relative; - offset-inline-start: -4px; + inset-inline-start: -4px; } .font-css-code-expander::before { diff --git a/devtools/client/themes/toolbox.css b/devtools/client/themes/toolbox.css index 8c109de6aca9..c78c75d6a431 100644 --- a/devtools/client/themes/toolbox.css +++ b/devtools/client/themes/toolbox.css @@ -181,7 +181,7 @@ #tools-chevron-menu-button::before { top: 0; - offset-inline-end: 0; + inset-inline-end: 0; background-image: var(--command-chevron-image); background-position: center; } diff --git a/devtools/docs/contributing/css.md b/devtools/docs/contributing/css.md index 5103bb13bfd1..5971b09fbf08 100644 --- a/devtools/docs/contributing/css.md +++ b/devtools/docs/contributing/css.md @@ -92,7 +92,7 @@ It's recommended to use SVG since it keeps the CSS clean when supporting multipl ### Text Direction * For margins, padding and borders, use `inline-start`/`inline-end` rather than `left`/`right`. * Example: Use `margin-inline-start: 3px;` not `margin-left: 3px`. -* For RTL-aware positioning (left/right), use `offset-inline-start/end`. +* For RTL-aware positioning (left/right), use `inset-inline-start/end`. * When there is no special RTL-aware property (eg. `float: left|right`) available, use the pseudo `:-moz-locale-dir(ltr|rtl)` (for XUL files) or `:dir(ltr|rtl)` (for HTML files). * Remember that while a tab content's scrollbar still shows on the right in RTL, an overflow scrollbar will show on the left. * Write `padding: 0 3px 4px;` instead of `padding: 0 3px 4px 3px;`. This makes it more obvious that the padding is symmetrical (so RTL won't be an issue). diff --git a/devtools/server/actors/animation-type-longhand.js b/devtools/server/actors/animation-type-longhand.js index 1811e85e7892..85ba687c9854 100644 --- a/devtools/server/actors/animation-type-longhand.js +++ b/devtools/server/actors/animation-type-longhand.js @@ -195,6 +195,10 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [ "display", "font-optical-sizing", "inline-size", + "inset-block-end", + "inset-block-start", + "inset-inline-end", + "inset-inline-start", "margin-block-end", "margin-block-start", "margin-inline-end", @@ -205,10 +209,6 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [ "min-block-size", "-moz-min-font-size-ratio", "min-inline-size", - "offset-block-end", - "offset-block-start", - "offset-inline-end", - "offset-inline-start", "padding-block-end", "padding-block-start", "padding-inline-end", diff --git a/devtools/server/actors/highlighters.css b/devtools/server/actors/highlighters.css index e64b83cf626d..b11b79fb381e 100644 --- a/devtools/server/actors/highlighters.css +++ b/devtools/server/actors/highlighters.css @@ -546,8 +546,8 @@ width: 16px; height: 16px; position: absolute; - offset-inline-start: 3px; - offset-block-start: 3px; + inset-inline-start: 3px; + inset-block-start: 3px; box-shadow: 0px 0px 0px black; border: solid 1px #fff; } From 688e2c1d75386d91ccfc044dfbbcba4302a245b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Wed, 27 Jun 2018 11:22:21 +0200 Subject: [PATCH 29/43] Bug 1464782: Put offset-* aliases behind a pref. r=xidorn MozReview-Commit-ID: Hl6Muim3wVH --- layout/style/test/property_database.js | 75 ++++++++++--------- modules/libpref/init/StaticPrefList.h | 7 ++ .../properties/longhands/position.mako.rs | 2 +- 3 files changed, 47 insertions(+), 37 deletions(-) diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 91e3e57c9d2e..555d1a1663de 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -6037,42 +6037,6 @@ var gCSSProperties = { ], invalid_values: [] }, - "offset-block-start": { - domProp: "offsetBlockStart", - inherited: false, - type: CSS_TYPE_SHORTHAND_AND_LONGHAND, - logical: true, - get_computed: logical_box_prop_get_computed, - alias_for: "inset-block-start", - subproperties: [ "inset-block-start" ], - }, - "offset-block-end": { - domProp: "offsetBlockEnd", - inherited: false, - type: CSS_TYPE_SHORTHAND_AND_LONGHAND, - logical: true, - get_computed: logical_box_prop_get_computed, - alias_for: "inset-block-end", - subproperties: [ "inset-block-end" ], - }, - "offset-inline-start": { - domProp: "offsetInlineStart", - inherited: false, - type: CSS_TYPE_SHORTHAND_AND_LONGHAND, - logical: true, - get_computed: logical_box_prop_get_computed, - alias_for: "inset-inline-start", - subproperties: [ "inset-inline-start" ], - }, - "offset-block-end": { - domProp: "offsetInlineEnd", - inherited: false, - type: CSS_TYPE_SHORTHAND_AND_LONGHAND, - logical: true, - get_computed: logical_box_prop_get_computed, - alias_for: "inset-inline-end", - subproperties: [ "inset-inline-end" ], - }, "padding-block-end": { domProp: "paddingBlockEnd", inherited: false, @@ -8124,6 +8088,45 @@ if (IsCSSPropertyPrefEnabled("layout.css.webkit-appearance.enabled")) { }; } +if (IsCSSPropertyPrefEnabled("layout.css.offset-logical-properties.enabled")) { + gCSSProperties["offset-block-start"] = { + domProp: "offsetBlockStart", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + alias_for: "inset-block-start", + subproperties: [ "inset-block-start" ], + }; + gCSSProperties["offset-block-end"] = { + domProp: "offsetBlockEnd", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + alias_for: "inset-block-end", + subproperties: [ "inset-block-end" ], + }; + gCSSProperties["offset-inline-start"] = { + domProp: "offsetInlineStart", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + alias_for: "inset-inline-start", + subproperties: [ "inset-inline-start" ], + }; + gCSSProperties["offset-inline-end"] = { + domProp: "offsetInlineEnd", + inherited: false, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + logical: true, + get_computed: logical_box_prop_get_computed, + alias_for: "inset-inline-end", + subproperties: [ "inset-inline-end" ], + }; +} + if (IsCSSPropertyPrefEnabled("layout.css.prefixes.gradients")) { gCSSProperties["background"].other_values.push( "-moz-radial-gradient(10% bottom, #ffffff, black) scroll no-repeat", diff --git a/modules/libpref/init/StaticPrefList.h b/modules/libpref/init/StaticPrefList.h index e0ab3ec72db7..58b539c17137 100644 --- a/modules/libpref/init/StaticPrefList.h +++ b/modules/libpref/init/StaticPrefList.h @@ -234,6 +234,13 @@ VARCACHE_PREF( bool, true ) +// Whether the offset-* logical property aliases are enabled. +VARCACHE_PREF( + "layout.css.offset-logical-properties.enabled", + layout_css_offset_logical_properties_enabled, + bool, true +) + // Should stray control characters be rendered visibly? #ifdef RELEASE_OR_BETA # define PREF_VALUE false diff --git a/servo/components/style/properties/longhands/position.mako.rs b/servo/components/style/properties/longhands/position.mako.rs index d7e4e27a3b74..3b34047fc5ad 100644 --- a/servo/components/style/properties/longhands/position.mako.rs +++ b/servo/components/style/properties/longhands/position.mako.rs @@ -29,7 +29,7 @@ "computed::LengthOrPercentageOrAuto::Auto", spec="https://drafts.csswg.org/css-logical-props/#propdef-inset-%s" % side, flags="GETCS_NEEDS_LAYOUT_FLUSH", - alias="offset-%s" % side, + alias="offset-%s:layout.css.offset-logical-properties.enabled" % side, animation_value_type="ComputedValue", logical=True, )} From 66a9ac9f63446d63c96619bea6006bd5644e3d2c Mon Sep 17 00:00:00 2001 From: Dorel Luca Date: Wed, 27 Jun 2018 17:53:26 +0300 Subject: [PATCH 30/43] Backed out 2 changesets (bug 1394102) for Web platform test failures. CLOSED TREE Backed out changeset 4133f5b222de (bug 1394102) Backed out changeset 39784ddfdbb0 (bug 1394102) --- dom/serviceworkers/ServiceWorkerPrivate.cpp | 250 +----------------- .../serviceworker-intercepted.https.html | 30 --- .../tests/fetch/api/resources/sw-intercept.js | 14 +- 3 files changed, 5 insertions(+), 289 deletions(-) diff --git a/dom/serviceworkers/ServiceWorkerPrivate.cpp b/dom/serviceworkers/ServiceWorkerPrivate.cpp index a6309c7e9369..7f906ce21d03 100644 --- a/dom/serviceworkers/ServiceWorkerPrivate.cpp +++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp @@ -11,7 +11,6 @@ #include "nsContentUtils.h" #include "nsICacheInfoChannel.h" #include "nsIHttpChannelInternal.h" -#include "nsIHttpProtocolHandler.h" #include "nsIHttpHeaderVisitor.h" #include "nsINamed.h" #include "nsINetworkInterceptController.h" @@ -117,232 +116,6 @@ ServiceWorkerPrivate::~ServiceWorkerPrivate() namespace { -class AbortChannelObserver; - -// Simple runnable that calls AbortChannelObserver::MaybeAbortAndReleaseSignal() -// on the worker thread. -class AbortSignalRunnable : public WorkerRunnable -{ - RefPtr mObserver; - bool mSignalToAbort; - -public: - AbortSignalRunnable(WorkerPrivate* aWorkerPrivate, - AbortChannelObserver* aObserver, - bool aSignalToAbort) - : WorkerRunnable(aWorkerPrivate) - , mObserver(aObserver) - , mSignalToAbort(aSignalToAbort) - { - MOZ_ASSERT(mObserver); - } - - bool - WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override; -}; - -// This object observes the terminations of nsIChannel objects. When |mChannel| -// is terminated, the AbortSignal object, owned by AbortSignalWorkerHolder, is -// aborted on the worker thread. This object is kept alive by the observer and -// by FetchEventRunnable until the AbortSignal is created on the the worker -// thread. -class AbortChannelObserver final : public nsIObserver -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - - static already_AddRefed - Create(nsIChannel* aChannel) - { - MOZ_ASSERT(aChannel); - AssertIsOnMainThread(); - - RefPtr observer = - new AbortChannelObserver(aChannel); - - // Let's use NS_HTTP_ON_STOP_REQUEST_TOPIC to know when the channel is - // released: maybe we have to abort the AbortSignal object. - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (NS_WARN_IF(!obs)) { - return nullptr; - } - - if (NS_FAILED(obs->AddObserver(observer, NS_HTTP_ON_STOP_REQUEST_TOPIC, - false))) { - return nullptr; - } - - return observer.forget(); - } - - NS_IMETHOD - Observe(nsISupports* aSubject, const char* aTopic, - const char16_t* aData) override - { - AssertIsOnMainThread(); - - // This is not our channel. - if (!SameCOMIdentity(aSubject, mChannel)) { - return NS_OK; - } - - // Maybe the observer is the only reason why this object is still alive. - // Let's keep it alive until the operation is completed. - RefPtr kungFuDeathGrip = this; - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->RemoveObserver(this, NS_HTTP_ON_STOP_REQUEST_TOPIC); - } - - nsCOMPtr internalChannel = - do_QueryInterface(mChannel); - NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE); - mChannel = nullptr; - - bool canceled = false; - Unused << NS_WARN_IF(NS_FAILED(internalChannel->GetCanceled(&canceled))); - - // From now on, we touch cross-process things. We need to protect them with - // the locking of a mutex. - RefPtr r; - { - MutexAutoLock lock(mMutex); - - mState = canceled ? eChannelAborted : eChannelSucceeded; - - if (!mWorkerRef) { - // CreateSignal() has not been called yet. - return NS_OK; - } - - // This will release AbortSignal on the correct thread. - r = new AbortSignalRunnable(mWorkerRef->GetUnsafePrivate(), this, canceled); - } - - if (NS_WARN_IF(!r->Dispatch())) { - // If this happens, it's not a big deal. |mWorkerRef| will be notified - // and AbortSignal will be released. - return NS_ERROR_FAILURE; - } - - return NS_OK; - } - - already_AddRefed - CreateSignal(WorkerPrivate* aWorkerPrivate) - { - MOZ_ASSERT(aWorkerPrivate); - MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); - - RefPtr abortSignal; - { - MutexAutoLock lock(mMutex); - MOZ_DIAGNOSTIC_ASSERT(!mWorkerRef); - - abortSignal = new AbortSignal(mState == eChannelAborted); - - if (mState != eChannelActive) { - return abortSignal.forget(); - } - } - - // This is needed to keep AbortSignal alive. - RefPtr self = this; - RefPtr workerRef = - WeakWorkerRef::Create(aWorkerPrivate, - [self]() { - self->MaybeAbortAndReleaseSignal(false); - } - ); - if (NS_WARN_IF(!workerRef)) { - return nullptr; - } - - { - MutexAutoLock lock(mMutex); - if (mState == eChannelActive) { - mSignal = abortSignal; - mWorkerRef.swap(workerRef); - } - } - - return abortSignal.forget(); - } - - void - Reset() - { - MOZ_ASSERT(IsCurrentThreadRunningWorker()); - - RefPtr workerRef; - { - MutexAutoLock lock(mMutex); - mSignal = nullptr; - workerRef.swap(mWorkerRef); - } - } - - void - MaybeAbortAndReleaseSignal(bool aToAbort) - { - MOZ_ASSERT(IsCurrentThreadRunningWorker()); - - RefPtr workerRef; - { - MutexAutoLock lock(mMutex); - MOZ_ASSERT(mWorkerRef); - - if (aToAbort && mSignal) { - mSignal->Abort(); - } - - workerRef.swap(mWorkerRef); - mSignal = nullptr; - } - } - -private: - explicit AbortChannelObserver(nsIChannel* aChannel) - : mChannel(aChannel) - , mMutex("AbortChannelObserver::mMutex") - , mState(eChannelActive) - {} - - ~AbortChannelObserver() - {} - - nsCOMPtr mChannel; - - // This object is touched in main-thread and workers but it's always released - // on the worker thread. When touched, the mutex is locked. - RefPtr mWorkerRef; - - RefPtr mSignal; - - mozilla::Mutex mMutex; - - // The following is protected by mutex. - enum { - // The channel is active. - eChannelActive, - - // the channel has been canceled. - eChannelAborted, - - // the cannel has been terminated successfully. - eChannelSucceeded, - } mState; -}; - -NS_IMPL_ISUPPORTS(AbortChannelObserver, nsIObserver) - -bool -AbortSignalRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) -{ - mObserver->MaybeAbortAndReleaseSignal(mSignalToAbort); - return true; -} - class CheckScriptEvaluationWithCallback final : public WorkerRunnable { nsMainThreadPtrHandle mServiceWorkerPrivate; @@ -1570,8 +1343,6 @@ class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable nsCString mReferrer; ReferrerPolicy mReferrerPolicy; nsString mIntegrity; - RefPtr mAbortObserver; - public: FetchEventRunnable(WorkerPrivate* aWorkerPrivate, KeepAliveToken* aKeepAliveToken, @@ -1737,11 +1508,6 @@ public: mUploadStream = uploadStream; } - mAbortObserver = AbortChannelObserver::Create(channel); - if (NS_WARN_IF(!mAbortObserver)) { - return NS_ERROR_FAILURE; - } - return NS_OK; } @@ -1774,9 +1540,6 @@ public: if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable))) { NS_WARNING("Failed to resume channel on FetchEventRunnable::Cancel()!\n"); } - - mAbortObserver = nullptr; - WorkerRunnable::Cancel(); return NS_OK; } @@ -1832,14 +1595,6 @@ private: } } - RefPtr abortSignal = - mAbortObserver->CreateSignal(aWorkerPrivate); - - RefPtr observer = mAbortObserver.forget(); - auto abortObserverRAII = mozilla::MakeScopeExit([observer] { - observer->Reset(); - }); - ErrorResult result; internalHeaders->SetGuard(HeadersGuardEnum::Immutable, result); if (NS_WARN_IF(result.Failed())) { @@ -1879,7 +1634,9 @@ private: return false; } - RefPtr request = new Request(global, internalReq, abortSignal); + // TODO This request object should be created with a AbortSignal object + // which should be aborted if the loading is aborted. See bug 1394102. + RefPtr request = new Request(global, internalReq, nullptr); MOZ_ASSERT_IF(internalReq->IsNavigationRequest(), request->Redirect() == RequestRedirect::Manual); @@ -1927,7 +1684,6 @@ private: MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(runnable.forget())); } - abortObserverRAII.release(); return true; } }; diff --git a/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html b/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html index a6b28e827b8b..623036d46639 100644 --- a/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html +++ b/testing/web-platform/tests/fetch/api/abort/serviceworker-intercepted.https.html @@ -94,36 +94,6 @@ await promise_rejects(t, "AbortError", reader.read()); await promise_rejects(t, "AbortError", reader.closed); }, "Stream errors once aborted."); - - promise_test(async t => { - const scope = SCOPE + "?q=aborted-signal"; - await setupRegistration(t, scope); - const iframe = await with_iframe(scope); - const w = iframe.contentWindow; - - const testReady = new Promise(resolve => { - w.navigator.serviceWorker.addEventListener('message', event => { - resolve(event.data); - }, { once: true }); - }); - - const controller = new w.AbortController(); - const signal = controller.signal; - - w.fetch('data.json?signal', { signal }); - - assert_true((await testReady) == "ready", ""); - controller.abort(); - - const aborted = new Promise(resolve => { - w.navigator.serviceWorker.addEventListener('message', event => { - resolve(event.data); - }, { once: true }); - }); - - assert_true((await aborted) == "aborted", ""); - }, "FetchEvent.request.signal must be aborted."); - diff --git a/testing/web-platform/tests/fetch/api/resources/sw-intercept.js b/testing/web-platform/tests/fetch/api/resources/sw-intercept.js index 2804e12a9cef..b8166b62a5c9 100644 --- a/testing/web-platform/tests/fetch/api/resources/sw-intercept.js +++ b/testing/web-platform/tests/fetch/api/resources/sw-intercept.js @@ -5,16 +5,6 @@ async function broadcast(msg) { } addEventListener('fetch', event => { - if (event.request.url.endsWith('?signal')) { - event.waitUntil(broadcast("ready")); - event.respondWith(new Promise((resolve, reject) => { - event.request.signal.onabort = () => { - broadcast("aborted"); - reject(); - }; - })); - } else { - event.waitUntil(broadcast(event.request.url)); - event.respondWith(fetch(event.request)); - } + event.waitUntil(broadcast(event.request.url)); + event.respondWith(fetch(event.request)); }); From 1f679b4e78271320016483571b0fdd33392f6c39 Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Fri, 22 Jun 2018 14:31:59 +0100 Subject: [PATCH 31/43] Bug 1470151 - Make dumpCoverage and resetCoverage return a Promise that is resolved when the parent process and all content processes are done with dumping or resetting coverage counters. r=froydnj --HG-- extra : rebase_source : a393fb7eddfad3be12162791c864fa7f2dad7f1b extra : source : ddde7dd347d451798becc0615468dd9acc5e609c --- dom/ipc/ContentChild.cpp | 10 ++- dom/ipc/ContentChild.h | 4 +- dom/ipc/PContent.ipdl | 4 +- tools/code-coverage/CodeCoverageHandler.cpp | 18 ++++- tools/code-coverage/CodeCoverageHandler.h | 6 +- tools/code-coverage/nsCodeCoverage.cpp | 84 +++++++++++++++++---- tools/code-coverage/nsICodeCoverage.idl | 6 +- 7 files changed, 102 insertions(+), 30 deletions(-) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 61f0ac058a19..ed1c09f724d4 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -3696,10 +3696,11 @@ ContentChild::RecvShareCodeCoverageMutex(const CrossProcessMutexHandle& aHandle) } mozilla::ipc::IPCResult -ContentChild::RecvDumpCodeCoverageCounters() +ContentChild::RecvDumpCodeCoverageCounters(DumpCodeCoverageCountersResolver&& aResolver) { #ifdef MOZ_CODE_COVERAGE - CodeCoverageHandler::DumpCounters(0); + CodeCoverageHandler::DumpCounters(); + aResolver(/* unused */ true); return IPC_OK(); #else MOZ_CRASH("Shouldn't receive this message in non-code coverage builds!"); @@ -3707,10 +3708,11 @@ ContentChild::RecvDumpCodeCoverageCounters() } mozilla::ipc::IPCResult -ContentChild::RecvResetCodeCoverageCounters() +ContentChild::RecvResetCodeCoverageCounters(ResetCodeCoverageCountersResolver&& aResolver) { #ifdef MOZ_CODE_COVERAGE - CodeCoverageHandler::ResetCounters(0); + CodeCoverageHandler::ResetCounters(); + aResolver(/* unused */ true); return IPC_OK(); #else MOZ_CRASH("Shouldn't receive this message in non-code coverage builds!"); diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 5870194b0b41..9695d53c389e 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -624,10 +624,10 @@ public: RecvShareCodeCoverageMutex(const CrossProcessMutexHandle& aHandle) override; virtual mozilla::ipc::IPCResult - RecvDumpCodeCoverageCounters() override; + RecvDumpCodeCoverageCounters(DumpCodeCoverageCountersResolver&& aResolver) override; virtual mozilla::ipc::IPCResult - RecvResetCodeCoverageCounters() override; + RecvResetCodeCoverageCounters(ResetCodeCoverageCountersResolver&& aResolver) override; virtual mozilla::ipc::IPCResult RecvSetInputEventQueueEnabled() override; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 7e93b3fc4ae3..2fd9d327535c 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -633,8 +633,8 @@ child: async SetPluginList(uint32_t pluginEpoch, PluginTag[] plugins, FakePluginTag[] fakePlugins); async ShareCodeCoverageMutex(CrossProcessMutexHandle handle); - async DumpCodeCoverageCounters(); - async ResetCodeCoverageCounters(); + async DumpCodeCoverageCounters() returns (bool unused); + async ResetCodeCoverageCounters() returns (bool unused); /* * IPC message to enable the input event queue on the main thread of the diff --git a/tools/code-coverage/CodeCoverageHandler.cpp b/tools/code-coverage/CodeCoverageHandler.cpp index 76dd399759a4..be687a590417 100644 --- a/tools/code-coverage/CodeCoverageHandler.cpp +++ b/tools/code-coverage/CodeCoverageHandler.cpp @@ -44,7 +44,7 @@ void counters_reset() { StaticAutoPtr CodeCoverageHandler::instance; -void CodeCoverageHandler::DumpCounters(int) +void CodeCoverageHandler::DumpCounters() { CrossProcessMutexAutoLock lock(*CodeCoverageHandler::Get()->GetMutex()); @@ -53,7 +53,12 @@ void CodeCoverageHandler::DumpCounters(int) printf_stderr("[CodeCoverage] Dump completed.\n"); } -void CodeCoverageHandler::ResetCounters(int) +void CodeCoverageHandler::DumpCountersSignalHandler(int) +{ + DumpCounters(); +} + +void CodeCoverageHandler::ResetCounters() { CrossProcessMutexAutoLock lock(*CodeCoverageHandler::Get()->GetMutex()); @@ -62,20 +67,25 @@ void CodeCoverageHandler::ResetCounters(int) printf_stderr("[CodeCoverage] Reset completed.\n"); } +void CodeCoverageHandler::ResetCountersSignalHandler(int) +{ + ResetCounters(); +} + void CodeCoverageHandler::SetSignalHandlers() { #ifndef XP_WIN printf_stderr("[CodeCoverage] Setting handlers for process %d.\n", getpid()); struct sigaction dump_sa; - dump_sa.sa_handler = CodeCoverageHandler::DumpCounters; + dump_sa.sa_handler = CodeCoverageHandler::DumpCountersSignalHandler; dump_sa.sa_flags = SA_RESTART; sigemptyset(&dump_sa.sa_mask); DebugOnly r1 = sigaction(SIGUSR1, &dump_sa, nullptr); MOZ_ASSERT(r1 == 0, "Failed to install GCOV SIGUSR1 handler"); struct sigaction reset_sa; - reset_sa.sa_handler = CodeCoverageHandler::ResetCounters; + reset_sa.sa_handler = CodeCoverageHandler::ResetCountersSignalHandler; reset_sa.sa_flags = SA_RESTART; sigemptyset(&reset_sa.sa_mask); DebugOnly r2 = sigaction(SIGUSR2, &reset_sa, nullptr); diff --git a/tools/code-coverage/CodeCoverageHandler.h b/tools/code-coverage/CodeCoverageHandler.h index 42e4725e46b1..0a29fabe15f1 100644 --- a/tools/code-coverage/CodeCoverageHandler.h +++ b/tools/code-coverage/CodeCoverageHandler.h @@ -18,8 +18,10 @@ public: static CodeCoverageHandler* Get(); CrossProcessMutex* GetMutex(); CrossProcessMutexHandle GetMutexHandle(int aProcId); - static void DumpCounters(int); - static void ResetCounters(int); + static void DumpCounters(); + static void DumpCountersSignalHandler(int); + static void ResetCounters(); + static void ResetCountersSignalHandler(int); private: CodeCoverageHandler(); diff --git a/tools/code-coverage/nsCodeCoverage.cpp b/tools/code-coverage/nsCodeCoverage.cpp index 904778508c8d..d6e1b7aeb529 100644 --- a/tools/code-coverage/nsCodeCoverage.cpp +++ b/tools/code-coverage/nsCodeCoverage.cpp @@ -7,9 +7,11 @@ #include "mozilla/CodeCoverageHandler.h" #include "mozilla/Unused.h" #include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/Promise.h" using namespace mozilla; using namespace mozilla::dom; +using namespace mozilla::ipc; NS_IMPL_ISUPPORTS(nsCodeCoverage, nsICodeCoverage) @@ -22,28 +24,82 @@ nsCodeCoverage::~nsCodeCoverage() { } -NS_IMETHODIMP nsCodeCoverage::DumpCounters() +enum RequestType { Dump, Reset }; + +class ProcessCount final { + NS_INLINE_DECL_REFCOUNTING(ProcessCount); + +public: + ProcessCount(uint32_t c) : mCount(c) {} + operator uint32_t() const { return mCount; } + ProcessCount& operator--() { mCount--; return *this; } + +private: + ~ProcessCount() {} + uint32_t mCount; +}; + +nsresult Request(JSContext* cx, Promise** aPromise, RequestType requestType) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); - CodeCoverageHandler::DumpCounters(0); - for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { - Unused << cp->SendDumpCodeCoverageCounters(); + nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx)); + if (NS_WARN_IF(!global)) { + return NS_ERROR_FAILURE; } + ErrorResult result; + RefPtr promise = Promise::Create(global, result); + if (NS_WARN_IF(result.Failed())) { + return result.StealNSResult(); + } + + uint32_t processCount = 0; + for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { + ++processCount; + } + + if (requestType == RequestType::Dump) { + CodeCoverageHandler::DumpCounters(); + } else if (requestType == RequestType::Reset) { + CodeCoverageHandler::ResetCounters(); + } + + if (processCount == 0) { + promise->MaybeResolveWithUndefined(); + } else { + RefPtr processCountHolder(new ProcessCount(processCount)); + + auto resolve = [processCountHolder, promise](bool unused) { + if (--(*processCountHolder) == 0) { + promise->MaybeResolveWithUndefined(); + } + }; + + auto reject = [promise](ResponseRejectReason aReason) { + promise->MaybeReject(NS_ERROR_FAILURE); + }; + + for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { + if (requestType == RequestType::Dump) { + cp->SendDumpCodeCoverageCounters(resolve, reject); + } else if (requestType == RequestType::Reset) { + cp->SendResetCodeCoverageCounters(resolve, reject); + } + } + } + + promise.forget(aPromise); return NS_OK; } -NS_IMETHODIMP nsCodeCoverage::ResetCounters() +NS_IMETHODIMP nsCodeCoverage::DumpCounters(JSContext *cx, Promise** aPromise) { - MOZ_ASSERT(XRE_IsParentProcess()); - MOZ_ASSERT(NS_IsMainThread()); - - CodeCoverageHandler::ResetCounters(0); - for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { - Unused << cp->SendResetCodeCoverageCounters(); - } - - return NS_OK; + return Request(cx, aPromise, RequestType::Dump); +} + +NS_IMETHODIMP nsCodeCoverage::ResetCounters(JSContext *cx, Promise** aPromise) +{ + return Request(cx, aPromise, RequestType::Reset); } diff --git a/tools/code-coverage/nsICodeCoverage.idl b/tools/code-coverage/nsICodeCoverage.idl index 4b4216572d16..e1f1a21316c6 100644 --- a/tools/code-coverage/nsICodeCoverage.idl +++ b/tools/code-coverage/nsICodeCoverage.idl @@ -19,10 +19,12 @@ interface nsICodeCoverage : nsISupports /** * Write the coverage counters to disk. */ - void dumpCounters(); + [implicit_jscontext] + Promise dumpCounters(); /** * Reset the coverage counters to 0 (as if nothing was executed). */ - void resetCounters(); + [implicit_jscontext] + Promise resetCounters(); }; From 41d0c90dd9de60b5ac69edefd4f7f4055ad3b7ff Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Thu, 21 Jun 2018 14:40:20 +0100 Subject: [PATCH 32/43] Bug 1470151 - Add a PerTestCoverageUtils JavaScript module to manage resetting/dumping coverage counters for tests. r=jmaher --HG-- extra : rebase_source : 9b7051f9b607bd205a9519e124688c7710a4c241 extra : source : 3579431e03dc89c5e09b069acdd45e15bd640fe6 --- tools/code-coverage/PerTestCoverageUtils.jsm | 64 ++++++++++++++++++++ tools/code-coverage/moz.build | 2 + 2 files changed, 66 insertions(+) create mode 100644 tools/code-coverage/PerTestCoverageUtils.jsm diff --git a/tools/code-coverage/PerTestCoverageUtils.jsm b/tools/code-coverage/PerTestCoverageUtils.jsm new file mode 100644 index 000000000000..c804aaa89b00 --- /dev/null +++ b/tools/code-coverage/PerTestCoverageUtils.jsm @@ -0,0 +1,64 @@ +/* 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/. */ + +/* exported PerTestCoverageUtils */ + +"use strict"; + +var EXPORTED_SYMBOLS = ["PerTestCoverageUtils"]; + +ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); + +class PerTestCoverageUtilsClass { + constructor(tmp_gcov_dir, gcov_dir) { + this.tmp_gcov_dir = tmp_gcov_dir; + this.gcov_dir = gcov_dir; + + this.codeCoverageService = Cc["@mozilla.org/tools/code-coverage;1"].getService(Ci.nsICodeCoverage); + } + + _awaitPromise(promise) { + let ret; + let complete = false; + let error = null; + promise.catch(e => error = e).then(v => { + ret = v; + complete = true; + }); + Services.tm.spinEventLoopUntil(() => complete); + if (error) { + throw new Error(error); + } + return ret; + } + + // Resets the counters to 0. + beforeTest() { + this._awaitPromise(this.codeCoverageService.resetCounters()); + } + + // Dumps counters and moves the gcda files in the directory expected by codecoverage.py. + afterTest() { + this._awaitPromise(this.codeCoverageService.dumpCounters()); + + let srcDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + srcDir.initWithPath(this.tmp_gcov_dir); + + let destDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + destDir.initWithPath(this.gcov_dir); + + let srcDirEntries = srcDir.directoryEntries; + while (srcDirEntries.hasMoreElements()) { + srcDirEntries.nextFile.moveTo(destDir, null); + } + } +} + +const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment); +// This is the directory where gcov is emitting the gcda files. +const tmp_gcov_dir = env.get("GCOV_PREFIX"); +// This is the directory where codecoverage.py is expecting to see the gcda files. +const gcov_dir = env.get("GCOV_RESULTS_DIR"); + +const PerTestCoverageUtils = gcov_dir ? new PerTestCoverageUtilsClass(tmp_gcov_dir, gcov_dir) : null; diff --git a/tools/code-coverage/moz.build b/tools/code-coverage/moz.build index c3bad16b9fa6..d66699a740a1 100644 --- a/tools/code-coverage/moz.build +++ b/tools/code-coverage/moz.build @@ -31,4 +31,6 @@ if CONFIG['MOZ_CODE_COVERAGE']: XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini'] MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini'] + TESTING_JS_MODULES += ['PerTestCoverageUtils.jsm'] + FINAL_LIBRARY = 'xul' From 37e7ee033d0e19c3baa5dac931f9e3420da015b3 Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Thu, 21 Jun 2018 14:41:32 +0100 Subject: [PATCH 33/43] Bug 1470151 - Support resetting/dumping coverage counters in per-test mode for xpcshell on Linux. r=jmaher --HG-- extra : rebase_source : f2d33c3f952a04c96a4ca02691e10ff5241522bf extra : source : c73f394a4bef01cccd76d635bce9d67e4a48f721 --- .../mozharness/scripts/desktop_unittest.py | 5 +++++ testing/xpcshell/head.js | 20 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/testing/mozharness/scripts/desktop_unittest.py b/testing/mozharness/scripts/desktop_unittest.py index d923158042b4..ea8ff30d625b 100755 --- a/testing/mozharness/scripts/desktop_unittest.py +++ b/testing/mozharness/scripts/desktop_unittest.py @@ -15,6 +15,7 @@ import re import sys import copy import shutil +import tempfile import glob import imp @@ -890,6 +891,10 @@ class DesktopUnittest(TestingMixin, MercurialScript, MozbaseMixin, if self.per_test_coverage: gcov_dir, jsvm_dir = self.set_coverage_env(env) + # Per-test reset/dump is only supported for xpcshell and + # Linux for the time being. + if not is_baseline_test and suite == 'xpcshell' and self._is_linux(): + env['GCOV_RESULTS_DIR'] = gcov_dir = tempfile.mkdtemp() return_code = self.run_command(final_cmd, cwd=dirs['abs_work_dir'], output_timeout=cmd_timeout, diff --git a/testing/xpcshell/head.js b/testing/xpcshell/head.js index 952649302db3..9c1a813c73e5 100644 --- a/testing/xpcshell/head.js +++ b/testing/xpcshell/head.js @@ -511,6 +511,22 @@ function _execute_test() { this[func] = Assert[func].bind(Assert); } + let perTestCoverageEnabled = false; + try { + ChromeUtils.import("resource://testing-common/PerTestCoverageUtils.jsm"); + perTestCoverageEnabled = true; + } catch (e) { + // If the module doesn't exist, code coverage is disabled. + // Otherwise, rethrow the exception. + if (e.result != Cr.NS_ERROR_FILE_NOT_FOUND) { + throw e; + } + } + + if (perTestCoverageEnabled) { + PerTestCoverageUtils.beforeTest(); + } + try { do_test_pending("MAIN run_test"); // Check if run_test() is defined. If defined, run it. @@ -529,6 +545,10 @@ function _execute_test() { if (coverageCollector != null) { coverageCollector.recordTestCoverage(_TEST_FILE[0]); } + + if (perTestCoverageEnabled) { + PerTestCoverageUtils.afterTest(); + } } catch (e) { _passed = false; // do_check failures are already logged and set _quit to true and throw From 1185b20068cd2a13908da809cc004b08d2e9ba63 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 27 Jun 2018 11:10:31 -0400 Subject: [PATCH 34/43] Bug 1471614 - remove some dead code in CycleCollectedJSRuntime; r=mccr8 Not only is it dead code, but it's a significant amount of useless work! --- xpcom/base/CycleCollectedJSRuntime.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index 02f4a15b1895..a5916dfe81b3 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -1636,9 +1636,7 @@ CycleCollectedJSRuntime::ErrorInterceptor::interceptError(JSContext* cx, const J // so nothing such should happen. nsContentUtils::ExtractErrorValues(cx, value, details.mFilename, &details.mLine, &details.mColumn, details.mMessage); - nsAutoCString stack; JS::UniqueChars buf = JS::FormatStackDump(cx, nullptr, /* showArgs = */ false, /* showLocals = */ false, /* showThisProps = */ false); - stack.Append(buf.get()); CopyUTF8toUTF16(buf.get(), details.mStack); mThrownError.emplace(std::move(details)); From 498696bbb5f8a593f33f24cb96666e7f9f714728 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 27 Jun 2018 16:12:41 +0100 Subject: [PATCH 35/43] Bug 1461573 - Mark various dynamicDirAuto-* tests as slightly fuzzy on Android due to painting inconsistency. r=mats --- layout/reftests/bidi/dirAuto/reftest.list | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/layout/reftests/bidi/dirAuto/reftest.list b/layout/reftests/bidi/dirAuto/reftest.list index 13527a088578..aaf5c20c4c48 100644 --- a/layout/reftests/bidi/dirAuto/reftest.list +++ b/layout/reftests/bidi/dirAuto/reftest.list @@ -44,19 +44,19 @@ random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == dir_auto-unset-contained == dynamicDirAuto-setLTR-InvalidDir7.html dynamicDirAuto-refLTR-LTR.html == dynamicDirAuto-setLTR-InvalidDir7.html dynamicDirAuto-refLTR-LTR.html == dynamicDirAuto-setRTL-Auto1.html dynamicDirAuto-refRTL-RTL.html -== dynamicDirAuto-setRTL-Auto2.html dynamicDirAuto-refRTL-RTL.html +fuzzy-if(Android,1,1) == dynamicDirAuto-setRTL-Auto2.html dynamicDirAuto-refRTL-RTL.html # android fuzz: bug 1461573 == dynamicDirAuto-setRTL-Auto3.html dynamicDirAuto-refRTL-RTL.html == dynamicDirAuto-setRTL-Auto4.html dynamicDirAuto-refRTL-RTL.html -== dynamicDirAuto-setRTL-Auto5.html dynamicDirAuto-refRTL-RTL.html +fuzzy-if(Android,1,1) == dynamicDirAuto-setRTL-Auto5.html dynamicDirAuto-refRTL-RTL.html # android fuzz: bug 1459239 == dynamicDirAuto-setRTL-Auto6.html dynamicDirAuto-refRTL-RTL.html == dynamicDirAuto-setRTL-LTR1.html dynamicDirAuto-refRTL-LTR.html == dynamicDirAuto-setRTL-LTR2.html dynamicDirAuto-refRTL-LTR.html == dynamicDirAuto-setRTL-LTR3.html dynamicDirAuto-refRTL-LTR.html -== dynamicDirAuto-setRTL-LTR4.html dynamicDirAuto-refRTL-LTR.html +fuzzy-if(Android,4,2) == dynamicDirAuto-setRTL-LTR4.html dynamicDirAuto-refRTL-LTR.html # android fuzz: bug 1356818 == dynamicDirAuto-setRTL-LTR5.html dynamicDirAuto-refRTL-LTR.html == dynamicDirAuto-setRTL-LTR6.html dynamicDirAuto-refRTL-LTR.html == dynamicDirAuto-setRTL-LTR7.html dynamicDirAuto-refRTL-LTR.html -== dynamicDirAuto-setRTL-LTR8.html dynamicDirAuto-refRTL-LTR.html +fuzzy-if(Android,4,2) == dynamicDirAuto-setRTL-LTR8.html dynamicDirAuto-refRTL-LTR.html # android fuzz: bug 1468922 == dynamicDirAuto-setRTL-RTL1.html dynamicDirAuto-refRTL-RTL.html == dynamicDirAuto-setRTL-RTL2.html dynamicDirAuto-refRTL-RTL.html == dynamicDirAuto-setRTL-RTL3.html dynamicDirAuto-refRTL-RTL.html @@ -105,7 +105,7 @@ fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)&&!layersGPUAccelerated&&!azur == dynamicDirAuto-ChangeText-RTL10.html dynamicDirAuto-refRTL-RTL.html == dynamicDirAuto-DeleteText-LTR1.html dynamicDirAuto-refLTR-LTR.html == dynamicDirAuto-DeleteText-LTR2.html dynamicDirAuto-refLTR-LTR.html -== dynamicDirAuto-DeleteText-LTR3.html dynamicDirAuto-refLTR-LTR.html +fuzzy-if(Android,3,2) == dynamicDirAuto-DeleteText-LTR3.html dynamicDirAuto-refLTR-LTR.html # android fuzz: bug 1467675 == dynamicDirAuto-DeleteText-RTL1.html dynamicDirAuto-refRTL-RTL.html == dynamicDirAuto-DeleteText-RTL2.html dynamicDirAuto-refRTL-RTL.html == dynamicDirAuto-DeleteText-RTL3.html dynamicDirAuto-refRTL-RTL.html From 1e3aec53f9e91f00485fbf66424381534138020f Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 27 Jun 2018 16:12:42 +0100 Subject: [PATCH 36/43] Bug 1422530 - Don't initiate new font downloads for a fontGroup where we've already set mSkipDrawing because we're waiting on a resource to be available. r=heycam --- gfx/thebes/gfxTextRun.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp index f00f9ace5afa..9feba02392db 100644 --- a/gfx/thebes/gfxTextRun.cpp +++ b/gfx/thebes/gfxTextRun.cpp @@ -1908,7 +1908,7 @@ gfxFontGroup::GetFontAt(int32_t i, uint32_t aCh) if (fe->mIsUserFontContainer) { gfxUserFontEntry* ufe = static_cast(fe); if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED && - ufe->CharacterInUnicodeRange(aCh) && + ufe->CharacterInUnicodeRange(aCh) && !mSkipDrawing && !FontLoadingForFamily(ff.Family(), aCh)) { ufe->Load(); ff.CheckState(mSkipDrawing); @@ -2091,7 +2091,8 @@ gfxFontGroup::GetFirstValidFont(uint32_t aCh, FontFamilyType* aGeneric) static_cast(mFonts[i].FontEntry()); bool inRange = ufe->CharacterInUnicodeRange(aCh); if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED && - inRange && !FontLoadingForFamily(ff.Family(), aCh)) { + inRange && !mSkipDrawing && + !FontLoadingForFamily(ff.Family(), aCh)) { ufe->Load(); ff.CheckState(mSkipDrawing); } @@ -2988,7 +2989,7 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh, // load if not already loaded but only if no other font in similar // range within family is loading if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED && - !FontLoadingForFamily(ff.Family(), aCh)) { + !mSkipDrawing && !FontLoadingForFamily(ff.Family(), aCh)) { ufe->Load(); ff.CheckState(mSkipDrawing); } From 899b2539d6bdb3d09487c120bf1fc2c0f150bd6b Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 27 Jun 2018 16:12:43 +0100 Subject: [PATCH 37/43] Bug 1471584 - Micro-optimize text shaping by avoiding an unnecessary virtual method call. r=lsalzman --- gfx/thebes/gfxHarfBuzzShaper.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/gfx/thebes/gfxHarfBuzzShaper.cpp b/gfx/thebes/gfxHarfBuzzShaper.cpp index ff638dcef73a..5f23eb1480cf 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.cpp +++ b/gfx/thebes/gfxHarfBuzzShaper.cpp @@ -57,7 +57,7 @@ gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfxFont *aFont) mNumLongVMetrics(0), mDefaultVOrg(-1.0), mUseFontGetGlyph(aFont->ProvidesGetGlyph()), - mUseFontGlyphWidths(false), + mUseFontGlyphWidths(aFont->ProvidesGlyphWidths()), mInitialized(false), mVerticalInitialized(false), mLoadedLocaGlyf(false), @@ -379,13 +379,13 @@ hb_position_t gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t *font, void *font_data, hb_codepoint_t glyph, void *user_data) { - const gfxHarfBuzzShaper::FontCallbackData *fcd = + const gfxHarfBuzzShaper::FontCallbackData* fcd = static_cast(font_data); - gfxFont *gfxfont = fcd->mShaper->GetFont(); - if (gfxfont->ProvidesGlyphWidths()) { - return gfxfont->GetGlyphWidth(*fcd->mDrawTarget, glyph); + const gfxHarfBuzzShaper* shaper = fcd->mShaper; + if (shaper->mUseFontGlyphWidths) { + return shaper->GetFont()->GetGlyphWidth(*fcd->mDrawTarget, glyph); } - return fcd->mShaper->GetGlyphHAdvance(glyph); + return shaper->GetGlyphHAdvance(glyph); } /* static */ @@ -1211,8 +1211,6 @@ gfxHarfBuzzShaper::Initialize() mInitialized = true; mCallbackData.mShaper = this; - mUseFontGlyphWidths = mFont->ProvidesGlyphWidths(); - if (!sHBFontFuncs) { // static function callback pointers, initialized by the first // harfbuzz shaper used From d65aee9de5995a0ceb255bdc08c2abada4a3ced8 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 27 Jun 2018 11:27:33 -0400 Subject: [PATCH 38/43] Bug 1471435 - part 1 - clean up nsFile{Input,Ouput}Stream::Create; r=valentin We should be using smart pointers here, and there's no need to check for allocation failure with our infallible new policy. --- netwerk/base/nsFileStreams.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/netwerk/base/nsFileStreams.cpp b/netwerk/base/nsFileStreams.cpp index f546a6b050c1..9d3efbc26363 100644 --- a/netwerk/base/nsFileStreams.cpp +++ b/netwerk/base/nsFileStreams.cpp @@ -449,13 +449,8 @@ nsFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) { NS_ENSURE_NO_AGGREGATION(aOuter); - nsFileInputStream* stream = new nsFileInputStream(); - if (stream == nullptr) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(stream); - nsresult rv = stream->QueryInterface(aIID, aResult); - NS_RELEASE(stream); - return rv; + RefPtr stream = new nsFileInputStream(); + return stream->QueryInterface(aIID, aResult); } nsresult @@ -727,13 +722,8 @@ nsFileOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) { NS_ENSURE_NO_AGGREGATION(aOuter); - nsFileOutputStream* stream = new nsFileOutputStream(); - if (stream == nullptr) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(stream); - nsresult rv = stream->QueryInterface(aIID, aResult); - NS_RELEASE(stream); - return rv; + RefPtr stream = new nsFileOutputStream(); + return stream->QueryInterface(aIID, aResult); } NS_IMETHODIMP From 9fd8289cf99029de424b1b12f986e8a8b7dc3257 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 27 Jun 2018 11:27:33 -0400 Subject: [PATCH 39/43] Bug 1471435 - part 2 - micro-optimize deferred MaybeOpen calls; r=valentin We shouldn't need to QueryInterface from nsIFile to nsIFile! --- netwerk/base/nsFileStreams.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netwerk/base/nsFileStreams.cpp b/netwerk/base/nsFileStreams.cpp index 9d3efbc26363..a8ffec6a98a7 100644 --- a/netwerk/base/nsFileStreams.cpp +++ b/netwerk/base/nsFileStreams.cpp @@ -303,7 +303,7 @@ nsFileStreamBase::MaybeOpen(nsIFile* aFile, int32_t aIoFlags, nsresult rv = aFile->Clone(getter_AddRefs(file)); NS_ENSURE_SUCCESS(rv, rv); - mOpenParams.localFile = do_QueryInterface(file); + mOpenParams.localFile = file.forget(); NS_ENSURE_TRUE(mOpenParams.localFile, NS_ERROR_UNEXPECTED); mState = eDeferredOpen; From 8aaca6bf90b4f7a747151d66e31124d85ecf68c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Wed, 27 Jun 2018 17:30:49 +0200 Subject: [PATCH 40/43] Bug 1464782: Allow logical aliases in the property database. r=me MozReview-Commit-ID: 68WqG0OViPQ --- layout/style/test/test_property_database.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/layout/style/test/test_property_database.html b/layout/style/test/test_property_database.html index 30e6618fcb21..e7751cf0b8fc 100644 --- a/layout/style/test/test_property_database.html +++ b/layout/style/test/test_property_database.html @@ -123,13 +123,15 @@ for (var prop in gCSSProperties) { } /* - * Test that only longhand properties are listed as logical properties. + * Test that only longhand properties or its aliases are listed as logical + * properties. */ for (var prop in gCSSProperties) { var entry = gCSSProperties[prop]; if (entry.logical) { - is(entry.type, CSS_TYPE_LONGHAND, - "property '" + prop + "' is listed as CSS_TYPE_LONGHAND due to its " + + ok(entry.type == CSS_TYPE_LONGHAND || + (entry.alias_for && gCSSProperties[entry.alias_for].logical), + "property '" + prop + "' is listed as CSS_TYPE_LONGHAND or is an alias due to it " + "being a logical property"); } } From f7aa7c9447b0603cd9f70f1ce543868615bb15e4 Mon Sep 17 00:00:00 2001 From: Dorel Luca Date: Wed, 27 Jun 2018 18:42:30 +0300 Subject: [PATCH 41/43] Backed out changeset 0ca9cade7725 (bug 1471614) for XPCshell failures on devtools/shared/tests/unit/test_css-properties-db.js --- xpcom/base/CycleCollectedJSRuntime.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index a5916dfe81b3..02f4a15b1895 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -1636,7 +1636,9 @@ CycleCollectedJSRuntime::ErrorInterceptor::interceptError(JSContext* cx, const J // so nothing such should happen. nsContentUtils::ExtractErrorValues(cx, value, details.mFilename, &details.mLine, &details.mColumn, details.mMessage); + nsAutoCString stack; JS::UniqueChars buf = JS::FormatStackDump(cx, nullptr, /* showArgs = */ false, /* showLocals = */ false, /* showThisProps = */ false); + stack.Append(buf.get()); CopyUTF8toUTF16(buf.get(), details.mStack); mThrownError.emplace(std::move(details)); From 713a85aa66955bb41989332630e4e1d60f541625 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 27 Jun 2018 11:10:31 -0400 Subject: [PATCH 42/43] Bug 1471614 - remove some dead code in CycleCollectedJSRuntime; r=mccr8 Not only is it dead code, but it's a significant amount of useless work! --- xpcom/base/CycleCollectedJSRuntime.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index 02f4a15b1895..a5916dfe81b3 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -1636,9 +1636,7 @@ CycleCollectedJSRuntime::ErrorInterceptor::interceptError(JSContext* cx, const J // so nothing such should happen. nsContentUtils::ExtractErrorValues(cx, value, details.mFilename, &details.mLine, &details.mColumn, details.mMessage); - nsAutoCString stack; JS::UniqueChars buf = JS::FormatStackDump(cx, nullptr, /* showArgs = */ false, /* showLocals = */ false, /* showThisProps = */ false); - stack.Append(buf.get()); CopyUTF8toUTF16(buf.get(), details.mStack); mThrownError.emplace(std::move(details)); From 3ef9fe1263335cbc5ad86e15eebb60ac52acc44c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Wed, 27 Jun 2018 17:59:04 +0200 Subject: [PATCH 43/43] Bug 1464782: Regenerate the devtools db. r=me MozReview-Commit-ID: FJxRDsDFsaU --- .../shared/css/generated/properties-db.js | 84 +++++++++++++++++-- 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/devtools/shared/css/generated/properties-db.js b/devtools/shared/css/generated/properties-db.js index 94a2a2f45d25..da21aaf6afcb 100644 --- a/devtools/shared/css/generated/properties-db.js +++ b/devtools/shared/css/generated/properties-db.js @@ -2736,10 +2736,10 @@ exports.CSS_PROPERTIES = { "padding-block-end", "padding-inline-start", "padding-inline-end", - "offset-block-start", - "offset-block-end", - "offset-inline-start", - "offset-inline-end", + "inset-block-start", + "inset-block-end", + "inset-inline-start", + "inset-inline-end", "block-size", "min-block-size", "max-block-size", @@ -6254,6 +6254,58 @@ exports.CSS_PROPERTIES = { "unset" ] }, + "inset-block-end": { + "isInherited": false, + "subproperties": [ + "inset-block-end" + ], + "supports": [], + "values": [ + "auto", + "inherit", + "initial", + "unset" + ] + }, + "inset-block-start": { + "isInherited": false, + "subproperties": [ + "inset-block-start" + ], + "supports": [], + "values": [ + "auto", + "inherit", + "initial", + "unset" + ] + }, + "inset-inline-end": { + "isInherited": false, + "subproperties": [ + "inset-inline-end" + ], + "supports": [], + "values": [ + "auto", + "inherit", + "initial", + "unset" + ] + }, + "inset-inline-start": { + "isInherited": false, + "subproperties": [ + "inset-inline-start" + ], + "supports": [], + "values": [ + "auto", + "inherit", + "initial", + "unset" + ] + }, "isolation": { "isInherited": false, "subproperties": [ @@ -7224,7 +7276,7 @@ exports.CSS_PROPERTIES = { "offset-block-end": { "isInherited": false, "subproperties": [ - "offset-block-end" + "inset-block-end" ], "supports": [], "values": [ @@ -7237,7 +7289,7 @@ exports.CSS_PROPERTIES = { "offset-block-start": { "isInherited": false, "subproperties": [ - "offset-block-start" + "inset-block-start" ], "supports": [], "values": [ @@ -7250,7 +7302,7 @@ exports.CSS_PROPERTIES = { "offset-inline-end": { "isInherited": false, "subproperties": [ - "offset-inline-end" + "inset-inline-end" ], "supports": [], "values": [ @@ -7263,7 +7315,7 @@ exports.CSS_PROPERTIES = { "offset-inline-start": { "isInherited": false, "subproperties": [ - "offset-inline-start" + "inset-inline-start" ], "supports": [], "values": [ @@ -9611,5 +9663,21 @@ exports.PREFERENCES = [ [ "-webkit-user-select", "layout.css.prefixes.webkit" + ], + [ + "offset-block-end", + "layout.css.offset-logical-properties.enabled" + ], + [ + "offset-block-start", + "layout.css.offset-logical-properties.enabled" + ], + [ + "offset-inline-end", + "layout.css.offset-logical-properties.enabled" + ], + [ + "offset-inline-start", + "layout.css.offset-logical-properties.enabled" ] ];