Merge mozilla-central to mozilla-inbound. a=merge

This commit is contained in:
Cosmin Sabou 2018-08-27 19:00:38 +03:00
commit d141575366
64 changed files with 2236 additions and 706 deletions

View file

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<blocklist lastupdate="1534781959544" xmlns="http://www.mozilla.org/2006/addons-blocklist">
<blocklist lastupdate="1535153064735" xmlns="http://www.mozilla.org/2006/addons-blocklist">
<emItems>
<emItem blockID="i334" id="{0F827075-B026-42F3-885D-98981EE7B1AE}">
<prefs/>
@ -2336,6 +2336,18 @@
<prefs/>
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
<emItem blockID="ee2d12a4-ea1d-4f3d-9df1-4303e8993f18" id="@testpilot-addon">
<prefs/>
<versionRange minVersion="0" maxVersion="2.0.8-dev-259fe19" severity="1"/>
</emItem>
<emItem blockID="ab029019-0e93-450a-8c11-ac31556c2a77" id="/^(({aeac6f90-5e17-46fe-8e81-9007264b907d})|({6ee25421-1bd5-4f0c-9924-79eb29a8889d})|({b317fa11-c23d-45b9-9fd8-9df41a094525})|({16ac3e8f-507a-4e04-966b-0247a196c0b4}))$/">
<prefs/>
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
<emItem blockID="46779b5a-2369-4007-bff0-857a657626ba" id="/^((fastplayer@fastsearch\.me)|(ff-search-flash-unlisted@mozilla\.com)|(inspiratiooo-unlisted@mozilla\.com)|(lite-search-ff-unlisted@mozilla\.com)|(mysearchprotect-unlisted@mozilla\.com)|(pdfconverter-unlisted@mozilla\.com)|(plugin-search-ff-unlisted@mozilla\.com)|(pro-search-ff-unlisted@mozilla\.com)|(pro-search-unlisted@mozilla\.com)|(searchincognito-unlisted@mozilla\.com)|(socopoco-search@mozilla\.com)|(socopoco-unlisted@mozilla\.com)|(\{08ea1e08-e237-42e7-ad60-811398c21d58\})|(\{0a56e2a0-a374-48b6-9afc-976680fab110\})|(\{193b040d-2a00-4406-b9ae-e0d345b53201\})|(\{1ffa2e79-7cd4-4fbf-8034-20bcb3463d20\})|(\{528cbbe2-3cde-4331-9344-e348cb310783\})|(\{6f7c2a42-515a-4797-b615-eaa9d78e8c80\})|(\{be2a3fba-7ea2-48b9-bbae-dffa7ae45ef8\})|(\{c0231a6b-c8c8-4453-abc9-c4a999a863bd\}))$/">
<prefs/>
<versionRange minVersion="0" maxVersion="*" severity="3"/>
</emItem>
</emItems>
<pluginItems>
<pluginItem blockID="p332">

View file

@ -100,9 +100,6 @@ let propNameWhitelist = [
// Bug 1441929
{propName: "--theme-search-overlays-semitransparent",
isFromDevTools: true},
// Bug 1441878
{propName: "--theme-codemirror-gutter-background",
isFromDevTools: true},
// These custom properties are retrieved directly from CSSOM
// in videocontrols.xml to get pre-defined style instead of computed
// dimensions, which is why they are not referenced by CSS.

View file

@ -712,9 +712,12 @@ this.tabs = class extends ExtensionAPI {
if (updateProperties.highlighted) {
if (!nativeTab.selected && !nativeTab.multiselected) {
tabbrowser.addToMultiSelectedTabs(nativeTab, false);
// Select the highlighted tab, this matches Chrome's behavior.
tabbrowser.lockClearMultiSelectionOnce();
tabbrowser.selectedTab = nativeTab;
// Select the highlighted tab unless active:false is provided.
// Note that Chrome selects it even in that case.
if (updateProperties.active !== false) {
tabbrowser.lockClearMultiSelectionOnce();
tabbrowser.selectedTab = nativeTab;
}
}
} else {
tabbrowser.removeFromMultiSelectedTabs(nativeTab, true);

View file

@ -115,25 +115,24 @@ add_task(async function test_update_highlighted() {
await expectResults(async () => {
await browser.tabs.update(tab2, {highlighted: true, active: false});
return {active: tab2, highlighted: [tab1, tab2], events: [
["onActivated", {tabId: tab2, windowId}],
return {active: tab1, highlighted: [tab1, tab2], events: [
["onHighlighted", {tabIds: [tab1, tab2], windowId}],
]};
}, "highlighting and (not really) inactivating non-highlighted tab");
}, "highlighting without activating non-highlighted tab");
await expectResults(async () => {
await browser.tabs.update(tab1, {highlighted: true, active: true});
return {active: tab1, highlighted: [tab1], events: [
["onActivated", {tabId: tab1, windowId}],
["onHighlighted", {tabIds: [tab1], windowId}],
await browser.tabs.update(tab2, {highlighted: true, active: true});
return {active: tab2, highlighted: [tab2], events: [
["onActivated", {tabId: tab2, windowId}],
["onHighlighted", {tabIds: [tab2], windowId}],
]};
}, "highlighting and activating inactive highlighted tab");
await expectResults(async () => {
await browser.tabs.update(tab2, {active: true, highlighted: true});
return {active: tab2, highlighted: [tab2], events: [
["onActivated", {tabId: tab2, windowId}],
["onHighlighted", {tabIds: [tab2], windowId}],
await browser.tabs.update(tab1, {active: true, highlighted: true});
return {active: tab1, highlighted: [tab1], events: [
["onActivated", {tabId: tab1, windowId}],
["onHighlighted", {tabIds: [tab1], windowId}],
]};
}, "highlighting and activating non-highlighted tab");

View file

@ -19,8 +19,8 @@ class FontPropertyValue extends PureComponent {
className: PropTypes.string,
defaultValue: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
label: PropTypes.string.isRequired,
min: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
max: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
min: PropTypes.number.isRequired,
max: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
step: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
@ -44,12 +44,18 @@ class FontPropertyValue extends PureComponent {
this.state = {
// Whether the user is dragging the slider thumb or pressing on the numeric stepper.
interactive: false,
value: null,
// Snapshot of the value from props before the user starts editing the number input.
// Used to restore the value when the input is left invalid.
initialValue: this.props.value,
// Snapshot of the value from props. Reconciled with props on blur.
// Used while the user is interacting with the inputs.
value: this.props.value,
};
this.autoIncrement = this.autoIncrement.bind(this);
this.onBlur = this.onBlur.bind(this);
this.onChange = this.onChange.bind(this);
this.onFocus = this.onFocus.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
this.onMouseDown = this.onMouseDown.bind(this);
@ -91,73 +97,137 @@ class FontPropertyValue extends PureComponent {
return value >= Math.floor(this.props.max);
}
/**
* Check if the given value is valid according to the constraints of this component.
* Ensure it is a number and that it does not go outside the min/max limits, unless
* allowed by the `allowAutoIncrement` props flag.
*
* @param {Number} value
* Numeric value
* @return {Boolean}
* Whether the value conforms to the components contraints.
*/
isValueValid(value) {
const { allowAutoIncrement, min, max } = this.props;
if (typeof value !== "number" || isNaN(value)) {
return false;
}
if (min !== undefined && value < min) {
return false;
}
// Ensure it does not exceed maximum value, unless auto-incrementing is permitted.
if (max !== undefined && value > this.props.max && !allowAutoIncrement) {
return false;
}
return true;
}
/**
* Handler for "blur" events from the range and number input fields.
* Reconciles the value between internal state and props.
* Marks the input as non-interactive so it may update in response to changes in props.
*/
onBlur() {
const isValid = this.isValueValid(this.state.value);
let value;
if (isValid) {
value = this.state.value;
} else if (this.state.value !== null) {
value = Math.max(this.props.min, Math.min(this.state.value, this.props.max));
} else {
value = this.state.initialValue;
}
this.updateValue(value);
this.toggleInteractiveState(false);
}
/**
* Handler for "change" events from the range and number input fields. Calls the change
* handler provided with props and updates internal state with the current value.
* Begins auto-incrementing if the value is already at the upper bound.
*
* Number inputs in Firefox can't be trusted to filter out non-digit characters,
* therefore we must implement our own validation.
* @see https://bugzilla.mozilla.org/show_bug.cgi?id=1398528
*
* @param {Event} e
* Change event.
*/
onChange(e) {
const value = parseFloat(e.target.value);
// Regular expresion to check for positive floating point or integer numbers.
// Whitespace and non-digit characters are invalid (aside from a single dot).
const regex = /^[0-9]+(.[0-9]+)?$/;
let string = e.target.value.trim();
if (e.target.validity.badInput) {
return;
}
// Prefix with zero if the string starts with a dot: .5 => 0.5
if (string.charAt(0) === "." && string.length > 1) {
string = "0" + string;
e.target.value = string;
}
// Accept empty strings to allow the input value to be completely erased while typing.
// A null value will be handled on blur. @see this.onBlur()
if (string === "") {
this.setState((prevState) => {
return {
...prevState,
value: null,
};
});
return;
}
// Catch any negative or irrational numbers.
if (!regex.test(string)) {
return;
}
const value = parseFloat(string);
this.updateValue(value);
}
// Stop auto-incrementing when dragging back down from the upper bound.
if (value < this.props.max && this.interval) {
this.stopAutoIncrement();
onFocus(e) {
if (e.target.type === "number") {
e.target.select();
}
// Begin auto-incrementing when reaching the upper bound.
if (this.isAtUpperBound(value) && this.state.interactive) {
this.startAutoIncrement();
}
this.setState((prevState) => {
return {
...prevState,
interactive: true,
initialValue: this.props.value,
};
});
}
/**
* Handler for "keydown" events from the range and number input fields.
* Toggles on the "interactive" state. @See toggleInteractiveState();
* Begins auto-incrementing if the value is already at the upper bound.
* Handler for "keydown" events from the range input field.
* Begin auto-incrementing if the slider value is already at the upper boun and the
* keyboard gesture requests a higher value.
*
* @param {Event} e
* KeyDown event.
*/
onKeyDown(e) {
const inputType = e.target.type;
if ([
KeyCodes.DOM_VK_UP,
KeyCodes.DOM_VK_DOWN,
KeyCodes.DOM_VK_RIGHT,
KeyCodes.DOM_VK_LEFT
].includes(e.keyCode)) {
this.toggleInteractiveState(true);
}
// Begin auto-incrementing if the value is already at the upper bound
// and the user gesture requests a higher value.
if (this.isAtUpperBound(this.props.value)) {
if ((inputType === "range" &&
e.keyCode === KeyCodes.DOM_VK_UP || e.keyCode === KeyCodes.DOM_VK_RIGHT) ||
(inputType === "number" &&
e.keyCode === KeyCodes.DOM_VK_UP)) {
this.startAutoIncrement();
}
if (this.isAtUpperBound(this.props.value) &&
e.keyCode === KeyCodes.DOM_VK_UP || e.keyCode === KeyCodes.DOM_VK_RIGHT) {
this.startAutoIncrement();
}
}
onKeyUp(e) {
if ([
KeyCodes.DOM_VK_UP,
KeyCodes.DOM_VK_DOWN,
KeyCodes.DOM_VK_RIGHT,
KeyCodes.DOM_VK_LEFT
].includes(e.keyCode)) {
this.toggleInteractiveState(false);
if (e.keyCode === KeyCodes.DOM_VK_UP || e.keyCode === KeyCodes.DOM_VK_RIGHT) {
this.stopAutoIncrement();
}
}
@ -225,15 +295,22 @@ class FontPropertyValue extends PureComponent {
}
/**
* Calls the given `onChange` callback with the current property, value and unit.
* Updates the internal state with the current value which will be used while
* interactive to prevent jittering when receiving debounced props from outside.
* Calls the given `onChange` callback with the current property name, value and unit
* if the value is valid according to the constraints of this component (min, max).
* Updates the internal state with the current value. This will be used to render the
* UI while the input is interactive and the user may be typing a value that's not yet
* valid.
*
* @see this.onBlur() for logic reconciling the internal state with props.
*
* @param {Number} value
* Numeric property value.
*/
updateValue(value) {
this.props.onChange(this.props.name, value, this.props.unit);
if (this.isValueValid(value)) {
this.props.onChange(this.props.name, value, this.props.unit);
}
this.setState((prevState) => {
return {
...prevState,
@ -282,11 +359,10 @@ class FontPropertyValue extends PureComponent {
max: this.props.max,
onBlur: this.onBlur,
onChange: this.onChange,
onKeyUp: this.onKeyUp,
onKeyDown: this.onKeyDown,
onFocus: this.onFocus,
step: this.props.step || 1,
// While interacting with the slider or numeric stepper, prevent updating value from
// outside props which may be debounced and could cause jitter when rendering.
// While interacting with the range and number inputs, prevent updating value from
// outside props which is debounced and causes jitter on successive renders.
value: this.state.interactive
? this.state.value
: this.props.value || this.props.defaultValue,
@ -295,6 +371,8 @@ class FontPropertyValue extends PureComponent {
const range = dom.input(
{
...defaults,
onKeyDown: this.onKeyDown,
onKeyUp: this.onKeyUp,
onMouseDown: this.onMouseDown,
onMouseUp: this.onMouseUp,
className: "font-value-slider",
@ -307,6 +385,8 @@ class FontPropertyValue extends PureComponent {
const input = dom.input(
{
...defaults,
// Remove upper limit from number input if it is allowed to auto-increment.
max: this.props.allowAutoIncrement ? null : this.props.max,
name: this.props.name,
className: "font-value-input",
type: "number",

View file

@ -102,7 +102,6 @@
--theme-arrowpanel-dimmed-further: hsla(0,0%,80%,.45);
--theme-arrowpanel-disabled-color: GrayText;
--theme-codemirror-gutter-background: #f4f4f4;
--theme-messageCloseButtonFilter: invert(0);
}
@ -205,7 +204,6 @@
--theme-arrowpanel-dimmed-further: rgba(249,249,250,.15);
--theme-arrowpanel-disabled-color: rgba(249,249,250,.5);
--theme-codemirror-gutter-background: #262b37;
--theme-messageCloseButtonFilter: invert(1);
}

View file

@ -300,19 +300,27 @@ class JSTerm extends Component {
"PageUp": () => {
if (this.autocompletePopup.isOpen) {
this.autocompletePopup.selectPreviousPageItem();
return null;
} else {
this.hud.outputScroller.scrollTop = Math.max(
0,
this.hud.outputScroller.scrollTop - this.hud.outputScroller.clientHeight
);
}
return "CodeMirror.Pass";
return null;
},
"PageDown": () => {
if (this.autocompletePopup.isOpen) {
this.autocompletePopup.selectNextPageItem();
return null;
} else {
this.hud.outputScroller.scrollTop = Math.min(
this.hud.outputScroller.scrollHeight,
this.hud.outputScroller.scrollTop + this.hud.outputScroller.clientHeight
);
}
return "CodeMirror.Pass";
return null;
},
"Home": () => {
@ -321,6 +329,11 @@ class JSTerm extends Component {
return null;
}
if (!this.getInputValue()) {
this.hud.outputScroller.scrollTop = 0;
return null;
}
return "CodeMirror.Pass";
},
@ -331,10 +344,17 @@ class JSTerm extends Component {
return null;
}
if (!this.getInputValue()) {
this.hud.outputScroller.scrollTop = this.hud.outputScroller.scrollHeight;
return null;
}
return "CodeMirror.Pass";
},
"Esc": false,
"Cmd-F": false,
"Ctrl-F": false,
}
});

View file

@ -9,13 +9,20 @@ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
"test/mochitest/test-console.html";
add_task(async function() {
// Only run in legacy JsTerm - fixme in Bug 1485510.
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
// Enable net messages in the console for this test.
await pushPref("devtools.browserconsole.filter.net", true);
// This is required for testing the text input in the browser console:
await pushPref("devtools.chrome.enabled", true);
// Run test with legacy JsTerm
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
await performTests();
// And then run it with the CodeMirror-powered one.
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
await performTests();
});
async function performTests() {
await addTab(TEST_URI);
const hud = await HUDService.toggleBrowserConsole();
@ -60,7 +67,7 @@ add_task(async function() {
is(getSimplifiedContextMenu(menuPopup).join("\n"), expectedContextMenu.join("\n"),
"The context menu has the expected entries for a simple log message");
menuPopup = await openContextMenu(hud, hud.jsterm.inputNode);
menuPopup = await openContextMenu(hud, hud.jsterm.node || hud.jsterm.inputNode);
expectedContextMenu = [
"#editmenu-undo (editmenu-undo) [disabled]",
@ -74,7 +81,9 @@ add_task(async function() {
"The context menu has the correct edit menu items");
await hideContextMenu(hud);
});
// Close the browser console.
await HUDService.toggleBrowserConsole();
}
function addPrefBasedEntries(expectedEntries) {
if (Services.prefs.getBoolPref("devtools.webconsole.sidebarToggle", false)) {

View file

@ -10,55 +10,51 @@
const TEST_URI = "data:text/html;charset=UTF-8,test";
const COMMANDS = ["document", "window", "window.location"];
const {
HISTORY_BACK,
HISTORY_FORWARD,
} = require("devtools/client/webconsole/constants");
add_task(async function() {
// Only run in legacy JsTerm - fixme in Bug 1485510.
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
await testHistory();
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
await testHistory();
});
async function testHistory() {
const { jsterm, ui } = await openNewTabAndConsole(TEST_URI);
ui.clearOutput();
const { jsterm } = await openNewTabAndConsole(TEST_URI);
jsterm.focus();
for (const command of COMMANDS) {
info(`Executing command ${command}`);
jsterm.setInputValue(command);
await jsterm.execute();
await jsterm.execute(command);
}
for (let x = COMMANDS.length - 1; x != -1; x--) {
jsterm.historyPeruse(HISTORY_BACK);
EventUtils.synthesizeKey("KEY_ArrowUp");
is(jsterm.getInputValue(), COMMANDS[x], "check history previous idx:" + x);
}
jsterm.historyPeruse(HISTORY_BACK);
EventUtils.synthesizeKey("KEY_ArrowUp");
is(jsterm.getInputValue(), COMMANDS[0], "test that item is still index 0");
jsterm.historyPeruse(HISTORY_BACK);
EventUtils.synthesizeKey("KEY_ArrowUp");
is(jsterm.getInputValue(), COMMANDS[0], "test that item is still still index 0");
for (let i = 1; i < COMMANDS.length; i++) {
jsterm.historyPeruse(HISTORY_FORWARD);
EventUtils.synthesizeKey("KEY_ArrowDown");
is(jsterm.getInputValue(), COMMANDS[i], "check history next idx:" + i);
}
jsterm.historyPeruse(HISTORY_FORWARD);
EventUtils.synthesizeKey("KEY_ArrowDown");
is(jsterm.getInputValue(), "", "check input is empty again");
// Simulate pressing Arrow_Down a few times and then if Arrow_Up shows
// the previous item from history again.
jsterm.historyPeruse(HISTORY_FORWARD);
jsterm.historyPeruse(HISTORY_FORWARD);
jsterm.historyPeruse(HISTORY_FORWARD);
EventUtils.synthesizeKey("KEY_ArrowDown");
EventUtils.synthesizeKey("KEY_ArrowDown");
EventUtils.synthesizeKey("KEY_ArrowDown");
is(jsterm.getInputValue(), "", "check input is still empty");
const idxLast = COMMANDS.length - 1;
jsterm.historyPeruse(HISTORY_BACK);
EventUtils.synthesizeKey("KEY_ArrowUp");
is(jsterm.getInputValue(), COMMANDS[idxLast], "check history next idx:" + idxLast);
}

View file

@ -12,9 +12,13 @@ const TEST_URI = "data:text/html;charset=utf-8,<p>bug 660806 - history " +
"navigation must not show the autocomplete popup";
add_task(async function() {
// Only run in legacy JsTerm - fixme in Bug 1485510.
// Run in legacy JsTerm.
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
await testHistory();
// And then in codeMirror JsTerm.
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
await testHistory();
});
async function testHistory() {
@ -25,25 +29,17 @@ async function testHistory() {
const onShown = function() {
ok(false, "popup shown");
};
popup.on("popup-opened", onShown);
await jsterm.execute(`window.foobarBug660806 = {
'location': 'value0',
'locationbar': 'value1'
}`);
popup.on("popup-opened", onShown);
ok(!popup.isOpen, "popup is not open");
ok(!jsterm.lastInputValue, "no lastInputValue");
jsterm.setInputValue("window.foobarBug660806.location");
is(jsterm.lastInputValue, "window.foobarBug660806.location",
"lastInputValue is correct");
EventUtils.synthesizeKey("KEY_Enter");
// Wait for the execution to complete and clear the value.
await waitFor(() => !jsterm.lastInputValue);
// Let's add this expression in the input history. We don't use setInputValue since
// it _does_ trigger an autocompletion request in codeMirror JsTerm.
await jsterm.execute("window.foobarBug660806.location");
const onSetInputValue = jsterm.once("set-input-value");
EventUtils.synthesizeKey("KEY_ArrowUp");
@ -53,8 +49,8 @@ async function testHistory() {
// before checking the popup status.
await new Promise(executeSoon);
is(jsterm.lastInputValue, "window.foobarBug660806.location",
"lastInputValue is correct, again");
is(jsterm.getInputValue(), "window.foobarBug660806.location",
"input has expected value");
ok(!popup.isOpen, "popup is not open");
popup.off("popup-opened", onShown);

View file

@ -10,8 +10,6 @@
const TEST_URI = "data:text/html;charset=utf8,";
add_task(async function() {
// Only run in legacy JsTerm - fixme in Bug 1485510.
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
// Should be removed when sidebar work is complete
await pushPref("devtools.webconsole.sidebarToggle", true);
@ -69,8 +67,7 @@ add_task(async function() {
await onSidebarShown;
sidebar = hud.ui.document.querySelector(".sidebar");
ok(!sidebar, "Sidebar hidden after sending esc");
const inputNode = hud.jsterm.inputNode;
ok(hasFocus(inputNode), "console input is focused after closing the sidebar");
ok(isJstermFocused(hud.jsterm), "console input is focused after closing the sidebar");
});
async function showSidebar(hud) {

View file

@ -7,14 +7,10 @@
const TEST_URI = "data:text/html;charset=utf-8,Test content focus after closing console";
add_task(async function() {
// Only run in legacy JsTerm - fixme in Bug 1485510.
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
const hud = await openNewTabAndConsole(TEST_URI);
const inputNode = hud.jsterm.inputNode;
info("Focus after console is opened");
ok(hasFocus(inputNode), "input node is focused after console is opened");
ok(isJstermFocused(hud.jsterm), "input node is focused after console is opened");
info("Closing console");
await closeConsole();

View file

@ -11,21 +11,17 @@
const TEST_URI = "data:text/html;charset=utf8,<p>Test console input focus";
add_task(async function() {
// Only run in legacy JsTerm - fixme in Bug 1485510.
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
const hud = await openNewTabAndConsole(TEST_URI);
const inputNode = hud.jsterm.inputNode;
const filterInput = hud.ui.outputNode.querySelector(".text-filter");
info("Focus after console is opened");
ok(hasFocus(inputNode), "input node is focused after console is opened");
ok(isJstermFocused(hud.jsterm), "jsterm is focused after console is opened");
filterInput.focus();
ok(hasFocus(filterInput), "filter input should be focused");
is(hasFocus(inputNode), false, "input node is not focused anymore");
is(isJstermFocused(hud.jsterm), false, "input node is not focused anymore");
info("Go to the inspector panel");
await openInspector();
@ -33,5 +29,5 @@ add_task(async function() {
info("Go back to the console");
await openConsole();
ok(hasFocus(inputNode), "input node is focused when coming from a different panel");
ok(isJstermFocused(hud.jsterm), "jsterm is focused when coming from a different panel");
});

View file

@ -14,48 +14,44 @@ const TEST_URI =
</script>`;
add_task(async function() {
// Only run in legacy JsTerm - fixme in Bug 1485510.
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
const hud = await openNewTabAndConsole(TEST_URI);
const inputNode = hud.jsterm.inputNode;
info("Focus after console is opened");
ok(hasFocus(inputNode), "input node is focused after console is opened");
ok(isJstermFocused(hud.jsterm), "input node is focused after console is opened");
hud.ui.clearOutput();
ok(hasFocus(inputNode), "input node is focused after output is cleared");
ok(isJstermFocused(hud.jsterm), "input node is focused after output is cleared");
info("Focus during message logging");
ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
content.wrappedJSObject.console.log("console message 2");
});
const msg = await waitFor(() => findMessage(hud, "console message 2"));
ok(hasFocus(inputNode, "input node is focused, first time"));
ok(isJstermFocused(hud.jsterm), "input node is focused, first time");
info("Focus after clicking in the output area");
await waitForBlurredInput(hud);
EventUtils.sendMouseEvent({type: "click"}, msg);
ok(hasFocus(inputNode), "input node is focused, second time");
ok(isJstermFocused(hud.jsterm), "input node is focused, second time");
info("Setting a text selection and making sure a click does not re-focus");
await waitForBlurredInput(hud);
const selection = hud.iframeWindow.getSelection();
selection.selectAllChildren(msg.querySelector(".message-body"));
EventUtils.sendMouseEvent({type: "click"}, msg);
ok(!hasFocus(inputNode), "input node not focused after text is selected");
ok(!isJstermFocused(hud.jsterm), "input node not focused after text is selected");
});
function waitForBlurredInput(hud) {
const inputNode = hud.jsterm.inputNode;
const node = hud.jsterm.node || hud.jsterm.inputNode;
return new Promise(resolve => {
const lostFocus = () => {
ok(!hasFocus(inputNode), "input node is not focused");
ok(!isJstermFocused(hud.jsterm), "input node is not focused");
resolve();
};
inputNode.addEventListener("blur", lostFocus, { once: true });
node.addEventListener("focusout", lostFocus, { once: true });
// The 'blur' event fires if we focus e.g. the filter box.
inputNode.ownerDocument.querySelector("input.text-filter").focus();
// The 'focusout' event fires if we focus e.g. the filter box.
node.ownerDocument.querySelector("input.text-filter").focus();
});
}

View file

@ -17,16 +17,23 @@ const TEST_URI =
`;
add_task(async function() {
// Only run in legacy JsTerm - fixme in Bug 1485510.
// Run in legacy JsTerm.
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
await testHistory();
// And then in codeMirror JsTerm.
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
await testHistory();
});
async function testHistory() {
const hud = await openNewTabAndConsole(TEST_URI);
info("Web Console opened");
const outputScroller = hud.ui.outputScroller;
await waitFor(() => findMessages(hud, "").length == 100);
let currentPosition = outputScroller.scrollTop;
const bottom = currentPosition;
hud.jsterm.inputNode.focus();
hud.jsterm.focus();
// Page up.
EventUtils.synthesizeKey("KEY_PageUp");
isnot(outputScroller.scrollTop, currentPosition,
@ -57,12 +64,12 @@ add_task(async function() {
}
synthesizeKeyShortcut(clearShortcut);
await waitFor(() => findMessages(hud, "").length == 0);
ok(hasFocus(hud.jsterm.inputNode), "jsterm input is focused");
ok(isJstermFocused(hud.jsterm), "jsterm input is focused");
// Focus filter
info("try ctrl-f to focus filter");
synthesizeKeyShortcut(WCUL10n.getStr("webconsole.find.key"));
ok(!hasFocus(hud.jsterm.inputNode), "jsterm input is not focused");
ok(!isJstermFocused(hud.jsterm), "jsterm input is not focused");
is(hud.ui.filterBox, outputScroller.ownerDocument.activeElement,
"filter input is focused");
});
}

View file

@ -8,9 +8,16 @@
const TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for splitting</p>";
add_task(async function() {
// Only run in legacy JsTerm - fixme in Bug 1485510.
// Run in legacy JsTerm.
await pushPref("devtools.webconsole.jsterm.codeMirror", false);
await testHistory();
// And then in codeMirror JsTerm.
await pushPref("devtools.webconsole.jsterm.codeMirror", true);
await testHistory();
});
async function testHistory() {
info("Test that the split console input is focused and restores the focus properly.");
const toolbox = await openNewTabAndToolbox(TEST_URI, "inspector");
@ -27,16 +34,15 @@ add_task(async function() {
ok(toolbox.splitConsole, "Split console is now visible");
activeElement = getActiveElement(toolbox.doc);
const inputNode = toolbox.getPanel("webconsole").hud.jsterm.inputNode;
is(activeElement, inputNode, "Split console input is focused by default");
const {jsterm} = toolbox.getPanel("webconsole").hud;
ok(isJstermFocused(jsterm), "Split console input is focused by default");
await toolbox.closeSplitConsole();
info("Making sure that the search box is refocused after closing the split console");
activeElement = getActiveElement(inspector.panelDoc);
is(activeElement, inspector.searchBox, "Search box is focused");
});
}
function getActiveElement(doc) {
let activeElement = doc.activeElement;

View file

@ -65,8 +65,8 @@ var PromisesActor = protocol.ActorClassWithSpec(promisesSpec, {
this._newPromises = [];
this._promisesSettled = [];
this.dbg.findSources().forEach(source => {
this.parentActor.sources.createSourceActors(source);
this.dbg.findScripts().forEach(s => {
this.parentActor.sources.createSourceActors(s.source);
});
this.dbg.onNewScript = s => {

View file

@ -170,23 +170,11 @@ ReplayDebugger.prototype = {
/////////////////////////////////////////////////////////
_getSource(id) {
const source = this._scriptSources[id];
if (source) {
return source;
if (!this._scriptSources[id]) {
const data = this._sendRequest({ type: "getSource", id });
this._scriptSources[id] = new ReplayDebuggerScriptSource(this, data);
}
return this._addSource(this._sendRequest({ type: "getSource", id }));
},
_addSource(data) {
if (!this._scriptSources[data.id]) {
this._scriptSources[data.id] = new ReplayDebuggerScriptSource(this, data);
}
return this._scriptSources[data.id];
},
findSources() {
const data = this._sendRequest({ type: "findSources" });
return data.map(source => this._addSource(source));
return this._scriptSources[id];
},
/////////////////////////////////////////////////////////

View file

@ -465,22 +465,6 @@ function getScriptData(id) {
};
}
function getSourceData(id) {
const source = gScriptSources.getObject(id);
const introductionScript = gScripts.getId(source.introductionScript);
return {
id: id,
text: source.text,
url: source.url,
displayURL: source.displayURL,
elementAttributeName: source.elementAttributeName,
introductionScript,
introductionOffset: introductionScript ? source.introductionOffset : undefined,
introductionType: source.introductionType,
sourceMapURL: source.sourceMapURL,
};
}
function forwardToScript(name) {
return request => gScripts.getObject(request.id)[name](request.value);
}
@ -511,16 +495,20 @@ const gRequestHandlers = {
return RecordReplayControl.getContent(request.url);
},
findSources(request) {
const sources = [];
gScriptSources.forEach((id) => {
sources.push(getSourceData(id));
});
return sources;
},
getSource(request) {
return getSourceData(request.id);
const source = gScriptSources.getObject(request.id);
const introductionScript = gScripts.getId(source.introductionScript);
return {
id: request.id,
text: source.text,
url: source.url,
displayURL: source.displayURL,
elementAttributeName: source.elementAttributeName,
introductionScript,
introductionOffset: introductionScript ? source.introductionOffset : undefined,
introductionType: source.introductionType,
sourceMapURL: source.sourceMapURL,
};
},
getObject(request) {

View file

@ -1111,12 +1111,22 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
},
/**
* Get the source lists from the debugger.
* Get the script and source lists from the debugger.
*/
_discoverSources: function() {
const sources = this.dbg.findSources();
return Promise.all(sources.map(source => {
return this.sources.createSourceActors(source);
// Only get one script per Debugger.Source.
const sourcesToScripts = new Map();
const scripts = this.dbg.findScripts();
for (let i = 0, len = scripts.length; i < len; i++) {
const s = scripts[i];
if (s.source) {
sourcesToScripts.set(s.source, s);
}
}
return Promise.all([...sourcesToScripts.values()].map(script => {
return this.sources.createSourceActors(script.source);
}));
},

View file

@ -18,6 +18,7 @@ const { LongStringActor } = require("devtools/server/actors/object/long-string")
const { createValueGrip, stringIsLong } = require("devtools/server/actors/object/utils");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const ErrorDocs = require("devtools/server/actors/errordocs");
const { evalWithDebugger } = require("devtools/server/actors/webconsole/eval-with-debugger");
loader.lazyRequireGetter(this, "NetworkMonitorActor", "devtools/server/actors/network-monitor", true);
loader.lazyRequireGetter(this, "ConsoleProgressListener", "devtools/server/actors/webconsole/listeners/console-progress", true);
@ -32,8 +33,6 @@ loader.lazyRequireGetter(this, "CONSOLE_WORKER_IDS", "devtools/server/actors/web
loader.lazyRequireGetter(this, "WebConsoleUtils", "devtools/server/actors/webconsole/utils", true);
loader.lazyRequireGetter(this, "EnvironmentActor", "devtools/server/actors/environment", true);
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
+loader.lazyRequireGetter(this, "evalWithDebugger",
"devtools/server/actors/webconsole/eval-with-debugger", true);
// Overwrite implemented listeners for workers so that we don't attempt
// to load an unsupported module.

View file

@ -39,11 +39,16 @@ struct FrameStatisticsData
uint64_t mInterKeyFrameMax_us = 0;
FrameStatisticsData() = default;
FrameStatisticsData(uint64_t aParsed, uint64_t aDecoded, uint64_t aDropped)
FrameStatisticsData(uint64_t aParsed,
uint64_t aDecoded,
uint64_t aDropped,
uint64_t aPresented)
: mParsedFrames(aParsed)
, mDecodedFrames(aDecoded)
, mPresentedFrames(aPresented)
, mDroppedFrames(aDropped)
{}
{
}
void
Accumulate(const FrameStatisticsData& aStats)
@ -115,7 +120,7 @@ public:
// Increments the parsed and decoded frame counters by the passed in counts.
// Can be called on any thread.
void NotifyDecodedFrames(const FrameStatisticsData& aStats)
void Accumulate(const FrameStatisticsData& aStats)
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mFrameStatisticsData.Accumulate(aStats);
@ -142,7 +147,7 @@ public:
~AutoNotifyDecoded()
{
if (mFrameStats) {
mFrameStats->NotifyDecodedFrames(mStats);
mFrameStats->Accumulate(mStats);
}
}

View file

@ -2808,7 +2808,7 @@ MediaFormatReader::DropDecodedSamples(TrackType aTrack)
decoder.mOutput.Clear();
decoder.mSizeOfQueue -= lengthDecodedQueue;
if (aTrack == TrackInfo::kVideoTrack && mFrameStats) {
mFrameStats->NotifyDecodedFrames({ 0, 0, lengthDecodedQueue });
mFrameStats->Accumulate({ 0, 0, lengthDecodedQueue, 0 });
}
}
@ -2840,7 +2840,7 @@ MediaFormatReader::VideoSkipReset(uint32_t aSkipped)
DropDecodedSamples(TrackInfo::kVideoTrack);
// Report the pending frames as dropped.
if (mFrameStats) {
mFrameStats->NotifyDecodedFrames({ 0, 0, SizeOfVideoQueueInFrames() });
mFrameStats->Accumulate({ 0, 0, SizeOfVideoQueueInFrames(), 0 });
}
// Cancel any pending demux request and pending demuxed samples.
@ -2848,7 +2848,7 @@ MediaFormatReader::VideoSkipReset(uint32_t aSkipped)
Reset(TrackType::kVideoTrack);
if (mFrameStats) {
mFrameStats->NotifyDecodedFrames({ aSkipped, 0, aSkipped });
mFrameStats->Accumulate({ aSkipped, 0, aSkipped, 0 });
}
mVideo.mNumSamplesSkippedTotal += aSkipped;

View file

@ -47,7 +47,9 @@ VideoSink::VideoSink(AbstractThread* aThread,
, mContainer(aContainer)
, mProducerID(ImageContainer::AllocateProducerID())
, mFrameStats(aFrameStats)
, mOldDroppedCount(0)
, mOldCompositorDroppedCount(mContainer ? mContainer->GetDroppedImageCount()
: 0)
, mPendingDroppedCount(0)
, mHasVideo(false)
, mUpdateScheduler(aThread)
, mVideoQueueSendToCompositorSize(aVQueueSentToCompositerSize)
@ -465,13 +467,6 @@ VideoSink::RenderVideoFrames(int32_t aMaxFrames,
if (images.Length() > 0) {
mContainer->SetCurrentFrames(frames[0]->mDisplay, images);
uint32_t droppedCount = mContainer->GetDroppedImageCount();
uint32_t dropped = droppedCount - mOldDroppedCount;
if (dropped > 0) {
mFrameStats.NotifyDecodedFrames({0, 0, dropped});
mOldDroppedCount = droppedCount;
VSINK_LOG_V("%u video frame discarded by compositor", dropped);
}
}
}
@ -486,6 +481,9 @@ VideoSink::UpdateRenderedVideoFrames()
const auto clockTime = mAudioSink->GetPosition(&nowTime);
MOZ_ASSERT(!clockTime.IsNegative(), "Should have positive clock time.");
uint32_t sentToCompositorCount = 0;
uint32_t droppedCount = 0;
// Skip frames up to the playback position.
TimeUnit lastFrameEndTime;
while (VideoQueue().GetSize() > mMinVideoQueueSize &&
@ -493,14 +491,35 @@ VideoSink::UpdateRenderedVideoFrames()
RefPtr<VideoData> frame = VideoQueue().PopFront();
lastFrameEndTime = frame->GetEndTime();
if (frame->IsSentToCompositor()) {
mFrameStats.NotifyPresentedFrame();
sentToCompositorCount++;
} else {
mFrameStats.NotifyDecodedFrames({ 0, 0, 1 });
droppedCount++;
VSINK_LOG_V("discarding video frame mTime=%" PRId64 " clock_time=%" PRId64,
frame->mTime.ToMicroseconds(), clockTime.ToMicroseconds());
}
}
if (droppedCount || sentToCompositorCount) {
uint32_t totalCompositorDroppedCount = mContainer->GetDroppedImageCount();
uint32_t compositorDroppedCount =
totalCompositorDroppedCount - mOldCompositorDroppedCount;
if (compositorDroppedCount > 0) {
mOldCompositorDroppedCount = totalCompositorDroppedCount;
VSINK_LOG_V("%u video frame previously discarded by compositor",
compositorDroppedCount);
}
mPendingDroppedCount += compositorDroppedCount;
uint32_t droppedReported = mPendingDroppedCount > sentToCompositorCount
? sentToCompositorCount
: mPendingDroppedCount;
mPendingDroppedCount -= droppedReported;
mFrameStats.Accumulate({ 0,
0,
droppedCount + droppedReported,
sentToCompositorCount - droppedReported });
}
// The presentation end time of the last video frame displayed is either
// the end time of the current frame, or if we dropped all frames in the
// queue, the end time of the last frame we removed from the queue.
@ -548,7 +567,12 @@ VideoSink::MaybeResolveEndPromise()
if (VideoQueue().GetSize() == 1) {
// Remove the last frame since we have sent it to compositor.
RefPtr<VideoData> frame = VideoQueue().PopFront();
mFrameStats.NotifyPresentedFrame();
if (mPendingDroppedCount > 0) {
mFrameStats.Accumulate({ 0, 0, 1, 0 });
mPendingDroppedCount--;
} else {
mFrameStats.NotifyPresentedFrame();
}
}
mEndPromiseHolder.ResolveIfExists(true, __func__);
}

View file

@ -132,7 +132,8 @@ private:
// The presentation end time of the last video frame which has been displayed.
TimeUnit mVideoFrameEndTime;
uint32_t mOldDroppedCount;
uint32_t mOldCompositorDroppedCount;
uint32_t mPendingDroppedCount;
// Event listeners for VideoQueue
MediaEventListener mPushListener;

View file

@ -77,12 +77,21 @@ FFmpegDataDecoder<LIBAV_VER>::InitDecoder()
mCodecContext->extradata_size = mExtraData->Length();
// FFmpeg may use SIMD instructions to access the data which reads the
// data in 32 bytes block. Must ensure we have enough data to read.
uint32_t padding_size =
#if LIBAVCODEC_VERSION_MAJOR >= 58
mExtraData->AppendElements(AV_INPUT_BUFFER_PADDING_SIZE);
AV_INPUT_BUFFER_PADDING_SIZE;
#else
mExtraData->AppendElements(FF_INPUT_BUFFER_PADDING_SIZE);
FF_INPUT_BUFFER_PADDING_SIZE;
#endif
mCodecContext->extradata = mExtraData->Elements();
mCodecContext->extradata = static_cast<uint8_t*>(
mLib->av_malloc(mExtraData->Length() + padding_size));
if (!mCodecContext->extradata) {
return MediaResult(NS_ERROR_OUT_OF_MEMORY,
RESULT_DETAIL("Couldn't init ffmpeg extradata"));
}
memcpy(mCodecContext->extradata,
mExtraData->Elements(),
mExtraData->Length());
} else {
mCodecContext->extradata_size = 0;
}
@ -225,6 +234,9 @@ FFmpegDataDecoder<LIBAV_VER>::ProcessShutdown()
StaticMutexAutoLock mon(sMonitor);
if (mCodecContext) {
if (mCodecContext->extradata) {
mLib->av_freep(&mCodecContext->extradata);
}
mLib->avcodec_close(mCodecContext);
mLib->av_freep(&mCodecContext);
#if LIBAVCODEC_VERSION_MAJOR >= 55

View file

@ -3018,7 +3018,8 @@ HTMLEditor::SetHTMLBackgroundColorWithTransaction(const nsAString& aColor)
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
GetNextSelectedCell(nullptr, getter_AddRefs(cellElement));
cellElement =
GetNextSelectedTableCellElement(*selection, ignoredError);
}
return NS_OK;
}
@ -3027,7 +3028,8 @@ HTMLEditor::SetHTMLBackgroundColorWithTransaction(const nsAString& aColor)
if (NS_FAILED(rv)) {
return rv;
}
GetNextSelectedCell(nullptr, getter_AddRefs(cellElement));
cellElement =
GetNextSelectedTableCellElement(*selection, ignoredError);
}
return NS_OK;
}

View file

@ -353,6 +353,33 @@ public:
return NS_OK;
}
/**
* GetFontColorState() returns foreground color information in first
* range of Selection.
* If first range of Selection is collapsed and there is a cache of style for
* new text, aIsMixed is set to false and aColor is set to the cached color.
* If first range of Selection is collapsed and there is no cached color,
* this returns the color of the node, aIsMixed is set to false and aColor is
* set to the color.
* If first range of Selection is not collapsed, this collects colors of
* each node in the range. If there are two or more colors, aIsMixed is set
* to true and aColor is truncated. If only one color is set to all of the
* range, aIsMixed is set to false and aColor is set to the color.
* If there is no Selection ranges, aIsMixed is set to false and aColor is
* truncated.
*
* @param aIsMixed Must not be nullptr. This is set to true
* if there is two or more colors in first
* range of Selection.
* @param aColor Returns the color if only one color is set to
* all of first range in Selection. Otherwise,
* returns empty string.
* @return Returns error only when illegal cases, e.g.,
* Selection instance has gone, first range
* Selection is broken.
*/
nsresult GetFontColorState(bool* aIsMixed, nsAString& aColor);
/**
* SetComposerCommandsUpdater() sets or unsets mComposerCommandsUpdater.
* This will crash in debug build if the editor already has an instance
@ -537,8 +564,9 @@ protected: // May be called by friends.
* a cell element, this returns nullptr. And even if 2nd or later
* range of Selection selects a cell element, also returns nullptr.
* Note that when this looks for a cell element, this resets the internal
* index of ranges of Selection. When you call GetNextSelectedCell() after
* a call of this, it'll return 2nd selected cell if there is.
* index of ranges of Selection. When you call
* GetNextSelectedTableCellElement() after a call of this, it'll return 2nd
* selected cell if there is.
*
* @param aSelection Selection for this editor.
* @param aRv Returns error if there is no selection or
@ -552,6 +580,32 @@ protected: // May be called by friends.
GetFirstSelectedTableCellElement(Selection& aSelection,
ErrorResult& aRv) const;
/**
* GetNextSelectedTableCellElement() is a stateful method to retrieve
* selected table cell elements which are selected by 2nd or later ranges
* of Selection. When you call GetFirstSelectedTableCellElement(), it
* resets internal counter of this method. Then, following calls of
* GetNextSelectedTableCellElement() scans the remaining ranges of Selection.
* If a range selects a <td> or <th> element, returns the cell element.
* If a range selects an element but neither <td> nor <th> element, this
* ignores the range. If a range is in a text node, returns null without
* throwing exception, but stops scanning the remaining ranges even you
* call this again.
* Note that this may cross <table> boundaries since this method just
* scans all ranges of Selection. Therefore, returning cells which
* belong to different <table> elements.
*
* @param Selection Selection for this editor.
* @param aRv Returns error if Selection doesn't have
* range properly.
* @return A <td> or <th> element if one of remaining
* ranges selects a <td> or <th> element unless
* this does not meet a range in a text node.
*/
already_AddRefed<Element>
GetNextSelectedTableCellElement(Selection& aSelection,
ErrorResult& aRv) const;
void IsNextCharInNodeWhitespace(nsIContent* aContent,
int32_t aOffset,
bool* outIsSpace,
@ -1878,8 +1932,9 @@ protected:
UniquePtr<CSSEditUtils> mCSSEditUtils;
// mSelectedCellIndex is reset by GetFirstSelectedTableCellElement(),
// then, it'll be referred and incremented by GetNextSelectedCell().
mutable int32_t mSelectedCellIndex;
// then, it'll be referred and incremented by
// GetNextSelectedTableCellElement().
mutable uint32_t mSelectedCellIndex;
nsString mLastStyleSheetURL;
nsString mLastOverrideStyleSheetURL;

View file

@ -880,7 +880,9 @@ FontColorStateCommand::GetCurrentState(HTMLEditor* aHTMLEditor,
bool outMixed;
nsAutoString outStateString;
nsresult rv = aHTMLEditor->GetFontColorState(&outMixed, outStateString);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoCString tOutStateString;
LossyCopyUTF16toASCII(outStateString, tOutStateString);

View file

@ -1760,20 +1760,25 @@ HTMLEditor::GetFontFaceState(bool* aMixed,
return NS_OK;
}
NS_IMETHODIMP
nsresult
HTMLEditor::GetFontColorState(bool* aMixed,
nsAString& aOutColor)
{
NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
if (NS_WARN_IF(!aMixed)) {
return NS_ERROR_INVALID_ARG;
}
*aMixed = true;
aOutColor.Truncate();
bool first, any, all;
nsresult rv =
GetInlinePropertyBase(*nsGkAtoms::font, nsGkAtoms::color, nullptr,
&first, &any, &all, &aOutColor);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (any && !all) {
return NS_OK; // mixed
}

View file

@ -812,8 +812,10 @@ HTMLEditor::DeleteTableCell(int32_t aNumber)
// to continue after we delete this row
int32_t nextRow = startRowIndex;
while (nextRow == startRowIndex) {
rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
NS_ENSURE_SUCCESS(rv, rv);
cell = GetNextSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
if (!cell) {
break;
}
@ -850,8 +852,10 @@ HTMLEditor::DeleteTableCell(int32_t aNumber)
// to continue after we delete this column
int32_t nextCol = startColIndex;
while (nextCol == startColIndex) {
rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
NS_ENSURE_SUCCESS(rv, rv);
cell = GetNextSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
if (!cell) {
break;
}
@ -875,8 +879,11 @@ HTMLEditor::DeleteTableCell(int32_t aNumber)
}
if (!deleteCol) {
// First get the next cell to delete
RefPtr<Element> nextCell;
rv = GetNextSelectedCell(nullptr, getter_AddRefs(nextCell));
RefPtr<Element> nextCell =
GetNextSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
NS_ENSURE_SUCCESS(rv, rv);
// Then delete the cell
@ -1005,9 +1012,9 @@ HTMLEditor::DeleteTableCellContents()
DeleteCellContents(cell);
if (firstSelectedCellElement) {
// We doing a selected cells, so do all of them
nsresult rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
cell = GetNextSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
} else {
cell = nullptr;
@ -1109,8 +1116,10 @@ HTMLEditor::DeleteTableColumn(int32_t aNumber)
// to continue after we delete this column
int32_t nextCol = startColIndex;
while (nextCol == startColIndex) {
rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
NS_ENSURE_SUCCESS(rv, rv);
cell = GetNextSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
if (!cell) {
break;
}
@ -1299,8 +1308,10 @@ HTMLEditor::DeleteTableRow(int32_t aNumber)
// to continue after we delete this row
int32_t nextRow = startRowIndex;
while (nextRow == startRowIndex) {
nsresult rv = GetNextSelectedCell(nullptr, getter_AddRefs(cell));
NS_ENSURE_SUCCESS(rv, rv);
cell = GetNextSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
if (!cell) {
break;
}
@ -1566,12 +1577,16 @@ HTMLEditor::SelectBlockOfCells(Element* aStartCell,
currentCellIndexes.mColumn > maxColumn) {
selection->RemoveRange(*range, IgnoreErrors());
// Since we've removed the range, decrement pointer to next range
MOZ_ASSERT(mSelectedCellIndex > 0);
mSelectedCellIndex--;
}
nsresult rv =
GetNextSelectedCell(getter_AddRefs(range), getter_AddRefs(cell));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
cell = GetNextSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
if (cell) {
MOZ_ASSERT(mSelectedCellIndex > 0);
range = selection->GetRangeAt(mSelectedCellIndex - 1);
}
}
@ -2174,17 +2189,25 @@ HTMLEditor::JoinTableCells(bool aMergeNonContiguousContents)
// is retained after joining. This leaves the target cell selected
// as well as the "non-contiguous" cells, so user can see what happened.
RefPtr<Selection> selection = GetSelection();
if (NS_WARN_IF(!selection)) {
return NS_ERROR_FAILURE;
}
RefPtr<Element> firstCell;
int32_t firstRowIndex, firstColIndex;
rv = GetFirstSelectedCellInTable(&firstRowIndex, &firstColIndex,
getter_AddRefs(firstCell));
NS_ENSURE_SUCCESS(rv, rv);
ErrorResult error;
bool joinSelectedCells = false;
if (firstCell) {
RefPtr<Element> secondCell;
rv = GetNextSelectedCell(nullptr, getter_AddRefs(secondCell));
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<Element> secondCell =
GetNextSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
// If only one cell is selected, join with cell to the right
joinSelectedCells = (secondCell != nullptr);
@ -2193,7 +2216,6 @@ HTMLEditor::JoinTableCells(bool aMergeNonContiguousContents)
if (joinSelectedCells) {
// We have selected cells: Join just contiguous cells
// and just merge contents if not contiguous
ErrorResult error;
TableSize tableSize(*this, *table, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
@ -3353,62 +3375,92 @@ HTMLEditor::GetFirstSelectedTableCellElement(Selection& aSelection,
return nullptr;
}
// Setup for GetNextSelectedCell()
// XXX Oh, increment it now? Rather than when GetNextSelectedCell() is
// called?
// Setup for GetNextSelectedTableCellElement()
// XXX Oh, increment it now? Rather than when
// GetNextSelectedTableCellElement() is called?
mSelectedCellIndex = 1;
return selectedCell.forget();
}
NS_IMETHODIMP
HTMLEditor::GetNextSelectedCell(nsRange** aRange,
Element** aCell)
HTMLEditor::GetNextSelectedCell(nsRange** aNextSelectedCellRange,
Element** aNextSelectedCellElement)
{
NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
*aCell = nullptr;
if (aRange) {
*aRange = nullptr;
if (NS_WARN_IF(!aNextSelectedCellElement)) {
return NS_ERROR_INVALID_ARG;
}
*aNextSelectedCellElement = nullptr;
if (aNextSelectedCellRange) {
*aNextSelectedCellRange = nullptr;
}
RefPtr<Selection> selection = GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
int32_t rangeCount = selection->RangeCount();
// Don't even try if index exceeds range count
if (mSelectedCellIndex >= rangeCount) {
return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
if (NS_WARN_IF(!selection)) {
return NS_ERROR_FAILURE;
}
// Scan through ranges to find next valid selected cell
RefPtr<nsRange> range;
for (; mSelectedCellIndex < rangeCount; mSelectedCellIndex++) {
range = selection->GetRangeAt(mSelectedCellIndex);
NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
ErrorResult error;
RefPtr<Element> nextSelectedCellElement =
GetNextSelectedTableCellElement(*selection, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
nsresult rv = HTMLEditor::GetCellFromRange(range, aCell);
// Failure here means the range doesn't contain a cell
NS_ENSURE_SUCCESS(rv, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
if (!nextSelectedCellElement) {
// not more range, or met a range which does not select <td> nor <th>.
return NS_OK;
}
// We found a selected cell
if (*aCell) {
break;
if (aNextSelectedCellRange) {
MOZ_ASSERT(mSelectedCellIndex > 0);
*aNextSelectedCellRange =
do_AddRef(selection->GetRangeAt(mSelectedCellIndex - 1)).take();
}
nextSelectedCellElement.forget(aNextSelectedCellElement);
return NS_OK;
}
already_AddRefed<Element>
HTMLEditor::GetNextSelectedTableCellElement(Selection& aSelection,
ErrorResult& aRv) const
{
MOZ_ASSERT(!aRv.Failed());
if (mSelectedCellIndex >= aSelection.RangeCount()) {
// We've already returned all selected cells.
return nullptr;
}
MOZ_ASSERT(mSelectedCellIndex > 0);
for (; mSelectedCellIndex < aSelection.RangeCount(); mSelectedCellIndex++) {
nsRange* range = aSelection.GetRangeAt(mSelectedCellIndex);
if (NS_WARN_IF(!range)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
// If we didn't find a cell, continue to next range in selection
}
// No cell means all remaining ranges were collapsed (cells were deleted)
NS_ENSURE_TRUE(*aCell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
RefPtr<Element> nextSelectedCellElement;
nsresult rv =
HTMLEditor::GetCellFromRange(range,
getter_AddRefs(nextSelectedCellElement));
if (NS_FAILED(rv)) {
// Failure means that the range is in non-element node, e.g., a text node.
// Returns nullptr without error if not found.
// XXX Why don't we just skip such range or incrementing
// mSelectedCellIndex for next call?
return nullptr;
}
if (aRange) {
range.forget(aRange);
if (nextSelectedCellElement) {
mSelectedCellIndex++;
return nextSelectedCellElement.forget();
}
}
// Setup for next cell
mSelectedCellIndex++;
return NS_OK;
// Returns nullptr without error if not found.
return nullptr;
}
NS_IMETHODIMP
@ -3667,6 +3719,7 @@ HTMLEditor::GetSelectedCellsType(Element* aElement,
bool allCellsInRowAreSelected = false;
bool allCellsInColAreSelected = false;
IgnoredErrorResult ignoredError;
while (selectedCell) {
CellIndexes selectedCellIndexes(*selectedCell, error);
if (NS_WARN_IF(error.Failed())) {
@ -3682,9 +3735,8 @@ HTMLEditor::GetSelectedCellsType(Element* aElement,
break;
}
}
DebugOnly<nsresult> rv =
GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell));
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
selectedCell = GetNextSelectedTableCellElement(*selection, ignoredError);
NS_WARNING_ASSERTION(!ignoredError.Failed(),
"Failed to get next selected table cell element");
}
@ -3698,7 +3750,6 @@ HTMLEditor::GetSelectedCellsType(Element* aElement,
indexArray.Clear();
// Start at first cell again
IgnoredErrorResult ignoredError;
selectedCell = GetFirstSelectedTableCellElement(*selection, ignoredError);
while (selectedCell) {
CellIndexes selectedCellIndexes(*selectedCell, error);
@ -3716,9 +3767,8 @@ HTMLEditor::GetSelectedCellsType(Element* aElement,
break;
}
}
DebugOnly<nsresult> rv =
GetNextSelectedCell(nullptr, getter_AddRefs(selectedCell));
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
selectedCell = GetNextSelectedTableCellElement(*selection, ignoredError);
NS_WARNING_ASSERTION(!ignoredError.Failed(),
"Failed to get next selected table cell element");
}
if (allCellsInColAreSelected) {

View file

@ -293,6 +293,7 @@ skip-if = toolkit == 'android' && debug # bug 1480702, causes permanent failure
[test_nsITableEditor_getCellIndexes.html]
[test_nsITableEditor_getFirstRow.html]
[test_nsITableEditor_getFirstSelectedCell.html]
[test_nsITableEditor_getNextSelectedCell.html]
[test_nsITableEditor_getTableSize.html]
[test_resizers_appearance.html]
[test_resizers_resizing_elements.html]

View file

@ -0,0 +1,262 @@
<!DOCTYPE>
<html>
<head>
<title>Test for nsITableEditor.getNextSelectedCell()</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<div id="display">
</div>
<div id="content" contenteditable></div>
<pre id="test">
</pre>
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
let editor = document.getElementById("content");
let selection = document.getSelection();
let rangeWrapper = {};
editor.innerHTML =
'<table id="table">' +
'<tr id="r1"><td id="c1-1">cell1-1</td><td id="c1-2">cell1-2</td><td id="c1-3">cell1-3</td><td id="c1-4" colspan="2" rowspan="2">cell1-4</td></tr>' +
'<tr id="r2"><th id="c2-1" rowspan="2">cell2-1</th><td id="c2-2">cell2-2<td id="c2-3">cell2-3</td></tr>' +
'<tr id="r3"><td id="c3-2">cell3-2</td><td id="c3-3">cell3-3</td><td id="c3-4" colspan="2">cell3-4</td></tr>' +
'<tr id="r4"><td id="c4-1" rowspan="4">cell4-1</td><td id="c4-2">cell4-2</td><td id="c4-3">cell4-3</td><th id="c4-4">cell4-4</th><td id="c4-5">cell4-5</td></tr>' +
'<tr id="r5"><th id="c5-2">cell5-2</th><th id="c5-3" colspan="2">' +
'<table><tr id="r2-1"><td id="c2-1-1">cell2-1-1</td></tr></table>' +
'</th><td id="c5-5">cell5-5</td></tr>' +
'<tr id="r6"><td id="c6-2">cell6-2</td><td id="c6-3">cell6-3</td><td id="c6-4"><p>cell6-4</p></td><td id="c6-5">cell6-5</td></tr>' +
'<tr id="r7"><td id="c7-2" colspan="4">cell7-2</td></tr>' +
'</table>';
let tr = document.getElementById("r1");
selection.setBaseAndExtent(tr, 0, tr, 1);
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell.getAttribute("id"), "c1-1",
"#1-1 nsITableEditor.getFirstSelectedCell() should return the first cell element in the first row");
cell = SpecialPowers.unwrap(getTableEditor().getNextSelectedCell(rangeWrapper));
is(cell, null,
"Next of #1-1 nsITableEditor.getNextSelectedCell() should return null if there is only one selected range");
selection.removeAllRanges();
tr = document.getElementById("r1");
let range = document.createRange();
range.setStart(tr, 1);
range.setEnd(tr, 2);
selection.addRange(range);
range = document.createRange();
range.setStart(tr, 2);
range.setEnd(tr, 3);
selection.addRange(range);
range = document.createRange();
range.setStart(tr, 3);
range.setEnd(tr, 4);
selection.addRange(range);
tr = document.getElementById("r2");
range = document.createRange();
range.setStart(tr, 0);
range.setEnd(tr, 1);
selection.addRange(range);
range = document.createRange();
range.setStart(tr, 1);
range.setEnd(tr, 2);
selection.addRange(range);
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell.getAttribute("id"), "c1-2",
"#1-2 nsITableEditor.getFirstSelectedCell() should return the second cell element in the first row");
cell = SpecialPowers.unwrap(getTableEditor().getNextSelectedCell(rangeWrapper));
is(cell.getAttribute("id"), "c1-3",
"#1-3 nsITableEditor.getNextSelectedCell() should return the third cell element in the first row");
is(rangeWrapper.value.startContainer, document.getElementById("r1"),
"#1-3 nsITableEditor.getNextSelectedCell() should return a range whose startContainer is the first row");
is(rangeWrapper.value.startOffset, 2,
"#1-3 nsITableEditor.getNextSelectedCell() should return a range whose startOffset is 2");
is(rangeWrapper.value.endContainer, document.getElementById("r1"),
"#1-3 nsITableEditor.getNextSelectedCell() should return a range whose endContainer is the first row");
is(rangeWrapper.value.endOffset, 3,
"#1-3 nsITableEditor.getNextSelectedCell() should return a range whose endOffset is 3");
cell = SpecialPowers.unwrap(getTableEditor().getNextSelectedCell(rangeWrapper));
is(cell.getAttribute("id"), "c1-4",
"#1-4 nsITableEditor.getNextSelectedCell() should return the forth cell element in the first row");
is(rangeWrapper.value.startContainer, document.getElementById("r1"),
"#1-4 nsITableEditor.getNextSelectedCell() should return a range whose startContainer is the first row");
is(rangeWrapper.value.startOffset, 3,
"#1-4 nsITableEditor.getNextSelectedCell() should return a range whose startOffset is 3");
is(rangeWrapper.value.endContainer, document.getElementById("r1"),
"#1-4 nsITableEditor.getNextSelectedCell() should return a range whose endContainer is the first row");
is(rangeWrapper.value.endOffset, 4,
"#1-4 nsITableEditor.getNextSelectedCell() should return a range whose endOffset is 4");
cell = SpecialPowers.unwrap(getTableEditor().getNextSelectedCell(rangeWrapper));
is(cell.getAttribute("id"), "c2-1",
"#2-1 nsITableEditor.getNextSelectedCell() should return the first cell element in the second row");
is(rangeWrapper.value.startContainer, document.getElementById("r2"),
"#2-1 nsITableEditor.getNextSelectedCell() should return a range whose startContainer is the second row");
is(rangeWrapper.value.startOffset, 0,
"#2-1 nsITableEditor.getNextSelectedCell() should return a range whose startOffset is 0");
is(rangeWrapper.value.endContainer, document.getElementById("r2"),
"#2-1 nsITableEditor.getNextSelectedCell() should return a range whose endContainer is the second row");
is(rangeWrapper.value.endOffset, 1,
"#2-1 nsITableEditor.getNextSelectedCell() should return a range whose endOffset is 1");
cell = SpecialPowers.unwrap(getTableEditor().getNextSelectedCell(rangeWrapper));
is(cell.getAttribute("id"), "c2-2",
"#2-2 nsITableEditor.getNextSelectedCell() should return the second cell element in the second row");
is(rangeWrapper.value.startContainer, document.getElementById("r2"),
"#2-2 nsITableEditor.getNextSelectedCell() should return a range whose startContainer is the second row");
is(rangeWrapper.value.startOffset, 1,
"#2-2 nsITableEditor.getNextSelectedCell() should return a range whose startOffset is 1");
is(rangeWrapper.value.endContainer, document.getElementById("r2"),
"#2-2 nsITableEditor.getNextSelectedCell() should return a range whose endContainer is the second row");
is(rangeWrapper.value.endOffset, 2,
"#2-2 nsITableEditor.getNextSelectedCell() should return a range whose endOffset is 2");
cell = SpecialPowers.unwrap(getTableEditor().getNextSelectedCell(rangeWrapper));
is(cell, null,
"Next of #2-2 nsITableEditor.getNextSelectedCell() should return null if we reached the last cell");
selection.removeAllRanges();
tr = document.getElementById("r6");
range = document.createRange();
range.setStart(tr, 1);
range.setEnd(tr, 2);
selection.addRange(range);
range = document.createRange();
range.setStart(document.getElementById("c6-4").firstChild, 0);
range.setEnd(document.getElementById("c6-4").firstChild, 1);
selection.addRange(range);
range = document.createRange();
range.setStart(tr, 3);
range.setEnd(tr, 4);
selection.addRange(range);
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell.getAttribute("id"), "c6-3",
"#6-3 nsITableEditor.getFirstSelectedCell() should return the second cell element in the sixth row");
// The <p> element in c6-4 is selected. In this case, the range is ignored
// by getNextSelectedCell(). So, next call should return the last range.
cell = SpecialPowers.unwrap(getTableEditor().getNextSelectedCell(rangeWrapper));
is(cell.getAttribute("id"), "c6-5",
"#6-5 nsITableEditor.getNextSelectedCell() should return the third cell element in the sixth row");
is(rangeWrapper.value.startContainer, document.getElementById("r6"),
"#6-5 nsITableEditor.getNextSelectedCell() should return a range whose startContainer is the sixth row");
is(rangeWrapper.value.startOffset, 3,
"#6-5 nsITableEditor.getNextSelectedCell() should return a range whose startOffset is 3");
is(rangeWrapper.value.endContainer, document.getElementById("r6"),
"#6-5 nsITableEditor.getNextSelectedCell() should return a range whose endContainer is the sixth row");
is(rangeWrapper.value.endOffset, 4,
"#6-5 nsITableEditor.getNextSelectedCell() should return a range whose endOffset is 4");
cell = SpecialPowers.unwrap(getTableEditor().getNextSelectedCell(rangeWrapper));
is(cell, null,
"Next of #6-5 nsITableEditor.getNextSelectedCell() should return null if we reached the last cell");
selection.removeAllRanges();
tr = document.getElementById("r2");
range = document.createRange();
range.setStart(tr, 2);
range.setEnd(tr, 3);
selection.addRange(range);
range = document.createRange();
range.setStart(document.getElementById("c4-1").firstChild, 0);
range.setEnd(document.getElementById("c4-1").firstChild, 7);
selection.addRange(range);
tr = document.getElementById("r7");
range = document.createRange();
range.setStart(tr, 0);
range.setEnd(tr, 1);
selection.addRange(range);
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell.getAttribute("id"), "c2-3",
"#2-3 nsITableEditor.getFirstSelectedCell() should return the third cell element in the second row");
// c4-1 is not selected even though it contains a range of Selection.
// In this case, getNextSelectedCell() returns null.
cell = SpecialPowers.unwrap(getTableEditor().getNextSelectedCell(rangeWrapper));
is(cell, null,
"#4-1 nsITableEditor.getNextSelectedCell() should return null if the range does not select <td> nor <th> element");
is(rangeWrapper.value, null,
"#4-1 nsITableEditor.getNextSelectedCell() should return null to the range if the range does not select <td> nor <th> element");
// Although c7-2 is selected, but if getNextSelectedCell() meets a range which
// does not select <td> nor <th>, it stops incrementing its internal counter.
// So, following getNextSelectedCell() should return null.
cell = SpecialPowers.unwrap(getTableEditor().getNextSelectedCell(rangeWrapper));
is(cell, null,
"#7-2 nsITableEditor.getNextSelectedCell() should return null if it reached a range which does not select <td> nor <th>");
selection.removeAllRanges();
tr = document.getElementById("r3");
range = document.createRange();
range.setStart(tr, 0);
range.setEnd(tr, 1);
selection.addRange(range);
tr = document.getElementById("r2-1");
range = document.createRange();
range.setStart(tr, 0);
range.setEnd(tr, 1);
selection.addRange(range);
tr = document.getElementById("r7");
range = document.createRange();
range.setStart(tr, 0);
range.setEnd(tr, 1);
selection.addRange(range);
cell = SpecialPowers.unwrap(getTableEditor().getFirstSelectedCell(rangeWrapper));
is(cell.getAttribute("id"), "c3-2",
"#3-2 nsITableEditor.getFirstSelectedCell() should return the first cell element in the third row");
// c2-1-1 is in another <table>, however, getNextSelectedCell() returns it
// since it works only with ranges of Selection.
cell = SpecialPowers.unwrap(getTableEditor().getNextSelectedCell(rangeWrapper));
is(cell.getAttribute("id"), "c2-1-1",
"#2-1-1 nsITableEditor.getNextSelectedCell() should return the cell element in the child <table> element");
is(rangeWrapper.value.startContainer, document.getElementById("r2-1"),
"#2-1-1 nsITableEditor.getNextSelectedCell() should return a range whose startContainer is the row in the child <table> element");
is(rangeWrapper.value.startOffset, 0,
"#2-1-1 nsITableEditor.getNextSelectedCell() should return a range whose startOffset is 0");
is(rangeWrapper.value.endContainer, document.getElementById("r2-1"),
"#2-1-1 nsITableEditor.getNextSelectedCell() should return a range whose endContainer is the row in the child <table> element");
is(rangeWrapper.value.endOffset, 1,
"#2-1-1 nsITableEditor.getNextSelectedCell() should return a range whose endOffset is 1");
cell = SpecialPowers.unwrap(getTableEditor().getNextSelectedCell(rangeWrapper));
is(cell.getAttribute("id"), "c7-2",
"#7-2 nsITableEditor.getNextSelectedCell() should return the cell element in the last row");
is(rangeWrapper.value.startContainer, document.getElementById("r7"),
"#7-2 nsITableEditor.getNextSelectedCell() should return a range whose startContainer is the last row");
is(rangeWrapper.value.startOffset, 0,
"#7-2 nsITableEditor.getNextSelectedCell() should return a range whose startOffset is 0");
is(rangeWrapper.value.endContainer, document.getElementById("r7"),
"#7-2 nsITableEditor.getNextSelectedCell() should return a range whose endContainer is the last row");
is(rangeWrapper.value.endOffset, 1,
"#7-2 nsITableEditor.getNextSelectedCell() should return a range whose endOffset is 1");
cell = SpecialPowers.unwrap(getTableEditor().getNextSelectedCell(rangeWrapper));
is(cell, null,
"Next of #7-2 nsITableEditor.getNextSelectedCell() should return null if we reached the last cell");
SimpleTest.finish();
});
function getTableEditor() {
var Ci = SpecialPowers.Ci;
var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
return editingSession.getEditorForWindow(window).QueryInterface(Ci.nsITableEditor);
}
</script>
</body>
</html>

View file

@ -224,14 +224,7 @@ interface nsIHTMLEditor : nsISupports
AString getFontFaceState(out boolean aMixed);
/**
* getFontColorState returns what font face is in the selection.
* @param aMixed True if there is more than one font color
* @return Color string. "" is returned for none.
*/
AString getFontColorState(out boolean aMixed);
/**
* getFontColorState returns what font face is in the selection.
* getBackgroundColorState returns what the background color of the selection.
* @param aMixed True if there is more than one font color
* @return Color string. "" is returned for none.
*/

View file

@ -336,18 +336,27 @@ interface nsITableEditor : nsISupports
*/
Element getFirstSelectedCellInTable(out long aRowIndex, out long aColIndex);
/** Get next selected cell element from first selection range.
* Assumes cell-selection model where each cell
* is in a separate range (selection parent node is table row)
* Always call GetFirstSelectedCell() to initialize stored index of "next" cell
* @param aCell Selected cell or null if no more selected cells
* or ranges don't contain cell selections
* @param aRange Optional: if not null, return the selection range
* associated with the cell
*
* Returns the DOM cell element
* (in C++: returns NS_EDITOR_ELEMENT_NOT_FOUND if an element is not found
* passes NS_SUCCEEDED macro)
*/
Element getNextSelectedCell(out Range aRange);
/**
* getNextSelectedCell() is a stateful method to retrieve selected table
* cell elements which are selected by 2nd or later ranges of Selection.
* When you call getFirstSelectedCell(), it resets internal counter of
* this method. Then, following calls of getNextSelectedCell() scans the
* remaining ranges of Selection. If a range selects a <td> or <th>
* element, returns the cell element. If a range selects an element but
* neither <td> nor <th> element, this ignores the range. If a range is
* in a text node, returns null without throwing exception, but stops
* scanning the remaining ranges even you call this again.
* Note that this may cross <table> boundaries since this method just
* scans all ranges of Selection. Therefore, returning cells which
* belong to different <table> elements.
*
* @param aNextSelectedCellRange [OUT] Returns null if this method returns
* null. Otherwise, i.e., found a range which
* selects a <td> or <th> element, returns the
* range.
* @return A <td> or <th> element if one of remaining
* ranges selects a <td> or <th> element unless
* this does not meet a range in a text node.
*/
Element getNextSelectedCell(out Range aNextSelectedCellRange);
};

View file

@ -64,6 +64,11 @@ VideoBridgeParent::AllocPTextureParent(const SurfaceDescriptor& aSharedData,
{
PTextureParent* parent =
TextureHost::CreateIPDLActor(this, aSharedData, aReadLock, aLayersBackend, aFlags, aSerial, Nothing());
if (!parent) {
return nullptr;
}
mTextureMap[aSerial] = parent;
return parent;
}

View file

@ -86,6 +86,33 @@ ToLowerCaseASCII(char32_t aChar)
return aChar;
}
char
ToUpperCaseASCII(char aChar)
{
if (aChar >= 'a' && aChar <= 'z') {
return aChar - 0x20;
}
return aChar;
}
char16_t
ToUpperCaseASCII(char16_t aChar)
{
if (aChar >= 'a' && aChar <= 'z') {
return aChar - 0x20;
}
return aChar;
}
char32_t
ToUpperCaseASCII(char32_t aChar)
{
if (aChar >= 'a' && aChar <= 'z') {
return aChar - 0x20;
}
return aChar;
}
void
ToLowerCase(const nsAString& aSource,
nsAString& aDest)

View file

@ -44,6 +44,10 @@ char ToLowerCaseASCII(const char aChar);
char16_t ToLowerCaseASCII(const char16_t aChar);
char32_t ToLowerCaseASCII(const char32_t aChar);
char ToUpperCaseASCII(const char aChar);
char16_t ToUpperCaseASCII(const char16_t aChar);
char32_t ToUpperCaseASCII(const char32_t aChar);
inline bool IsUpperCase(uint32_t c) {
return ToLowerCase(c) != c;
}

View file

@ -515,13 +515,12 @@ IPDLParamTraits<Shmem>::Read(const IPC::Message* aMsg, PickleIterator* aIter,
}
Shmem::SharedMemory* rawmem = aActor->LookupSharedMemory(id);
if (rawmem) {
*aResult = Shmem(
Shmem::PrivateIPDLCaller(),
rawmem, id);
return true;
if (!rawmem) {
return false;
}
*aResult = Shmem();
*aResult = Shmem(
Shmem::PrivateIPDLCaller(),
rawmem, id);
return true;
}

View file

@ -357,10 +357,26 @@ other kinds of objects.
[visible frame][vf] currently on the calling thread's stack, or `null`
if there are no visible frames on the stack.
<code>findSources()</code>
: Return an array of all [`Debugger.Source`][source] instances of all debuggee
<code>findSources([<i>query</i>]) <i>(not yet implemented)</i></code>
: Return an array of all [`Debugger.Source`][source] instances matching
<i>query</i>. Each source appears only once in the array. <i>Query</i>
is an object whose properties restrict which sources are returned; a
source must meet all the criteria given by <i>query</i> to be returned.
If <i>query</i> is omitted, we return all sources of all debuggee
scripts.
<i>Query</i> may have the following properties:
`url`
: The source's `url` property must be equal to this value.
`global`
: The source must have been evaluated in the scope of the given global
object. If this property's value is a [`Debugger.Object`][object] instance
belonging to this `Debugger` instance, then its referent is used. If the
object is not a global object, then the global in whose scope it was
allocated is used.
Note that the result may include sources that can no longer ever be
used by the debuggee: say, eval code that has finished running, or
source for unreachable functions. Whether such sources appear can be

View file

@ -1,4 +0,0 @@
// In a debugger with no debuggees, findSources should return no scripts.
const dbg = new Debugger;
assertEq(dbg.findSources().length, 0);

View file

@ -1,15 +0,0 @@
// In a debugger with scripts, findSources finds the script source.
const g = newGlobal();
// Declare a function in order to keep the script source alive across GC.
g.evaluate(`function fa() {}`, { fileName: "a.js" });
g.evaluate(`function fb() {}`, { fileName: "b.js" });
g.evaluate(`function fc() {}`, { fileName: "c.js" });
const dbg = new Debugger();
const gw = dbg.addDebuggee(g);
const sources = dbg.findSources();
assertEq(sources.filter(s => s.url == "a.js").length, 1);
assertEq(sources.filter(s => s.url == "b.js").length, 1);
assertEq(sources.filter(s => s.url == "c.js").length, 1);

View file

@ -1,19 +0,0 @@
// In a debugger with multiple debuggees, findSources finds script sources across all debuggees.
const g1 = newGlobal();
const g2 = newGlobal();
// Declare a function in order to keep the script source alive across GC.
g1.evaluate(`function fa() {}`, { fileName: "a.js" });
g1.evaluate(`function fb() {}`, { fileName: "b.js" });
g2.evaluate(`function fc() {}`, { fileName: "c.js" });
g2.evaluate(`function fd() {}`, { fileName: "d.js" });
const dbg = new Debugger();
const g1w = dbg.addDebuggee(g1);
const g2w = dbg.addDebuggee(g2);
const sources = dbg.findSources();
assertEq(dbg.findSources().filter(s => s.url == "a.js").length, 1);
assertEq(dbg.findSources().filter(s => s.url == "b.js").length, 1);
assertEq(dbg.findSources().filter(s => s.url == "c.js").length, 1);
assertEq(dbg.findSources().filter(s => s.url == "d.js").length, 1);

View file

@ -4188,73 +4188,19 @@ Debugger::removeDebuggeeGlobal(FreeOp* fop, GlobalObject* global,
static inline DebuggerSourceReferent GetSourceReferent(JSObject* obj);
class MOZ_STACK_CLASS Debugger::QueryBase
{
protected:
QueryBase(JSContext* cx, Debugger* dbg)
: cx(cx),
debugger(dbg),
iterMarker(&cx->runtime()->gc),
realms(cx->zone()),
oom(false)
{}
// The context in which we should do our work.
JSContext* cx;
// The debugger for which we conduct queries.
Debugger* debugger;
// Require the set of realms to stay fixed while this query is alive.
gc::AutoEnterIteration iterMarker;
using RealmSet = HashSet<Realm*, DefaultHasher<Realm*>, ZoneAllocPolicy>;
// A script must be in one of these realms to match the query.
RealmSet realms;
// Indicates whether OOM has occurred while matching.
bool oom;
bool addRealm(Realm* realm) {
return realms.put(realm);
}
// Arrange for this query to match only scripts that run in |global|.
bool matchSingleGlobal(GlobalObject* global) {
MOZ_ASSERT(realms.count() == 0);
if (!addRealm(global->realm())) {
ReportOutOfMemory(cx);
return false;
}
return true;
}
// Arrange for this ScriptQuery to match all scripts running in debuggee
// globals.
bool matchAllDebuggeeGlobals() {
MOZ_ASSERT(realms.count() == 0);
// Build our realm set from the debugger's set of debuggee globals.
for (WeakGlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) {
if (!addRealm(r.front()->realm())) {
ReportOutOfMemory(cx);
return false;
}
}
return true;
}
};
/*
* A class for parsing 'findScripts' query arguments and searching for
* scripts that match the criteria they represent.
*/
class MOZ_STACK_CLASS Debugger::ScriptQuery : public Debugger::QueryBase
class MOZ_STACK_CLASS Debugger::ScriptQuery
{
public:
/* Construct a ScriptQuery to use matching scripts for |dbg|. */
ScriptQuery(JSContext* cx, Debugger* dbg)
: QueryBase(cx, dbg),
ScriptQuery(JSContext* cx, Debugger* dbg):
cx(cx),
debugger(dbg),
iterMarker(&cx->runtime()->gc),
realms(cx->zone()),
url(cx),
displayURLString(cx),
hasSource(false),
@ -4265,7 +4211,8 @@ class MOZ_STACK_CLASS Debugger::ScriptQuery : public Debugger::QueryBase
innermostForRealm(cx->zone()),
scriptVector(cx, ScriptVector(cx)),
lazyScriptVector(cx, LazyScriptVector(cx)),
wasmInstanceVector(cx, WasmInstanceObjectVector(cx))
wasmInstanceVector(cx, WasmInstanceObjectVector(cx)),
oom(false)
{}
/*
@ -4489,6 +4436,20 @@ class MOZ_STACK_CLASS Debugger::ScriptQuery : public Debugger::QueryBase
}
private:
/* The context in which we should do our work. */
JSContext* cx;
/* The debugger for which we conduct queries. */
Debugger* debugger;
/* Require the set of realms to stay fixed while the ScriptQuery is alive. */
gc::AutoEnterIteration iterMarker;
using RealmSet = HashSet<Realm*, DefaultHasher<Realm*>, ZoneAllocPolicy>;
/* A script must be in one of these realms to match the query. */
RealmSet realms;
/* If this is a string, matching scripts have urls equal to it. */
RootedValue url;
@ -4538,6 +4499,39 @@ class MOZ_STACK_CLASS Debugger::ScriptQuery : public Debugger::QueryBase
*/
Rooted<WasmInstanceObjectVector> wasmInstanceVector;
/* Indicates whether OOM has occurred while matching. */
bool oom;
bool addRealm(Realm* realm) {
return realms.put(realm);
}
/* Arrange for this ScriptQuery to match only scripts that run in |global|. */
bool matchSingleGlobal(GlobalObject* global) {
MOZ_ASSERT(realms.count() == 0);
if (!addRealm(global->realm())) {
ReportOutOfMemory(cx);
return false;
}
return true;
}
/*
* Arrange for this ScriptQuery to match all scripts running in debuggee
* globals.
*/
bool matchAllDebuggeeGlobals() {
MOZ_ASSERT(realms.count() == 0);
// Build our realm set from the debugger's set of debuggee globals.
for (WeakGlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) {
if (!addRealm(r.front()->realm())) {
ReportOutOfMemory(cx);
return false;
}
}
return true;
}
/*
* Given that parseQuery or omittedQuery has been called, prepare to match
* scripts. Set urlCString and displayURLChars as appropriate.
@ -4771,160 +4765,6 @@ Debugger::findScripts(JSContext* cx, unsigned argc, Value* vp)
return true;
}
/*
* A class for searching sources for 'findSources'.
*/
class MOZ_STACK_CLASS Debugger::SourceQuery : public Debugger::QueryBase
{
public:
using SourceSet = JS::GCHashSet<JSObject*,
js::MovableCellHasher<JSObject*>,
ZoneAllocPolicy>;
SourceQuery(JSContext* cx, Debugger* dbg)
: QueryBase(cx, dbg),
sources(cx, SourceSet(cx->zone()))
{}
bool findSources() {
if (!matchAllDebuggeeGlobals())
return false;
Realm* singletonRealm = nullptr;
if (realms.count() == 1)
singletonRealm = realms.all().front();
// Search each realm for debuggee scripts.
MOZ_ASSERT(sources.empty());
oom = false;
IterateScripts(cx, singletonRealm, this, considerScript);
IterateLazyScripts(cx, singletonRealm, this, considerLazyScript);
if (oom) {
ReportOutOfMemory(cx);
return false;
}
// TODO: Until such time that wasm modules are real ES6 modules,
// unconditionally consider all wasm toplevel instance scripts.
for (WeakGlobalObjectSet::Range r = debugger->allDebuggees(); !r.empty(); r.popFront()) {
for (wasm::Instance* instance : r.front()->realm()->wasm.instances()) {
consider(instance->object());
if (oom) {
ReportOutOfMemory(cx);
return false;
}
}
}
return true;
}
Handle<SourceSet> foundSources() const {
return sources;
}
private:
Rooted<SourceSet> sources;
static void considerScript(JSRuntime* rt, void* data, JSScript* script,
const JS::AutoRequireNoGC& nogc) {
SourceQuery* self = static_cast<SourceQuery*>(data);
self->consider(script, nogc);
}
static void considerLazyScript(JSRuntime* rt, void* data, LazyScript* lazyScript,
const JS::AutoRequireNoGC& nogc) {
SourceQuery* self = static_cast<SourceQuery*>(data);
self->consider(lazyScript, nogc);
}
void consider(JSScript* script, const JS::AutoRequireNoGC& nogc) {
if (oom || script->selfHosted())
return;
Realm* realm = script->realm();
if (!realms.has(realm))
return;
if (!script->sourceObject())
return;
ScriptSourceObject* source =
&UncheckedUnwrap(script->sourceObject())->as<ScriptSourceObject>();
if (!sources.put(source))
oom = true;
}
void consider(LazyScript* lazyScript, const JS::AutoRequireNoGC& nogc) {
if (oom)
return;
Realm* realm = lazyScript->realm();
if (!realms.has(realm))
return;
// If the script is already delazified, it should already be handled.
if (lazyScript->maybeScript())
return;
ScriptSourceObject* source = &lazyScript->sourceObject();
if (!sources.put(source))
oom = true;
}
void consider(WasmInstanceObject* instanceObject) {
if (oom)
return;
if (!sources.put(instanceObject))
oom = true;
}
};
static inline DebuggerSourceReferent
AsSourceReferent(JSObject* obj)
{
if (obj->is<ScriptSourceObject>()) {
return AsVariant(&obj->as<ScriptSourceObject>());
}
return AsVariant(&obj->as<WasmInstanceObject>());
}
/* static */ bool
Debugger::findSources(JSContext* cx, unsigned argc, Value* vp)
{
THIS_DEBUGGER(cx, argc, vp, "findSources", args, dbg);
if (gc::GCRuntime::temporaryAbortIfWasmGc(cx)) {
JS_ReportErrorASCII(cx, "API temporarily unavailable under wasm gc");
return false;
}
SourceQuery query(cx, dbg);
if (!query.findSources())
return false;
Handle<SourceQuery::SourceSet> sources(query.foundSources());
size_t resultLength = sources.count();
RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, resultLength));
if (!result)
return false;
result->ensureDenseInitializedLength(cx, 0, resultLength);
size_t i = 0;
for (auto iter = sources.get().iter(); !iter.done(); iter.next()) {
Rooted<DebuggerSourceReferent> sourceReferent(cx, AsSourceReferent(iter.get()));
RootedObject sourceObject(cx, dbg->wrapVariantReferent(cx, sourceReferent));
if (!sourceObject)
return false;
result->setDenseElement(i, ObjectValue(*sourceObject));
i++;
}
args.rval().setObject(*result);
return true;
}
/*
* A class for parsing 'findObjects' query arguments and searching for objects
* that match the criteria they represent.
@ -5341,7 +5181,6 @@ const JSFunctionSpec Debugger::methods[] = {
JS_FN("getNewestFrame", Debugger::getNewestFrame, 0, 0),
JS_FN("clearAllBreakpoints", Debugger::clearAllBreakpoints, 0, 0),
JS_FN("findScripts", Debugger::findScripts, 1, 0),
JS_FN("findSources", Debugger::findSources, 1, 0),
JS_FN("findObjects", Debugger::findObjects, 1, 0),
JS_FN("findAllGlobals", Debugger::findAllGlobals, 0, 0),
JS_FN("makeGlobalObjectReference", Debugger::makeGlobalObjectReference, 1, 0),

View file

@ -566,9 +566,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
uint32_t traceLoggerScriptedCallsLastDrainedSize;
uint32_t traceLoggerScriptedCallsLastDrainedIteration;
class QueryBase;
class ScriptQuery;
class SourceQuery;
class ObjectQuery;
MOZ_MUST_USE bool addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> obj);
@ -720,7 +718,6 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
static bool getNewestFrame(JSContext* cx, unsigned argc, Value* vp);
static bool clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp);
static bool findScripts(JSContext* cx, unsigned argc, Value* vp);
static bool findSources(JSContext* cx, unsigned argc, Value* vp);
static bool findObjects(JSContext* cx, unsigned argc, Value* vp);
static bool findAllGlobals(JSContext* cx, unsigned argc, Value* vp);
static bool makeGlobalObjectReference(JSContext* cx, unsigned argc, Value* vp);

View file

@ -26,6 +26,7 @@
#include "prprf.h"
#include "nsReadableUtils.h"
#include "mozilla/net/MozURL_ffi.h"
#include "mozilla/TextUtils.h"
//
// setenv MOZ_LOG nsStandardURL:5
@ -435,10 +436,10 @@ ParseIPv4Number(const nsACString& input, int32_t base, uint32_t& number, uint32_
for (; current < end; ++current) {
value *= base;
char c = *current;
MOZ_ASSERT((base == 10 && isdigit(c)) ||
MOZ_ASSERT((base == 10 && IsAsciiDigit(c)) ||
(base == 8 && c >= '0' && c <= '7') ||
(base == 16 && isxdigit(c)));
if (isdigit(c)) {
(base == 16 && IsAsciiHexDigit(c)));
if (IsAsciiDigit(c)) {
value += c - '0';
} else if (c >= 'a' && c <= 'f') {
value += c - 'a' + 10;

View file

@ -132,9 +132,13 @@ TRRService::ReadPrefs(const char *name)
{
MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
if (!name || !strcmp(name, TRR_PREF("mode"))) {
// 0 - off, 1 - parallel, 2 - TRR first, 3 - TRR only, 4 - shadow
// 0 - off, 1 - parallel, 2 - TRR first, 3 - TRR only, 4 - shadow,
// 5 - explicit off
uint32_t tmp;
if (NS_SUCCEEDED(Preferences::GetUint(TRR_PREF("mode"), &tmp))) {
if (tmp > MODE_TRROFF) {
tmp = MODE_TRROFF;
}
mMode = tmp;
}
}

View file

@ -9,6 +9,7 @@
#include "prprf.h"
#include "mozilla/Logging.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/TextUtils.h"
#include "prtime.h"
#include "nsIOService.h"
@ -186,9 +187,9 @@ nsFtpState::OnControlDataAvailable(const char *aData, uint32_t aDataLen)
// Does this start with a response code?
bool startNum = (line.Length() >= 3 &&
isdigit(line[0]) &&
isdigit(line[1]) &&
isdigit(line[2]));
IsAsciiDigit(line[0]) &&
IsAsciiDigit(line[1]) &&
IsAsciiDigit(line[2]));
if (mResponseMsg.IsEmpty()) {
// If we get here, then we know that we have a complete line, and

View file

@ -11,13 +11,19 @@
#include "plstr.h"
#include "nsDebug.h"
#include "prprf.h"
#include "nsUnicharUtils.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/TextUtils.h"
#include "mozilla/Sprintf.h"
/* ==================================================================== */
using mozilla::CheckedInt;
using mozilla::IsAsciiDigit;
using mozilla::IsAsciiAlpha;
using mozilla::IsAsciiLowercaseAlpha;
using mozilla::IsAsciiAlphanumeric;
static const int kMaxFTPListLen = 32768;
@ -147,9 +153,9 @@ int ParseFTPList(const char *line, struct list_state *state,
result->fe_type = 'f'; /* its a file */
else if (*p == 'm')
{
if (isdigit(line[pos]))
if (IsAsciiDigit(line[pos]))
{
while (pos < linelen && isdigit(line[pos]))
while (pos < linelen && IsAsciiDigit(line[pos]))
pos++;
if (pos < linelen && line[pos] == ',')
{
@ -163,9 +169,9 @@ int ParseFTPList(const char *line, struct list_state *state,
}
else if (*p == 's')
{
if (isdigit(line[pos]))
if (IsAsciiDigit(line[pos]))
{
while (pos < linelen && isdigit(line[pos]))
while (pos < linelen && IsAsciiDigit(line[pos]))
pos++;
if (pos < linelen && line[pos] == ',' &&
((&line[pos]) - (p+1)) < int(sizeof(result->fe_size)-1) )
@ -175,7 +181,7 @@ int ParseFTPList(const char *line, struct list_state *state,
}
}
}
else if (isalpha(*p)) /* 'i'/'up' or unknown "fact" (property) */
else if (IsAsciiAlpha(*p)) /* 'i'/'up' or unknown "fact" (property) */
{
while (pos < linelen && *++p != ',')
pos++;
@ -253,9 +259,9 @@ int ParseFTPList(const char *line, struct list_state *state,
break;
}
else if (p[pos] != '.' && p[pos] != '~' &&
!isdigit(p[pos]) && !isalpha(p[pos]))
!IsAsciiAlphanumeric(p[pos]))
break;
else if (isalpha(p[pos]) && p[pos] != toupper(p[pos]))
else if (IsAsciiLowercaseAlpha(p[pos]))
break;
}
if (pos > 0)
@ -271,9 +277,9 @@ int ParseFTPList(const char *line, struct list_state *state,
{
pos--;
if (p[pos] != '$' && p[pos] != '_' && p[pos] != '-' &&
p[pos] != '~' && !isdigit(p[pos]) && !isalpha(p[pos]))
p[pos] != '~' && !IsAsciiAlphanumeric(p[pos]))
break;
else if (isalpha(p[pos]) && p[pos] != toupper(p[pos]))
else if (IsAsciiLowercaseAlpha(p[pos]))
break;
}
if (pos == 0)
@ -309,9 +315,9 @@ int ParseFTPList(const char *line, struct list_state *state,
((toklen[3]==10 || toklen[3]==11 ) &&
(tokens[3][toklen[3]-3]) == '.' )
) && /* time in [H]H:MM[:SS[.CC]] format */
isdigit(*tokens[1]) && /* size */
isdigit(*tokens[2]) && /* date */
isdigit(*tokens[3]) /* time */
IsAsciiDigit(*tokens[1]) && /* size */
IsAsciiDigit(*tokens[2]) && /* date */
IsAsciiDigit(*tokens[3]) /* time */
)
{
lstyle = 'V';
@ -348,7 +354,7 @@ int ParseFTPList(const char *line, struct list_state *state,
while (lstyle && pos < toklen[0] && *p != ']')
{
if (*p != '$' && *p != '.' && *p != '_' && *p != '-' &&
*p != '~' && !isdigit(*p) && !isalpha(*p))
*p != '~' && !IsAsciiAlphanumeric(*p))
lstyle = 0;
pos++;
p++;
@ -368,9 +374,9 @@ int ParseFTPList(const char *line, struct list_state *state,
while (lstyle && pos < toklen[0] && *p != ';')
{
if (*p != '$' && *p != '.' && *p != '_' && *p != '-' &&
*p != '~' && !isdigit(*p) && !isalpha(*p))
*p != '~' && !IsAsciiAlphanumeric(*p))
lstyle = 0;
else if (isalpha(*p) && *p != toupper(*p))
else if (IsAsciiLowercaseAlpha(*p))
lstyle = 0;
p++;
pos++;
@ -381,7 +387,7 @@ int ParseFTPList(const char *line, struct list_state *state,
lstyle = 0;
for (pos++;lstyle && pos < toklen[0];pos++)
{
if (!isdigit(tokens[0][pos]))
if (!IsAsciiDigit(tokens[0][pos]))
lstyle = 0;
}
}
@ -404,11 +410,11 @@ int ParseFTPList(const char *line, struct list_state *state,
state->carry_buf_len = pos;
return '?'; /* tell caller to treat as junk */
}
else if (isdigit(*tokens[1])) /* not no-privs message */
else if (IsAsciiDigit(*tokens[1])) /* not no-privs message */
{
for (pos = 0; lstyle && pos < (toklen[1]); pos++)
{
if (!isdigit((tokens[1][pos])) && (tokens[1][pos]) != '/')
if (!IsAsciiDigit((tokens[1][pos])) && (tokens[1][pos]) != '/')
lstyle = 0;
}
if (lstyle && numtoks > 4) /* Multinet or UCX but not CMU */
@ -429,7 +435,7 @@ int ParseFTPList(const char *line, struct list_state *state,
state->parsed_one = 1;
state->lstyle = lstyle;
if (isdigit(*tokens[1])) /* not permission denied etc */
if (IsAsciiDigit(*tokens[1])) /* not permission denied etc */
{
/* strip leading directory name */
if (*tokens[0] == '[') /* CMU server */
@ -491,8 +497,8 @@ int ParseFTPList(const char *line, struct list_state *state,
if (*p == '-')
p++;
tbuf[0] = p[0];
tbuf[1] = tolower(p[1]);
tbuf[2] = tolower(p[2]);
tbuf[1] = ToLowerCaseASCII(p[1]);
tbuf[2] = ToLowerCaseASCII(p[2]);
month_num = 0;
for (pos = 0; pos < (12*3); pos+=3)
{
@ -518,7 +524,7 @@ int ParseFTPList(const char *line, struct list_state *state,
return result->fe_type;
} /* if (isdigit(*tokens[1])) */
} /* if (IsAsciiDigit(*tokens[1])) */
return '?'; /* junk */
@ -575,9 +581,9 @@ int ParseFTPList(const char *line, struct list_state *state,
if ( (*tokens[pos+1] == '-' &&
*tokens[pos+2] == '-' &&
*tokens[pos+3] == '-') ||
(isdigit(*tokens[pos+1]) &&
isdigit(*tokens[pos+2]) &&
isdigit(*tokens[pos+3])) )
(IsAsciiDigit(*tokens[pos+1]) &&
IsAsciiDigit(*tokens[pos+2]) &&
IsAsciiDigit(*tokens[pos+3])) )
{
lstyle = 'C';
tokmarker = pos;
@ -593,7 +599,7 @@ int ParseFTPList(const char *line, struct list_state *state,
{
for (pos = 0, p = tokens[0]; lstyle && pos < toklen[0]; pos++, p++)
{
if (isalpha(*p) && toupper(*p) != *p)
if (IsAsciiLowercaseAlpha(*p))
lstyle = 0;
}
for (pos = tokmarker+1; pos <= tokmarker+3; pos++)
@ -602,7 +608,7 @@ int ParseFTPList(const char *line, struct list_state *state,
{
for (p = tokens[pos]; lstyle && p<(tokens[pos]+toklen[pos]); p++)
{
if (!isdigit(*p))
if (!IsAsciiDigit(*p))
lstyle = 0;
}
}
@ -623,7 +629,7 @@ int ParseFTPList(const char *line, struct list_state *state,
else if (pos != 2 && pos != 5)
lstyle = 0;
}
else if (*p != '-' && !isdigit(*p))
else if (*p != '-' && !IsAsciiDigit(*p))
lstyle = 0;
else if (*p == '-' && pos != 4 && pos != 7)
lstyle = 0;
@ -631,7 +637,7 @@ int ParseFTPList(const char *line, struct list_state *state,
for (pos = 0, p = tokens[tokmarker+5];
lstyle && pos < toklen[tokmarker+5]; pos++, p++)
{
if (*p != ':' && !isdigit(*p))
if (*p != ':' && !IsAsciiDigit(*p))
lstyle = 0;
else if (*p == ':' && pos != (toklen[tokmarker+5]-3)
&& pos != (toklen[tokmarker+5]-6))
@ -723,16 +729,16 @@ int ParseFTPList(const char *line, struct list_state *state,
// "05-03-13 22:01 <DIR> APPS"
if ((numtoks >= 4) && (toklen[0] == 8 || toklen[0] == 10) &&
(toklen[1] == 5 || toklen[1] == 7) &&
(*tokens[2] == '<' || isdigit(*tokens[2])) )
(*tokens[2] == '<' || IsAsciiDigit(*tokens[2])) )
{
p = tokens[0];
if ( isdigit(p[0]) && isdigit(p[1]) && p[2]=='-' &&
isdigit(p[3]) && isdigit(p[4]) && p[5]=='-' &&
isdigit(p[6]) && isdigit(p[7]) )
if ( IsAsciiDigit(p[0]) && IsAsciiDigit(p[1]) && p[2]=='-' &&
IsAsciiDigit(p[3]) && IsAsciiDigit(p[4]) && p[5]=='-' &&
IsAsciiDigit(p[6]) && IsAsciiDigit(p[7]) )
{
p = tokens[1];
if ( isdigit(p[0]) && isdigit(p[1]) && p[2]==':' &&
isdigit(p[3]) && isdigit(p[4]) &&
if ( IsAsciiDigit(p[0]) && IsAsciiDigit(p[1]) && p[2]==':' &&
IsAsciiDigit(p[3]) && IsAsciiDigit(p[4]) &&
(toklen[1] == 5 || (toklen[1] == 7 &&
(p[5]=='A' || p[5]=='P') && p[6]=='M')))
{
@ -745,7 +751,7 @@ int ParseFTPList(const char *line, struct list_state *state,
{
for (pos = 1; (lstyle && pos < toklen[2]); pos++)
{
if (!isdigit(*++p))
if (!IsAsciiDigit(*++p))
lstyle = 0;
}
}
@ -873,16 +879,16 @@ int ParseFTPList(const char *line, struct list_state *state,
*/
p = &(line[toklen[0]]);
/* \s(\d\d-\d\d-\d\d)\s+(\d\d:\d\d)\s */
if (numtoks >= 4 && toklen[0] <= 18 && isdigit(*tokens[0]) &&
if (numtoks >= 4 && toklen[0] <= 18 && IsAsciiDigit(*tokens[0]) &&
(linelen - toklen[0]) >= (53-18) &&
p[18-18] == ' ' && p[34-18] == ' ' &&
p[37-18] == '-' && p[40-18] == '-' && p[43-18] == ' ' &&
p[45-18] == ' ' && p[48-18] == ':' && p[51-18] == ' ' &&
isdigit(p[35-18]) && isdigit(p[36-18]) &&
isdigit(p[38-18]) && isdigit(p[39-18]) &&
isdigit(p[41-18]) && isdigit(p[42-18]) &&
isdigit(p[46-18]) && isdigit(p[47-18]) &&
isdigit(p[49-18]) && isdigit(p[50-18])
IsAsciiDigit(p[35-18]) && IsAsciiDigit(p[36-18]) &&
IsAsciiDigit(p[38-18]) && IsAsciiDigit(p[39-18]) &&
IsAsciiDigit(p[41-18]) && IsAsciiDigit(p[42-18]) &&
IsAsciiDigit(p[46-18]) && IsAsciiDigit(p[47-18]) &&
IsAsciiDigit(p[49-18]) && IsAsciiDigit(p[50-18])
)
{
lstyle = 'O'; /* OS/2 */
@ -890,7 +896,7 @@ int ParseFTPList(const char *line, struct list_state *state,
{
for (pos = 1; lstyle && pos < toklen[0]; pos++)
{
if (!isdigit(tokens[0][pos]))
if (!IsAsciiDigit(tokens[0][pos]))
lstyle = 0;
}
}
@ -1036,34 +1042,34 @@ int ParseFTPList(const char *line, struct list_state *state,
* (\d\d\d\d|\d\:\d\d|\d\d\:\d\d|\d\:\d\d\:\d\d|\d\d\:\d\d\:\d\d)
* \s+(.+)$
*/
if (isdigit(*tokens[pos]) /* size */
if (IsAsciiDigit(*tokens[pos]) /* size */
/* (\w\w\w) */
&& toklen[pos+1] == 3 && isalpha(*tokens[pos+1]) &&
isalpha(tokens[pos+1][1]) && isalpha(tokens[pos+1][2])
&& toklen[pos+1] == 3 && IsAsciiAlpha(*tokens[pos+1]) &&
IsAsciiAlpha(tokens[pos+1][1]) && IsAsciiAlpha(tokens[pos+1][2])
/* (\d|\d\d) */
&& isdigit(*tokens[pos+2]) &&
&& IsAsciiDigit(*tokens[pos+2]) &&
(toklen[pos+2] == 1 ||
(toklen[pos+2] == 2 && isdigit(tokens[pos+2][1])))
&& toklen[pos+3] >= 4 && isdigit(*tokens[pos+3])
(toklen[pos+2] == 2 && IsAsciiDigit(tokens[pos+2][1])))
&& toklen[pos+3] >= 4 && IsAsciiDigit(*tokens[pos+3])
/* (\d\:\d\d\:\d\d|\d\d\:\d\d\:\d\d) */
&& (toklen[pos+3] <= 5 || (
(toklen[pos+3] == 7 || toklen[pos+3] == 8) &&
(tokens[pos+3][toklen[pos+3]-3]) == ':'))
&& isdigit(tokens[pos+3][toklen[pos+3]-2])
&& isdigit(tokens[pos+3][toklen[pos+3]-1])
&& IsAsciiDigit(tokens[pos+3][toklen[pos+3]-2])
&& IsAsciiDigit(tokens[pos+3][toklen[pos+3]-1])
&& (
/* (\d\d\d\d) */
((toklen[pos+3] == 4 || toklen[pos+3] == 5) &&
isdigit(tokens[pos+3][1]) &&
isdigit(tokens[pos+3][2]) )
IsAsciiDigit(tokens[pos+3][1]) &&
IsAsciiDigit(tokens[pos+3][2]) )
/* (\d\:\d\d|\d\:\d\d\:\d\d) */
|| ((toklen[pos+3] == 4 || toklen[pos+3] == 7) &&
(tokens[pos+3][1]) == ':' &&
isdigit(tokens[pos+3][2]) && isdigit(tokens[pos+3][3]))
IsAsciiDigit(tokens[pos+3][2]) && IsAsciiDigit(tokens[pos+3][3]))
/* (\d\d\:\d\d|\d\d\:\d\d\:\d\d) */
|| ((toklen[pos+3] == 5 || toklen[pos+3] == 8) &&
isdigit(tokens[pos+3][1]) && (tokens[pos+3][2]) == ':' &&
isdigit(tokens[pos+3][3]) && isdigit(tokens[pos+3][4]))
IsAsciiDigit(tokens[pos+3][1]) && (tokens[pos+3][2]) == ':' &&
IsAsciiDigit(tokens[pos+3][3]) && IsAsciiDigit(tokens[pos+3][4]))
)
)
{
@ -1075,7 +1081,7 @@ int ParseFTPList(const char *line, struct list_state *state,
unsigned int i;
for (i = 0; i < toklen[tokmarker]; i++)
{
if (!isdigit(*p++))
if (!IsAsciiDigit(*p++))
{
lstyle = 0;
break;
@ -1274,7 +1280,7 @@ int ParseFTPList(const char *line, struct list_state *state,
* CMT.CSV 0 Jul 06 1995 14:56 RHA
*/
if (numtoks >= 4 && toklen[0] < 13 &&
((toklen[1] == 5 && *tokens[1] == '<') || isdigit(*tokens[1])) )
((toklen[1] == 5 && *tokens[1] == '<') || IsAsciiDigit(*tokens[1])) )
{
if (numtoks == 4
&& (toklen[2] == 8 || toklen[2] == 9)
@ -1282,12 +1288,12 @@ int ParseFTPList(const char *line, struct list_state *state,
((tokens[2][2]) == '-' && (tokens[2][5]) == '-'))
&& (toklen[3] == 4 || toklen[3] == 5)
&& (tokens[3][toklen[3]-3]) == ':'
&& isdigit(tokens[2][0]) && isdigit(tokens[2][1])
&& isdigit(tokens[2][3]) && isdigit(tokens[2][4])
&& isdigit(tokens[2][6]) && isdigit(tokens[2][7])
&& (toklen[2] < 9 || isdigit(tokens[2][8]))
&& isdigit(tokens[3][toklen[3]-1]) && isdigit(tokens[3][toklen[3]-2])
&& isdigit(tokens[3][toklen[3]-4]) && isdigit(*tokens[3])
&& IsAsciiDigit(tokens[2][0]) && IsAsciiDigit(tokens[2][1])
&& IsAsciiDigit(tokens[2][3]) && IsAsciiDigit(tokens[2][4])
&& IsAsciiDigit(tokens[2][6]) && IsAsciiDigit(tokens[2][7])
&& (toklen[2] < 9 || IsAsciiDigit(tokens[2][8]))
&& IsAsciiDigit(tokens[3][toklen[3]-1]) && IsAsciiDigit(tokens[3][toklen[3]-2])
&& IsAsciiDigit(tokens[3][toklen[3]-4]) && IsAsciiDigit(*tokens[3])
)
{
lstyle = 'w';
@ -1296,13 +1302,13 @@ int ParseFTPList(const char *line, struct list_state *state,
&& toklen[2] == 3 && toklen[3] == 2
&& toklen[4] == 4 && toklen[5] == 5
&& (tokens[5][2]) == ':'
&& isalpha(tokens[2][0]) && isalpha(tokens[2][1])
&& isalpha(tokens[2][2])
&& isdigit(tokens[3][0]) && isdigit(tokens[3][1])
&& isdigit(tokens[4][0]) && isdigit(tokens[4][1])
&& isdigit(tokens[4][2]) && isdigit(tokens[4][3])
&& isdigit(tokens[5][0]) && isdigit(tokens[5][1])
&& isdigit(tokens[5][3]) && isdigit(tokens[5][4])
&& IsAsciiAlpha(tokens[2][0]) && IsAsciiAlpha(tokens[2][1])
&& IsAsciiAlpha(tokens[2][2])
&& IsAsciiDigit(tokens[3][0]) && IsAsciiDigit(tokens[3][1])
&& IsAsciiDigit(tokens[4][0]) && IsAsciiDigit(tokens[4][1])
&& IsAsciiDigit(tokens[4][2]) && IsAsciiDigit(tokens[4][3])
&& IsAsciiDigit(tokens[5][0]) && IsAsciiDigit(tokens[5][1])
&& IsAsciiDigit(tokens[5][3]) && IsAsciiDigit(tokens[5][4])
/* could also check that (&(tokens[5][5]) - tokens[2]) == 17 */
)
{
@ -1316,7 +1322,7 @@ int ParseFTPList(const char *line, struct list_state *state,
{
for (pos = 0; lstyle && pos < toklen[1]; pos++)
{
if (!isdigit(*p++))
if (!IsAsciiDigit(*p++))
lstyle = 0;
}
} /* not <DIR> */
@ -1334,7 +1340,7 @@ int ParseFTPList(const char *line, struct list_state *state,
result->fe_type = 'd';
p = tokens[1];
if (isdigit(*p))
if (IsAsciiDigit(*p))
{
result->fe_type = 'f';
pos = toklen[1];
@ -1347,9 +1353,9 @@ int ParseFTPList(const char *line, struct list_state *state,
p = tokens[2];
if (toklen[2] == 3) /* Chameleon */
{
tbuf[0] = toupper(p[0]);
tbuf[1] = tolower(p[1]);
tbuf[2] = tolower(p[2]);
tbuf[0] = ToUpperCaseASCII(p[0]);
tbuf[1] = ToLowerCaseASCII(p[1]);
tbuf[2] = ToLowerCaseASCII(p[2]);
for (pos = 0; pos < (12*3); pos+=3)
{
if (tbuf[0] == month_names[pos+0] &&
@ -1467,7 +1473,7 @@ int ParseFTPList(const char *line, struct list_state *state,
if (linelen > pos)
{
p = &line[pos];
if ((*p == '-' || *p == '=' || isdigit(*p)) &&
if ((*p == '-' || *p == '=' || IsAsciiDigit(*p)) &&
((linelen == (pos+1)) ||
(linelen >= (pos+3) && p[1] == ' ' && p[2] == ' ')) )
{
@ -1494,7 +1500,7 @@ int ParseFTPList(const char *line, struct list_state *state,
{
for (pos = 0; lstyle && pos < toklen[tokmarker]; pos++)
{
if (!isdigit(tokens[tokmarker][pos]))
if (!IsAsciiDigit(tokens[tokmarker][pos]))
lstyle = 0;
}
}
@ -1551,7 +1557,7 @@ int ParseFTPList(const char *line, struct list_state *state,
result->fe_type = 'd';
}
}
else if (isdigit(*tokens[tokmarker]))
else if (IsAsciiDigit(*tokens[tokmarker]))
{
pos = toklen[tokmarker];
if (pos > (sizeof(result->fe_size)-1))
@ -1569,24 +1575,24 @@ int ParseFTPList(const char *line, struct list_state *state,
pos = toklen[pos];
if ((pos == 4 || pos == 5)
&& isdigit(*p) && isdigit(p[pos-1]) && isdigit(p[pos-2])
&& IsAsciiDigit(*p) && IsAsciiDigit(p[pos-1]) && IsAsciiDigit(p[pos-2])
&& ((pos == 5 && p[2] == ':') ||
(pos == 4 && (isdigit(p[1]) || p[1] == ':')))
(pos == 4 && (IsAsciiDigit(p[1]) || p[1] == ':')))
)
{
month_num = tokmarker+1; /* assumed position of month field */
pos = tokmarker+2; /* assumed position of mday field */
if (isdigit(*tokens[month_num])) /* positions are reversed */
if (IsAsciiDigit(*tokens[month_num])) /* positions are reversed */
{
month_num++;
pos--;
}
p = tokens[month_num];
if (isdigit(*tokens[pos])
if (IsAsciiDigit(*tokens[pos])
&& (toklen[pos] == 1 ||
(toklen[pos] == 2 && isdigit(tokens[pos][1])))
(toklen[pos] == 2 && IsAsciiDigit(tokens[pos][1])))
&& toklen[month_num] == 3
&& isalpha(*p) && isalpha(p[1]) && isalpha(p[2]) )
&& IsAsciiAlpha(*p) && IsAsciiAlpha(p[1]) && IsAsciiAlpha(p[2]) )
{
pos = atoi(tokens[pos]);
if (pos > 0 && pos <= 31)

View file

@ -1174,4 +1174,4 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
static const int32_t kUnknownId = -1;
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1543497165654000);
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1543836493627000);

View file

@ -8,7 +8,7 @@
/*****************************************************************************/
#include <stdint.h>
const PRTime gPreloadListExpirationTime = INT64_C(1545916292150000);
const PRTime gPreloadListExpirationTime = INT64_C(1546255516939000);
%%
0-1.party, 1
0.me.uk, 1

File diff suppressed because one or more lines are too long

View file

@ -1 +1,135 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="./manifest.json"><title>React App</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script type="text/javascript" src="./static/js/main.js"></script></body></html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
Notice the use of . in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "./favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<script type="text/javascript" src="./js/testfile.js?id=0"></script>
<script type="text/javascript" src="./js/testfile.js?id=1"></script>
<script type="text/javascript" src="./js/testfile.js?id=2"></script>
<script type="text/javascript" src="./js/testfile.js?id=3"></script>
<script type="text/javascript" src="./js/testfile.js?id=4"></script>
<script type="text/javascript" src="./js/testfile.js?id=5"></script>
<script type="text/javascript" src="./js/testfile.js?id=6"></script>
<script type="text/javascript" src="./js/testfile.js?id=7"></script>
<script type="text/javascript" src="./js/testfile.js?id=8"></script>
<script type="text/javascript" src="./js/testfile.js?id=9"></script>
<script type="text/javascript" src="./js/testfile.js?id=10"></script>
<script type="text/javascript" src="./js/testfile.js?id=11"></script>
<script type="text/javascript" src="./js/testfile.js?id=12"></script>
<script type="text/javascript" src="./js/testfile.js?id=13"></script>
<script type="text/javascript" src="./js/testfile.js?id=14"></script>
<script type="text/javascript" src="./js/testfile.js?id=15"></script>
<script type="text/javascript" src="./js/testfile.js?id=16"></script>
<script type="text/javascript" src="./js/testfile.js?id=17"></script>
<script type="text/javascript" src="./js/testfile.js?id=18"></script>
<script type="text/javascript" src="./js/testfile.js?id=19"></script>
<script type="text/javascript" src="./js/testfile.js?id=20"></script>
<script type="text/javascript" src="./js/testfile.js?id=21"></script>
<script type="text/javascript" src="./js/testfile.js?id=22"></script>
<script type="text/javascript" src="./js/testfile.js?id=23"></script>
<script type="text/javascript" src="./js/testfile.js?id=24"></script>
<script type="text/javascript" src="./js/testfile.js?id=25"></script>
<script type="text/javascript" src="./js/testfile.js?id=26"></script>
<script type="text/javascript" src="./js/testfile.js?id=27"></script>
<script type="text/javascript" src="./js/testfile.js?id=28"></script>
<script type="text/javascript" src="./js/testfile.js?id=29"></script>
<script type="text/javascript" src="./js/testfile.js?id=30"></script>
<script type="text/javascript" src="./js/testfile.js?id=31"></script>
<script type="text/javascript" src="./js/testfile.js?id=32"></script>
<script type="text/javascript" src="./js/testfile.js?id=33"></script>
<script type="text/javascript" src="./js/testfile.js?id=34"></script>
<script type="text/javascript" src="./js/testfile.js?id=35"></script>
<script type="text/javascript" src="./js/testfile.js?id=36"></script>
<script type="text/javascript" src="./js/testfile.js?id=37"></script>
<script type="text/javascript" src="./js/testfile.js?id=38"></script>
<script type="text/javascript" src="./js/testfile.js?id=39"></script>
<script type="text/javascript" src="./js/testfile.js?id=40"></script>
<script type="text/javascript" src="./js/testfile.js?id=41"></script>
<script type="text/javascript" src="./js/testfile.js?id=42"></script>
<script type="text/javascript" src="./js/testfile.js?id=43"></script>
<script type="text/javascript" src="./js/testfile.js?id=44"></script>
<script type="text/javascript" src="./js/testfile.js?id=45"></script>
<script type="text/javascript" src="./js/testfile.js?id=46"></script>
<script type="text/javascript" src="./js/testfile.js?id=47"></script>
<script type="text/javascript" src="./js/testfile.js?id=48"></script>
<script type="text/javascript" src="./js/testfile.js?id=49"></script>
<script type="text/javascript" src="./js/testfile.js?id=50"></script>
<script type="text/javascript" src="./js/testfile.js?id=51"></script>
<script type="text/javascript" src="./js/testfile.js?id=52"></script>
<script type="text/javascript" src="./js/testfile.js?id=53"></script>
<script type="text/javascript" src="./js/testfile.js?id=54"></script>
<script type="text/javascript" src="./js/testfile.js?id=55"></script>
<script type="text/javascript" src="./js/testfile.js?id=56"></script>
<script type="text/javascript" src="./js/testfile.js?id=57"></script>
<script type="text/javascript" src="./js/testfile.js?id=58"></script>
<script type="text/javascript" src="./js/testfile.js?id=59"></script>
<script type="text/javascript" src="./js/testfile.js?id=60"></script>
<script type="text/javascript" src="./js/testfile.js?id=61"></script>
<script type="text/javascript" src="./js/testfile.js?id=62"></script>
<script type="text/javascript" src="./js/testfile.js?id=63"></script>
<script type="text/javascript" src="./js/testfile.js?id=64"></script>
<script type="text/javascript" src="./js/testfile.js?id=65"></script>
<script type="text/javascript" src="./js/testfile.js?id=66"></script>
<script type="text/javascript" src="./js/testfile.js?id=67"></script>
<script type="text/javascript" src="./js/testfile.js?id=68"></script>
<script type="text/javascript" src="./js/testfile.js?id=69"></script>
<script type="text/javascript" src="./js/testfile.js?id=70"></script>
<script type="text/javascript" src="./js/testfile.js?id=71"></script>
<script type="text/javascript" src="./js/testfile.js?id=72"></script>
<script type="text/javascript" src="./js/testfile.js?id=73"></script>
<script type="text/javascript" src="./js/testfile.js?id=74"></script>
<script type="text/javascript" src="./js/testfile.js?id=75"></script>
<script type="text/javascript" src="./js/testfile.js?id=76"></script>
<script type="text/javascript" src="./js/testfile.js?id=77"></script>
<script type="text/javascript" src="./js/testfile.js?id=78"></script>
<script type="text/javascript" src="./js/testfile.js?id=79"></script>
<script type="text/javascript" src="./js/testfile.js?id=80"></script>
<script type="text/javascript" src="./js/testfile.js?id=81"></script>
<script type="text/javascript" src="./js/testfile.js?id=82"></script>
<script type="text/javascript" src="./js/testfile.js?id=83"></script>
<script type="text/javascript" src="./js/testfile.js?id=84"></script>
<script type="text/javascript" src="./js/testfile.js?id=85"></script>
<script type="text/javascript" src="./js/testfile.js?id=86"></script>
<script type="text/javascript" src="./js/testfile.js?id=87"></script>
<script type="text/javascript" src="./js/testfile.js?id=88"></script>
<script type="text/javascript" src="./js/testfile.js?id=89"></script>
<script type="text/javascript" src="./js/testfile.js?id=90"></script>
<script type="text/javascript" src="./js/testfile.js?id=91"></script>
<script type="text/javascript" src="./js/testfile.js?id=92"></script>
<script type="text/javascript" src="./js/testfile.js?id=93"></script>
<script type="text/javascript" src="./js/testfile.js?id=94"></script>
<script type="text/javascript" src="./js/testfile.js?id=95"></script>
<script type="text/javascript" src="./js/testfile.js?id=96"></script>
<script type="text/javascript" src="./js/testfile.js?id=97"></script>
<script type="text/javascript" src="./js/testfile.js?id=98"></script>
<script type="text/javascript" src="./js/testfile.js?id=99"></script>
<script type="text/javascript" src="./js/testfile.js?id=100"></script>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<script type="text/javascript" src="./static/js/main.js"></script></body>
</html>

File diff suppressed because one or more lines are too long

View file

@ -10,6 +10,7 @@ const { openDebuggerAndLog, reloadDebuggerAndLog } = require("./debugger-helpers
const EXPECTED = {
sources: 14,
file: "ga.js",
sourceURL: COMPLICATED_URL,
text: "Math;function ga(a,b){return a.name=b}"
};

View file

@ -11,8 +11,9 @@ const { createContext, openDebuggerAndLog, pauseDebugger, reloadDebuggerAndLog,
removeBreakpoints, resume, step } = require("./debugger-helpers");
const EXPECTED = {
sources: 7,
sources: 107,
file: "App.js",
sourceURL: `${PAGES_BASE_URL}custom/debugger/static/js/App.js`,
text: "import React, { Component } from 'react';"
};

View file

@ -122,6 +122,14 @@ function waitForSources(dbg, expectedSources) {
return waitForState(dbg, countSources, "count sources");
}
function waitForSource(dbg, sourceURL) {
const { selectors } = dbg;
function hasSource(state) {
return selectors.getSourceByURL(state, sourceURL);
}
return waitForState(dbg, hasSource, `has source ${sourceURL}`);
}
async function waitForPaused(dbg) {
const onLoadedScope = waitForLoadedScopes(dbg);
const onStateChange = waitForState(
@ -201,7 +209,7 @@ function evalInContent(dbg, tab, testFunction) {
async function openDebuggerAndLog(label, expected) {
const onLoad = async (toolbox, panel) => {
const dbg = await createContext(panel);
await waitForSources(dbg, expected.sources);
await waitForSource(dbg, expected.sourceURL);
await selectSource(dbg, expected.file);
await waitForText(dbg, expected.file, expected.text);
await waitForMetaData(dbg);

View file

@ -10,6 +10,7 @@ const { openDebuggerAndLog, reloadDebuggerAndLog } = require("./debugger-helpers
const EXPECTED = {
sources: 1,
file: "simple.html",
sourceURL: SIMPLE_URL,
text: "This is a simple page"
};

View file

@ -66,6 +66,12 @@ exports.getToolbox = function() {
return gDevTools.getToolbox(target);
};
exports.navigateTo = function(url) {
let tab = getActiveTab();
let target = TargetFactory.forTab(tab);
target.activeTab.navigateTo(url);
};
/**
* Wait for any pending paint.
* The tool may have touched the DOM elements at the very end of the current test.

View file

@ -65,11 +65,9 @@ customElements.define("printpreview-toolbar", class PrintPreviewToolbar extends
<toolbarseparator class="toolbarseparator-primary"/>
<button label="&close.label;" accesskey="&close.accesskey;" oncommand="PrintUtils.exitPrintPreview();" icon="close"/>
<data id="print-preview-prompt-title" value="&customPrompt.title;"/>
`, `
<!DOCTYPE bindings [
<!ENTITY % printPreviewDTD SYSTEM "chrome://global/locale/printPreview.dtd" >
%printPreviewDTD;
]>`));
`, [
"chrome://global/locale/printPreview.dtd",
]));
this.mPrintButton = document.getElementById("print-preview-print");

View file

@ -24,8 +24,8 @@ class MozXULElement extends XULElement {
* This process is required instead of calling the createElement method directly
* because bindings get attached when:
*
* 1) the node gets a layout frame constructed, or
* 2) the node gets its JavaScript reflector created, if it's in the document,
* 1. the node gets a layout frame constructed, or
* 2. the node gets its JavaScript reflector created, if it's in the document,
*
* whichever happens first. The createElement method would return a JavaScript
* reflector, but the element wouldn't be in the document, so the node wouldn't
@ -33,18 +33,24 @@ class MozXULElement extends XULElement {
* document, it won't get XBL attached until either the frame is constructed or
* the reflector is garbage collected and the element is touched again.
*
* @param str
* @param {string} str
* String with the XML representation of XUL elements.
* @param preamble
* String to be inserted above any markup. This can be used
* to insert XML entity text, for instance.
* @param {string[]} [entities]
* An array of DTD URLs containing entity definitions.
*
* @return DocumentFragment containing the corresponding element tree, including
* element nodes but excluding any text node.
* @return {DocumentFragment} `DocumentFragment` instance containing
* the corresponding element tree, including element nodes
* but excluding any text node.
*/
static parseXULToFragment(str, preamble = "") {
static parseXULToFragment(str, entities = []) {
let doc = gXULDOMParser.parseFromString(`
${preamble}
${entities.length ? `<!DOCTYPE bindings [
${entities.reduce((preamble, url, index) => {
return preamble + `<!ENTITY % _dtd-${index} SYSTEM "${url}">
%_dtd-${index};
`;
}, "")}
]>` : ""}
<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
${str}
</box>

View file

@ -8,10 +8,9 @@
const cachedFragments = {
get entities() {
return `<!DOCTYPE bindings [
<!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd">
%textcontextDTD;
]>`;
return [
"chrome://global/locale/textcontext.dtd",
];
},
get editMenuItems() {
return `