diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 64767b49559..ea8a49ba651 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -42,6 +42,7 @@ pref("extensions.getAddons.getWithPerformance.url", "https://services.addons.moz pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%&platform=%OS%&appver=%VERSION%"); pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%/%COMPATIBILITY_MODE%?src=firefox"); pref("extensions.webservice.discoverURL", "https://services.addons.mozilla.org/%LOCALE%/firefox/discovery/pane/%VERSION%/%OS%/%COMPATIBILITY_MODE%"); +pref("extensions.getAddons.recommended.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/%API_VERSION%/list/recommended/all/%MAX_RESULTS%/%OS%/%VERSION%?src=firefox"); // Blocklist preferences pref("extensions.blocklist.enabled", true); @@ -1059,6 +1060,7 @@ pref("devtools.debugger.remote-host", "localhost"); pref("devtools.debugger.remote-autoconnect", false); pref("devtools.debugger.remote-connection-retries", 3); pref("devtools.debugger.remote-timeout", 20000); +pref("devtools.debugger.source-maps-enabled", false); // The default Debugger UI settings pref("devtools.debugger.ui.win-x", 0); diff --git a/browser/devtools/debugger/debugger-controller.js b/browser/devtools/debugger/debugger-controller.js index fc2f51a5087..9df4b43e126 100644 --- a/browser/devtools/debugger/debugger-controller.js +++ b/browser/devtools/debugger/debugger-controller.js @@ -253,6 +253,8 @@ let DebuggerController = { if (aCallback) { aCallback(); } + }, { + useSourceMaps: Services.prefs.getBoolPref("devtools.debugger.source-maps-enabled") }); }, @@ -1102,7 +1104,7 @@ SourceScripts.prototype = { */ _onSourcesAdded: function SS__onSourcesAdded(aResponse) { if (aResponse.error) { - Cu.reportError("Error getting sources: " + aResponse.message); + Cu.reportError(new Error("Error getting sources: " + aResponse.message)); return; } diff --git a/browser/devtools/debugger/test/Makefile.in b/browser/devtools/debugger/test/Makefile.in index 7f01762ad84..07eeed6da26 100644 --- a/browser/devtools/debugger/test/Makefile.in +++ b/browser/devtools/debugger/test/Makefile.in @@ -96,6 +96,7 @@ MOCHITEST_BROWSER_TESTS = \ browser_dbg_bfcache.js \ browser_dbg_progress-listener-bug.js \ browser_dbg_chrome-debugging.js \ + browser_dbg_source_maps-01.js \ head.js \ helpers.js \ $(NULL) @@ -127,6 +128,10 @@ MOCHITEST_BROWSER_PAGES = \ test-function-search-01.js \ test-function-search-02.js \ test-function-search-03.js \ + binary_search.html \ + binary_search.coffee \ + binary_search.js \ + binary_search.map \ $(NULL) MOCHITEST_BROWSER_FILES_PARTS = MOCHITEST_BROWSER_TESTS MOCHITEST_BROWSER_PAGES diff --git a/browser/devtools/debugger/test/binary_search.coffee b/browser/devtools/debugger/test/binary_search.coffee new file mode 100644 index 00000000000..e3dacdaaab5 --- /dev/null +++ b/browser/devtools/debugger/test/binary_search.coffee @@ -0,0 +1,18 @@ +# Uses a binary search algorithm to locate a value in the specified array. +window.binary_search = (items, value) -> + + start = 0 + stop = items.length - 1 + pivot = Math.floor (start + stop) / 2 + + while items[pivot] isnt value and start < stop + + # Adjust the search area. + stop = pivot - 1 if value < items[pivot] + start = pivot + 1 if value > items[pivot] + + # Recalculate the pivot. + pivot = Math.floor (stop + start) / 2 + + # Make sure we've found the correct value. + if items[pivot] is value then pivot else -1 \ No newline at end of file diff --git a/browser/devtools/debugger/test/binary_search.html b/browser/devtools/debugger/test/binary_search.html new file mode 100644 index 00000000000..f9615da7c40 --- /dev/null +++ b/browser/devtools/debugger/test/binary_search.html @@ -0,0 +1,12 @@ + + + + + Browser Debugger Source Map Test + + + + + + diff --git a/browser/devtools/debugger/test/binary_search.js b/browser/devtools/debugger/test/binary_search.js new file mode 100644 index 00000000000..aa3dff3bc82 --- /dev/null +++ b/browser/devtools/debugger/test/binary_search.js @@ -0,0 +1,29 @@ +// Generated by CoffeeScript 1.6.1 +(function() { + + window.binary_search = function(items, value) { + var pivot, start, stop; + start = 0; + stop = items.length - 1; + pivot = Math.floor((start + stop) / 2); + while (items[pivot] !== value && start < stop) { + if (value < items[pivot]) { + stop = pivot - 1; + } + if (value > items[pivot]) { + start = pivot + 1; + } + pivot = Math.floor((stop + start) / 2); + } + if (items[pivot] === value) { + return pivot; + } else { + return -1; + } + }; + +}).call(this); + +/* +//@ sourceMappingURL=binary_search.map +*/ diff --git a/browser/devtools/debugger/test/binary_search.map b/browser/devtools/debugger/test/binary_search.map new file mode 100644 index 00000000000..c5aaeab2f01 --- /dev/null +++ b/browser/devtools/debugger/test/binary_search.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "binary_search.js", + "sourceRoot": "", + "sources": [ + "binary_search.coffee" + ], + "names": [], + "mappings": ";AACA;CAAA;CAAA,CAAA,CAAuB,EAAA,CAAjB,GAAkB,IAAxB;CAEE,OAAA,UAAA;CAAA,EAAQ,CAAR,CAAA;CAAA,EACQ,CAAR,CAAa,CAAL;CADR,EAEQ,CAAR,CAAA;CAEA,EAA0C,CAAR,CAAtB,MAAN;CAGJ,EAA6B,CAAR,CAAA,CAArB;CAAA,EAAQ,CAAR,CAAQ,GAAR;QAAA;CACA,EAA6B,CAAR,CAAA,CAArB;CAAA,EAAQ,EAAR,GAAA;QADA;CAAA,EAIQ,CAAI,CAAZ,CAAA;CAXF,IAIA;CAUA,GAAA,CAAS;CAAT,YAA8B;MAA9B;AAA0C,CAAD,YAAA;MAhBpB;CAAvB,EAAuB;CAAvB" +} \ No newline at end of file diff --git a/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js b/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js index 93ae5ff0e49..fc3b2efb54b 100644 --- a/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js +++ b/browser/devtools/debugger/test/browser_dbg_bug731394_editor-contextmenu.js @@ -27,19 +27,22 @@ function test() gDebugger = gPane.panelWin; resumed = true; - gDebugger.addEventListener("Debugger:SourceShown", onScriptShown); + gDebugger.addEventListener("Debugger:SourceShown", onSourceShown); - gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() { - framesAdded = true; - executeSoon(startTest); - }); + gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", + onFramesAdded); executeSoon(function() { gDebuggee.firstCall(); }); }); - function onScriptShown(aEvent) { + function onFramesAdded(aEvent) { + framesAdded = true; + executeSoon(startTest); + } + + function onSourceShown(aEvent) { scriptShown = aEvent.detail.url.indexOf("-02.js") != -1; executeSoon(startTest); } @@ -48,8 +51,8 @@ function test() { if (scriptShown && framesAdded && resumed && !testStarted) { testStarted = true; - gDebugger.removeEventListener("Debugger:SourceShown", onScriptShown); - Services.tm.currentThread.dispatch({ run: performTest }, 0); + gDebugger.removeEventListener("Debugger:SourceShown", onSourceShown); + executeSoon(performTest); } } diff --git a/browser/devtools/debugger/test/browser_dbg_source_maps-01.js b/browser/devtools/debugger/test/browser_dbg_source_maps-01.js new file mode 100644 index 00000000000..c8ff8aae95f --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_source_maps-01.js @@ -0,0 +1,156 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test that we can set breakpoints and step through source mapped coffee + * script. + */ + +const TAB_URL = EXAMPLE_URL + "binary_search.html"; + +var gPane = null; +var gTab = null; +var gDebuggee = null; +var gDebugger = null; + +function test() +{ + let scriptShown = false; + let framesAdded = false; + let resumed = false; + let testStarted = false; + + Services.prefs.setBoolPref("devtools.debugger.source-maps-enabled", true); + + debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) { + resumed = true; + gTab = aTab; + gDebuggee = aDebuggee; + gPane = aPane; + gDebugger = gPane.panelWin; + + gDebugger.addEventListener("Debugger:SourceShown", function _onSourceShown(aEvent) { + gDebugger.removeEventListener("Debugger:SourceShown", _onSourceShown); + ok(aEvent.detail.url.indexOf(".coffee") != -1, + "The debugger should show the source mapped coffee script file."); + ok(gDebugger.editor.getText().search(/isnt/) != -1, + "The debugger's editor should have the coffee script source displayed."); + + testSetBreakpoint(); + }); + }); +} + +function testSetBreakpoint() { + let { activeThread } = gDebugger.DebuggerController; + activeThread.interrupt(function (aResponse) { + activeThread.setBreakpoint({ + url: EXAMPLE_URL + "binary_search.coffee", + line: 5 + }, function (aResponse, bpClient) { + ok(!aResponse.error, + "Should be able to set a breakpoint in a coffee script file."); + testSetBreakpointBlankLine(); + }); + }); +} + +function testSetBreakpointBlankLine() { + let { activeThread } = gDebugger.DebuggerController; + activeThread.setBreakpoint({ + url: EXAMPLE_URL + "binary_search.coffee", + line: 3 + }, function (aResponse, bpClient) { + ok(aResponse.actualLocation, + "Because 3 is empty, we should have an actualLocation"); + is(aResponse.actualLocation.url, EXAMPLE_URL + "binary_search.coffee", + "actualLocation.url should be source mapped to the coffee file"); + is(aResponse.actualLocation.line, 2, + "actualLocation.line should be source mapped back to 2"); + testHitBreakpoint(); + }); +} + +function testHitBreakpoint() { + let { activeThread } = gDebugger.DebuggerController; + activeThread.resume(function (aResponse) { + ok(!aResponse.error, "Shouldn't get an error resuming"); + is(aResponse.type, "resumed", "Type should be 'resumed'"); + + activeThread.addOneTimeListener("paused", function (aEvent, aPacket) { + is(aPacket.type, "paused", + "We should now be paused again"); + is(aPacket.why.type, "breakpoint", + "and the reason we should be paused is because we hit a breakpoint"); + + // Check that we stopped at the right place, by making sure that the + // environment is in the state that we expect. + is(aPacket.frame.environment.bindings.variables.start.value, 0, + "'start' is 0"); + is(aPacket.frame.environment.bindings.variables.stop.value.type, "undefined", + "'stop' hasn't been assigned to yet"); + is(aPacket.frame.environment.bindings.variables.pivot.value.type, "undefined", + "'pivot' hasn't been assigned to yet"); + + waitForCaretPos(4, testStepping); + }); + + // This will cause the breakpoint to be hit, and put us back in the paused + // state. + executeSoon(function() { + gDebuggee.binary_search([0, 2, 3, 5, 7, 10], 5); + }); + }); +} + +function testStepping() { + let { activeThread } = gDebugger.DebuggerController; + activeThread.stepIn(function (aResponse) { + ok(!aResponse.error, "Shouldn't get an error resuming"); + is(aResponse.type, "resumed", "Type should be 'resumed'"); + + // After stepping, we will pause again, so listen for that. + activeThread.addOneTimeListener("paused", function (aEvent, aPacket) { + + // Check that we stopped at the right place, by making sure that the + // environment is in the state that we expect. + is(aPacket.frame.environment.bindings.variables.start.value, 0, + "'start' is 0"); + is(aPacket.frame.environment.bindings.variables.stop.value, 5, + "'stop' hasn't been assigned to yet"); + is(aPacket.frame.environment.bindings.variables.pivot.value.type, "undefined", + "'pivot' hasn't been assigned to yet"); + + waitForCaretPos(5, closeDebuggerAndFinish); + }); + }); +} + +function waitForCaretPos(number, callback) +{ + // Poll every few milliseconds until the source editor line is active. + let count = 0; + let intervalID = window.setInterval(function() { + info("count: " + count + " "); + if (++count > 50) { + ok(false, "Timed out while polling for the line."); + window.clearInterval(intervalID); + return closeDebuggerAndFinish(); + } + if (gDebugger.DebuggerView.editor.getCaretPosition().line != number) { + return; + } + // We got the source editor at the expected line, it's safe to callback. + window.clearInterval(intervalID); + callback(); + }, 100); +} + +registerCleanupFunction(function() { + Services.prefs.setBoolPref("devtools.debugger.source-maps-enabled", false); + removeTab(gTab); + gPane = null; + gTab = null; + gDebuggee = null; + gDebugger = null; +}); diff --git a/browser/devtools/framework/Target.jsm b/browser/devtools/framework/Target.jsm index 94b8c0988f8..d3b685726ad 100644 --- a/browser/devtools/framework/Target.jsm +++ b/browser/devtools/framework/Target.jsm @@ -322,13 +322,15 @@ TabTarget.prototype = { event.title = aPacket.title; // Send any stored event payload (DOMWindow or nsIRequest) for backwards // compatibility with non-remotable tools. - event._navPayload = this._navPayload; if (aPacket.state == "start") { + event._navPayload = this._navRequest; this.emit("will-navigate", event); + this._navRequest = null; } else { + event._navPayload = this._navWindow; this.emit("navigate", event); + this._navWindow = null; } - this._navPayload = null; }.bind(this); this.client.addListener("tabNavigated", this._onTabNavigated); }, @@ -467,7 +469,7 @@ TabWebProgressListener.prototype = { // Emit the event if the target is not remoted or store the payload for // later emission otherwise. if (this.target._client) { - this.target._navPayload = request; + this.target._navRequest = request; } else { this.target.emit("will-navigate", request); } @@ -485,7 +487,7 @@ TabWebProgressListener.prototype = { // Emit the event if the target is not remoted or store the payload for // later emission otherwise. if (this.target._client) { - this.target._navPayload = window; + this.target._navWindow = window; } else { this.target.emit("navigate", window); } @@ -500,6 +502,8 @@ TabWebProgressListener.prototype = { this.target.tab.linkedBrowser.removeProgressListener(this); } this.target._webProgressListener = null; + this.target._navRequest = null; + this.target._navWindow = null; this.target = null; } }; diff --git a/browser/devtools/webconsole/test/Makefile.in b/browser/devtools/webconsole/test/Makefile.in index bea96298e45..caa8fa267f0 100644 --- a/browser/devtools/webconsole/test/Makefile.in +++ b/browser/devtools/webconsole/test/Makefile.in @@ -121,6 +121,7 @@ MOCHITEST_BROWSER_FILES = \ browser_console_variables_view_while_debugging.js \ browser_console.js \ browser_longstring_hang.js \ + browser_console_consolejsm_output.js \ head.js \ $(NULL) diff --git a/browser/devtools/webconsole/test/browser_console_consolejsm_output.js b/browser/devtools/webconsole/test/browser_console_consolejsm_output.js new file mode 100644 index 00000000000..9bd8b10c192 --- /dev/null +++ b/browser/devtools/webconsole/test/browser_console_consolejsm_output.js @@ -0,0 +1,118 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Test that Console.jsm outputs messages to the Browser Console, bug 851231. + +function test() +{ + HUDConsoleUI.toggleBrowserConsole().then(consoleOpened); + let hud = null; + + function consoleOpened(aHud) + { + hud = aHud; + hud.jsterm.clearOutput(true); + + let console = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).console; + + console.time("foobarTimer"); + let foobar = { bug851231prop: "bug851231value" }; + + console.log("bug851231-log"); + console.info("bug851231-info"); + console.warn("bug851231-warn"); + console.error("bug851231-error", foobar); + console.debug("bug851231-debug"); + console.trace(); + console.dir(document); + console.timeEnd("foobarTimer"); + + info("wait for the Console.jsm messages"); + + waitForMessages({ + webconsole: hud, + messages: [ + { + name: "console.log output", + text: "bug851231-log", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + name: "console.info output", + text: "bug851231-info", + category: CATEGORY_WEBDEV, + severity: SEVERITY_INFO, + }, + { + name: "console.warn output", + text: "bug851231-warn", + category: CATEGORY_WEBDEV, + severity: SEVERITY_WARNING, + }, + { + name: "console.error output", + text: /\bbug851231-error\b.+\[object Object\]/, + category: CATEGORY_WEBDEV, + severity: SEVERITY_ERROR, + objects: true, + }, + { + name: "console.debug output", + text: "bug851231-debug", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + name: "console.trace output", + consoleTrace: { + file: "browser_console_consolejsm_output.js", + fn: "consoleOpened", + }, + }, + { + name: "console.dir output", + consoleDir: "[object XULDocument]", + }, + { + name: "console.time output", + consoleTime: "foobarTimer", + }, + { + name: "console.timeEnd output", + consoleTimeEnd: "foobarTimer", + }, + ], + }).then((aResults) => { + let consoleErrorMsg = aResults[3]; + ok(consoleErrorMsg, "console.error message element found"); + let clickable = consoleErrorMsg.clickableElements[0]; + ok(clickable, "clickable object found for console.error"); + + let onFetch = (aEvent, aVar) => { + // Skip the notification from console.dir variablesview-fetched. + if (aVar._variablesView != hud.jsterm._variablesView) { + return; + } + hud.jsterm.off("variablesview-fetched", onFetch); + + ok(aVar, "object inspector opened on click"); + + findVariableViewProperties(aVar, [{ + name: "bug851231prop", + value: "bug851231value", + }], { webconsole: hud }).then(finishTest); + }; + + hud.jsterm.on("variablesview-fetched", onFetch); + + scrollOutputToNode(clickable); + + info("wait for variablesview-fetched"); + executeSoon(() => + EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow)); + }); + } +} diff --git a/browser/devtools/webconsole/test/browser_longstring_hang.js b/browser/devtools/webconsole/test/browser_longstring_hang.js index 4ec5b2070be..0a36006801e 100644 --- a/browser/devtools/webconsole/test/browser_longstring_hang.js +++ b/browser/devtools/webconsole/test/browser_longstring_hang.js @@ -42,13 +42,10 @@ function test() function onInitialString(aResults) { - let msg = [...aResults[0].matched][0]; - ok(msg, "console.log result message element"); - - let clickable = msg.querySelector(".longStringEllipsis"); + let clickable = aResults[0].longStrings[0]; ok(clickable, "long string ellipsis is shown"); - scrollToVisible(clickable); + scrollOutputToNode(clickable); executeSoon(() => { EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow); @@ -73,16 +70,4 @@ function test() }).then(finishTest); }); } - - function scrollToVisible(aNode) - { - let richListBoxNode = aNode.parentNode; - while (richListBoxNode.tagName != "richlistbox") { - richListBoxNode = richListBoxNode.parentNode; - } - - let boxObject = richListBoxNode.scrollBoxObject; - let nsIScrollBoxObject = boxObject.QueryInterface(Ci.nsIScrollBoxObject); - nsIScrollBoxObject.ensureElementIsVisible(aNode); - } } diff --git a/browser/devtools/webconsole/test/head.js b/browser/devtools/webconsole/test/head.js index 97e84c9a0cb..4f15d19d4a5 100644 --- a/browser/devtools/webconsole/test/head.js +++ b/browser/devtools/webconsole/test/head.js @@ -252,7 +252,7 @@ function waitForOpenContextMenu(aContextMenu, aOptions) { function dumpConsoles() { if (gPendingOutputTest) { - console.log("dumpConsoles"); + console.log("dumpConsoles start"); for each (let hud in HUDService.hudReferences) { if (!hud.outputNode) { console.debug("no output content for", hud.hudId); @@ -261,25 +261,38 @@ function dumpConsoles() console.debug("output content for", hud.hudId); for (let elem of hud.outputNode.childNodes) { - let text = getMessageElementText(elem); - let repeats = elem.querySelector(".webconsole-msg-repeat"); - if (repeats) { - repeats = repeats.getAttribute("value"); - } - console.debug("date", elem.timestamp, - "class", elem.className, - "category", elem.category, - "severity", elem.severity, - "repeats", repeats, - "clipboardText", elem.clipboardText, - "text", text); + dumpMessageElement(elem); } } + console.log("dumpConsoles end"); gPendingOutputTest = 0; } } +/** + * Dump to output debug information for the given webconsole message. + * + * @param nsIDOMNode aMessage + * The message element you want to display. + */ +function dumpMessageElement(aMessage) +{ + let text = getMessageElementText(aMessage); + let repeats = aMessage.querySelector(".webconsole-msg-repeat"); + if (repeats) { + repeats = repeats.getAttribute("value"); + } + console.debug("id", aMessage.getAttribute("id"), + "date", aMessage.timestamp, + "class", aMessage.className, + "category", aMessage.category, + "severity", aMessage.severity, + "repeats", repeats, + "clipboardText", aMessage.clipboardText, + "text", text); +} + function finishTest() { browser = hudId = hud = filterBox = outputNode = cs = null; @@ -888,6 +901,104 @@ function waitForMessages(aOptions) return result; } + function checkConsoleTrace(aRule, aElement) + { + let elemText = getMessageElementText(aElement); + let trace = aRule.consoleTrace; + + if (!checkText("Stack trace from ", elemText)) { + return false; + } + + let clickable = aElement.querySelector(".hud-clickable"); + if (!clickable) { + ok(false, "console.trace() message is missing .hud-clickable"); + displayErrorContext(aRule, aElement); + return false; + } + aRule.clickableElements = [clickable]; + + if (trace.file && + !checkText("from " + trace.file + ", ", elemText)) { + ok(false, "console.trace() message is missing the file name: " + + trace.file); + displayErrorContext(aRule, aElement); + return false; + } + + if (trace.fn && + !checkText(", function " + trace.fn + ", ", elemText)) { + ok(false, "console.trace() message is missing the function name: " + + trace.fn); + displayErrorContext(aRule, aElement); + return false; + } + + if (trace.line && + !checkText(", line " + trace.line + ".", elemText)) { + ok(false, "console.trace() message is missing the line number: " + + trace.line); + displayErrorContext(aRule, aElement); + return false; + } + + aRule.category = CATEGORY_WEBDEV; + aRule.severity = SEVERITY_LOG; + + return true; + } + + function checkConsoleTime(aRule, aElement) + { + let elemText = getMessageElementText(aElement); + let time = aRule.consoleTime; + + if (!checkText(time + ": timer started", elemText)) { + return false; + } + + aRule.category = CATEGORY_WEBDEV; + aRule.severity = SEVERITY_LOG; + + return true; + } + + function checkConsoleTimeEnd(aRule, aElement) + { + let elemText = getMessageElementText(aElement); + let time = aRule.consoleTimeEnd; + let regex = new RegExp(time + ": \\d+ms"); + + if (!checkText(regex, elemText)) { + return false; + } + + aRule.category = CATEGORY_WEBDEV; + aRule.severity = SEVERITY_LOG; + + return true; + } + + function checkConsoleDir(aRule, aElement) + { + if (!aElement.classList.contains("webconsole-msg-inspector")) { + return false; + } + + let elemText = getMessageElementText(aElement); + if (!checkText(aRule.consoleDir, elemText)) { + return false; + } + + let iframe = aElement.querySelector("iframe"); + if (!iframe) { + ok(false, "console.dir message has no iframe"); + return false; + } + + return true; + } + function checkMessage(aRule, aElement) { let elemText = getMessageElementText(aElement); @@ -900,16 +1011,41 @@ function waitForMessages(aOptions) return false; } - if (aRule.category) { - if (aElement.category != aRule.category) { - return false; - } + if (aRule.consoleTrace && !checkConsoleTrace(aRule, aElement)) { + return false; } - if (aRule.severity) { - if (aElement.severity != aRule.severity) { - return false; + if (aRule.consoleTime && !checkConsoleTime(aRule, aElement)) { + return false; + } + + if (aRule.consoleTimeEnd && !checkConsoleTimeEnd(aRule, aElement)) { + return false; + } + + if (aRule.consoleDir && !checkConsoleDir(aRule, aElement)) { + return false; + } + + let partialMatch = !!(aRule.consoleTrace || aRule.consoleTime || + aRule.consoleTimeEnd); + + if (aRule.category && aElement.category != aRule.category) { + if (partialMatch) { + is(aElement.category, aRule.category, + "message category for rule: " + displayRule(aRule)); + displayErrorContext(aRule, aElement); } + return false; + } + + if (aRule.severity && aElement.severity != aRule.severity) { + if (partialMatch) { + is(aElement.severity, aRule.severity, + "message severity for rule: " + displayRule(aRule)); + displayErrorContext(aRule, aElement); + } + return false; } if (aRule.repeats) { @@ -919,9 +1055,32 @@ function waitForMessages(aOptions) } } - let longString = !!aElement.querySelector(".longStringEllipsis"); - if ("longString" in aRule && aRule.longString != longString) { - return false; + if ("longString" in aRule) { + let longStrings = aElement.querySelectorAll(".longStringEllipsis"); + if (aRule.longString != !!longStrings[0]) { + if (partialMatch) { + is(!!longStrings[0], aRule.longString, + "long string existence check failed for message rule: " + + displayRule(aRule)); + displayErrorContext(aRule, aElement); + } + return false; + } + aRule.longStrings = longStrings; + } + + if ("objects" in aRule) { + let clickables = aElement.querySelectorAll(".hud-clickable"); + if (aRule.objects != !!clickables[0]) { + if (partialMatch) { + is(!!clickables[0], aRule.objects, + "objects existence check failed for message rule: " + + displayRule(aRule)); + displayErrorContext(aRule, aElement); + } + return false; + } + aRule.clickableElements = clickables; } let count = aRule.count || 1; @@ -936,6 +1095,16 @@ function waitForMessages(aOptions) function onMessagesAdded(aEvent, aNewElements) { for (let elem of aNewElements) { + let location = elem.querySelector(".webconsole-location"); + if (location) { + let url = location.getAttribute("title"); + // Prevent recursion with the browser console and any potential + // messages coming from head.js. + if (url.indexOf("browser/devtools/webconsole/test/head.js") != -1) { + continue; + } + } + for (let rule of rules) { if (rule._ruleMatched) { continue; @@ -989,6 +1158,13 @@ function waitForMessages(aOptions) return aRule.name || aRule.text; } + function displayErrorContext(aRule, aElement) + { + console.log("error occured during rule " + displayRule(aRule)); + console.log("while checking the following message"); + dumpMessageElement(aElement); + } + executeSoon(() => { onMessagesAdded("messages-added", webconsole.outputNode.childNodes); if (rulesMatched != rules.length) { @@ -1001,3 +1177,22 @@ function waitForMessages(aOptions) return deferred.promise; } + + +/** + * Scroll the Web Console output to the given node. + * + * @param nsIDOMNode aNode + * The node to scroll to. + */ +function scrollOutputToNode(aNode) +{ + let richListBoxNode = aNode.parentNode; + while (richListBoxNode.tagName != "richlistbox") { + richListBoxNode = richListBoxNode.parentNode; + } + + let boxObject = richListBoxNode.scrollBoxObject; + let nsIScrollBoxObject = boxObject.QueryInterface(Ci.nsIScrollBoxObject); + nsIScrollBoxObject.ensureElementIsVisible(aNode); +} diff --git a/content/media/omx/MediaOmxReader.cpp b/content/media/omx/MediaOmxReader.cpp index a5fd8200dad..311673f2137 100644 --- a/content/media/omx/MediaOmxReader.cpp +++ b/content/media/omx/MediaOmxReader.cpp @@ -331,5 +331,18 @@ nsresult MediaOmxReader::GetBuffered(mozilla::dom::TimeRanges* aBuffered, int64_ return NS_OK; } +void MediaOmxReader::OnDecodeThreadFinish() { + if (mOmxDecoder.get()) { + mOmxDecoder->Pause(); + } +} + +void MediaOmxReader::OnDecodeThreadStart() { + if (mOmxDecoder.get()) { + nsresult result = mOmxDecoder->Play(); + NS_ASSERTION(result == NS_OK, "OmxDecoder should be in play state to continue decoding"); + } +} + } // namespace mozilla diff --git a/content/media/omx/MediaOmxReader.h b/content/media/omx/MediaOmxReader.h index 0079b8ef107..4ced74430ea 100644 --- a/content/media/omx/MediaOmxReader.h +++ b/content/media/omx/MediaOmxReader.h @@ -57,6 +57,10 @@ public: MetadataTags** aTags); virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime); virtual nsresult GetBuffered(mozilla::dom::TimeRanges* aBuffered, int64_t aStartTime); + + virtual void OnDecodeThreadStart() MOZ_OVERRIDE; + + virtual void OnDecodeThreadFinish() MOZ_OVERRIDE; }; } // namespace mozilla diff --git a/content/media/omx/OmxDecoder.cpp b/content/media/omx/OmxDecoder.cpp index 31ad1e7c023..8b0ad58d53a 100644 --- a/content/media/omx/OmxDecoder.cpp +++ b/content/media/omx/OmxDecoder.cpp @@ -141,6 +141,7 @@ OmxDecoder::OmxDecoder(MediaResource *aResource, mVideoBuffer(nullptr), mAudioBuffer(nullptr), mIsVideoSeeking(false), + mPaused(false), mAudioMetadataRead(false) { } @@ -666,3 +667,32 @@ void OmxDecoder::ReleaseAllPendingVideoBuffersLocked() } mPendingVideoBuffers.clear(); } + +nsresult OmxDecoder::Play() { + if (!mPaused) { + return NS_OK; + } + if (mVideoSource.get() && mVideoSource->start() != OK) { + return NS_ERROR_UNEXPECTED; + } + + if (mAudioSource.get()&& mAudioSource->start() != OK) { + return NS_ERROR_UNEXPECTED; + } + mPaused = false; + return NS_OK; +} + +void OmxDecoder::Pause() { + if (mPaused) { + return; + } + if (mVideoSource.get()) { + mVideoSource->pause(); + } + + if (mAudioSource.get()) { + mAudioSource->pause(); + } + mPaused = true; +} diff --git a/content/media/omx/OmxDecoder.h b/content/media/omx/OmxDecoder.h index 3d6ed639eff..d5842cded99 100644 --- a/content/media/omx/OmxDecoder.h +++ b/content/media/omx/OmxDecoder.h @@ -132,6 +132,10 @@ class OmxDecoder : public RefBase { bool ToVideoFrame(VideoFrame *aFrame, int64_t aTimeUs, void *aData, size_t aSize, bool aKeyFrame); bool ToAudioFrame(AudioFrame *aFrame, int64_t aTimeUs, void *aData, size_t aDataOffset, size_t aSize, int32_t aAudioChannels, int32_t aAudioSampleRate); + + //True if decoder is in a paused state + bool mPaused; + public: OmxDecoder(MediaResource *aResource, AbstractMediaDecoder *aDecoder); ~OmxDecoder(); @@ -172,6 +176,12 @@ public: } bool ReleaseVideoBuffer(MediaBuffer *aBuffer); + + //Change decoder into a playing state + nsresult Play(); + + //Change decoder into a paused state + void Pause(); }; } diff --git a/dom/contacts/tests/test_contacts_basics.html b/dom/contacts/tests/test_contacts_basics.html index 2e3fd21dec6..8393efda273 100644 --- a/dom/contacts/tests/test_contacts_basics.html +++ b/dom/contacts/tests/test_contacts_basics.html @@ -1,17 +1,17 @@ - Test for Bug {674720} WebContacts + Test for Bug 674720 WebContacts -Mozilla Bug {674720} +Mozilla Bug 674720