Merge m-c to inbound, a=merge

This commit is contained in:
Wes Kocher 2016-08-16 22:06:58 -07:00
commit c84ec3b3f3
277 changed files with 5313 additions and 3573 deletions

View File

@ -419,11 +419,11 @@
// display debug information about the cert error.
var errorCode = document.getElementById("errorCode");
if (errorCode) {
errorCode.href = "#technicalInformation";
errorCode.href = "javascript:void(0)";
errorCode.addEventListener("click", () => {
var div = document.getElementById("certificateErrorDebugInformation");
if (toggleDisplay(div) == "block") {
div.scrollIntoView({block: "start", behavior: "smooth"});
let debugInfo = document.getElementById("certificateErrorDebugInformation");
if (toggleDisplay(debugInfo) == "block") {
debugInfo.scrollIntoView({block: "start", behavior: "smooth"});
}
}, false);
}
@ -646,7 +646,6 @@
</div>
<div id="certificateErrorDebugInformation">
<a name="technicalInformation"></a>
<button id="copyToClipboard">&certerror.copyToClipboard.label;</button>
<div id="certificateErrorText"/>
<button id="copyToClipboard">&certerror.copyToClipboard.label;</button>

View File

@ -6798,7 +6798,6 @@ var gIdentityHandler = {
if (this._identityPopup.state == "open") {
this.updateSitePermissions();
this._identityPopupMultiView.setHeightToFit();
}
},
@ -7317,7 +7316,6 @@ var gIdentityHandler = {
button.setAttribute("class", "identity-popup-permission-remove-button");
button.addEventListener("command", () => {
this._permissionList.removeChild(container);
this._identityPopupMultiView.setHeightToFit();
if (aPermission.inUse &&
["camera", "microphone", "screen"].includes(aPermission.id)) {
let windowId = this._sharingState.windowId;

View File

@ -16,7 +16,7 @@
<panelmultiview id="identity-popup-multiView"
mainViewId="identity-popup-mainView">
<panelview id="identity-popup-mainView" flex="1">
<panelview id="identity-popup-mainView">
<!-- Security Section -->
<hbox id="identity-popup-security" class="identity-popup-section">

View File

@ -318,7 +318,7 @@
break;
case "popuphidden":
this.removeAttribute("panelopen");
this._mainView.style.removeProperty("height");
this._mainView.style.removeProperty("max-height");
this.showMainView();
this._mainViewObserver.disconnect();
break;
@ -346,7 +346,7 @@
// Ignore the mutation that'll fire when we set the height of
// the main view.
this.ignoreMutations = true;
this._mainView.style.height =
this._mainView.style.maxHeight =
this.getBoundingClientRect().height + "px";
this.ignoreMutations = false;
]]></body>

View File

@ -809,11 +809,11 @@ extensions.registerSchemaAPI("tabs", (extension, context) => {
},
insertCSS: function(tabId, details) {
return self.tabs._execute(tabId, details, "css", "insertCSS");
return self.tabs._execute(tabId, details, "css", "insertCSS").then(() => {});
},
removeCSS: function(tabId, details) {
return self.tabs._execute(tabId, details, "css", "removeCSS");
return self.tabs._execute(tabId, details, "css", "removeCSS").then(() => {});
},
connect: function(tabId, connectInfo) {

View File

@ -70,9 +70,14 @@ add_task(function* testDefaultDetails() {
"/foo/bar.png",
{"19": "foo/bar.png"},
{"38": "foo/bar.png"},
{"19": "foo/bar.png", "38": "baz/quux.png"},
];
if (window.devicePixelRatio > 1) {
icons.push({"19": "baz/quux.png", "38": "foo/bar.png"});
} else {
icons.push({"19": "foo/bar.png", "38": "baz/quux@2x.png"});
}
let expectedURL = new RegExp(String.raw`^moz-extension://[^/]+/foo/bar\.png$`);
for (let icon of icons) {
@ -95,6 +100,7 @@ add_task(function* testDefaultDetails() {
files: {
"foo/bar.png": imageBuffer,
"baz/quux.png": imageBuffer,
"baz/quux@2x.png": imageBuffer,
},
});

View File

@ -21,7 +21,8 @@ add_task(function* testExecuteScript() {
browser.tabs.executeScript({
code: "42",
}).then(result => {
browser.test.assertEq(42, result, "Expected callback result");
browser.test.assertEq(1, result.length, "Expected one callback result");
browser.test.assertEq(42, result[0], "Expected callback result");
}),
browser.tabs.executeScript({
@ -37,13 +38,15 @@ add_task(function* testExecuteScript() {
browser.tabs.executeScript({
file: "script.js",
}).then(result => {
browser.test.assertEq(undefined, result, "Expected callback result");
browser.test.assertEq(1, result.length, "Expected one callback result");
browser.test.assertEq(undefined, result[0], "Expected callback result");
}),
browser.tabs.executeScript({
file: "script2.js",
}).then(result => {
browser.test.assertEq(27, result, "Expected callback result");
browser.test.assertEq(1, result.length, "Expected one callback result");
browser.test.assertEq(27, result[0], "Expected callback result");
}),
browser.tabs.executeScript({
@ -62,9 +65,10 @@ add_task(function* testExecuteScript() {
code: "location.href;",
runAt: "document_end",
}).then(result => {
browser.test.assertTrue(typeof(result) == "string", "Result is a string");
browser.test.assertEq(1, result.length, "Expected callback result");
browser.test.assertEq("string", typeof result[0], "Result is a string");
browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result), "Result is correct");
browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result[0]), "Result is correct");
}),
browser.tabs.executeScript({
@ -118,7 +122,7 @@ add_task(function* testExecuteScript() {
browser.tabs.executeScript({
code: "Promise.resolve(42)",
}).then(result => {
browser.test.assertEq(42, result, "Got expected promise resolution value as result");
browser.test.assertEq(42, result[0], "Got expected promise resolution value as result");
}),
browser.tabs.executeScript({
@ -138,19 +142,21 @@ add_task(function* testExecuteScript() {
code: "location.href;",
frameId: frames[0].frameId,
}).then(result => {
browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result), `Result for frameId[0] is correct: ${result}`);
browser.test.assertEq(1, result.length, "Expected one result");
browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result[0]), `Result for frameId[0] is correct: ${result[0]}`);
}),
browser.tabs.executeScript({
code: "location.href;",
frameId: frames[1].frameId,
}).then(result => {
browser.test.assertEq("http://mochi.test:8888/", result, "Result for frameId[1] is correct");
browser.test.assertEq(1, result.length, "Expected one result");
browser.test.assertEq("http://mochi.test:8888/", result[0], "Result for frameId[1] is correct");
}),
browser.tabs.create({url: "http://example.com/"}).then(tab => {
return browser.tabs.executeScript(tab.id, {code: "location.href"}).then(result => {
browser.test.assertEq("http://example.com/", result, "Script executed correctly in new tab");
browser.test.assertEq("http://example.com/", result[0], "Script executed correctly in new tab");
return browser.tabs.remove(tab.id);
});

View File

@ -49,7 +49,7 @@ add_task(function* testExecuteScript() {
return browser.tabs.executeScript({
code: `(${checkCSS})()`,
});
}).then(result => {
}).then(([result]) => {
browser.test.assertEq(background, result[0], "Expected background color");
browser.test.assertEq(foreground, result[1], "Expected foreground color");
return next();

View File

@ -33,14 +33,14 @@ add_task(function* () {
return awaitLoad(tabId);
}).then(() => {
return browser.tabs.executeScript(tabId, {code: "document.body.textContent"});
}).then(textContent => {
}).then(([textContent]) => {
browser.test.assertEq("", textContent, "`textContent` should be empty when bypassCache=false");
return browser.tabs.reload(tabId, {bypassCache: true});
}).then(() => {
return awaitLoad(tabId);
}).then(() => {
return browser.tabs.executeScript(tabId, {code: "document.body.textContent"});
}).then(textContent => {
}).then(([textContent]) => {
let [pragma, cacheControl] = textContent.split(":");
browser.test.assertEq("no-cache", pragma, "`pragma` should be set to `no-cache` when bypassCache is true");
browser.test.assertEq("no-cache", cacheControl, "`cacheControl` should be set to `no-cache` when bypassCache is true");

View File

@ -66,7 +66,7 @@ add_task(function* testExecuteScript() {
return browser.tabs.executeScript({
code: `(${checkCSS})()`,
});
}).then(result => {
}).then(([result]) => {
browser.test.assertEq(background, result[0], "Expected background color");
browser.test.assertEq(foreground, result[1], "Expected foreground color");
return next();

View File

@ -188,17 +188,12 @@ add_task(function* back_navigation_on_browser_tab_should_close_dialog() {
});
add_task(function* escape_should_close_dialog() {
todo(false, "BrowserTestUtils.sendChar('VK_ESCAPE') triggers " +
"'can't access dead object' on `navigator` in this test. " +
"See bug 1238065.")
return;
yield open_subdialog_and_test_generic_start_state(tab.linkedBrowser);
info("canceling the dialog");
yield close_subdialog_and_test_generic_end_state(tab.linkedBrowser,
function() { return BrowserTestUtils.sendChar("VK_ESCAPE", tab.linkedBrowser); },
null, undefined, {runClosingFnOutsideOfContentTask: true});
function() { return BrowserTestUtils.synthesizeKey("VK_ESCAPE", {}, tab.linkedBrowser); },
"cancel", 0, {runClosingFnOutsideOfContentTask: true});
});
add_task(function* correct_width_and_height_should_be_used_for_dialog() {

View File

@ -126,7 +126,7 @@
border: 1px solid rgb(192,192,192);
border-radius: 3px;
margin: 5px;
padding: 2px 12px;
padding: 2px 10px;
background-color: rgb(251,251,251);
color: rgb(71,71,71);
box-shadow: 0 1px rgba(255, 255, 255, 0.5),
@ -134,6 +134,25 @@
-moz-appearance: none;
}
.customizationmode-button > .box-inherit {
border-width: 0;
padding-inline-start: 0;
padding-inline-end: 0;
}
.customizationmode-button > .button-icon {
margin-inline-start: 0;
}
.customizationmode-button:not([type=menu]) > .button-text {
margin-inline-end: 0;
}
.customizationmode-button > .button-menu-dropmarker {
margin-inline-end: 0;
padding-inline-end: 0;
}
#customization-titlebar-visibility-button[checked],
#customization-devedition-theme-button[checked] {
background-color: rgb(218, 218, 218);
@ -157,11 +176,6 @@
-moz-image-region: rect(0, 24px, 24px, 0);
}
#customization-lwtheme-button,
#customization-titlebar-visibility-button {
padding: 2px 7px;
}
#customization-lwtheme-button > .box-inherit > .box-inherit > .button-text,
#customization-titlebar-visibility-button > .button-box > .button-text {
/* Sadly, button.css thinks its margins are perfect for everyone. */

View File

@ -155,6 +155,7 @@ def old_configure_options(*options):
@old_configure_options(
'--cache-file',
'--datadir',
'--enable-accessibility',
'--enable-address-sanitizer',
'--enable-alsa',
@ -249,6 +250,8 @@ def old_configure_options(*options):
'--enable-webrtc',
'--enable-xul',
'--enable-zipwriter',
'--includedir',
'--libdir',
'--no-create',
'--prefix',
'--with-android-cxx-stl',

View File

@ -62,6 +62,32 @@ def normsep(path):
return mozpath.normsep(path)
@imports('ctypes')
@imports(_from='ctypes', _import='wintypes')
@imports(_from='mozbuild.configure.constants', _import='WindowsBinaryType')
def windows_binary_type(path):
"""Obtain the type of a binary on Windows.
Returns WindowsBinaryType constant.
"""
GetBinaryTypeW = ctypes.windll.kernel32.GetBinaryTypeW
GetBinaryTypeW.argtypes = [wintypes.LPWSTR, wintypes.POINTER(wintypes.DWORD)]
GetBinaryTypeW.restype = wintypes.BOOL
bin_type = wintypes.DWORD()
res = GetBinaryTypeW(path, ctypes.byref(bin_type))
if not res:
die('could not obtain binary type of %s' % path)
if bin_type.value == 0:
return WindowsBinaryType('win32')
elif bin_type.value == 6:
return WindowsBinaryType('win64')
# If we see another binary type, something is likely horribly wrong.
else:
die('unsupported binary type on %s: %s' % (path, bin_type))
@imports('ctypes')
@imports(_from='ctypes', _import='wintypes')
def get_GetShortPathNameW():

View File

@ -119,6 +119,7 @@ support-files =
doc_terminate-on-tab-close.html
doc_watch-expressions.html
doc_watch-expression-button.html
doc_whitespace-property-names.html
doc_with-frame.html
doc_worker-source-map.html
doc_WorkerActor.attach-tab1.html
@ -521,6 +522,8 @@ skip-if = e10s && debug
skip-if = e10s && debug
[browser_dbg_variables-view-07.js]
skip-if = e10s && debug
[browser_dbg_variables-view-08.js]
skip-if = e10s && debug
[browser_dbg_variables-view-accessibility.js]
subsuite = clipboard
skip-if = e10s && debug

View File

@ -121,7 +121,5 @@ var test = Task.async(function* () {
}
}
debugger;
resumeDebuggerThenCloseAndFinish(panel);
});

View File

@ -28,9 +28,11 @@ var test = Task.async(function* () {
ok(scope, "Should get the current function's scope.");
let proxy;
[...scope].forEach(function([name, value]) {
if(name === "proxy") proxy = value;
});
for (let [name, value] of scope) {
if (name === "proxy") {
proxy = value;
}
}
ok(proxy, "Should have found the proxy variable");
info("Expanding variable 'proxy'");
@ -52,16 +54,16 @@ var test = Task.async(function* () {
}
} else {
is(property, "<handler>", "There shouldn't be properties other than <target> and <handler>");
for(let [subprop, subdata] of data) if(subprop === "name") {
is(subdata.value, "handler", "The value of '<handler>' should be the [[ProxyHandler]]");
foundHandler = true;
for (let [subprop, subdata] of data) {
if(subprop === "name") {
is(subdata.value, "handler", "The value of '<handler>' should be the [[ProxyHandler]]");
foundHandler = true;
}
}
}
}
ok(foundTarget, "Should have found the '<target>' property containing the [[ProxyTarget]]");
ok(foundHandler, "Should have found the '<handler>' property containing the [[ProxyHandler]]");
debugger;
resumeDebuggerThenCloseAndFinish(panel);
});

View File

@ -0,0 +1,61 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that property values are not missing when the property names only contain whitespace.
*/
const TAB_URL = EXAMPLE_URL + "doc_whitespace-property-names.html";
var test = Task.async(function* () {
let options = {
source: TAB_URL,
line: 1
};
var dbg = initDebugger(TAB_URL, options);
const [tab,, panel] = yield dbg;
const debuggerLineNumber = 24;
const scopes = waitForCaretAndScopes(panel, debuggerLineNumber);
callInTab(tab, "doPause");
yield scopes;
const variables = panel.panelWin.DebuggerView.Variables;
ok(variables, "Should get the variables view.");
const scope = [...variables][0];
ok(scope, "Should get the current function's scope.");
let obj;
for (let [name, value] of scope) {
if (name === "obj") {
obj = value;
}
}
ok(obj, "Should have found the 'obj' variable");
info("Expanding variable 'obj'");
let expanded = once(variables, "fetched");
obj.expand();
yield expanded;
let values = [" ", "\r", "\n", "\t", "\f", "\uFEFF", "\xA0"];
let count = values.length;
for (let [property, value] of obj) {
let index = values.indexOf(property);
if (index >= 0) {
--count;
is(value._nameString, property,
"The _nameString is different than the property name");
is(value._valueString, index + "",
"The _valueString is different than the stringified value");
is(value._valueLabel.getAttribute("value"), index + "",
"The _valueLabel value is different than the stringified value");
}
}
is(count, 0, "There are " + count + " missing properties");
resumeDebuggerThenCloseAndFinish(panel);
});

View File

@ -0,0 +1,29 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Debugger + Whitespace property name test page</title>
</head>
<body>
<script>
window.obj = {
" ": 0,
"\r": 1,
"\n": 2,
"\t": 3,
"\f": 4,
"\uFEFF": 5,
"\xA0": 6
};
window.doPause = function () {
var obj = window.obj;
debugger;
};
</script>
</body>
</html>

View File

@ -23,6 +23,9 @@ const SCROLL_REPEAT_MS = 100;
const EventEmitter = require("devtools/shared/event-emitter");
const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
// Some margin may be required for visible element detection.
const SCROLL_MARGIN = 1;
/**
* Component to replicate functionality of XUL arrowscrollbox
* for breadcrumbs
@ -40,6 +43,9 @@ function ArrowScrollBox(win, container) {
ArrowScrollBox.prototype = {
// Scroll behavior, exposed for testing
scrollBehavior: "smooth",
/**
* Build the HTML, add to the DOM and start listening to
* events
@ -68,10 +74,26 @@ ArrowScrollBox.prototype = {
this.inner.addEventListener("overflow", this.onOverflow, false);
},
/**
* Determine whether the current text directionality is RTL
*/
isRtl: function () {
return this.win.getComputedStyle(this.container).direction === "rtl";
},
/**
* Scroll to the specified element using the current scroll behavior
* @param {Element} element element to scroll
* @param {String} block desired alignment of element after scrolling
*/
scrollToElement: function (element, block) {
element.scrollIntoView({ block: block, behavior: this.scrollBehavior });
},
/**
* Call the given function once; then continuously
* while the mouse button is held
* @param {repeatFn} the function to repeat while the button is held
* @param {Function} repeatFn the function to repeat while the button is held
*/
clickOrHold: function (repeatFn) {
let timer;
@ -109,7 +131,7 @@ ArrowScrollBox.prototype = {
}
let element = this.inner.childNodes[0];
element.scrollIntoView({ block: "start", behavior: "smooth" });
this.scrollToElement(element, "start");
},
/**
@ -122,7 +144,7 @@ ArrowScrollBox.prototype = {
}
let element = children[children.length - 1];
element.scrollIntoView({ block: "start", behavior: "smooth" });
this.scrollToElement(element, "start");
},
/**
@ -135,7 +157,8 @@ ArrowScrollBox.prototype = {
return;
}
element.scrollIntoView({ block: "start", behavior: "smooth" });
let block = this.isRtl() ? "end" : "start";
this.scrollToElement(element, block);
};
this.clickOrHold(scrollToStart);
@ -151,7 +174,8 @@ ArrowScrollBox.prototype = {
return;
}
element.scrollIntoView({ block: "end", behavior: "smooth" });
let block = this.isRtl() ? "start" : "end";
this.scrollToElement(element, block);
};
this.clickOrHold(scrollToEnd);
@ -195,47 +219,73 @@ ArrowScrollBox.prototype = {
this.emit("overflow");
},
/**
* Check whether the element is to the left of its container but does
* not also span the entire container.
* @param {Number} left the left scroll point of the container
* @param {Number} right the right edge of the container
* @param {Number} elementLeft the left edge of the element
* @param {Number} elementRight the right edge of the element
*/
elementLeftOfContainer: function (left, right, elementLeft, elementRight) {
return elementLeft < (left - SCROLL_MARGIN)
&& elementRight < (right - SCROLL_MARGIN);
},
/**
* Check whether the element is to the right of its container but does
* not also span the entire container.
* @param {Number} left the left scroll point of the container
* @param {Number} right the right edge of the container
* @param {Number} elementLeft the left edge of the element
* @param {Number} elementRight the right edge of the element
*/
elementRightOfContainer: function (left, right, elementLeft, elementRight) {
return elementLeft > (left + SCROLL_MARGIN)
&& elementRight > (right + SCROLL_MARGIN);
},
/**
* Get the first (i.e. furthest left for LTR)
* non visible element in the scroll box
* non or partly visible element in the scroll box
*/
getFirstInvisibleElement: function () {
let start = this.inner.scrollLeft;
let end = this.inner.scrollLeft + this.inner.clientWidth;
let crumbs = this.inner.childNodes;
for (let i = crumbs.length - 1; i > -1; i--) {
let element = crumbs[i];
let elementRight = element.offsetLeft + element.offsetWidth;
if (element.offsetLeft < start) {
// edge case, check the element isn't already visible
if (elementRight >= end) {
continue;
}
return element;
}
}
let elementsList = Array.from(this.inner.childNodes).reverse();
return null;
let predicate = this.isRtl() ?
this.elementRightOfContainer : this.elementLeftOfContainer;
return this.findFirstWithBounds(elementsList, predicate);
},
/**
* Get the last (i.e. furthest right for LTR)
* non-visible element in the scroll box
* non or partly visible element in the scroll box
*/
getLastInvisibleElement: function () {
let end = this.inner.scrollLeft + this.inner.clientWidth;
let elementStart = 0;
for (let element of this.inner.childNodes) {
let elementEnd = elementStart + element.offsetWidth;
if (elementEnd > end) {
// Edge case: check the element isn't bigger than the
// container and thus already in view
if (elementStart > this.inner.scrollLeft) {
return element;
}
}
let predicate = this.isRtl() ?
this.elementLeftOfContainer : this.elementRightOfContainer;
return this.findFirstWithBounds(this.inner.childNodes, predicate);
},
elementStart = elementEnd;
/**
* Find the first element that matches the given predicate, called with bounds
* information
* @param {Array} elements an ordered list of elements
* @param {Function} predicate a function to be called with bounds
* information
*/
findFirstWithBounds: function (elements, predicate) {
let left = this.inner.scrollLeft;
let right = left + this.inner.clientWidth;
for (let element of elements) {
let elementLeft = element.offsetLeft - element.parentElement.offsetLeft;
let elementRight = elementLeft + element.offsetWidth;
// Check that the starting edge of the element is out of the visible area
// and that the ending edge does not span the whole container
if (predicate(left, right, elementLeft, elementRight)) {
return element;
}
}
return null;
@ -725,7 +775,7 @@ HTMLBreadcrumbs.prototype = {
// FIXME bug 684352: make sure its immediate neighbors are visible too.
if (!this.isDestroyed) {
let element = this.nodeHierarchy[this.currentIndex].button;
element.scrollIntoView({ block: "end", behavior: "smooth" });
this.arrowScrollBox.scrollToElement(element, "end");
}
},
@ -815,6 +865,7 @@ HTMLBreadcrumbs.prototype = {
}
// If this was an interesting deletion; then trim the breadcrumb trail
let trimmed = false;
if (reason === "markupmutation") {
for (let {type, removed} of mutations) {
if (type !== "childList") {
@ -825,6 +876,7 @@ HTMLBreadcrumbs.prototype = {
let removedIndex = this.indexOf(node);
if (removedIndex > -1) {
this.cutAfter(removedIndex - 1);
trimmed = true;
}
}
}
@ -833,6 +885,10 @@ HTMLBreadcrumbs.prototype = {
if (!this.selection.isElementNode()) {
// no selection
this.setCursor(-1);
if (trimmed) {
// Since something changed, notify the interested parties.
this.inspector.emit("breadcrumbs-updated", this.selection.nodeFront);
}
return;
}

View File

@ -483,6 +483,9 @@ CssComputedView.prototype = {
onItem: (propView) => {
propView.refresh();
},
onCancel: () => {
deferred.reject("_refreshProcess of computed view cancelled");
},
onDone: () => {
this._refreshProcess = null;
this.noResults.hidden = this.numVisibleProperties > 0;

View File

@ -182,17 +182,21 @@ InspectorPanel.prototype = {
this._supportsScrollIntoView = false;
this._supportsResolveRelativeURL = false;
return promise.all([
this._target.actorHasMethod("domwalker", "duplicateNode").then(value => {
this._supportsDuplicateNode = value;
}).catch(e => console.error(e)),
this._target.actorHasMethod("domnode", "scrollIntoView").then(value => {
this._supportsScrollIntoView = value;
}).catch(e => console.error(e)),
this._target.actorHasMethod("inspector", "resolveRelativeURL").then(value => {
this._supportsResolveRelativeURL = value;
}).catch(e => console.error(e)),
]);
// Use getActorDescription first so that all actorHasMethod calls use
// a cached response from the server.
return this._target.getActorDescription("domwalker").then(desc => {
return promise.all([
this._target.actorHasMethod("domwalker", "duplicateNode").then(value => {
this._supportsDuplicateNode = value;
}).catch(e => console.error(e)),
this._target.actorHasMethod("domnode", "scrollIntoView").then(value => {
this._supportsScrollIntoView = value;
}).catch(e => console.error(e)),
this._target.actorHasMethod("inspector", "resolveRelativeURL").then(value => {
this._supportsResolveRelativeURL = value;
}).catch(e => console.error(e)),
]);
});
},
_deferredOpen: function (defaultSelection) {

View File

@ -132,6 +132,7 @@ InspectorSearch.prototype = {
},
_onClearSearch: function () {
this.searchBox.classList.remove("devtools-style-searchbox-no-match");
this.searchBox.value = "";
this.searchClearButton.hidden = true;
}

View File

@ -23,9 +23,7 @@ support-files =
[browser_layout_rotate-labels-on-sides.js]
[browser_layout_sync.js]
[browser_layout_tooltips.js]
# [browser_layout_update-after-navigation.js]
# Disabled for too many intermittent failures (bug 1288213)
# [browser_layout_update-after-reload.js]
# Disabled for too many intermittent failures (bug 1287745)
[browser_layout_update-after-navigation.js]
[browser_layout_update-after-reload.js]
# [browser_layout_update-in-iframes.js]
# Bug 1020038 layout-view updates for iframe elements changes

View File

@ -4,6 +4,7 @@ subsuite = devtools
support-files =
doc_inspector_add_node.html
doc_inspector_breadcrumbs.html
doc_inspector_breadcrumbs_visibility.html
doc_inspector_delete-selected-node-01.html
doc_inspector_delete-selected-node-02.html
doc_inspector_embed.html
@ -48,6 +49,7 @@ support-files =
skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
[browser_inspector_breadcrumbs_mutations.js]
[browser_inspector_breadcrumbs_namespaced.js]
[browser_inspector_breadcrumbs_visibility.js]
[browser_inspector_delete-selected-node-01.js]
[browser_inspector_delete-selected-node-02.js]
[browser_inspector_delete-selected-node-03.js]

View File

@ -0,0 +1,106 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the start and end buttons on the breadcrumb trail bring the right
// crumbs into the visible area, for both LTR and RTL
let { Toolbox } = require("devtools/client/framework/toolbox");
const TEST_URI = URL_ROOT + "doc_inspector_breadcrumbs_visibility.html";
const NODE_ONE = "div#aVeryLongIdToExceedTheBreadcrumbTruncationLimit";
const NODE_TWO = "div#anotherVeryLongIdToExceedTheBreadcrumbTruncationLimit";
const NODE_THREE = "div#aThirdVeryLongIdToExceedTheTruncationLimit";
const NODE_FOUR = "div#aFourthOneToExceedTheTruncationLimit";
const NODE_FIVE = "div#aFifthOneToExceedTheTruncationLimit";
const NODE_SIX = "div#aSixthOneToExceedTheTruncationLimit";
const NODE_SEVEN = "div#aSeventhOneToExceedTheTruncationLimit";
const NODES = [
{ action: "start", title: NODE_SIX },
{ action: "start", title: NODE_FIVE },
{ action: "start", title: NODE_FOUR },
{ action: "start", title: NODE_THREE },
{ action: "start", title: NODE_TWO },
{ action: "start", title: NODE_ONE },
{ action: "end", title: NODE_TWO },
{ action: "end", title: NODE_THREE },
{ action: "end", title: NODE_FOUR },
{ action: "end", title: NODE_FIVE },
{ action: "end", title: NODE_SIX }
];
add_task(function* () {
let { inspector, toolbox } = yield openInspectorForURL(TEST_URI);
// No way to wait for scrolling to end (Bug 1172171)
// Rather than wait a max time; limit test to instant scroll behavior
inspector.breadcrumbs.arrowScrollBox.scrollBehavior = "instant";
yield toolbox.switchHost(Toolbox.HostType.WINDOW);
let hostWindow = toolbox._host._window;
let originalWidth = hostWindow.outerWidth;
let originalHeight = hostWindow.outerHeight;
hostWindow.resizeTo(640, 300);
info("Testing transitions ltr");
yield pushPref("intl.uidirection.en-US", "ltr");
yield testBreadcrumbTransitions(hostWindow, inspector);
info("Testing transitions rtl");
yield pushPref("intl.uidirection.en-US", "rtl");
yield testBreadcrumbTransitions(hostWindow, inspector);
hostWindow.resizeTo(originalWidth, originalHeight);
});
function* testBreadcrumbTransitions(hostWindow, inspector) {
let breadcrumbs = inspector.panelDoc.getElementById("inspector-breadcrumbs");
let startBtn = breadcrumbs.querySelector(".scrollbutton-up");
let endBtn = breadcrumbs.querySelector(".scrollbutton-down");
let container = breadcrumbs.querySelector(".html-arrowscrollbox-inner");
let breadcrumbsUpdated = inspector.once("breadcrumbs-updated");
info("Selecting initial node");
yield selectNode(NODE_SEVEN, inspector);
// So just need to wait for a duration
yield breadcrumbsUpdated;
let initialCrumb = container.querySelector("button[checked]");
is(isElementInViewport(hostWindow, initialCrumb), true,
"initial element was visible");
for (let node of NODES) {
info("Checking for visibility of crumb " + node.title);
if (node.action === "end") {
info("Simulating click of end button");
EventUtils.synthesizeMouseAtCenter(endBtn, {}, inspector.panelWin);
} else if (node.action === "start") {
info("Simulating click of start button");
EventUtils.synthesizeMouseAtCenter(startBtn, {}, inspector.panelWin);
}
yield breadcrumbsUpdated;
let selector = "button[title=\"" + node.title + "\"]";
let relevantCrumb = container.querySelector(selector);
is(isElementInViewport(hostWindow, relevantCrumb), true,
node.title + " crumb is visible");
}
}
function isElementInViewport(window, el) {
let rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= window.innerHeight &&
rect.right <= window.innerWidth
);
}
registerCleanupFunction(function () {
// Restore the host type for other tests.
Services.prefs.clearUserPref("devtools.toolbox.host");
});

View File

@ -93,6 +93,7 @@ add_task(function* () {
function* deleteNodeWithContextMenu(selector) {
yield selectNode(selector, inspector);
let nodeToBeDeleted = inspector.selection.nodeFront;
info("Getting the node container in the markup view.");
let container = yield getContainerForSelector(selector, inspector);
@ -111,6 +112,16 @@ add_task(function* () {
info("Waiting for inspector to update.");
yield inspector.once("inspector-updated");
// Since the mutations are sent asynchronously from the server, the
// inspector-updated event triggered by the deletion might happen before
// the mutation is received and the element is removed from the
// breadcrumbs. See bug 1284125.
if (inspector.breadcrumbs.indexOf(nodeToBeDeleted) > -1) {
info("Crumbs haven't seen deletion. Waiting for breadcrumbs-updated.");
yield inspector.once("breadcrumbs-updated");
}
return menuItem;
}

View File

@ -0,0 +1,22 @@
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=windows-1252">
</head>
<body>
<div id="aVeryLongIdToExceedTheBreadcrumbTruncationLimit">
<div id="anotherVeryLongIdToExceedTheBreadcrumbTruncationLimit">
<div id="aThirdVeryLongIdToExceedTheTruncationLimit">
<div id="aFourthOneToExceedTheTruncationLimit">
<div id="aFifthOneToExceedTheTruncationLimit">
<div id="aSixthOneToExceedTheTruncationLimit">
<div id="aSeventhOneToExceedTheTruncationLimit">
A text node at the end
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -165,6 +165,7 @@ HarBuilder.prototype = {
request.httpVersion = file.httpVersion || "";
request.headers = this.buildHeaders(file.requestHeaders);
request.headers = this.appendHeadersPostData(request.headers, file);
request.cookies = this.buildCookies(file.requestCookies);
request.queryString = NetworkHelper.parseQueryString(
@ -199,6 +200,33 @@ HarBuilder.prototype = {
return this.buildNameValuePairs(input.headers);
},
appendHeadersPostData: function (input = [], file) {
if (!file.requestPostData) {
return input;
}
this.fetchData(file.requestPostData.postData.text).then(value => {
let contentType = value.match(/Content-Type: ([^;\s]+)/);
let contentLength = value.match(/Content-Length: (.+)/);
if (contentType && contentType.length > 1) {
input.push({
name: "Content-Type",
value: contentType[1]
});
}
if (contentLength && contentLength.length > 1) {
input.push({
name: "Content-Length",
value: contentLength[1]
});
}
});
return input;
},
buildCookies: function (input) {
if (!input) {
return [];

View File

@ -12,8 +12,8 @@ const URL = EXAMPLE_URL.replace("http:", "https:");
const TEST_URL = URL + "service-workers/status-codes.html";
var test = Task.async(function* () {
let [, debuggee, monitor] = yield initNetMonitor(TEST_URL, null, true);
add_task(function* () {
let [tab, , monitor] = yield initNetMonitor(TEST_URL, null, true);
info("Starting test... ");
let { NetMonitorView } = monitor.panelWin;
@ -35,11 +35,16 @@ var test = Task.async(function* () {
];
info("Registering the service worker...");
yield debuggee.registerServiceWorker();
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
yield content.wrappedJSObject.registerServiceWorker();
});
info("Performing requests...");
debuggee.performRequests();
yield waitForNetworkEvents(monitor, REQUEST_DATA.length);
let wait = waitForNetworkEvents(monitor, REQUEST_DATA.length);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
let index = 0;
for (let request of REQUEST_DATA) {
@ -64,8 +69,9 @@ var test = Task.async(function* () {
}
info("Unregistering the service worker...");
yield debuggee.unregisterServiceWorker();
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
yield content.wrappedJSObject.unregisterServiceWorker();
});
yield teardown(monitor);
finish();
});

View File

@ -37,24 +37,19 @@ define(function (require, exports, module) {
delim = (i == array.length - 1 ? "" : ", ");
if (value === array) {
items.push(Reference({
key: i,
object: value,
delim: delim
}));
} else {
items.push(ItemRep({
key: i,
object: value,
delim: delim
}));
}
items.push(ItemRep({
key: i,
object: value,
// Hardcode tiny mode to avoid recursive handling.
mode: "tiny",
delim: delim
}));
} catch (exc) {
items.push(ItemRep({
key: i,
object: exc,
delim: delim,
key: i
mode: "tiny",
delim: delim
}));
}
}
@ -173,30 +168,16 @@ define(function (require, exports, module) {
let object = this.props.object;
let delim = this.props.delim;
let mode = this.props.mode;
return (
DOM.span({},
Rep({object: object}),
Rep({object: object, mode: mode}),
delim
)
);
}
}));
/**
* Renders cycle references in an array.
*/
let Reference = React.createFactory(React.createClass({
displayName: "Reference",
render: function () {
let tooltip = "Circular reference";
return (
DOM.span({title: tooltip},
"[…]")
);
}
}));
function supportsObject(object, type) {
return Array.isArray(object) ||
Object.prototype.toString.call(object) === "[object Arguments]";

View File

@ -11,10 +11,8 @@ define(function (require, exports, module) {
const React = require("devtools/client/shared/vendor/react");
// Reps
const { isGrip } = require("./rep-utils");
// Shortcuts
const { span } = React.DOM;
const { createFactories, isGrip } = require("./rep-utils");
const { rep } = createFactories(require("./grip").Grip);
/**
* Renders DOM event objects.
@ -26,40 +24,18 @@ define(function (require, exports, module) {
object: React.PropTypes.object.isRequired
},
getTitle: function (grip) {
if (this.props.objectLink) {
return this.props.objectLink({
object: grip
}, grip.preview.type);
}
return grip.preview.type;
},
summarizeEvent: function (grip) {
let info = [];
let eventFamily = grip.class;
let props = grip.preview.properties;
if (eventFamily == "MouseEvent") {
info.push("clientX=", props.clientX, ", clientY=", props.clientY);
} else if (eventFamily == "KeyboardEvent") {
info.push("charCode=", props.charCode, ", keyCode=", props.keyCode);
} else if (eventFamily == "MessageEvent") {
info.push("origin=", props.origin, ", data=", props.data);
}
return info.join("");
},
render: function () {
let grip = this.props.object;
return (
span({className: "objectBox objectBox-event"},
this.getTitle(grip),
this.summarizeEvent(grip)
)
);
// Use `Object.assign` to keep `this.props` without changes becuase:
// 1. JSON.stringify/JSON.parse is slow.
// 2. Immutable.js is planned for the future.
let props = Object.assign({}, this.props);
props.object = Object.assign({}, this.props.object);
props.object.preview = Object.assign({}, this.props.object.preview);
props.object.preview.ownProperties = props.object.preview.properties;
delete props.object.preview.properties;
props.object.ownPropertyLength =
Object.keys(props.object.preview.ownProperties).length;
return rep(props);
},
});

View File

@ -56,6 +56,10 @@ define(function (require, exports, module) {
}
let delim;
// number of grip.preview.items is limited to 10, but we may have more
// items in grip-array
let delimMax = grip.preview.length > array.length ?
array.length : array.length - 1;
let provider = this.props.provider;
for (let i = 0; i < array.length && i < max; i++) {
@ -63,7 +67,7 @@ define(function (require, exports, module) {
let itemGrip = array[i];
let value = provider ? provider.getValue(itemGrip) : itemGrip;
delim = (i == array.length - 1 ? "" : ", ");
delim = (i == delimMax ? "" : ", ");
if (value === array) {
items.push(Reference({
@ -86,14 +90,15 @@ define(function (require, exports, module) {
)));
}
}
if (array.length > max) {
if (array.length > max || grip.preview.length > array.length) {
let objectLink = this.props.objectLink || span;
let leftItemNum = grip.preview.length - max > 0 ?
grip.preview.length - max : grip.preview.length - array.length;
items.push(Caption({
key: "more",
object: objectLink({
object: this.props.object
}, (grip.preview.length - max) + " more…")
}, leftItemNum + " more…")
}));
}

View File

@ -110,7 +110,8 @@ define(function (require, exports, module) {
indexes.forEach((i) => {
let name = Object.keys(ownProperties)[i];
let value = ownProperties[name].value;
let prop = ownProperties[name];
let value = prop.value !== undefined ? prop.value : prop;
props.push(PropRep(Object.assign({}, this.props, {
key: name,
mode: "tiny",
@ -144,7 +145,7 @@ define(function (require, exports, module) {
}
let prop = ownProperties[name];
let value = prop.value;
let value = prop.value !== undefined ? prop.value : prop;
// Type is specified in grip's "class" field and for primitive
// values use typeof.

View File

@ -96,7 +96,8 @@ define(function (require, exports, module) {
return props;
}
let mode = this.props.mode;
// Hardcode tiny mode to avoid recursive handling.
let mode = "tiny";
try {
for (let name in object) {

View File

@ -35,8 +35,11 @@ define(function (require, exports, module) {
let croppedString = this.props.cropLimit ?
cropMultipleLines(text, this.props.cropLimit) : cropMultipleLines(text);
let formattedString = this.props.omitQuotes ?
croppedString : "\"" + croppedString + "\"";
return (
span({className: "objectBox objectBox-string"}, "\"" + croppedString + "\""
span({className: "objectBox objectBox-string"}, formattedString
)
);
},

View File

@ -35,7 +35,6 @@
height: calc(100% - 24px);
}
.tabs .tab-panel-box,
.tabs .tab-panel {
height: 100%;
}

View File

@ -161,7 +161,7 @@ window.onload = Task.async(function* () {
function testRecursiveArray() {
let stub = [1];
stub.push(stub);
const defaultOutput = `[ 1, [] ]`;
const defaultOutput = `[ 1, [2] ]`;
const modeTests = [
{
@ -194,7 +194,7 @@ window.onload = Task.async(function* () {
p4: "s4"
}
];
const defaultOutput = `[ Object { p1: "s1", p3: "s3", p4: "s4", 1 more… } ]`;
const defaultOutput = `[ Object ]`;
const modeTests = [
{

View File

@ -35,22 +35,30 @@ window.onload = Task.async(function* () {
function testEvent() {
const renderedComponent = renderComponent(Event.rep, { object: getGripStub("testEvent") });
is(renderedComponent.textContent, "beforeprint", "Event rep has expected text content for an event");
is(renderedComponent.textContent,
"Event { isTrusted: true, eventPhase: 2, bubbles: false, 7 more… }",
"Event rep has expected text content for an event");
}
function testMouseEvent() {
const renderedComponent = renderComponent(Event.rep, { object: getGripStub("testMouseEvent") });
is(renderedComponent.textContent, "clickclientX=62, clientY=18", "Event rep has expected text content for a mouse event");
is(renderedComponent.textContent,
"MouseEvent { buttons: 0, clientX: 62, clientY: 18, 2 more… }",
"Event rep has expected text content for a mouse event");
}
function testKeyboardEvent() {
const renderedComponent = renderComponent(Event.rep, { object: getGripStub("testKeyboardEvent") });
is(renderedComponent.textContent, "keyupcharCode=0, keyCode=17", "Event rep has expected text content for a keyboard event");
is(renderedComponent.textContent,
"KeyboardEvent { key: \"Control\", charCode: 0, keyCode: 17 }",
"Event rep has expected text content for a keyboard event");
}
function testMessageEvent() {
const renderedComponent = renderComponent(Event.rep, { object: getGripStub("testMessageEvent") });
is(renderedComponent.textContent, "messageorigin=null, data=test data", "Event rep has expected text content for a message event");
is(renderedComponent.textContent,
"MessageEvent { isTrusted: false, data: \"test data\", origin: \"null\", 7 more… }",
"Event rep has expected text content for a message event");
}
function getGripStub(name) {

View File

@ -32,6 +32,7 @@ window.onload = Task.async(function* () {
yield testMoreThanShortMaxProps();
yield testMoreThanLongMaxProps();
yield testRecursiveArray();
yield testPreviewLimit();
yield testNamedNodeMap();
} catch(e) {
@ -190,6 +191,34 @@ window.onload = Task.async(function* () {
testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
}
function testPreviewLimit() {
const testName = "testPreviewLimit";
const shortOutput = `Array[ 0, 1, 2, 8 more… ]`;
const defaultOutput = `Array[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1 more… ]`;
const modeTests = [
{
mode: undefined,
expectedOutput: shortOutput,
},
{
mode: "tiny",
expectedOutput: `[11]`,
},
{
mode: "short",
expectedOutput: shortOutput,
},
{
mode: "long",
expectedOutput: defaultOutput,
}
];
testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
}
function testNamedNodeMap() {
const testName = "testNamedNodeMap";
@ -311,6 +340,22 @@ window.onload = Task.async(function* () {
return longArrayGrip;
case "testPreviewLimit":
return {
"type": "object",
"class": "Array",
"actor": "server1.conn1.obj31",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 12,
"preview": {
"kind": "ArrayLike",
"length": 11,
"items": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
}
};
case "testRecursiveArray":
return {
"type": "object",

View File

@ -160,7 +160,7 @@ window.onload = Task.async(function* () {
strProp: "test string",
arrProp: [1]
};
const defaultOutput = `Object { strProp: "test string", objProp: Object { id: 1, arr: [ 2 ] }, arrProp: [ 1 ] }`;
const defaultOutput = `Object { strProp: "test string", objProp: Object, arrProp: [1] }`;
const modeTests = [
{

View File

@ -27,6 +27,7 @@ window.onload = Task.async(function* () {
yield testMultiline();
yield testMultilineOpen();
yield testMultilineLimit();
yield testOmitQuotes();
} catch(e) {
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
} finally {
@ -48,10 +49,18 @@ window.onload = Task.async(function* () {
is(renderedComponent.textContent, "\"aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n\"", "String rep has expected text content for multiline string when open");
}
function testOmitQuotes(){
const renderedComponent = renderComponent(StringRep.rep, { object: getGripStub("testOmitQuotes"), omitQuotes: true });
is(renderedComponent.textContent, "abc","String rep has expected to omit quotes");
}
function getGripStub(name) {
switch (name) {
case "testMultiline":
return "aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n";
return "aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n";
break;
case "testOmitQuotes":
return "abc";
}
}
});

View File

@ -1257,7 +1257,7 @@ function Scope(aView, aName, aFlags = {}) {
this.contextMenuId = aView.contextMenuId;
this.separatorStr = aView.separatorStr;
this._init(aName.trim(), aFlags);
this._init(aName, aFlags);
}
Scope.prototype = {
@ -1822,7 +1822,7 @@ Scope.prototype = {
let name = this._name = document.createElement("label");
name.className = "plain name";
name.setAttribute("value", aName);
name.setAttribute("value", aName.trim());
name.setAttribute("crop", "end");
let title = this._title = document.createElement("hbox");

View File

@ -37,7 +37,6 @@
/* TODO: bug 1265759: should apply to .devtools-searchinput once all searchbox
is converted to html*/
#inspector-searchbox {
flex: 1;
width: 100%;
}

View File

@ -462,7 +462,7 @@
.devtools-searchinput-clear {
position: absolute;
top: 3.5px;
right: 7px;
offset-inline-end: 7px;
padding: 0;
border: 0;
width: 16px;

View File

@ -447,8 +447,6 @@ JSTerm.prototype = {
const { ConsoleCommand } = require("devtools/client/webconsole/new-console-output/types");
let message = new ConsoleCommand({
messageText: executeString,
source: "javascript",
type: "command",
// @TODO remove category and severity
category: "input",
severity: "log",

View File

@ -0,0 +1,39 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
FILTER_TEXT_SET,
FILTER_TOGGLE,
FILTERS_CLEAR
} = require("../constants");
function filterTextSet(text) {
return {
type: FILTER_TEXT_SET,
text
};
}
function filterToggle(filter) {
return {
type: FILTER_TOGGLE,
filter,
};
}
function filtersClear() {
return {
type: FILTERS_CLEAR
};
}
module.exports = {
filterTextSet,
filterToggle,
filtersClear
};

View File

@ -13,10 +13,7 @@ const { IdGenerator } = require("devtools/client/webconsole/new-console-output/u
const {
MESSAGE_ADD,
MESSAGES_CLEAR,
SEVERITY_FILTER,
MESSAGES_SEARCH,
FILTERS_CLEAR,
MESSAGES_CLEAR
} = require("../constants");
const defaultIdGenerator = new IdGenerator();
@ -39,29 +36,5 @@ function messagesClear() {
};
}
function severityFilter(filter, toggled) {
return {
type: SEVERITY_FILTER,
filter,
toggled
};
}
function filtersClear() {
return {
type: FILTERS_CLEAR
};
}
function messagesSearch(searchText) {
return {
type: MESSAGES_SEARCH,
searchText
};
}
exports.messageAdd = messageAdd;
exports.messagesClear = messagesClear;
exports.severityFilter = severityFilter;
exports.filtersClear = filtersClear;
exports.messagesSearch = messagesSearch;

View File

@ -4,6 +4,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'filters.js',
'messages.js',
'ui.js',
)

View File

@ -7,12 +7,12 @@
"use strict";
const {
FILTERBAR_TOGGLE,
FILTER_BAR_TOGGLE,
} = require("../constants");
function filterBarToggle(show) {
return {
type: FILTERBAR_TOGGLE
type: FILTER_BAR_TOGGLE
};
}

View File

@ -12,12 +12,13 @@ const {
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
const messagesActions = require("devtools/client/webconsole/new-console-output/actions/messages");
const { filterTextSet, filtersClear } = require("devtools/client/webconsole/new-console-output/actions/filters");
const { messagesClear } = require("devtools/client/webconsole/new-console-output/actions/messages");
const uiActions = require("devtools/client/webconsole/new-console-output/actions/ui");
const {
SEVERITY_FILTER
MESSAGE_LEVEL
} = require("../constants");
const FilterToggleButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-toggle-button").FilterToggleButton);
const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button").FilterButton);
const FilterBar = createClass({
@ -28,77 +29,73 @@ const FilterBar = createClass({
ui: PropTypes.object.isRequired
},
onClearOutputButtonClick: function () {
this.props.dispatch(messagesActions.messagesClear());
onClickMessagesClear: function () {
this.props.dispatch(messagesClear());
},
onToggleFilterConfigBarButtonClick: function () {
onClickFilterBarToggle: function () {
this.props.dispatch(uiActions.filterBarToggle());
},
onClearFiltersButtonClick: function () {
this.props.dispatch(messagesActions.filtersClear());
onClickFiltersClear: function () {
this.props.dispatch(filtersClear());
},
onSearchInput: function (e) {
this.props.dispatch(messagesActions.messagesSearch(e.target.value));
this.props.dispatch(filterTextSet(e.target.value));
},
render() {
const {dispatch, filter, ui} = this.props;
let configFilterBarVisible = ui.configFilterBarVisible;
let filterBarVisible = ui.filterBarVisible;
let children = [];
children.push(dom.div({className: "devtools-toolbar webconsole-filterbar-primary"},
dom.button({
className: "devtools-button devtools-clear-icon",
title: "Clear output",
onClick: this.onClearOutputButtonClick
onClick: this.onClickMessagesClear
}),
dom.button({
className: "devtools-button devtools-filter-icon" + (
configFilterBarVisible ? " checked" : ""),
filterBarVisible ? " checked" : ""),
title: "Toggle filter bar",
onClick: this.onToggleFilterConfigBarButtonClick
onClick: this.onClickFilterBarToggle
}),
dom.input({
className: "devtools-plaininput",
type: "search",
value: filter.searchText,
value: filter.text,
placeholder: "Filter output",
onInput: this.onSearchInput
})
));
if (configFilterBarVisible) {
if (filterBarVisible) {
children.push(
dom.div({className: "devtools-toolbar"},
FilterToggleButton({
FilterButton({
active: filter.error,
label: "Errors",
filterType: SEVERITY_FILTER,
filterKey: "error",
filterKey: MESSAGE_LEVEL.ERROR,
dispatch
}),
FilterToggleButton({
FilterButton({
active: filter.warn,
label: "Warnings",
filterType: SEVERITY_FILTER,
filterKey: "warn",
filterKey: MESSAGE_LEVEL.WARN,
dispatch
}),
FilterToggleButton({
FilterButton({
active: filter.log,
label: "Logs",
filterType: SEVERITY_FILTER,
filterKey: "log",
filterKey: MESSAGE_LEVEL.LOG,
dispatch
}),
FilterToggleButton({
FilterButton({
active: filter.info,
label: "Info",
filterType: SEVERITY_FILTER,
filterKey: "info",
filterKey: MESSAGE_LEVEL.INFO,
dispatch
})
)
@ -116,7 +113,7 @@ const FilterBar = createClass({
"."),
dom.button({
className: "menu-filter-button",
onClick: this.onClearFiltersButtonClick
onClick: this.onClickFiltersClear
}, "Remove filters")
)
);

View File

@ -8,28 +8,21 @@ const {
DOM: dom,
PropTypes
} = require("devtools/client/shared/vendor/react");
const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
const {
SEVERITY_FILTER
} = require("../constants");
const actions = require("devtools/client/webconsole/new-console-output/actions/filters");
const FilterToggleButton = createClass({
const FilterButton = createClass({
displayName: "FilterToggleButton",
displayName: "FilterButton",
propTypes: {
label: PropTypes.string.isRequired,
filterType: PropTypes.string.isRequired,
filterKey: PropTypes.string.isRequired,
active: PropTypes.bool.isRequired,
dispatch: PropTypes.func.isRequired,
},
onClick: function () {
if (this.props.filterType === SEVERITY_FILTER) {
this.props.dispatch(actions.severityFilter(
this.props.filterKey, !this.props.active));
}
this.props.dispatch(actions.filterToggle(this.props.filterKey));
},
render() {
@ -47,4 +40,4 @@ const FilterToggleButton = createClass({
}
});
exports.FilterToggleButton = FilterToggleButton;
exports.FilterButton = FilterButton;

View File

@ -10,7 +10,7 @@ DIRS += [
DevToolsModules(
'console-output.js',
'filter-bar.js',
'filter-toggle-button.js',
'filter-button.js',
'grip-message-body.js',
'message-container.js',
'message-icon.js',

View File

@ -8,10 +8,10 @@
const actionTypes = {
MESSAGE_ADD: "MESSAGE_ADD",
MESSAGES_CLEAR: "MESSAGES_CLEAR",
SEVERITY_FILTER: "SEVERITY_FILTER",
MESSAGES_SEARCH: "MESSAGES_SEARCH",
FILTER_TOGGLE: "FILTER_TOGGLE",
FILTER_TEXT_SET: "FILTER_TEXT_SET",
FILTERS_CLEAR: "FILTERS_CLEAR",
FILTERBAR_TOGGLE: "FILTERBAR_TOGGLE",
FILTER_BAR_TOGGLE: "FILTER_BAR_TOGGLE",
};
const categories = {
@ -97,7 +97,7 @@ const chromeRDPEnums = {
};
const filterTypes = {
SEVERITY_FILTER: "SEVERITY_FILTER"
FILTER_TOGGLE: "FILTER_TOGGLE"
};
// Combine into a single constants object

View File

@ -13,19 +13,20 @@ const FilterState = Immutable.Record({
warn: true,
info: true,
log: true,
searchText: ""
text: ""
});
function filters(state = new FilterState(), action) {
switch (action.type) {
case constants.SEVERITY_FILTER:
let {filter, toggled} = action;
return state.set(filter, toggled);
case constants.FILTER_TOGGLE:
const {filter} = action;
const active = !state.get(filter);
return state.set(filter, active);
case constants.FILTERS_CLEAR:
return new FilterState();
case constants.MESSAGES_SEARCH:
let {searchText} = action;
return state.set("searchText", searchText);
case constants.FILTER_TEXT_SET:
let {text} = action;
return state.set("text", text);
}
return state;

View File

@ -9,14 +9,14 @@ const constants = require("devtools/client/webconsole/new-console-output/constan
const Immutable = require("devtools/client/shared/vendor/immutable");
const Ui = Immutable.Record({
configFilterBarVisible: false,
filterBarVisible: false,
filteredMessageVisible: false
});
function ui(state = new Ui(), action) {
switch (action.type) {
case constants.FILTERBAR_TOGGLE:
return state.set("configFilterBarVisible", !state.configFilterBarVisible);
case constants.FILTER_BAR_TOGGLE:
return state.set("filterBarVisible", !state.filterBarVisible);
}
return state;

View File

@ -16,18 +16,18 @@ function getAllMessages(state) {
return prune(
search(
filterSeverity(messages, filters),
filters.searchText
filters.text
),
logLimit
);
}
function filterSeverity(messages, filters) {
return messages.filter((message) => filters[message.severity] === true);
return messages.filter((message) => filters[message.level] === true);
}
function search(messages, searchText = "") {
if (searchText === "") {
function search(messages, text = "") {
if (text === "") {
return messages;
}
@ -39,7 +39,7 @@ function search(messages, searchText = "") {
return message
.parameters.join("")
.toLocaleLowerCase()
.includes(searchText.toLocaleLowerCase());
.includes(text.toLocaleLowerCase());
});
}

View File

@ -22,7 +22,6 @@ function configureStore(Services) {
warn: Services.prefs.getBoolPref("devtools.webconsole.filter.warn"),
info: Services.prefs.getBoolPref("devtools.webconsole.filter.info"),
log: Services.prefs.getBoolPref("devtools.webconsole.filter.log"),
searchText: ""
})
};

View File

@ -0,0 +1,54 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const actions = require("devtools/client/webconsole/new-console-output/actions/filters");
const {
FILTER_TEXT_SET,
FILTER_TOGGLE,
FILTERS_CLEAR,
MESSAGE_LEVEL
} = require("devtools/client/webconsole/new-console-output/constants");
const expect = require("expect");
describe("Filter actions:", () => {
describe("filterTextSet", () => {
it("creates expected action", () => {
const action = actions.filterTextSet("test");
const expected = {
type: FILTER_TEXT_SET,
text: "test"
};
expect(action).toEqual(expected);
});
});
describe("filterToggle", () => {
it("creates expected action", () => {
const action = actions.filterToggle(MESSAGE_LEVEL.ERROR);
const expected = {
type: FILTER_TOGGLE,
filter: "error"
};
expect(action).toEqual(expected);
});
});
describe("filterTextApply", () => {
});
describe("filtersClear", () => {
it("creates expected action", () => {
const action = actions.filtersClear();
const expected = {
type: FILTERS_CLEAR
};
expect(action).toEqual(expected);
});
});
});

View File

@ -0,0 +1,23 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const actions = require("devtools/client/webconsole/new-console-output/actions/ui");
const {
FILTER_BAR_TOGGLE
} = require("devtools/client/webconsole/new-console-output/constants");
const expect = require("expect");
describe("UI actions:", () => {
describe("filterBarToggle", () => {
it("creates expected action", () => {
const action = actions.filterBarToggle();
const expected = {
type: FILTER_BAR_TOGGLE
};
expect(action).toEqual(expected);
});
});
});

View File

@ -58,7 +58,7 @@ function timeit(cb) {
window.onload = Task.async(function* () {
const { configureStore } = browserRequire("devtools/client/webconsole/new-console-output/store");
const { messagesSearch, filtersClear } = browserRequire("devtools/client/webconsole/new-console-output/actions/messages");
const { filterTextSet, filtersClear } = browserRequire("devtools/client/webconsole/new-console-output/actions/filters");
const NewConsoleOutputWrapper = browserRequire("devtools/client/webconsole/new-console-output/new-console-output-wrapper");
const wrapper = new NewConsoleOutputWrapper(document.querySelector("#output"), {});
@ -72,7 +72,7 @@ window.onload = Task.async(function* () {
info("took " + time + " seconds to render messages");
time = yield timeit(() => {
store.dispatch(messagesSearch("Odd text"));
store.dispatch(filterTextSet("Odd text"));
});
info("took " + time + " seconds to search filter half the messages");

View File

@ -4,7 +4,6 @@
const { stubConsoleMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs");
const { ConsoleApiCall } = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call");
const jsdom = require("mocha-jsdom");
const expect = require("expect");
const {
@ -12,7 +11,6 @@ const {
} = require("devtools/client/webconsole/new-console-output/test/helpers");
describe("ConsoleAPICall component:", () => {
jsdom();
describe("console.log", () => {
it("renders string grips", () => {
const message = stubConsoleMessages.get("console.log('foobar', 'test')");

View File

@ -5,7 +5,6 @@
const { stubConsoleMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs");
const { EvaluationResult } = require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result");
const jsdom = require("mocha-jsdom");
const expect = require("expect");
const {
@ -13,7 +12,6 @@ const {
} = require("devtools/client/webconsole/new-console-output/test/helpers");
describe("EvaluationResult component:", () => {
jsdom();
it("renders a grip result", () => {
const message = stubConsoleMessages.get("new Date(0)");
const props = {

View File

@ -0,0 +1,96 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Require helper is necessary to load certain modules.
require("devtools/client/webconsole/new-console-output/test/requireHelper")();
const { render, mount } = require("enzyme");
const { createFactory } = require("devtools/client/shared/vendor/react");
const Provider = createFactory(require("react-redux").Provider);
const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button").FilterButton);
const FilterBar = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar"));
const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
const {
MESSAGES_CLEAR,
MESSAGE_LEVEL
} = require("devtools/client/webconsole/new-console-output/constants");
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
const expect = require("expect");
const sinon = require("sinon");
describe("FilterBar component:", () => {
it("initial render", () => {
const store = setupStore([]);
const wrapper = render(Provider({store}, FilterBar({})));
const toolbar = wrapper.find(
".devtools-toolbar.webconsole-filterbar-primary"
);
// Clear button
expect(toolbar.children().eq(0).attr("class"))
.toBe("devtools-button devtools-clear-icon");
expect(toolbar.children().eq(0).attr("title")).toBe("Clear output");
// Filter bar toggle
expect(toolbar.children().eq(1).attr("class"))
.toBe("devtools-button devtools-filter-icon");
expect(toolbar.children().eq(1).attr("title")).toBe("Toggle filter bar");
// Text filter
expect(toolbar.children().eq(2).attr("class")).toBe("devtools-plaininput");
expect(toolbar.children().eq(2).attr("placeholder")).toBe("Filter output");
expect(toolbar.children().eq(2).attr("type")).toBe("search");
expect(toolbar.children().eq(2).attr("value")).toBe("");
});
it("displays filter bar when button is clicked", () => {
const store = setupStore([]);
expect(getAllUi(store.getState()).filterBarVisible).toBe(false);
const wrapper = mount(Provider({store}, FilterBar({})));
wrapper.find(".devtools-filter-icon").simulate("click");
expect(getAllUi(store.getState()).filterBarVisible).toBe(true);
// Buttons are displayed
const buttonProps = {
active: true,
dispatch: store.dispatch
};
const logButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Logs", filterKey: MESSAGE_LEVEL.LOG }));
const infoButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Info", filterKey: MESSAGE_LEVEL.INFO }));
const warnButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Warnings", filterKey: MESSAGE_LEVEL.WARN }));
const errorButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Errors", filterKey: MESSAGE_LEVEL.ERROR }));
expect(wrapper.contains([errorButton, warnButton, logButton, infoButton])).toBe(true);
});
it("fires MESSAGES_CLEAR action when clear button is clicked", () => {
const store = setupStore([]);
store.dispatch = sinon.spy();
const wrapper = mount(Provider({store}, FilterBar({})));
wrapper.find(".devtools-clear-icon").simulate("click");
const call = store.dispatch.getCall(0);
expect(call.args[0]).toEqual({
type: MESSAGES_CLEAR
});
});
it("sets filter text when text is typed", () => {
const store = setupStore([]);
const wrapper = mount(Provider({store}, FilterBar({})));
wrapper.find(".devtools-plaininput").simulate("input", { target: { value: "a" } });
expect(store.getState().filters.text).toBe("a");
});
});

View File

@ -0,0 +1,52 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Require helper is necessary to load certain modules.
require("devtools/client/webconsole/new-console-output/test/requireHelper")();
const { render, shallow } = require("enzyme");
const { createFactory } = require("devtools/client/shared/vendor/react");
const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button").FilterButton);
const {
FILTER_TOGGLE,
MESSAGE_LEVEL
} = require("devtools/client/webconsole/new-console-output/constants");
const expect = require("expect");
const sinon = require("sinon");
describe("FilterButton component:", () => {
const props = {
active: true,
label: "Error",
filterKey: MESSAGE_LEVEL.ERROR,
dispatch: sinon.spy()
};
it("displays as active when turned on", () => {
const wrapper = render(FilterButton(props));
expect(wrapper.html()).toBe(
"<button class=\"menu-filter-button checked\">Error</button>"
);
});
it("displays as inactive when turned off", () => {
const inactiveProps = Object.assign({}, props, { active: false });
const wrapper = render(FilterButton(inactiveProps));
expect(wrapper.html()).toBe(
"<button class=\"menu-filter-button\">Error</button>"
);
});
it("fires FILTER_TOGGLE action when clicked", () => {
const wrapper = shallow(FilterButton(props));
wrapper.find("button").simulate("click");
const call = props.dispatch.getCall(0);
expect(call.args[0]).toEqual({
type: FILTER_TOGGLE,
filter: MESSAGE_LEVEL.ERROR
});
});
});

View File

@ -9,7 +9,6 @@ const { ConsoleApiCall } = require("devtools/client/webconsole/new-console-outpu
const { EvaluationResult } = require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result");
const { PageError } = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
const jsdom = require("mocha-jsdom");
const expect = require("expect");
const {
@ -18,7 +17,6 @@ const {
} = require("devtools/client/webconsole/new-console-output/test/helpers");
describe("MessageContainer component:", () => {
jsdom();
it("pipes data to children as expected", () => {
const message = stubConsoleMessages.get("console.log('foobar', 'test')");
const rendered = renderComponent(MessageContainer, {message});

View File

@ -7,7 +7,6 @@ const {
} = require("devtools/client/webconsole/new-console-output/constants");
const { MessageIcon } = require("devtools/client/webconsole/new-console-output/components/message-icon");
const jsdom = require("mocha-jsdom");
const expect = require("expect");
const {
@ -15,8 +14,6 @@ const {
} = require("devtools/client/webconsole/new-console-output/test/helpers");
describe("MessageIcon component:", () => {
jsdom();
it("renders icon based on severity", () => {
const rendered = renderComponent(MessageIcon, { severity: SEVERITY_ERROR });

View File

@ -6,7 +6,6 @@ const { stubConsoleMessages } = require("devtools/client/webconsole/new-console-
const { PageError } = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
const jsdom = require("mocha-jsdom");
const expect = require("expect");
const {
@ -14,7 +13,6 @@ const {
} = require("devtools/client/webconsole/new-console-output/test/helpers");
describe("PageError component:", () => {
jsdom();
it("renders a page error", () => {
const message = stubConsoleMessages.get("ReferenceError");
const rendered = renderComponent(PageError, {message});

View File

@ -4,7 +4,6 @@
const { MessageRepeat } = require("devtools/client/webconsole/new-console-output/components/message-repeat");
const jsdom = require("mocha-jsdom");
const expect = require("expect");
const {
@ -12,8 +11,6 @@ const {
} = require("devtools/client/webconsole/new-console-output/test/helpers");
describe("MessageRepeat component:", () => {
jsdom();
it("renders repeated value correctly", () => {
const rendered = renderComponent(MessageRepeat, { repeat: 99 });
expect(rendered.classList.contains("message-repeats")).toBe(true);

View File

@ -3,6 +3,8 @@
"use strict";
require("devtools/client/webconsole/new-console-output/test/requireHelper")();
let ReactDOM = require("devtools/client/shared/vendor/react-dom");
let React = require("devtools/client/shared/vendor/react");
var TestUtils = React.addons.TestUtils;

View File

@ -0,0 +1,26 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const requireHacker = require("require-hacker");
module.exports = () => {
try {
requireHacker.global_hook("default", path => {
switch (path) {
case "react-dom/server":
return `const React = require('react-dev'); module.exports = React`;
case "react-addons-test-utils":
return `const React = require('react-dev'); module.exports = React.addons.TestUtils`;
case "react":
return `const React = require('react-dev'); module.exports = React`;
case "devtools/client/shared/vendor/react":
return `const React = require('react-dev'); module.exports = React`;
case "devtools/client/shared/vendor/react.default":
return `const React = require('react-dev'); module.exports = React`;
}
});
} catch (e) {
// Do nothing. This means the hook is already registered.
}
};

View File

@ -5,20 +5,98 @@
const expect = require("expect");
const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
const actions = require("devtools/client/webconsole/new-console-output/actions/filters");
const { messageAdd } = require("devtools/client/webconsole/new-console-output/actions/messages");
const { ConsoleCommand } = require("devtools/client/webconsole/new-console-output/types");
const { getAllMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
describe("Search", () => {
it("matches on value grips", () => {
const store = setupStore([
"console.log('foobar', 'test')",
"console.warn('danger, will robinson!')",
"console.log(undefined)"
]);
store.dispatch(actions.messagesSearch("danger"));
describe("Filtering", () => {
const numMessages = 5;
const store = setupStore([
"console.log('foobar', 'test')",
"console.warn('danger, will robinson!')",
"console.log(undefined)",
"ReferenceError"
]);
// Add a console command as well
store.dispatch(messageAdd(new ConsoleCommand({ messageText: `console.warn("x")` })));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(1);
beforeEach(() => {
store.dispatch(actions.filtersClear());
});
describe("Severity filter", () => {
it("filters log messages", () => {
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.LOG));
let messages = getAllMessages(store.getState());
// @TODO It currently filters console command. This should be -2, not -3.
expect(messages.size).toEqual(numMessages - 3);
});
// @TODO add info stub
it("filters info messages");
it("filters warning messages", () => {
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.WARN));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages - 1);
});
it("filters error messages", () => {
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages - 1);
});
});
describe("Text filter", () => {
it("matches on value grips", () => {
store.dispatch(actions.filterTextSet("danger"));
let messages = getAllMessages(store.getState());
// @TODO figure out what this should filter
// This does not filter out PageErrors or console commands
expect(messages.size).toEqual(3);
});
});
describe("Combined filters", () => {
// @TODO add test
it("filters");
});
});
describe("Clear filters", () => {
it("clears all filters", () => {
const store = setupStore([]);
// Setup test case
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
store.dispatch(actions.filterTextSet("foobar"));
let filters = getAllFilters(store.getState());
expect(filters.toJS()).toEqual({
"error": false,
"info": true,
"log": true,
"warn": true,
"text": "foobar"
});
store.dispatch(actions.filtersClear());
filters = getAllFilters(store.getState());
expect(filters.toJS()).toEqual({
"error": true,
"info": true,
"log": true,
"warn": true,
"text": ""
});
});
});

View File

@ -7,14 +7,21 @@
const Immutable = require("devtools/client/shared/vendor/immutable");
const {
MESSAGE_SOURCE,
MESSAGE_TYPE,
MESSAGE_LEVEL
} = require("devtools/client/webconsole/new-console-output/constants");
exports.ConsoleCommand = Immutable.Record({
id: null,
allowRepeating: false,
messageText: null,
source: null,
type: null,
category: null,
severity: null,
source: MESSAGE_SOURCE.JAVASCRIPT,
type: MESSAGE_TYPE.COMMAND,
level: MESSAGE_LEVEL.LOG,
category: "input",
severity: MESSAGE_TYPE.LOG,
});
exports.ConsoleMessage = Immutable.Record({

View File

@ -5,12 +5,15 @@
"amd-loader": "0.0.5",
"babel-preset-es2015": "^6.6.0",
"babel-register": "^6.7.2",
"enzyme": "^2.4.1",
"expect": "^1.16.0",
"jsdom": "^9.4.1",
"jsdom-global": "^2.0.0",
"mocha": "^2.5.3",
"mocha-jsdom": "^1.1.0"
"require-hacker": "^2.1.4",
"sinon": "^1.17.5"
},
"scripts": {
"test": "NODE_PATH=`pwd`/../../../ mocha new-console-output/test/**/*.test.js --compilers js:babel-register"
"test": "NODE_PATH=`pwd`/../../../:`pwd`/../../../devtools/client/shared/vendor/ mocha new-console-output/test/**/*.test.js --compilers js:babel-register -r jsdom-global/register"
}
}

View File

@ -567,125 +567,119 @@ exports.createEmptyNodeList = function(doc) {
* See BUG 664991: GCLI's keyboard handling should be updated to use DOM-L3
* https://bugzilla.mozilla.org/show_bug.cgi?id=664991
*/
if (typeof 'KeyEvent' === 'undefined') {
/* jshint -W040 */
exports.KeyEvent = this.KeyEvent;
}
else {
exports.KeyEvent = {
DOM_VK_CANCEL: 3,
DOM_VK_HELP: 6,
DOM_VK_BACK_SPACE: 8,
DOM_VK_TAB: 9,
DOM_VK_CLEAR: 12,
DOM_VK_RETURN: 13,
DOM_VK_SHIFT: 16,
DOM_VK_CONTROL: 17,
DOM_VK_ALT: 18,
DOM_VK_PAUSE: 19,
DOM_VK_CAPS_LOCK: 20,
DOM_VK_ESCAPE: 27,
DOM_VK_SPACE: 32,
DOM_VK_PAGE_UP: 33,
DOM_VK_PAGE_DOWN: 34,
DOM_VK_END: 35,
DOM_VK_HOME: 36,
DOM_VK_LEFT: 37,
DOM_VK_UP: 38,
DOM_VK_RIGHT: 39,
DOM_VK_DOWN: 40,
DOM_VK_PRINTSCREEN: 44,
DOM_VK_INSERT: 45,
DOM_VK_DELETE: 46,
DOM_VK_0: 48,
DOM_VK_1: 49,
DOM_VK_2: 50,
DOM_VK_3: 51,
DOM_VK_4: 52,
DOM_VK_5: 53,
DOM_VK_6: 54,
DOM_VK_7: 55,
DOM_VK_8: 56,
DOM_VK_9: 57,
DOM_VK_SEMICOLON: 59,
DOM_VK_EQUALS: 61,
DOM_VK_A: 65,
DOM_VK_B: 66,
DOM_VK_C: 67,
DOM_VK_D: 68,
DOM_VK_E: 69,
DOM_VK_F: 70,
DOM_VK_G: 71,
DOM_VK_H: 72,
DOM_VK_I: 73,
DOM_VK_J: 74,
DOM_VK_K: 75,
DOM_VK_L: 76,
DOM_VK_M: 77,
DOM_VK_N: 78,
DOM_VK_O: 79,
DOM_VK_P: 80,
DOM_VK_Q: 81,
DOM_VK_R: 82,
DOM_VK_S: 83,
DOM_VK_T: 84,
DOM_VK_U: 85,
DOM_VK_V: 86,
DOM_VK_W: 87,
DOM_VK_X: 88,
DOM_VK_Y: 89,
DOM_VK_Z: 90,
DOM_VK_CONTEXT_MENU: 93,
DOM_VK_NUMPAD0: 96,
DOM_VK_NUMPAD1: 97,
DOM_VK_NUMPAD2: 98,
DOM_VK_NUMPAD3: 99,
DOM_VK_NUMPAD4: 100,
DOM_VK_NUMPAD5: 101,
DOM_VK_NUMPAD6: 102,
DOM_VK_NUMPAD7: 103,
DOM_VK_NUMPAD8: 104,
DOM_VK_NUMPAD9: 105,
DOM_VK_MULTIPLY: 106,
DOM_VK_ADD: 107,
DOM_VK_SEPARATOR: 108,
DOM_VK_SUBTRACT: 109,
DOM_VK_DECIMAL: 110,
DOM_VK_DIVIDE: 111,
DOM_VK_F1: 112,
DOM_VK_F2: 113,
DOM_VK_F3: 114,
DOM_VK_F4: 115,
DOM_VK_F5: 116,
DOM_VK_F6: 117,
DOM_VK_F7: 118,
DOM_VK_F8: 119,
DOM_VK_F9: 120,
DOM_VK_F10: 121,
DOM_VK_F11: 122,
DOM_VK_F12: 123,
DOM_VK_F13: 124,
DOM_VK_F14: 125,
DOM_VK_F15: 126,
DOM_VK_F16: 127,
DOM_VK_F17: 128,
DOM_VK_F18: 129,
DOM_VK_F19: 130,
DOM_VK_F20: 131,
DOM_VK_F21: 132,
DOM_VK_F22: 133,
DOM_VK_F23: 134,
DOM_VK_F24: 135,
DOM_VK_NUM_LOCK: 144,
DOM_VK_SCROLL_LOCK: 145,
DOM_VK_COMMA: 188,
DOM_VK_PERIOD: 190,
DOM_VK_SLASH: 191,
DOM_VK_BACK_QUOTE: 192,
DOM_VK_OPEN_BRACKET: 219,
DOM_VK_BACK_SLASH: 220,
DOM_VK_CLOSE_BRACKET: 221,
DOM_VK_QUOTE: 222,
DOM_VK_META: 224
};
}
exports.KeyEvent = {
DOM_VK_CANCEL: 3,
DOM_VK_HELP: 6,
DOM_VK_BACK_SPACE: 8,
DOM_VK_TAB: 9,
DOM_VK_CLEAR: 12,
DOM_VK_RETURN: 13,
DOM_VK_SHIFT: 16,
DOM_VK_CONTROL: 17,
DOM_VK_ALT: 18,
DOM_VK_PAUSE: 19,
DOM_VK_CAPS_LOCK: 20,
DOM_VK_ESCAPE: 27,
DOM_VK_SPACE: 32,
DOM_VK_PAGE_UP: 33,
DOM_VK_PAGE_DOWN: 34,
DOM_VK_END: 35,
DOM_VK_HOME: 36,
DOM_VK_LEFT: 37,
DOM_VK_UP: 38,
DOM_VK_RIGHT: 39,
DOM_VK_DOWN: 40,
DOM_VK_PRINTSCREEN: 44,
DOM_VK_INSERT: 45,
DOM_VK_DELETE: 46,
DOM_VK_0: 48,
DOM_VK_1: 49,
DOM_VK_2: 50,
DOM_VK_3: 51,
DOM_VK_4: 52,
DOM_VK_5: 53,
DOM_VK_6: 54,
DOM_VK_7: 55,
DOM_VK_8: 56,
DOM_VK_9: 57,
DOM_VK_SEMICOLON: 59,
DOM_VK_EQUALS: 61,
DOM_VK_A: 65,
DOM_VK_B: 66,
DOM_VK_C: 67,
DOM_VK_D: 68,
DOM_VK_E: 69,
DOM_VK_F: 70,
DOM_VK_G: 71,
DOM_VK_H: 72,
DOM_VK_I: 73,
DOM_VK_J: 74,
DOM_VK_K: 75,
DOM_VK_L: 76,
DOM_VK_M: 77,
DOM_VK_N: 78,
DOM_VK_O: 79,
DOM_VK_P: 80,
DOM_VK_Q: 81,
DOM_VK_R: 82,
DOM_VK_S: 83,
DOM_VK_T: 84,
DOM_VK_U: 85,
DOM_VK_V: 86,
DOM_VK_W: 87,
DOM_VK_X: 88,
DOM_VK_Y: 89,
DOM_VK_Z: 90,
DOM_VK_CONTEXT_MENU: 93,
DOM_VK_NUMPAD0: 96,
DOM_VK_NUMPAD1: 97,
DOM_VK_NUMPAD2: 98,
DOM_VK_NUMPAD3: 99,
DOM_VK_NUMPAD4: 100,
DOM_VK_NUMPAD5: 101,
DOM_VK_NUMPAD6: 102,
DOM_VK_NUMPAD7: 103,
DOM_VK_NUMPAD8: 104,
DOM_VK_NUMPAD9: 105,
DOM_VK_MULTIPLY: 106,
DOM_VK_ADD: 107,
DOM_VK_SEPARATOR: 108,
DOM_VK_SUBTRACT: 109,
DOM_VK_DECIMAL: 110,
DOM_VK_DIVIDE: 111,
DOM_VK_F1: 112,
DOM_VK_F2: 113,
DOM_VK_F3: 114,
DOM_VK_F4: 115,
DOM_VK_F5: 116,
DOM_VK_F6: 117,
DOM_VK_F7: 118,
DOM_VK_F8: 119,
DOM_VK_F9: 120,
DOM_VK_F10: 121,
DOM_VK_F11: 122,
DOM_VK_F12: 123,
DOM_VK_F13: 124,
DOM_VK_F14: 125,
DOM_VK_F15: 126,
DOM_VK_F16: 127,
DOM_VK_F17: 128,
DOM_VK_F18: 129,
DOM_VK_F19: 130,
DOM_VK_F20: 131,
DOM_VK_F21: 132,
DOM_VK_F22: 133,
DOM_VK_F23: 134,
DOM_VK_F24: 135,
DOM_VK_NUM_LOCK: 144,
DOM_VK_SCROLL_LOCK: 145,
DOM_VK_COMMA: 188,
DOM_VK_PERIOD: 190,
DOM_VK_SLASH: 191,
DOM_VK_BACK_QUOTE: 192,
DOM_VK_OPEN_BRACKET: 219,
DOM_VK_BACK_SLASH: 220,
DOM_VK_CLOSE_BRACKET: 221,
DOM_VK_QUOTE: 222,
DOM_VK_META: 224
};

View File

@ -56,6 +56,14 @@ AnimValuesStyleRule::MightMapInheritedStyleData()
return mStyleBits & NS_STYLE_INHERITED_STRUCT_MASK;
}
bool
AnimValuesStyleRule::GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
nsCSSValue* aValue)
{
MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
return false;
}
#ifdef DEBUG
void
AnimValuesStyleRule::List(FILE* out, int32_t aIndent) const

View File

@ -32,6 +32,8 @@ public:
// nsIStyleRule implementation
void MapRuleInfoInto(nsRuleData* aRuleData) override;
bool MightMapInheritedStyleData() override;
bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
nsCSSValue* aValue) override;
#ifdef DEBUG
void List(FILE* out = stdout, int32_t aIndent = 0) const override;
#endif

View File

@ -62,4 +62,19 @@ AnimationUtils::IsOffscreenThrottlingEnabled()
return sOffscreenThrottlingEnabled;
}
/* static */ bool
AnimationUtils::IsCoreAPIEnabled()
{
static bool sCoreAPIEnabled;
static bool sPrefCached = false;
if (!sPrefCached) {
sPrefCached = true;
Preferences::AddBoolVarCache(&sCoreAPIEnabled,
"dom.animations-api.core.enabled");
}
return sCoreAPIEnabled;
}
} // namespace mozilla

View File

@ -60,6 +60,13 @@ public:
*/
static bool
IsOffscreenThrottlingEnabled();
/**
* Returns true if the preference to enable the core Web Animations API is
* true.
*/
static bool
IsCoreAPIEnabled();
};
} // namespace mozilla

View File

@ -6,6 +6,7 @@
#include "mozilla/KeyframeEffectParams.h"
#include "mozilla/AnimationUtils.h"
#include "mozilla/KeyframeUtils.h"
#include "mozilla/RangedPtr.h"
#include "nsReadableUtils.h"
@ -111,6 +112,13 @@ KeyframeEffectParams::ParseSpacing(const nsAString& aSpacing,
{
aInvalidPacedProperty.Truncate();
// Ignore spacing if the core API is not enabled since it is not yet ready to
// ship.
if (!AnimationUtils::IsCoreAPIEnabled()) {
aSpacingMode = SpacingMode::distribute;
return;
}
// Parse spacing.
// distribute | paced({ident})
// https://w3c.github.io/web-animations/#dom-keyframeeffectreadonly-spacing

View File

@ -42,6 +42,7 @@ support-files =
mozilla/file_hide_and_show.html
mozilla/file_partial_keyframes.html
mozilla/file_transform_limits.html
mozilla/file_underlying-discrete-value.html
style/file_animation-seeking-with-current-time.html
style/file_animation-seeking-with-start-time.html
testcommon.js
@ -91,5 +92,6 @@ skip-if = (toolkit == 'gonk' && debug)
[mozilla/test_partial_keyframes.html]
[mozilla/test_set-easing.html]
[mozilla/test_transform_limits.html]
[mozilla/test_underlying-discrete-value.html]
[style/test_animation-seeking-with-current-time.html]
[style/test_animation-seeking-with-start-time.html]

View File

@ -0,0 +1,192 @@
<!doctype html>
<meta charset=utf-8>
<script src="../testcommon.js"></script>
<body>
<script>
"use strict";
// Tests that we correctly extract the underlying value when the animation
// type is 'discrete'.
const discreteTests = [
{
stylesheet: {
"@keyframes keyframes":
"from { align-content: flex-start; } to { align-content: flex-end; } "
},
expectedKeyframes: [
{ computedOffset: 0, alignContent: "flex-start" },
{ computedOffset: 1, alignContent: "flex-end" }
],
explanation: "Test for fully-specified keyframes"
},
{
stylesheet: {
"@keyframes keyframes": "from { align-content: flex-start; }"
},
// The value of 100% should be 'stretch',
// but we are not supporting underlying value.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1295401
expectedKeyframes: [
{ computedOffset: 0, alignContent: "flex-start" },
{ computedOffset: 1, alignContent: "unset" }
],
explanation: "Test for 0% keyframe only"
},
{
stylesheet: {
"@keyframes keyframes": "to { align-content: flex-end; }"
},
// The value of 0% should be 'stretch',
// but we are not supporting underlying value.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1295401
expectedKeyframes: [
{ computedOffset: 0, alignContent: "unset" },
{ computedOffset: 1, alignContent: "flex-end" }
],
explanation: "Test for 100% keyframe only"
},
{
stylesheet: {
"@keyframes keyframes": "50% { align-content: center; }",
"#target": "align-content: space-between;"
},
expectedKeyframes: [
{ computedOffset: 0, alignContent: "space-between" },
{ computedOffset: 0.5, alignContent: "center" },
{ computedOffset: 1, alignContent: "space-between" }
],
explanation: "Test for no 0%/100% keyframes " +
"and specified style on target element"
},
{
stylesheet: {
"@keyframes keyframes": "50% { align-content: center; }"
},
attributes: {
style: "align-content: space-between"
},
expectedKeyframes: [
{ computedOffset: 0, alignContent: "space-between" },
{ computedOffset: 0.5, alignContent: "center" },
{ computedOffset: 1, alignContent: "space-between" }
],
explanation: "Test for no 0%/100% keyframes " +
"and specified style on target element using style attribute"
},
{
stylesheet: {
"@keyframes keyframes": "50% { align-content: center; }",
"#target": "align-content: inherit;"
},
// The value of 0%/100% should be 'stretch',
// but we are not supporting underlying value.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1295401
expectedKeyframes: [
{ computedOffset: 0, alignContent: "inherit" },
{ computedOffset: 0.5, alignContent: "center" },
{ computedOffset: 1, alignContent: "inherit" }
],
explanation: "Test for no 0%/100% keyframes " +
"and 'inherit' specified on target element"
},
{
stylesheet: {
"@keyframes keyframes": "50% { align-content: center; }",
".target": "align-content: space-between;"
},
attributes: {
class: "target"
},
expectedKeyframes: [
{ computedOffset: 0, alignContent: "space-between" },
{ computedOffset: 0.5, alignContent: "center" },
{ computedOffset: 1, alignContent: "space-between" }
],
explanation: "Test for no 0%/100% keyframes " +
"and specified style on target element using class selector"
},
{
stylesheet: {
"@keyframes keyframes": "50% { align-content: center; }",
"div": "align-content: space-between;"
},
expectedKeyframes: [
{ computedOffset: 0, alignContent: "space-between" },
{ computedOffset: 0.5, alignContent: "center" },
{ computedOffset: 1, alignContent: "space-between" }
],
explanation: "Test for no 0%/100% keyframes " +
"and specified style on target element using type selector"
},
{
stylesheet: {
"@keyframes keyframes": "50% { align-content: center; }",
"div": "align-content: space-between;",
".target": "align-content: flex-start;",
"#target": "align-content: flex-end;"
},
attributes: {
class: "target"
},
expectedKeyframes: [
{ computedOffset: 0, alignContent: "flex-end" },
{ computedOffset: 0.5, alignContent: "center" },
{ computedOffset: 1, alignContent: "flex-end" }
],
explanation: "Test for no 0%/100% keyframes " +
"and specified style on target element " +
"using ID selector that overrides class selector"
},
{
stylesheet: {
"@keyframes keyframes": "50% { align-content: center; }",
"div": "align-content: space-between !important;",
".target": "align-content: flex-start;",
"#target": "align-content: flex-end;"
},
attributes: {
class: "target"
},
expectedKeyframes: [
{ computedOffset: 0, alignContent: "space-between" },
{ computedOffset: 0.5, alignContent: "center" },
{ computedOffset: 1, alignContent: "space-between" }
],
explanation: "Test for no 0%/100% keyframes " +
"and specified style on target element " +
"using important type selector that overrides other rules"
},
];
discreteTests.forEach(testcase => {
test(t => {
addStyle(t, testcase.stylesheet);
const div = addDiv(t, { "id": "target" });
if (testcase.attributes) {
for (let attributeName in testcase.attributes) {
div.setAttribute(attributeName, testcase.attributes[attributeName]);
}
}
div.style.animation = "keyframes 100s";
const keyframes = div.getAnimations()[0].effect.getKeyframes();
const expectedKeyframes = testcase.expectedKeyframes;
assert_equals(keyframes.length, expectedKeyframes.length,
`keyframes.length should be ${ expectedKeyframes.length }`);
keyframes.forEach((keyframe, index) => {
const expectedKeyframe = expectedKeyframes[index];
assert_equals(keyframe.computedOffset, expectedKeyframe.computedOffset,
`computedOffset of keyframes[${ index }] should be ` +
`${ expectedKeyframe.computedOffset }`);
assert_equals(keyframe.alignContent, expectedKeyframe.alignContent,
`alignContent of keyframes[${ index }] should be ` +
`${ expectedKeyframe.alignContent }`);
});
}, testcase.explanation);
});
done();
</script>
</body>

View File

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<script>
'use strict';
setup({explicit_done: true});
SpecialPowers.pushPrefEnv(
{ "set": [["dom.animations-api.core.enabled", true]]},
function() {
window.open("file_underlying-discrete-value.html");
});
</script>
</html>

View File

@ -192,6 +192,14 @@ nsMappedAttributes::MightMapInheritedStyleData()
return true;
}
/* virtual */ bool
nsMappedAttributes::GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
nsCSSValue* aValue)
{
MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
return false;
}
#ifdef DEBUG
/* virtual */ void
nsMappedAttributes::List(FILE* out, int32_t aIndent) const

View File

@ -75,6 +75,8 @@ public:
// nsIStyleRule
virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
virtual bool MightMapInheritedStyleData() override;
virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
nsCSSValue* aValue) override;
#ifdef DEBUG
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
#endif

View File

@ -469,26 +469,22 @@ suite.test('bug-1054803', function() {
ok(found == 1, "found size " + size.toSource() + " in pictureSizes");
});
var sizeGenerator = Iterator(expSizes);
var sizeIterator = expSizes.values();
return new Promise(function(resolve, reject) {
function nextSize() {
try {
var size = sizeGenerator.next()[1];
var sync = suite.waitParameterPush();
cam.setPictureSize(size);
sync.then(function() {
var got = cam.getPictureSize();
ok(got.width == size.width && got.height == size.height,
"Set size " + size.toSource() + ", got size " + got.toSource());
nextSize();
}, reject);
} catch(e) {
if (e instanceof StopIteration) {
resolve();
} else {
reject(e);
}
var {value:size, done} = sizeIterator.next();
if (done) {
resolve();
return;
}
var sync = suite.waitParameterPush();
cam.setPictureSize(size);
sync.then(function() {
var got = cam.getPictureSize();
ok(got.width == size.width && got.height == size.height,
"Set size " + size.toSource() + ", got size " + got.toSource());
nextSize();
}, reject);
}
nextSize();

View File

@ -174,6 +174,14 @@ BodyRule::MightMapInheritedStyleData()
return false;
}
/* virtual */ bool
BodyRule::GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
nsCSSValue* aValue)
{
MOZ_ASSERT(false, "GetDiscretelyAnimatedCSSValue is not implemented yet");
return false;
}
#ifdef DEBUG
/* virtual */ void
BodyRule::List(FILE* out, int32_t aIndent) const

View File

@ -29,6 +29,8 @@ public:
// nsIStyleRule interface
virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
virtual bool MightMapInheritedStyleData() override;
virtual bool GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty,
nsCSSValue* aValue) override;
#ifdef DEBUG
virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
#endif

View File

@ -2617,20 +2617,6 @@ HTMLMediaElement::NotifyXPCOMShutdown()
ShutdownDecoder();
}
void
HTMLMediaElement::ResetConnectionState()
{
SetCurrentTime(0);
FireTimeUpdate(false);
DispatchAsyncEvent(NS_LITERAL_STRING("ended"));
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
ChangeDelayLoadStatus(false);
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
if (mDecoder) {
ShutdownDecoder();
}
}
void
HTMLMediaElement::Play(ErrorResult& aRv)
{
@ -3378,19 +3364,7 @@ nsresult HTMLMediaElement::InitializeDecoderForChannel(nsIChannel* aChannel,
mChannelLoader = nullptr;
}
// We postpone the |FinishDecoderSetup| function call until we get
// |OnConnected| signal from MediaStreamController which is held by
// RtspMediaResource.
if (DecoderTraits::DecoderWaitsForOnConnected(mimeType)) {
decoder->SetResource(resource);
SetDecoder(decoder);
if (aListener) {
*aListener = nullptr;
}
return NS_OK;
} else {
return FinishDecoderSetup(decoder, resource, aListener);
}
return FinishDecoderSetup(decoder, resource, aListener);
}
nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,

View File

@ -439,10 +439,6 @@ public:
return mNetworkState;
}
// Called by the media decoder object, on the main thread,
// when the connection between Rtsp server and client gets lost.
virtual void ResetConnectionState() final override;
void NotifyXPCOMShutdown() final override;
// Called by media decoder when the audible state changed or when input is
@ -719,13 +715,6 @@ public:
// A method to check whether we are currently playing.
bool IsCurrentlyPlaying() const;
/**
* A public wrapper for FinishDecoderSetup()
*/
nsresult FinishDecoderSetup(MediaDecoder* aDecoder, MediaResource* aStream) {
return FinishDecoderSetup(aDecoder, aStream, nullptr);
}
// Returns true if the media element is being destroyed. Used in
// dormancy checks to prevent dormant processing for an element
// that will soon be gone.

View File

@ -561,7 +561,7 @@ TextTrackManager::TimeMarchesOn()
mTimeMarchesOnDispatched = false;
// Early return if we don't have any TextTracks.
if (mTextTracks->Length() == 0) {
if (!mTextTracks || mTextTracks->Length() == 0) {
return;
}

View File

@ -922,11 +922,13 @@ ContentChild::InitXPCOM()
ClipboardCapabilities clipboardCaps;
DomainPolicyClone domainPolicy;
StructuredCloneData initialData;
OptionalURIParams userContentSheetURL;
SendGetXPCOMProcessAttributes(&isOffline, &isConnected,
&isLangRTL, &haveBidiKeyboards,
&mAvailableDictionaries,
&clipboardCaps, &domainPolicy, &initialData);
&clipboardCaps, &domainPolicy, &initialData,
&userContentSheetURL);
RecvSetOffline(isOffline);
RecvSetConnectivity(isConnected);
RecvBidiKeyboardNotify(isLangRTL, haveBidiKeyboards);
@ -964,6 +966,10 @@ ContentChild::InitXPCOM()
global->SetInitialProcessData(data);
}
// The stylesheet cache is not ready yet. Store this URL for future use.
nsCOMPtr<nsIURI> ucsURL = DeserializeURI(userContentSheetURL);
nsLayoutStylesheetCache::SetUserContentCSSURL(ucsURL);
// This will register cross-process observer.
mozilla::dom::time::InitializeDateCacheCleaner();
}

View File

@ -195,6 +195,8 @@
#include "nsIBidiKeyboard.h"
#include "nsLayoutStylesheetCache.h"
#ifdef MOZ_WEBRTC
#include "signaling/src/peerconnection/WebrtcGlobalParent.h"
#endif
@ -2975,7 +2977,8 @@ ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
InfallibleTArray<nsString>* dictionaries,
ClipboardCapabilities* clipboardCaps,
DomainPolicyClone* domainPolicy,
StructuredCloneData* aInitialData)
StructuredCloneData* aInitialData,
OptionalURIParams* aUserContentCSSURL)
{
nsCOMPtr<nsIIOService> io(do_GetIOService());
MOZ_ASSERT(io, "No IO service?");
@ -3032,6 +3035,16 @@ ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
}
}
// XXX bug 1046166
// Content processes have no permission to read profile, so we send the
// file URL instead.
StyleSheetHandle::RefPtr ucs = nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet();
if (ucs) {
SerializeURI(ucs->GetSheetURI(), *aUserContentCSSURL);
} else {
SerializeURI(nullptr, *aUserContentCSSURL);
}
return true;
}

View File

@ -732,7 +732,8 @@ private:
InfallibleTArray<nsString>* dictionaries,
ClipboardCapabilities* clipboardCaps,
DomainPolicyClone* domainPolicy,
StructuredCloneData* initialData) override;
StructuredCloneData* initialData,
OptionalURIParams* aUserContentSheetURL) override;
virtual bool
DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override;

View File

@ -722,7 +722,8 @@ parent:
bool haveBidiKeyboards, nsString[] dictionaries,
ClipboardCapabilities clipboardCaps,
DomainPolicyClone domainPolicy,
StructuredCloneData initialData);
StructuredCloneData initialData,
OptionalURIParams userContentSheetURL);
sync CreateChildProcess(IPCTabContext context,
ProcessPriority priority,

View File

@ -33,10 +33,6 @@
#include "nsIPrincipal.h"
#include "mozilla/dom/HTMLMediaElement.h"
#endif
#ifdef NECKO_PROTOCOL_rtsp
#include "RtspOmxDecoder.h"
#include "RtspOmxReader.h"
#endif
#ifdef MOZ_DIRECTSHOW
#include "DirectShowDecoder.h"
#include "DirectShowReader.h"
@ -273,29 +269,6 @@ static char const *const gOMXWebMCodecs[] = {
#endif
#ifdef NECKO_PROTOCOL_rtsp
static const char* const gRtspTypes[2] = {
"RTSP",
nullptr
};
static bool
IsRtspSupportedType(const nsACString& aMimeType)
{
return MediaDecoder::IsRtspEnabled() &&
CodecListContains(gRtspTypes, aMimeType);
}
#endif
/* static */
bool DecoderTraits::DecoderWaitsForOnConnected(const nsACString& aMimeType) {
#ifdef NECKO_PROTOCOL_rtsp
return CodecListContains(gRtspTypes, aMimeType);
#else
return false;
#endif
}
#ifdef MOZ_ANDROID_OMX
static bool
IsAndroidMediaType(const nsACString& aType)
@ -547,11 +520,6 @@ DecoderTraits::CanHandleMediaType(const char* aMIMEType,
EnsureAndroidMediaPluginHost()->FindDecoder(nsDependentCString(aMIMEType), nullptr)) {
return CANPLAY_MAYBE;
}
#endif
#ifdef NECKO_PROTOCOL_rtsp
if (IsRtspSupportedType(nsDependentCString(aMIMEType))) {
return CANPLAY_MAYBE;
}
#endif
return CANPLAY_NO;
}
@ -615,12 +583,6 @@ InstantiateDecoder(const nsACString& aType,
return decoder.forget();
}
#endif
#ifdef NECKO_PROTOCOL_rtsp
if (IsRtspSupportedType(aType)) {
decoder = new RtspOmxDecoder(aOwner);
return decoder.forget();
}
#endif
#ifdef MOZ_ANDROID_OMX
if (MediaDecoder::IsAndroidMediaPluginEnabled() &&
EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
@ -753,9 +715,6 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
IsAACSupportedType(aType) ||
#ifdef MOZ_DIRECTSHOW
IsDirectShowSupportedType(aType) ||
#endif
#ifdef NECKO_PROTOCOL_rtsp
IsRtspSupportedType(aType) ||
#endif
false;
}

View File

@ -71,10 +71,6 @@ public:
// vice versa.
static bool IsSupportedInVideoDocument(const nsACString& aType);
// Returns true if we should not start decoder until we receive
// OnConnected signal. (currently RTSP only)
static bool DecoderWaitsForOnConnected(const nsACString& aType);
static bool IsWebMTypeAndEnabled(const nsACString& aType);
static bool IsWebMAudioType(const nsACString& aType);
static bool IsMP4TypeAndEnabled(const nsACString& aType,

View File

@ -183,22 +183,6 @@ MediaDecoder::ResourceCallback::SetMediaSeekable(bool aMediaSeekable)
}
}
void
MediaDecoder::ResourceCallback::ResetConnectionState()
{
MOZ_ASSERT(NS_IsMainThread());
if (mDecoder) {
mDecoder->ResetConnectionState();
}
}
nsresult
MediaDecoder::ResourceCallback::FinishDecoderSetup(MediaResource* aResource)
{
MOZ_ASSERT(NS_IsMainThread());
return mDecoder ? mDecoder->FinishDecoderSetup(aResource) : NS_ERROR_FAILURE;
}
void
MediaDecoder::ResourceCallback::NotifyNetworkError()
{
@ -499,6 +483,11 @@ MediaDecoder::IsInfinite() const
return mInfiniteStream;
}
#define INIT_MIRROR(name, val) \
name(AbstractThread::MainThread(), val, "MediaDecoder::" #name " (Mirror)")
#define INIT_CANONICAL(name, val) \
name(AbstractThread::MainThread(), val, "MediaDecoder::" #name " (Canonical)")
MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner)
: mWatchManager(this, AbstractThread::MainThread())
, mDormantSupported(false)
@ -526,53 +515,29 @@ MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner)
Preferences::GetInt("media.decoder.heuristic.dormant.timeout",
DEFAULT_HEURISTIC_DORMANT_TIMEOUT_MSECS))
, mIsHeuristicDormant(false)
, mStateMachineIsShutdown(AbstractThread::MainThread(), true,
"MediaDecoder::mStateMachineIsShutdown (Mirror)")
, mBuffered(AbstractThread::MainThread(), TimeIntervals(),
"MediaDecoder::mBuffered (Mirror)")
, mNextFrameStatus(AbstractThread::MainThread(),
MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
"MediaDecoder::mNextFrameStatus (Mirror)")
, mCurrentPosition(AbstractThread::MainThread(), 0,
"MediaDecoder::mCurrentPosition (Mirror)")
, mStateMachineDuration(AbstractThread::MainThread(), NullableTimeUnit(),
"MediaDecoder::mStateMachineDuration (Mirror)")
, mPlaybackPosition(AbstractThread::MainThread(), 0,
"MediaDecoder::mPlaybackPosition (Mirror)")
, mIsAudioDataAudible(AbstractThread::MainThread(), false,
"MediaDecoder::mIsAudioDataAudible (Mirror)")
, mVolume(AbstractThread::MainThread(), 0.0,
"MediaDecoder::mVolume (Canonical)")
, mPlaybackRate(AbstractThread::MainThread(), 1.0,
"MediaDecoder::mPlaybackRate (Canonical)")
, mPreservesPitch(AbstractThread::MainThread(), true,
"MediaDecoder::mPreservesPitch (Canonical)")
, mEstimatedDuration(AbstractThread::MainThread(), NullableTimeUnit(),
"MediaDecoder::mEstimatedDuration (Canonical)")
, mExplicitDuration(AbstractThread::MainThread(), Maybe<double>(),
"MediaDecoder::mExplicitDuration (Canonical)")
, mPlayState(AbstractThread::MainThread(), PLAY_STATE_LOADING,
"MediaDecoder::mPlayState (Canonical)")
, mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED,
"MediaDecoder::mNextState (Canonical)")
, mLogicallySeeking(AbstractThread::MainThread(), false,
"MediaDecoder::mLogicallySeeking (Canonical)")
, mSameOriginMedia(AbstractThread::MainThread(), false,
"MediaDecoder::mSameOriginMedia (Canonical)")
, mMediaPrincipalHandle(AbstractThread::MainThread(), PRINCIPAL_HANDLE_NONE,
"MediaDecoder::mMediaPrincipalHandle (Canonical)")
, mPlaybackBytesPerSecond(AbstractThread::MainThread(), 0.0,
"MediaDecoder::mPlaybackBytesPerSecond (Canonical)")
, mPlaybackRateReliable(AbstractThread::MainThread(), true,
"MediaDecoder::mPlaybackRateReliable (Canonical)")
, mDecoderPosition(AbstractThread::MainThread(), 0,
"MediaDecoder::mDecoderPosition (Canonical)")
, mMediaSeekable(AbstractThread::MainThread(), true,
"MediaDecoder::mMediaSeekable (Canonical)")
, mMediaSeekableOnlyInBufferedRanges(AbstractThread::MainThread(), false,
"MediaDecoder::mMediaSeekableOnlyInBufferedRanges (Canonical)")
, mIsVisible(AbstractThread::MainThread(), !aOwner->IsHidden(),
"MediaDecoder::mIsVisible (Canonical)")
, INIT_MIRROR(mStateMachineIsShutdown, true)
, INIT_MIRROR(mBuffered, TimeIntervals())
, INIT_MIRROR(mNextFrameStatus, MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED)
, INIT_MIRROR(mCurrentPosition, 0)
, INIT_MIRROR(mStateMachineDuration, NullableTimeUnit())
, INIT_MIRROR(mPlaybackPosition, 0)
, INIT_MIRROR(mIsAudioDataAudible, false)
, INIT_CANONICAL(mVolume, 0.0)
, INIT_CANONICAL(mPlaybackRate, 1.0)
, INIT_CANONICAL(mPreservesPitch, true)
, INIT_CANONICAL(mEstimatedDuration, NullableTimeUnit())
, INIT_CANONICAL(mExplicitDuration, Maybe<double>())
, INIT_CANONICAL(mPlayState, PLAY_STATE_LOADING)
, INIT_CANONICAL(mNextState, PLAY_STATE_PAUSED)
, INIT_CANONICAL(mLogicallySeeking, false)
, INIT_CANONICAL(mSameOriginMedia, false)
, INIT_CANONICAL(mMediaPrincipalHandle, PRINCIPAL_HANDLE_NONE)
, INIT_CANONICAL(mPlaybackBytesPerSecond, 0.0)
, INIT_CANONICAL(mPlaybackRateReliable, true)
, INIT_CANONICAL(mDecoderPosition, 0)
, INIT_CANONICAL(mMediaSeekable, true)
, INIT_CANONICAL(mMediaSeekableOnlyInBufferedRanges, false)
, INIT_CANONICAL(mIsVisible, !aOwner->IsHidden())
, mTelemetryReported(false)
{
MOZ_COUNT_CTOR(MediaDecoder);
@ -612,6 +577,9 @@ MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner)
MediaShutdownManager::Instance().Register(this);
}
#undef INIT_MIRROR
#undef INIT_CANONICAL
void
MediaDecoder::Shutdown()
{
@ -1026,26 +994,6 @@ MediaDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
NotifySuspendedStatusChanged();
}
nsresult
MediaDecoder::FinishDecoderSetup(MediaResource* aResource)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsShutdown());
HTMLMediaElement* element = mOwner->GetMediaElement();
NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
element->FinishDecoderSetup(this, aResource);
return NS_OK;
}
void
MediaDecoder::ResetConnectionState()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!IsShutdown());
mOwner->ResetConnectionState();
MOZ_ASSERT(IsShutdown());
}
void
MediaDecoder::NetworkError()
{
@ -1755,15 +1703,6 @@ MediaDecoder::IsWebMEnabled()
return Preferences::GetBool("media.webm.enabled");
}
#ifdef NECKO_PROTOCOL_rtsp
bool
MediaDecoder::IsRtspEnabled()
{
//Currently the Rtsp decoded by omx.
return (Preferences::GetBool("media.rtsp.enabled", false) && IsOmxEnabled());
}
#endif
#ifdef MOZ_OMX_DECODER
bool
MediaDecoder::IsOmxEnabled()

View File

@ -85,8 +85,6 @@ public:
MediaDecoderOwner* GetMediaOwner() const override;
void SetInfinite(bool aInfinite) override;
void SetMediaSeekable(bool aMediaSeekable) override;
void ResetConnectionState() override;
nsresult FinishDecoderSetup(MediaResource* aResource) override;
void NotifyNetworkError() override;
void NotifyDecodeError() override;
void NotifyDataArrived() override;
@ -462,10 +460,6 @@ private:
static bool IsWaveEnabled();
static bool IsWebMEnabled();
#ifdef NECKO_PROTOCOL_rtsp
static bool IsRtspEnabled();
#endif
#ifdef MOZ_OMX_DECODER
static bool IsOmxEnabled();
#endif
@ -887,12 +881,6 @@ private:
// no longer considered to be infinite.
void SetInfinite(bool aInfinite);
// Reset the decoder and notify the media element that
// server connection is closed.
void ResetConnectionState();
nsresult FinishDecoderSetup(MediaResource* aResource);
// Called by MediaResource when the principal of the resource has
// changed. Called on main thread only.
void NotifyPrincipalChanged();

View File

@ -134,12 +134,6 @@ public:
// ImageContainer containing the video data.
virtual VideoFrameContainer* GetVideoFrameContainer() = 0;
// Called by the decoder object, on the main thread,
// when the connection between Rtsp server and client gets lost.
// The decoder owner should call Shutdown() on the decoder and drop the
// reference to the decoder to prevent further calls into the decoder.
virtual void ResetConnectionState() = 0;
// Called by media decoder when the audible state changed
virtual void SetAudibleState(bool aAudible) = 0;

Some files were not shown because too many files have changed in this diff Show More