diff --git a/browser/app/firefox.exe.manifest b/browser/app/firefox.exe.manifest index f27e149e3b43..1e1d6f651d27 100644 --- a/browser/app/firefox.exe.manifest +++ b/browser/app/firefox.exe.manifest @@ -19,6 +19,16 @@ /> + + + + + diff --git a/browser/config/mozconfigs/win32/mingwclang b/browser/config/mozconfigs/win32/mingwclang index 253ee951854b..29331cddeec2 100644 --- a/browser/config/mozconfigs/win32/mingwclang +++ b/browser/config/mozconfigs/win32/mingwclang @@ -30,6 +30,7 @@ ac_add_options --target=i686-w64-mingw32 ac_add_options --with-toolchain-prefix=i686-w64-mingw32- ac_add_options --disable-warnings-as-errors +MOZ_COPY_PDBS=1 # Temporary config settings until we get these working on mingw ac_add_options --disable-accessibility # https://sourceforge.net/p/mingw-w64/bugs/648/ diff --git a/browser/config/mozconfigs/win64/mingwclang b/browser/config/mozconfigs/win64/mingwclang index 305bc9e36f5b..5e9446e0acdc 100755 --- a/browser/config/mozconfigs/win64/mingwclang +++ b/browser/config/mozconfigs/win64/mingwclang @@ -30,6 +30,7 @@ ac_add_options --target=x86_64-w64-mingw32 ac_add_options --with-toolchain-prefix=x86_64-w64-mingw32- ac_add_options --disable-warnings-as-errors +MOZ_COPY_PDBS=1 # Temporary config settings until we get these working on mingw ac_add_options --disable-accessibility # https://sourceforge.net/p/mingw-w64/bugs/648/ diff --git a/browser/modules/Sanitizer.jsm b/browser/modules/Sanitizer.jsm index 6e0b7950ff07..e283c909450f 100644 --- a/browser/modules/Sanitizer.jsm +++ b/browser/modules/Sanitizer.jsm @@ -711,6 +711,14 @@ async function sanitizeSessionPrincipals() { return; } + // When PREF_COOKIE_LIFETIME is set to ACCEPT_SESSION, any new cookie will be + // marked as session only. But we don't touch the existing ones. For this + // reason, here we delete any existing cookie, at shutdown. + await new Promise(resolve => { + Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_COOKIES, + resolve); + }); + let principals = await new Promise(resolve => { quotaManagerService.getUsage(request => { if (request.resultCode != Cr.NS_OK) { diff --git a/build/moz.configure/toolchain.configure b/build/moz.configure/toolchain.configure index a8a4b9e7c8cf..118784bb0be0 100755 --- a/build/moz.configure/toolchain.configure +++ b/build/moz.configure/toolchain.configure @@ -1126,11 +1126,13 @@ def check_have_64_bit(have_64_bit, compiler_have_64_bit): 'about the target bitness.') -@depends(c_compiler) -def default_debug_flags(compiler_info): +@depends(c_compiler, target) +def default_debug_flags(compiler_info, target): # Debug info is ON by default. if compiler_info.type in ('msvc', 'clang-cl'): return '-Zi' + elif target.kernel == 'WINNT' and compiler_info.type == 'clang': + return '-g -gcodeview' return '-g' @@ -1992,11 +1994,16 @@ add_old_configure_assignment('LIBFUZZER_FLAGS', libfuzzer_flags.use_flags) @depends(target, c_compiler) def make_shared_library(target, compiler): if target.os == 'WINNT': - if compiler.type in ('gcc', 'clang'): + if compiler.type == 'gcc': return namespace( mkshlib=['$(CXX)', '$(DSO_LDOPTS)', '-o', '$@'], mkcshlib=['$(CC)', '$(DSO_LDOPTS)', '-o', '$@'], ) + elif compiler.type == 'clang': + return namespace( + mkshlib=['$(CXX)', '$(DSO_LDOPTS)', '-Wl,-pdb,$(LINK_PDBFILE)', '-o', '$@'], + mkcshlib=['$(CC)', '$(DSO_LDOPTS)', '-Wl,-pdb,$(LINK_PDBFILE)', '-o', '$@'], + ) else: linker = [ '$(LINKER)', diff --git a/build/win64/mozconfig.asan b/build/win64/mozconfig.asan index 0a7fd2b7a544..af6f1817e64e 100644 --- a/build/win64/mozconfig.asan +++ b/build/win64/mozconfig.asan @@ -7,6 +7,7 @@ if [ -d "$topsrcdir/clang" ]; then mk_export_correct_style LIB export LDFLAGS="clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib" + export MOZ_COPY_PDBS=1 export LLVM_SYMBOLIZER="$topsrcdir/clang/bin/llvm-symbolizer.exe" export MOZ_CLANG_RT_ASAN_LIB_PATH="${CLANG_LIB_DIR}/clang_rt.asan_dynamic-x86_64.dll" fi diff --git a/config/rules.mk b/config/rules.mk index 65e5110786f4..45dc538c9fd3 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -126,6 +126,9 @@ endif # MKSHLIB endif # FORCE_SHARED_LIB ifeq ($(OS_ARCH),WINNT) + +LINK_PDBFILE ?= $(basename $(@F)).pdb + ifndef GNU_CC # @@ -147,7 +150,6 @@ endif COMPILE_CFLAGS += $(COMPILE_PDB_FLAG) COMPILE_CXXFLAGS += $(COMPILE_PDB_FLAG) -LINK_PDBFILE ?= $(basename $(@F)).pdb ifdef MOZ_DEBUG CODFILE=$(basename $(@F)).cod endif @@ -161,6 +163,12 @@ MOZ_PROGRAM_LDFLAGS += -Wl,-rpath -Wl,@executable_path/Frameworks endif endif +ifeq ($(OS_ARCH),WINNT) +ifeq ($(CC_TYPE),clang) +MOZ_PROGRAM_LDFLAGS += -Wl,-pdb,$(dir $@)/$(LINK_PDBFILE) +endif +endif + ifeq ($(HOST_OS_ARCH),WINNT) HOST_PDBFILE=$(basename $(@F)).pdb HOST_PDB_FLAG ?= -Fd$(HOST_PDBFILE) @@ -819,13 +827,13 @@ DUMP_SYMS_TARGETS := endif endif -ifdef MOZ_CRASHREPORTER -$(foreach file,$(DUMP_SYMS_TARGETS),$(eval $(call syms_template,$(file),$(notdir $(file))_syms.track))) -else ifneq (,$(and $(LLVM_SYMBOLIZER),$(filter WINNT,$(OS_ARCH)),$(MOZ_AUTOMATION))) +ifdef MOZ_COPY_PDBS PDB_FILES = $(addsuffix .pdb,$(basename $(DUMP_SYMS_TARGETS))) PDB_DEST ?= $(FINAL_TARGET) PDB_TARGET = syms INSTALL_TARGETS += PDB +else ifdef MOZ_CRASHREPORTER +$(foreach file,$(DUMP_SYMS_TARGETS),$(eval $(call syms_template,$(file),$(notdir $(file))_syms.track))) endif cargo_host_flag := --target=$(RUST_HOST_TARGET) diff --git a/config/version_win.pl b/config/version_win.pl index 47c7dfe30e4a..a2ac99b8f1a2 100755 --- a/config/version_win.pl +++ b/config/version_win.pl @@ -274,12 +274,14 @@ print RCFILE qq{ my $versionlevel=0; my $insideversion=0; +my $has_manifest=0; if (open(RCINCLUDE, "<$rcinclude")) { print RCFILE "// From included resource $rcinclude\n"; # my $mstring=""; while () { + $has_manifest = 1 if /^1 (24|RT_MANIFEST) "$binary.manifest"/; $_ =~ s/\@MOZ_APP_DISPLAYNAME\@/$displayname/g; print RCFILE $_; # my $instr=$_; @@ -330,6 +332,10 @@ if (open(RCINCLUDE, "<$rcinclude")) my $fileflags = join(' | ', @fileflags); +print RCFILE qq{ +1 RT_MANIFEST "$binary.manifest" +} if !$has_manifest && $binary =~ /\.exe$/ && -e "$objdir/$binary.manifest"; + print RCFILE qq{ diff --git a/devtools/client/inspector/inspector.js b/devtools/client/inspector/inspector.js index ba9c8f65fcba..44df7dc9b994 100644 --- a/devtools/client/inspector/inspector.js +++ b/devtools/client/inspector/inspector.js @@ -876,7 +876,7 @@ Inspector.prototype = { let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar"); if (this.is3PaneModeEnabled && defaultTab === "ruleview") { - defaultTab = "computedview"; + defaultTab = "layoutview"; } // Append all side panels diff --git a/devtools/client/inspector/rules/test/browser.ini b/devtools/client/inspector/rules/test/browser.ini index 07d46b26e235..7bb3e6e30e53 100644 --- a/devtools/client/inspector/rules/test/browser.ini +++ b/devtools/client/inspector/rules/test/browser.ini @@ -181,6 +181,7 @@ skip-if = (os == "win" && debug) # bug 963492: win. [browser_rules_grid-toggle_02.js] [browser_rules_grid-toggle_03.js] [browser_rules_grid-toggle_04.js] +[browser_rules_grid-toggle_05.js] [browser_rules_gridline-names-autocomplete.js] [browser_rules_guessIndentation.js] [browser_rules_highlight-used-fonts.js] diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js index 34f7fb55a6f0..12d90c8239a5 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01.js @@ -29,7 +29,7 @@ add_task(async function() { const gridToggle = container.querySelector(".ruleview-grid"); info("Checking the initial state of the CSS grid toggle in the rule-view."); - ok(gridToggle, "Grid highlighter toggle is visible."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); @@ -41,6 +41,7 @@ add_task(async function() { info("Checking the CSS grid highlighter is created and toggle button is active in " + "the rule-view."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); @@ -52,6 +53,7 @@ add_task(async function() { info("Checking the CSS grid highlighter is not shown and toggle button is not active " + "in the rule-view."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js index f0f7324f27d5..51ad0599cf45 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_01b.js @@ -29,7 +29,7 @@ add_task(async function() { const gridToggle = container.querySelector(".ruleview-grid"); info("Checking the initial state of the CSS grid toggle in the rule-view."); - ok(gridToggle, "Grid highlighter toggle is visible."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); @@ -41,6 +41,7 @@ add_task(async function() { info("Checking the CSS grid highlighter is created and toggle button is active in " + "the rule-view."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); @@ -52,6 +53,7 @@ add_task(async function() { info("Checking the CSS grid highlighter is not shown and toggle button is not active " + "in the rule-view."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js index 58c6e6b37782..1093169a0942 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_02.js @@ -34,7 +34,9 @@ add_task(async function() { const overriddenGridToggle = overriddenContainer.querySelector(".ruleview-grid"); info("Checking the initial state of the CSS grid toggle in the rule-view."); - ok(gridToggle && overriddenGridToggle, "Grid highlighter toggles are visible."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); + ok(!overriddenGridToggle.hasAttribute("disabled"), + "Grid highlighter toggle is not disabled."); ok(!gridToggle.classList.contains("active") && !overriddenGridToggle.classList.contains("active"), "Grid highlighter toggle buttons are not active."); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js index df614c0ae3d3..1f1fabb6c10c 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_03.js @@ -35,7 +35,7 @@ add_task(async function() { info("Checking the state of the CSS grid toggle for the first grid container in the " + "rule-view."); - ok(gridToggle, "Grid highlighter toggle is visible."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); @@ -48,6 +48,7 @@ add_task(async function() { info("Checking the CSS grid highlighter is created and toggle button is active in " + "the rule-view."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); @@ -60,7 +61,7 @@ add_task(async function() { info("Checking the state of the CSS grid toggle for the second grid container in the " + "rule-view."); - ok(gridToggle, "Grid highlighter toggle is visible."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is still shown."); @@ -85,7 +86,7 @@ add_task(async function() { info("Checking the state of the CSS grid toggle for the first grid container in the " + "rule-view."); - ok(gridToggle, "Grid highlighter toggle is visible."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); }); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js index 8f45bfbd0ccd..f3a09b12a317 100644 --- a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_04.js @@ -29,7 +29,7 @@ add_task(async function() { const gridToggle = container.querySelector(".ruleview-grid"); info("Checking the initial state of the CSS grid toggle in the rule-view."); - ok(gridToggle, "Grid highlighter toggle is visible."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); @@ -41,6 +41,7 @@ add_task(async function() { info("Checking the CSS grid highlighter is created and toggle button is active in " + "the rule-view."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); @@ -52,6 +53,7 @@ add_task(async function() { info("Checking the CSS grid highlighter is not shown and toggle button is not active " + "in the rule-view."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); ok(!gridToggle.classList.contains("active"), "Grid highlighter toggle button is not active."); ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); diff --git a/devtools/client/inspector/rules/test/browser_rules_grid-toggle_05.js b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_05.js new file mode 100644 index 000000000000..6d1c5a80421d --- /dev/null +++ b/devtools/client/inspector/rules/test/browser_rules_grid-toggle_05.js @@ -0,0 +1,102 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that the grid toggle is hidden when the maximum number of grid highlighters +// have been reached. + +const TEST_URI = ` + +
+
cell1
+
cell2
+
+
+
cell1
+
cell2
+
+
+
cell1
+
cell2
+
+`; + +add_task(async function() { + await pushPref("devtools.gridinspector.maxHighlighters", 2); + await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + const { inspector, gridInspector } = await openLayoutView(); + const ruleView = selectRuleView(inspector); + const { document: doc } = gridInspector; + const { highlighters } = inspector; + + await selectNode("#grid1", inspector); + const gridList = doc.getElementById("grid-list"); + const checkbox2 = gridList.children[1].querySelector("input"); + const checkbox3 = gridList.children[2].querySelector("input"); + const container = getRuleViewProperty(ruleView, ".grid", "display").valueSpan; + const gridToggle = container.querySelector(".ruleview-grid"); + + info("Checking the initial state of the CSS grid toggle in the rule-view."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); + ok(!gridToggle.classList.contains("active"), + "Grid highlighter toggle button is not active."); + ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown."); + + info("Toggling ON the CSS grid highlighter for #grid2."); + let onHighlighterShown = highlighters.once("grid-highlighter-shown"); + checkbox2.click(); + await onHighlighterShown; + + info("Checking the CSS grid toggle for #grid1 is not disabled and not active."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); + ok(!gridToggle.classList.contains("active"), + "Grid highlighter toggle button is not active."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + + info("Toggling ON the CSS grid highlighter for #grid3."); + onHighlighterShown = highlighters.once("grid-highlighter-shown"); + checkbox3.click(); + await onHighlighterShown; + + info("Checking the CSS grid toggle for #grid1 is disabled."); + ok(gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is disabled."); + is(highlighters.gridHighlighters.size, 2, "CSS grid highlighters are shown."); + + info("Toggling OFF the CSS grid highlighter for #grid3."); + let onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); + checkbox3.click(); + await onHighlighterHidden; + + info("Checking the CSS grid toggle for #grid1 is not disabled and not active."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); + ok(!gridToggle.classList.contains("active"), + "Grid highlighter toggle button is not active."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); + + info("Toggling ON the CSS grid highlighter for #grid1 from the rule-view."); + onHighlighterShown = highlighters.once("grid-highlighter-shown"); + gridToggle.click(); + await onHighlighterShown; + + info("Checking the CSS grid toggle for #grid1 is not disabled."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); + ok(gridToggle.classList.contains("active"), "Grid highlighter toggle is active."); + is(highlighters.gridHighlighters.size, 2, "CSS grid highlighters are shown."); + + info("Toggling OFF the CSS grid highlighter for #grid1 from the rule-view."); + onHighlighterHidden = highlighters.once("grid-highlighter-hidden"); + gridToggle.click(); + await onHighlighterHidden; + + info("Checking the CSS grid toggle for #grid1 is not disabled and not active."); + ok(!gridToggle.hasAttribute("disabled"), "Grid highlighter toggle is not disabled."); + ok(!gridToggle.classList.contains("active"), + "Grid highlighter toggle button is not active."); + is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown."); +}); diff --git a/devtools/client/inspector/rules/views/text-property-editor.js b/devtools/client/inspector/rules/views/text-property-editor.js index ef5402067a31..d7ac2690cafb 100644 --- a/devtools/client/inspector/rules/views/text-property-editor.js +++ b/devtools/client/inspector/rules/views/text-property-editor.js @@ -76,6 +76,7 @@ function TextPropertyEditor(ruleEditor, property) { this.prop = property; this.prop.editor = this; this.browserWindow = this.doc.defaultView.top; + this._populatedComputed = false; this._hasPendingClick = false; this._clickedElementOptions = null; @@ -521,22 +522,22 @@ TextPropertyEditor.prototype = { } } + const nodeFront = this.ruleView.inspector.selection.nodeFront; + const flexToggle = this.valueSpan.querySelector(".ruleview-flex"); if (flexToggle) { flexToggle.setAttribute("title", l10n("rule.flexToggle.tooltip")); - if (this.ruleView.highlighters.flexboxHighlighterShown === - this.ruleView.inspector.selection.nodeFront) { - flexToggle.classList.add("active"); - } + flexToggle.classList.toggle("active", + this.ruleView.highlighters.flexboxHighlighterShown === nodeFront); } const gridToggle = this.valueSpan.querySelector(".ruleview-grid"); if (gridToggle) { gridToggle.setAttribute("title", l10n("rule.gridToggle.tooltip")); - if (this.ruleView.highlighters.gridHighlighters.has( - this.ruleView.inspector.selection.nodeFront)) { - gridToggle.classList.add("active"); - } + gridToggle.classList.toggle("active", + this.ruleView.highlighters.gridHighlighters.has(nodeFront)); + gridToggle.toggleAttribute("disabled", + !this.ruleView.highlighters.canGridHighlighterToggle(nodeFront)); } const shapeToggle = this.valueSpan.querySelector(".ruleview-shapeswatch"); diff --git a/devtools/client/inspector/shared/highlighters-overlay.js b/devtools/client/inspector/shared/highlighters-overlay.js index d6abca229821..3bc15cd6e9b9 100644 --- a/devtools/client/inspector/shared/highlighters-overlay.js +++ b/devtools/client/inspector/shared/highlighters-overlay.js @@ -28,6 +28,8 @@ class HighlightersOverlay { this.highlighterUtils = this.inspector.toolbox.highlighterUtils; this.store = this.inspector.store; this.telemetry = inspector.telemetry; + this.maxGridHighlighters = + Services.prefs.getIntPref("devtools.gridinspector.maxHighlighters"); // Collection of instantiated highlighter actors like FlexboxHighlighter, // ShapesHighlighter and GeometryEditorHighlighter. @@ -432,19 +434,16 @@ class HighlightersOverlay { * "rule" represents the rule view. */ async showGridHighlighter(node, options, trigger) { - const maxHighlighters = - Services.prefs.getIntPref("devtools.gridinspector.maxHighlighters"); - // When the grid highlighter has the given node, it is probably called with new // highlighting options, so skip any extra grid highlighter handling. if (!this.gridHighlighters.has(node)) { - if (maxHighlighters === 1) { + if (this.maxGridHighlighters === 1) { // Only one grid highlighter can be shown at a time. Hides any instantiated // grid highlighters. for (const nodeFront of this.gridHighlighters.keys()) { await this.hideGridHighlighter(nodeFront); } - } else if (this.gridHighlighters.size === maxHighlighters) { + } else if (this.gridHighlighters.size === this.maxGridHighlighters) { // The maximum number of grid highlighters shown have been reached. Don't show // any additional grid highlighters. return; @@ -496,8 +495,6 @@ class HighlightersOverlay { return; } - this._toggleRuleViewIcon(node, false, ".ruleview-grid"); - // Hide the highlighter and put it in the pool of extra grid highlighters // so that it can be reused. const highlighter = this.gridHighlighters.get(node); @@ -507,6 +504,8 @@ class HighlightersOverlay { this.state.grids.delete(node); this.gridHighlighters.delete(node); + this._toggleRuleViewIcon(node, false, ".ruleview-grid"); + // Emit the NodeFront of the grid container element that the grid highlighter was // hidden for. this.emit("grid-highlighter-hidden", node); @@ -790,12 +789,22 @@ class HighlightersOverlay { * The selector of the rule view icon to toggle. */ _toggleRuleViewIcon(node, active, selector) { - if (this.inspector.selection.nodeFront != node) { + const ruleViewEl = this.inspector.getPanel("ruleview").view.element; + + if (this.inspector.selection.nodeFront !== node) { + if (selector === ".ruleview-grid") { + for (const icon of ruleViewEl.querySelectorAll(selector)) { + if (this.canGridHighlighterToggle(this.inspector.selection.nodeFront)) { + icon.removeAttribute("disabled"); + } else { + icon.setAttribute("disabled", true); + } + } + } + return; } - const ruleViewEl = this.inspector.getPanel("ruleview").view.element; - for (const icon of ruleViewEl.querySelectorAll(selector)) { icon.classList.toggle("active", active); } diff --git a/devtools/client/inspector/test/browser_inspector_sidebarstate.js b/devtools/client/inspector/test/browser_inspector_sidebarstate.js index ab9657173848..ec7108275ac5 100644 --- a/devtools/client/inspector/test/browser_inspector_sidebarstate.js +++ b/devtools/client/inspector/test/browser_inspector_sidebarstate.js @@ -8,16 +8,6 @@ const TEST_URI = "data:text/html;charset=UTF-8," + const OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT; const TELEMETRY_DATA = [ - { - timestamp: null, - category: "devtools.main", - method: "tool_timer", - object: "computedview", - value: null, - extra: { - time_open: "" - } - }, { timestamp: null, category: "devtools.main", @@ -32,7 +22,17 @@ const TELEMETRY_DATA = [ timestamp: null, category: "devtools.main", method: "tool_timer", - object: "ruleview", + object: "fontinspector", + value: null, + extra: { + time_open: "" + } + }, + { + timestamp: null, + category: "devtools.main", + method: "tool_timer", + object: "computedview", value: null, extra: { time_open: "" @@ -50,18 +50,18 @@ add_task(async function() { let { inspector, toolbox } = await openInspectorForURL(TEST_URI); + info("Selecting font inspector."); + inspector.sidebar.select("fontinspector"); + + is(inspector.sidebar.getCurrentTabID(), "fontinspector", + "Font Inspector is selected"); + info("Selecting computed view."); inspector.sidebar.select("computedview"); is(inspector.sidebar.getCurrentTabID(), "computedview", "Computed View is selected"); - info("Selecting layout view."); - inspector.sidebar.select("layoutview"); - - is(inspector.sidebar.getCurrentTabID(), "layoutview", - "Layout View is selected"); - info("Closing inspector."); await toolbox.destroy(); @@ -73,8 +73,8 @@ add_task(async function() { await inspector.sidebar.once("select"); } - is(inspector.sidebar.getCurrentTabID(), "layoutview", - "Layout view is selected by default."); + is(inspector.sidebar.getCurrentTabID(), "computedview", + "Computed view is selected by default."); checkTelemetryResults(); }); diff --git a/devtools/client/preferences/devtools-client.js b/devtools/client/preferences/devtools-client.js index e437d22c207d..f3c322b82ec8 100644 --- a/devtools/client/preferences/devtools-client.js +++ b/devtools/client/preferences/devtools-client.js @@ -33,7 +33,7 @@ pref("devtools.command-button-noautohide.enabled", false); // Enable the Inspector pref("devtools.inspector.enabled", true); // What was the last active sidebar in the inspector -pref("devtools.inspector.activeSidebar", "ruleview"); +pref("devtools.inspector.activeSidebar", "layoutview"); pref("devtools.inspector.remote", false); // Enable the 3 pane mode in the inspector diff --git a/devtools/client/shared/test/browser_telemetry_sidebar.js b/devtools/client/shared/test/browser_telemetry_sidebar.js index 9041ea576d56..275dfec94038 100644 --- a/devtools/client/shared/test/browser_telemetry_sidebar.js +++ b/devtools/client/shared/test/browser_telemetry_sidebar.js @@ -19,7 +19,7 @@ const DATA = [ object: "inspector", value: null, extra: { - oldpanel: "computedview", + oldpanel: "layoutview", newpanel: "animationinspector" } }, @@ -156,8 +156,8 @@ function checkResults() { // here. checkTelemetry("DEVTOOLS_INSPECTOR_OPENED_COUNT", "", [1, 0, 0], "array"); checkTelemetry("DEVTOOLS_RULEVIEW_OPENED_COUNT", "", [1, 0, 0], "array"); - checkTelemetry("DEVTOOLS_COMPUTEDVIEW_OPENED_COUNT", "", [3, 0, 0], "array"); - checkTelemetry("DEVTOOLS_LAYOUTVIEW_OPENED_COUNT", "", [2, 0, 0], "array"); + checkTelemetry("DEVTOOLS_COMPUTEDVIEW_OPENED_COUNT", "", [2, 0, 0], "array"); + checkTelemetry("DEVTOOLS_LAYOUTVIEW_OPENED_COUNT", "", [3, 0, 0], "array"); checkTelemetry("DEVTOOLS_FONTINSPECTOR_OPENED_COUNT", "", [2, 0, 0], "array"); checkTelemetry("DEVTOOLS_COMPUTEDVIEW_TIME_ACTIVE_SECONDS", "", null, "hasentries"); checkTelemetry("DEVTOOLS_LAYOUTVIEW_TIME_ACTIVE_SECONDS", "", null, "hasentries"); diff --git a/devtools/client/themes/rules.css b/devtools/client/themes/rules.css index 64f2e6189d9d..1051a4509ebd 100644 --- a/devtools/client/themes/rules.css +++ b/devtools/client/themes/rules.css @@ -440,6 +440,11 @@ border-radius: 0; } +.ruleview-grid[disabled] { + cursor: default; + opacity: 0.5; +} + .ruleview-shape-point.active, .ruleview-shapeswatch.active + .ruleview-shape > .ruleview-shape-point:hover { background-color: var(--rule-highlight-background-color); diff --git a/dom/base/TimeoutExecutor.cpp b/dom/base/TimeoutExecutor.cpp index 036460251558..ee60094bb68d 100644 --- a/dom/base/TimeoutExecutor.cpp +++ b/dom/base/TimeoutExecutor.cpp @@ -56,22 +56,18 @@ TimeoutExecutor::ScheduleDelayed(const TimeStamp& aDeadline, nsresult rv = NS_OK; if (!mTimer) { - mTimer = NS_NewTimer(); + mTimer = NS_NewTimer(mOwner->EventTarget()); NS_ENSURE_TRUE(mTimer, NS_ERROR_OUT_OF_MEMORY); uint32_t earlyMicros = 0; MOZ_ALWAYS_SUCCEEDS(mTimer->GetAllowedEarlyFiringMicroseconds(&earlyMicros)); mAllowedEarlyFiringTime = TimeDuration::FromMicroseconds(earlyMicros); + } else { + // Always call Cancel() in case we are re-using a timer. + rv = mTimer->Cancel(); + NS_ENSURE_SUCCESS(rv, rv); } - // Always call Cancel() in case we are re-using a timer. Otherwise - // the subsequent SetTarget() may fail. - rv = mTimer->Cancel(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mTimer->SetTarget(mOwner->EventTarget()); - NS_ENSURE_SUCCESS(rv, rv); - // Calculate the delay based on the deadline and current time. If we have // a minimum delay set then clamp to that value. // diff --git a/dom/file/FileReader.cpp b/dom/file/FileReader.cpp index e6451bb48c74..a8d5b472078a 100644 --- a/dom/file/FileReader.cpp +++ b/dom/file/FileReader.cpp @@ -531,14 +531,13 @@ void FileReader::StartProgressEventTimer() { if (!mProgressNotifier) { - mProgressNotifier = NS_NewTimer(); + mProgressNotifier = NS_NewTimer(mTarget); } if (mProgressNotifier) { mProgressEventWasDelayed = false; mTimerIsActive = true; mProgressNotifier->Cancel(); - mProgressNotifier->SetTarget(mTarget); mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL, nsITimer::TYPE_ONE_SHOT); } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index ca4a7f204172..4396f1170296 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1592,7 +1592,7 @@ ContentParent::MarkAsDead() void ContentParent::OnChannelError() { - RefPtr content(this); + RefPtr kungFuDeathGrip(this); PContentParent::OnChannelError(); } @@ -1738,14 +1738,14 @@ struct DelayedDeleteContentParentTask : public Runnable { explicit DelayedDeleteContentParentTask(ContentParent* aObj) : Runnable("dom::DelayedDeleteContentParentTask") - , mObj(aObj) + , mKungFuDeathGrip(aObj) { } // No-op NS_IMETHOD Run() override { return NS_OK; } - RefPtr mObj; + RefPtr mKungFuDeathGrip; }; } // namespace diff --git a/dom/security/featurepolicy/FeaturePolicyUtils.cpp b/dom/security/featurepolicy/FeaturePolicyUtils.cpp index 27a3b18788c4..7c94600e4763 100644 --- a/dom/security/featurepolicy/FeaturePolicyUtils.cpp +++ b/dom/security/featurepolicy/FeaturePolicyUtils.cpp @@ -23,16 +23,16 @@ struct FeatureMap { */ static FeatureMap sSupportedFeatures[] = { { "autoplay", FeaturePolicyUtils::FeaturePolicyValue::eAll }, - { "camera", FeaturePolicyUtils::FeaturePolicyValue::eAll }, - { "encrypted-media", FeaturePolicyUtils::FeaturePolicyValue::eAll }, - { "fullscreen", FeaturePolicyUtils::FeaturePolicyValue::eAll }, - { "geolocation", FeaturePolicyUtils::FeaturePolicyValue::eAll }, - { "microphone", FeaturePolicyUtils::FeaturePolicyValue::eAll }, - { "midi", FeaturePolicyUtils::FeaturePolicyValue::eAll }, - { "payment", FeaturePolicyUtils::FeaturePolicyValue::eAll }, + { "camera", FeaturePolicyUtils::FeaturePolicyValue::eAll }, + { "encrypted-media", FeaturePolicyUtils::FeaturePolicyValue::eAll }, + { "fullscreen", FeaturePolicyUtils::FeaturePolicyValue::eAll }, + { "geolocation", FeaturePolicyUtils::FeaturePolicyValue::eAll }, + { "microphone", FeaturePolicyUtils::FeaturePolicyValue::eAll }, + { "midi", FeaturePolicyUtils::FeaturePolicyValue::eAll }, + { "payment", FeaturePolicyUtils::FeaturePolicyValue::eAll }, // TODO: not supported yet!!! - { "speaker", FeaturePolicyUtils::FeaturePolicyValue::eAll }, - { "vr", FeaturePolicyUtils::FeaturePolicyValue::eAll }, + { "speaker", FeaturePolicyUtils::FeaturePolicyValue::eAll }, + { "vr", FeaturePolicyUtils::FeaturePolicyValue::eAll }, }; /* static */ bool diff --git a/dom/xhr/XMLHttpRequestMainThread.cpp b/dom/xhr/XMLHttpRequestMainThread.cpp index 7cdd4ca86aae..556e180b1fed 100644 --- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -3142,13 +3142,13 @@ XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout, ErrorResult& aRv) } } -void -XMLHttpRequestMainThread::SetTimerEventTarget(nsITimer* aTimer) +nsIEventTarget* +XMLHttpRequestMainThread::GetTimerEventTarget() { if (nsCOMPtr global = GetOwnerGlobal()) { - nsCOMPtr target = global->EventTargetFor(TaskCategory::Other); - aTimer->SetTarget(target); + return global->EventTargetFor(TaskCategory::Other); } + return nullptr; } nsresult @@ -3183,8 +3183,7 @@ XMLHttpRequestMainThread::StartTimeoutTimer() } if (!mTimeoutTimer) { - mTimeoutTimer = NS_NewTimer(); - SetTimerEventTarget(mTimeoutTimer); + mTimeoutTimer = NS_NewTimer(GetTimerEventTarget()); } uint32_t elapsed = (uint32_t)((PR_Now() - mRequestSentTime) / PR_USEC_PER_MSEC); @@ -3601,8 +3600,7 @@ void XMLHttpRequestMainThread::StartProgressEventTimer() { if (!mProgressNotifier) { - mProgressNotifier = NS_NewTimer(); - SetTimerEventTarget(mProgressNotifier); + mProgressNotifier = NS_NewTimer(GetTimerEventTarget()); } if (mProgressNotifier) { mProgressTimerIsActive = true; @@ -3628,8 +3626,7 @@ XMLHttpRequestMainThread::MaybeStartSyncTimeoutTimer() return eErrorOrExpired; } - mSyncTimeoutTimer = NS_NewTimer(); - SetTimerEventTarget(mSyncTimeoutTimer); + mSyncTimeoutTimer = NS_NewTimer(GetTimerEventTarget()); if (!mSyncTimeoutTimer) { return eErrorOrExpired; } diff --git a/dom/xhr/XMLHttpRequestMainThread.h b/dom/xhr/XMLHttpRequestMainThread.h index 1db9c353ae66..b8fb689ed646 100644 --- a/dom/xhr/XMLHttpRequestMainThread.h +++ b/dom/xhr/XMLHttpRequestMainThread.h @@ -514,7 +514,7 @@ protected: nsresult OnRedirectVerifyCallback(nsresult result); - void SetTimerEventTarget(nsITimer* aTimer); + nsIEventTarget* GetTimerEventTarget(); nsresult DispatchToMainThread(already_AddRefed aRunnable); diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index d51732a78d7b..eae46bebd0c6 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -737,6 +737,7 @@ WebRenderMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport, helper.ReportTexture(aReport.vertex_data_textures, "vertex-data"); helper.ReportTexture(aReport.render_target_textures, "render-targets"); helper.ReportTexture(aReport.texture_cache_textures, "texture-cache"); + helper.ReportTexture(aReport.depth_target_textures, "depth-targets"); FinishAsyncMemoryReport(); }, diff --git a/gfx/webrender/res/brush.glsl b/gfx/webrender/res/brush.glsl index f8c7c387e373..d55e21c77351 100644 --- a/gfx/webrender/res/brush.glsl +++ b/gfx/webrender/res/brush.glsl @@ -22,6 +22,7 @@ void brush_vs( #define BRUSH_FLAG_SEGMENT_RELATIVE 2 #define BRUSH_FLAG_SEGMENT_REPEAT_X 4 #define BRUSH_FLAG_SEGMENT_REPEAT_Y 8 +#define BRUSH_FLAG_TEXEL_RECT 16 void main(void) { // Load the brush instance from vertex attributes. diff --git a/gfx/webrender/res/brush_image.glsl b/gfx/webrender/res/brush_image.glsl index 140c89b97204..768c6e10c9af 100644 --- a/gfx/webrender/res/brush_image.glsl +++ b/gfx/webrender/res/brush_image.glsl @@ -51,7 +51,7 @@ void brush_vs( mat4 transform, PictureTask pic_task, int brush_flags, - vec4 texel_rect + vec4 segment_data ) { ImageBrushData image_data = fetch_image_data(prim_address); @@ -76,19 +76,18 @@ void brush_vs( local_rect = segment_rect; stretch_size = local_rect.size; - // Note: Here we can assume that texels in device - // space map to local space, due to how border-image - // works. That assumption may not hold if this - // is used for other purposes in the future. if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) { - stretch_size.x = (texel_rect.z - texel_rect.x) / pic_task.common_data.device_pixel_scale; + stretch_size.x = (segment_data.z - segment_data.x); } if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) { - stretch_size.y = (texel_rect.w - texel_rect.y) / pic_task.common_data.device_pixel_scale; + stretch_size.y = (segment_data.w - segment_data.y); } - uv0 = res.uv_rect.p0 + texel_rect.xy; - uv1 = res.uv_rect.p0 + texel_rect.zw; + // If the extra data is a texel rect, modify the UVs. + if ((brush_flags & BRUSH_FLAG_TEXEL_RECT) != 0) { + uv0 = res.uv_rect.p0 + segment_data.xy; + uv1 = res.uv_rect.p0 + segment_data.zw; + } } vUv.z = res.layer; diff --git a/gfx/webrender/res/ps_split_composite.glsl b/gfx/webrender/res/ps_split_composite.glsl index 6ed1f926c0f2..d3db7e9713a8 100644 --- a/gfx/webrender/res/ps_split_composite.glsl +++ b/gfx/webrender/res/ps_split_composite.glsl @@ -10,7 +10,6 @@ flat varying vec4 vUvSampleBounds; #ifdef WR_VERTEX_SHADER struct SplitGeometry { vec2 local[4]; - RectWithSize local_rect; }; SplitGeometry fetch_split_geometry(int address) { @@ -18,7 +17,6 @@ SplitGeometry fetch_split_geometry(int address) { vec4 data0 = TEXEL_FETCH(sGpuCache, uv, 0, ivec2(0, 0)); vec4 data1 = TEXEL_FETCH(sGpuCache, uv, 0, ivec2(1, 0)); - vec4 data2 = TEXEL_FETCH(sGpuCache, uv, 0, ivec2(2, 0)); SplitGeometry geo; geo.local = vec2[4]( @@ -27,7 +25,6 @@ SplitGeometry fetch_split_geometry(int address) { data1.xy, data1.zw ); - geo.local_rect = RectWithSize(data2.xy, data2.zw); return geo; } @@ -98,7 +95,7 @@ void main(void) { max_uv - vec2(0.5) ) / texture_size.xyxy; - vec2 f = (local_pos - geometry.local_rect.p0) / geometry.local_rect.size; + vec2 f = (local_pos - ph.local_rect.p0) / ph.local_rect.size; f = bilerp( extra_data.st_tl, extra_data.st_tr, diff --git a/gfx/webrender/src/batch.rs b/gfx/webrender/src/batch.rs index 231fc43191a9..61066fa0a0f0 100644 --- a/gfx/webrender/src/batch.rs +++ b/gfx/webrender/src/batch.rs @@ -20,12 +20,13 @@ use plane_split::{BspSplitter, Clipper, Polygon, Splitter}; use prim_store::{BrushKind, BrushPrimitive, BrushSegmentTaskId, DeferredResolve}; use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveIndex}; use prim_store::{VisibleGradientTile, PrimitiveInstance}; -use prim_store::{BorderSource, Primitive, PrimitiveDetails}; +use prim_store::{BrushSegment, BorderSource, Primitive, PrimitiveDetails}; use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskTree}; use renderer::{BlendMode, ImageBufferKind, ShaderColorMode}; use renderer::BLOCKS_PER_UV_RECT; use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache, ImageProperties}; use scene::FilterOpHelpers; +use smallvec::SmallVec; use std::{f32, i32, usize}; use tiling::{RenderTargetContext}; use util::{MatrixHelpers, TransformedRectKind}; @@ -419,6 +420,15 @@ impl AlphaBatchContainer { } } +/// Each segment can optionally specify a per-segment +/// texture set and one user data field. +#[derive(Debug, Copy, Clone)] +struct SegmentInstanceData { + textures: BatchTextures, + user_data: i32, + is_opaque_override: Option, +} + /// Encapsulates the logic of building batches for items that are blended. pub struct AlphaBatchBuilder { pub batch_list: BatchList, @@ -551,7 +561,6 @@ impl AlphaBatchBuilder { let gpu_blocks = [ [local_points[0].x, local_points[0].y, local_points[1].x, local_points[1].y].into(), [local_points[2].x, local_points[2].y, local_points[3].x, local_points[3].y].into(), - pic_metadata.local_rect.into(), ]; let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks); @@ -1103,37 +1112,29 @@ impl AlphaBatchBuilder { ); } _ => { - // TODO(gw): As an interim step, just return one value for the - // per-segment user data. In the future, this method - // will be expanded to optionally return a list of - // (BatchTextures, user_data) per segment, which will - // allow a different texture / render task to be used - // per segment. - if let Some((batch_kind, textures, user_data, segment_user_data)) = brush.get_batch_params( - ctx.resource_cache, - gpu_cache, - deferred_resolves, - ctx.prim_store.chase_id == Some(prim_instance.prim_index), + if let Some(params) = brush.get_batch_params( + ctx.resource_cache, + gpu_cache, + deferred_resolves, + ctx.prim_store.chase_id == Some(prim_instance.prim_index), ) { - let prim_header_index = prim_headers.push(&prim_header, user_data); + let prim_header_index = prim_headers.push(&prim_header, params.prim_user_data); if cfg!(debug_assertions) && ctx.prim_store.chase_id == Some(prim_instance.prim_index) { println!("\t{:?} {:?}, task relative bounds {:?}", - batch_kind, prim_header_index, bounding_rect); + params.batch_kind, prim_header_index, bounding_rect); } self.add_brush_to_batch( brush, + ¶ms, prim_instance, - batch_kind, specified_blend_mode, non_segmented_blend_mode, - textures, prim_header_index, clip_task_address, bounding_rect, transform_kind, render_tasks, - segment_user_data, ); } } @@ -1270,80 +1271,147 @@ impl AlphaBatchBuilder { ); } + /// Add a single segment instance to a batch. + fn add_segment_to_batch( + &mut self, + segment: &BrushSegment, + segment_data: &SegmentInstanceData, + segment_index: i32, + batch_kind: BrushBatchKind, + prim_instance: &PrimitiveInstance, + prim_header_index: PrimitiveHeaderIndex, + alpha_blend_mode: BlendMode, + bounding_rect: &WorldRect, + transform_kind: TransformedRectKind, + render_tasks: &RenderTaskTree, + ) { + let clip_task_address = match segment.clip_task_id { + BrushSegmentTaskId::RenderTaskId(id) => + render_tasks.get_task_address(id), + BrushSegmentTaskId::Opaque => OPAQUE_TASK_ADDRESS, + BrushSegmentTaskId::Empty => return, + }; + + // If the segment instance data specifies opacity for that + // segment, use it. Otherwise, assume opacity for the segment + // from the overall primitive opacity. + let is_segment_opaque = match segment_data.is_opaque_override { + Some(is_opaque) => is_opaque, + None => prim_instance.opacity.is_opaque, + }; + + let is_inner = segment.edge_flags.is_empty(); + let needs_blending = !is_segment_opaque || + segment.clip_task_id.needs_blending() || + (!is_inner && transform_kind == TransformedRectKind::Complex); + + let instance = PrimitiveInstanceData::from(BrushInstance { + segment_index, + edge_flags: segment.edge_flags, + clip_task_address, + brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION | segment.brush_flags, + prim_header_index, + user_data: segment_data.user_data, + }); + + let batch_key = BatchKey { + blend_mode: if needs_blending { alpha_blend_mode } else { BlendMode::None }, + kind: BatchKind::Brush(batch_kind), + textures: segment_data.textures, + }; + + self.batch_list.push_single_instance( + batch_key, + bounding_rect, + prim_instance.prim_index, + instance, + ); + } + + /// Add any segment(s) from a brush to batches. fn add_brush_to_batch( &mut self, brush: &BrushPrimitive, + params: &BrushBatchParameters, prim_instance: &PrimitiveInstance, - batch_kind: BrushBatchKind, alpha_blend_mode: BlendMode, non_segmented_blend_mode: BlendMode, - textures: BatchTextures, prim_header_index: PrimitiveHeaderIndex, clip_task_address: RenderTaskAddress, bounding_rect: &WorldRect, transform_kind: TransformedRectKind, render_tasks: &RenderTaskTree, - user_data: i32, ) { - let base_instance = BrushInstance { - prim_header_index, - clip_task_address, - segment_index: 0, - edge_flags: EdgeAaSegmentMask::all(), - brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION, - user_data, - }; - - match brush.segment_desc { - Some(ref segment_desc) => { - for (i, segment) in segment_desc.segments.iter().enumerate() { - let is_inner = segment.edge_flags.is_empty(); - let needs_blending = !prim_instance.opacity.is_opaque || - segment.clip_task_id.needs_blending() || - (!is_inner && transform_kind == TransformedRectKind::Complex); - - let clip_task_address = match segment.clip_task_id { - BrushSegmentTaskId::RenderTaskId(id) => - render_tasks.get_task_address(id), - BrushSegmentTaskId::Opaque => OPAQUE_TASK_ADDRESS, - BrushSegmentTaskId::Empty => continue, - }; - - let instance = PrimitiveInstanceData::from(BrushInstance { - segment_index: i as i32, - edge_flags: segment.edge_flags, - clip_task_address, - brush_flags: base_instance.brush_flags | segment.brush_flags, - ..base_instance - }); - - let batch_key = BatchKey { - blend_mode: if needs_blending { alpha_blend_mode } else { BlendMode::None }, - kind: BatchKind::Brush(batch_kind), - textures, - }; - - self.batch_list.push_single_instance( - batch_key, + match (&brush.segment_desc, ¶ms.segment_data) { + (Some(ref segment_desc), SegmentDataKind::Instanced(ref segment_data)) => { + // In this case, we have both a list of segments, and a list of + // per-segment instance data. Zip them together to build batches. + debug_assert_eq!(segment_desc.segments.len(), segment_data.len()); + for (segment_index, (segment, segment_data)) in segment_desc.segments + .iter() + .zip(segment_data.iter()) + .enumerate() { + self.add_segment_to_batch( + segment, + segment_data, + segment_index as i32, + params.batch_kind, + prim_instance, + prim_header_index, + alpha_blend_mode, bounding_rect, - prim_instance.prim_index, - instance, + transform_kind, + render_tasks, ); } } - None => { + (Some(ref segment_desc), SegmentDataKind::Shared(ref segment_data)) => { + // A list of segments, but the per-segment data is common + // between all segments. + for (segment_index, segment) in segment_desc.segments + .iter() + .enumerate() { + self.add_segment_to_batch( + segment, + segment_data, + segment_index as i32, + params.batch_kind, + prim_instance, + prim_header_index, + alpha_blend_mode, + bounding_rect, + transform_kind, + render_tasks, + ); + } + } + (None, SegmentDataKind::Shared(ref segment_data)) => { + // No segments, and thus no per-segment instance data. let batch_key = BatchKey { blend_mode: non_segmented_blend_mode, - kind: BatchKind::Brush(batch_kind), - textures, + kind: BatchKind::Brush(params.batch_kind), + textures: segment_data.textures, }; + let instance = PrimitiveInstanceData::from(BrushInstance { + segment_index: 0, + edge_flags: EdgeAaSegmentMask::all(), + clip_task_address, + brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION, + prim_header_index, + user_data: segment_data.user_data, + }); self.batch_list.push_single_instance( batch_key, bounding_rect, prim_instance.prim_index, - PrimitiveInstanceData::from(base_instance), + PrimitiveInstanceData::from(instance), ); } + (None, SegmentDataKind::Instanced(..)) => { + // We should never hit the case where there are no segments, + // but a list of segment instance data. + unreachable!(); + } } } } @@ -1426,6 +1494,58 @@ fn get_image_tile_params( } } +/// Either a single texture / user data for all segments, +/// or a list of one per segment. +enum SegmentDataKind { + Shared(SegmentInstanceData), + Instanced(SmallVec<[SegmentInstanceData; 8]>), +} + +/// The parameters that are specific to a kind of brush, +/// used by the common method to add a brush to batches. +struct BrushBatchParameters { + batch_kind: BrushBatchKind, + prim_user_data: [i32; 3], + segment_data: SegmentDataKind, +} + +impl BrushBatchParameters { + /// This brush instance has a list of per-segment + /// instance data. + fn instanced( + batch_kind: BrushBatchKind, + prim_user_data: [i32; 3], + segment_data: SmallVec<[SegmentInstanceData; 8]>, + ) -> Self { + BrushBatchParameters { + batch_kind, + prim_user_data, + segment_data: SegmentDataKind::Instanced(segment_data), + } + } + + /// This brush instance shares the per-segment data + /// across all segments. + fn shared( + batch_kind: BrushBatchKind, + textures: BatchTextures, + prim_user_data: [i32; 3], + segment_user_data: i32, + ) -> Self { + BrushBatchParameters { + batch_kind, + prim_user_data, + segment_data: SegmentDataKind::Shared( + SegmentInstanceData { + textures, + user_data: segment_user_data, + is_opaque_override: None, + } + ), + } + } +} + impl BrushPrimitive { fn get_batch_params( &self, @@ -1433,7 +1553,7 @@ impl BrushPrimitive { gpu_cache: &mut GpuCache, deferred_resolves: &mut Vec, is_chased: bool, - ) -> Option<(BrushBatchKind, BatchTextures, [i32; 3], i32)> { + ) -> Option { match self.kind { BrushKind::Image { request, ref source, .. } => { let cache_item = match *source { @@ -1463,7 +1583,7 @@ impl BrushPrimitive { } else { let textures = BatchTextures::color(cache_item.texture_id); - Some(( + Some(BrushBatchParameters::shared( BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), textures, [ @@ -1478,7 +1598,7 @@ impl BrushPrimitive { BrushKind::LineDecoration { ref handle, style, .. } => { match style { LineStyle::Solid => { - Some(( + Some(BrushBatchParameters::shared( BrushBatchKind::Solid, BatchTextures::no_texture(), [0; 3], @@ -1492,7 +1612,7 @@ impl BrushPrimitive { .get_cached_render_task(handle.as_ref().unwrap()); let cache_item = resource_cache.get_texture_cache_item(&rt_cache_entry.handle); let textures = BatchTextures::color(cache_item.texture_id); - Some(( + Some(BrushBatchParameters::shared( BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), textures, [ @@ -1506,48 +1626,69 @@ impl BrushPrimitive { } } BrushKind::Border { ref source, .. } => { - let cache_item = match *source { + match *source { BorderSource::Image(request) => { - resolve_image( + let cache_item = resolve_image( request, resource_cache, gpu_cache, deferred_resolves, - ) - } - BorderSource::Border { ref handle, .. } => { - let rt_handle = match *handle { - Some(ref handle) => handle, - None => return None, - }; - let rt_cache_entry = resource_cache - .get_cached_render_task(rt_handle); - resource_cache.get_texture_cache_item(&rt_cache_entry.handle) - } - }; + ); - if cache_item.texture_id == TextureSource::Invalid { - None - } else { - let textures = BatchTextures::color(cache_item.texture_id); + if cache_item.texture_id == TextureSource::Invalid { + return None; + } - Some(( - BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), - textures, - [ - ShaderColorMode::Image as i32, - RasterizationSpace::Local as i32, - 0, - ], - cache_item.uv_rect_handle.as_int(gpu_cache), - )) + let textures = BatchTextures::color(cache_item.texture_id); + + Some(BrushBatchParameters::shared( + BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), + textures, + [ + ShaderColorMode::Image as i32, + RasterizationSpace::Local as i32, + 0, + ], + cache_item.uv_rect_handle.as_int(gpu_cache), + )) + } + BorderSource::Border { ref segments, .. } => { + let mut segment_data = SmallVec::new(); + + // Collect the segment instance data from each render + // task for each valid edge / corner of the border. + + for segment in segments { + let rt_cache_entry = resource_cache + .get_cached_render_task(segment.handle.as_ref().unwrap()); + let cache_item = resource_cache + .get_texture_cache_item(&rt_cache_entry.handle); + segment_data.push( + SegmentInstanceData { + textures: BatchTextures::color(cache_item.texture_id), + user_data: cache_item.uv_rect_handle.as_int(gpu_cache), + is_opaque_override: Some(segment.is_opaque), + } + ); + } + + Some(BrushBatchParameters::instanced( + BrushBatchKind::Image(ImageBufferKind::Texture2DArray), + [ + ShaderColorMode::Image as i32, + RasterizationSpace::Local as i32, + 0, + ], + segment_data, + )) + } } } BrushKind::Picture { .. } => { panic!("bug: get_batch_key is handled at higher level for pictures"); } BrushKind::Solid { .. } => { - Some(( + Some(BrushBatchParameters::shared( BrushBatchKind::Solid, BatchTextures::no_texture(), [0; 3], @@ -1555,7 +1696,7 @@ impl BrushPrimitive { )) } BrushKind::Clear => { - Some(( + Some(BrushBatchParameters::shared( BrushBatchKind::Solid, BatchTextures::no_texture(), [0; 3], @@ -1563,7 +1704,7 @@ impl BrushPrimitive { )) } BrushKind::RadialGradient { ref stops_handle, .. } => { - Some(( + Some(BrushBatchParameters::shared( BrushBatchKind::RadialGradient, BatchTextures::no_texture(), [ @@ -1575,7 +1716,7 @@ impl BrushPrimitive { )) } BrushKind::LinearGradient { ref stops_handle, .. } => { - Some(( + Some(BrushBatchParameters::shared( BrushBatchKind::LinearGradient, BatchTextures::no_texture(), [ @@ -1631,7 +1772,7 @@ impl BrushPrimitive { color_space, ); - Some(( + Some(BrushBatchParameters::shared( kind, textures, [ diff --git a/gfx/webrender/src/border.rs b/gfx/webrender/src/border.rs index 62eaf8bb9aa4..0733100439ad 100644 --- a/gfx/webrender/src/border.rs +++ b/gfx/webrender/src/border.rs @@ -4,15 +4,15 @@ use api::{BorderRadius, BorderSide, BorderStyle, ColorF, ColorU, DeviceRect, DeviceSize}; use api::{LayoutSideOffsets, LayoutSizeAu, LayoutPrimitiveInfo, LayoutToDeviceScale}; -use api::{DeviceVector2D, DevicePoint, DeviceIntSize, LayoutRect, LayoutSize, NormalBorder}; +use api::{DeviceVector2D, DevicePoint, LayoutRect, LayoutSize, NormalBorder, DeviceIntSize}; use api::{AuHelpers}; -use app_units::Au; use ellipse::Ellipse; -use euclid::SideOffsets2D; use display_list_flattener::DisplayListFlattener; use gpu_types::{BorderInstance, BorderSegment, BrushFlags}; -use prim_store::{BrushKind, BrushPrimitive, BrushSegment, BrushSegmentVec}; -use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain}; +use prim_store::{BorderSegmentInfo, BrushKind, BrushPrimitive, BrushSegment, BrushSegmentVec}; +use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain, BrushSegmentDescriptor}; +use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind}; +use smallvec::SmallVec; use util::{lerp, RectHelpers}; // Using 2048 as the maximum radius in device space before which we @@ -93,38 +93,30 @@ impl From for BorderSideAu { } } +/// Cache key that uniquely identifies a border +/// edge in the render task cache. #[derive(Clone, Debug, Hash, PartialEq, Eq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct BorderCacheKey { - pub left: BorderSideAu, - pub right: BorderSideAu, - pub top: BorderSideAu, - pub bottom: BorderSideAu, - pub radius: BorderRadiusAu, - pub widths: SideOffsets2D, +pub struct BorderEdgeCacheKey { + pub side: BorderSideAu, + pub size: LayoutSizeAu, pub do_aa: bool, - pub scale: Au, + pub segment: BorderSegment, } -impl BorderCacheKey { - pub fn new(border: &NormalBorder, widths: &LayoutSideOffsets) -> Self { - BorderCacheKey { - left: border.left.into(), - top: border.top.into(), - right: border.right.into(), - bottom: border.bottom.into(), - widths: SideOffsets2D::new( - Au::from_f32_px(widths.top), - Au::from_f32_px(widths.right), - Au::from_f32_px(widths.bottom), - Au::from_f32_px(widths.left), - ), - radius: border.radius.into(), - do_aa: border.do_aa, - scale: Au(0), - } - } +/// Cache key that uniquely identifies a border +/// corner in the render task cache. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct BorderCornerCacheKey { + pub widths: LayoutSizeAu, + pub radius: LayoutSizeAu, + pub side0: BorderSideAu, + pub side1: BorderSideAu, + pub segment: BorderSegment, + pub do_aa: bool, } pub fn ensure_no_corner_overlap( @@ -177,15 +169,16 @@ impl<'a> DisplayListFlattener<'a> { &mut self, info: &LayoutPrimitiveInfo, border: &NormalBorder, - widths: &LayoutSideOffsets, + widths: LayoutSideOffsets, clip_and_scroll: ScrollNodeAndClipChain, ) { let mut border = *border; ensure_no_corner_overlap(&mut border.radius, &info.rect); - let prim = BrushPrimitive::new( - BrushKind::new_border(border, *widths), - None, + let prim = create_normal_border_prim( + &info.rect, + border, + widths, ); self.add_primitive( @@ -199,6 +192,7 @@ impl<'a> DisplayListFlattener<'a> { pub trait BorderSideHelpers { fn border_color(&self, is_inner_border: bool) -> ColorF; + fn is_opaque(&self) -> bool; } impl BorderSideHelpers for BorderSide { @@ -227,6 +221,11 @@ impl BorderSideHelpers for BorderSide { let black = if lighter { 0.7 } else { 0.3 }; ColorF::new(black, black, black, self.color.a) } + + /// Returns true if all pixels in this border style are opaque. + fn is_opaque(&self) -> bool { + self.color.a >= 1.0 && self.style.is_opaque() + } } /// The kind of border corner clip. @@ -517,74 +516,6 @@ impl DotInfo { } } -#[derive(Debug)] -pub struct BorderSegmentInfo { - task_rect: DeviceRect, - segment: BorderSegment, - radius: DeviceSize, - widths: DeviceSize, -} - -bitflags! { - /// Whether we depend on the available size for the border (effectively in - /// the local rect of the primitive), and in which direction. - /// - /// Note that this relies on the corners being only dependent on the border - /// widths and radius. - /// - /// This is not just a single boolean to allow instance caching for border - /// boxes where one of the directions differ but not the one on the affected - /// border is. - /// - /// This allows sharing instances for stuff like paragraphs of different - /// heights separated by horizontal borders. - pub struct AvailableSizeDependence : u8 { - /// There's a dependence on the vertical direction, that is, at least - /// one of the right or left edges is dashed or dotted. - const VERTICAL = 1 << 0; - /// Same but for the horizontal direction. - const HORIZONTAL = 1 << 1; - } -} - -/// This is the data that describes a single border with (up to) four sides and -/// four corners. -/// -/// This object gets created for each border primitive at least once. Note, -/// however, that the instances this produces via `build_instances()` can and -/// will be shared by multiple borders, as long as they share the same cache -/// key. -/// -/// Segments, however, also get build once per primitive. -/// -/// So the important invariant to preserve when going through this code is that -/// the result of `build_instances()` would remain invariant for a given cache -/// key. -/// -/// That means, then, that `border_segments` can't depend at all on something -/// that isn't on the key like the available_size, while the brush segments can -/// (and will, since we skip painting empty segments caused by things like edges -/// getting constrained by huge border-radius). -/// -/// Note that the cache key is not only `BorderCacheKey`, but also a -/// `size` from `RenderTaskCacheKey`, which will always be zero unless -/// `available_size_dependence` is non-empty, which is effectively just dashed -/// and dotted borders for now, since the spacing between the dash and dots -/// changes depending on that size. -#[derive(Debug)] -pub struct BorderRenderTaskInfo { - pub border_segments: Vec, - pub size: DeviceIntSize, - pub available_size_dependence: AvailableSizeDependence, - do_aa: bool, -} - -#[derive(PartialEq, Eq)] -enum DependsOnAvailableSize { - No, - Yes, -} - /// Information needed to place and draw a border edge. #[derive(Debug)] struct EdgeInfo { @@ -592,24 +523,20 @@ struct EdgeInfo { local_offset: f32, /// Size of the edge in local space. local_size: f32, - /// Size in device pixels needed in the render task. - device_size: f32, - /// Whether this edge depends on the available size. - depends_on_available_size: bool, + /// Local stretch size for this edge (repeat past this). + stretch_size: f32, } impl EdgeInfo { fn new( local_offset: f32, local_size: f32, - device_size: f32, - depends_on_avail_size: DependsOnAvailableSize, + stretch_size: f32, ) -> Self { Self { local_offset, local_size, - device_size, - depends_on_available_size: depends_on_avail_size == DependsOnAvailableSize::Yes, + stretch_size, } } } @@ -645,11 +572,10 @@ fn get_edge_info( style: BorderStyle, side_width: f32, avail_size: f32, - scale: f32, ) -> EdgeInfo { // To avoid division by zero below. - if side_width <= 0.0 { - return EdgeInfo::new(0.0, 0.0, 0.0, DependsOnAvailableSize::No); + if side_width <= 0.0 || avail_size <= 0.0 { + return EdgeInfo::new(0.0, 0.0, 0.0); } match style { @@ -657,436 +583,233 @@ fn get_edge_info( // Basically, two times the dash size. let (half_dash, _num_half_dashes) = compute_half_dash(side_width, avail_size); - let device_size = (2.0 * 2.0 * half_dash * scale).round(); - EdgeInfo::new(0., avail_size, device_size, DependsOnAvailableSize::Yes) + let stretch_size = 2.0 * 2.0 * half_dash; + EdgeInfo::new(0., avail_size, stretch_size) } BorderStyle::Dotted => { let dot_and_space_size = 2.0 * side_width; if avail_size < dot_and_space_size * 0.75 { - return EdgeInfo::new(0.0, 0.0, 0.0, DependsOnAvailableSize::Yes); + return EdgeInfo::new(0.0, 0.0, 0.0); } let approx_dot_count = avail_size / dot_and_space_size; let dot_count = approx_dot_count.floor().max(1.0); let used_size = dot_count * dot_and_space_size; let extra_space = avail_size - used_size; - let device_size = dot_and_space_size * scale; + let stretch_size = dot_and_space_size; let offset = (extra_space * 0.5).round(); - EdgeInfo::new(offset, used_size, device_size, DependsOnAvailableSize::Yes) + EdgeInfo::new(offset, used_size, stretch_size) } _ => { - EdgeInfo::new(0.0, avail_size, 8.0, DependsOnAvailableSize::No) + EdgeInfo::new(0.0, avail_size, 8.0) } } } -impl BorderRenderTaskInfo { - pub fn new( - rect: &LayoutRect, - border: &NormalBorder, - widths: &LayoutSideOffsets, - scale: LayoutToDeviceScale, - brush_segments: &mut BrushSegmentVec, - ) -> Option { - let mut border_segments = Vec::new(); - - let dp_width_top = (widths.top * scale.0).ceil(); - let dp_width_bottom = (widths.bottom * scale.0).ceil(); - let dp_width_left = (widths.left * scale.0).ceil(); - let dp_width_right = (widths.right * scale.0).ceil(); - - let dp_corner_tl = (border.radius.top_left * scale).ceil(); - let dp_corner_tr = (border.radius.top_right * scale).ceil(); - let dp_corner_bl = (border.radius.bottom_left * scale).ceil(); - let dp_corner_br = (border.radius.bottom_right * scale).ceil(); - - let dp_size_tl = DeviceSize::new( - dp_corner_tl.width.max(dp_width_left), - dp_corner_tl.height.max(dp_width_top), - ); - let dp_size_tr = DeviceSize::new( - dp_corner_tr.width.max(dp_width_right), - dp_corner_tr.height.max(dp_width_top), - ); - let dp_size_br = DeviceSize::new( - dp_corner_br.width.max(dp_width_right), - dp_corner_br.height.max(dp_width_bottom), - ); - let dp_size_bl = DeviceSize::new( - dp_corner_bl.width.max(dp_width_left), - dp_corner_bl.height.max(dp_width_bottom), - ); - - let local_size_tl = LayoutSize::new( - border.radius.top_left.width.max(widths.left), - border.radius.top_left.height.max(widths.top), - ); - let local_size_tr = LayoutSize::new( - border.radius.top_right.width.max(widths.right), - border.radius.top_right.height.max(widths.top), - ); - let local_size_br = LayoutSize::new( - border.radius.bottom_right.width.max(widths.right), - border.radius.bottom_right.height.max(widths.bottom), - ); - let local_size_bl = LayoutSize::new( - border.radius.bottom_left.width.max(widths.left), - border.radius.bottom_left.height.max(widths.bottom), - ); - - let top_edge_info = get_edge_info( - border.top.style, - widths.top, - rect.size.width - local_size_tl.width - local_size_tr.width, - scale.0, - ); - let bottom_edge_info = get_edge_info( - border.bottom.style, - widths.bottom, - rect.size.width - local_size_bl.width - local_size_br.width, - scale.0, - ); - let inner_width = top_edge_info.device_size.max(bottom_edge_info.device_size).ceil(); - - let left_edge_info = get_edge_info( - border.left.style, - widths.left, - rect.size.height - local_size_tl.height - local_size_bl.height, - scale.0, - ); - let right_edge_info = get_edge_info( - border.right.style, - widths.right, - rect.size.height - local_size_tr.height - local_size_br.height, - scale.0, - ); - - let inner_height = left_edge_info.device_size.max(right_edge_info.device_size).ceil(); - - let size = DeviceSize::new( - dp_size_tl.width.max(dp_size_bl.width) + inner_width + dp_size_tr.width.max(dp_size_br.width), - dp_size_tl.height.max(dp_size_tr.height) + inner_height + dp_size_bl.height.max(dp_size_br.height), - ); - - if size.width == 0.0 || size.height == 0.0 { - return None; - } - - let mut size_dependence = AvailableSizeDependence::empty(); - if top_edge_info.depends_on_available_size || - bottom_edge_info.depends_on_available_size - { - size_dependence.insert(AvailableSizeDependence::HORIZONTAL); - } - - if left_edge_info.depends_on_available_size || - right_edge_info.depends_on_available_size - { - size_dependence.insert(AvailableSizeDependence::VERTICAL); - } - - add_edge_segment( - LayoutRect::from_floats( - rect.origin.x, - rect.origin.y + local_size_tl.height + left_edge_info.local_offset, - rect.origin.x + widths.left, - rect.origin.y + local_size_tl.height + left_edge_info.local_offset + left_edge_info.local_size, - ), - DeviceRect::from_floats( - 0.0, - dp_size_tl.height, - dp_width_left, - dp_size_tl.height + left_edge_info.device_size, - ), - &border.left, - BorderSegment::Left, - EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT, - &mut border_segments, - BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y, - brush_segments, - ); - - add_edge_segment( - LayoutRect::from_floats( - rect.origin.x + local_size_tl.width + top_edge_info.local_offset, - rect.origin.y, - rect.origin.x + local_size_tl.width + top_edge_info.local_offset + top_edge_info.local_size, - rect.origin.y + widths.top, - ), - DeviceRect::from_floats( - dp_size_tl.width, - 0.0, - dp_size_tl.width + top_edge_info.device_size, - dp_width_top, - ), - &border.top, - BorderSegment::Top, - EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM, - &mut border_segments, - BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X, - brush_segments, - ); - - add_edge_segment( - LayoutRect::from_floats( - rect.origin.x + rect.size.width - widths.right, - rect.origin.y + local_size_tr.height + right_edge_info.local_offset, - rect.origin.x + rect.size.width, - rect.origin.y + local_size_tr.height + right_edge_info.local_offset + right_edge_info.local_size, - ), - DeviceRect::from_floats( - size.width - dp_width_right, - dp_size_tr.height, - size.width, - dp_size_tr.height + right_edge_info.device_size, - ), - &border.right, - BorderSegment::Right, - EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT, - &mut border_segments, - BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y, - brush_segments, - ); - - add_edge_segment( - LayoutRect::from_floats( - rect.origin.x + local_size_bl.width + bottom_edge_info.local_offset, - rect.origin.y + rect.size.height - widths.bottom, - rect.origin.x + local_size_bl.width + bottom_edge_info.local_offset + bottom_edge_info.local_size, - rect.origin.y + rect.size.height, - ), - DeviceRect::from_floats( - dp_size_bl.width, - size.height - dp_width_bottom, - dp_size_bl.width + bottom_edge_info.device_size, - size.height, - ), - &border.bottom, - BorderSegment::Bottom, - EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP, - &mut border_segments, - BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X, - brush_segments, - ); - - add_corner_segment( - LayoutRect::from_floats( - rect.origin.x, - rect.origin.y, - rect.origin.x + local_size_tl.width, - rect.origin.y + local_size_tl.height, - ), - DeviceRect::from_floats( - 0.0, - 0.0, - dp_size_tl.width, - dp_size_tl.height, - ), - &border.left, - &border.top, - DeviceSize::new(dp_width_left, dp_width_top), - dp_corner_tl, - BorderSegment::TopLeft, - EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT, - &mut border_segments, - brush_segments, - ); - - add_corner_segment( - LayoutRect::from_floats( - rect.origin.x + rect.size.width - local_size_tr.width, - rect.origin.y, - rect.origin.x + rect.size.width, - rect.origin.y + local_size_tr.height, - ), - DeviceRect::from_floats( - size.width - dp_size_tr.width, - 0.0, - size.width, - dp_size_tr.height, - ), - &border.top, - &border.right, - DeviceSize::new(dp_width_right, dp_width_top), - dp_corner_tr, - BorderSegment::TopRight, - EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT, - &mut border_segments, - brush_segments, - ); - - add_corner_segment( - LayoutRect::from_floats( - rect.origin.x + rect.size.width - local_size_br.width, - rect.origin.y + rect.size.height - local_size_br.height, - rect.origin.x + rect.size.width, - rect.origin.y + rect.size.height, - ), - DeviceRect::from_floats( - size.width - dp_size_br.width, - size.height - dp_size_br.height, - size.width, - size.height, - ), - &border.right, - &border.bottom, - DeviceSize::new(dp_width_right, dp_width_bottom), - dp_corner_br, - BorderSegment::BottomRight, - EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT, - &mut border_segments, - brush_segments, - ); - - add_corner_segment( - LayoutRect::from_floats( - rect.origin.x, - rect.origin.y + rect.size.height - local_size_bl.height, - rect.origin.x + local_size_bl.width, - rect.origin.y + rect.size.height, - ), - DeviceRect::from_floats( - 0.0, - size.height - dp_size_bl.height, - dp_size_bl.width, - size.height, - ), - &border.bottom, - &border.left, - DeviceSize::new(dp_width_left, dp_width_bottom), - dp_corner_bl, - BorderSegment::BottomLeft, - EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT, - &mut border_segments, - brush_segments, - ); - - Some(BorderRenderTaskInfo { - border_segments, - size: size.to_i32(), - available_size_dependence: size_dependence, - do_aa: border.do_aa, - }) - } - - /// Returns the cache key size for this task, based on our available size - /// dependence computed earlier. - #[inline] - pub fn cache_key_size( - &self, - local_size: &LayoutSize, - scale: LayoutToDeviceScale, - ) -> DeviceIntSize { - let mut size = DeviceIntSize::zero(); - if self.available_size_dependence.is_empty() { - return size; - } - - let device_size = (*local_size * scale).to_i32(); - if self.available_size_dependence.contains(AvailableSizeDependence::VERTICAL) { - size.height = device_size.height; - } - if self.available_size_dependence.contains(AvailableSizeDependence::HORIZONTAL) { - size.width = device_size.width; - } - size - } - - pub fn build_instances(&self, border: &NormalBorder) -> Vec { - let mut instances = Vec::new(); - - for info in &self.border_segments { - let (side0, side1, flip0, flip1) = match info.segment { - BorderSegment::Left => (&border.left, &border.left, false, false), - BorderSegment::Top => (&border.top, &border.top, false, false), - BorderSegment::Right => (&border.right, &border.right, true, true), - BorderSegment::Bottom => (&border.bottom, &border.bottom, true, true), - BorderSegment::TopLeft => (&border.left, &border.top, false, false), - BorderSegment::TopRight => (&border.top, &border.right, false, true), - BorderSegment::BottomRight => (&border.right, &border.bottom, true, true), - BorderSegment::BottomLeft => (&border.bottom, &border.left, true, false), - }; - - let style0 = if side0.style.is_hidden() { - side1.style - } else { - side0.style - }; - let style1 = if side1.style.is_hidden() { - side0.style - } else { - side1.style - }; - - let color0 = side0.border_color(flip0); - let color1 = side1.border_color(flip1); - - add_segment( - info.task_rect, - style0, - style1, - color0, - color1, - info.segment, - &mut instances, - info.widths, - info.radius, - self.do_aa, - ); - } - - instances - } - - /// Computes the maximum scale that we allow for this set of border parameters. - /// capping the scale will result in rendering very large corners at a lower - /// resolution and stretching them, so they will have the right shape, but - /// blurrier. - pub fn get_max_scale( - radii: &BorderRadius, - widths: &LayoutSideOffsets - ) -> LayoutToDeviceScale { - let r = radii.top_left.width - .max(radii.top_left.height) - .max(radii.top_right.width) - .max(radii.top_right.height) - .max(radii.bottom_left.width) - .max(radii.bottom_left.height) - .max(radii.bottom_right.width) - .max(radii.bottom_right.height) - .max(widths.top) - .max(widths.bottom) - .max(widths.left) - .max(widths.right); - - LayoutToDeviceScale::new(MAX_BORDER_RESOLUTION as f32 / r) - } -} - -fn add_brush_segment( - image_rect: LayoutRect, - task_rect: DeviceRect, - brush_flags: BrushFlags, - edge_flags: EdgeAaSegmentMask, +/// Create the set of border segments and render task +/// cache keys for a given CSS border. +fn create_border_segments( + rect: &LayoutRect, + border: &NormalBorder, + widths: &LayoutSideOffsets, + border_segments: &mut SmallVec<[BorderSegmentInfo; 8]>, brush_segments: &mut BrushSegmentVec, ) { - if image_rect.size.width <= 0. || image_rect.size.width <= 0. { - return; - } - - brush_segments.push( - BrushSegment::new( - image_rect, - /* may_need_clip_mask = */ true, - edge_flags, - [ - task_rect.origin.x, - task_rect.origin.y, - task_rect.origin.x + task_rect.size.width, - task_rect.origin.y + task_rect.size.height, - ], - brush_flags, - ) + let local_size_tl = LayoutSize::new( + border.radius.top_left.width.max(widths.left), + border.radius.top_left.height.max(widths.top), ); + let local_size_tr = LayoutSize::new( + border.radius.top_right.width.max(widths.right), + border.radius.top_right.height.max(widths.top), + ); + let local_size_br = LayoutSize::new( + border.radius.bottom_right.width.max(widths.right), + border.radius.bottom_right.height.max(widths.bottom), + ); + let local_size_bl = LayoutSize::new( + border.radius.bottom_left.width.max(widths.left), + border.radius.bottom_left.height.max(widths.bottom), + ); + + let top_edge_info = get_edge_info( + border.top.style, + widths.top, + rect.size.width - local_size_tl.width - local_size_tr.width, + ); + let bottom_edge_info = get_edge_info( + border.bottom.style, + widths.bottom, + rect.size.width - local_size_bl.width - local_size_br.width, + ); + + let left_edge_info = get_edge_info( + border.left.style, + widths.left, + rect.size.height - local_size_tl.height - local_size_bl.height, + ); + let right_edge_info = get_edge_info( + border.right.style, + widths.right, + rect.size.height - local_size_tr.height - local_size_br.height, + ); + + add_edge_segment( + LayoutRect::from_floats( + rect.origin.x, + rect.origin.y + local_size_tl.height + left_edge_info.local_offset, + rect.origin.x + widths.left, + rect.origin.y + local_size_tl.height + left_edge_info.local_offset + left_edge_info.local_size, + ), + &left_edge_info, + border.left, + widths.left, + BorderSegment::Left, + EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT, + brush_segments, + border_segments, + border.do_aa, + ); + add_edge_segment( + LayoutRect::from_floats( + rect.origin.x + local_size_tl.width + top_edge_info.local_offset, + rect.origin.y, + rect.origin.x + local_size_tl.width + top_edge_info.local_offset + top_edge_info.local_size, + rect.origin.y + widths.top, + ), + &top_edge_info, + border.top, + widths.top, + BorderSegment::Top, + EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM, + brush_segments, + border_segments, + border.do_aa, + ); + add_edge_segment( + LayoutRect::from_floats( + rect.origin.x + rect.size.width - widths.right, + rect.origin.y + local_size_tr.height + right_edge_info.local_offset, + rect.origin.x + rect.size.width, + rect.origin.y + local_size_tr.height + right_edge_info.local_offset + right_edge_info.local_size, + ), + &right_edge_info, + border.right, + widths.right, + BorderSegment::Right, + EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT, + brush_segments, + border_segments, + border.do_aa, + ); + add_edge_segment( + LayoutRect::from_floats( + rect.origin.x + local_size_bl.width + bottom_edge_info.local_offset, + rect.origin.y + rect.size.height - widths.bottom, + rect.origin.x + local_size_bl.width + bottom_edge_info.local_offset + bottom_edge_info.local_size, + rect.origin.y + rect.size.height, + ), + &bottom_edge_info, + border.bottom, + widths.bottom, + BorderSegment::Bottom, + EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP, + brush_segments, + border_segments, + border.do_aa, + ); + + add_corner_segment( + LayoutRect::from_floats( + rect.origin.x, + rect.origin.y, + rect.origin.x + local_size_tl.width, + rect.origin.y + local_size_tl.height, + ), + border.left, + border.top, + LayoutSize::new(widths.left, widths.top), + border.radius.top_left, + BorderSegment::TopLeft, + EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT, + brush_segments, + border_segments, + border.do_aa, + ); + add_corner_segment( + LayoutRect::from_floats( + rect.origin.x + rect.size.width - local_size_tr.width, + rect.origin.y, + rect.origin.x + rect.size.width, + rect.origin.y + local_size_tr.height, + ), + border.top, + border.right, + LayoutSize::new(widths.right, widths.top), + border.radius.top_right, + BorderSegment::TopRight, + EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT, + brush_segments, + border_segments, + border.do_aa, + ); + add_corner_segment( + LayoutRect::from_floats( + rect.origin.x + rect.size.width - local_size_br.width, + rect.origin.y + rect.size.height - local_size_br.height, + rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height, + ), + border.right, + border.bottom, + LayoutSize::new(widths.right, widths.bottom), + border.radius.bottom_right, + BorderSegment::BottomRight, + EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT, + brush_segments, + border_segments, + border.do_aa, + ); + add_corner_segment( + LayoutRect::from_floats( + rect.origin.x, + rect.origin.y + rect.size.height - local_size_bl.height, + rect.origin.x + local_size_bl.width, + rect.origin.y + rect.size.height, + ), + border.bottom, + border.left, + LayoutSize::new(widths.left, widths.bottom), + border.radius.bottom_left, + BorderSegment::BottomLeft, + EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT, + brush_segments, + border_segments, + border.do_aa, + ); +} + +/// Computes the maximum scale that we allow for this set of border parameters. +/// capping the scale will result in rendering very large corners at a lower +/// resolution and stretching them, so they will have the right shape, but +/// blurrier. +pub fn get_max_scale_for_border( + radii: &BorderRadius, + widths: &LayoutSideOffsets +) -> LayoutToDeviceScale { + let r = radii.top_left.width + .max(radii.top_left.height) + .max(radii.top_right.width) + .max(radii.top_right.height) + .max(radii.bottom_left.width) + .max(radii.bottom_left.height) + .max(radii.bottom_right.width) + .max(radii.bottom_right.height) + .max(widths.top) + .max(widths.bottom) + .max(widths.left) + .max(widths.right); + + LayoutToDeviceScale::new(MAX_BORDER_RESOLUTION as f32 / r) } fn add_segment( @@ -1209,17 +932,19 @@ fn add_segment( } } +/// Add a corner segment (if valid) to the list of +/// border segments for this primitive. fn add_corner_segment( image_rect: LayoutRect, - task_rect: DeviceRect, - side0: &BorderSide, - side1: &BorderSide, - widths: DeviceSize, - radius: DeviceSize, + side0: BorderSide, + side1: BorderSide, + widths: LayoutSize, + radius: LayoutSize, segment: BorderSegment, edge_flags: EdgeAaSegmentMask, - border_segments: &mut Vec, brush_segments: &mut BrushSegmentVec, + border_segments: &mut SmallVec<[BorderSegmentInfo; 8]>, + do_aa: bool, ) { if side0.color.a <= 0.0 && side1.color.a <= 0.0 { return; @@ -1233,31 +958,58 @@ fn add_corner_segment( return; } - border_segments.push(BorderSegmentInfo { - task_rect, - segment, - radius, - widths, - }); + if image_rect.size.width <= 0. || image_rect.size.height <= 0. { + return; + } - add_brush_segment( - image_rect, - task_rect, - BrushFlags::SEGMENT_RELATIVE, - edge_flags, - brush_segments, + let is_opaque = + side0.is_opaque() && + side1.is_opaque() && + radius.width <= 0.0 && + radius.height <= 0.0; + + brush_segments.push( + BrushSegment::new( + image_rect, + /* may_need_clip_mask = */ true, + edge_flags, + [0.0; 4], + BrushFlags::SEGMENT_RELATIVE, + ) ); + + border_segments.push(BorderSegmentInfo { + handle: None, + local_task_size: image_rect.size, + is_opaque, + cache_key: RenderTaskCacheKey { + size: DeviceIntSize::zero(), + kind: RenderTaskCacheKeyKind::BorderCorner( + BorderCornerCacheKey { + do_aa, + side0: side0.into(), + side1: side1.into(), + segment, + radius: radius.to_au(), + widths: widths.to_au(), + } + ), + }, + }); } +/// Add an edge segment (if valid) to the list of +/// border segments for this primitive. fn add_edge_segment( image_rect: LayoutRect, - task_rect: DeviceRect, - side: &BorderSide, + edge_info: &EdgeInfo, + side: BorderSide, + width: f32, segment: BorderSegment, edge_flags: EdgeAaSegmentMask, - border_segments: &mut Vec, - brush_flags: BrushFlags, brush_segments: &mut BrushSegmentVec, + border_segments: &mut SmallVec<[BorderSegmentInfo; 8]>, + do_aa: bool, ) { if side.color.a <= 0.0 { return; @@ -1267,18 +1019,141 @@ fn add_edge_segment( return; } - border_segments.push(BorderSegmentInfo { - task_rect, - segment, - radius: DeviceSize::zero(), - widths: task_rect.size, - }); + let (size, brush_flags) = match segment { + BorderSegment::Left | BorderSegment::Right => { + (LayoutSize::new(width, edge_info.stretch_size), BrushFlags::SEGMENT_REPEAT_Y) + } + BorderSegment::Top | BorderSegment::Bottom => { + (LayoutSize::new(edge_info.stretch_size, width), BrushFlags::SEGMENT_REPEAT_X) + } + _ => { + unreachable!(); + } + }; - add_brush_segment( - image_rect, - task_rect, - brush_flags, - edge_flags, - brush_segments, + if image_rect.size.width <= 0. || image_rect.size.height <= 0. { + return; + } + + let is_opaque = side.is_opaque(); + + brush_segments.push( + BrushSegment::new( + image_rect, + /* may_need_clip_mask = */ true, + edge_flags, + [0.0, 0.0, size.width, size.height], + BrushFlags::SEGMENT_RELATIVE | brush_flags, + ) ); + + border_segments.push(BorderSegmentInfo { + handle: None, + local_task_size: size, + is_opaque, + cache_key: RenderTaskCacheKey { + size: DeviceIntSize::zero(), + kind: RenderTaskCacheKeyKind::BorderEdge( + BorderEdgeCacheKey { + do_aa, + side: side.into(), + size: size.to_au(), + segment, + }, + ), + }, + }); +} + +/// Build the set of border instances needed to draw a border +/// segment into the render task cache. +pub fn build_border_instances( + cache_key: &RenderTaskCacheKey, + border: &NormalBorder, + scale: LayoutToDeviceScale, +) -> Vec { + let mut instances = Vec::new(); + + let (segment, widths, radius) = match cache_key.kind { + RenderTaskCacheKeyKind::BorderEdge(ref key) => { + (key.segment, LayoutSize::from_au(key.size), LayoutSize::zero()) + } + RenderTaskCacheKeyKind::BorderCorner(ref key) => { + (key.segment, LayoutSize::from_au(key.widths), LayoutSize::from_au(key.radius)) + } + _ => { + unreachable!(); + } + }; + + let (side0, side1, flip0, flip1) = match segment { + BorderSegment::Left => (&border.left, &border.left, false, false), + BorderSegment::Top => (&border.top, &border.top, false, false), + BorderSegment::Right => (&border.right, &border.right, true, true), + BorderSegment::Bottom => (&border.bottom, &border.bottom, true, true), + BorderSegment::TopLeft => (&border.left, &border.top, false, false), + BorderSegment::TopRight => (&border.top, &border.right, false, true), + BorderSegment::BottomRight => (&border.right, &border.bottom, true, true), + BorderSegment::BottomLeft => (&border.bottom, &border.left, true, false), + }; + + let style0 = if side0.style.is_hidden() { + side1.style + } else { + side0.style + }; + let style1 = if side1.style.is_hidden() { + side0.style + } else { + side1.style + }; + + let color0 = side0.border_color(flip0); + let color1 = side1.border_color(flip1); + + let widths = (widths * scale).ceil(); + let radius = (radius * scale).ceil(); + + add_segment( + DeviceRect::new(DevicePoint::zero(), cache_key.size.to_f32()), + style0, + style1, + color0, + color1, + segment, + &mut instances, + widths, + radius, + border.do_aa, + ); + + instances +} + +pub fn create_normal_border_prim( + local_rect: &LayoutRect, + border: NormalBorder, + widths: LayoutSideOffsets, +) -> BrushPrimitive { + let mut brush_segments = BrushSegmentVec::new(); + let mut border_segments = SmallVec::new(); + + create_border_segments( + local_rect, + &border, + &widths, + &mut border_segments, + &mut brush_segments, + ); + + BrushPrimitive::new( + BrushKind::new_border( + border, + widths, + border_segments, + ), + Some(BrushSegmentDescriptor { + segments: brush_segments, + }), + ) } diff --git a/gfx/webrender/src/clip_scroll_tree.rs b/gfx/webrender/src/clip_scroll_tree.rs index d105beb31200..1f4b9f3b1909 100644 --- a/gfx/webrender/src/clip_scroll_tree.rs +++ b/gfx/webrender/src/clip_scroll_tree.rs @@ -405,8 +405,8 @@ impl ClipScrollTree { pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size)); pt.add_item(format!("scroll offset: {:?}", scrolling_info.offset)); } - SpatialNodeType::ReferenceFrame(ref info) => { - pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform)); + SpatialNodeType::ReferenceFrame(ref _info) => { + pt.new_level(format!("ReferenceFrame")); pt.add_item(format!("index: {:?}", index)); } } diff --git a/gfx/webrender/src/device/gl.rs b/gfx/webrender/src/device/gl.rs index e87ae7b87325..5399eff1b56f 100644 --- a/gfx/webrender/src/device/gl.rs +++ b/gfx/webrender/src/device/gl.rs @@ -3,18 +3,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::super::shader_source; -use api::{ColorF, ImageFormat}; +use api::{ColorF, ImageFormat, MemoryReport}; use api::{DeviceIntPoint, DeviceIntRect, DeviceUintRect, DeviceUintSize}; use api::TextureTarget; #[cfg(any(feature = "debug_renderer", feature="capture"))] use api::ImageDescriptor; use euclid::Transform3D; use gleam::gl; -use internal_types::{FastHashMap, RenderTargetInfo}; +use internal_types::{FastHashMap, LayerIndex, RenderTargetInfo}; use log::Level; use smallvec::SmallVec; use std::cell::RefCell; use std::cmp; +use std::collections::hash_map::Entry; use std::fs::File; use std::io::Read; use std::marker::PhantomData; @@ -442,9 +443,28 @@ pub struct Texture { width: u32, height: u32, filter: TextureFilter, - render_target: Option, - fbo_ids: Vec, - depth_rb: Option, + /// Framebuffer Objects, one for each layer of the texture, allowing this + /// texture to be rendered to. Empty if this texture is not used as a render + /// target. + fbos: Vec, + /// Same as the above, but with a depth buffer attached. + /// + /// FBOs are cheap to create but expensive to reconfigure (since doing so + /// invalidates framebuffer completeness caching). Moreover, rendering with + /// a depth buffer attached but the depth write+test disabled relies on the + /// driver to optimize it out of the rendering pass, which most drivers + /// probably do but, according to jgilbert, is best not to rely on. + /// + /// So we lazily generate a second list of FBOs with depth. This list is + /// empty if this texture is not used as a render target _or_ if it is, but + /// the depth buffer has never been requested. + /// + /// Note that we always fill fbos, and then lazily create fbos_with_depth + /// when needed. We could make both lazy (i.e. render targets would have one + /// or the other, but not both, unless they were actually used in both + /// configurations). But that would complicate a lot of logic in this module, + /// and FBOs are cheap enough to create. + fbos_with_depth: Vec, last_frame_used: FrameId, } @@ -453,10 +473,6 @@ impl Texture { DeviceUintSize::new(self.width, self.height) } - pub fn get_render_target_layer_count(&self) -> usize { - self.fbo_ids.len() - } - pub fn get_layer_count(&self) -> i32 { self.layer_count } @@ -470,17 +486,8 @@ impl Texture { self.filter } - #[cfg(any(feature = "debug_renderer", feature = "capture"))] - pub fn get_render_target(&self) -> Option { - self.render_target.clone() - } - - pub fn has_depth(&self) -> bool { - self.depth_rb.is_some() - } - - pub fn get_rt_info(&self) -> Option<&RenderTargetInfo> { - self.render_target.as_ref() + pub fn supports_depth(&self) -> bool { + !self.fbos_with_depth.is_empty() } pub fn used_in_frame(&self, frame_id: FrameId) -> bool { @@ -713,6 +720,22 @@ pub enum ShaderError { Link(String, String), // name, error message } +/// A refcounted depth target, which may be shared by multiple textures across +/// the device. +struct SharedDepthTarget { + /// The Render Buffer Object representing the depth target. + rbo_id: RBOId, + /// Reference count. When this drops to zero, the RBO is deleted. + refcount: usize, +} + +#[cfg(feature = "debug")] +impl Drop for SharedDepthTarget { + fn drop(&mut self) { + debug_assert!(thread::panicking() || self.refcount == 0); + } +} + pub struct Device { gl: Rc, // device state @@ -735,6 +758,12 @@ pub struct Device { bgra_format_internal: gl::GLuint, bgra_format_external: gl::GLuint, + /// Map from texture dimensions to shared depth buffers for render targets. + /// + /// Render targets often have the same width/height, so we can save memory + /// by sharing these across targets. + depth_targets: FastHashMap, + // debug inside_frame: bool, @@ -759,6 +788,35 @@ pub struct Device { extensions: Vec, } +/// Contains the parameters necessary to bind a texture-backed draw target. +#[derive(Clone, Copy)] +pub struct TextureDrawTarget<'a> { + /// The target texture. + pub texture: &'a Texture, + /// The slice within the texture array to draw to. + pub layer: LayerIndex, + /// Whether to draw with the texture's associated depth target. + pub with_depth: bool, +} + +/// Contains the parameters necessary to bind a texture-backed read target. +#[derive(Clone, Copy)] +pub struct TextureReadTarget<'a> { + /// The source texture. + pub texture: &'a Texture, + /// The slice within the texture array to read from. + pub layer: LayerIndex, +} + +impl<'a> From> for TextureReadTarget<'a> { + fn from(t: TextureDrawTarget<'a>) -> Self { + TextureReadTarget { + texture: t.texture, + layer: t.layer, + } + } +} + impl Device { pub fn new( gl: Rc, @@ -829,6 +887,8 @@ impl Device { bgra_format_internal, bgra_format_external, + depth_targets: FastHashMap::default(), + bound_textures: [0; 16], bound_program: 0, bound_vao: 0, @@ -1006,9 +1066,9 @@ impl Device { } } - pub fn bind_read_target(&mut self, texture_and_layer: Option<(&Texture, i32)>) { - let fbo_id = texture_and_layer.map_or(FBOId(self.default_read_fbo), |texture_and_layer| { - texture_and_layer.0.fbo_ids[texture_and_layer.1 as usize] + pub fn bind_read_target(&mut self, texture_target: Option) { + let fbo_id = texture_target.map_or(FBOId(self.default_read_fbo), |target| { + target.texture.fbos[target.layer] }); self.bind_read_target_impl(fbo_id) @@ -1025,11 +1085,15 @@ impl Device { pub fn bind_draw_target( &mut self, - texture_and_layer: Option<(&Texture, i32)>, + texture_target: Option, dimensions: Option, ) { - let fbo_id = texture_and_layer.map_or(FBOId(self.default_draw_fbo), |texture_and_layer| { - texture_and_layer.0.fbo_ids[texture_and_layer.1 as usize] + let fbo_id = texture_target.map_or(FBOId(self.default_draw_fbo), |target| { + if target.with_depth { + target.texture.fbos_with_depth[target.layer] + } else { + target.texture.fbos[target.layer] + } }); self.bind_draw_target_impl(fbo_id); @@ -1232,9 +1296,8 @@ impl Device { layer_count, format, filter, - render_target, - fbo_ids: vec![], - depth_rb: None, + fbos: vec![], + fbos_with_depth: vec![], last_frame_used: self.frame_id, }; self.bind_texture(DEFAULT_TEXTURE, &texture); @@ -1309,7 +1372,10 @@ impl Device { // Set up FBOs, if required. if let Some(rt_info) = render_target { - self.init_fbos(&mut texture, rt_info); + self.init_fbos(&mut texture, false); + if rt_info.has_depth { + self.init_fbos(&mut texture, true); + } } texture @@ -1349,7 +1415,7 @@ impl Device { debug_assert!(dst.height >= src.height); let rect = DeviceIntRect::new(DeviceIntPoint::zero(), src.get_dimensions().to_i32()); - for (read_fbo, draw_fbo) in src.fbo_ids.iter().zip(&dst.fbo_ids) { + for (read_fbo, draw_fbo) in src.fbos.iter().zip(&dst.fbos) { self.bind_read_target_impl(*read_fbo); self.bind_draw_target_impl(*draw_fbo); self.blit_render_target(rect, rect); @@ -1359,15 +1425,19 @@ impl Device { /// Notifies the device that the contents of a render target are no longer /// needed. + /// + /// FIXME(bholley): We could/should invalidate the depth targets earlier + /// than the color targets, i.e. immediately after each pass. pub fn invalidate_render_target(&mut self, texture: &Texture) { - let attachments: &[gl::GLenum] = if texture.has_depth() { - &[gl::COLOR_ATTACHMENT0, gl::DEPTH_ATTACHMENT] + let (fbos, attachments) = if texture.supports_depth() { + (&texture.fbos_with_depth, + &[gl::COLOR_ATTACHMENT0, gl::DEPTH_ATTACHMENT] as &[gl::GLenum]) } else { - &[gl::COLOR_ATTACHMENT0] + (&texture.fbos, &[gl::COLOR_ATTACHMENT0] as &[gl::GLenum]) }; let original_bound_fbo = self.bound_draw_fbo; - for fbo_id in texture.fbo_ids.iter() { + for fbo_id in fbos.iter() { // Note: The invalidate extension may not be supported, in which // case this is a no-op. That's ok though, because it's just a // hint. @@ -1386,42 +1456,28 @@ impl Device { rt_info: RenderTargetInfo, ) { texture.last_frame_used = self.frame_id; - texture.render_target = Some(rt_info); - // If the depth target requirements changed, just drop the FBOs and - // reinitialize. - // - // FIXME(bholley): I have a patch to do this better. - if rt_info.has_depth != texture.has_depth() { - self.deinit_fbos(texture); - self.init_fbos(texture, rt_info); + // Add depth support if needed. + if rt_info.has_depth && !texture.supports_depth() { + self.init_fbos(texture, true); } } - fn init_fbos(&mut self, texture: &mut Texture, rt_info: RenderTargetInfo) { - // Generate the FBOs. - assert!(texture.fbo_ids.is_empty()); - texture.fbo_ids.extend( - self.gl.gen_framebuffers(texture.layer_count).into_iter().map(FBOId) - ); + fn init_fbos(&mut self, texture: &mut Texture, with_depth: bool) { + let (fbos, depth_rb) = if with_depth { + let depth_target = self.acquire_depth_target(texture.get_dimensions()); + (&mut texture.fbos_with_depth, Some(depth_target)) + } else { + (&mut texture.fbos, None) + }; - // Optionally generate a depth target. - if rt_info.has_depth { - let renderbuffer_ids = self.gl.gen_renderbuffers(1); - let depth_rb = renderbuffer_ids[0]; - texture.depth_rb = Some(RBOId(depth_rb)); - self.gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb); - self.gl.renderbuffer_storage( - gl::RENDERBUFFER, - gl::DEPTH_COMPONENT24, - texture.width as _, - texture.height as _, - ); - } + // Generate the FBOs. + assert!(fbos.is_empty()); + fbos.extend(self.gl.gen_framebuffers(texture.layer_count).into_iter().map(FBOId)); // Bind the FBOs. let original_bound_fbo = self.bound_draw_fbo; - for (fbo_index, &fbo_id) in texture.fbo_ids.iter().enumerate() { + for (fbo_index, &fbo_id) in fbos.iter().enumerate() { self.bind_external_draw_target(fbo_id); match texture.target { gl::TEXTURE_2D_ARRAY => { @@ -1445,7 +1501,7 @@ impl Device { } } - if let Some(depth_rb) = texture.depth_rb { + if let Some(depth_rb) = depth_rb { self.gl.framebuffer_renderbuffer( gl::DRAW_FRAMEBUFFER, gl::DEPTH_ATTACHMENT, @@ -1457,15 +1513,9 @@ impl Device { self.bind_external_draw_target(original_bound_fbo); } - fn deinit_fbos(&mut self, texture: &mut Texture) { - if let Some(RBOId(depth_rb)) = texture.depth_rb.take() { - self.gl.delete_renderbuffers(&[depth_rb]); - texture.depth_rb = None; - } - - if !texture.fbo_ids.is_empty() { - let fbo_ids: Vec<_> = texture - .fbo_ids + fn deinit_fbos(&mut self, fbos: &mut Vec) { + if !fbos.is_empty() { + let fbo_ids: SmallVec<[gl::GLuint; 8]> = fbos .drain(..) .map(|FBOId(fbo_id)| fbo_id) .collect(); @@ -1473,6 +1523,40 @@ impl Device { } } + fn acquire_depth_target(&mut self, dimensions: DeviceUintSize) -> RBOId { + let gl = &self.gl; + let target = self.depth_targets.entry(dimensions).or_insert_with(|| { + let renderbuffer_ids = gl.gen_renderbuffers(1); + let depth_rb = renderbuffer_ids[0]; + gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb); + gl.renderbuffer_storage( + gl::RENDERBUFFER, + gl::DEPTH_COMPONENT24, + dimensions.width as _, + dimensions.height as _, + ); + SharedDepthTarget { + rbo_id: RBOId(depth_rb), + refcount: 0, + } + }); + target.refcount += 1; + target.rbo_id + } + + fn release_depth_target(&mut self, dimensions: DeviceUintSize) { + let mut entry = match self.depth_targets.entry(dimensions) { + Entry::Occupied(x) => x, + Entry::Vacant(..) => panic!("Releasing unknown depth target"), + }; + debug_assert!(entry.get().refcount != 0); + entry.get_mut().refcount -= 1; + if entry.get().refcount == 0 { + let t = entry.remove(); + self.gl.delete_renderbuffers(&[t.rbo_id.0]); + } + } + pub fn blit_render_target(&mut self, src_rect: DeviceIntRect, dest_rect: DeviceIntRect) { debug_assert!(self.inside_frame); @@ -1492,7 +1576,13 @@ impl Device { pub fn delete_texture(&mut self, mut texture: Texture) { debug_assert!(self.inside_frame); - self.deinit_fbos(&mut texture); + let had_depth = texture.supports_depth(); + self.deinit_fbos(&mut texture.fbos); + self.deinit_fbos(&mut texture.fbos_with_depth); + if had_depth { + self.release_depth_target(texture.get_dimensions()); + } + self.gl.delete_textures(&[texture.id]); for bound_texture in &mut self.bound_textures { @@ -2317,6 +2407,18 @@ impl Device { }, } } + + /// Generates a memory report for the resources managed by the device layer. + pub fn report_memory(&self) -> MemoryReport { + let mut report = MemoryReport::default(); + for dim in self.depth_targets.keys() { + // DEPTH24 textures generally reserve 3 bytes for depth and 1 byte + // for stencil, so we measure them as 32 bytes. + let pixels: u32 = dim.width * dim.height; + report.depth_target_textures += (pixels as usize) * 4; + } + report + } } struct FormatDesc { diff --git a/gfx/webrender/src/display_list_flattener.rs b/gfx/webrender/src/display_list_flattener.rs index 1f0aabc3ca84..7866cb879fa2 100644 --- a/gfx/webrender/src/display_list_flattener.rs +++ b/gfx/webrender/src/display_list_flattener.rs @@ -5,7 +5,7 @@ use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayListIter, ClipAndScrollInfo}; use api::{ClipId, ColorF, ComplexClipRegion, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; -use api::{DevicePixelScale, DeviceUintRect, DisplayItemRef, ExtendMode, ExternalScrollId}; +use api::{DisplayItemRef, ExtendMode, ExternalScrollId}; use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, RasterSpace, GradientStop}; use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint, ColorDepth}; use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D}; @@ -32,7 +32,7 @@ use render_backend::{DocumentView}; use resource_cache::{FontInstanceMap, ImageRequest}; use scene::{Scene, ScenePipeline, StackingContextHelpers}; use scene_builder::DocumentResources; -use spatial_node::{SpatialNodeType, StickyFrameInfo}; +use spatial_node::{StickyFrameInfo}; use std::{f32, mem}; use std::collections::vec_deque::VecDeque; use tiling::{CompositeOps}; @@ -209,7 +209,6 @@ impl<'a> DisplayListFlattener<'a> { &root_pipeline.viewport_size, &root_pipeline.content_size, ); - flattener.setup_viewport_offset(view.inner_rect, view.accumulated_scale_factor()); flattener.flatten_root(root_pipeline, &root_pipeline.viewport_size); debug_assert!(flattener.sc_stack.is_empty()); @@ -1257,20 +1256,6 @@ impl<'a> DisplayListFlattener<'a> { index } - pub fn setup_viewport_offset( - &mut self, - inner_rect: DeviceUintRect, - device_pixel_scale: DevicePixelScale, - ) { - let viewport_offset = (inner_rect.origin.to_vector().to_f32() / device_pixel_scale).round(); - let root_id = self.clip_scroll_tree.root_reference_frame_index(); - let root_node = &mut self.clip_scroll_tree.spatial_nodes[root_id.0]; - if let SpatialNodeType::ReferenceFrame(ref mut info) = root_node.node_type { - info.resolved_transform = - LayoutVector2D::new(viewport_offset.x, viewport_offset.y).into(); - } - } - pub fn push_root( &mut self, pipeline_id: PipelineId, @@ -1484,7 +1469,10 @@ impl<'a> DisplayListFlattener<'a> { &info, pending_primitive.clip_and_scroll.clip_chain_id, pending_primitive.clip_and_scroll.spatial_node_index, - pending_primitive.container.create_shadow(&pending_shadow.shadow), + pending_primitive.container.create_shadow( + &pending_shadow.shadow, + &info.rect, + ), ); // Add the new primitive to the shadow picture. @@ -1687,7 +1675,9 @@ impl<'a> DisplayListFlattener<'a> { // Use segment relative interpolation for all // instances in this primitive. - let mut brush_flags = BrushFlags::SEGMENT_RELATIVE; + let mut brush_flags = + BrushFlags::SEGMENT_RELATIVE | + BrushFlags::SEGMENT_TEXEL_RECT; // Enable repeat modes on the segment. if repeat_horizontal == RepeatMode::Repeat { @@ -1845,7 +1835,12 @@ impl<'a> DisplayListFlattener<'a> { self.add_primitive(clip_and_scroll, info, Vec::new(), prim); } BorderDetails::Normal(ref border) => { - self.add_normal_border(info, border, &border_item.widths, clip_and_scroll); + self.add_normal_border( + info, + border, + border_item.widths, + clip_and_scroll, + ); } } } diff --git a/gfx/webrender/src/gpu_glyph_renderer.rs b/gfx/webrender/src/gpu_glyph_renderer.rs index 20d80093c16e..93697f06a2bc 100644 --- a/gfx/webrender/src/gpu_glyph_renderer.rs +++ b/gfx/webrender/src/gpu_glyph_renderer.rs @@ -7,7 +7,7 @@ use api::{DeviceIntPoint, DeviceIntRect, DeviceUintSize, FontRenderMode}; use api::{ImageFormat, TextureTarget}; use debug_colors; -use device::{Device, Texture, TextureFilter, VAO}; +use device::{Device, Texture, TextureDrawTarget, TextureFilter, VAO}; use euclid::{Point2D, Size2D, Transform3D, TypedVector2D, Vector2D}; use internal_types::RenderTargetInfo; use pathfinder_gfx_utils::ShelfBinPacker; @@ -194,7 +194,11 @@ impl Renderer { projection, &mut self.renderer_errors); - self.device.bind_draw_target(Some((¤t_page.texture, 0)), Some(*target_size)); + self.device.bind_draw_target(Some(TextureDrawTarget { + texture: ¤t_page.texture, + layer: 0, + with_depth: false, + }), Some(*target_size)); self.device.clear_target(Some([0.0, 0.0, 0.0, 0.0]), None, None); self.device.set_blend(true); diff --git a/gfx/webrender/src/gpu_types.rs b/gfx/webrender/src/gpu_types.rs index 2ab663c539c9..59c8690ed44a 100644 --- a/gfx/webrender/src/gpu_types.rs +++ b/gfx/webrender/src/gpu_types.rs @@ -89,7 +89,7 @@ pub struct ScalingInstance { pub src_task_address: RenderTaskAddress, } -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[repr(C)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -318,6 +318,8 @@ bitflags! { const SEGMENT_REPEAT_X = 0x4; /// Repeat UVs vertically. const SEGMENT_REPEAT_Y = 0x8; + /// The extra segment data is a texel rect. + const SEGMENT_TEXEL_RECT = 0x10; } } diff --git a/gfx/webrender/src/internal_types.rs b/gfx/webrender/src/internal_types.rs index 176332cff45d..579763081291 100644 --- a/gfx/webrender/src/internal_types.rs +++ b/gfx/webrender/src/internal_types.rs @@ -38,6 +38,19 @@ pub type FastHashSet = HashSet>; #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct CacheTextureId(pub u64); +/// Canonical type for texture layer indices. +/// +/// WebRender is currently not very consistent about layer index types. Some +/// places use i32 (since that's the type used in various OpenGL APIs), some +/// places use u32 (since having it be signed is non-sensical, but the +/// underlying graphics APIs generally operate on 32-bit integers) and some +/// places use usize (since that's most natural in Rust). +/// +/// Going forward, we aim to us usize throughout the codebase, since that allows +/// operations like indexing without a cast, and convert to the required type in +/// the device module when making calls into the platform layer. +pub type LayerIndex = usize; + /// Identifies a render pass target that is persisted until the end of the frame. /// /// By default, only the targets of the immediately-preceding pass are bound as diff --git a/gfx/webrender/src/platform/windows/font.rs b/gfx/webrender/src/platform/windows/font.rs index a476947762c6..ac90e0d5c234 100644 --- a/gfx/webrender/src/platform/windows/font.rs +++ b/gfx/webrender/src/platform/windows/font.rs @@ -131,9 +131,20 @@ impl FontContext { } let system_fc = dwrote::FontCollection::system(); - let font = match system_fc.get_font_from_descriptor(&font_handle) { - Some(font) => font, - None => { panic!("missing descriptor {:?}", font_handle) } + // A version of get_font_from_descriptor() that panics early to help with bug 1455848 + let font = if let Some(family) = system_fc.get_font_family_by_name(&font_handle.family_name) { + let font = family.get_first_matching_font(font_handle.weight, font_handle.stretch, font_handle.style); + // Exact matches only here + if font.weight() == font_handle.weight && + font.stretch() == font_handle.stretch && + font.style() == font_handle.style + { + font + } else { + panic!("font mismatch for descriptor {:?} {:?}", font_handle, font.to_descriptor()) + } + } else { + panic!("missing font family for descriptor {:?}", font_handle) }; let face = font.create_font_face(); self.fonts.insert(*font_key, face); diff --git a/gfx/webrender/src/prim_store.rs b/gfx/webrender/src/prim_store.rs index 5f3bbe3203ea..cbe3b9b4cc75 100644 --- a/gfx/webrender/src/prim_store.rs +++ b/gfx/webrender/src/prim_store.rs @@ -7,10 +7,10 @@ use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode, DeviceRect use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, TileOffset}; use api::{RasterSpace, LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize, LayoutToWorldTransform}; use api::{LayoutVector2D, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat}; -use api::{DeviceIntSideOffsets, WorldPixel, BoxShadowClipMode, LayoutToWorldScale, NormalBorder, WorldRect}; +use api::{DeviceIntSideOffsets, WorldPixel, BoxShadowClipMode, NormalBorder, WorldRect, LayoutToWorldScale}; use api::{PicturePixel, RasterPixel, ColorDepth, LineStyle, LineOrientation, LayoutSizeAu, AuHelpers}; use app_units::Au; -use border::{BorderCacheKey, BorderRenderTaskInfo}; +use border::{get_max_scale_for_border, build_border_instances, create_normal_border_prim}; use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, SpatialNodeIndex}; use clip::{ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem, ClipNodeCollector}; use euclid::{TypedTransform3D, TypedRect, TypedScale}; @@ -25,7 +25,7 @@ use intern; use picture::{PictureCompositeMode, PicturePrimitive}; #[cfg(debug_assertions)] use render_backend::FrameId; -use render_task::{BlitSource, RenderTask, RenderTaskCacheKey}; +use render_task::{BlitSource, RenderTask, RenderTaskCacheKey, to_cache_size}; use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHandle}; use renderer::{MAX_VERTEX_TEXTURE_WIDTH}; use resource_cache::{ImageProperties, ImageRequest, ResourceCache}; @@ -345,13 +345,21 @@ pub struct VisibleGradientTile { pub local_clip_rect: LayoutRect, } +/// Information about how to cache a border segment, +/// along with the current render task cache entry. +#[derive(Debug)] +pub struct BorderSegmentInfo { + pub handle: Option, + pub local_task_size: LayoutSize, + pub cache_key: RenderTaskCacheKey, + pub is_opaque: bool, +} + #[derive(Debug)] pub enum BorderSource { Image(ImageRequest), Border { - handle: Option, - cache_key: BorderCacheKey, - task_info: Option, + segments: SmallVec<[BorderSegmentInfo; 8]>, border: NormalBorder, widths: LayoutSideOffsets, }, @@ -456,18 +464,19 @@ impl BrushKind { // Construct a brush that is a border with `border` style and `widths` // dimensions. - pub fn new_border(mut border: NormalBorder, widths: LayoutSideOffsets) -> BrushKind { + pub fn new_border( + mut border: NormalBorder, + widths: LayoutSideOffsets, + segments: SmallVec<[BorderSegmentInfo; 8]>, + ) -> BrushKind { // FIXME(emilio): Is this the best place to do this? border.normalize(&widths); - let cache_key = BorderCacheKey::new(&border, &widths); BrushKind::Border { source: BorderSource::Border { border, widths, - cache_key, - task_info: None, - handle: None, + segments, } } } @@ -1412,7 +1421,11 @@ impl PrimitiveContainer { // Create a clone of this PrimitiveContainer, applying whatever // changes are necessary to the primitive to support rendering // it as part of the supplied shadow. - pub fn create_shadow(&self, shadow: &Shadow) -> PrimitiveContainer { + pub fn create_shadow( + &self, + shadow: &Shadow, + prim_rect: &LayoutRect, + ) -> PrimitiveContainer { match *self { PrimitiveContainer::TextRun(ref info) => { let mut font = FontInstance { @@ -1440,22 +1453,25 @@ impl PrimitiveContainer { )) } BrushKind::Border { ref source } => { - let source = match *source { + let prim = match *source { BorderSource::Image(request) => { - BrushKind::Border { - source: BorderSource::Image(request) - } - }, + BrushPrimitive::new( + BrushKind::Border { + source: BorderSource::Image(request) + }, + None, + ) + } BorderSource::Border { border, widths, .. } => { let border = border.with_color(shadow.color); - BrushKind::new_border(border, widths) - + create_normal_border_prim( + prim_rect, + border, + widths, + ) } }; - PrimitiveContainer::Brush(BrushPrimitive::new( - source, - None, - )) + PrimitiveContainer::Brush(prim) } BrushKind::LineDecoration { style, orientation, wavy_line_thickness, .. } => { PrimitiveContainer::Brush(BrushPrimitive::new_line_decoration( @@ -1960,13 +1976,6 @@ impl PrimitiveStore { prim_instance.clipped_world_rect = Some(clipped_world_rect); - prim.build_prim_segments_if_needed( - prim_instance, - pic_state, - frame_state, - frame_context, - ); - prim.update_clip_task( prim_instance, prim_context, @@ -2839,9 +2848,46 @@ impl Primitive { ); } } - BorderSource::Border { .. } => { - // Handled earlier since we need to update the segment - // descriptor *before* update_clip_task() is called. + BorderSource::Border { ref border, ref widths, ref mut segments, .. } => { + // TODO(gw): When drawing in screen raster mode, we should also incorporate a + // scale factor from the world transform to get an appropriately + // sized border task. + let world_scale = LayoutToWorldScale::new(1.0); + let mut scale = world_scale * frame_context.device_pixel_scale; + let max_scale = get_max_scale_for_border(&border.radius, widths); + scale.0 = scale.0.min(max_scale.0); + + // For each edge and corner, request the render task by content key + // from the render task cache. This ensures that the render task for + // this segment will be available for batching later in the frame. + for segment in segments { + // Update the cache key device size based on requested scale. + segment.cache_key.size = to_cache_size(segment.local_task_size * scale); + + segment.handle = Some(frame_state.resource_cache.request_render_task( + segment.cache_key.clone(), + frame_state.gpu_cache, + frame_state.render_tasks, + None, + segment.is_opaque, + |render_tasks| { + let task = RenderTask::new_border_segment( + segment.cache_key.size, + build_border_instances( + &segment.cache_key, + border, + scale, + ), + ); + + let task_id = render_tasks.add(task); + + pic_state.tasks.push(task_id); + + task_id + } + )); + } } } } @@ -3092,95 +3138,6 @@ impl Primitive { } } } - - fn build_prim_segments_if_needed( - &mut self, - prim_instance: &mut PrimitiveInstance, - pic_state: &mut PictureState, - frame_state: &mut FrameBuildingState, - frame_context: &FrameBuildingContext, - ) { - let brush = match self.details { - PrimitiveDetails::Brush(ref mut brush) => brush, - PrimitiveDetails::TextRun(..) => return, - }; - - if let BrushKind::Border { - source: BorderSource::Border { - ref border, - ref mut cache_key, - ref widths, - ref mut handle, - ref mut task_info, - .. - } - } = brush.kind { - // TODO(gw): When drawing in screen raster mode, we should also incorporate a - // scale factor from the world transform to get an appropriately - // sized border task. - let world_scale = LayoutToWorldScale::new(1.0); - let mut scale = world_scale * frame_context.device_pixel_scale; - let max_scale = BorderRenderTaskInfo::get_max_scale(&border.radius, &widths); - scale.0 = scale.0.min(max_scale.0); - let scale_au = Au::from_f32_px(scale.0); - - // NOTE(emilio): This `needs_update` relies on the local rect for a - // given primitive being immutable. If that changes, this code - // should probably handle changes to it as well, retaining the old - // size in cache_key. - let needs_update = scale_au != cache_key.scale; - - let mut new_segments = BrushSegmentVec::new(); - - let local_rect = &self.metadata.local_rect; - if needs_update { - cache_key.scale = scale_au; - - *task_info = BorderRenderTaskInfo::new( - local_rect, - border, - widths, - scale, - &mut new_segments, - ); - } - - *handle = task_info.as_ref().map(|task_info| { - frame_state.resource_cache.request_render_task( - RenderTaskCacheKey { - size: task_info.cache_key_size(&local_rect.size, scale), - kind: RenderTaskCacheKeyKind::Border(cache_key.clone()), - }, - frame_state.gpu_cache, - frame_state.render_tasks, - None, - false, // todo - |render_tasks| { - let task = RenderTask::new_border( - task_info.size, - task_info.build_instances(border), - ); - - let task_id = render_tasks.add(task); - - pic_state.tasks.push(task_id); - - task_id - } - ) - }); - - if needs_update { - brush.segment_desc = Some(BrushSegmentDescriptor { - segments: new_segments, - }); - - // The segments have changed, so force the GPU cache to - // re-upload the primitive information. - frame_state.gpu_cache.invalidate(&mut prim_instance.gpu_location); - } - } - } } pub fn get_raster_rects( diff --git a/gfx/webrender/src/render_backend.rs b/gfx/webrender/src/render_backend.rs index 8211fa91edd0..7af23bdc008c 100644 --- a/gfx/webrender/src/render_backend.rs +++ b/gfx/webrender/src/render_backend.rs @@ -277,6 +277,12 @@ impl Document { FrameMsg::AppendDynamicProperties(property_bindings) => { self.dynamic_properties.add_properties(property_bindings); } + FrameMsg::SetPinchZoom(factor) => { + if self.view.pinch_zoom_factor != factor.get() { + self.view.pinch_zoom_factor = factor.get(); + self.frame_is_valid = false; + } + } } DocumentOps::nop() @@ -508,9 +514,6 @@ impl RenderBackend { SceneMsg::SetPageZoom(factor) => { doc.view.page_zoom_factor = factor.get(); } - SceneMsg::SetPinchZoom(factor) => { - doc.view.pinch_zoom_factor = factor.get(); - } SceneMsg::SetWindowParameters { window_size, inner_rect, diff --git a/gfx/webrender/src/render_task.rs b/gfx/webrender/src/render_task.rs index f2c679e7b3a6..fbf3f2b7475e 100644 --- a/gfx/webrender/src/render_task.rs +++ b/gfx/webrender/src/render_task.rs @@ -7,7 +7,7 @@ use api::{DevicePixelScale, ImageDescriptor, ImageFormat}; use api::{LineStyle, LineOrientation, LayoutSize}; #[cfg(feature = "pathfinder")] use api::FontRenderMode; -use border::BorderCacheKey; +use border::{BorderCornerCacheKey, BorderEdgeCacheKey}; use box_shadow::{BoxShadowCacheKey}; use clip::{ClipDataStore, ClipItem, ClipStore, ClipNodeRange}; use clip_scroll_tree::SpatialNodeIndex; @@ -18,7 +18,7 @@ use freelist::{FreeList, FreeListHandle, WeakFreeListHandle}; use glyph_rasterizer::GpuGlyphCacheKey; use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; use gpu_types::{BorderInstance, ImageSource, UvRectKind}; -use internal_types::{CacheTextureId, FastHashMap, SavedTargetIndex}; +use internal_types::{CacheTextureId, FastHashMap, LayerIndex, SavedTargetIndex}; #[cfg(feature = "pathfinder")] use pathfinder_partitioner::mesh::Mesh; use picture::PictureCacheKey; @@ -113,7 +113,7 @@ impl RenderTaskTree { debug_assert!(pass_index == passes.len() - 1); } RenderTaskLocation::Dynamic(..) | - RenderTaskLocation::TextureCache(..) => { + RenderTaskLocation::TextureCache { .. } => { debug_assert!(pass_index < passes.len() - 1); } } @@ -192,7 +192,14 @@ pub enum RenderTaskLocation { Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize), /// The output of the `RenderTask` will be persisted beyond this frame, and /// thus should be drawn into the `TextureCache`. - TextureCache(CacheTextureId, i32, DeviceIntRect), + TextureCache { + /// Which texture in the texture cache should be drawn into. + texture: CacheTextureId, + /// The target layer in the above texture. + layer: LayerIndex, + /// The target region within the above layer. + rect: DeviceIntRect, + }, } #[derive(Debug)] @@ -386,7 +393,7 @@ impl RenderTask { let size = match location { RenderTaskLocation::Dynamic(_, size) => size, RenderTaskLocation::Fixed(rect) => rect.size, - RenderTaskLocation::TextureCache(_, _, rect) => rect.size, + RenderTaskLocation::TextureCache { rect, .. } => rect.size, }; render_task_sanity_check(&size); @@ -655,7 +662,7 @@ impl RenderTask { ) } - pub fn new_border( + pub fn new_border_segment( size: DeviceIntSize, instances: Vec, ) -> Self { @@ -839,7 +846,7 @@ impl RenderTask { match self.location { RenderTaskLocation::Fixed(..) => DeviceIntSize::zero(), RenderTaskLocation::Dynamic(_, size) => size, - RenderTaskLocation::TextureCache(_, _, rect) => rect.size, + RenderTaskLocation::TextureCache { rect, .. } => rect.size, } } @@ -868,7 +875,7 @@ impl RenderTask { RenderTaskLocation::Dynamic(None, _) => { (DeviceIntRect::zero(), RenderTargetIndex(0)) } - RenderTaskLocation::TextureCache(_, layer, rect) => { + RenderTaskLocation::TextureCache {layer, rect, .. } => { (rect, RenderTargetIndex(layer as usize)) } } @@ -1044,7 +1051,7 @@ impl RenderTask { RenderTaskLocation::Dynamic(..) => { self.saved_index = Some(SavedTargetIndex::PENDING); } - RenderTaskLocation::TextureCache(..) => { + RenderTaskLocation::TextureCache { .. } => { panic!("Unable to mark a permanently cached task for saving!"); } } @@ -1060,7 +1067,8 @@ pub enum RenderTaskCacheKeyKind { #[allow(dead_code)] Glyph(GpuGlyphCacheKey), Picture(PictureCacheKey), - Border(BorderCacheKey), + BorderEdge(BorderEdgeCacheKey), + BorderCorner(BorderCornerCacheKey), LineDecoration(LineDecorationCacheKey), } @@ -1161,7 +1169,7 @@ impl RenderTaskCache { // Find out what size to alloc in the texture cache. let size = match render_task.location { RenderTaskLocation::Fixed(..) | - RenderTaskLocation::TextureCache(..) => { + RenderTaskLocation::TextureCache { .. } => { panic!("BUG: dynamic task was expected"); } RenderTaskLocation::Dynamic(_, size) => size, @@ -1203,11 +1211,11 @@ impl RenderTaskCache { let (texture_id, texture_layer, uv_rect) = texture_cache.get_cache_location(&entry.handle); - render_task.location = RenderTaskLocation::TextureCache( - texture_id, - texture_layer, - uv_rect.to_i32() - ); + render_task.location = RenderTaskLocation::TextureCache { + texture: texture_id, + layer: texture_layer, + rect: uv_rect.to_i32(), + }; } } } diff --git a/gfx/webrender/src/renderer.rs b/gfx/webrender/src/renderer.rs index 94e9fe0829bc..66bec87ef77e 100644 --- a/gfx/webrender/src/renderer.rs +++ b/gfx/webrender/src/renderer.rs @@ -36,7 +36,7 @@ use batch::{BatchKind, BatchTextures, BrushBatchKind}; use capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage}; use debug_colors; use device::{DepthFunction, Device, FrameId, Program, UploadMethod, Texture, PBO}; -use device::{ExternalTexture, FBOId, TextureSlot}; +use device::{ExternalTexture, FBOId, TextureDrawTarget, TextureReadTarget, TextureSlot}; use device::{ShaderError, TextureFilter, VertexUsageHint, VAO, VBO, CustomVAO}; use device::{ProgramCache, ReadPixelsFormat}; @@ -54,7 +54,7 @@ use gpu_glyph_renderer::GpuGlyphRenderer; use gpu_types::ScalingInstance; use internal_types::{TextureSource, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError}; use internal_types::{CacheTextureId, DebugOutput, FastHashMap, RenderedDocument, ResultMsg}; -use internal_types::{TextureUpdateList, TextureUpdateOp, TextureUpdateSource}; +use internal_types::{LayerIndex, TextureUpdateList, TextureUpdateOp, TextureUpdateSource}; use internal_types::{RenderTargetInfo, SavedTargetIndex}; use prim_store::DeferredResolve; use profiler::{BackendProfileCounters, FrameProfileCounters, @@ -1337,7 +1337,11 @@ impl GpuCacheTexture { device.bind_program(program); device.bind_custom_vao(vao); device.bind_draw_target( - Some((texture, 0)), + Some(TextureDrawTarget { + texture, + layer: 0, + with_depth: false, + }), Some(texture.get_dimensions()), ); device.draw_nonindexed_points(0, count as _); @@ -2918,7 +2922,7 @@ impl Renderer { fn handle_readback_composite( &mut self, - render_target: Option<(&Texture, i32)>, + render_target: Option, framebuffer_size: DeviceUintSize, scissor_rect: Option, source: &RenderTask, @@ -2951,7 +2955,11 @@ impl Renderer { // Called per-instance in case the layer (and therefore FBO) // changes. The device will skip the GL call if the requested // target is already bound. - let cache_draw_target = (cache_texture, readback_layer.0 as i32); + let cache_draw_target = TextureDrawTarget { + texture: cache_texture, + layer: readback_layer.0 as usize, + with_depth: false, + }; self.device.bind_draw_target(Some(cache_draw_target), None); let mut src = DeviceIntRect::new( @@ -2968,7 +2976,7 @@ impl Renderer { dest.size.height = -dest.size.height; } - self.device.bind_read_target(render_target); + self.device.bind_read_target(render_target.map(|r| r.into())); self.device.blit_render_target(src, dest); // Restore draw target to current pass render target + layer. @@ -2997,22 +3005,22 @@ impl Renderer { let source_rect = match blit.source { BlitJobSource::Texture(texture_id, layer, source_rect) => { // A blit from a texture into this target. - let src_texture = self.texture_resolver + let texture = self.texture_resolver .resolve(&texture_id) .expect("BUG: invalid source texture"); - self.device.bind_read_target(Some((src_texture, layer))); + self.device.bind_read_target(Some(TextureReadTarget { texture, layer: layer as usize })); source_rect } BlitJobSource::RenderTask(task_id) => { // A blit from the child render task into this target. // TODO(gw): Support R8 format here once we start // creating mips for alpha masks. - let src_texture = self.texture_resolver + let texture = self.texture_resolver .resolve(&TextureSource::PrevPassColor) .expect("BUG: invalid source texture"); let source = &render_tasks[task_id]; let (source_rect, layer) = source.get_target_rect(); - self.device.bind_read_target(Some((src_texture, layer.0 as i32))); + self.device.bind_read_target(Some(TextureReadTarget { texture, layer: layer.0 })); source_rect } }; @@ -3059,7 +3067,7 @@ impl Renderer { fn draw_color_target( &mut self, - render_target: Option<(&Texture, i32)>, + render_target: Option, target: &ColorRenderTarget, framebuffer_target_rect: DeviceUintRect, target_size: DeviceUintSize, @@ -3074,8 +3082,8 @@ impl Renderer { let _gm = self.gpu_profile.start_marker("color target"); // sanity check for the depth buffer - if let Some((texture, _)) = render_target { - assert!(texture.has_depth() >= target.needs_depth()); + if let Some(t) = render_target { + assert!(t.texture.supports_depth() >= target.needs_depth()); } let framebuffer_kind = if render_target.is_none() { @@ -3372,7 +3380,7 @@ impl Renderer { dest_rect.origin.y += dest_rect.size.height; dest_rect.size.height *= -1; - self.device.bind_read_target(render_target); + self.device.bind_read_target(render_target.map(|r| r.into())); self.device.bind_external_draw_target(fbo_id); self.device.blit_render_target(src_rect, dest_rect); handler.unlock(output.pipeline_id); @@ -3382,7 +3390,7 @@ impl Renderer { fn draw_alpha_target( &mut self, - render_target: (&Texture, i32), + render_target: TextureDrawTarget, target: &AlphaRenderTarget, target_size: DeviceUintSize, projection: &Transform3D, @@ -3527,7 +3535,7 @@ impl Renderer { fn draw_texture_cache_target( &mut self, texture: &CacheTextureId, - layer: i32, + layer: LayerIndex, target: &TextureCacheRenderTarget, render_tasks: &RenderTaskTree, stats: &mut RendererStats, @@ -3561,8 +3569,11 @@ impl Renderer { let texture = self.texture_resolver .resolve(&texture_source) .expect("BUG: invalid target texture"); - self.device - .bind_draw_target(Some((texture, layer)), Some(target_size)); + self.device.bind_draw_target(Some(TextureDrawTarget { + texture, + layer, + with_depth: false, + }), Some(target_size)); } self.device.disable_depth(); @@ -3801,7 +3812,7 @@ impl Renderer { // create a new texture. let selector = TargetSelector { size: list.max_size, - num_layers: list.targets.len() as _, + num_layers: list.targets.len(), format: list.format, }; let index = self.texture_resolver.render_target_pool @@ -3809,7 +3820,7 @@ impl Renderer { .position(|texture| { selector == TargetSelector { size: texture.get_dimensions(), - num_layers: texture.get_render_target_layer_count(), + num_layers: texture.get_layer_count() as usize, format: texture.get_format(), } }); @@ -3980,7 +3991,11 @@ impl Renderer { ); self.draw_alpha_target( - (&alpha_tex.as_ref().unwrap().texture, target_index as i32), + TextureDrawTarget { + texture: &alpha_tex.as_ref().unwrap().texture, + layer: target_index, + with_depth: false, + }, target, alpha.max_size, &projection, @@ -4002,7 +4017,11 @@ impl Renderer { ); self.draw_color_target( - Some((&color_tex.as_ref().unwrap().texture, target_index as i32)), + Some(TextureDrawTarget { + texture: &color_tex.as_ref().unwrap().texture, + layer: target_index, + with_depth: target.needs_depth(), + }), target, frame.inner_rect, color.max_size, @@ -4106,7 +4125,7 @@ impl Renderer { let fb_width = framebuffer_size.width as i32; let num_layers: i32 = self.texture_resolver.render_target_pool .iter() - .map(|texture| texture.get_render_target_layer_count() as i32) + .map(|texture| texture.get_layer_count() as i32) .sum(); if num_layers * (size + spacing) > fb_width { @@ -4120,10 +4139,9 @@ impl Renderer { let dimensions = texture.get_dimensions(); let src_rect = DeviceIntRect::new(DeviceIntPoint::zero(), dimensions.to_i32()); - let layer_count = texture.get_render_target_layer_count(); - for layer_index in 0 .. layer_count { - self.device - .bind_read_target(Some((texture, layer_index as i32))); + let layer_count = texture.get_layer_count() as usize; + for layer in 0 .. layer_count { + self.device.bind_read_target(Some(TextureReadTarget { texture, layer })); let x = fb_width - (spacing + size) * (target_index + 1); let y = spacing; @@ -4168,9 +4186,9 @@ impl Renderer { DeviceIntSize::new(dimensions.width as i32, dimensions.height as i32), ); - let layer_count = texture.get_layer_count(); - for layer_index in 0 .. layer_count { - self.device.bind_read_target(Some((texture, layer_index))); + let layer_count = texture.get_layer_count() as usize; + for layer in 0 .. layer_count { + self.device.bind_read_target(Some(TextureReadTarget { texture, layer})); let x = fb_width - (spacing + size) * (i as i32 + 1); @@ -4277,7 +4295,7 @@ impl Renderer { let size = texture.get_dimensions(); let mut texels = vec![0; (size.width * size.height * 16) as usize]; self.device.begin_frame(); - self.device.bind_read_target(Some((texture, 0))); + self.device.bind_read_target(Some(TextureReadTarget { texture, layer: 0 })); self.device.read_pixels_into( DeviceUintRect::new(DeviceUintPoint::zero(), size), ReadPixelsFormat::Standard(ImageFormat::RGBAF32), @@ -4362,6 +4380,9 @@ impl Renderer { // Texture cache and render target GPU memory. report += self.texture_resolver.report_memory(); + // Textures held internally within the device layer. + report += self.device.report_memory(); + report } @@ -4631,7 +4652,6 @@ struct PlainTexture { size: (u32, u32, i32), format: ImageFormat, filter: TextureFilter, - render_target: Option, } @@ -4741,7 +4761,6 @@ impl Renderer { size: (rect.size.width, rect.size.height, texture.get_layer_count()), format: texture.get_format(), filter: texture.get_filter(), - render_target: texture.get_render_target(), } } @@ -4749,6 +4768,7 @@ impl Renderer { fn load_texture( target: TextureTarget, plain: &PlainTexture, + rt_info: Option, root: &PathBuf, device: &mut Device ) -> (Texture, Vec) @@ -4768,7 +4788,7 @@ impl Renderer { plain.size.0, plain.size.1, plain.filter, - plain.render_target, + rt_info, plain.size.2, ); device.upload_texture_immediate(&texture, &texels); @@ -4939,6 +4959,7 @@ impl Renderer { let t = Self::load_texture( TextureTarget::Array, &texture, + Some(RenderTargetInfo { has_depth: false }), &root, &mut self.device ); @@ -4952,6 +4973,7 @@ impl Renderer { let (t, gpu_cache_data) = Self::load_texture( TextureTarget::Default, &renderer.gpu_cache, + Some(RenderTargetInfo { has_depth: false }), &root, &mut self.device, ); @@ -4996,11 +5018,11 @@ impl Renderer { size: (descriptor.size.width, descriptor.size.height, layer_count), format: descriptor.format, filter, - render_target: None, }; let t = Self::load_texture( target, &plain_tex, + None, &root, &mut self.device ); diff --git a/gfx/webrender/src/spatial_node.rs b/gfx/webrender/src/spatial_node.rs index bccbf90d4167..debe4ec60fa9 100644 --- a/gfx/webrender/src/spatial_node.rs +++ b/gfx/webrender/src/spatial_node.rs @@ -122,7 +122,6 @@ impl SpatialNode { let source_perspective = source_perspective.map_or_else( LayoutFastTransform::identity, |perspective| perspective.into()); let info = ReferenceFrameInfo { - resolved_transform: LayoutFastTransform::identity(), source_transform: source_transform.unwrap_or(PropertyBinding::Value(identity)), source_perspective, origin_in_parent_reference_frame, @@ -256,7 +255,7 @@ impl SpatialNode { let scrolled_perspective = info.source_perspective .pre_translate(&state.parent_accumulated_scroll_offset) .post_translate(-state.parent_accumulated_scroll_offset); - info.resolved_transform = + let resolved_transform = LayoutFastTransform::with_vector(info.origin_in_parent_reference_frame) .pre_mul(&source_transform.into()) .pre_mul(&scrolled_perspective); @@ -265,7 +264,7 @@ impl SpatialNode { // our parent reference frame, plus any accumulated scrolling offsets from nodes // between our reference frame and this node. Finally, we also include // whatever local transformation this reference frame provides. - let relative_transform = info.resolved_transform + let relative_transform = resolved_transform .post_translate(state.parent_accumulated_scroll_offset) .to_transform() .with_destination::(); @@ -622,10 +621,6 @@ impl ScrollFrameInfo { /// Contains information about reference frames. #[derive(Copy, Clone, Debug)] pub struct ReferenceFrameInfo { - /// The transformation that establishes this reference frame, relative to the parent - /// reference frame. The origin of the reference frame is included in the transformation. - pub resolved_transform: LayoutFastTransform, - /// The source transform and perspective matrices provided by the stacking context /// that forms this reference frame. We maintain the property binding information /// here so that we can resolve the animated transform and update the tree each diff --git a/gfx/webrender/src/texture_cache.rs b/gfx/webrender/src/texture_cache.rs index fae9e5e274a4..3a53c944d774 100644 --- a/gfx/webrender/src/texture_cache.rs +++ b/gfx/webrender/src/texture_cache.rs @@ -9,7 +9,7 @@ use device::TextureFilter; use freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle}; use gpu_cache::{GpuCache, GpuCacheHandle}; use gpu_types::{ImageSource, UvRectKind}; -use internal_types::{CacheTextureId, TextureUpdateList, TextureUpdateSource}; +use internal_types::{CacheTextureId, LayerIndex, TextureUpdateList, TextureUpdateSource}; use internal_types::{RenderTargetInfo, TextureSource, TextureUpdate, TextureUpdateOp}; use profiler::{ResourceProfileCounter, TextureCacheProfileCounters}; use render_backend::FrameId; @@ -541,12 +541,14 @@ impl TextureCache { } } - // A more detailed version of get(). This allows access to the actual - // device rect of the cache allocation. + /// A more detailed version of get(). This allows access to the actual + /// device rect of the cache allocation. + /// + /// Returns a tuple identifying the texture, the layer, and the region. pub fn get_cache_location( &self, handle: &TextureCacheHandle, - ) -> (CacheTextureId, i32, DeviceUintRect) { + ) -> (CacheTextureId, LayerIndex, DeviceUintRect) { let handle = handle .entry .as_ref() @@ -567,7 +569,7 @@ impl TextureCache { } => (layer_index, origin), }; (entry.texture_id, - layer_index as i32, + layer_index as usize, DeviceUintRect::new(origin, entry.size)) } diff --git a/gfx/webrender/src/tiling.rs b/gfx/webrender/src/tiling.rs index f7cc3c6c5aca..1b011d10f7ea 100644 --- a/gfx/webrender/src/tiling.rs +++ b/gfx/webrender/src/tiling.rs @@ -285,10 +285,8 @@ impl RenderTargetList { pub fn check_ready(&self, t: &Texture) { assert_eq!(t.get_dimensions(), self.max_size); assert_eq!(t.get_format(), self.format); - assert_eq!(t.get_render_target_layer_count(), self.targets.len()); assert_eq!(t.get_layer_count() as usize, self.targets.len()); - assert_eq!(t.has_depth(), t.get_rt_info().unwrap().has_depth); - assert_eq!(t.has_depth(), self.needs_depth()); + assert!(t.supports_depth() >= self.needs_depth()); } } @@ -821,7 +819,7 @@ pub enum RenderPassKind { OffScreen { alpha: RenderTargetList, color: RenderTargetList, - texture_cache: FastHashMap<(CacheTextureId, i32), TextureCacheRenderTarget>, + texture_cache: FastHashMap<(CacheTextureId, usize), TextureCacheRenderTarget>, }, } @@ -950,8 +948,8 @@ impl RenderPass { // Find a target to assign this task to, or create a new // one if required. let texture_target = match task.location { - RenderTaskLocation::TextureCache(texture_id, layer, _) => { - Some((texture_id, layer)) + RenderTaskLocation::TextureCache { texture, layer, .. } => { + Some((texture, layer)) } RenderTaskLocation::Fixed(..) => { None diff --git a/gfx/webrender_api/src/api.rs b/gfx/webrender_api/src/api.rs index a0dc049420dc..9baa9bae6a38 100644 --- a/gfx/webrender_api/src/api.rs +++ b/gfx/webrender_api/src/api.rs @@ -232,7 +232,7 @@ impl Transaction { } pub fn set_pinch_zoom(&mut self, pinch_zoom: ZoomFactor) { - self.scene_ops.push(SceneMsg::SetPinchZoom(pinch_zoom)); + self.frame_ops.push(FrameMsg::SetPinchZoom(pinch_zoom)); } pub fn set_pan(&mut self, pan: DeviceIntPoint) { @@ -523,7 +523,6 @@ pub struct AddFontInstance { pub enum SceneMsg { UpdateEpoch(PipelineId, Epoch), SetPageZoom(ZoomFactor), - SetPinchZoom(ZoomFactor), SetRootPipeline(PipelineId), RemovePipeline(PipelineId), SetDisplayList { @@ -554,6 +553,7 @@ pub enum FrameMsg { GetScrollNodeState(MsgSender>), UpdateDynamicProperties(DynamicProperties), AppendDynamicProperties(DynamicProperties), + SetPinchZoom(ZoomFactor), } impl fmt::Debug for SceneMsg { @@ -562,7 +562,6 @@ impl fmt::Debug for SceneMsg { SceneMsg::UpdateEpoch(..) => "SceneMsg::UpdateEpoch", SceneMsg::SetDisplayList { .. } => "SceneMsg::SetDisplayList", SceneMsg::SetPageZoom(..) => "SceneMsg::SetPageZoom", - SceneMsg::SetPinchZoom(..) => "SceneMsg::SetPinchZoom", SceneMsg::RemovePipeline(..) => "SceneMsg::RemovePipeline", SceneMsg::SetWindowParameters { .. } => "SceneMsg::SetWindowParameters", SceneMsg::SetRootPipeline(..) => "SceneMsg::SetRootPipeline", @@ -582,6 +581,7 @@ impl fmt::Debug for FrameMsg { FrameMsg::EnableFrameOutput(..) => "FrameMsg::EnableFrameOutput", FrameMsg::UpdateDynamicProperties(..) => "FrameMsg::UpdateDynamicProperties", FrameMsg::AppendDynamicProperties(..) => "FrameMsg::AppendDynamicProperties", + FrameMsg::SetPinchZoom(..) => "FrameMsg::SetPinchZoom", }) } } @@ -791,6 +791,7 @@ pub struct MemoryReport { pub vertex_data_textures: usize, pub render_target_textures: usize, pub texture_cache_textures: usize, + pub depth_target_textures: usize, } impl ::std::ops::AddAssign for MemoryReport { @@ -808,6 +809,7 @@ impl ::std::ops::AddAssign for MemoryReport { self.vertex_data_textures += other.vertex_data_textures; self.render_target_textures += other.render_target_textures; self.texture_cache_textures += other.texture_cache_textures; + self.depth_target_textures += other.depth_target_textures; } } diff --git a/gfx/webrender_api/src/display_item.rs b/gfx/webrender_api/src/display_item.rs index 47474ace98e6..b543c69270a6 100644 --- a/gfx/webrender_api/src/display_item.rs +++ b/gfx/webrender_api/src/display_item.rs @@ -435,6 +435,29 @@ impl BorderStyle { pub fn is_hidden(&self) -> bool { *self == BorderStyle::Hidden || *self == BorderStyle::None } + + /// Returns true if the border style itself is opaque. Other + /// factors (such as color, or border radii) may mean that + /// the border segment isn't opaque regardless of this. + pub fn is_opaque(&self) -> bool { + match *self { + BorderStyle::None | + BorderStyle::Double | + BorderStyle::Dotted | + BorderStyle::Dashed | + BorderStyle::Hidden => { + false + } + + BorderStyle::Solid | + BorderStyle::Groove | + BorderStyle::Ridge | + BorderStyle::Inset | + BorderStyle::Outset => { + true + } + } + } } #[repr(u32)] diff --git a/gfx/webrender_bindings/revision.txt b/gfx/webrender_bindings/revision.txt index 829f83699f83..b04fb7b84fd0 100644 --- a/gfx/webrender_bindings/revision.txt +++ b/gfx/webrender_bindings/revision.txt @@ -1 +1 @@ -98d507003c07c003ef0e0297dc4d29ee896a5868 +74f265e447d2927c27d4320c676779956d39eaf0 diff --git a/gfx/webrender_bindings/webrender_ffi_generated.h b/gfx/webrender_bindings/webrender_ffi_generated.h index 9fe1f27cd7dc..96a1c7de649e 100644 --- a/gfx/webrender_bindings/webrender_ffi_generated.h +++ b/gfx/webrender_bindings/webrender_ffi_generated.h @@ -542,6 +542,7 @@ struct MemoryReport { uintptr_t vertex_data_textures; uintptr_t render_target_textures; uintptr_t texture_cache_textures; + uintptr_t depth_target_textures; bool operator==(const MemoryReport& aOther) const { return primitive_stores == aOther.primitive_stores && @@ -556,7 +557,8 @@ struct MemoryReport { gpu_cache_textures == aOther.gpu_cache_textures && vertex_data_textures == aOther.vertex_data_textures && render_target_textures == aOther.render_target_textures && - texture_cache_textures == aOther.texture_cache_textures; + texture_cache_textures == aOther.texture_cache_textures && + depth_target_textures == aOther.depth_target_textures; } }; diff --git a/ipc/app/plugin-container.exe.manifest b/ipc/app/plugin-container.exe.manifest index e3bdc56b0818..3ccedc20c267 100644 --- a/ipc/app/plugin-container.exe.manifest +++ b/ipc/app/plugin-container.exe.manifest @@ -18,6 +18,16 @@ language="*" /> + + + + + diff --git a/ipc/glue/BackgroundChild.h b/ipc/glue/BackgroundChild.h index 8f2e1dea0550..34590c23c75c 100644 --- a/ipc/glue/BackgroundChild.h +++ b/ipc/glue/BackgroundChild.h @@ -7,7 +7,6 @@ #ifndef mozilla_ipc_backgroundchild_h__ #define mozilla_ipc_backgroundchild_h__ -#include "base/process.h" #include "mozilla/Attributes.h" #include "mozilla/ipc/Transport.h" @@ -47,7 +46,6 @@ class BackgroundChild final friend class mozilla::dom::ContentChild; friend class mozilla::dom::ContentParent; - typedef base::ProcessId ProcessId; typedef mozilla::ipc::Transport Transport; public: diff --git a/js/src/builtin/Array.cpp b/js/src/builtin/Array.cpp index ad4fbd98ed85..c643252921aa 100644 --- a/js/src/builtin/Array.cpp +++ b/js/src/builtin/Array.cpp @@ -412,9 +412,6 @@ js::GetElementsWithAdder(JSContext* cx, HandleObject obj, HandleObject receiver, return true; } -static bool -ObjectMayHaveExtraIndexedProperties(JSObject* obj); - static inline bool IsPackedArrayOrNoExtraIndexedProperties(JSObject* obj, uint64_t length) { @@ -1050,8 +1047,8 @@ ObjectMayHaveExtraIndexedOwnProperties(JSObject* obj) * elements. This includes other indexed properties in its shape hierarchy, and * indexed properties or elements along its prototype chain. */ -static bool -ObjectMayHaveExtraIndexedProperties(JSObject* obj) +bool +js::ObjectMayHaveExtraIndexedProperties(JSObject* obj) { MOZ_ASSERT_IF(obj->hasDynamicPrototype(), !obj->isNative()); diff --git a/js/src/builtin/Array.h b/js/src/builtin/Array.h index 977c68ff3d30..8df5611c5155 100644 --- a/js/src/builtin/Array.h +++ b/js/src/builtin/Array.h @@ -196,6 +196,9 @@ array_construct(JSContext* cx, unsigned argc, Value* vp); extern bool IsCrossRealmArrayConstructor(JSContext* cx, const Value& v, bool* result); +extern bool +ObjectMayHaveExtraIndexedProperties(JSObject* obj); + class MOZ_NON_TEMPORARY_CLASS ArraySpeciesLookup final { /* diff --git a/js/src/jit-test/tests/cacheir/bug1494537.js b/js/src/jit-test/tests/cacheir/bug1494537.js new file mode 100644 index 000000000000..7bf139819c40 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1494537.js @@ -0,0 +1,125 @@ +setJitCompilerOption("ion.forceinlineCaches", 1); + +let offsets = [213, 559, 255, 515, 30, 507, 252, 329, 487, 7]; + +function update_index(i, j) { + var offset = offsets[j % offsets.length]; + return i + offset; +} + +function compute_index(initial, count) { + for (var i = 0; i < count; i++) { + initial = update_index(initial, i); + } + return initial; +} + +// This is written so that the IC added in the bug activates. +function mutate_array(array, count, epsilon = 0) { + var index = 0; + for (var i = 0; i < count; i++) { + index = update_index(index, i); + array[index] = i + epsilon; + } + return array[offsets[0]+offsets[1]] === (1 + epsilon) && + array[10] === undefined; +} + +// Monomorphizing mutate_array to ensure we get the IC chains we want +function create_variant(variant) { + var source = mutate_array.toString().replace("mutate_array", "mutate_array_"+variant); + return source; +} + +function test_basic() { + eval(create_variant("basic")); + var x = []; + + var count = 100; + assertEq(mutate_array_basic(x, count), true); + var end = compute_index(0, count); + assertEq(x[end], count - 1); + assertEq(x[end - 1], undefined); +} + +// Ensure the IC respects frozen. +function test_frozen() { + eval(create_variant("frozen")); + var x = []; + Object.freeze(x); + + var count = 100; + assertEq(mutate_array_frozen(x, count), false); + assertEq(x.length, 0); + + var end = compute_index(0, count); + + var y = []; + assertEq(mutate_array_frozen(y, count), true); + assertEq(y[end], count - 1); + Object.freeze(y); + + // After a mutated array is frozen, can't subsequently modify elements + assertEq(mutate_array_frozen(x, count, 10), false); + assertEq(y[end], count - 1); +} + +// Let's make sure updates to the array happen as expected. +function test_update() { + eval(create_variant("update")); + + var x = []; + var count = 100; + assertEq(mutate_array_update(x, count), true); + var end = compute_index(0, count); + assertEq(x[end], count - 1); + assertEq(x[end - 1], undefined); + + var epsilon = 2; + mutate_array_update(x, 200, epsilon); + assertEq(x[end], count -1 + epsilon) +} + +// Elements may be non-writable, let us not write them. +function test_nonwritable() { + eval(create_variant("nonwritable")); + var x = []; + var count = 100; + var index = compute_index(0, 10); + Object.defineProperty(x, index, {value: -10, writable: false}); + mutate_array_nonwritable(x, count); + assertEq(x[index], -10); +} + +// Random indices can get setters, let's make sure we honour those. +function test_setter() { + eval(create_variant("setter")); + var x = []; + var count = 100; + var index = compute_index(0, 80); + var sigil = 0; + Object.defineProperty(x, index, {set(newVal) {sigil++; }}); + mutate_array_setter(x, count); + assertEq(sigil, 1); + assertEq(x[index], undefined); +} + +// Ensure indexes on the prototype don't break things; +// +function test_proto_indices() { + eval(create_variant("proto_indices")); + var x = []; + var count = 100; + var index = compute_index(0, 80); + x.__proto__[index] = "hello"; + mutate_array_proto_indices(x, count); + assertEq(x.__proto__[index], "hello"); + assertEq(x[index], 79); +} + +test_basic(); +test_frozen(); +test_update(); +test_nonwritable(); +test_setter(); +test_proto_indices(); diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp index abef641ffacb..7330137dc03f 100644 --- a/js/src/jit/BaselineCacheIRCompiler.cpp +++ b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -1879,6 +1879,33 @@ BaselineCacheIRCompiler::emitCallProxySetByValue() return true; } +bool +BaselineCacheIRCompiler::emitCallAddOrUpdateSparseElementHelper() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + Register id = allocator.useRegister(masm, reader.int32OperandId()); + ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId()); + bool strict = reader.readBool(); + AutoScratchRegister scratch(allocator, masm); + + allocator.discardStack(masm); + + AutoStubFrame stubFrame(*this); + stubFrame.enter(masm, scratch); + + masm.Push(Imm32(strict)); + masm.Push(val); + masm.Push(id); + masm.Push(obj); + + if (!callVM(masm, AddOrUpdateSparseElementHelperInfo)) { + return false; + } + stubFrame.leave(masm); + return true; +} + + bool BaselineCacheIRCompiler::emitMegamorphicSetElement() { diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index 4b342dd54bce..5f7ad849abef 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -3450,6 +3450,9 @@ SetPropIRGenerator::tryAttachStub() if (tryAttachSetTypedElement(obj, objId, index, indexId, rhsValId)) { return true; } + if (tryAttachAddOrUpdateSparseElement(obj, objId, index, indexId, rhsValId)) { + return true; + } return false; } return false; @@ -4066,6 +4069,89 @@ SetPropIRGenerator::tryAttachSetDenseElementHole(HandleObject obj, ObjOperandId return true; } +// Add an IC for adding or updating a sparse array element. +bool +SetPropIRGenerator::tryAttachAddOrUpdateSparseElement(HandleObject obj, ObjOperandId objId, + uint32_t index, Int32OperandId indexId, + ValOperandId rhsId) +{ + JSOp op = JSOp(*pc_); + MOZ_ASSERT(IsPropertySetOp(op) || IsPropertyInitOp(op)); + + if (op != JSOP_SETELEM && op != JSOP_STRICTSETELEM) { + return false; + } + + if (!obj->isNative()) { + return false; + } + RootedNativeObject nobj(cx_, &obj->as()); + + // We cannot attach a stub to a non-extensible object + if (!nobj->isExtensible()) { + return false; + } + + // Stub doesn't handle negative indices. + if (index > INT_MAX) { + return false; + } + + // We also need to be past the end of the dense capacity, to ensure sparse. + if (index < nobj->getDenseInitializedLength()) { + return false; + } + + // Only handle Array objects in this stub. + if (!nobj->is()) { + return false; + } + RootedArrayObject aobj(cx_, &obj->as()); + + // Don't attach if we're adding to an array with non-writable length. + bool isAdd = (index >= aobj->length()); + if (isAdd && !aobj->lengthIsWritable()) { + return false; + } + + // Indexed properties on the prototype chain aren't handled by the helper. + if (ObjectMayHaveExtraIndexedProperties(aobj->staticPrototype())) { + return false; + } + + // Ensure we are still talking about an array class. + writer.guardClass(objId, GuardClassKind::Array); + + // The helper we are going to call only applies to non-dense elements. + writer.guardIndexGreaterThanDenseInitLength(objId, indexId); + + // Guard extensible: We may be trying to add a new element, and so we'd best + // be able to do so safely. + writer.guardIsExtensible(objId); + + // Ensures we are able to efficiently able to map to an integral jsid. + writer.guardIndexIsNonNegative(indexId); + + // Shape guard the prototype chain to avoid shadowing indexes from appearing. + // Dense elements may appear on the prototype chain (and prototypes may + // have a different notion of which elements are dense), but they can + // only be data properties, so our specialized Set handler is ok to bind + // to them. + ShapeGuardProtoChain(writer, obj, objId); + + // Ensure that if we're adding an element to the object, the object's + // length is writable. + writer.guardIndexIsValidUpdateOrAdd(objId, indexId); + + writer.callAddOrUpdateSparseElementHelper(objId, indexId, rhsId, + /* strict = */op == JSOP_STRICTSETELEM); + writer.returnFromIC(); + + trackAttached("AddOrUpdateSparseElement"); + return true; +} + + bool SetPropIRGenerator::tryAttachSetTypedElement(HandleObject obj, ObjOperandId objId, uint32_t index, Int32OperandId indexId, diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index cb52deec033a..b5e034052bef 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -201,6 +201,7 @@ extern const char* const CacheKindNames[]; _(GuardClass) /* Guard an object class, per GuardClassKind */ \ _(GuardAnyClass) /* Guard an arbitrary class for an object */ \ _(GuardCompartment) \ + _(GuardIsExtensible) \ _(GuardIsNativeFunction) \ _(GuardIsNativeObject) \ _(GuardIsProxy) \ @@ -222,6 +223,9 @@ extern const char* const CacheKindNames[]; _(GuardHasGetterSetter) \ _(GuardGroupHasUnanalyzedNewScript) \ _(GuardIndexIsNonNegative) \ + _(GuardIndexGreaterThanDenseCapacity) \ + _(GuardIndexGreaterThanArrayLength) \ + _(GuardIndexIsValidUpdateOrAdd) \ _(GuardIndexGreaterThanDenseInitLength) \ _(GuardTagNotEqual) \ _(GuardXrayExpandoShapeAndDefaultProto) \ @@ -267,6 +271,7 @@ extern const char* const CacheKindNames[]; _(CallSetArrayLength) \ _(CallProxySet) \ _(CallProxySetByValue) \ + _(CallAddOrUpdateSparseElementHelper) \ _(CallInt32ToString) \ _(CallNumberToString) \ \ @@ -765,6 +770,9 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter // Use RawWord, because compartments never move and it can't be GCed. addStubField(uintptr_t(compartment), StubField::Type::RawWord); } + void guardIsExtensible(ObjOperandId obj) { + writeOpWithOperandId(CacheOp::GuardIsExtensible, obj); + } void guardNoDetachedTypedObjects() { writeOp(CacheOp::GuardNoDetachedTypedObjects); } @@ -811,6 +819,18 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter writeOpWithOperandId(CacheOp::GuardIndexGreaterThanDenseInitLength, obj); writeOperandId(index); } + void guardIndexGreaterThanDenseCapacity(ObjOperandId obj, Int32OperandId index) { + writeOpWithOperandId(CacheOp::GuardIndexGreaterThanDenseCapacity, obj); + writeOperandId(index); + } + void guardIndexGreaterThanArrayLength(ObjOperandId obj, Int32OperandId index) { + writeOpWithOperandId(CacheOp::GuardIndexGreaterThanArrayLength, obj); + writeOperandId(index); + } + void guardIndexIsValidUpdateOrAdd(ObjOperandId obj, Int32OperandId index) { + writeOpWithOperandId(CacheOp::GuardIndexIsValidUpdateOrAdd, obj); + writeOperandId(index); + } void guardTagNotEqual(ValueTagOperandId lhs, ValueTagOperandId rhs) { writeOpWithOperandId(CacheOp::GuardTagNotEqual, lhs); writeOperandId(rhs); @@ -1041,6 +1061,12 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter writeOperandId(rhs); buffer_.writeByte(uint32_t(strict)); } + void callAddOrUpdateSparseElementHelper(ObjOperandId obj, Int32OperandId id, ValOperandId rhs, bool strict) { + writeOpWithOperandId(CacheOp::CallAddOrUpdateSparseElementHelper, obj); + writeOperandId(id); + writeOperandId(rhs); + buffer_.writeByte(uint32_t(strict)); + } StringOperandId callInt32ToString(Int32OperandId id) { StringOperandId res(nextOperandId_++); writeOpWithOperandId(CacheOp::CallInt32ToString, id); @@ -1753,6 +1779,10 @@ class MOZ_RAII SetPropIRGenerator : public IRGenerator bool tryAttachSetDenseElementHole(HandleObject obj, ObjOperandId objId, uint32_t index, Int32OperandId indexId, ValOperandId rhsId); + bool tryAttachAddOrUpdateSparseElement(HandleObject obj, ObjOperandId objId, uint32_t index, + Int32OperandId indexId, ValOperandId rhsId); + + bool tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id, ValOperandId rhsId, bool handleDOMProxies); bool tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id, diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp index f440167e666b..407b71b0593e 100644 --- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -1734,6 +1734,35 @@ CacheIRCompiler::emitGuardClass() return true; } +bool +CacheIRCompiler::emitGuardIsExtensible() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + AutoScratchRegister scratch(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + Address shape(obj, ShapedObject::offsetOfShape()); + masm.loadPtr(shape, scratch); + + Address baseShape(scratch, Shape::offsetOfBaseShape()); + masm.loadPtr(baseShape, scratch); + + Address baseShapeFlags(scratch, BaseShape::offsetOfFlags()); + masm.loadPtr(baseShapeFlags, scratch); + + masm.and32(Imm32(js::BaseShape::NOT_EXTENSIBLE), scratch); + + // Spectre-style checks are not needed here because we do not + // interpret data based on this check. + masm.branch32(Assembler::Equal, scratch, Imm32(js::BaseShape::NOT_EXTENSIBLE), + failure->label()); + return true; +} + bool CacheIRCompiler::emitGuardIsNativeFunction() { @@ -2836,7 +2865,7 @@ CacheIRCompiler::emitGuardIndexGreaterThanDenseInitLength() // Load obj->elements. masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); - // Ensure index >= capacity. + // Ensure index >= initLength. Label outOfBounds; Address capacity(scratch, ObjectElements::offsetOfInitializedLength()); masm.spectreBoundsCheck32(index, capacity, scratch2, &outOfBounds); @@ -2846,6 +2875,89 @@ CacheIRCompiler::emitGuardIndexGreaterThanDenseInitLength() return true; } +bool +CacheIRCompiler::emitGuardIndexGreaterThanDenseCapacity() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + Register index = allocator.useRegister(masm, reader.int32OperandId()); + AutoScratchRegister scratch(allocator, masm); + AutoScratchRegister scratch2(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + // Load obj->elements. + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); + + // Ensure index >= capacity. + Label outOfBounds; + Address capacity(scratch, ObjectElements::offsetOfCapacity()); + masm.spectreBoundsCheck32(index, capacity, scratch2, &outOfBounds); + masm.jump(failure->label()); + masm.bind(&outOfBounds); + + return true; +} + +bool +CacheIRCompiler::emitGuardIndexGreaterThanArrayLength() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + Register index = allocator.useRegister(masm, reader.int32OperandId()); + AutoScratchRegister scratch(allocator, masm); + AutoScratchRegister scratch2(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + // Load obj->elements. + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); + + // Ensure index >= length; + Label outOfBounds; + Address length(scratch, ObjectElements::offsetOfLength()); + masm.spectreBoundsCheck32(index, length, scratch2, &outOfBounds); + masm.jump(failure->label()); + masm.bind(&outOfBounds); + return true; +} + +bool +CacheIRCompiler::emitGuardIndexIsValidUpdateOrAdd() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + Register index = allocator.useRegister(masm, reader.int32OperandId()); + AutoScratchRegister scratch(allocator, masm); + AutoScratchRegister scratch2(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) { + return false; + } + + // Load obj->elements. + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); + + Label success; + + // If length is writable, branch to &success. All indices are writable. + Address flags(scratch, ObjectElements::offsetOfFlags()); + masm.branchTest32(Assembler::Zero, flags, + Imm32(ObjectElements::Flags::NONWRITABLE_ARRAY_LENGTH), + &success); + + // Otherwise, ensure index is in bounds. + Address length(scratch, ObjectElements::offsetOfLength()); + masm.spectreBoundsCheck32(index, length, scratch2, + /* failure = */ failure->label()); + masm.bind(&success); + return true; +} + bool CacheIRCompiler::emitGuardTagNotEqual() { diff --git a/js/src/jit/CacheIRCompiler.h b/js/src/jit/CacheIRCompiler.h index 2614e66eaed9..07304488b800 100644 --- a/js/src/jit/CacheIRCompiler.h +++ b/js/src/jit/CacheIRCompiler.h @@ -32,6 +32,7 @@ namespace jit { _(GuardType) \ _(GuardClass) \ _(GuardGroupHasUnanalyzedNewScript) \ + _(GuardIsExtensible) \ _(GuardIsNativeFunction) \ _(GuardFunctionPrototype) \ _(GuardIsNativeObject) \ @@ -46,6 +47,9 @@ namespace jit { _(GuardAndGetNumberFromString) \ _(GuardAndGetIndexFromString) \ _(GuardIndexIsNonNegative) \ + _(GuardIndexGreaterThanDenseCapacity) \ + _(GuardIndexGreaterThanArrayLength) \ + _(GuardIndexIsValidUpdateOrAdd) \ _(GuardIndexGreaterThanDenseInitLength) \ _(GuardTagNotEqual) \ _(GuardXrayExpandoShapeAndDefaultProto)\ diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp index 6140f72de870..4a928278c434 100644 --- a/js/src/jit/IonCacheIRCompiler.cpp +++ b/js/src/jit/IonCacheIRCompiler.cpp @@ -2265,6 +2265,28 @@ IonCacheIRCompiler::emitCallProxySetByValue() return callVM(masm, ProxySetPropertyByValueInfo); } +bool +IonCacheIRCompiler::emitCallAddOrUpdateSparseElementHelper() +{ + AutoSaveLiveRegisters save(*this); + + Register obj = allocator.useRegister(masm, reader.objOperandId()); + Register id = allocator.useRegister(masm, reader.int32OperandId()); + ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId()); + bool strict = reader.readBool(); + + Label done; + prepareVMCall(masm, save); + + masm.Push(Imm32(strict)); + masm.Push(val); + masm.Push(id); + masm.Push(obj); + + return callVM(masm, AddOrUpdateSparseElementHelperInfo); +} + + bool IonCacheIRCompiler::emitMegamorphicSetElement() { diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index b6db6526c4ad..5a3b0e60b6a2 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -2072,5 +2072,10 @@ typedef bool (*NativeGetElementFn)(JSContext*, HandleNativeObject, HandleValue, const VMFunction NativeGetElementInfo = FunctionInfo(NativeGetElement, "NativeGetProperty"); +typedef bool (*AddOrUpdateSparseElementHelperFn)(JSContext* cx, HandleArrayObject obj, + int32_t int_id, HandleValue v, bool strict); +const VMFunction AddOrUpdateSparseElementHelperInfo = + FunctionInfo(AddOrUpdateSparseElementHelper, "AddOrUpdateSparseElementHelper"); + } // namespace jit } // namespace js diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 6dd045750d0d..da27a134af9b 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -982,6 +982,8 @@ extern const VMFunction ProxyHasOwnInfo; extern const VMFunction NativeGetElementInfo; +extern const VMFunction AddOrUpdateSparseElementHelperInfo; + // TailCall VMFunctions extern const VMFunction DoConcatStringObjectInfo; diff --git a/js/src/jit/x86/Assembler-x86.h b/js/src/jit/x86/Assembler-x86.h index 12faf71d0ee9..58c817c2bd5e 100644 --- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -459,20 +459,11 @@ class Assembler : public AssemblerX86Shared MOZ_ASSERT(dest.size() == 16); masm.vhaddpd_rr(src.encoding(), dest.encoding()); } - void vsubpd(const Operand& src1, FloatRegister src0, FloatRegister dest) { + void vsubpd(FloatRegister src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE2()); MOZ_ASSERT(src0.size() == 16); MOZ_ASSERT(dest.size() == 16); - switch (src1.kind()) { - case Operand::MEM_REG_DISP: - masm.vsubpd_mr(src1.disp(), src1.base(), src0.encoding(), dest.encoding()); - break; - case Operand::MEM_ADDRESS32: - masm.vsubpd_mr(src1.address(), src0.encoding(), dest.encoding()); - break; - default: - MOZ_CRASH("unexpected operand kind"); - } + masm.vsubpd_rr(src1.encoding(), src0.encoding(), dest.encoding()); } void vpunpckldq(FloatRegister src1, FloatRegister src0, FloatRegister dest) { diff --git a/js/src/jit/x86/BaseAssembler-x86.h b/js/src/jit/x86/BaseAssembler-x86.h index 17fa8ce02c62..55bb2c52d0b7 100644 --- a/js/src/jit/x86/BaseAssembler-x86.h +++ b/js/src/jit/x86/BaseAssembler-x86.h @@ -176,14 +176,6 @@ class BaseAssemblerX86 : public BaseAssembler { twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, src1, src0, dst); } - void vsubpd_mr(int32_t offset, RegisterID base, XMMRegisterID src0, XMMRegisterID dst) - { - twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, offset, base, src0, dst); - } - void vsubpd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst) - { - twoByteOpSimd("vsubpd", VEX_PD, OP2_SUBPS_VpsWps, address, src0, dst); - } void vpunpckldq_rr(XMMRegisterID src1, XMMRegisterID src0, XMMRegisterID dst) { twoByteOpSimd("vpunpckldq", VEX_PD, OP2_PUNPCKLDQ, src1, src0, dst); diff --git a/js/src/jit/x86/MacroAssembler-x86.cpp b/js/src/jit/x86/MacroAssembler-x86.cpp index 876af6c6e72f..e62eb48870a0 100644 --- a/js/src/jit/x86/MacroAssembler-x86.cpp +++ b/js/src/jit/x86/MacroAssembler-x86.cpp @@ -1120,15 +1120,6 @@ MacroAssembler::wasmTruncateFloat32ToUInt64(FloatRegister input, Register64 outp // ======================================================================== // Convert floating point. -// vpunpckldq requires 16-byte boundary for memory operand. -// See convertUInt64ToDouble for the details. -MOZ_ALIGNED_DECL(static const uint64_t, 16) TO_DOUBLE[4] = { - 0x4530000043300000LL, - 0x0LL, - 0x4330000000000000LL, - 0x4530000000000000LL -}; - bool MacroAssembler::convertUInt64ToDoubleNeedsTemp() { @@ -1187,8 +1178,16 @@ MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, Regist // here, each 64-bit part of dest represents following double: // HI(dest) = 0x 1.00000HHHHHHHH * 2**84 == 2**84 + 0x HHHHHHHH 00000000 // LO(dest) = 0x 1.00000LLLLLLLL * 2**52 == 2**52 + 0x 00000000 LLLLLLLL - movePtr(ImmWord((uintptr_t)TO_DOUBLE), temp); - vpunpckldq(Operand(temp, 0), dest128, dest128); + // See convertUInt64ToDouble for the details. + static const int32_t CST1[4] = { + 0x43300000, + 0x45300000, + 0x0, + 0x0, + }; + + loadConstantSimd128Int(SimdConstant::CreateX4(CST1), ScratchSimd128Reg); + vpunpckldq(ScratchSimd128Reg, dest128, dest128); // Subtract a constant C2 from dest, for each 64-bit part: // C2 = 0x 45300000 00000000 43300000 00000000 @@ -1198,7 +1197,15 @@ MacroAssembler::convertUInt64ToDouble(Register64 src, FloatRegister dest, Regist // after the operation each 64-bit part of dest represents following: // HI(dest) = double(0x HHHHHHHH 00000000) // LO(dest) = double(0x 00000000 LLLLLLLL) - vsubpd(Operand(temp, sizeof(uint64_t) * 2), dest128, dest128); + static const int32_t CST2[4] = { + 0x0, + 0x43300000, + 0x0, + 0x45300000, + }; + + loadConstantSimd128Int(SimdConstant::CreateX4(CST2), ScratchSimd128Reg); + vsubpd(ScratchSimd128Reg, dest128, dest128); // Add HI(dest) and LO(dest) in double and store it into LO(dest), // LO(dest) = double(0x HHHHHHHH 00000000) + double(0x 00000000 LLLLLLLL) diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index 52c69afd12d0..188695b5c681 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -2107,6 +2107,48 @@ DefineNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, return result.succeed(); } +bool +js::AddOrUpdateSparseElementHelper(JSContext* cx, HandleArrayObject obj, int32_t int_id, + HandleValue v, bool strict) +{ + MOZ_ASSERT(INT_FITS_IN_JSID(int_id)); + RootedId id(cx, INT_TO_JSID(int_id)); + + // This helper doesn't handle the case where the index may be in the dense elements + MOZ_ASSERT(int_id >= 0); + MOZ_ASSERT(uint32_t(int_id) >= obj->getDenseInitializedLength()); + + // First decide if this is an add or an update. Because the IC guards have + // already ensured this exists exterior to the dense array range, and the + // prototype checks have ensured there are no indexes on the prototype, we + // can use the shape lineage to find the element if it exists: + RootedShape shape(cx, obj->lastProperty()->search(cx, id)); + + // If we didn't find the shape, we're on the add path: delegate to + // AddSparseElement: + if (shape == nullptr) { + Rooted desc(cx); + desc.setDataDescriptor(v, JSPROP_ENUMERATE); + desc.assertComplete(); + + return AddOrChangeProperty(cx, obj, id, desc); + } + + // At this point we're updating a property: See SetExistingProperty + if (shape->writable() && shape->isDataProperty()) { + // While all JSID_INT properties use a single TI entry, + // nothing yet has inspected the updated value so we *must* use setSlotWithType(). + obj->setSlotWithType(cx, shape, v, /* overwriting = */ true); + return true; + } + + // We don't know exactly what this object looks like, hit the slowpath. + RootedValue receiver(cx, ObjectValue(*obj)); + JS::ObjectOpResult result; + return SetProperty(cx, obj, id, v, receiver, result) && + result.checkStrictErrorOrWarning(cx, obj, id, strict); +} + /*** [[HasProperty]] *****************************************************************************/ diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h index 0a6cfac8cb32..b3b51723b8cf 100644 --- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -1622,6 +1622,10 @@ bool SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, HandleValue receiver, ObjectOpResult& result); +bool +AddOrUpdateSparseElementHelper(JSContext* cx, HandleArrayObject obj, int32_t int_id, + HandleValue v, bool strict); + /* * Indicates whether an assignment operation is qualified (`x.y = 0`) or * unqualified (`y = 0`). In strict mode, the latter is an error if no such diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 7af4390bf5e3..df0baefd17a6 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -1159,8 +1159,10 @@ class Shape : public gc::TenuredCell void fixupGetterSetterForBarrier(JSTracer* trc); void updateBaseShapeAfterMovingGC(); -#ifdef DEBUG // For JIT usage. + static inline size_t offsetOfBaseShape() { return offsetof(Shape, base_); } + +#ifdef DEBUG static inline size_t offsetOfImmutableFlags() { return offsetof(Shape, immutableFlags); } static inline uint32_t fixedSlotsMask() { return FIXED_SLOTS_MASK; } #endif diff --git a/js/src/vm/ShapedObject.h b/js/src/vm/ShapedObject.h index da5687350e49..b6f3f8fb4e4e 100644 --- a/js/src/vm/ShapedObject.h +++ b/js/src/vm/ShapedObject.h @@ -11,6 +11,8 @@ namespace js { +namespace jit { class CacheIRCompiler; } + /* * Shaped objects are a variant of JSObject that use a GCPtrShape for their * |shapeOrExpando_| field. All objects that point to a js::Shape as their @@ -58,6 +60,8 @@ class ShapedObject : public JSObject // See JSObject::offsetOfGroup() comment. friend class js::jit::MacroAssembler; + friend class js::jit::CacheIRCompiler; + static constexpr size_t offsetOfShape() { static_assert(offsetOfShapeOrExpando() == offsetof(shadow::Object, shape), "shadow shape must match actual shape"); diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp index 6dbaeb563695..dc358e798c4b 100644 --- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -366,7 +366,6 @@ class SCInput { static void getPair(uint64_t data, uint32_t* tagp, uint32_t* datap); MOZ_MUST_USE bool read(uint64_t* p); - MOZ_MUST_USE bool readNativeEndian(uint64_t* p); MOZ_MUST_USE bool readPair(uint32_t* tagp, uint32_t* datap); MOZ_MUST_USE bool readDouble(double* p); MOZ_MUST_USE bool readBytes(void* p, size_t nbytes); @@ -702,18 +701,6 @@ SCInput::read(uint64_t* p) return true; } -bool -SCInput::readNativeEndian(uint64_t* p) -{ - if (!point.canPeek()) { - *p = 0; // initialize to shut GCC up - return reportTruncated(); - } - *p = point.peek(); - point.next(); - return true; -} - bool SCInput::readPair(uint32_t* tagp, uint32_t* datap) { @@ -854,7 +841,7 @@ bool SCInput::readPtr(void** p) { uint64_t u; - if (!readNativeEndian(&u)) { + if (!read(&u)) { return false; } *p = reinterpret_cast(u); diff --git a/js/xpconnect/shell/xpcshell.exe.manifest b/js/xpconnect/shell/xpcshell.exe.manifest index c35b77af4360..1c671f14ef98 100644 --- a/js/xpconnect/shell/xpcshell.exe.manifest +++ b/js/xpconnect/shell/xpcshell.exe.manifest @@ -12,6 +12,16 @@ type="win32" /> XPConnect Shell + + + + + diff --git a/moz.configure b/moz.configure index 2bd815ad5711..c52bd1616258 100755 --- a/moz.configure +++ b/moz.configure @@ -39,6 +39,12 @@ def imply_disable_compile_environment(value): if value: return False +option(env='MOZ_COPY_PDBS', + help='For builds that do not support symbols in the normal fashion,' + ' generate and copy them into the resulting build archive.') + +set_config('MOZ_COPY_PDBS', depends_if('MOZ_COPY_PDBS')(lambda _: True)) + imply_option('--enable-compile-environment', imply_disable_compile_environment) option('--disable-compile-environment', diff --git a/mozglue/build/Makefile.in b/mozglue/build/Makefile.in index 0a86b3c15d73..35d101d5fe2d 100644 --- a/mozglue/build/Makefile.in +++ b/mozglue/build/Makefile.in @@ -11,6 +11,12 @@ mozglue.def: mozglue.def.in $(GLOBAL_DEPS) $(call py_action,preprocessor,$(if $(MOZ_REPLACE_MALLOC),-DMOZ_REPLACE_MALLOC) $(ACDEFINES) $< -o $@) GARBAGE += mozglue.def + +# Rebuild mozglue.dll if the manifest changes - it's included by mozglue.rc. +# (this dependency should really be just for mozglue.dll, not other targets) +# Note the manifest file exists in the tree, so we use the explicit filename +# here. +EXTRA_DEPS += mozglue.dll.manifest endif include $(topsrcdir)/mozglue/build/replace_malloc.mk diff --git a/mozglue/build/moz.build b/mozglue/build/moz.build index dacfa1a2e226..bf086fb154f7 100644 --- a/mozglue/build/moz.build +++ b/mozglue/build/moz.build @@ -35,6 +35,7 @@ if CONFIG['OS_TARGET'] == 'WINNT': DELAYLOAD_DLLS += [ 'user32.dll', ] + RCINCLUDE = 'mozglue.rc' if CONFIG['MOZ_PGO'] and CONFIG['CC_TYPE'] == 'clang-cl': SOURCES += ['cygprofile.cpp'] diff --git a/mozglue/build/mozglue.dll.manifest b/mozglue/build/mozglue.dll.manifest new file mode 100644 index 000000000000..037eae4f77fe --- /dev/null +++ b/mozglue/build/mozglue.dll.manifest @@ -0,0 +1,9 @@ + + + + + diff --git a/mozglue/build/mozglue.rc b/mozglue/build/mozglue.rc new file mode 100644 index 000000000000..a678fa3b3d4f --- /dev/null +++ b/mozglue/build/mozglue.rc @@ -0,0 +1,6 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +1 RT_MANIFEST "mozglue.dll.manifest" diff --git a/parser/html/nsHtml5StreamParser.cpp b/parser/html/nsHtml5StreamParser.cpp index 81e522b97b72..9a6a777d29cc 100644 --- a/parser/html/nsHtml5StreamParser.cpp +++ b/parser/html/nsHtml5StreamParser.cpp @@ -177,14 +177,13 @@ nsHtml5StreamParser::nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor, , mFeedChardet(false) , mInitialEncodingWasFromParentFrame(false) , mHasHadErrors(false) - , mFlushTimer(NS_NewTimer()) + , mFlushTimer(NS_NewTimer(mEventTarget)) , mFlushTimerMutex("nsHtml5StreamParser mFlushTimerMutex") , mFlushTimerArmed(false) , mFlushTimerEverFired(false) , mMode(aMode) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - mFlushTimer->SetTarget(mEventTarget); #ifdef DEBUG mAtomTable.SetPermittedLookupEventTarget(mEventTarget); #endif diff --git a/python/mozbuild/mozbuild/action/check_binary.py b/python/mozbuild/mozbuild/action/check_binary.py index e34211fa38b7..aea4d36d3e25 100644 --- a/python/mozbuild/mozbuild/action/check_binary.py +++ b/python/mozbuild/mozbuild/action/check_binary.py @@ -39,6 +39,7 @@ TARGET = { 'readelf': '{}readelf'.format( buildconfig.substs.get('TOOLCHAIN_PREFIX', '')), 'nm': '{}nm'.format(buildconfig.substs.get('TOOLCHAIN_PREFIX', '')), + 'readobj': '{}readobj'.format(buildconfig.substs.get('TOOLCHAIN_PREFIX', '')), } if buildconfig.substs.get('HAVE_64BIT_BUILD'): @@ -190,7 +191,24 @@ def check_nsmodules(target, binary): symbols.append((int(data[2], 16), GUESSED_NSMODULE_SIZE, name)) else: - for line in get_output(target['nm'], '-P', binary): + # MinGW-Clang, when building pdbs, doesn't include the symbol table into + # the final module. To get the NSModule info, we can look at the exported + # symbols. (#1475562) + if buildconfig.substs['OS_ARCH'] == 'WINNT' and \ + buildconfig.substs['HOST_OS_ARCH'] != 'WINNT': + readobj_output = get_output(target['readobj'], '-coff-exports', binary) + # Transform the output of readobj into nm-like output + output = [] + for line in readobj_output: + if "Name" in line: + name = line.replace("Name:", "").strip() + elif "RVA" in line: + rva = line.replace("RVA:", "").strip() + output.append("%s r %s" % (name, rva)) + else: + output = get_output(target['nm'], '-P', binary) + + for line in output: data = line.split() # Some symbols may not have a size listed at all. if len(data) == 3: diff --git a/taskcluster/scripts/misc/build-clang-7-mingw.sh b/taskcluster/scripts/misc/build-clang-7-mingw.sh index d21ec25868f4..014e880c5893 100755 --- a/taskcluster/scripts/misc/build-clang-7-mingw.sh +++ b/taskcluster/scripts/misc/build-clang-7-mingw.sh @@ -275,6 +275,11 @@ build_windres() { # Manually install only nm and windres cp binutils/windres $INSTALL_DIR/bin/$machine-w64-mingw32-windres cp binutils/nm-new $INSTALL_DIR/bin/$machine-w64-mingw32-nm + + pushd $INSTALL_DIR/bin/ + ln -s llvm-readobj $machine-w64-mingw32-readobj + popd + popd } diff --git a/testing/marionette/reftest.js b/testing/marionette/reftest.js index 45d2d0f2684f..898d52abab39 100644 --- a/testing/marionette/reftest.js +++ b/testing/marionette/reftest.js @@ -97,7 +97,7 @@ reftest.Runner = class { let reftestWin = this.parentWindow.open( "chrome://marionette/content/reftest.xul", "reftest", - "chrome,dialog,height=600,width=600"); + "chrome,height=600,width=600"); await new Promise(resolve => { reftestWin.addEventListener("load", resolve, {once: true}); diff --git a/testing/marionette/reftest.xul b/testing/marionette/reftest.xul index fe2d1cfb4848..2c7b96e0d1dc 100644 --- a/testing/marionette/reftest.xul +++ b/testing/marionette/reftest.xul @@ -1,2 +1,5 @@ - + \ No newline at end of file diff --git a/testing/mozbase/mozdevice/tests/manifest.ini b/testing/mozbase/mozdevice/tests/manifest.ini index aac8b424b6a0..c078c87e7349 100644 --- a/testing/mozbase/mozdevice/tests/manifest.ini +++ b/testing/mozbase/mozdevice/tests/manifest.ini @@ -4,3 +4,4 @@ skip-if = python == 3 [test_socket_connection.py] [test_is_app_installed.py] [test_chown.py] +[test_escape_command_line.py] diff --git a/testing/mozbase/mozdevice/tests/test_escape_command_line.py b/testing/mozbase/mozdevice/tests/test_escape_command_line.py new file mode 100644 index 000000000000..23056ceab0d6 --- /dev/null +++ b/testing/mozbase/mozdevice/tests/test_escape_command_line.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +from __future__ import absolute_import + +import mozunit + + +def test_escape_command_line(mock_adb_object, redirect_stdout_and_assert): + """Test _escape_command_line.""" + cases = { + # expected output : test input + 'adb shell ls -l': ['adb', 'shell', 'ls', '-l'], + 'adb shell "ls -l"': ['adb', 'shell', 'ls -l'], + '-e "if (true)"': ['-e', 'if (true)'], + '-e "if (x === \\"hello\\")"': ['-e', 'if (x === "hello")'], + '-e "if (x === \'hello\')"': ['-e', "if (x === 'hello')"], + } + for expected, input in cases.items(): + assert mock_adb_object._escape_command_line(input) == expected + + +if __name__ == '__main__': + mozunit.main() diff --git a/testing/web-platform/meta/background-fetch/abort.https.window.js.ini b/testing/web-platform/meta/background-fetch/abort.https.window.js.ini new file mode 100644 index 000000000000..6bb44762d35d --- /dev/null +++ b/testing/web-platform/meta/background-fetch/abort.https.window.js.ini @@ -0,0 +1,7 @@ +[abort.https.window.html] + [Calling BackgroundFetchRegistration.abort sets the correct fields and responses are still available] + expected: FAIL + + [Aborting the same registration twice fails] + expected: FAIL + diff --git a/testing/web-platform/meta/background-fetch/fetch.https.window.js.ini b/testing/web-platform/meta/background-fetch/fetch.https.window.js.ini index 21136acc61c6..ebd1f8da01c4 100644 --- a/testing/web-platform/meta/background-fetch/fetch.https.window.js.ini +++ b/testing/web-platform/meta/background-fetch/fetch.https.window.js.ini @@ -38,3 +38,6 @@ [Requests with PUT method require CORS Preflight and succeed.] expected: FAIL + [Registration object gets updated values when a background fetch completes.] + expected: FAIL + diff --git a/testing/web-platform/meta/css/compositing/mix-blend-mode/mix-blend-mode-parent-element-overflow-scroll.html.ini b/testing/web-platform/meta/css/compositing/mix-blend-mode/mix-blend-mode-parent-element-overflow-scroll.html.ini new file mode 100644 index 000000000000..ac02f42f1ccf --- /dev/null +++ b/testing/web-platform/meta/css/compositing/mix-blend-mode/mix-blend-mode-parent-element-overflow-scroll.html.ini @@ -0,0 +1,3 @@ +[mix-blend-mode-parent-element-overflow-scroll.html] + disabled: + if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1499779 diff --git a/testing/web-platform/meta/css/css-properties-values-api/self-utils.html.ini b/testing/web-platform/meta/css/css-properties-values-api/self-utils.html.ini new file mode 100644 index 000000000000..46ebce691a64 --- /dev/null +++ b/testing/web-platform/meta/css/css-properties-values-api/self-utils.html.ini @@ -0,0 +1,7 @@ +[self-utils.html] + [Default initial values of generated properties are valid (self-test).] + expected: FAIL + + [Generated properties respect inherits flag] + expected: FAIL + diff --git a/testing/web-platform/meta/dom/interfaces.html.ini b/testing/web-platform/meta/dom/interfaces.html.ini index 9e9921690b3e..87edc3378456 100644 --- a/testing/web-platform/meta/dom/interfaces.html.ini +++ b/testing/web-platform/meta/dom/interfaces.html.ini @@ -88,7 +88,6 @@ expected: FAIL - [interfaces.html?1-1000] [Test driver] expected: FAIL @@ -103,7 +102,6 @@ [Test driver] expected: FAIL - [Document interface: attribute origin] expected: FAIL @@ -175,7 +173,6 @@ [interfaces.html?exclude=Node] prefs: [dom.window.event.enabled:true] - [Document interface: attribute origin] expected: FAIL @@ -242,3 +239,6 @@ [Range interface: existence and properties of interface prototype object] expected: FAIL + [Document interface: existence and properties of interface prototype object's @@unscopables property] + expected: FAIL + diff --git a/testing/web-platform/meta/feature-policy/experimental-features/lazyload/lazyload-image-attribute-on-sanity-check-tentative.sub.html.ini b/testing/web-platform/meta/feature-policy/experimental-features/lazyload/lazyload-image-attribute-on-sanity-check-tentative.sub.html.ini new file mode 100644 index 000000000000..7682fec0085d --- /dev/null +++ b/testing/web-platform/meta/feature-policy/experimental-features/lazyload/lazyload-image-attribute-on-sanity-check-tentative.sub.html.ini @@ -0,0 +1,4 @@ +[lazyload-image-attribute-on-sanity-check-tentative.sub.html] + [Verify 'lazyload' attribute state 'on' works as expected: image loads only when in viewport.] + expected: FAIL + diff --git a/testing/web-platform/meta/feature-policy/picture-in-picture-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini b/testing/web-platform/meta/feature-policy/picture-in-picture-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini index 036a9f0405a5..908332d10faf 100644 --- a/testing/web-platform/meta/feature-policy/picture-in-picture-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini +++ b/testing/web-platform/meta/feature-policy/picture-in-picture-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html.ini @@ -5,3 +5,6 @@ [Untitled] expected: FAIL + [Feature-Policy allow="picture-in-picture" disallows cross-origin navigation in an iframe.] + expected: FAIL + diff --git a/testing/web-platform/meta/feature-policy/picture-in-picture-disabled-by-feature-policy.https.sub.html.ini b/testing/web-platform/meta/feature-policy/picture-in-picture-disabled-by-feature-policy.https.sub.html.ini index ae3bb4405ef4..a9760fa5eadb 100644 --- a/testing/web-platform/meta/feature-policy/picture-in-picture-disabled-by-feature-policy.https.sub.html.ini +++ b/testing/web-platform/meta/feature-policy/picture-in-picture-disabled-by-feature-policy.https.sub.html.ini @@ -2,3 +2,12 @@ [Untitled] expected: FAIL + [Feature-Policy header: picture-in-picture "none" disallows cross-origin iframes.] + expected: FAIL + + [Feature-Policy header: picture-in-picture "none" disallows same-origin iframes.] + expected: FAIL + + [Feature-Policy header: picture-in-picture "none" disallows the top-level document.] + expected: FAIL + diff --git a/testing/web-platform/meta/feature-policy/reporting/generic-sensor-reporting.https.html.ini b/testing/web-platform/meta/feature-policy/reporting/generic-sensor-reporting.https.html.ini new file mode 100644 index 000000000000..907c9c908c30 --- /dev/null +++ b/testing/web-platform/meta/feature-policy/reporting/generic-sensor-reporting.https.html.ini @@ -0,0 +1,4 @@ +[generic-sensor-reporting.https.html] + [Generic Sensor Report Format] + expected: FAIL + diff --git a/testing/web-platform/meta/fetch/api/request/destination/fetch-destination.https.html.ini b/testing/web-platform/meta/fetch/api/request/destination/fetch-destination.https.html.ini new file mode 100644 index 000000000000..40a1c2e68e65 --- /dev/null +++ b/testing/web-platform/meta/fetch/api/request/destination/fetch-destination.https.html.ini @@ -0,0 +1,5 @@ +[fetch-destination.https.html] + [HTMLLinkElement with rel=preload and as=audio fetches with a "audio" Request.destination] + expected: + if not debug and webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL + diff --git a/testing/web-platform/meta/html/rendering/bindings/the-textarea-element-0/cols-zero.html.ini b/testing/web-platform/meta/html/rendering/bindings/the-textarea-element-0/cols-zero.html.ini index fb6ef36fe347..2ccf5e494d24 100644 --- a/testing/web-platform/meta/html/rendering/bindings/the-textarea-element-0/cols-zero.html.ini +++ b/testing/web-platform/meta/html/rendering/bindings/the-textarea-element-0/cols-zero.html.ini @@ -1,5 +1,3 @@ [cols-zero.html] disabled: if verify and (os == "mac"): fails in verify mode - expected: - if os == "mac": FAIL diff --git a/testing/web-platform/meta/html/rendering/bindings/the-textarea-element-0/rows-zero.html.ini b/testing/web-platform/meta/html/rendering/bindings/the-textarea-element-0/rows-zero.html.ini index 409250047613..0e6281d964c2 100644 --- a/testing/web-platform/meta/html/rendering/bindings/the-textarea-element-0/rows-zero.html.ini +++ b/testing/web-platform/meta/html/rendering/bindings/the-textarea-element-0/rows-zero.html.ini @@ -1,5 +1,3 @@ [rows-zero.html] disabled: if verify and (os == "mac"): fails in verify mode - expected: - if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL diff --git a/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html.ini b/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html.ini index 4975c6c24664..3afd30fda2ac 100644 --- a/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html.ini +++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ol-type-unsupported-invalid.html.ini @@ -1,5 +1,3 @@ [ol-type-unsupported-invalid.html] disabled: if verify and (os == "mac"): fails in verify mode - expected: - if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL diff --git a/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-supported-xhtml.xhtml.ini b/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-supported-xhtml.xhtml.ini index f82b88e94c53..e15855d0f1d4 100644 --- a/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-supported-xhtml.xhtml.ini +++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-supported-xhtml.xhtml.ini @@ -1,5 +1,3 @@ [ul-type-supported-xhtml.xhtml] disabled: if verify and (os == "mac"): fails in verify mode - expected: - if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL diff --git a/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-supported.html.ini b/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-supported.html.ini index 35e5f4595262..40d269eb4ed2 100644 --- a/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-supported.html.ini +++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-supported.html.ini @@ -1,5 +1,3 @@ [ul-type-supported.html] disabled: if verify and (os == "mac"): fails in verify mode - expected: - if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL diff --git a/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html.ini b/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html.ini index b48f1a7d9ebd..2adaba70cb7b 100644 --- a/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html.ini +++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/lists/ul-type-unsupported-invalid.html.ini @@ -1,5 +1,3 @@ [ul-type-unsupported-invalid.html] disabled: if verify and (os == "mac"): fails in verify mode - expected: - if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL diff --git a/testing/web-platform/meta/html/rendering/the-details-element/details-before.html.ini b/testing/web-platform/meta/html/rendering/the-details-element/details-before.html.ini index 2628da449120..005bc0987324 100644 --- a/testing/web-platform/meta/html/rendering/the-details-element/details-before.html.ini +++ b/testing/web-platform/meta/html/rendering/the-details-element/details-before.html.ini @@ -1,7 +1,4 @@ [details-before.html] disabled: if verify and (os == "mac"): fails in verify mode - expected: - if debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): PASS - if not debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): PASS - FAIL + expected: FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-with-base.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-with-base.html.ini index ca83f9719065..5f41542e78db 100644 --- a/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-with-base.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-with-base.html.ini @@ -1,5 +1,3 @@ [iframe-with-base.html] disabled: if verify and (os == "mac"): fails in verify mode - expected: - if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/document-adopt-base-url.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/document-adopt-base-url.html.ini index e81bfea400b9..f8624a345539 100644 --- a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/document-adopt-base-url.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/document-adopt-base-url.html.ini @@ -1,5 +1,3 @@ [document-adopt-base-url.html] disabled: if verify and (os == "mac"): fails in verify mode - expected: - if os == "mac": FAIL diff --git a/testing/web-platform/meta/infrastructure/reftest/reftest_or_0.html.ini b/testing/web-platform/meta/infrastructure/reftest/reftest_or_0.html.ini deleted file mode 100644 index 3884f268904f..000000000000 --- a/testing/web-platform/meta/infrastructure/reftest/reftest_or_0.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[reftest_or_0.html] - expected: - if debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL - if not debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL diff --git a/testing/web-platform/meta/mozilla-sync b/testing/web-platform/meta/mozilla-sync index 0e96d052ad04..f90975d70d99 100644 --- a/testing/web-platform/meta/mozilla-sync +++ b/testing/web-platform/meta/mozilla-sync @@ -1,2 +1,2 @@ -local: 05a38ce912d57ee269bd0af8f0b3ced1bb5f068b -upstream: 9f2daa2a5d08d0d44f680501af2b62292f6264f0 +local: db5952ad2f9586639fd0aeea8a5cd2d9967c101e +upstream: 37d83def16bacfa66abac065f8f5adc8f7e7a4fc diff --git a/testing/web-platform/meta/picture-in-picture/idlharness.window.js.ini b/testing/web-platform/meta/picture-in-picture/idlharness.window.js.ini index 766dd1669017..3dfb3609d11a 100644 --- a/testing/web-platform/meta/picture-in-picture/idlharness.window.js.ini +++ b/testing/web-platform/meta/picture-in-picture/idlharness.window.js.ini @@ -110,3 +110,9 @@ [EnterPictureInPictureEvent interface: existence and properties of interface object] expected: FAIL + [HTMLVideoElement interface: operation requestPictureInPicture()] + expected: FAIL + + [HTMLVideoElement interface: video must inherit property "requestPictureInPicture()" with the proper type] + expected: FAIL + diff --git a/testing/web-platform/meta/service-workers/service-worker/service-worker-header.https.html.ini b/testing/web-platform/meta/service-workers/service-worker/service-worker-header.https.html.ini new file mode 100644 index 000000000000..c1d9879f95c7 --- /dev/null +++ b/testing/web-platform/meta/service-workers/service-worker/service-worker-header.https.html.ini @@ -0,0 +1,4 @@ +[service-worker-header.https.html] + [A request to fetch service worker importScripts() resources during the update should not have Service-Worker header] + expected: TIMEOUT + diff --git a/testing/web-platform/meta/svg/shapes/ellipse-04.svg.ini b/testing/web-platform/meta/svg/shapes/ellipse-04.svg.ini deleted file mode 100644 index 7d468f97d86d..000000000000 --- a/testing/web-platform/meta/svg/shapes/ellipse-04.svg.ini +++ /dev/null @@ -1,3 +0,0 @@ -[ellipse-04.svg] - expected: - if os == "mac": FAIL diff --git a/testing/web-platform/meta/svg/shapes/line-dasharray.svg.ini b/testing/web-platform/meta/svg/shapes/line-dasharray.svg.ini index 6c723c282ee9..fb16519844a6 100644 --- a/testing/web-platform/meta/svg/shapes/line-dasharray.svg.ini +++ b/testing/web-platform/meta/svg/shapes/line-dasharray.svg.ini @@ -1,4 +1,2 @@ [line-dasharray.svg] - expected: - if os == "mac": FAIL restart-after: true diff --git a/testing/web-platform/meta/trusted-types/Document-write.tentative.html.ini b/testing/web-platform/meta/trusted-types/Document-write.tentative.html.ini index fcff1ec25784..5d2f777d21c2 100644 --- a/testing/web-platform/meta/trusted-types/Document-write.tentative.html.ini +++ b/testing/web-platform/meta/trusted-types/Document-write.tentative.html.ini @@ -2,3 +2,6 @@ [document.write with html assigned via policy (successful URL transformation).] expected: FAIL + [document.write with html assigned via policy (successful transformation).] + expected: FAIL + diff --git a/testing/web-platform/meta/url/failure.html.ini b/testing/web-platform/meta/url/failure.html.ini index d72cadfc7a1b..025471036ebe 100644 --- a/testing/web-platform/meta/url/failure.html.ini +++ b/testing/web-platform/meta/url/failure.html.ini @@ -482,3 +482,39 @@ [window.open(): a// should throw] expected: FAIL + [sendBeacon(): https://� should throw] + expected: FAIL + + [window.open(): https://� should throw] + expected: FAIL + + [Location's href: https://� should throw] + expected: FAIL + + [XHR: https://� should throw] + expected: FAIL + + [URL's constructor's base argument: https://� should throw] + expected: FAIL + + [URL's href: https://� should throw] + expected: FAIL + + [sendBeacon(): https://� should throw] + expected: FAIL + + [window.open(): https://� should throw] + expected: FAIL + + [Location's href: https://� should throw] + expected: FAIL + + [XHR: https://� should throw] + expected: FAIL + + [URL's constructor's base argument: https://� should throw] + expected: FAIL + + [URL's href: https://� should throw] + expected: FAIL + diff --git a/testing/web-platform/meta/wasm/idlharness.any.js.ini b/testing/web-platform/meta/wasm/idlharness.any.js.ini deleted file mode 100644 index c5ad27c3c545..000000000000 --- a/testing/web-platform/meta/wasm/idlharness.any.js.ini +++ /dev/null @@ -1,368 +0,0 @@ -[idlharness.any.html] - [Module interface: existence and properties of interface object] - expected: FAIL - - [Module interface object length] - expected: FAIL - - [Module interface object name] - expected: FAIL - - [Module interface: existence and properties of interface prototype object] - expected: FAIL - - [Module interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [Module interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [Module interface: operation exports(Module)] - expected: FAIL - - [Module interface: operation imports(Module)] - expected: FAIL - - [Module interface: operation customSections(Module, USVString)] - expected: FAIL - - [Module must be primary interface of mod] - expected: FAIL - - [Stringification of mod] - expected: FAIL - - [Instance interface: existence and properties of interface object] - expected: FAIL - - [Instance interface object length] - expected: FAIL - - [Instance interface object name] - expected: FAIL - - [Instance interface: existence and properties of interface prototype object] - expected: FAIL - - [Instance interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [Instance interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [Instance interface: attribute exports] - expected: FAIL - - [Instance must be primary interface of instance] - expected: FAIL - - [Stringification of instance] - expected: FAIL - - [Instance interface: instance must inherit property "exports" with the proper type] - expected: FAIL - - [Memory interface: existence and properties of interface object] - expected: FAIL - - [Memory interface object length] - expected: FAIL - - [Memory interface object name] - expected: FAIL - - [Memory interface: existence and properties of interface prototype object] - expected: FAIL - - [Memory interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [Memory interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [Memory interface: operation grow(unsigned long)] - expected: FAIL - - [Memory interface: attribute buffer] - expected: FAIL - - [Memory must be primary interface of memory] - expected: FAIL - - [Stringification of memory] - expected: FAIL - - [Memory interface: memory must inherit property "grow(unsigned long)" with the proper type] - expected: FAIL - - [Memory interface: calling grow(unsigned long) on memory with too few arguments must throw TypeError] - expected: FAIL - - [Memory interface: memory must inherit property "buffer" with the proper type] - expected: FAIL - - [Table interface: existence and properties of interface object] - expected: FAIL - - [Table interface object length] - expected: FAIL - - [Table interface object name] - expected: FAIL - - [Table interface: existence and properties of interface prototype object] - expected: FAIL - - [Table interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [Table interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [Table interface: operation grow(unsigned long)] - expected: FAIL - - [Table interface: operation get(unsigned long)] - expected: FAIL - - [Table interface: operation set(unsigned long, Function)] - expected: FAIL - - [Table interface: attribute length] - expected: FAIL - - [Global interface: existence and properties of interface object] - expected: FAIL - - [Global interface object length] - expected: FAIL - - [Global interface object name] - expected: FAIL - - [Global interface: existence and properties of interface prototype object] - expected: FAIL - - [Global interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [Global interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [Global interface: operation valueOf()] - expected: FAIL - - [Global interface: attribute value] - expected: FAIL - - [CompileError interface: existence and properties of interface object] - expected: FAIL - - [CompileError interface object length] - expected: FAIL - - [CompileError interface object name] - expected: FAIL - - [CompileError interface: existence and properties of interface prototype object] - expected: FAIL - - [CompileError interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [CompileError interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [LinkError interface: existence and properties of interface object] - expected: FAIL - - [LinkError interface object length] - expected: FAIL - - [LinkError interface object name] - expected: FAIL - - [LinkError interface: existence and properties of interface prototype object] - expected: FAIL - - [LinkError interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [LinkError interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [RuntimeError interface: existence and properties of interface object] - expected: FAIL - - [RuntimeError interface object length] - expected: FAIL - - [RuntimeError interface object name] - expected: FAIL - - [RuntimeError interface: existence and properties of interface prototype object] - expected: FAIL - - [RuntimeError interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [RuntimeError interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - -[idlharness.any.worker.html] - [Module interface: existence and properties of interface object] - expected: FAIL - - [Module interface object length] - expected: FAIL - - [Module interface object name] - expected: FAIL - - [Module interface: existence and properties of interface prototype object] - expected: FAIL - - [Module interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [Module interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [Module interface: operation exports(Module)] - expected: FAIL - - [Module interface: operation imports(Module)] - expected: FAIL - - [Module interface: operation customSections(Module, USVString)] - expected: FAIL - - [Module must be primary interface of mod] - expected: FAIL - - [Stringification of mod] - expected: FAIL - - [Instance interface: existence and properties of interface object] - expected: FAIL - - [Instance interface object length] - expected: FAIL - - [Instance interface object name] - expected: FAIL - - [Instance interface: existence and properties of interface prototype object] - expected: FAIL - - [Instance interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [Instance interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [Instance interface: attribute exports] - expected: FAIL - - [Instance must be primary interface of instance] - expected: FAIL - - [Stringification of instance] - expected: FAIL - - [Instance interface: instance must inherit property "exports" with the proper type] - expected: FAIL - - [Memory interface: existence and properties of interface object] - expected: FAIL - - [Memory interface object length] - expected: FAIL - - [Memory interface object name] - expected: FAIL - - [Memory interface: existence and properties of interface prototype object] - expected: FAIL - - [Memory interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [Memory interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [Memory interface: operation grow(unsigned long)] - expected: FAIL - - [Memory interface: attribute buffer] - expected: FAIL - - [Memory must be primary interface of memory] - expected: FAIL - - [Stringification of memory] - expected: FAIL - - [Memory interface: memory must inherit property "grow(unsigned long)" with the proper type] - expected: FAIL - - [Memory interface: calling grow(unsigned long) on memory with too few arguments must throw TypeError] - expected: FAIL - - [Memory interface: memory must inherit property "buffer" with the proper type] - expected: FAIL - - [Table interface: existence and properties of interface object] - expected: FAIL - - [Table interface object length] - expected: FAIL - - [Table interface object name] - expected: FAIL - - [Table interface: existence and properties of interface prototype object] - expected: FAIL - - [Table interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [Table interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [Table interface: operation grow(unsigned long)] - expected: FAIL - - [Table interface: operation get(unsigned long)] - expected: FAIL - - [Table interface: operation set(unsigned long, Function)] - expected: FAIL - - [Table interface: attribute length] - expected: FAIL - - [Global interface: existence and properties of interface object] - expected: FAIL - - [Global interface object length] - expected: FAIL - - [Global interface object name] - expected: FAIL - - [Global interface: existence and properties of interface prototype object] - expected: FAIL - - [Global interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [Global interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [Global interface: operation valueOf()] - expected: FAIL - - [Global interface: attribute value] - expected: FAIL diff --git a/testing/web-platform/meta/webrtc/RTCDTMFSender-ontonechange-long.https.html.ini b/testing/web-platform/meta/webrtc/RTCDTMFSender-ontonechange-long.https.html.ini new file mode 100644 index 000000000000..c899f2930290 --- /dev/null +++ b/testing/web-platform/meta/webrtc/RTCDTMFSender-ontonechange-long.https.html.ini @@ -0,0 +1,9 @@ +[RTCDTMFSender-ontonechange-long.https.html] + [insertDTMF with duration greater than 6000 should be clamped to 6000] + disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1420640 + expected: + if debug and not webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL + if debug and not webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86") and (bits == 32): FAIL + if not debug and not webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL + if not debug and webrender and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL + diff --git a/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/selectors/default_styles/italic_object_default_font-style.html.ini b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/selectors/default_styles/italic_object_default_font-style.html.ini index 10a644945906..92e3c95b2b3f 100644 --- a/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/selectors/default_styles/italic_object_default_font-style.html.ini +++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/selectors/default_styles/italic_object_default_font-style.html.ini @@ -1,5 +1,3 @@ [italic_object_default_font-style.html] disabled: if verify and (os == "mac"): fails in verify mode - expected: - if os == "mac": FAIL diff --git a/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/selectors/default_styles/underline_object_default_font-style.html.ini b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/selectors/default_styles/underline_object_default_font-style.html.ini index c02446d9f650..2ccbb0a37bbc 100644 --- a/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/selectors/default_styles/underline_object_default_font-style.html.ini +++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/selectors/default_styles/underline_object_default_font-style.html.ini @@ -1,5 +1,3 @@ [underline_object_default_font-style.html] disabled: if verify and (os == "mac"): fails in verify mode - expected: - if os == "mac": FAIL diff --git a/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/too_many_cues.html.ini b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/too_many_cues.html.ini index 9bb2b2c25c78..d2a63ceb2880 100644 --- a/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/too_many_cues.html.ini +++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/too_many_cues.html.ini @@ -1,2 +1,5 @@ [too_many_cues.html] - expected: FAIL + expected: + if debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): PASS + if not debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): PASS + FAIL diff --git a/testing/web-platform/tests/background-fetch/abort.https.window.js b/testing/web-platform/tests/background-fetch/abort.https.window.js new file mode 100644 index 000000000000..db01bf94b85f --- /dev/null +++ b/testing/web-platform/tests/background-fetch/abort.https.window.js @@ -0,0 +1,59 @@ +// META: script=/service-workers/service-worker/resources/test-helpers.sub.js +// META: script=resources/utils.js +'use strict'; + +// Covers basic functionality provided by BackgroundFetchManager.abort(). +// https://wicg.github.io/background-fetch/#background-fetch-registration-abort + +backgroundFetchTest(async (test, backgroundFetch) => { + const registration = await backgroundFetch.fetch( + uniqueId(), + ['resources/feature-name.txt', '/serviceworker/resources/slow-response.php']); + + assert_true(await registration.abort()); + assert_false(await registration.abort()); + +}, 'Aborting the same registration twice fails'); + +backgroundFetchTest(async (test, backgroundFetch) => { + const registration = await backgroundFetch.fetch( + uniqueId(), + ['resources/feature-name.txt', '/serviceworker/resources/slow-response.php']); + + await new Promise(resolve => { + let aborted = false; + const expectedResultText = 'Background Fetch'; + + registration.onprogress = async event => { + if (event.target.downloaded < expectedResultText.length) + return; + + if (aborted) + return; + + // Abort after the first file has been downloaded and check the results. + + aborted = true; + assert_true(await registration.abort()); + + const {type, eventRegistration, results} = await getMessageFromServiceWorker(); + + assert_equals(eventRegistration.result, 'failure'); + assert_equals(eventRegistration.failureReason, 'aborted'); + assert_equals(registration.result, 'failure'); + assert_equals(registration.failureReason, 'aborted'); + + assert_equals(type, 'backgroundfetchabort'); + + // The abort might have gone through before the first result was persisted. + if (results.length === 1) { + assert_true(results[0].url.includes('resources/feature-name.txt')); + assert_equals(results[0].status, 200); + assert_equals(results[0].text, expectedResultText); + } + + resolve(); + }; + }); + +}, 'Calling BackgroundFetchRegistration.abort sets the correct fields and responses are still available'); \ No newline at end of file diff --git a/testing/web-platform/tests/background-fetch/fetch.https.window.js b/testing/web-platform/tests/background-fetch/fetch.https.window.js index 77297186ca45..9ed7e94da3bf 100644 --- a/testing/web-platform/tests/background-fetch/fetch.https.window.js +++ b/testing/web-platform/tests/background-fetch/fetch.https.window.js @@ -145,6 +145,24 @@ backgroundFetchTest(async (test, backgroundFetch) => { }, 'Using Background Fetch to successfully fetch a single resource'); +backgroundFetchTest(async (test, backgroundFetch) => { + const registrationId = uniqueId(); + const registration = + await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt'); + + assert_equals(registration.result, ''); + assert_equals(registration.failureReason, ''); + + const {type, eventRegistration, results} = + await getMessageFromServiceWorker(); + assert_equals('backgroundfetchsuccess', type); + + assert_equals(eventRegistration.id, registration.id); + assert_equals(registration.result, 'success'); + assert_equals(registration.failureReason, ''); + +}, 'Registration object gets updated values when a background fetch completes.'); + backgroundFetchTest(async (test, backgroundFetch) => { const registrationId = uniqueId(); @@ -240,6 +258,9 @@ backgroundFetchTest(async (test, backgroundFetch) => { assert_equals(eventRegistration.result, 'failure'); assert_equals(eventRegistration.failureReason, 'bad-status'); + assert_equals(registration.result, 'failure'); + assert_equals(registration.failureReason, 'bad-status'); + }, 'Using Background Fetch to fetch a non-existent resource should fail.'); backgroundFetchTest(async (test, backgroundFetch) => { diff --git a/testing/web-platform/tests/background-fetch/service_workers/sw.js b/testing/web-platform/tests/background-fetch/service_workers/sw.js index af4655dbad4e..2e3fbfff1a83 100644 --- a/testing/web-platform/tests/background-fetch/service_workers/sw.js +++ b/testing/web-platform/tests/background-fetch/service_workers/sw.js @@ -27,3 +27,4 @@ function handleBackgroundFetchUpdateEvent(event) { self.addEventListener('backgroundfetchsuccess', handleBackgroundFetchUpdateEvent); self.addEventListener('backgroundfetchfail', handleBackgroundFetchUpdateEvent); +self.addEventListener('backgroundfetchabort', handleBackgroundFetchUpdateEvent); diff --git a/testing/web-platform/tests/css/css-properties-values-api/registered-property-computation.html b/testing/web-platform/tests/css/css-properties-values-api/registered-property-computation.html index 30d6b4bd609b..cbcc0bea64d4 100644 --- a/testing/web-platform/tests/css/css-properties-values-api/registered-property-computation.html +++ b/testing/web-platform/tests/css/css-properties-values-api/registered-property-computation.html @@ -1,117 +1,115 @@ - + +
+
diff --git a/testing/web-platform/tests/css/css-properties-values-api/resources/utils.js b/testing/web-platform/tests/css/css-properties-values-api/resources/utils.js new file mode 100644 index 000000000000..c4dc3fd5a8d9 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/resources/utils.js @@ -0,0 +1,86 @@ +let next_property_id = 1; + +// Generate a unique property name on the form --prop-N. +function generate_name() { + return `--prop-${next_property_id++}`; +} + +// Produce a compatible initial value for the specified syntax. +function any_initial_value(syntax) { + let components = syntax.split('|').map(x => x.trim()) + let first_component = components[0]; + + if (first_component.endsWith('+') || first_component.endsWith('#')) + first_component = first_component.slice(0, -1); + + switch (first_component) { + case '*': + case '': + return 'NULL'; + case '': + return '0deg'; + case '': + return 'rgb(0, 0, 0)'; + case '': + case '': + return 'url(0)'; + case '': + case '': + case '': + case '': + return '0'; + case '': + return '0%'; + case '': + return '0dpi'; + case '