Merge mozilla-central to mozilla-inbound. a=merge on a CLOSED TREE

This commit is contained in:
Andreea Pavel 2018-06-23 00:52:57 +03:00
Родитель 08c517ede3 ef549e2095
Коммит 2104aead80
41 изменённых файлов: 685 добавлений и 120 удалений

Просмотреть файл

@ -2007,8 +2007,13 @@
// Ctrl (Cmd for mac) key is pressed
if (this.multiselected) {
gBrowser.removeFromMultiSelectedTabs(this);
} else {
gBrowser.addToMultiSelectedTabs(this);
if (this == gBrowser.selectedTab) {
gBrowser.selectedTab = gBrowser.lastMultiSelectedTab;
}
} else if (this != gBrowser.selectedTab) {
for (let tab of [this, gBrowser.selectedTab]) {
gBrowser.addToMultiSelectedTabs(tab);
}
gBrowser.lastMultiSelectedTab = this;
}
return;

Просмотреть файл

@ -7,6 +7,7 @@ support-files =
[browser_accessibility_indicator.js]
skip-if = (verify && debug && (os == 'linux'))
[browser_allow_process_switches_despite_related_browser.js]
[browser_bug_1387976_restore_lazy_tab_browser_muted_state.js]
[browser_bug580956.js]
[browser_close_tab_by_dblclick.js]
[browser_contextmenu_openlink_after_tabnavigated.js]
@ -14,43 +15,43 @@ skip-if = (verify && debug && (os == 'linux'))
support-files =
test_bug1358314.html
[browser_isLocalAboutURI.js]
[browser_tabCloseProbes.js]
[browser_tabSpinnerProbe.js]
skip-if = !e10s # Tab spinner is e10s only.
[browser_tabSwitchPrintPreview.js]
skip-if = os == 'mac'
[browser_multiselect_tabs_active_tab_selected_by_default.js]
[browser_multiselect_tabs_close_using_shortcuts.js]
[browser_multiselect_tabs_close.js]
[browser_multiselect_tabs_mute_unmute.js]
support-files =
../general/audio.ogg
../general/file_mediaPlayback.html
[browser_multiselect_tabs_positional_attrs.js]
[browser_multiselect_tabs_using_Ctrl.js]
[browser_multiselect_tabs_using_Shift.js]
[browser_navigatePinnedTab.js]
[browser_new_file_whitelisted_http_tab.js]
skip-if = !e10s # Test only relevant for e10s.
[browser_new_web_tab_in_file_process_pref.js]
skip-if = !e10s # Pref and test only relevant for e10s.
[browser_newwindow_tabstrip_overflow.js]
[browser_opened_file_tab_navigated_to_web.js]
[browser_new_tab_insert_position.js]
skip-if = (debug && os == 'linux' && bits == 32) #Bug 1455882, disabled on Linux32 for almost permafailing
support-files = file_new_tab_page.html
[browser_new_web_tab_in_file_process_pref.js]
skip-if = !e10s # Pref and test only relevant for e10s.
[browser_newwindow_tabstrip_overflow.js]
[browser_open_newtab_start_observer_notification.js]
[browser_opened_file_tab_navigated_to_web.js]
[browser_overflowScroll.js]
[browser_pinnedTabs.js]
[browser_pinnedTabs_clickOpen.js]
[browser_pinnedTabs_closeByKeyboard.js]
[browser_pinnedTabs.js]
[browser_positional_attributes.js]
skip-if = (verify && (os == 'win' || os == 'mac'))
[browser_preloadedBrowser_zoom.js]
[browser_reload_deleted_file.js]
skip-if = (debug && os == 'mac') || (debug && os == 'linux' && bits == 64) #Bug 1421183, disabled on Linux/OSX for leaked windows
[browser_tabCloseProbes.js]
[browser_tabReorder_overflow.js]
[browser_tabSpinnerProbe.js]
skip-if = !e10s # Tab spinner is e10s only.
[browser_tabSwitchPrintPreview.js]
skip-if = os == 'mac'
[browser_tabswitch_updatecommands.js]
[browser_viewsource_of_data_URI_in_file_process.js]
[browser_visibleTabs_bookmarkAllTabs.js]
[browser_visibleTabs_contextMenu.js]
[browser_open_newtab_start_observer_notification.js]
[browser_bug_1387976_restore_lazy_tab_browser_muted_state.js]
[browser_multiselect_tabs_using_Ctrl.js]
[browser_multiselect_tabs_using_Shift.js]
[browser_multiselect_tabs_close.js]
[browser_multiselect_tabs_positional_attrs.js]
[browser_multiselect_tabs_close_using_shortcuts.js]
[browser_multiselect_tabs_mute_unmute.js]
support-files =
../general/audio.ogg
../general/file_mediaPlayback.html

Просмотреть файл

@ -0,0 +1,49 @@
const PREF_MULTISELECT_TABS = "browser.tabs.multiselect";
add_task(async function multiselectActiveTabByDefault() {
await SpecialPowers.pushPrefEnv({
set: [[PREF_MULTISELECT_TABS, true]]
});
const tab1 = await addTab();
const tab2 = await addTab();
const tab3 = await addTab();
await BrowserTestUtils.switchTab(gBrowser, tab1);
info("Try multiselecting Tab1 (active) with click+CtrlKey");
await triggerClickOn(tab1, { ctrlKey: true });
is(gBrowser.selectedTab, tab1, "Tab1 is active");
ok(!tab1.multiselected,
"Tab1 is not multi-selected because we are not in multi-select context yet");
ok(!tab2.multiselected, "Tab2 is not multi-selected");
ok(!tab3.multiselected, "Tab3 is not multi-selected");
is(gBrowser.multiSelectedTabsCount, 0, "Zero tabs multi-selected");
info("We multi-select tab1 and tab2 with ctrl key down");
await triggerClickOn(tab2, { ctrlKey: true });
await triggerClickOn(tab3, { ctrlKey: true });
is(gBrowser.selectedTab, tab1, "Tab1 is active");
ok(tab1.multiselected, "Tab1 is multi-selected");
ok(tab2.multiselected, "Tab2 is multi-selected");
ok(tab3.multiselected, "Tab3 is multi-selected");
is(gBrowser.multiSelectedTabsCount, 3, "Three tabs multi-selected");
is(gBrowser.lastMultiSelectedTab, tab3, "Tab3 is the last multi-selected tab");
info("Unselect tab1 from multi-selection using ctrlKey");
await BrowserTestUtils.switchTab(gBrowser, triggerClickOn(tab1, { ctrlKey: true }));
isnot(gBrowser.selectedTab, tab1, "Tab1 is not active anymore");
is(gBrowser.selectedTab, tab3, "Tab3 is active");
ok(!tab1.multiselected, "Tab1 is not multi-selected");
ok(tab2.multiselected, "Tab2 is multi-selected");
ok(tab3.multiselected, "Tab3 is multi-selected");
is(gBrowser.multiSelectedTabsCount, 2, "Two tabs multi-selected");
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2);
BrowserTestUtils.removeTab(tab3);
});

Просмотреть файл

@ -18,7 +18,7 @@ add_task(async function usingTabCloseButton() {
is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
await triggerClickOn(tab1, { ctrlKey: true });
await BrowserTestUtils.switchTab(gBrowser, tab1);
await triggerClickOn(tab2, { ctrlKey: true });
ok(tab1.multiselected, "Tab1 is multiselected");
@ -69,7 +69,7 @@ add_task(async function usingTabContextMenu() {
is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
await triggerClickOn(tab1, { ctrlKey: true });
await BrowserTestUtils.switchTab(gBrowser, tab1);
await triggerClickOn(tab2, { ctrlKey: true });
ok(tab1.multiselected, "Tab1 is multiselected");

Просмотреть файл

@ -24,13 +24,13 @@ add_task(async function using_Ctrl_W() {
await triggerClickOn(tab2, { ctrlKey: true });
await triggerClickOn(tab3, { ctrlKey: true });
is(gBrowser.selectedTab, tab1, "Tab1 is focused");
ok(!tab1.multiselected, "Tab1 is not multiselected");
ok(tab1.multiselected, "Tab1 is multiselected");
ok(tab2.multiselected, "Tab2 is multiselected");
ok(tab3.multiselected, "Tab3 is multiselected");
ok(!tab4.multiselected, "Tab4 is not multiselected");
is(gBrowser.multiSelectedTabsCount, 2, "Two multiselected tabs");
is(gBrowser.multiSelectedTabsCount, 3, "Three multiselected tabs");
let tab1Closing = BrowserTestUtils.waitForTabClosing(tab1);
let tab2Closing = BrowserTestUtils.waitForTabClosing(tab2);
let tab3Closing = BrowserTestUtils.waitForTabClosing(tab3);
@ -40,28 +40,29 @@ add_task(async function using_Ctrl_W() {
const shouldBeClosing = key == "w" || AppConstants.platform != "macosx";
if (shouldBeClosing) {
await tab1Closing;
await tab2Closing;
await tab3Closing;
}
is(gBrowser.selectedTab, tab1, "Tab1 is still focused");
ok(!tab1.closing, "Tab1 is not closing");
ok(!tab4.closing, "Tab4 is not closing");
if (shouldBeClosing) {
ok(tab1.closing, "Tab1 is closing");
ok(tab2.closing, "Tab2 is closing");
ok(tab3.closing, "Tab3 is closing");
is(gBrowser.multiSelectedTabsCount, 0, "Zero multiselected tabs");
} else {
ok(!tab1.closing, "Tab1 is not closing");
ok(!tab2.closing, "Tab2 is not closing");
ok(!tab3.closing, "Tab3 is not closing");
is(gBrowser.multiSelectedTabsCount, 2, "Still Two multiselected tabs");
is(gBrowser.multiSelectedTabsCount, 3, "Still Three multiselected tabs");
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2);
BrowserTestUtils.removeTab(tab3);
}
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab4);
}
});

Просмотреть файл

@ -30,7 +30,8 @@ add_task(async function clickWithPrefSet() {
});
let mSelectedTabs = gBrowser._multiSelectedTabsSet;
const initialFocusedTab = gBrowser.selectedTab;
const initialFocusedTab = await addTab();
await BrowserTestUtils.switchTab(gBrowser, initialFocusedTab);
const tab = await addTab();
await triggerClickOn(tab, { ctrlKey: true });
@ -42,6 +43,7 @@ add_task(async function clickWithPrefSet() {
ok(!tab.multiselected && !mSelectedTabs.has(tab), "Tab is not (multi) selected anymore");
is(gBrowser.selectedTab, initialFocusedTab, "Focused tab still doesn't change");
BrowserTestUtils.removeTab(initialFocusedTab);
BrowserTestUtils.removeTab(tab);
});
@ -56,8 +58,8 @@ add_task(async function clearSelection() {
const tab2 = await addTab();
const tab3 = await addTab();
info("We multi-select tab1 and tab2 with ctrl key down");
await triggerClickOn(tab1, { ctrlKey: true });
await BrowserTestUtils.switchTab(gBrowser, tab1);
info("We multi-select tab2 with ctrl key down");
await triggerClickOn(tab2, { ctrlKey: true });
ok(tab1.multiselected && gBrowser._multiSelectedTabsSet.has(tab1), "Tab1 is (multi) selected");

Просмотреть файл

@ -86,19 +86,20 @@ add_task(async function itemsInTheCollectionBeforeShiftClicking() {
await triggerClickOn(tab3, { ctrlKey: true });
is(gBrowser.selectedTab, tab1, "Tab1 still has focus");
is(gBrowser.multiSelectedTabsCount, 1, "One tab is multi-selected");
is(gBrowser.multiSelectedTabsCount, 2, "Two tabs are multi-selected");
ok(tab1.multiselected && mSelectedTabs.has(tab1), "Tab1 is multi-selected");
ok(tab3.multiselected && mSelectedTabs.has(tab3), "Tab3 is multi-selected");
info("Click on tab5 while holding Shift key");
await triggerClickOn(tab5, { shiftKey: true });
is(gBrowser.selectedTab, tab1, "Tab1 still has focus");
ok(!tab1.multiselected && !mSelectedTabs.has(tab1), "Tab1 is not multi-selected");
ok(tab1.multiselected && mSelectedTabs.has(tab1), "Tab1 is multi-selected");
ok(!tab2.multiselected && !mSelectedTabs.has(tab2), "Tab2 is not multi-selected ");
ok(tab3.multiselected && mSelectedTabs.has(tab3), "Tab3 is multi-selected");
ok(tab4.multiselected && mSelectedTabs.has(tab4), "Tab4 is multi-selected");
ok(tab5.multiselected && mSelectedTabs.has(tab5), "Tab5 is multi-selected");
is(gBrowser.multiSelectedTabsCount, 3, "Three tabs are multi-selected");
is(gBrowser.multiSelectedTabsCount, 4, "Four tabs are multi-selected");
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2);

Просмотреть файл

@ -112,7 +112,7 @@ class AddonsControls extends Component {
dom.button({
id: "load-addon-from-file",
onClick: this.loadAddonFromFile,
}, Strings.GetStringFromName("loadTemporaryAddon"))
}, Strings.GetStringFromName("loadTemporaryAddon2"))
),
AddonsInstallError({
error: this.state.installError,

Просмотреть файл

@ -14,7 +14,7 @@ const FontSize = createFactory(require("./FontSize"));
const FontStyle = createFactory(require("./FontStyle"));
const FontWeight = createFactory(require("./FontWeight"));
const { getStr } = require("../utils/l10n");
const { getStr, getFormatStr } = require("../utils/l10n");
const Types = require("../types");
class FontEditor extends PureComponent {
@ -79,16 +79,45 @@ class FontEditor extends PureComponent {
});
});
}
renderFamilesNotUsed(familiesNotUsed = []) {
if (!familiesNotUsed.length) {
return null;
}
const familiesList = familiesNotUsed.map(family => {
return dom.div(
{
className: "font-family-unused",
},
family
);
});
return dom.details(
{},
dom.summary(
{
className: "font-family-unused-header",
},
getFormatStr("fontinspector.familiesNotUsedLabel", familiesNotUsed.length)
),
familiesList
);
}
/**
* Render font family, font name, and metadata for all fonts used on selected node.
*
* @param {Array} fonts
* Fonts used on selected node.
* @param {Array} families
* Font familes declared on selected node.
* @param {Function} onToggleFontHighlight
* Callback to trigger in-context highlighting of text that uses a font.
* @return {DOMNode}
*/
renderFontFamily(fonts, onToggleFontHighlight) {
renderFontFamily(fonts, families, onToggleFontHighlight) {
if (!fonts.length) {
return null;
}
@ -119,7 +148,8 @@ class FontEditor extends PureComponent {
{
className: "font-control-box",
},
fontList
fontList,
this.renderFamilesNotUsed(families.notUsed)
)
);
}
@ -215,7 +245,7 @@ class FontEditor extends PureComponent {
render() {
const { fontEditor, onToggleFontHighlight } = this.props;
const { fonts, axes, instance, properties } = fontEditor;
const { fonts, families, axes, instance, properties } = fontEditor;
// Pick the first font to show editor controls regardless of how many fonts are used.
const font = fonts[0];
const hasFontAxes = font && font.variationAxes;
@ -237,7 +267,7 @@ class FontEditor extends PureComponent {
// Render empty state message for nodes that don't have font properties.
!hasWeight && this.renderWarning(),
// Always render UI for font family, format and font file URL.
this.renderFontFamily(fonts, onToggleFontHighlight),
this.renderFontFamily(fonts, families, onToggleFontHighlight),
// Render UI for font variation instances if they are defined.
hasFontInstances && this.renderInstances(font.variationInstances, instance),
// Always render UI for font size.

Просмотреть файл

@ -411,8 +411,7 @@ class FontInspector {
const familiesUsedLowercase = families.used.map(family => family.toLowerCase());
// Font family names declared but not used.
families.notUsed = fontFamilies
.map(family => family.toLowerCase())
.filter(family => !familiesUsedLowercase.includes(family));
.filter(family => !familiesUsedLowercase.includes(family.toLowerCase()));
return families;
}

Просмотреть файл

@ -21,6 +21,7 @@ skip-if = !e10s # too slow on !e10s, logging fully serialized actors (Bug 144659
subsuite = clipboard
[browser_fontinspector_edit-previews.js]
[browser_fontinspector_expand-css-code.js]
[browser_fontinspector_family-unused.js]
[browser_fontinspector_other-fonts.js]
[browser_fontinspector_reveal-in-page.js]
[browser_fontinspector_theme-change.js]

Просмотреть файл

@ -27,7 +27,7 @@
}
div {
font-family:Arial;
font-family:bar;
font-family:bar, "Missing Family", sans-serif;
}
.normal-text {
font-family: barnormal;

Просмотреть файл

@ -0,0 +1,39 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TEST_URI = URL_ROOT + "browser_fontinspector.html";
// Test that unused font families show up in the font editor.
add_task(async function() {
await pushPref("devtools.inspector.fonteditor.enabled", true);
const { inspector, view } = await openFontInspectorForURL(TEST_URI);
const viewDoc = view.document;
await testFamiliesUnused(inspector, viewDoc);
await testZeroFamiliesUnused(inspector, viewDoc);
});
function getUnusedFontFamilies(viewDoc) {
return [...viewDoc.querySelectorAll("#font-editor .font-family-unused")]
.map(el => el.textContent);
}
async function testFamiliesUnused(inspector, viewDoc) {
await selectNode("div", inspector);
const unused = getUnusedFontFamilies(viewDoc);
is(unused.length, 2, "Two font families were not used");
is(unused[0], "Missing Family", "First unused family is correct");
is(unused[1], "sans-serif", "Second unused family is correct");
}
async function testZeroFamiliesUnused(inspector, viewDoc) {
await selectNode(".normal-text", inspector);
const unused = getUnusedFontFamilies(viewDoc);
const header = viewDoc.querySelector("#font-editor .font-family-unused-header");
is(unused.length, 0, "All font families were used");
is(header, null, "Container for unused font families was not rendered");
}

Просмотреть файл

@ -8,5 +8,6 @@ const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/font-inspector.properties");
module.exports = {
getFormatStr: (...args) => L10N.getFormatStr(...args),
getStr: (...args) => L10N.getStr(...args),
};

Просмотреть файл

@ -83,7 +83,8 @@ skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keybo
[browser_inspector_highlighter-cssshape_03.js]
[browser_inspector_highlighter-cssshape_04.js]
[browser_inspector_highlighter-cssshape_05.js]
[browser_inspector_highlighter-cssshape_06.js]
[browser_inspector_highlighter-cssshape_06-scale.js]
[browser_inspector_highlighter-cssshape_06-translate.js]
[browser_inspector_highlighter-cssshape_07.js]
[browser_inspector_highlighter-cssshape_iframe_01.js]
skip-if = (verify && debug)

Просмотреть файл

@ -18,7 +18,6 @@ add_task(async function() {
const highlighters = view.highlighters;
const config = { inspector, view, highlighters, testActor, helper };
await testTranslate(config);
await testScale(config);
});
@ -34,48 +33,6 @@ async function teardown(config) {
await toggleShapesHighlighter(view, selector, property, false);
}
async function testTranslate(config) {
const { testActor, helper, highlighters } = config;
const options = { transformMode: true };
const property = "clip-path";
for (const selector of SHAPE_SELECTORS) {
await setup({selector, property, options, ...config});
const { mouse } = helper;
const { center, width, height } = await getBoundingBoxInPx({selector, ...config});
const [x, y] = center;
const dx = width / 10;
const dy = height / 10;
let onShapeChangeApplied;
info(`Translating ${selector}`);
onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
await mouse.down(x, y, selector);
await mouse.move(x + dx, y + dy, selector);
await mouse.up(x + dx, y + dy, selector);
await onShapeChangeApplied;
let newBB = await getBoundingBoxInPx({selector, ...config});
isnot(newBB.center[0], x, `${selector} translated on y axis`);
isnot(newBB.center[1], y, `${selector} translated on x axis`);
info(`Translating ${selector} back`);
onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
await mouse.down(x + dx, y + dy, selector);
await mouse.move(x, y, selector);
await mouse.up(x, y, selector);
await testActor.reflow();
await onShapeChangeApplied;
newBB = await getBoundingBoxInPx({selector, ...config});
is(newBB.center[0], x, `${selector} translated back on x axis`);
is(newBB.center[1], y, `${selector} translated back on y axis`);
await teardown({selector, property, ...config});
}
}
async function testScale(config) {
const { testActor, helper, highlighters } = config;
const options = { transformMode: true };

Просмотреть файл

@ -0,0 +1,103 @@
/* 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/. */
"use strict";
// Test that shapes are updated correctly on mouse events in transform mode.
const TEST_URL = URL_ROOT + "doc_inspector_highlighter_cssshapes.html";
const HIGHLIGHTER_TYPE = "ShapesHighlighter";
const SHAPE_SELECTORS = ["#polygon-transform", "#circle", "#ellipse", "#inset"];
add_task(async function() {
const env = await openInspectorForURL(TEST_URL);
const helper = await getHighlighterHelperFor(HIGHLIGHTER_TYPE)(env);
const {testActor, inspector} = env;
const view = selectRuleView(inspector);
const highlighters = view.highlighters;
const config = { inspector, view, highlighters, testActor, helper };
await testTranslate(config);
});
async function setup(config) {
const { inspector, view, selector, property, options } = config;
await selectNode(selector, inspector);
await toggleShapesHighlighter(view, selector, property, true, options);
}
async function teardown(config) {
const { view, selector, property } = config;
info(`Turn off shapes highlighter for ${selector}`);
await toggleShapesHighlighter(view, selector, property, false);
}
async function testTranslate(config) {
const { testActor, helper, highlighters } = config;
const options = { transformMode: true };
const property = "clip-path";
for (const selector of SHAPE_SELECTORS) {
await setup({selector, property, options, ...config});
const { mouse } = helper;
const { center, width, height } = await getBoundingBoxInPx({selector, ...config});
const [x, y] = center;
const dx = width / 10;
const dy = height / 10;
let onShapeChangeApplied;
info(`Translating ${selector}`);
onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
await mouse.down(x, y, selector);
await mouse.move(x + dx, y + dy, selector);
await mouse.up(x + dx, y + dy, selector);
await testActor.reflow();
await onShapeChangeApplied;
let newBB = await getBoundingBoxInPx({selector, ...config});
isnot(newBB.center[0], x, `${selector} translated on y axis`);
isnot(newBB.center[1], y, `${selector} translated on x axis`);
info(`Translating ${selector} back`);
onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
await mouse.down(x + dx, y + dy, selector);
await mouse.move(x, y, selector);
await mouse.up(x, y, selector);
await testActor.reflow();
await onShapeChangeApplied;
newBB = await getBoundingBoxInPx({selector, ...config});
is(newBB.center[0], x, `${selector} translated back on x axis`);
is(newBB.center[1], y, `${selector} translated back on y axis`);
await teardown({selector, property, ...config});
}
}
async function getBoundingBoxInPx(config) {
const { testActor, selector, inspector, highlighters } = config;
const quads = await testActor.getAllAdjustedQuads(selector);
const { width, height } = quads.content[0].bounds;
const highlightedNode = await getNodeFront(selector, inspector);
const computedStyle = await inspector.pageStyle.getComputed(highlightedNode);
const paddingTop = parseFloat(computedStyle["padding-top"].value);
const paddingLeft = parseFloat(computedStyle["padding-left"].value);
// path is always of form "Mx y Lx y Lx y Lx y Z", where x/y are numbers
const path = await testActor.getHighlighterNodeAttribute(
"shapes-bounding-box", "d", highlighters.highlighters[HIGHLIGHTER_TYPE]);
const coords = path.replace(/[MLZ]/g, "").split(" ").map((n, i) => {
return i % 2 === 0 ? paddingLeft + width * n / 100 : paddingTop + height * n / 100;
});
const nw = [coords[0], coords[1]];
const ne = [coords[2], coords[3]];
const se = [coords[4], coords[5]];
const sw = [coords[6], coords[7]];
const center = [(nw[0] + se[0]) / 2, (nw[1] + se[1]) / 2];
const shapeWidth = Math.sqrt((ne[0] - nw[0]) ** 2 + (ne[1] - nw[1]) ** 2);
const shapeHeight = Math.sqrt((sw[0] - nw[0]) ** 2 + (sw[1] - nw[1]) ** 2);
return { nw, ne, se, sw, center, width: shapeWidth, height: shapeHeight };
}

Просмотреть файл

@ -53,10 +53,10 @@ addonDebugging.tooltip = Turning this on will allow you to debug add-ons and var
# (https://developer.mozilla.org/docs/Tools/about:debugging#Enabling_add-on_debugging)
addonDebugging.learnMore = Learn more
# LOCALIZATION NOTE (loadTemporaryAddon):
# LOCALIZATION NOTE (loadTemporaryAddon2):
# This string is displayed as a label of a button that allows the user to
# load additional add-ons.
loadTemporaryAddon = Load Temporary Add-on
loadTemporaryAddon2 = Load Temporary Add-on
# LOCALIZATION NOTE (addonInstallError):
# This string is displayed when an error occurs while installing an addon.

Просмотреть файл

@ -55,3 +55,8 @@ fontinspector.fontWeightLabel=Weight
# LOCALIZATION NOTE (fontinspector.fontItalicLabel): This label is shown next to the UI
# in the font editor which allows the user to change the style of the font to italic.
fontinspector.fontItalicLabel=Italic
# LOCALIZATION NOTE (fontinspector.familiesNotUsedLabel): This label is shown at the top
# of the list of unused font families. %S is the number of unused font families. It is
# always a positive integer larger than zero.
fontinspector.familiesNotUsedLabel=%S not used

Просмотреть файл

@ -156,6 +156,17 @@
-moz-user-select: none;
}
.font-family-unused-header {
-moz-user-select: none;
margin-bottom: .7em;
cursor: pointer;
}
.font-family-unused {
margin-bottom: .3em;
color: var(--grey-50);
}
.font-instance-select:active{
outline: none;
}

Просмотреть файл

@ -1883,12 +1883,18 @@ class ShapesHighlighter extends AutoRefreshHighlighter {
// used width and height of the reference box as sqrt(width^2+height^2)/sqrt(2).
const computedSize = Math.sqrt((width ** 2) + (height ** 2)) / Math.sqrt(2);
// Position coordinates for circle center in pixels.
const cxPx = width * center[0] / 100;
const cyPx = height * center[1] / 100;
if (radius === "closest-side") {
// radius is the distance from center to closest side of reference box
radius = Math.min(center[0], center[1], 100 - center[0], 100 - center[1]);
radius = Math.min(cxPx, cyPx, width - cxPx, height - cyPx);
radius = coordToPercent(`${radius}px`, computedSize);
} else if (radius === "farthest-side") {
// radius is the distance from center to farthest side of reference box
radius = Math.max(center[0], center[1], 100 - center[0], 100 - center[1]);
radius = Math.max(cxPx, cyPx, width - cxPx, height - cyPx);
radius = coordToPercent(`${radius}px`, computedSize);
} else if (radius.includes("calc(")) {
radius = evalCalcExpression(radius.substring(5, radius.length - 1), computedSize);
} else {

Просмотреть файл

@ -2720,12 +2720,6 @@ HTMLMediaElement::FastSeek(double aTime, ErrorResult& aRv)
already_AddRefed<Promise>
HTMLMediaElement::SeekToNextFrame(ErrorResult& aRv)
{
if (mSeekDOMPromise) {
// We can't perform NextFrameSeek while seek is already in action.
// Just return the pending seek promise.
return do_AddRef(mSeekDOMPromise);
}
/* This will cause JIT code to be kept around longer, to help performance
* when using SeekToNextFrame to iterate through every frame of a video.
*/

Просмотреть файл

@ -876,6 +876,21 @@ public:
// Do nothing. We will resume video decoding in the decoding state.
}
// We specially handle next frame seeks by ignoring them if we're already
// seeking.
RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override
{
if (aTarget.IsNextFrame()) {
// We ignore next frame seeks if we already have a seek pending
SLOG("Already SEEKING, ignoring seekToNextFrame");
MOZ_ASSERT(!mSeekJob.mPromise.IsEmpty(), "Seek shouldn't be finished");
return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true,
__func__);
}
return StateObject::HandleSeek(aTarget);
}
protected:
SeekJob mSeekJob;
EventVisibility mVisibility;

Просмотреть файл

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<title>Bug 1450845: Avoid seek to next frame when already seeking</title>
<script>
async function boom() {
let video = document.getElementById('video');
// Internally play causes a seek, make sure we don't crash during this
video.play();
try {
await document.getElementById('video').seekToNextFrame();
} catch (e) {
// We don't mind if the promise was rejected so long as we don't crash
}
// Didn't crash
// Stop playback and cause a seek to 0
video.pause();
video.currentTime = 0;
try {
await document.getElementById('video').seekToNextFrame();
} finally {
// Didn't crash
document.documentElement.removeAttribute("class");
}
}
window.addEventListener('load', boom)
</script>
</head>
<body>
<video id='video' src='data:video/webm;base64,GkXfowEAAAAAAAAfQoaBAUL3gQFC8oEEQvOBQoWBAhhTgGcBAAAAAAAB6BFNm3RALE27i1OrhBVJqWZTrIHfTbuMU6uEFlSua1OsggEwTbuMU6uEHFO7a1OsggHL7AEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVSalmAQAAAAAAAEUqTYCNTGF2ZjU3LjI5LjEwMVdBjUxhdmY1Ny4yOS4xMDFzpJBAb17Yv2oNAF1ZEESuco33RImIQFCAAAAAAAAWVK5rAQAAAAAAADyuAQAAAAAAADPXgQFzxYEBnIEAIrWcg3VuZIaFVl9WUDmDgQEj44OEAfygVeABAAAAAAAAB7CCAUC6gfAfQ7Z1AQAAAAAAAEfngQCjqYEAAICCSYNCABPwDvYAOCQcGFQAAFBh9jAAABML7AAATEnjdRwIJ+gAo5eBACEAhgBAkpwATEAABCasAABekcXgAB'>
</body>
</html>

Просмотреть файл

@ -97,6 +97,7 @@ load 1384248.html
load 1389304.html
load 1393272.webm
load 1411322.html
load 1450845.html
load disconnect-wrong-destination.html
load analyser-channels-1.html
skip-if(verify&&isDebugBuild&&gtkWidget) load audiocontext-double-suspend.html

Просмотреть файл

@ -0,0 +1,103 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 https://mozilla.org/MPL/2.0/. */
#include "AudioWorkletNode.h"
#include "AudioParamMap.h"
#include "mozilla/dom/AudioWorkletNodeBinding.h"
#include "mozilla/dom/MessagePort.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(AudioWorkletNode, AudioNode)
AudioWorkletNode::AudioWorkletNode(AudioContext* aAudioContext,
const nsAString& aName)
: AudioNode(aAudioContext,
2,
ChannelCountMode::Max,
ChannelInterpretation::Speakers)
, mNodeName(aName)
{
}
/* static */ already_AddRefed<AudioWorkletNode>
AudioWorkletNode::Constructor(const GlobalObject& aGlobal,
AudioContext& aAudioContext,
const nsAString& aName,
const AudioWorkletNodeOptions& aOptions,
ErrorResult& aRv)
{
if (aAudioContext.CheckClosed(aRv)) {
return nullptr;
}
if (aOptions.mNumberOfInputs == 0 && aOptions.mNumberOfOutputs == 0) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr;
}
if (aOptions.mOutputChannelCount.WasPassed()) {
if (aOptions.mOutputChannelCount.Value().Length() !=
aOptions.mNumberOfOutputs) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return nullptr;
}
for (uint32_t channelCount : aOptions.mOutputChannelCount.Value()) {
if (channelCount == 0 ||
channelCount > WebAudioUtils::MaxChannelCount) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr;
}
}
}
RefPtr<AudioWorkletNode> audioWorkletNode =
new AudioWorkletNode(&aAudioContext, aName);
audioWorkletNode->Initialize(aOptions, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return audioWorkletNode.forget();
}
AudioParamMap* AudioWorkletNode::GetParameters(ErrorResult& aRv) const
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return nullptr;
}
MessagePort* AudioWorkletNode::GetPort(ErrorResult& aRv) const
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return nullptr;
}
JSObject*
AudioWorkletNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return AudioWorkletNodeBinding::Wrap(aCx, this, aGivenProto);
}
size_t
AudioWorkletNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
return amount;
}
size_t
AudioWorkletNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 https://mozilla.org/MPL/2.0/. */
#ifndef AudioWorkletNode_h_
#define AudioWorkletNode_h_
#include "AudioNode.h"
namespace mozilla {
namespace dom {
class AudioParamMap;
struct AudioWorkletNodeOptions;
class MessagePort;
class AudioWorkletNode : public AudioNode
{
public:
NS_DECL_ISUPPORTS_INHERITED
IMPL_EVENT_HANDLER(processorerror)
static already_AddRefed<AudioWorkletNode>
Constructor(const GlobalObject& aGlobal,
AudioContext& aAudioContext,
const nsAString& aName,
const AudioWorkletNodeOptions& aOptions,
ErrorResult& aRv);
AudioParamMap* GetParameters(ErrorResult& aRv) const;
MessagePort* GetPort(ErrorResult& aRv) const;
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
const char* NodeType() const override
{
return "AudioWorkletNode";
}
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
private:
AudioWorkletNode(AudioContext* aAudioContext, const nsAString& aName);
~AudioWorkletNode() = default;
nsString mNodeName;
};
} // namespace dom
} // namespace mozilla
#endif // AudioWorkletNode_h_

Просмотреть файл

@ -52,6 +52,7 @@ EXPORTS.mozilla.dom += [
'AudioParamMap.h',
'AudioProcessingEvent.h',
'AudioScheduledSourceNode.h',
'AudioWorkletNode.h',
'BiquadFilterNode.h',
'ChannelMergerNode.h',
'ChannelSplitterNode.h',
@ -89,6 +90,7 @@ UNIFIED_SOURCES += [
'AudioParamMap.cpp',
'AudioProcessingEvent.cpp',
'AudioScheduledSourceNode.cpp',
'AudioWorkletNode.cpp',
'BiquadFilterNode.cpp',
'ChannelMergerNode.cpp',
'ChannelSplitterNode.cpp',

Просмотреть файл

@ -158,6 +158,8 @@ var interfaceNamesInGlobalScope =
{name: "AudioScheduledSourceNode", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "AudioStreamTrack", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "AudioWorkletNode", insecureContext: false, disabled: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "AuthenticatorAssertionResponse"},
// IMPORTANT: Do not change this list without review from a DOM peer!

Просмотреть файл

@ -0,0 +1,29 @@
/* -*- Mode: IDL; 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/.
*
* The origin of this IDL file is
* https://webaudio.github.io/web-audio-api/
*
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
*/
dictionary AudioWorkletNodeOptions : AudioNodeOptions {
unsigned long numberOfInputs = 1;
unsigned long numberOfOutputs = 1;
sequence<unsigned long> outputChannelCount;
record<DOMString, double> parameterData;
object? processorOptions = null;
};
[SecureContext, Pref="dom.audioworklet.enabled",
Constructor (BaseAudioContext context, DOMString name, optional AudioWorkletNodeOptions options)]
interface AudioWorkletNode : AudioNode {
[Throws]
readonly attribute AudioParamMap parameters;
[Throws]
readonly attribute MessagePort port;
attribute EventHandler onprocessorerror;
};

Просмотреть файл

@ -385,6 +385,7 @@ WEBIDL_FILES = [
'AudioTrack.webidl',
'AudioTrackList.webidl',
'AudioWorkletGlobalScope.webidl',
'AudioWorkletNode.webidl',
'AutocompleteInfo.webidl',
'BarProp.webidl',
'BaseAudioContext.webidl',

Просмотреть файл

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Clock</title>
<style type="text/css">
* {
box-sizing: border-box !important;
}
.inner {
width: 100%;
height: 100%;
background: white;
border-radius: 100%;
box-shadow: 0px 0px 18px black inset;
}
</style>
</head>
<body>
<div style="width: 588px; height: 588px; padding: 26px;">
<div class="inner"></div>
</div>
</body></html>

Просмотреть файл

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Clock</title>
<style type="text/css">
* {
box-sizing: border-box !important;
}
.inner {
width: 100%;
height: 100%;
background: white;
border-radius: 100%;
box-shadow: 0px 0px 18px black inset;
}
</style>
</head>
<body>
<div style="width: 588px; height: 588px; padding: 26.01px;">
<div class="inner"></div>
</div>
</body></html>

Просмотреть файл

@ -13,3 +13,4 @@ fuzzy(100,30) == 1149923.html 1149923-ref.html # use fuzzy due to few distorted
== 1435143.html 1435143-ref.html
== 1444904.html 1444904-ref.html
== 1451168.html 1451168-ref.html
fuzzy(5-32,21908-26354) fuzzy-if(webrender,0-1,0-3) == 1463802.html 1463802-ref.html

Просмотреть файл

@ -46,6 +46,7 @@ raptor-firefox-speedometer:
run-on-projects:
by-test-platform:
macosx.*: ['try', 'mozilla-central']
linux64.*: ['try', 'mozilla-central']
default: ['try']
max-run-time: 1500
mozharness:

Просмотреть файл

@ -41,6 +41,8 @@ function pprint(ss, ...values) {
return prettyElement(val);
} else if (["[object Window]", "[object ChromeWindow]"].includes(proto)) {
return prettyWindowGlobal(val);
} else if (proto == "[object Attr]") {
return prettyAttr(val);
}
return prettyObject(val);
}
@ -63,6 +65,10 @@ function pprint(ss, ...values) {
return `[${proto.substring(1, proto.length - 1)} ${win.location}]`;
}
function prettyAttr(obj) {
return `[object Attr ${obj.name}="${obj.value}"]`;
}
function prettyObject(obj) {
let proto = Object.prototype.toString.call(obj);
let s = "";

Просмотреть файл

@ -7,8 +7,8 @@
[DEFAULT]
type = benchmark
test_url = http://localhost:<port>/Speedometer/index.html?raptor
page_cycles = 1
page_timeout = 120000
page_cycles = 5
page_timeout = 180000
unit = score
lower_is_better = false
alert_threshold = 2.0

Просмотреть файл

@ -5,7 +5,7 @@ import json
import os
from datetime import datetime, timedelta
import tarfile
from vcs import Mercurial
import vcs
import requests
from cStringIO import StringIO
@ -14,10 +14,28 @@ def abs_path(path):
def hg_commits(repo_root):
hg = Mercurial.get_func(repo_root)
return [item for item in hg("log", "-fl50", "--template={node}\n",
"testing/web-platform/tests/", "testing/web-platform/mozilla/tests").split("\n")
if item]
hg = vcs.Mercurial.get_func(repo_root)
for item in hg("log", "-fl50", "--template={node}\n", "testing/web-platform/tests",
"testing/web-platform/mozilla/tests").splitlines():
yield item
def git_commits(repo_root):
git = vcs.Git.get_func(repo_root)
for item in git("log", "--format=%H", "-n50", "testing/web-platform/tests",
"testing/web-platform/mozilla/tests").splitlines():
yield git("cinnabar", "git2hg", item)
def get_commits(logger, repo_root):
if vcs.Mercurial.is_hg_repo(repo_root):
return hg_commits(repo_root)
elif vcs.Git.is_git_repo(repo_root):
return git_commits(repo_root)
logger.warning("No VCS found")
return False
def should_download(logger, manifest_path, rebuild_time=timedelta(days=5)):
@ -59,10 +77,12 @@ def taskcluster_url(logger, commits):
def download_manifest(logger, wpt_dir, commits_func, url_func, force=False):
if not force and not should_download(logger, wpt_dir):
if not force and not should_download(logger, os.path.join(wpt_dir, "meta", "MANIFEST.json")):
return False
commits = commits_func()
if not commits:
return False
url = url_func(logger, commits) + "/artifacts/public/manifests.tar.gz"
if not url:
@ -106,7 +126,7 @@ def create_parser():
def download_from_taskcluster(logger, wpt_dir, repo_root, force=False):
return download_manifest(logger, wpt_dir, lambda: hg_commits(repo_root),
return download_manifest(logger, wpt_dir, lambda: get_commits(logger, repo_root),
taskcluster_url, force)

Просмотреть файл

@ -2,19 +2,50 @@ import os
import subprocess
class Mercurial(object):
def __init__(self, repo_root):
self.root = os.path.abspath(repo_root)
self.hg = Mercurial.get_func(repo_root)
@staticmethod
def get_func(repo_path):
def get_func(repo_root):
def hg(cmd, *args):
full_cmd = ["hg", cmd] + list(args)
try:
return subprocess.check_output(full_cmd, cwd=repo_path, stderr=subprocess.STDOUT)
except Exception as e:
raise(e)
return subprocess.check_output(full_cmd, cwd=repo_root)
# TODO: Test on Windows.
return hg
@staticmethod
def is_hg_repo(repo_root):
try:
with open(os.devnull, 'w') as devnull:
subprocess.check_call(["hg", "root"], cwd=repo_root, stdout=devnull,
stderr=devnull)
except subprocess.CalledProcessError:
return False
# TODO: Test on windows
return True
class Git(object):
def __init__(self, repo_root, url_base):
self.root = os.path.abspath(repo_root)
self.git = Git.get_func(repo_root)
@staticmethod
def get_func(repo_root):
def git(cmd, *args):
full_cmd = ["git", cmd] + list(args)
return subprocess.check_output(full_cmd, cwd=repo_root)
# TODO: Test on Windows.
return git
@staticmethod
def is_git_repo(repo_root):
try:
with open(os.devnull, 'w') as devnull:
subprocess.check_call(["git", "rev-parse", "--show-cdup"], cwd=repo_root,
stdout=devnull, stderr=devnull)
except subprocess.CalledProcessError:
return False
# TODO: Test on windows
return True

Просмотреть файл

@ -731,11 +731,21 @@ body:not(.loaded) .toolbar:-moz-locale-dir(rtl) {
list-style: decimal;
}
/* Hide elements with common "hidden" class names */
/* Visually hide (but don't display: none) screen reader elements */
.moz-reader-content .visually-hidden,
.moz-reader-content .visuallyhidden,
.moz-reader-content .hidden,
.moz-reader-content .invisible,
.moz-reader-content .sr-only {
display: inline-block;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
border-width: 0;
}
/* Hide elements with common "hidden" class names */
.moz-reader-content .hidden,
.moz-reader-content .invisible {
display: none;
}

Просмотреть файл

@ -808,6 +808,7 @@ GK_ATOM(onpopuphiding, "onpopuphiding")
GK_ATOM(onpopuppositioned, "onpopuppositioned")
GK_ATOM(onpopupshowing, "onpopupshowing")
GK_ATOM(onpopupshown, "onpopupshown")
GK_ATOM(onprocessorerror, "onprocessorerror")
GK_ATOM(onpush, "onpush")
GK_ATOM(onpushsubscriptionchange, "onpushsubscriptionchange")
GK_ATOM(onRadioStateChange, "onRadioStateChange")