author | David Anderson <danderson@mozilla.com> |
Fri, 20 Jan 2012 10:12:37 -0800 | |
changeset 105594 | 577a1c24f51942a2deeedaec7bd06125d5fc39f1 |
parent 105593 | 9711a0b8495f933f84d0be655874cba75a507e11 (current diff) |
parent 84955 | 49936b49aff3498fecf5b0c87684e135c19199f7 (diff) |
child 105595 | 7685f6b2f00d84c0e32bd85a730957958791c266 |
push id | 23447 |
push user | danderson@mozilla.com |
push date | Tue, 11 Sep 2012 17:34:27 +0000 |
treeherder | autoland@fdfaef738a00 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 12.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/accessible/src/mac/nsRoleMap.h +++ b/accessible/src/mac/nsRoleMap.h @@ -69,17 +69,17 @@ static const NSString* AXRoles [] = { NSAccessibilityGroupRole, // ROLE_TABLE NSAccessibilityGroupRole, // ROLE_COLUMNHEADER NSAccessibilityGroupRole, // ROLE_ROWHEADER NSAccessibilityColumnRole, // ROLE_COLUMN NSAccessibilityRowRole, // ROLE_ROW NSAccessibilityGroupRole, // ROLE_CELL @"AXLink", // ROLE_LINK. 10.4+ the attr first define in SDK 10.4, so we define it here too. ROLE_LINK @"AXHelpTag", // ROLE_HELPBALLOON - NSAccessibilityUnknownRole, // ROLE_CHARACTER. unusued on OS X + NSAccessibilityUnknownRole, // ROLE_CHARACTER. unused on OS X NSAccessibilityListRole, // ROLE_LIST NSAccessibilityRowRole, // ROLE_LISTITEM NSAccessibilityOutlineRole, // ROLE_OUTLINE NSAccessibilityRowRole, // ROLE_OUTLINEITEM. XXX: use OutlineRow as subrole. NSAccessibilityGroupRole, // ROLE_PAGETAB NSAccessibilityGroupRole, // ROLE_PROPERTYPAGE NSAccessibilityUnknownRole, // ROLE_INDICATOR NSAccessibilityImageRole, // ROLE_GRAPHIC
--- a/accessible/tests/mochitest/events.js +++ b/accessible/tests/mochitest/events.js @@ -26,16 +26,18 @@ const EVENT_TEXT_CARET_MOVED = nsIAccess const EVENT_TEXT_INSERTED = nsIAccessibleEvent.EVENT_TEXT_INSERTED; const EVENT_TEXT_REMOVED = nsIAccessibleEvent.EVENT_TEXT_REMOVED; const EVENT_TEXT_SELECTION_CHANGED = nsIAccessibleEvent.EVENT_TEXT_SELECTION_CHANGED; const EVENT_VALUE_CHANGE = nsIAccessibleEvent.EVENT_VALUE_CHANGE; //////////////////////////////////////////////////////////////////////////////// // General +Components.utils.import("resource://gre/modules/Services.jsm"); + /** * Set up this variable to dump events into DOM. */ var gA11yEventDumpID = ""; /** * Set up this variable to dump event processing into console. */ @@ -1409,34 +1411,28 @@ function expandedStateChecker(aIsEnabled //////////////////////////////////////////////////////////////////////////////// // General var gA11yEventListeners = {}; var gA11yEventApplicantsCount = 0; var gA11yEventObserver = { - // The service reference needs to live in the observer, instead of as a global var, - // to be available in observe() catch case too. - observerService : - Components.classes["@mozilla.org/observer-service;1"] - .getService(nsIObserverService), - observe: function observe(aSubject, aTopic, aData) { if (aTopic != "accessible-event") return; var event; try { event = aSubject.QueryInterface(nsIAccessibleEvent); } catch (ex) { // After a test is aborted (i.e. timed out by the harness), this exception is soon triggered. // Remove the leftover observer, otherwise it "leaks" to all the following tests. - this.observerService.removeObserver(this, "accessible-event"); + Services.obs.removeObserver(this, "accessible-event"); // Forward the exception, with added explanation. throw "[accessible/events.js, gA11yEventObserver.observe] This is expected if a previous test has been aborted... Initial exception was: [ " + ex + " ]"; } var listenersArray = gA11yEventListeners[event.eventType]; var eventFromDumpArea = false; if (gLogger.isEnabled()) { // debug stuff eventFromDumpArea = true; @@ -1480,24 +1476,22 @@ var gA11yEventObserver = } }; function listenA11yEvents(aStartToListen) { if (aStartToListen) { // Add observer when adding the first applicant only. if (!(gA11yEventApplicantsCount++)) - gA11yEventObserver.observerService - .addObserver(gA11yEventObserver, "accessible-event", false); + Services.obs.addObserver(gA11yEventObserver, "accessible-event", false); } else { // Remove observer when there are no more applicants only. // '< 0' case should not happen, but just in case: removeObserver() will throw. if (--gA11yEventApplicantsCount <= 0) - gA11yEventObserver.observerService - .removeObserver(gA11yEventObserver, "accessible-event"); + Services.obs.removeObserver(gA11yEventObserver, "accessible-event"); } } function addA11yEventListener(aEventType, aEventHandler) { if (!(aEventType in gA11yEventListeners)) gA11yEventListeners[aEventType] = new Array(); @@ -1604,21 +1598,18 @@ var gLogger = }, /** * Log message to error console. */ logToAppConsole: function logger_logToAppConsole(aMsg) { if (gA11yEventDumpToAppConsole) - consoleService.logStringMessage("events: " + aMsg); - }, - - consoleService: Components.classes["@mozilla.org/consoleservice;1"]. - getService(Components.interfaces.nsIConsoleService) + Services.console.logStringMessage("events: " + aMsg); + } }; //////////////////////////////////////////////////////////////////////////////// // Sequence /** * Base class of sequence item.
--- a/accessible/tests/mochitest/events/test_focus_autocomplete.xul +++ b/accessible/tests/mochitest/events/test_focus_autocomplete.xul @@ -1,15 +1,19 @@ <?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin" type="text/css"?> <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- Firefox searchbar --> <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?> +<!-- SeaMonkey searchbar --> +<?xml-stylesheet href="chrome://navigator/content/navigator.css" + type="text/css"?> <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title="Accessible focus event testing"> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
--- a/accessible/tests/mochitest/focus/test_takeFocus.xul +++ b/accessible/tests/mochitest/focus/test_takeFocus.xul @@ -1,16 +1,13 @@ <?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin" type="text/css"?> <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> -<?xml-stylesheet href="chrome://browser/content/browser.css" - type="text/css"?> - <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title="Accessible focus testing"> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
--- a/accessible/tests/mochitest/states/test_expandable.xul +++ b/accessible/tests/mochitest/states/test_expandable.xul @@ -1,14 +1,19 @@ <?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin" type="text/css"?> <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- Firefox searchbar --> <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?> +<!-- SeaMonkey searchbar --> +<?xml-stylesheet href="chrome://navigator/content/navigator.css" + type="text/css"?> <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title="Expanded state change events tests for comboboxes and autocompletes."> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1036,16 +1036,17 @@ pref("devtools.tilt.enabled", true); // Enable the rules view pref("devtools.ruleview.enabled", true); // Enable the Scratchpad tool. pref("devtools.scratchpad.enabled", true); // Enable the Style Editor. pref("devtools.styleeditor.enabled", true); +pref("devtools.styleeditor.transitions", true); // Enable tools for Chrome development. pref("devtools.chrome.enabled", false); // Disable the GCLI enhanced command line. pref("devtools.gcli.enable", true); // The last Web Console height. This is initially 0 which means that the Web
--- a/browser/base/content/test/browser_bug416661.js +++ b/browser/base/content/test/browser_bug416661.js @@ -38,17 +38,18 @@ function test() { gBrowser.selectedTab = tabElm; afterZoomAndLoad(start_test_prefNotSet); content.location = "http://mochi.test:8888/browser/browser/base/content/test/zoom_test.html"; } function afterZoomAndLoad(cb) { - let didLoad = didZoom = false; + let didLoad = false; + let didZoom = false; tabElm.linkedBrowser.addEventListener("load", function() { tabElm.linkedBrowser.removeEventListener("load", arguments.callee, true); didLoad = true; if (didZoom) executeSoon(cb); }, true); let oldSZFB = ZoomManager.setZoomForBrowser; ZoomManager.setZoomForBrowser = function(browser, value) {
--- a/browser/base/content/test/browser_bug555224.js +++ b/browser/base/content/test/browser_bug555224.js @@ -32,17 +32,18 @@ * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ const TEST_PAGE = "/browser/browser/base/content/test/dummy_page.html"; var gTestTab, gBgTab, gTestZoom; function afterZoomAndLoad(aCallback, aTab) { - let didLoad = didZoom = false; + let didLoad = false; + let didZoom = false; aTab.linkedBrowser.addEventListener("load", function() { aTab.linkedBrowser.removeEventListener("load", arguments.callee, true); didLoad = true; if (didZoom) executeSoon(aCallback); }, true); let oldAPTS = FullZoom._applyPrefToSetting; FullZoom._applyPrefToSetting = function(value, browser) {
--- a/browser/base/content/test/browser_bug592338.js +++ b/browser/base/content/test/browser_bug592338.js @@ -1,15 +1,17 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ const TESTROOT = "http://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/"; -Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm"); +var tempScope = {}; +Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", tempScope); +var LightweightThemeManager = tempScope.LightweightThemeManager; function wait_for_notification(aCallback) { PopupNotifications.panel.addEventListener("popupshown", function() { PopupNotifications.panel.removeEventListener("popupshown", arguments.callee, false); aCallback(PopupNotifications.panel); }, false); }
--- a/browser/base/content/test/browser_bug623155.js +++ b/browser/base/content/test/browser_bug623155.js @@ -38,16 +38,18 @@ 6. The redirected URI is <https://www.ba a cert-error page. 7. Check the URLbar's value, expecting <https://www.bank1.com/#FG> 8. End. */ +var gNewTab; + function test() { waitForExplicitFinish(); // Load a URI in the background. gNewTab = gBrowser.addTab(REDIRECT_FROM + "#BG"); gBrowser.getBrowserForTab(gNewTab) .webProgress .addProgressListener(gWebProgressListener,
--- a/browser/base/content/test/browser_gestureSupport.js +++ b/browser/base/content/test/browser_gestureSupport.js @@ -378,17 +378,17 @@ function test_latchedGesture(gesture, in }; // Test the gestures in each direction. test_emitLatchedEvents(eventPrefix, 500, cmd); test_emitLatchedEvents(eventPrefix, -500, cmd); // Restore the gesture to its original configuration. gPrefService.setBoolPref(branch + "latched", oldLatchedValue); - for (dir in cmd) + for (let dir in cmd) test_removeCommand(cmd[dir]); } // Test whether non-latched events are triggered upon sufficient motion. function test_thresholdGesture(gesture, inc, dec, eventPrefix) { let branch = test_prefBranch + gesture + ".";
--- a/browser/base/content/test/browser_identity_UI.js +++ b/browser/base/content/test/browser_identity_UI.js @@ -63,17 +63,17 @@ var tests = [ { name: "IP address", location: "http://127.0.0.1:8888/", host: "127.0.0.1:8888", effectiveHost: "127.0.0.1" }, ] -let gCurrentTest, gCurrentTestIndex = -1; +let gCurrentTest, gCurrentTestIndex = -1, gTestDesc; // Go through the tests in both directions, to add additional coverage for // transitions between different states. let gForward = true; function nextTest() { if (gForward) gCurrentTestIndex++; else gCurrentTestIndex--;
--- a/browser/devtools/styleeditor/StyleEditor.jsm +++ b/browser/devtools/styleeditor/StyleEditor.jsm @@ -54,16 +54,33 @@ const SAVE_ERROR = "error-save"; // max update frequency in ms (avoid potential typing lag and/or flicker) // @see StyleEditor.updateStylesheet const UPDATE_STYLESHEET_THROTTLE_DELAY = 500; // @see StyleEditor._persistExpando const STYLESHEET_EXPANDO = "-moz-styleeditor-stylesheet-"; +const TRANSITIONS_PREF = "devtools.styleeditor.transitions"; + +const TRANSITION_CLASS = "moz-styleeditor-transitioning"; +const TRANSITION_DURATION_MS = 500; +const TRANSITION_RULE = "\ +:root.moz-styleeditor-transitioning, :root.moz-styleeditor-transitioning * {\ +-moz-transition-duration: " + TRANSITION_DURATION_MS + "ms !important; \ +-moz-transition-delay: 0ms !important;\ +-moz-transition-timing-function: ease-out !important;\ +-moz-transition-property: all !important;\ +}"; + +/** + * Style Editor module-global preferences + */ +const TRANSITIONS_ENABLED = Services.prefs.getBoolPref(TRANSITIONS_PREF); + /** * StyleEditor constructor. * * The StyleEditor is initialized 'headless', it does not display source * or receive input. Setting inputElement attaches a DOMElement to handle this. * * An editor can be created stand-alone or created by StyleEditorChrome to @@ -102,16 +119,19 @@ function StyleEditor(aDocument, aStyleSh this._errorMessage = null; // @see errorMessage // listeners for significant editor actions. @see addActionListener this._actionListeners = []; // this is to perform pending updates before editor closing this._onWindowUnloadBinding = this._onWindowUnload.bind(this); + + this._transitionRefCount = 0; + this._focusOnSourceEditorReady = false; } StyleEditor.prototype = { /** * Retrieve the content document this editor will apply changes to. * * @return DOMDocument @@ -400,18 +420,23 @@ StyleEditor.prototype = { * onAttach: Called when an input element has been attached. * Arguments: (StyleEditor editor) * @see inputElement * * onDetach: Called when input element has been detached. * Arguments: (StyleEditor editor) * @see inputElement * - * onCommit: Called when changes have been committed/applied - * to the live DOM style sheet. + * onUpdate: Called when changes are being applied to the live + * DOM style sheet but might not be complete from + * a WYSIWYG perspective (eg. transitioned update). + * Arguments: (StyleEditor editor) + * + * onCommit: Called when changes have been completely committed + * /applied to the live DOM style sheet. * Arguments: (StyleEditor editor) * } * * All listener methods are optional. * * @param IStyleEditorActionListener aListener * @see removeActionListener */ @@ -635,25 +660,60 @@ StyleEditor.prototype = { // when it is enabled back. @see enableStylesheet if (this.sourceEditor) { this._state.text = this.sourceEditor.getText(); } let source = this._state.text; let oldNode = this.styleSheet.ownerNode; let oldIndex = this.styleSheetIndex; - - let newNode = this.contentDocument.createElement("style"); + let content = this.contentDocument; + let newNode = content.createElement("style"); newNode.setAttribute("type", "text/css"); - newNode.appendChild(this.contentDocument.createTextNode(source)); + newNode.appendChild(content.createTextNode(source)); oldNode.parentNode.replaceChild(newNode, oldNode); - this._styleSheet = this.contentDocument.styleSheets[oldIndex]; + this._styleSheet = content.styleSheets[oldIndex]; this._persistExpando(); + if (!TRANSITIONS_ENABLED) { + this._triggerAction("Update"); + this._triggerAction("Commit"); + return; + } + + // Insert the global transition rule + // Use a ref count to make sure we do not add it multiple times.. and remove + // it only when all pending StyleEditor-generated transitions ended. + if (!this._transitionRefCount) { + this._styleSheet.insertRule(TRANSITION_RULE, 0); + content.documentElement.classList.add(TRANSITION_CLASS); + } + + this._transitionRefCount++; + + // Set up clean up and commit after transition duration (+10% buffer) + // @see _onTransitionEnd + content.defaultView.setTimeout(this._onTransitionEnd.bind(this), + Math.floor(TRANSITION_DURATION_MS * 1.1)); + + this._triggerAction("Update"); + }, + + /** + * This cleans up class and rule added for transition effect and then trigger + * Commit as the changes have been completed. + */ + _onTransitionEnd: function SE__onTransitionEnd() + { + if (--this._transitionRefCount == 0) { + this.contentDocument.documentElement.classList.remove(TRANSITION_CLASS); + this.styleSheet.deleteRule(0); + } + this._triggerAction("Commit"); }, /** * Show file picker and return the file user selected. * * @param mixed aFile * Optional nsIFile or string representing the filename to auto-select.
--- a/browser/devtools/styleeditor/StyleEditorChrome.jsm +++ b/browser/devtools/styleeditor/StyleEditorChrome.jsm @@ -151,21 +151,24 @@ StyleEditorChrome.prototype = { aContentWindow.removeEventListener("unload", onContentUnload, false); if (this.contentWindow == aContentWindow) { this.contentWindow = null; // detach } }.bind(this); aContentWindow.addEventListener("unload", onContentUnload, false); if (aContentWindow.document.readyState == "complete") { + this._root.classList.remove("loading"); this._populateChrome(); return; } else { + this._root.classList.add("loading"); let onContentReady = function () { aContentWindow.removeEventListener("load", onContentReady, false); + this._root.classList.remove("loading"); this._populateChrome(); }.bind(this); aContentWindow.addEventListener("load", onContentReady, false); } }, /** * Retrieve the content document attached to this chrome. @@ -294,26 +297,32 @@ StyleEditorChrome.prototype = { let editor = new StyleEditor(this.contentDocument); this._editors.push(editor); editor.addActionListener(this); editor.importFromFile(this._mockImportFile || null, this._window); }.bind(this)); }, /** - * Reset the chrome UI to an empty state. + * Reset the chrome UI to an empty and ready state. */ _resetChrome: function SEC__resetChrome() { this._editors.forEach(function (aEditor) { aEditor.removeActionListener(this); }.bind(this)); this._editors = []; this._view.removeAll(); + + // (re)enable UI + let matches = this._root.querySelectorAll("toolbarbutton,input,select"); + for (let i = 0; i < matches.length; ++i) { + matches[i].removeAttribute("disabled"); + } }, /** * Populate the chrome UI according to the content document. * * @see StyleEditor._setupShadowStyleSheet */ _populateChrome: function SEC__populateChrome()
--- a/browser/devtools/styleeditor/splitview.css +++ b/browser/devtools/styleeditor/splitview.css @@ -41,16 +41,20 @@ box, -moz-box-flex: 1; -moz-box-orient: vertical; } .splitview-nav-container { -moz-box-pack: center; } +.loading .splitview-nav-container > .placeholder { + display: none !important; +} + .splitview-controller, .splitview-main { -moz-box-flex: 0; } .splitview-controller { min-height: 3em; max-height: 14em;
--- a/browser/devtools/styleeditor/styleeditor.xul +++ b/browser/devtools/styleeditor/styleeditor.xul @@ -49,28 +49,30 @@ xmlns="http://www.w3.org/1999/xhtml" id="style-editor-chrome-window" title="&window.title;" windowtype="Tools:StyleEditor" width="800" height="280" persist="screenX screenY width height sizemode"> <xul:script type="application/javascript" src="chrome://global/content/globalOverlay.js"/> -<xul:box id="style-editor-chrome" class="splitview-root"> +<xul:box id="style-editor-chrome" class="splitview-root loading"> <xul:box class="splitview-controller" id="stylesheets-controller" persist="width height"> <xul:box class="splitview-main"> <xul:toolbar class="devtools-toolbar"> <xul:toolbarbutton class="style-editor-newButton devtools-toolbarbutton" accesskey="&newButton.accesskey;" tooltiptext="&newButton.tooltip;" - label="&newButton.label;"/> + label="&newButton.label;" + disabled="true"/> <xul:toolbarbutton class="style-editor-importButton devtools-toolbarbutton" accesskey="&importButton.accesskey;" tooltiptext="&importButton.tooltip;" - label="&importButton.label;"/> + label="&importButton.label;" + disabled="true"/> <xul:spacer flex="1"/> <xul:textbox class="splitview-filter devtools-searchinput" type="search" flex="1" tooltiptext="&searchInput.tooltip;" placeholder="&searchInput.placeholder;"/> </xul:toolbar> </xul:box> <xul:box class="splitview-nav-container">
--- a/browser/devtools/styleeditor/test/browser_styleeditor_loading.js +++ b/browser/devtools/styleeditor/test/browser_styleeditor_loading.js @@ -13,16 +13,28 @@ function test() // launch Style Editor right when the tab is created (before load) // this checks that the Style Editor still launches correctly when it is opened // *while* the page is still loading launchStyleEditorChrome(function (aChrome) { isnot(gBrowser.selectedBrowser.contentWindow.document.readyState, "complete", "content document is still loading"); + let root = gChromeWindow.document.querySelector(".splitview-root"); + ok(root.classList.contains("loading"), + "style editor root element has 'loading' class name"); + + let button = gChromeWindow.document.querySelector(".style-editor-newButton"); + ok(button.hasAttribute("disabled"), + "new style sheet button is disabled"); + + button = gChromeWindow.document.querySelector(".style-editor-importButton"); + ok(button.hasAttribute("disabled"), + "import button is disabled"); + if (!aChrome.isContentAttached) { aChrome.addChromeListener({ onContentAttach: run }); } else { run(aChrome); } }); @@ -30,10 +42,22 @@ function test() content.location = TESTCASE_URI; } function run(aChrome) { is(aChrome.contentWindow.document.readyState, "complete", "content document is complete"); + let root = gChromeWindow.document.querySelector(".splitview-root"); + ok(!root.classList.contains("loading"), + "style editor root element does not have 'loading' class name anymore"); + + let button = gChromeWindow.document.querySelector(".style-editor-newButton"); + ok(!button.hasAttribute("disabled"), + "new style sheet button is enabled"); + + button = gChromeWindow.document.querySelector(".style-editor-importButton"); + ok(!button.hasAttribute("disabled"), + "import button is enabled"); + finish(); }
--- a/browser/devtools/styleeditor/test/browser_styleeditor_new.js +++ b/browser/devtools/styleeditor/test/browser_styleeditor_new.js @@ -1,14 +1,16 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ const TESTCASE_URI = TEST_BASE + "simple.html"; +const TRANSITION_CLASS = "moz-styleeditor-transitioning"; + function test() { waitForExplicitFinish(); addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) { aChrome.addChromeListener({ onContentAttach: run, @@ -25,16 +27,17 @@ function test() function run(aChrome) { is(aChrome.editors.length, 2, "there is 2 stylesheets initially"); } let gAddedCount = 0; // to add new stylesheet after the 2 initial stylesheets let gNewEditor; // to make sure only one new stylesheet got created +let gUpdateCount = 0; // to make sure only one Update event is triggered let gCommitCount = 0; // to make sure only one Commit event is triggered function testEditorAdded(aChrome, aEditor) { gAddedCount++; if (gAddedCount == 2) { waitForFocus(function () { // create a new style sheet let newButton = gChromeWindow.document.querySelector(".style-editor-newButton"); @@ -77,16 +80,23 @@ function testEditorAdded(aChrome, aEdito "content's background color is initially white"); for each (let c in "body{background-color:red;}") { EventUtils.synthesizeKey(c, {}, gChromeWindow); } }, gChromeWindow) ; }, + onUpdate: function (aEditor) { + gUpdateCount++; + + ok(content.document.documentElement.classList.contains(TRANSITION_CLASS), + "StyleEditor's transition class has been added to content"); + }, + onCommit: function (aEditor) { gCommitCount++; ok(aEditor.hasFlag("new"), "new editor still has NEW flag"); ok(aEditor.hasFlag("unsaved"), "new editor has UNSAVED flag after modification"); @@ -94,17 +104,21 @@ function testEditorAdded(aChrome, aEdito let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent; is(parseInt(ruleCount), 1, "new editor shows 1 rule after modification"); let computedStyle = content.getComputedStyle(content.document.body, null); is(computedStyle.backgroundColor, "rgb(255, 0, 0)", "content's background color has been updated to red"); + ok(!content.document.documentElement.classList.contains(TRANSITION_CLASS), + "StyleEditor's transition class has been removed from content"); + executeSoon(function () { + is(gUpdateCount, 1, "received only one Update event (throttle)"); is(gCommitCount, 1, "received only one Commit event (throttle)"); aEditor.removeActionListener(listener); gNewEditor = null; finish(); }); }
--- a/browser/devtools/webconsole/test/browser_warn_user_about_replaced_api.js +++ b/browser/devtools/webconsole/test/browser_warn_user_about_replaced_api.js @@ -32,17 +32,17 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -const TEST_REPLACED_API_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console-replaced-api.html"; +const TEST_REPLACED_API_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-replaced-api.html"; function test() { waitForExplicitFinish(); // First test that the warning does not appear on a normal page (about:blank) addTab("about:blank"); browser.addEventListener("load", function() { browser.removeEventListener("load", arguments.callee, true);
--- a/browser/devtools/webconsole/test/browser_webconsole_basic_net_logging.js +++ b/browser/devtools/webconsole/test/browser_webconsole_basic_net_logging.js @@ -36,17 +36,17 @@ * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // Tests that the page's resources are displayed in the console as they're // loaded -const TEST_NETWORK_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-network.html" + "?_date=" + Date.now(); +const TEST_NETWORK_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-network.html" + "?_date=" + Date.now(); function test() { addTab("data:text/html,Web Console basic network logging test"); browser.addEventListener("load", onLoad, true); } function onLoad(aEvent) { browser.removeEventListener(aEvent.type, arguments.callee, true);
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_578437_page_reload.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_578437_page_reload.js @@ -34,17 +34,17 @@ * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // Tests that the console object still exists after a page reload. -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", onLoad, false); } function onLoad() { browser.removeEventListener("DOMContentLoaded", onLoad, false);
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_580030_errors_after_page_reload.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_580030_errors_after_page_reload.js @@ -35,17 +35,17 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // Tests that errors still show up in the Web Console after a page reload. -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-error.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-error.html"; function test() { expectUncaughtException(); addTab(TEST_URI); browser.addEventListener("load", onLoad, true); } // see bug 580030: the error handler fails silently after page reload.
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js @@ -7,17 +7,17 @@ * Patrick Walton <pcwalton@mozilla.com> * * ***** END LICENSE BLOCK ***** */ // Tests that appropriately-localized timestamps are printed. Cu.import("resource:///modules/HUDService.jsm"); -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", testTimestamp, false); function testTimestamp() { browser.removeEventListener("DOMContentLoaded", testTimestamp, false);
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_581231_close_button.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_581231_close_button.js @@ -5,17 +5,17 @@ * * Contributor(s): * Patrick Walton <pcwalton@mozilla.com> * * ***** END LICENSE BLOCK ***** */ // Tests that the Web Console close button functions. -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", testCloseButton, false); } function testCloseButton() { browser.removeEventListener("DOMContentLoaded", testCloseButton, false);
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_585237_line_limit.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_585237_line_limit.js @@ -7,17 +7,17 @@ * Patrick Walton <pcwalton@mozilla.com> * Mihai Șucan <mihai.sucan@gmail.com> * * ***** END LICENSE BLOCK ***** */ // Tests that the Web Console limits the number of lines displayed according to // the user's preferences. -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", testLineLimit, false); } function testLineLimit() {
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_585956_console_trace.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_585956_console_trace.js @@ -31,17 +31,17 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-585956-console-trace.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-585956-console-trace.html"; function test() { addTab(TEST_URI); browser.addEventListener("load", tabLoaded, true); } function tabLoaded() { browser.removeEventListener("load", tabLoaded, true);
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_588730_text_node_insertion.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_588730_text_node_insertion.js @@ -35,17 +35,17 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // Tests that adding text to one of the output labels doesn't cause errors. -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", testTextNodeInsertion, false); } // Test for bug 588730: Adding a text node to an existing label element causes
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_593003_iframe_wrong_hud.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_593003_iframe_wrong_hud.js @@ -31,21 +31,21 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-593003-iframe-wrong-hud.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-593003-iframe-wrong-hud.html"; -const TEST_IFRAME_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-593003-iframe-wrong-hud-iframe.html"; +const TEST_IFRAME_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-593003-iframe-wrong-hud-iframe.html"; -const TEST_DUMMY_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html"; +const TEST_DUMMY_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; let tab1, tab2; function test() { addTab(TEST_URI); tab1 = tab; browser.addEventListener("load", tab1Loaded, true); }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js @@ -3,17 +3,17 @@ * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * Contributor(s): * Mihai Șucan <mihai.sucan@gmail.com> * * ***** END LICENSE BLOCK ***** */ -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; let tab1, tab2, win1, win2; let noErrors = true; function tab1Loaded(aEvent) { browser.removeEventListener(aEvent.type, arguments.callee, true); win2 = OpenBrowserWindow();
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_597460_filter_scroll.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_597460_filter_scroll.js @@ -3,17 +3,17 @@ * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * Contributor(s): * Mihai Șucan <mihai.sucan@gmail.com> * * ***** END LICENSE BLOCK ***** */ -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-network.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-network.html"; function tabLoad(aEvent) { browser.removeEventListener(aEvent.type, arguments.callee, true); openConsole(); let hudId = HUDService.getHudIdByWindow(content); hud = HUDService.hudReferences[hudId];
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_597756_reopen_closed_tab.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_597756_reopen_closed_tab.js @@ -3,17 +3,17 @@ * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * Contributor(s): * Mihai Șucan <mihai.sucan@gmail.com> * * ***** END LICENSE BLOCK ***** */ -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-597756-reopen-closed-tab.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-597756-reopen-closed-tab.html"; let newTabIsOpen = false; function tabLoaded(aEvent) { gBrowser.selectedBrowser.removeEventListener(aEvent.type, arguments.callee, true); HUDService.activateHUDForContext(gBrowser.selectedTab);
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_599725_response_headers.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_599725_response_headers.js @@ -3,17 +3,17 @@ * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * Contributor(s): * Mihai Șucan <mihai.sucan@gmail.com> * * ***** END LICENSE BLOCK ***** */ -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-599725-response-headers.sjs"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-599725-response-headers.sjs"; let lastFinishedRequest = null; function requestDoneCallback(aHttpRequest) { lastFinishedRequest = aHttpRequest; }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_600183_charset.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_600183_charset.js @@ -3,17 +3,17 @@ * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * Contributor(s): * Mihai Șucan <mihai.sucan@gmail.com> * * ***** END LICENSE BLOCK ***** */ -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-600183-charset.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-600183-charset.html"; let lastFinishedRequest = null; function requestDoneCallback(aHttpRequest) { lastFinishedRequest = aHttpRequest; }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_601177_log_levels.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_601177_log_levels.js @@ -3,17 +3,17 @@ * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * Contributor(s): * Mihai Șucan <mihai.sucan@gmail.com> * * ***** END LICENSE BLOCK ***** */ -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-601177-log-levels.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-601177-log-levels.html"; let msgs; function onContentLoaded() { let hudId = HUDService.getHudIdByWindow(content); let HUD = HUDService.hudReferences[hudId]; msgs = HUD.outputNode.querySelectorAll(".hud-msg-node");
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_603750_websocket.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_603750_websocket.js @@ -3,17 +3,17 @@ * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * Contributor(s): * Mihai Șucan <mihai.sucan@gmail.com> * * ***** END LICENSE BLOCK ***** */ -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-603750-websocket.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-603750-websocket.html"; const pref_ws = "network.websocket.enabled"; const pref_block = "network.websocket.override-security-block"; let errors = 0; let lastWindowId = 0; let oldPref_ws; let TestObserver = {
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_613013_console_api_iframe.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_613013_console_api_iframe.js @@ -3,17 +3,17 @@ * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * Contributor(s): * Mihai Șucan <mihai.sucan@gmail.com> * * ***** END LICENSE BLOCK ***** */ -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-613013-console-api-iframe.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-613013-console-api-iframe.html"; let TestObserver = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), observe: function test_observe(aMessage, aTopic, aData) { if (aTopic == "console-api-log-event") { executeSoon(performTest);
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_630733_response_redirect_headers.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_630733_response_redirect_headers.js @@ -2,17 +2,17 @@ /* * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * Contributor(s): * Mihai Sucan <mihai.sucan@gmail.com> */ -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-bug-630733-response-redirect-headers.sjs"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-630733-response-redirect-headers.sjs"; let lastFinishedRequests = {}; function requestDoneCallback(aHttpRequest) { let status = aHttpRequest.response.status. replace(/^HTTP\/\d\.\d (\d+).+$/, "$1"); lastFinishedRequests[status] = aHttpRequest;
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_632817.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_632817.js @@ -1,17 +1,17 @@ /* vim:set ts=2 sw=2 sts=2 et: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ // Tests that network log messages bring up the network panel. -const TEST_NETWORK_REQUEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-network-request.html"; +const TEST_NETWORK_REQUEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-network-request.html"; -const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/test//test-image.png"; +const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/test/test-image.png"; const TEST_DATA_JSON_CONTENT = '{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }'; let lastRequest = null; let requestCallback = null; function test()
--- a/browser/devtools/webconsole/test/browser_webconsole_console_extras.js +++ b/browser/devtools/webconsole/test/browser_webconsole_console_extras.js @@ -32,17 +32,17 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // Tests that the basic console.log()-style APIs and filtering work. -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console-extras.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-extras.html"; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", onLoad, false); } function onLoad() { browser.removeEventListener("DOMContentLoaded", onLoad, false);
--- a/browser/devtools/webconsole/test/browser_webconsole_live_filtering_of_message_types.js +++ b/browser/devtools/webconsole/test/browser_webconsole_live_filtering_of_message_types.js @@ -35,17 +35,17 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // Tests that the message type filter checkboxes work. -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", testLiveFilteringOfMessageTypes, false); } function testLiveFilteringOfMessageTypes() {
--- a/browser/devtools/webconsole/test/browser_webconsole_live_filtering_on_search_strings.js +++ b/browser/devtools/webconsole/test/browser_webconsole_live_filtering_on_search_strings.js @@ -35,17 +35,17 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // Tests that the text filter box works. -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", testLiveFilteringOnSearchStrings, false); } function testLiveFilteringOnSearchStrings() {
--- a/browser/devtools/webconsole/test/browser_webconsole_log_node_classes.js +++ b/browser/devtools/webconsole/test/browser_webconsole_log_node_classes.js @@ -36,17 +36,17 @@ * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // Tests that console logging via the console API produces nodes of the correct // CSS classes. -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", testLogNodeClasses, false); } function testLogNodeClasses() { browser.removeEventListener("DOMContentLoaded", testLogNodeClasses,
--- a/browser/devtools/webconsole/test/browser_webconsole_message_node_id.js +++ b/browser/devtools/webconsole/test/browser_webconsole_message_node_id.js @@ -30,17 +30,17 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", onLoad, false); } function onLoad() { browser.removeEventListener("DOMContentLoaded", onLoad, false);
--- a/browser/devtools/webconsole/test/browser_webconsole_netlogging.js +++ b/browser/devtools/webconsole/test/browser_webconsole_netlogging.js @@ -7,19 +7,19 @@ * Julian Viereck <jviereck@mozilla.com> * Patrick Walton <pcwalton@mozilla.com> * Mihai Șucan <mihai.sucan@gmail.com> * * ***** END LICENSE BLOCK ***** */ // Tests that network log messages bring up the network panel. -const TEST_NETWORK_REQUEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-network-request.html"; +const TEST_NETWORK_REQUEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-network-request.html"; -const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/test//test-image.png"; +const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/test/test-image.png"; const TEST_DATA_JSON_CONTENT = '{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }'; let lastRequest = null; let requestCallback = null; function test()
--- a/browser/devtools/webconsole/test/browser_webconsole_network_panel.js +++ b/browser/devtools/webconsole/test/browser_webconsole_network_panel.js @@ -36,19 +36,19 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // Tests that the network panel works. -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html"; -const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/test//test-image.png"; -const TEST_ENCODING_ISO_8859_1 = "http://example.com/browser/browser/devtools/webconsole/test//test-encoding-ISO-8859-1.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; +const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/test/test-image.png"; +const TEST_ENCODING_ISO_8859_1 = "http://example.com/browser/browser/devtools/webconsole/test/test-encoding-ISO-8859-1.html"; let testDriver; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", testNetworkPanel, false); }
--- a/browser/devtools/webconsole/test/browser_webconsole_registries.js +++ b/browser/devtools/webconsole/test/browser_webconsole_registries.js @@ -36,17 +36,17 @@ * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ // Tests that the HUD service keeps an accurate registry of all the Web Console // instances. -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-console.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html"; function test() { addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", testRegistries, false); } function testRegistries() { browser.removeEventListener("DOMContentLoaded", testRegistries, false);
--- a/browser/devtools/webconsole/test/browser_webconsole_view_source.js +++ b/browser/devtools/webconsole/test/browser_webconsole_view_source.js @@ -1,15 +1,15 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ // Tests that source URLs in the Web Console can be clicked to display the // standard View Source window. -const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//test-error.html"; +const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-error.html"; function test() { expectUncaughtException(); addTab(TEST_URI); browser.addEventListener("DOMContentLoaded", testViewSource, false); } function testViewSource() {
--- a/browser/devtools/webconsole/test/test-bug-593003-iframe-wrong-hud.html +++ b/browser/devtools/webconsole/test/test-bug-593003-iframe-wrong-hud.html @@ -3,11 +3,11 @@ <head> <title>WebConsole test: iframe associated to the wrong HUD</title> <!-- Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ --> </head> <body> <p>WebConsole test: iframe associated to the wrong HUD.</p> <iframe - src="http://example.com/browser/browser/devtools/webconsole/test//test-bug-593003-iframe-wrong-hud-iframe.html"></iframe> + src="http://example.com/browser/browser/devtools/webconsole/test/test-bug-593003-iframe-wrong-hud-iframe.html"></iframe> </body> </html>
--- a/browser/devtools/webconsole/test/test-network-request.html +++ b/browser/devtools/webconsole/test/test-network-request.html @@ -22,14 +22,14 @@ makeXhr('post', 'test-data.json', "Hello world!", aCallback); } // --></script> </head> <body> <h1>Heads Up Display HTTP Logging Testpage</h1> <h2>This page is used to test the HTTP logging.</h2> - <form action="http://example.com/browser/browser/devtools/webconsole/test//test-network-request.html" method="post"> + <form action="http://example.com/browser/browser/devtools/webconsole/test/test-network-request.html" method="post"> <input name="name" type="text" value="foo bar"><br> <input name="age" type="text" value="144"><br> </form> </body> </html>
--- a/browser/installer/Makefile.in +++ b/browser/installer/Makefile.in @@ -87,16 +87,20 @@ endif include $(topsrcdir)/ipc/app/defs.mk DEFINES += -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME) ifneq (,$(filter aurora beta,$(MOZ_UPDATE_CHANNEL))) DEFINES += -DSHIP_FEEDBACK=1 endif +ifneq (,$(filter WINNT Darwin Android,$(OS_TARGET))) +DEFINES += -DMOZ_SHARED_MOZGLUE=1 +endif + ifdef MOZ_PKG_MANIFEST_P MOZ_PKG_MANIFEST = package-manifest $(MOZ_PKG_MANIFEST): $(MOZ_PKG_MANIFEST_P) $(GLOBAL_DEPS) $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $< > $@ GARBAGE += $(MOZ_PKG_MANIFEST) endif
--- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -44,17 +44,19 @@ #ifndef MOZ_STATIC_JS @BINPATH@/@DLL_PREFIX@mozjs@DLL_SUFFIX@ #endif @BINPATH@/@DLL_PREFIX@plc4@DLL_SUFFIX@ @BINPATH@/@DLL_PREFIX@plds4@DLL_SUFFIX@ @BINPATH@/@DLL_PREFIX@xpcom@DLL_SUFFIX@ @BINPATH@/@DLL_PREFIX@nspr4@DLL_SUFFIX@ @BINPATH@/@DLL_PREFIX@mozalloc@DLL_SUFFIX@ +#ifdef MOZ_SHARED_MOZGLUE @BINPATH@/@DLL_PREFIX@mozglue@DLL_SUFFIX@ +#endif #ifdef XP_MACOSX @BINPATH@/XUL #else @BINPATH@/@DLL_PREFIX@xul@DLL_SUFFIX@ #endif #ifdef XP_MACOSX @BINPATH@/@MOZ_CHILD_PROCESS_NAME@.app/ @BINPATH@/@DLL_PREFIX@plugin_child_interpose@DLL_SUFFIX@
--- a/browser/installer/removed-files.in +++ b/browser/installer/removed-files.in @@ -927,16 +927,18 @@ xpicleanup@BIN_SUFFIX@ components/nsTelephonyWorker.js components/Telephony.manifest components/Telephony.js components/nsWifiWorker.js components/nsWifiWorker.manifest #endif components/txEXSLTRegExFunctions.js components/Weave.js + components/Webapps.js + components/Webapps.manifest components/WebContentConverter.js defaults/autoconfig/platform.js defaults/autoconfig/prefcalls.js defaults/pref/firefox-branding.js defaults/pref/firefox.js defaults/pref/firefox-l10n.js defaults/pref/services-sync.js defaults/profile/bookmarks.html @@ -1052,16 +1054,17 @@ xpicleanup@BIN_SUFFIX@ modules/services-sync/type_records/passwords.js modules/services-sync/type_records/prefs.js modules/services-sync/type_records/tabs.js modules/services-sync/util.js modules/stylePanel.jsm modules/tabview/AllTabs.jsm modules/tabview/groups.jsm modules/tabview/utils.jsm + modules/Webapps.jsm modules/WindowDraggingUtils.jsm #ifdef XP_WIN modules/WindowsJumpLists.jsm modules/WindowsPreviewPerTab.jsm #endif modules/XPCOMUtils.jsm modules/XPIProvider.jsm res/contenteditable.css @@ -1142,16 +1145,17 @@ xpicleanup@BIN_SUFFIX@ components/content_xslt.xpt components/content_xtf.xpt components/contentprefs.xpt components/cookie.xpt components/crashreporter.xpt components/directory.xpt components/docshell.xpt components/dom.xpt + components/dom_apps.xpt components/dom_base.xpt #ifdef MOZ_B2G_RIL components/dom_telephony.xpt components/dom_wifi.xpt components/dom_system_b2g.xpt #endif components/dom_canvas.xpt components/dom_core.xpt
--- a/browser/themes/gnomestripe/devtools/splitview.css +++ b/browser/themes/gnomestripe/devtools/splitview.css @@ -36,16 +36,22 @@ * * ***** END LICENSE BLOCK ***** */ .splitview-nav-container { background-color: hsl(208,11%,27%); color: white; } +.loading .splitview-nav-container { + background-image: url(chrome://global/skin/icons/loading_16.png); + background-repeat: no-repeat; + background-position: center center; +} + .splitview-nav { -moz-appearance: none; margin: 0; box-shadow: inset -1px 0 0 #222426; } .splitview-nav > li { color: white; @@ -65,17 +71,16 @@ .splitview-nav > li { outline: 0; vertical-align: bottom; } .placeholder { -moz-box-flex: 1; - -moz-box-back: center; text-align: center; } .splitview-nav > li.splitview-active { background-image: url(itemArrow-ltr.png), -moz-linear-gradient(left, black, black), -moz-linear-gradient(hsl(206,61%,40%), hsl(206,61%,31%)); background-repeat: no-repeat, no-repeat, repeat-x;
--- a/browser/themes/gnomestripe/devtools/styleeditor.css +++ b/browser/themes/gnomestripe/devtools/styleeditor.css @@ -31,16 +31,20 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ +#style-editor-chrome { + background-color: hsl(208,11%,27%); +} + .stylesheet-title, .stylesheet-name { text-decoration: none; color: hsl(207,17%,88%); } .stylesheet-name { font-size: 13px;
new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2527df6e72b5c80b91e180af9644ba96a5c148d6 GIT binary patch literal 2552 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 O0000<MNUMnLSTaa5%5U>
new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..389f689aa2a7611f53977594c048f79c4991affd GIT binary patch literal 1538 zc$@(O2L1VoP)<h;3K|Lk000e1NJLTq001BW001Ni1^@s6=<7J*00001b5ch_0Itp) z=>Px#32;bRa{vGf6951U69E94oEQKA1*b_wK~z}7z1Lk#Q)e8<@o%-;l_RE+GhM_8 z&Z*1|BRcVpq)WWhXf)~+B`Co`d;xW!BVJ5kIGwy`!52``xw)t_CD}4`B+G6vOo(iX zAqGoCN3E_gn~4eZ{GVOW9(rnfS}x{EPSW4gp8lWbJkL3wb8G-7F1F?XH@0n9?j>wH zak13_tlYL?d9&;MH5F<v+O*OZFO$I5_>H^6-f+R4E27Q<y$u)K&cd7(UVw%RZik4v z4rDf5a8DA^O$RHbEHi-X{I$7&8<-8)fMK8?SO=UGku1B2{xi@Aqyz28TYhi?!y@{1 zAQN~9v?`@)$J2ph=dPBDNEI+4tocn~9?%nNumRMaxjYs46_^9`0x!dgXYb5g<6#_I z-I>dCfxE%t1Hjgud28D1&Ros{egobxbpYV#=}Y0AAXL00f1L-Qw)uNwh`Y9|wOM7Q z)V<(H2a30@OAWQxG+pWdvcfxS4}H+}z%cM4?7J7iGXa2zm_})pm8kcEAJ>Q4zCQVF zhv<?OdA95T)vgwYuAAokWoS)QyIP!OMVq}UqM%h4k%<6%Kh3!rYTI4#$<k=kWvi-h zb^zhUvx6N7UlY##JGOWeT?5swmXYA#J}xWT+-~(-yXdkuym)zWz6YS9=5%(j6X`(i zp@x<(LkE=>Znhn2Xi>4U9?<^)J_ODIDUn61>d!d#@5u85OeinTZxkiFMTt=ypu9NW zD@w9O$-eU9{AwjaXb+w8SXqhWDG{YardQTBJ+7>6S_M#9+qAK=wrNO-JgKa0sxa2T zfs%rw(N52Sk^(PKoyc_`o<HBUwd-KbslC8q;2q%V!J1QuGS-O#{xV&L|Ht;l(UVy~ zV{jVZ1pW@*A4(3C6m%IKNNe-w05={ibC?!dTASYitbDM{(VW)iH-`9NnZp(@C(^-p ze|g?vPiyn%0%w8Vv^Kvp)QMp6>p*5&n|~4!mtEXE`XAO7=#phuy4ix%pPo8_VP(EA zv?37}=n?mUh@ASPuT%vBiCK{99<sE+vtrt~ESTvYdc}f)71P>hx`$$0F!Rb_R0{@H zr+9+j8*@CcI>ly{1q=%wu1+y5m~mw=wgqZ<#L@y4h<F~b$f91gVET8S4jBnVw_wJ( zK8G3}iE6=&bA8T-xpTY(0_GzvvJy2B;PIx}h6Rt-r$)CR<@B9|EEvq6pRfhfn(rAF z3~iZbZ2=>?$%4Vc3=cp`LyyUVl%~6e1%q4Px1}`QRk89&91EtNyyNf|%pbL2urQ-h z>H2P^>qc>a!NLr$()Dbm>-z=^GpY&b7&~B<C7|a?Kvxpbr)#dB$C|5W6@cdI*{HdC zh9sar(Of-a7ChR%FscQQwl5sBAkK9ko_$3Nx{~X<O%^2Ab%!t1*JHtynp-g~cvie< z%mU5Tp99?R70og&G|koT09N{nW;JWBel<42S+;mNkq*Ay=;^R)u71M;&DHM=bs|{& zI*_Tk`X>?j+#fHG{)eRnN+ipU5|ahm@%v6-7$s&4BHjy75%@&p<YV_sRif_&T793T z1->0~#$`crecvk<Jl!#;J-NOwwgp;UZ&V9>JJUVE@5knNXS&TQiyE`w*{*cMf+@AV zu`NK?EiD+&^FTBU97q4`5YeMspdGpGK%&1FXh&{4eR~#o<ITA#2QBdL$uKPNmwXW2 z0_|{DLKgV;W+rUG<gdC73!Z<rFpdRcelPHqE*_opL%*9W&<=MS7Wnoqv}uRCRjfR$ zi=_pVt8Y1;mn|B#z*oAs5hc4(ViX7Pl`i(8BpW6Be5H%4QG&6DPI-(hB6%XBL}YqW zRp;ZRs?JpaNmZR2ld3w0MC3_QRp*!mzOp4zE%22sF)SF*8u(}bM_ox3H%%5KRoo0; osISL@w<<bfTJUfA(k_7i0M-VmrtThlf&c&j07*qoM6N<$f_IMEO8@`>
--- a/browser/themes/pinstripe/devtools/splitview.css +++ b/browser/themes/pinstripe/devtools/splitview.css @@ -36,16 +36,22 @@ * * ***** END LICENSE BLOCK ***** */ .splitview-nav-container { background-color: hsl(208,11%,27%); color: white; } +.loading .splitview-nav-container { + background-image: url(chrome://global/skin/icons/loading_16.png); + background-repeat: no-repeat; + background-position: center center; +} + .splitview-nav { -moz-appearance: none; margin: 0; box-shadow: inset -1px 0 0 #222426; } .splitview-nav > li { color: white; @@ -65,17 +71,16 @@ .splitview-nav > li { outline: 0; vertical-align: bottom; } .placeholder { -moz-box-flex: 1; - -moz-box-back: center; text-align: center; } .splitview-nav > li.splitview-active { background-image: url(itemArrow-ltr.png), -moz-linear-gradient(left, black, black), -moz-linear-gradient(hsl(206,61%,40%), hsl(206,61%,31%)); background-repeat: no-repeat, no-repeat, repeat-x;
--- a/browser/themes/pinstripe/devtools/styleeditor.css +++ b/browser/themes/pinstripe/devtools/styleeditor.css @@ -31,16 +31,20 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ +#style-editor-chrome { + background-color: hsl(208,11%,27%); +} + .stylesheet-title, .stylesheet-name { text-decoration: none; color: hsl(207,17%,88%); } .stylesheet-name { font-size: 13px;
new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2527df6e72b5c80b91e180af9644ba96a5c148d6 GIT binary patch literal 2552 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 O0000<MNUMnLSTaa5%5U>
new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..75d6f13d8c6e6a1cfc0d5766cfec91378e0b60c0 GIT binary patch literal 1568 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 Su_w;}0000<MNUMnLSTY9P~RW`
--- a/browser/themes/winstripe/devtools/splitview.css +++ b/browser/themes/winstripe/devtools/splitview.css @@ -36,16 +36,22 @@ * * ***** END LICENSE BLOCK ***** */ .splitview-nav-container { background-color: hsl(211,21%,26%); color: white; } +.loading .splitview-nav-container { + background-image: url(chrome://global/skin/icons/loading_16.png); + background-repeat: no-repeat; + background-position: center center; +} + .splitview-nav { -moz-appearance: none; margin: 0; box-shadow: inset -1px 0 0 #222426; } .splitview-nav > li { color: white; @@ -65,17 +71,16 @@ .splitview-nav > li { outline: 0; vertical-align: bottom; } .placeholder { -moz-box-flex: 1; - -moz-box-back: center; text-align: center; } .splitview-nav > li.splitview-active { background-image: url(itemArrow-ltr.png), -moz-linear-gradient(left, black, black), -moz-linear-gradient(hsl(200,100%,33%), hsl(200,100%,25%)); background-repeat: no-repeat, no-repeat, repeat-x;
--- a/browser/themes/winstripe/devtools/styleeditor.css +++ b/browser/themes/winstripe/devtools/styleeditor.css @@ -31,16 +31,20 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ +#style-editor-chrome { + background-color: hsl(211,21%,26%); +} + .stylesheet-title, .stylesheet-name { text-decoration: none; color: hsl(210,30%,85%); } .stylesheet-name { font-size: 13px;
new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2527df6e72b5c80b91e180af9644ba96a5c148d6 GIT binary patch literal 2552 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 O0000<MNUMnLSTaa5%5U>
new file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..33bb4a320e76cc7a346ca4beb60e89a85a48aa2c GIT binary patch literal 1704 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
--- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -1994,34 +1994,37 @@ nsresult nsHTMLMediaElement::InitializeD return NS_ERROR_FAILURE; nsRefPtr<nsMediaDecoder> decoder = aOriginal->Clone(); if (!decoder) return NS_ERROR_FAILURE; LOG(PR_LOG_DEBUG, ("%p Cloned decoder %p from %p", this, decoder.get(), aOriginal)); if (!decoder->Init(this)) { + LOG(PR_LOG_DEBUG, ("%p Failed to init cloned decoder %p", this, decoder.get())); return NS_ERROR_FAILURE; } double duration = aOriginal->GetDuration(); if (duration >= 0) { decoder->SetDuration(duration); decoder->SetSeekable(aOriginal->IsSeekable()); } nsMediaStream* stream = originalStream->CloneData(decoder); if (!stream) { + LOG(PR_LOG_DEBUG, ("%p Failed to cloned stream for decoder %p", this, decoder.get())); return NS_ERROR_FAILURE; } mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING; nsresult rv = decoder->Load(stream, nsnull, aOriginal); if (NS_FAILED(rv)) { + LOG(PR_LOG_DEBUG, ("%p Failed to load decoder/stream for decoder %p", this, decoder.get())); return rv; } return FinishDecoderSetup(decoder); } nsresult nsHTMLMediaElement::InitializeDecoderForChannel(nsIChannel *aChannel, nsIStreamListener **aListener)
--- a/content/media/nsBuiltinDecoder.cpp +++ b/content/media/nsBuiltinDecoder.cpp @@ -188,16 +188,17 @@ nsresult nsBuiltinDecoder::Load(nsMediaS { // Hold the lock while we do this to set proper lock ordering // expectations for dynamic deadlock detectors: decoder lock(s) // should be grabbed before the cache lock ReentrantMonitorAutoEnter mon(mReentrantMonitor); nsresult rv = aStream->Open(aStreamListener); if (NS_FAILED(rv)) { + LOG(PR_LOG_DEBUG, ("%p Failed to open stream!", this)); delete aStream; return rv; } mStream = aStream; } mDecoderStateMachine = CreateStateMachine(); @@ -240,29 +241,31 @@ nsresult nsBuiltinDecoder::RequestFrameB return res; } nsresult nsBuiltinDecoder::ScheduleStateMachineThread() { NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); NS_ASSERTION(mDecoderStateMachine, "Must have state machine to start state machine thread"); + NS_ENSURE_STATE(mDecoderStateMachine); if (mShuttingDown) return NS_OK; ReentrantMonitorAutoEnter mon(mReentrantMonitor); nsBuiltinDecoderStateMachine* m = static_cast<nsBuiltinDecoderStateMachine*>(mDecoderStateMachine.get()); return m->ScheduleStateMachine(); } nsresult nsBuiltinDecoder::Play() { NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); ReentrantMonitorAutoEnter mon(mReentrantMonitor); + NS_ASSERTION(mDecoderStateMachine != nsnull, "Should have state machine."); nsresult res = ScheduleStateMachineThread(); NS_ENSURE_SUCCESS(res,res); if (mPlayState == PLAY_STATE_SEEKING) { mNextState = PLAY_STATE_PLAYING; return NS_OK; } if (mPlayState == PLAY_STATE_ENDED) return Seek(0); @@ -827,39 +830,29 @@ void nsBuiltinDecoder::ChangeState(PlayS } if (mPlayState == PLAY_STATE_SHUTDOWN) { mReentrantMonitor.NotifyAll(); return; } mPlayState = aState; - switch (aState) { - case PLAY_STATE_PAUSED: - /* No action needed */ - break; - case PLAY_STATE_PLAYING: - mDecoderStateMachine->Play(); - break; - case PLAY_STATE_SEEKING: - mDecoderStateMachine->Seek(mRequestedSeekTime); - mRequestedSeekTime = -1.0; - break; - case PLAY_STATE_LOADING: - /* No action needed */ - break; - case PLAY_STATE_START: - /* No action needed */ - break; - case PLAY_STATE_ENDED: - /* No action needed */ - break; - case PLAY_STATE_SHUTDOWN: - /* No action needed */ - break; + if (mDecoderStateMachine) { + switch (aState) { + case PLAY_STATE_PLAYING: + mDecoderStateMachine->Play(); + break; + case PLAY_STATE_SEEKING: + mDecoderStateMachine->Seek(mRequestedSeekTime); + mRequestedSeekTime = -1.0; + break; + default: + /* No action needed */ + break; + } } mReentrantMonitor.NotifyAll(); } void nsBuiltinDecoder::PlaybackPositionChanged() { NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); if (mShuttingDown) @@ -968,17 +961,19 @@ void nsBuiltinDecoder::Suspend() void nsBuiltinDecoder::Resume(bool aForceBuffering) { NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); if (mStream) { mStream->Resume(); } if (aForceBuffering) { ReentrantMonitorAutoEnter mon(mReentrantMonitor); - mDecoderStateMachine->StartBuffering(); + if (mDecoderStateMachine) { + mDecoderStateMachine->StartBuffering(); + } } } void nsBuiltinDecoder::StopProgressUpdates() { NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), "Should be on state machine or decode thread."); GetReentrantMonitor().AssertCurrentThreadIn();
--- a/content/media/nsBuiltinDecoderStateMachine.cpp +++ b/content/media/nsBuiltinDecoderStateMachine.cpp @@ -40,16 +40,17 @@ #include "nsAudioStream.h" #include "nsTArray.h" #include "nsBuiltinDecoder.h" #include "nsBuiltinDecoderReader.h" #include "nsBuiltinDecoderStateMachine.h" #include "mozilla/mozalloc.h" #include "VideoUtils.h" #include "nsTimeRanges.h" +#include "nsDeque.h" #include "mozilla/Preferences.h" #include "mozilla/StdInt.h" using namespace mozilla; using namespace mozilla::layers; #ifdef PR_LOGGING @@ -224,35 +225,47 @@ public: // Return the global state machine thread. Call from any thread. nsIThread* GetGlobalStateMachineThread() { ReentrantMonitorAutoEnter mon(mMonitor); NS_ASSERTION(mStateMachineThread, "Should have non-null state machine thread!"); return mStateMachineThread; } + // Requests that a decode thread be created for aStateMachine. The thread + // may be created immediately, or after some delay, once a thread becomes + // available. The request can be cancelled using CancelCreateDecodeThread(). + // It's the callers responsibility to not call this more than once for any + // given state machine. + nsresult RequestCreateDecodeThread(nsBuiltinDecoderStateMachine* aStateMachine); + + // Cancels a request made by RequestCreateDecodeThread to create a decode + // thread for aStateMachine. + nsresult CancelCreateDecodeThread(nsBuiltinDecoderStateMachine* aStateMachine); + // Maximum number of active decode threads allowed. When more // than this number are active the thread creation will fail. static const PRUint32 MAX_DECODE_THREADS = 25; // Returns the number of active decode threads. // Call on any thread. Holds the internal monitor so don't // call with any other monitor held to avoid deadlock. PRUint32 GetDecodeThreadCount(); - // Keep track of the fact that a decode thread was created. - // Call on any thread. Holds the internal monitor so don't - // call with any other monitor held to avoid deadlock. - void NoteDecodeThreadCreated(); - // Keep track of the fact that a decode thread was destroyed. // Call on any thread. Holds the internal monitor so don't // call with any other monitor held to avoid deadlock. void NoteDecodeThreadDestroyed(); +#ifdef DEBUG + // Returns true if aStateMachine has a pending request for a + // decode thread. + bool IsQueued(nsBuiltinDecoderStateMachine* aStateMachine); +#endif + private: // Holds global instance of StateMachineTracker. // Writable on main thread only. static StateMachineTracker* mInstance; // Reentrant monitor that must be obtained to access // the decode thread count member and methods. ReentrantMonitor mMonitor; @@ -266,16 +279,20 @@ private: // currently instantiated. Access only with the // mMonitor lock held. Can be used from any thread. PRUint32 mDecodeThreadCount; // Global state machine thread. Write on the main thread // only, read from the decoder threads. Synchronized via // the mMonitor. nsIThread* mStateMachineThread; + + // Queue of state machines waiting for decode threads. Entries at the front + // get their threads first. + nsDeque mPending; }; StateMachineTracker* StateMachineTracker::mInstance = nsnull; StateMachineTracker& StateMachineTracker::Instance() { if (!mInstance) { NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); @@ -291,57 +308,119 @@ void StateMachineTracker::EnsureGlobalSt if (mStateMachineCount == 0) { NS_ASSERTION(!mStateMachineThread, "Should have null state machine thread!"); nsresult res = NS_NewThread(&mStateMachineThread, nsnull); NS_ABORT_IF_FALSE(NS_SUCCEEDED(res), "Can't create media state machine thread"); } mStateMachineCount++; } - + +#ifdef DEBUG +bool StateMachineTracker::IsQueued(nsBuiltinDecoderStateMachine* aStateMachine) +{ + ReentrantMonitorAutoEnter mon(mMonitor); + PRInt32 size = mPending.GetSize(); + for (int i = 0; i < size; ++i) { + nsBuiltinDecoderStateMachine* m = + static_cast<nsBuiltinDecoderStateMachine*>(mPending.ObjectAt(i)); + if (m == aStateMachine) { + return true; + } + } + return false; +} +#endif + void StateMachineTracker::CleanupGlobalStateMachine() { NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); NS_ABORT_IF_FALSE(mStateMachineCount > 0, "State machine ref count must be > 0"); mStateMachineCount--; if (mStateMachineCount == 0) { LOG(PR_LOG_DEBUG, ("Destroying media state machine thread")); + NS_ASSERTION(mPending.GetSize() == 0, "Shouldn't all requests be handled by now?"); { ReentrantMonitorAutoEnter mon(mMonitor); nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(mStateMachineThread); NS_RELEASE(mStateMachineThread); mStateMachineThread = nsnull; NS_DispatchToMainThread(event); NS_ASSERTION(mDecodeThreadCount == 0, "Decode thread count must be zero."); mInstance = nsnull; } delete this; } } -void StateMachineTracker::NoteDecodeThreadCreated() -{ - ReentrantMonitorAutoEnter mon(mMonitor); - ++mDecodeThreadCount; -} - void StateMachineTracker::NoteDecodeThreadDestroyed() { ReentrantMonitorAutoEnter mon(mMonitor); --mDecodeThreadCount; + while (mDecodeThreadCount < MAX_DECODE_THREADS && mPending.GetSize() > 0) { + nsBuiltinDecoderStateMachine* m = + static_cast<nsBuiltinDecoderStateMachine*>(mPending.PopFront()); + nsresult rv; + { + ReentrantMonitorAutoExit exitMon(mMonitor); + rv = m->StartDecodeThread(); + } + if (NS_SUCCEEDED(rv)) { + ++mDecodeThreadCount; + } + } } PRUint32 StateMachineTracker::GetDecodeThreadCount() { ReentrantMonitorAutoEnter mon(mMonitor); return mDecodeThreadCount; } +nsresult StateMachineTracker::CancelCreateDecodeThread(nsBuiltinDecoderStateMachine* aStateMachine) { + ReentrantMonitorAutoEnter mon(mMonitor); + PRInt32 size = mPending.GetSize(); + for (PRInt32 i = 0; i < size; ++i) { + void* m = static_cast<nsBuiltinDecoderStateMachine*>(mPending.ObjectAt(i)); + if (m == aStateMachine) { + mPending.RemoveObjectAt(i); + break; + } + } + NS_ASSERTION(!IsQueued(aStateMachine), "State machine should no longer have queued request."); + return NS_OK; +} + +nsresult StateMachineTracker::RequestCreateDecodeThread(nsBuiltinDecoderStateMachine* aStateMachine) +{ + NS_ENSURE_STATE(aStateMachine); + ReentrantMonitorAutoEnter mon(mMonitor); + if (mPending.GetSize() > 0 || mDecodeThreadCount + 1 >= MAX_DECODE_THREADS) { + // If there's already state machines in the queue, or we've exceeded the + // limit, append the state machine to the queue of state machines waiting + // for a decode thread. This ensures state machines already waiting get + // their threads first. + mPending.Push(aStateMachine); + return NS_OK; + } + nsresult rv; + { + ReentrantMonitorAutoExit exitMon(mMonitor); + rv = aStateMachine->StartDecodeThread(); + } + if (NS_SUCCEEDED(rv)) { + ++mDecodeThreadCount; + } + NS_ASSERTION(mDecodeThreadCount <= MAX_DECODE_THREADS, + "Should keep to thread limit!"); + return NS_OK; +} + nsBuiltinDecoderStateMachine::nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDecoder, nsBuiltinDecoderReader* aReader, bool aRealTime) : mDecoder(aDecoder), mState(DECODER_STATE_DECODING_METADATA), mCbCrSize(0), mPlayDuration(0), mStartTime(-1), @@ -362,16 +441,17 @@ nsBuiltinDecoderStateMachine::nsBuiltinD mDecodeThreadIdle(false), mStopAudioThread(true), mQuickBuffering(false), mIsRunning(false), mRunAgain(false), mDispatchedRunEvent(false), mDecodeThreadWaiting(false), mRealTime(aRealTime), + mRequestedNewDecodeThread(false), mEventManager(aDecoder) { MOZ_COUNT_CTOR(nsBuiltinDecoderStateMachine); NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); StateMachineTracker::Instance().EnsureGlobalStateMachine(); // only enable realtime mode when "media.realtime_decoder.enabled" is true. @@ -381,16 +461,20 @@ nsBuiltinDecoderStateMachine::nsBuiltinD mBufferingWait = mRealTime ? 0 : BUFFERING_WAIT; mLowDataThresholdUsecs = mRealTime ? 0 : LOW_DATA_THRESHOLD_USECS; } nsBuiltinDecoderStateMachine::~nsBuiltinDecoderStateMachine() { NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); MOZ_COUNT_DTOR(nsBuiltinDecoderStateMachine); + NS_ASSERTION(!StateMachineTracker::Instance().IsQueued(this), + "Should not have a pending request for a new decode thread"); + NS_ASSERTION(!mRequestedNewDecodeThread, + "Should not have (or flagged) a pending request for a new decode thread"); if (mTimer) mTimer->Cancel(); mTimer = nsnull; StateMachineTracker::Instance().CleanupGlobalStateMachine(); } bool nsBuiltinDecoderStateMachine::HasFutureAudio() const { @@ -1186,28 +1270,40 @@ void nsBuiltinDecoderStateMachine::Seek( mState = DECODER_STATE_SEEKING; ScheduleStateMachine(); } void nsBuiltinDecoderStateMachine::StopDecodeThread() { NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread."); mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); + if (mRequestedNewDecodeThread) { + // We've requested that the decode be created, but it hasn't been yet. + // Cancel that request. + NS_ASSERTION(!mDecodeThread, + "Shouldn't have a decode thread until after request processed"); + StateMachineTracker::Instance().CancelCreateDecodeThread(this); + mRequestedNewDecodeThread = false; + } mStopDecodeThread = true; mDecoder->GetReentrantMonitor().NotifyAll(); if (mDecodeThread) { LOG(PR_LOG_DEBUG, ("%p Shutdown decode thread", mDecoder.get())); { ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); mDecodeThread->Shutdown(); StateMachineTracker::Instance().NoteDecodeThreadDestroyed(); } mDecodeThread = nsnull; mDecodeThreadIdle = false; } + NS_ASSERTION(!mRequestedNewDecodeThread, + "Any pending requests for decode threads must be canceled and unflagged"); + NS_ASSERTION(!StateMachineTracker::Instance().IsQueued(this), + "Any pending requests for decode threads must be canceled"); } void nsBuiltinDecoderStateMachine::StopAudioThread() { mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); mStopAudioThread = true; mDecoder->GetReentrantMonitor().NotifyAll(); if (mAudioThread) { @@ -1216,77 +1312,94 @@ void nsBuiltinDecoderStateMachine::StopA ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); mAudioThread->Shutdown(); } mAudioThread = nsnull; } } nsresult -nsBuiltinDecoderStateMachine::StartDecodeThread() +nsBuiltinDecoderStateMachine::ScheduleDecodeThread() { NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread."); mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); - PRUint32 count = 0; - bool created = false; - { + + mStopDecodeThread = false; + if (mState >= DECODER_STATE_COMPLETED) { + return NS_OK; + } + if (mDecodeThread) { + NS_ASSERTION(!mRequestedNewDecodeThread, + "Shouldn't have requested new decode thread when we have a decode thread"); + // We already have a decode thread... + if (mDecodeThreadIdle) { + // ... and it's not been shutdown yet, wake it up. + nsCOMPtr<nsIRunnable> event = + NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::DecodeThreadRun); + mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL); + mDecodeThreadIdle = false; + } + return NS_OK; + } else if (!mRequestedNewDecodeThread) { + // We don't already have a decode thread, request a new one. + mRequestedNewDecodeThread = true; ReentrantMonitorAutoExit mon(mDecoder->GetReentrantMonitor()); - count = StateMachineTracker::Instance().GetDecodeThreadCount(); + StateMachineTracker::Instance().RequestCreateDecodeThread(this); + } + return NS_OK; +} + +nsresult +nsBuiltinDecoderStateMachine::StartDecodeThread() +{ + NS_ASSERTION(StateMachineTracker::Instance().GetDecodeThreadCount() < + StateMachineTracker::MAX_DECODE_THREADS, + "Should not have reached decode thread limit"); + + ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); + NS_ASSERTION(!StateMachineTracker::Instance().IsQueued(this), + "Should not already have a pending request for a new decode thread."); + NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread."); + NS_ASSERTION(!mDecodeThread, "Should not have decode thread yet"); + NS_ASSERTION(mRequestedNewDecodeThread, "Should have requested this..."); + + mRequestedNewDecodeThread = false; + + nsresult rv = NS_NewThread(getter_AddRefs(mDecodeThread), + nsnull, + MEDIA_THREAD_STACK_SIZE); + if (NS_FAILED(rv)) { + // Give up, report error to media element. + nsCOMPtr<nsIRunnable> event = + NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DecodeError); + NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); + return rv; } - mStopDecodeThread = false; - if ((mDecodeThread && !mDecodeThreadIdle) || mState >= DECODER_STATE_COMPLETED) - return NS_OK; - - if (!mDecodeThread && count > StateMachineTracker::MAX_DECODE_THREADS) { - // Have to run one iteration of the state machine loop to ensure the - // shutdown state is processed. - ScheduleStateMachine(); - mState = DECODER_STATE_SHUTDOWN; - return NS_ERROR_FAILURE; - } - - if (!mDecodeThread) { - nsresult rv = NS_NewThread(getter_AddRefs(mDecodeThread), - nsnull, - MEDIA_THREAD_STACK_SIZE); - if (NS_FAILED(rv)) { - // Have to run one iteration of the state machine loop to ensure the - // shutdown state is processed. - ScheduleStateMachine(); - mState = DECODER_STATE_SHUTDOWN; - return rv; - } - created = true; - } nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::DecodeThreadRun); mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL); mDecodeThreadIdle = false; - if (created) { - ReentrantMonitorAutoExit mon(mDecoder->GetReentrantMonitor()); - StateMachineTracker::Instance().NoteDecodeThreadCreated(); - } - return NS_OK; } nsresult nsBuiltinDecoderStateMachine::StartAudioThread() { NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), "Should be on state machine or decode thread."); mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); mStopAudioThread = false; if (HasAudio() && !mAudioThread) { nsresult rv = NS_NewThread(getter_AddRefs(mAudioThread), nsnull, MEDIA_THREAD_STACK_SIZE); if (NS_FAILED(rv)) { + LOG(PR_LOG_DEBUG, ("%p Changed state to SHUTDOWN because failed to create audio thread", mDecoder.get())); mState = DECODER_STATE_SHUTDOWN; return rv; } nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::AudioLoop); mAudioThread->Dispatch(event, NS_DISPATCH_NORMAL); } return NS_OK; @@ -1634,17 +1747,17 @@ nsresult nsBuiltinDecoderStateMachine::R // dispatch an event to the main thread to release the decoder and // state machine. NS_DispatchToCurrentThread(new nsDispatchDisposeEvent(mDecoder, this)); return NS_OK; } case DECODER_STATE_DECODING_METADATA: { // Ensure we have a decode thread to decode metadata. - return StartDecodeThread(); + return ScheduleDecodeThread(); } case DECODER_STATE_DECODING: { if (mDecoder->GetState() != nsBuiltinDecoder::PLAY_STATE_PLAYING && IsPlaying()) { // We're playing, but the element/decoder is in paused state. Stop // playing! Note we do this before StopDecodeThread() below because @@ -1658,17 +1771,17 @@ nsresult nsBuiltinDecoderStateMachine::R // The decode buffers are full, and playback is paused. Shutdown the // decode thread. StopDecodeThread(); return NS_OK; } // We're playing and/or our decode buffers aren't full. Ensure we have // an active decode thread. - if (NS_FAILED(StartDecodeThread())) { + if (NS_FAILED(ScheduleDecodeThread())) { NS_WARNING("Failed to start media decode thread!"); return NS_ERROR_FAILURE; } AdvanceFrame(); NS_ASSERTION(mDecoder->GetState() != nsBuiltinDecoder::PLAY_STATE_PLAYING || IsStateMachineScheduled(), "Must have timer scheduled"); return NS_OK; @@ -1723,17 +1836,17 @@ nsresult nsBuiltinDecoderStateMachine::R StartPlayback(); } NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled"); return NS_OK; } case DECODER_STATE_SEEKING: { // Ensure we have a decode thread to perform the seek. - return StartDecodeThread(); + return ScheduleDecodeThread(); } case DECODER_STATE_COMPLETED: { StopDecodeThread(); if (mState != DECODER_STATE_COMPLETED) { // While we're waiting for the decode thread to shutdown, we can // change state, for example to seeking or shutdown state.
--- a/content/media/nsBuiltinDecoderStateMachine.h +++ b/content/media/nsBuiltinDecoderStateMachine.h @@ -248,16 +248,22 @@ public: // machine again. nsresult ScheduleStateMachine(); // Schedules the shared state machine thread to run the state machine // in aUsecs microseconds from now, if it's not already scheduled to run // earlier, in which case the request is discarded. nsresult ScheduleStateMachine(PRInt64 aUsecs); + // Creates and starts a new decode thread. Don't call this directly, + // request a new decode thread by calling + // StateMachineTracker::RequestCreateDecodeThread(). + // The decoder monitor must not be held. Called on the state machine thread. + nsresult StartDecodeThread(); + // Timer function to implement ScheduleStateMachine(aUsecs). void TimeoutExpired(); // Set the media fragment end time. aEndTime is in microseconds. void SetFragmentEndTime(PRInt64 aEndTime); // Drop reference to decoder. Only called during shutdown dance. void ReleaseDecoder() { mDecoder = nsnull; } @@ -347,27 +353,30 @@ protected: PRUint32 aChannels, PRUint64 aFrameOffset); // Pops an audio chunk from the front of the audio queue, and pushes its // audio data to the audio hardware. MozAudioAvailable data is also queued // here. Called on the audio thread. PRUint32 PlayFromAudioQueue(PRUint64 aFrameOffset, PRUint32 aChannels); - // Stops the decode thread. The decoder monitor must be held with exactly + // Stops the decode thread, and if we have a pending request for a new + // decode thread it is canceled. The decoder monitor must be held with exactly // one lock count. Called on the state machine thread. void StopDecodeThread(); // Stops the audio thread. The decoder monitor must be held with exactly // one lock count. Called on the state machine thread. void StopAudioThread(); - // Starts the decode thread. The decoder monitor must be held with exactly - // one lock count. Called on the state machine thread. - nsresult StartDecodeThread(); + // Ensures the decode thread is running if it already exists, or requests + // a new decode thread be started if there currently is no decode thread. + // The decoder monitor must be held with exactly one lock count. Called on + // the state machine thread. + nsresult ScheduleDecodeThread(); // Starts the audio thread. The decoder monitor must be held with exactly // one lock count. Called on the state machine thread. nsresult StartAudioThread(); // The main loop for the audio thread. Sent to the thread as // an nsRunnableMethod. This continually does blocking writes to // to audio stream to play audio data. @@ -620,16 +629,20 @@ protected: // True if the decode thread has gone filled its buffers and is now // waiting to be awakened before it continues decoding. Synchronized // by the decoder monitor. bool mDecodeThreadWaiting; // True is we are decoding a realtime stream, like a camera stream bool mRealTime; + + // True if we've requested a new decode thread, but it has not yet been + // created. Synchronized by the decoder monitor. + bool mRequestedNewDecodeThread; PRUint32 mBufferingWait; PRInt64 mLowDataThresholdUsecs; private: // Manager for queuing and dispatching MozAudioAvailable events. The // event manager is accessed from the state machine and audio threads, // and takes care of synchronizing access to its internal queue.
--- a/content/media/test/Makefile.in +++ b/content/media/test/Makefile.in @@ -145,16 +145,17 @@ include $(topsrcdir)/config/rules.mk test_seek.html \ test_seek2.html \ test_seekLies.html \ test_seek_out_of_range.html \ test_source.html \ test_source_write.html \ test_standalone.html \ test_timeupdate_small_files.html \ + test_too_many_elements.html \ test_volume.html \ test_video_to_canvas.html \ use_large_cache.js \ test_audiowrite.html \ $(NULL) # Don't run in suite ifndef MOZ_SUITE
--- a/content/media/test/crashtests/crashtests.list +++ b/content/media/test/crashtests/crashtests.list @@ -3,9 +3,9 @@ load 466607-1.html load 466945-1.html load 468763-1.html load 474744-1.html HTTP load 481136-1.html # needs to be HTTP to recognize the ogg as an audio file? load 493915-1.html skip-if(Android) load 495794-1.html load 492286-1.xhtml load 576612-1.html -load 691096-1.html +skip-if(Android) load 691096-1.html # Android sound API can't handle playing large number of sounds at once.
--- a/content/media/test/manifest.js +++ b/content/media/test/manifest.js @@ -1,18 +1,18 @@ // In each list of tests below, test file types that are not supported should // be ignored. To make sure tests respect that, we include a file of type // "bogus/duh" in each list. // These are small test files, good for just seeing if something loads. We // really only need one test file per backend here. var gSmallTests = [ + { name:"small-shot.ogg", type:"audio/ogg", duration:0.276 }, { name:"r11025_s16_c1.wav", type:"audio/x-wav", duration:1.0 }, { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.233 }, - { name:"small-shot.ogg", type:"audio/ogg", duration:0.276 }, { name:"seek.webm", type:"video/webm", duration:3.966 }, { name:"bogus.duh", type:"bogus/duh" } ]; // Used by test_progress to ensure we get the correct progress information // during resource download. var gProgressTests = [ { name:"r11025_u8_c1.wav", type:"audio/x-wav", duration:1.0, size:11069 }, @@ -294,16 +294,24 @@ function checkMetadata(msg, e, test) { function getPlayableVideo(candidates) { var v = document.createElement("video"); var resources = candidates.filter(function(x){return /^video/.test(x.type) && v.canPlayType(x.type);}); if (resources.length > 0) return resources[0]; return null; } +function getPlayableAudio(candidates) { + var v = document.createElement("audio"); + var resources = candidates.filter(function(x){return /^audio/.test(x.type) && v.canPlayType(x.type);}); + if (resources.length > 0) + return resources[0]; + return null; +} + // Number of tests to run in parallel. Warning: Each media element requires // at least 3 threads (4 on Linux), and on Linux each thread uses 10MB of // virtual address space. Beware! var PARALLEL_TESTS = 2; // When true, we'll loop forever on whatever test we run. Use this to debug // intermittent test failures. const DEBUG_TEST_LOOP_FOREVER = false;
new file mode 100644 --- /dev/null +++ b/content/media/test/test_too_many_elements.html @@ -0,0 +1,68 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=713381 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 713381</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="text/javascript" src="manifest.js"></script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=713381">Mozilla Bug 713381</a> +<p id="display"></p> +<div id="content" style="display: none"> +</div> + +<p>Received loadeddata event for <span id="result">0</span> <span id="expected"></span>audio elements.</p> +<pre id="test"> +<script type="application/javascript"> + +/** Test for Bug 713381 **/ + +const num = 500; +var loadeddata = 0; + +var result = document.getElementById("result"); +document.getElementById("expected").innerHTML = " of " + num + " "; + +var finish = function(testNum) { + return function() { + ok(true, "Received loadeddata event for instance " + testNum ); + loadeddata++; + if (loadeddata == num) { + ok(true, "Should receive loadeddata events for all " + num + " elements."); + SimpleTest.finish(); + } + } +}; + +var resource = getPlayableAudio(gSmallTests); + +if (resource == null) { + todo(false, "No types supported"); +} else { + SimpleTest.waitForExplicitFinish(); + // Load the resource, and play it to ensure it's entirely downloaded. + // Once it's played through, create a large number of audio elements which + // are the same resource. These will share data with the other resource, and + // so be really cheap to create. + var res = new Audio(resource.name); + res.addEventListener("ended", + function() { + for (var i=0; i<num; ++i) { + var a = new Audio(resource.name); + a.addEventListener("loadeddata", finish(i), false); + } + }, false); + res.play(); +} + +setInterval(function() { result.innerHTML = loadeddata; }, 1000); + +</script> +</pre> +</body> +</html>
--- a/dom/Makefile.in +++ b/dom/Makefile.in @@ -61,18 +61,23 @@ DIRS = \ interfaces/xul \ interfaces/storage \ interfaces/json \ interfaces/offline \ interfaces/geolocation \ interfaces/notification \ interfaces/svg \ interfaces/smil \ + $(NULL) + +ifeq (gonk,$(MOZ_WIDGET_TOOLKIT)) +DIRS += \ interfaces/apps \ $(NULL) +endif DIRS += \ base \ battery \ sms \ src \ locales \ network \
copy from dom/sms/src/fallback/SmsDatabaseService.cpp copy to dom/sms/src/ril/SmsDatabaseService.cpp
--- a/dom/sms/src/ril/SmsService.cpp +++ b/dom/sms/src/ril/SmsService.cpp @@ -75,17 +75,20 @@ SmsService::GetNumberOfMessagesForText(c return NS_OK; } mRIL->GetNumberOfMessagesForText(aText, aResult); return NS_OK; } NS_IMETHODIMP -SmsService::Send(const nsAString& aNumber, const nsAString& aMessage) +SmsService::Send(const nsAString& aNumber, + const nsAString& aMessage, + PRInt32 aRequestId, + PRUint64 aProcessId) { if (!mRIL) { return NS_OK; } mRIL->SendSMS(aNumber, aMessage); return NS_OK; }
--- a/dom/sms/src/ril/SmsService.h +++ b/dom/sms/src/ril/SmsService.h @@ -35,30 +35,30 @@ * * ***** END LICENSE BLOCK ***** */ #ifndef mozilla_dom_sms_SmsService_h #define mozilla_dom_sms_SmsService_h #include "nsISmsService.h" #include "nsCOMPtr.h" -#include "nsITelephone.h" +#include "nsIRadioInterfaceLayer.h" namespace mozilla { namespace dom { namespace sms { class SmsService : public nsISmsService { public: NS_DECL_ISUPPORTS NS_DECL_NSISMSSERVICE SmsService(); protected: - nsCOMPtr<nsITelephone> mRIL; + nsCOMPtr<nsIRadioInterfaceLayer> mRIL; }; } // namespace sms } // namespace dom } // namespace mozilla #endif // mozilla_dom_sms_SmsService_h
--- a/dom/src/geolocation/Makefile.in +++ b/dom/src/geolocation/Makefile.in @@ -74,15 +74,20 @@ LOCAL_INCLUDES += $(MOZ_QT_CFLAGS) \ $(NULL) endif ifeq ($(MOZ_WIDGET_TOOLKIT),android) LOCAL_INCLUDES += -I$(topsrcdir)/dom/system/android \ $(NULL) endif +ifeq ($(MOZ_WIDGET_TOOLKIT),gonk) +LOCAL_INCLUDES += -I$(topsrcdir)/dom/system/b2g \ + $(NULL) +endif + EXPORTS += nsGeoPositionIPCSerialiser.h include $(topsrcdir)/config/config.mk include $(topsrcdir)/ipc/chromium/chromium-config.mk include $(topsrcdir)/config/rules.mk DEFINES += -D_IMPL_NS_LAYOUT
--- a/dom/src/geolocation/nsGeolocation.cpp +++ b/dom/src/geolocation/nsGeolocation.cpp @@ -83,16 +83,20 @@ #ifdef MOZ_ENABLE_QTMOBILITY #include "QTMLocationProvider.h" #endif #ifdef MOZ_WIDGET_ANDROID #include "AndroidLocationProvider.h" #endif +#ifdef MOZ_WIDGET_GONK +#include "GonkGPSGeolocationProvider.h" +#endif + #include "nsIDOMDocument.h" #include "nsIDocument.h" // Some limit to the number of get or watch geolocation requests // that a window can make. #define MAX_GEO_REQUESTS_PER_WINDOW 1500 using mozilla::unused; // <snicker> @@ -572,16 +576,23 @@ nsresult nsGeolocationService::Init() mProviders.AppendObject(provider); #endif #ifdef MOZ_WIDGET_ANDROID provider = new AndroidLocationProvider(); if (provider) mProviders.AppendObject(provider); #endif + +#ifdef MOZ_WIDGET_GONK + provider = GonkGPSGeolocationProvider::GetSingleton(); + if (provider) + mProviders.AppendObject(provider); +#endif + return NS_OK; } nsGeolocationService::~nsGeolocationService() { } NS_IMETHODIMP
new file mode 100644 --- /dev/null +++ b/dom/system/b2g/GonkGPSGeolocationProvider.cpp @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Kan-Ru Chen <kchen@mozilla.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include <pthread.h> +#include <hardware/gps.h> + +#include "mozilla/Preferences.h" +#include "nsGeoPosition.h" +#include "GonkGPSGeolocationProvider.h" + +using namespace mozilla; + +NS_IMPL_ISUPPORTS1(GonkGPSGeolocationProvider, nsIGeolocationProvider) + +GonkGPSGeolocationProvider* GonkGPSGeolocationProvider::sSingleton; + +static void +LocationCallback(GpsLocation* location) +{ + nsRefPtr<GonkGPSGeolocationProvider> provider = + GonkGPSGeolocationProvider::GetSingleton(); + nsCOMPtr<nsIGeolocationUpdate> callback = provider->GetLocationCallback(); + + if (!callback) + return; + + nsRefPtr<nsGeoPosition> somewhere = new nsGeoPosition(location->latitude, + location->longitude, + location->altitude, + location->accuracy, + location->accuracy, + location->bearing, + location->speed, + location->timestamp); + callback->Update(somewhere); +} + +typedef void *(*pthread_func)(void *); + +/** Callback for creating a thread that can call into the JS codes. + */ +static pthread_t +CreateThreadCallback(const char* name, void (*start)(void *), void* arg) +{ + pthread_t thread; + pthread_attr_t attr; + + pthread_attr_init(&attr); + + /* Unfortunately pthread_create and the callback disagreed on what + * start function should return. + */ + pthread_create(&thread, &attr, reinterpret_cast<pthread_func>(start), arg); + + return thread; +} + +static GpsCallbacks gCallbacks = { + sizeof(GpsCallbacks), + LocationCallback, + NULL, /* StatusCallback */ + NULL, /* SvStatusCallback */ + NULL, /* NmeaCallback */ + NULL, /* SetCapabilitiesCallback */ + NULL, /* AcquireWakelockCallback */ + NULL, /* ReleaseWakelockCallback */ + CreateThreadCallback, +}; + +GonkGPSGeolocationProvider::GonkGPSGeolocationProvider() + : mStarted(false) +{ + mGpsInterface = GetGPSInterface(); +} + +GonkGPSGeolocationProvider::~GonkGPSGeolocationProvider() +{ + Shutdown(); + sSingleton = NULL; +} + +/* static */ already_AddRefed<GonkGPSGeolocationProvider> +GonkGPSGeolocationProvider::GetSingleton() +{ + if (!sSingleton) + sSingleton = new GonkGPSGeolocationProvider(); + + NS_ADDREF(sSingleton); + return sSingleton; +} + +already_AddRefed<nsIGeolocationUpdate> +GonkGPSGeolocationProvider::GetLocationCallback() +{ + nsCOMPtr<nsIGeolocationUpdate> callback = mLocationCallback; + return callback.forget(); +} + +const GpsInterface* +GonkGPSGeolocationProvider::GetGPSInterface() +{ + hw_module_t* module; + + if (hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module)) + return NULL; + + hw_device_t* device; + if (module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device)) + return NULL; + + gps_device_t* gps_device = (gps_device_t *)device; + return gps_device->get_gps_interface(gps_device); +} + +NS_IMETHODIMP +GonkGPSGeolocationProvider::Startup() +{ + if (mStarted) + return NS_OK; + + NS_ENSURE_TRUE(mGpsInterface, NS_ERROR_FAILURE); + + PRInt32 update = Preferences::GetInt("geo.default.update", 1000); + + mGpsInterface->init(&gCallbacks); + mGpsInterface->start(); + mGpsInterface->set_position_mode(GPS_POSITION_MODE_STANDALONE, + GPS_POSITION_RECURRENCE_PERIODIC, + update, 0, 0); + return NS_OK; +} + +NS_IMETHODIMP +GonkGPSGeolocationProvider::Watch(nsIGeolocationUpdate* aCallback) +{ + mLocationCallback = aCallback; + + return NS_OK; +} + +NS_IMETHODIMP +GonkGPSGeolocationProvider::Shutdown() +{ + if (!mStarted) + return NS_OK; + + mGpsInterface->stop(); + mGpsInterface->cleanup(); + + return NS_OK; +}
new file mode 100644 --- /dev/null +++ b/dom/system/b2g/GonkGPSGeolocationProvider.h @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Kan-Ru Chen <kchen@mozilla.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +#ifndef GonkGPSGeolocationProvider_h +#define GonkGPSGeolocationProvider_h + +#include <hardware/gps.h> // for GpsInterface +#include "nsIGeolocationProvider.h" + +class GonkGPSGeolocationProvider : public nsIGeolocationProvider +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGEOLOCATIONPROVIDER + + static already_AddRefed<GonkGPSGeolocationProvider> GetSingleton(); + + already_AddRefed<nsIGeolocationUpdate> GetLocationCallback(); + +private: + + /* Client should use GetSingleton() to get the provider instance. */ + GonkGPSGeolocationProvider(); + GonkGPSGeolocationProvider(const GonkGPSGeolocationProvider &); + GonkGPSGeolocationProvider & operator = (const GonkGPSGeolocationProvider &); + ~GonkGPSGeolocationProvider(); + + const GpsInterface* GetGPSInterface(); + + static GonkGPSGeolocationProvider* sSingleton; + + bool mStarted; + const GpsInterface* mGpsInterface; + nsCOMPtr<nsIGeolocationUpdate> mLocationCallback; +}; + +#endif /* GonkGPSGeolocationProvider_h */
--- a/dom/system/b2g/Makefile.in +++ b/dom/system/b2g/Makefile.in @@ -50,24 +50,39 @@ FORCE_STATIC_LIB = 1 include $(topsrcdir)/dom/dom-config.mk CPPSRCS = \ SystemWorkerManager.cpp \ $(NULL) ifeq (gonk,$(MOZ_WIDGET_TOOLKIT)) -CPPSRCS += AudioManager.cpp +CPPSRCS += \ + AudioManager.cpp \ + GonkGPSGeolocationProvider.cpp \ + $(NULL) endif XPIDLSRCS = \ nsIAudioManager.idl \ + nsIRadioInterfaceLayer.idl \ nsIWorkerHolder.idl \ $(NULL) LOCAL_INCLUDES = \ -I$(topsrcdir)/dom/base \ + -I$(topsrcdir)/dom/src/geolocation \ -I$(topsrcdir)/dom/telephony \ -I$(topsrcdir)/dom/wifi \ -I$(topsrcdir)/content/events/src \ $(NULL) +EXTRA_COMPONENTS = \ + RadioInterfaceLayer.manifest \ + RadioInterfaceLayer.js \ + $(NULL) + +EXTRA_JS_MODULES = \ + ril_consts.js \ + ril_worker.js \ + $(NULL) + include $(topsrcdir)/config/rules.mk
rename from dom/telephony/nsTelephonyWorker.js rename to dom/system/b2g/RadioInterfaceLayer.js --- a/dom/telephony/nsTelephonyWorker.js +++ b/dom/system/b2g/RadioInterfaceLayer.js @@ -9,23 +9,24 @@ * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Telephony. * * The Initial Developer of the Original Code is - * The Mozilla Foundation. + * the Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Ben Turner <bent.mozilla@gmail.com> (Original Author) * Philipp von Weitershausen <philipp@weitershausen.de> + * Sinker Li <thinker@codemud.net> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -43,45 +44,45 @@ const {classes: Cc, interfaces: Ci, util Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); var RIL = {}; Cu.import("resource://gre/modules/ril_consts.js", RIL); const DEBUG = true; // set to false to suppress debug messages -const TELEPHONYWORKER_CID = +const RADIOINTERFACELAYER_CID = Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}"); const DATACALLINFO_CID = Components.ID("{ef474cd9-94f7-4c05-a31b-29b9de8a10d2}"); const nsIAudioManager = Ci.nsIAudioManager; -const nsITelephone = Ci.nsITelephone; +const nsIRadioInterfaceLayer = Ci.nsIRadioInterfaceLayer; const kSmsReceivedObserverTopic = "sms-received"; const DOM_SMS_DELIVERY_RECEIVED = "received"; XPCOMUtils.defineLazyServiceGetter(this, "gSmsService", "@mozilla.org/sms/smsservice;1", "nsISmsService"); function convertRILCallState(state) { switch (state) { case RIL.CALL_STATE_ACTIVE: - return nsITelephone.CALL_STATE_CONNECTED; + return nsIRadioInterfaceLayer.CALL_STATE_CONNECTED; case RIL.CALL_STATE_HOLDING: - return nsITelephone.CALL_STATE_HELD; + return nsIRadioInterfaceLayer.CALL_STATE_HELD; case RIL.CALL_STATE_DIALING: - return nsITelephone.CALL_STATE_DIALING; + return nsIRadioInterfaceLayer.CALL_STATE_DIALING; case RIL.CALL_STATE_ALERTING: - return nsITelephone.CALL_STATE_RINGING; + return nsIRadioInterfaceLayer.CALL_STATE_RINGING; case RIL.CALL_STATE_INCOMING: - return nsITelephone.CALL_STATE_INCOMING; + return nsIRadioInterfaceLayer.CALL_STATE_INCOMING; case RIL.CALL_STATE_WAITING: - return nsITelephone.CALL_STATE_HELD; // XXX This may not be right... + return nsIRadioInterfaceLayer.CALL_STATE_HELD; // XXX This may not be right... default: throw new Error("Unknown rilCallState: " + state); } } /** * Fake nsIAudioManager implementation so that we can run the telephony * code in a non-Gonk build. @@ -121,38 +122,38 @@ DataCallInfo.protoptype = { classID: DATACALLINFO_CID, classInfo: XPCOMUtils.generateCI({classID: DATACALLINFO_CID, classDescription: "DataCallInfo", interfaces: [Ci.nsIDataCallInfo]}), QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataCallInfo]), }; -function nsTelephonyWorker() { +function RadioInterfaceLayer() { this.worker = new ChromeWorker("resource://gre/modules/ril_worker.js"); this.worker.onerror = this.onerror.bind(this); this.worker.onmessage = this.onmessage.bind(this); debug("Starting Worker\n"); this.currentState = { signalStrength: null, operator: null, radioState: null, cardState: null }; } -nsTelephonyWorker.prototype = { +RadioInterfaceLayer.prototype = { - classID: TELEPHONYWORKER_CID, - classInfo: XPCOMUtils.generateCI({classID: TELEPHONYWORKER_CID, - classDescription: "Telephone", + classID: RADIOINTERFACELAYER_CID, + classInfo: XPCOMUtils.generateCI({classID: RADIOINTERFACELAYER_CID, + classDescription: "RadioInterfaceLayer", interfaces: [Ci.nsIWorkerHolder, - Ci.nsITelephone]}), + Ci.nsIRadioInterfaceLayer]}), QueryInterface: XPCOMUtils.generateQI([Ci.nsIWorkerHolder, - Ci.nsITelephone]), + Ci.nsIRadioInterfaceLayer]), onerror: function onerror(event) { debug("Got an error: " + event.filename + ":" + event.lineno + ": " + event.message + "\n"); event.preventDefault(); }, /** @@ -214,38 +215,38 @@ nsTelephonyWorker.prototype = { updateCallAudioState: function updateCallAudioState() { if (!this._activeCall) { // Disable audio. gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL; debug("No active call, put audio system into PHONE_STATE_NORMAL."); return; } switch (this._activeCall.state) { - case nsITelephone.CALL_STATE_INCOMING: + case nsIRadioInterfaceLayer.CALL_STATE_INCOMING: gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_RINGTONE; debug("Incoming call, put audio system into PHONE_STATE_RINGTONE."); break; - case nsITelephone.CALL_STATE_DIALING: // Fall through... - case nsITelephone.CALL_STATE_CONNECTED: + case nsIRadioInterfaceLayer.CALL_STATE_DIALING: // Fall through... + case nsIRadioInterfaceLayer.CALL_STATE_CONNECTED: gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL; gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION, nsIAudioManager.FORCE_NONE); debug("Active call, put audio system into PHONE_STATE_IN_CALL."); break; } }, /** * Handle call state changes by updating our current state and the audio * system. */ handleCallStateChange: function handleCallStateChange(call) { debug("handleCallStateChange: " + JSON.stringify(call)); call.state = convertRILCallState(call.state); - if (call.state == nsITelephone.CALL_STATE_CONNECTED) { + if (call.state == nsIRadioInterfaceLayer.CALL_STATE_CONNECTED) { // This is now the active call. this._activeCall = call; } this.updateCallAudioState(); this._deliverCallback("callStateChanged", [call.callIndex, call.state, call.number]); }, @@ -254,17 +255,18 @@ nsTelephonyWorker.prototype = { */ handleCallDisconnected: function handleCallDisconnected(call) { debug("handleCallDisconnected: " + JSON.stringify(call)); if (this._activeCall.callIndex == call.callIndex) { this._activeCall = null; } this.updateCallAudioState(); this._deliverCallback("callStateChanged", - [call.callIndex, nsITelephone.CALL_STATE_DISCONNECTED, + [call.callIndex, + nsIRadioInterfaceLayer.CALL_STATE_DISCONNECTED, call.number]); }, /** * Handle calls delivered in response to a 'enumerateCalls' request. */ handleEnumerateCalls: function handleEnumerateCalls(calls) { debug("handleEnumerateCalls: " + JSON.stringify(calls)); @@ -322,17 +324,17 @@ nsTelephonyWorker.prototype = { this._deliverDataCallCallback("receiveDataCallList", [datacalls, datacalls.length]); }, // nsIRadioWorker worker: null, - // nsITelephone + // nsIRadioInterfaceLayer currentState: null, dial: function dial(number) { debug("Dialing " + number); this.worker.postMessage({type: "dial", number: number}); }, @@ -535,18 +537,18 @@ nsTelephonyWorker.prototype = { }, getDataCallList: function getDataCallList() { this.worker.postMessage({type: "getDataCallList"}); }, }; -const NSGetFactory = XPCOMUtils.generateNSGetFactory([nsTelephonyWorker]); +const NSGetFactory = XPCOMUtils.generateNSGetFactory([RadioInterfaceLayer]); let debug; if (DEBUG) { debug = function (s) { - dump("-*- TelephonyWorker component: " + s + "\n"); + dump("-*- RadioInterfaceLayer: " + s + "\n"); }; } else { debug = function (s) {}; }
rename from dom/telephony/nsTelephonyWorker.manifest rename to dom/system/b2g/RadioInterfaceLayer.manifest --- a/dom/telephony/nsTelephonyWorker.manifest +++ b/dom/system/b2g/RadioInterfaceLayer.manifest @@ -1,1 +1,1 @@ -component {2d831c8d-6017-435b-a80c-e5d422810cea} nsTelephonyWorker.js +component {2d831c8d-6017-435b-a80c-e5d422810cea} RadioInterfaceLayer.js
--- a/dom/system/b2g/SystemWorkerManager.cpp +++ b/dom/system/b2g/SystemWorkerManager.cpp @@ -36,37 +36,37 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "SystemWorkerManager.h" #include "nsIObserverService.h" #include "nsIJSContextStack.h" -#include "nsITelephone.h" +#include "nsIRadioInterfaceLayer.h" #include "nsIWifi.h" #include "nsIWorkerHolder.h" #include "nsIXPConnect.h" #include "jstypedarray.h" #include "mozilla/dom/workers/Workers.h" #include "mozilla/ipc/Ril.h" #include "nsContentUtils.h" #include "nsServiceManagerUtils.h" -#include "nsTelephonyWorker.h" #include "nsThreadUtils.h" +#include "nsRadioInterfaceLayer.h" #include "nsWifiWorker.h" USING_TELEPHONY_NAMESPACE USING_WORKERS_NAMESPACE using namespace mozilla::ipc; namespace { -NS_DEFINE_CID(kTelephonyWorkerCID, NS_TELEPHONYWORKER_CID); +NS_DEFINE_CID(kRadioInterfaceLayerCID, NS_RADIOINTERFACELAYER_CID); NS_DEFINE_CID(kWifiWorkerCID, NS_WIFIWORKER_CID); // Doesn't carry a reference, we're owned by services. SystemWorkerManager *gInstance = nsnull; class ConnectWorkerToRIL : public WorkerTask { public: @@ -219,17 +219,17 @@ SystemWorkerManager::Init() nsresult rv = nsContentUtils::ThreadJSContextStack()->GetSafeJSContext(&cx); NS_ENSURE_SUCCESS(rv, rv); nsCxPusher pusher; if (!cx || !pusher.Push(cx, false)) { return NS_ERROR_FAILURE; } - rv = InitTelephone(cx); + rv = InitRIL(cx); NS_ENSURE_SUCCESS(rv, rv); rv = InitWifi(cx); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIObserverService> obs = do_GetService(NS_OBSERVERSERVICE_CONTRACTID); if (!obs) { @@ -247,17 +247,17 @@ void SystemWorkerManager::Shutdown() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); mShutdown = true; StopRil(); - mTelephoneWorker = nsnull; + mRILWorker = nsnull; mWifiWorker = nsnull; nsCOMPtr<nsIObserverService> obs = do_GetService(NS_OBSERVERSERVICE_CONTRACTID); if (obs) { obs->RemoveObserver(this, WORKERS_SHUTDOWN_TOPIC); } } @@ -290,37 +290,37 @@ SystemWorkerManager::GetInterfaceRequest return gInstance; } NS_IMETHODIMP SystemWorkerManager::GetInterface(const nsIID &aIID, void **aResult) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - if (aIID.Equals(NS_GET_IID(nsITelephone))) { - return CallQueryInterface(mTelephoneWorker, - reinterpret_cast<nsITelephone**>(aResult)); + if (aIID.Equals(NS_GET_IID(nsIRadioInterfaceLayer))) { + return CallQueryInterface(mRILWorker, + reinterpret_cast<nsIRadioInterfaceLayer**>(aResult)); } if (aIID.Equals(NS_GET_IID(nsIWifi))) { return CallQueryInterface(mWifiWorker, reinterpret_cast<nsIWifi**>(aResult)); } NS_WARNING("Got nothing for the requested IID!"); return NS_ERROR_NO_INTERFACE; } nsresult -SystemWorkerManager::InitTelephone(JSContext *cx) +SystemWorkerManager::InitRIL(JSContext *cx) { // We're keeping as much of this implementation as possible in JS, so the real - // worker lives in nsTelephonyWorker.js. All we do here is hold it alive and + // worker lives in RadioInterfaceLayer.js. All we do here is hold it alive and // hook it up to the RIL thread. - nsCOMPtr<nsIWorkerHolder> worker = do_CreateInstance(kTelephonyWorkerCID); + nsCOMPtr<nsIWorkerHolder> worker = do_CreateInstance(kRadioInterfaceLayerCID); NS_ENSURE_TRUE(worker, NS_ERROR_FAILURE); jsval workerval; nsresult rv = worker->GetWorker(&workerval); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(!JSVAL_IS_PRIMITIVE(workerval), NS_ERROR_UNEXPECTED); @@ -340,17 +340,17 @@ SystemWorkerManager::InitTelephone(JSCon if (!wctd->PostTask(connection)) { return NS_ERROR_UNEXPECTED; } // Now that we're set up, connect ourselves to the RIL thread. mozilla::RefPtr<RILReceiver> receiver = new RILReceiver(wctd); StartRil(receiver); - mTelephoneWorker = worker; + mRILWorker = worker; return NS_OK; } nsresult SystemWorkerManager::InitWifi(JSContext *cx) { nsCOMPtr<nsIWorkerHolder> worker = do_CreateInstance(kWifiWorkerCID); NS_ENSURE_TRUE(worker, NS_ERROR_FAILURE);
--- a/dom/system/b2g/SystemWorkerManager.h +++ b/dom/system/b2g/SystemWorkerManager.h @@ -65,20 +65,20 @@ public: static nsIInterfaceRequestor* GetInterfaceRequestor(); private: SystemWorkerManager(); ~SystemWorkerManager(); - nsresult InitTelephone(JSContext *cx); + nsresult InitRIL(JSContext *cx); nsresult InitWifi(JSContext *cx); - nsCOMPtr<nsIWorkerHolder> mTelephoneWorker; + nsCOMPtr<nsIWorkerHolder> mRILWorker; nsCOMPtr<nsIWorkerHolder> mWifiWorker; bool mShutdown; }; END_TELEPHONY_NAMESPACE #endif // mozilla_dom_system_b2g_systemworkermanager_h__
rename from dom/telephony/nsITelephone.idl rename to dom/system/b2g/nsIRadioInterfaceLayer.idl --- a/dom/telephony/nsITelephone.idl +++ b/dom/system/b2g/nsIRadioInterfaceLayer.idl @@ -14,17 +14,19 @@ * The Original Code is Telephony. * * The Initial Developer of the Original Code is * the Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): - * Philipp von Weitershausen <philipp@weitershausen.de> + * Philipp von Weitershausen <philipp@weitershausen.de> + * Ben Turner <bent.mozilla@gmail.com> + * Sinker Li <thinker@codemud.net> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -32,58 +34,95 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsISupports.idl" -[scriptable, uuid(9b7e3a01-9c45-4af3-81bb-1bf08a842226)] -interface nsITelephoneCallback : nsISupports +[scriptable, uuid(03eafd60-d138-4f09-81b4-90cd4996b3c7)] +interface nsIRILTelephonyCallback : nsISupports { - // 'callState' uses the CALL_STATE values from nsITelephone. + /** + * Notified when a telephony call changes state. + * + * @param callIndex + * Call identifier assigned by the RIL. + * @param callState + * One of the nsIRadioInterfaceLayer::CALL_STATE_* values. + * @param number + * Number of the other party. + */ void callStateChanged(in unsigned long callIndex, in unsigned short callState, in AString number); - // 'callState' uses the CALL_STATE values from nsITelephone. Return true to - // continue enumeration or false to cancel. + /** + * Called when nsIRadioInterfaceLayer is asked to enumerate the current + * telephony call state (nsIRadioInterfaceLayer::enumerateCalls). This is + * called once per call that is currently managed by the RIL. + * + * @param callIndex + * Call identifier assigned by the RIL. + * @param callState + * One of the nsIRadioInterfaceLayer::CALL_STATE_* values. + * @param number + * Number of the other party. + * @param isActive + * Indicates whether this call is the active one. + * + * @return true to continue enumeration or false to cancel. + */ boolean enumerateCallState(in unsigned long callIndex, in unsigned short callState, in AString number, in boolean isActive); }; -[scriptable, uuid(8399fddd-471c-41ac-8f35-99f7dbb738ec)] -interface nsIDataCallInfo : nsISupports +[scriptable, uuid(66a55943-e63b-4731-aece-9c04bfc14019)] +interface nsIRILDataCallInfo : nsISupports { readonly attribute unsigned long callState; readonly attribute AString cid; readonly attribute AString apn; }; -[scriptable, uuid(36cc4b89-0338-4ff7-a3c2-d78e60f2ea98)] -interface nsIPhoneDataCallCallback : nsISupports +[scriptable, uuid(cea91bcb-3cfb-42bb-8638-dae89e8870fc)] +interface nsIRILDataCallback : nsISupports { /** - * This method is called when the state of a data call is changed. + * Notified when a data call changes state. * - * @param dataState use DATACALL_STATE_* values from nsITelephone. + * @param cid + * The CID of the data call. + * @param interfaceName + * Name of the associated network interface. + * @param dataState + * One of the nsIRadioInterfaceLayer::DATACALL_STATE_* values. */ void dataCallStateChanged(in AString cid, in AString interfaceName, in unsigned short callState); - void receiveDataCallList([array,size_is(aLength)] in nsIDataCallInfo aDataCalls, - in unsigned long aLength); + /** + * Called when nsIRadioInterfaceLayer is asked to enumerate the current + * data call state. + * + * @param datacalls + * Array of nsIRILDataCallInfo objects. + * @param length + * Lenght of the aforementioned array. + */ + void receiveDataCallList([array,size_is(length)] in nsIRILDataCallInfo dataCalls, + in unsigned long length); }; -[scriptable, uuid(78ed0beb-d6ad-42f8-929a-8d003285784f)] -interface nsITelephone : nsISupports +[scriptable, uuid(9b7e3a01-9c45-4af3-81bb-1bf08a842226)] +interface nsIRadioInterfaceLayer : nsISupports { const unsigned short CALL_STATE_UNKNOWN = 0; const unsigned short CALL_STATE_DIALING = 1; const unsigned short CALL_STATE_RINGING = 2; const unsigned short CALL_STATE_BUSY = 3; const unsigned short CALL_STATE_CONNECTING = 4; const unsigned short CALL_STATE_CONNECTED = 5; const unsigned short CALL_STATE_HOLDING = 6; @@ -97,52 +136,54 @@ interface nsITelephone : nsISupports const unsigned short DATACALL_STATE_UNKNOWN = 0; const unsigned short DATACALL_STATE_CONNECTING = 1; const unsigned short DATACALL_STATE_CONNECTED = 2; const unsigned short DATACALL_STATE_DISCONNECTING = 3; const unsigned short DATACALL_STATE_DISCONNECTED = 4; readonly attribute jsval currentState; - void registerCallback(in nsITelephoneCallback callback); - void unregisterCallback(in nsITelephoneCallback callback); + void registerCallback(in nsIRILTelephonyCallback callback); + void unregisterCallback(in nsIRILTelephonyCallback callback); /** * Will continue calling callback.enumerateCallState until the callback * returns false. */ - void enumerateCalls(in nsITelephoneCallback callback); + void enumerateCalls(in nsIRILTelephonyCallback callback); /** * Functionality for making and managing phone calls. */ void dial(in DOMString number); void hangUp(in unsigned long callIndex); void startTone(in DOMString dtmfChar); void stopTone(); void answerCall(in unsigned long callIndex); void rejectCall(in unsigned long callIndex); attribute bool microphoneMuted; attribute bool speakerEnabled; - // PDP APIs + /** + * PDP APIs + */ void setupDataCall(in long radioTech, in DOMString apn, in DOMString user, in DOMString passwd, in long chappap, in DOMString pdptype); void deactivateDataCall(in DOMString cid, in DOMString reason); void getDataCallList(); - void registerDataCallCallback(in nsIPhoneDataCallCallback callback); - void unregisterDataCallCallback(in nsIPhoneDataCallCallback callback); + void registerDataCallCallback(in nsIRILDataCallback callback); + void unregisterDataCallCallback(in nsIRILDataCallback callback); /** * SMS-related functionality. */ unsigned short getNumberOfMessagesForText(in DOMString text); void sendSMS(in DOMString number, in DOMString message); };
rename from dom/telephony/nsTelephonyWorker.h rename to dom/system/b2g/nsRadioInterfaceLayer.h --- a/dom/telephony/nsTelephonyWorker.h +++ b/dom/system/b2g/nsRadioInterfaceLayer.h @@ -30,12 +30,12 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -// This must always match the CID given in nsTelephonyWorker.manifest! -#define NS_TELEPHONYWORKER_CID \ +// This must always match the CID given in RadioInterfaceLayer.manifest! +#define NS_RADIOINTERFACELAYER_CID \ { 0x2d831c8d, 0x6017, 0x435b, \ { 0xa8, 0x0c, 0xe5, 0xd4, 0x22, 0x81, 0x0c, 0xea } }
--- a/dom/telephony/Makefile.in +++ b/dom/telephony/Makefile.in @@ -56,23 +56,17 @@ CPPSRCS = \ CallEvent.cpp \ $(NULL) XPIDLSRCS = \ nsIDOMNavigatorTelephony.idl \ nsIDOMTelephony.idl \ nsIDOMTelephonyCall.idl \ nsIDOMCallEvent.idl \ - nsITelephone.idl \ - $(NULL) - -EXTRA_COMPONENTS = \ - nsTelephonyWorker.manifest \ - nsTelephonyWorker.js \ $(NULL) -EXTRA_JS_MODULES = \ - ril_consts.js \ - ril_worker.js \ - $(NULL) +#LOCAL_INCLUDES = \ +# -I$(topsrcdir)/dom/base \ +# -I$(topsrcdir)/dom/system/b2g \ +# -I$(topsrcdir)/content/events/src \ +# $(NULL) include $(topsrcdir)/config/rules.mk -
--- a/dom/telephony/Telephony.cpp +++ b/dom/telephony/Telephony.cpp @@ -107,49 +107,49 @@ nsTArrayToJSArray(JSContext* aCx, JSObje *aResultArray = arrayObj; return NS_OK; } } // anonymous namespace Telephony::~Telephony() { - if (mTelephone && mTelephoneCallback) { - mTelephone->UnregisterCallback(mTelephoneCallback); + if (mRIL && mRILTelephonyCallback) { + mRIL->UnregisterCallback(mRILTelephonyCallback); } if (mRooted) { NS_DROP_JS_OBJECTS(this, Telephony); } } // static already_AddRefed<Telephony> -Telephony::Create(nsPIDOMWindow* aOwner, nsITelephone* aTelephone) +Telephony::Create(nsPIDOMWindow* aOwner, nsIRadioInterfaceLayer* aRIL) { NS_ASSERTION(aOwner, "Null owner!"); - NS_ASSERTION(aTelephone, "Null telephone!"); + NS_ASSERTION(aRIL, "Null RIL!"); nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner); NS_ENSURE_TRUE(sgo, nsnull); nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext(); NS_ENSURE_TRUE(scriptContext, nsnull); nsRefPtr<Telephony> telephony = new Telephony(); telephony->mOwner = aOwner; telephony->mScriptContext.swap(scriptContext); - telephony->mTelephone = aTelephone; - telephony->mTelephoneCallback = new TelephoneCallback(telephony); + telephony->mRIL = aRIL; + telephony->mRILTelephonyCallback = new RILTelephonyCallback(telephony); - nsresult rv = aTelephone->EnumerateCalls(telephony->mTelephoneCallback); + nsresult rv = aRIL->EnumerateCalls(telephony->mRILTelephonyCallback); NS_ENSURE_SUCCESS(rv, nsnull); - rv = aTelephone->RegisterCallback(telephony->mTelephoneCallback); + rv = aRIL->RegisterCallback(telephony->mRILTelephonyCallback); NS_ENSURE_SUCCESS(rv, nsnull); return telephony.forget(); } void Telephony::SwitchActiveCall(TelephonyCall* aCall) { @@ -190,78 +190,78 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_ NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Telephony) NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetWrapperCache) NS_IMPL_ADDREF_INHERITED(Telephony, nsDOMEventTargetWrapperCache) NS_IMPL_RELEASE_INHERITED(Telephony, nsDOMEventTargetWrapperCache) DOMCI_DATA(Telephony, Telephony) -NS_IMPL_ISUPPORTS1(Telephony::TelephoneCallback, nsITelephoneCallback) +NS_IMPL_ISUPPORTS1(Telephony::RILTelephonyCallback, nsIRILTelephonyCallback) NS_IMETHODIMP Telephony::Dial(const nsAString& aNumber, nsIDOMTelephonyCall** aResult) { NS_ENSURE_ARG(!aNumber.IsEmpty()); for (PRUint32 index = 0; index < mCalls.Length(); index++) { const nsRefPtr<TelephonyCall>& tempCall = mCalls[index]; if (tempCall->IsOutgoing() && - tempCall->CallState() < nsITelephone::CALL_STATE_CONNECTED) { + tempCall->CallState() < nsIRadioInterfaceLayer::CALL_STATE_CONNECTED) { // One call has been dialed already and we only support one outgoing call // at a time. NS_WARNING("Only permitted to dial one call at a time!"); return NS_ERROR_NOT_AVAILABLE; } } - nsresult rv = mTelephone->Dial(aNumber); + nsresult rv = mRIL->Dial(aNumber); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr<TelephonyCall> call = - TelephonyCall::Create(this, aNumber, nsITelephone::CALL_STATE_DIALING); + TelephonyCall::Create(this, aNumber, nsIRadioInterfaceLayer::CALL_STATE_DIALING); NS_ASSERTION(call, "This should never fail!"); NS_ASSERTION(mCalls.Contains(call), "Should have auto-added new call!"); call.forget(aResult); return NS_OK; } NS_IMETHODIMP Telephony::GetMuted(bool* aMuted) { - nsresult rv = mTelephone->GetMicrophoneMuted(aMuted); + nsresult rv = mRIL->GetMicrophoneMuted(aMuted); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP Telephony::SetMuted(bool aMuted) { - nsresult rv = mTelephone->SetMicrophoneMuted(aMuted); + nsresult rv = mRIL->SetMicrophoneMuted(aMuted); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP Telephony::GetSpeakerEnabled(bool* aSpeakerEnabled) { - nsresult rv = mTelephone->GetSpeakerEnabled(aSpeakerEnabled); + nsresult rv = mRIL->GetSpeakerEnabled(aSpeakerEnabled); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP Telephony::SetSpeakerEnabled(bool aSpeakerEnabled) { - nsresult rv = mTelephone->SetSpeakerEnabled(aSpeakerEnabled); + nsresult rv = mRIL->SetSpeakerEnabled(aSpeakerEnabled); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP Telephony::GetActive(jsval* aActive) { @@ -334,26 +334,26 @@ Telephony::StartTone(const nsAString& aD NS_WARNING("Empty tone string will be ignored"); return NS_OK; } if (aDTMFChar.Length() > 1) { return NS_ERROR_INVALID_ARG; } - nsresult rv = mTelephone->StartTone(aDTMFChar); + nsresult rv = mRIL->StartTone(aDTMFChar); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP Telephony::StopTone() { - nsresult rv = mTelephone->StopTone(); + nsresult rv = mRIL->StopTone(); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP Telephony::SendTones(const nsAString& aTones, PRUint32 aToneDuration, PRUint32 aIntervalDuration) @@ -373,53 +373,53 @@ Telephony::CallStateChanged(PRUint32 aCa nsRefPtr<TelephonyCall> modifiedCall; nsRefPtr<TelephonyCall> outgoingCall; for (PRUint32 index = 0; index < mCalls.Length(); index++) { nsRefPtr<TelephonyCall>& tempCall = mCalls[index]; if (tempCall->CallIndex() == kOutgoingPlaceholderCallIndex) { NS_ASSERTION(!outgoingCall, "More than one outgoing call not supported!"); - NS_ASSERTION(tempCall->CallState() == nsITelephone::CALL_STATE_DIALING, + NS_ASSERTION(tempCall->CallState() == nsIRadioInterfaceLayer::CALL_STATE_DIALING, "Something really wrong here!"); // Stash this for later, we may need it if aCallIndex doesn't match one of // our other calls. outgoingCall = tempCall; } else if (tempCall->CallIndex() == aCallIndex) { // We already know about this call so just update its state. modifiedCall = tempCall; outgoingCall = nsnull; break; } } // If nothing matched above and the call state isn't incoming but we do have // an outgoing call then we must be seeing a status update for our outgoing // call. if (!modifiedCall && - aCallState != nsITelephone::CALL_STATE_INCOMING && + aCallState != nsIRadioInterfaceLayer::CALL_STATE_INCOMING && outgoingCall) { outgoingCall->UpdateCallIndex(aCallIndex); modifiedCall.swap(outgoingCall); } if (modifiedCall) { // Change state. modifiedCall->ChangeState(aCallState); // See if this should replace our current active call. - if (aCallState == nsITelephone::CALL_STATE_CONNECTED) { + if (aCallState == nsIRadioInterfaceLayer::CALL_STATE_CONNECTED) { SwitchActiveCall(modifiedCall); } return NS_OK; } // Didn't know anything about this call before now, must be incoming. - NS_ASSERTION(aCallState == nsITelephone::CALL_STATE_INCOMING, + NS_ASSERTION(aCallState == nsIRadioInterfaceLayer::CALL_STATE_INCOMING, "Serious logic problem here!"); nsRefPtr<TelephonyCall> call = TelephonyCall::Create(this, aNumber, aCallState, aCallIndex); NS_ASSERTION(call, "This should never fail!"); NS_ASSERTION(mCalls.Contains(call), "Should have auto-added new call!"); @@ -506,17 +506,17 @@ NS_NewTelephony(nsPIDOMWindow* aWindow, return NS_OK; } } // Security checks passed, make a telephony object. nsIInterfaceRequestor* ireq = SystemWorkerManager::GetInterfaceRequestor(); NS_ENSURE_TRUE(ireq, NS_ERROR_UNEXPECTED); - nsCOMPtr<nsITelephone> telephone = do_GetInterface(ireq); - NS_ENSURE_TRUE(telephone, NS_ERROR_UNEXPECTED); + nsCOMPtr<nsIRadioInterfaceLayer> ril = do_GetInterface(ireq); + NS_ENSURE_TRUE(ril, NS_ERROR_UNEXPECTED); - nsRefPtr<Telephony> telephony = Telephony::Create(innerWindow, telephone); + nsRefPtr<Telephony> telephony = Telephony::Create(innerWindow, ril); NS_ENSURE_TRUE(telephony, NS_ERROR_UNEXPECTED); telephony.forget(aTelephony); return NS_OK; }
--- a/dom/telephony/Telephony.h +++ b/dom/telephony/Telephony.h @@ -39,51 +39,51 @@ #ifndef mozilla_dom_telephony_telephony_h__ #define mozilla_dom_telephony_telephony_h__ #include "TelephonyCommon.h" #include "nsIDOMTelephony.h" #include "nsIDOMTelephonyCall.h" -#include "nsITelephone.h" +#include "nsIRadioInterfaceLayer.h" class nsIScriptContext; class nsPIDOMWindow; BEGIN_TELEPHONY_NAMESPACE class Telephony : public nsDOMEventTargetWrapperCache, public nsIDOMTelephony { - nsCOMPtr<nsITelephone> mTelephone; - nsCOMPtr<nsITelephoneCallback> mTelephoneCallback; + nsCOMPtr<nsIRadioInterfaceLayer> mRIL; + nsCOMPtr<nsIRILTelephonyCallback> mRILTelephonyCallback; NS_DECL_EVENT_HANDLER(incoming); TelephonyCall* mActiveCall; nsTArray<nsRefPtr<TelephonyCall> > mCalls; // Cached calls array object. Cleared whenever mCalls changes and then rebuilt // once a page looks for the liveCalls attribute. JSObject* mCallsArray; bool mRooted; public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMTELEPHONY - NS_DECL_NSITELEPHONECALLBACK + NS_DECL_NSIRILTELEPHONYCALLBACK NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetWrapperCache::) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED( Telephony, nsDOMEventTargetWrapperCache) static already_AddRefed<Telephony> - Create(nsPIDOMWindow* aOwner, nsITelephone* aTelephone); + Create(nsPIDOMWindow* aOwner, nsIRadioInterfaceLayer* aRIL); nsIDOMEventTarget* ToIDOMEventTarget() const { return static_cast<nsDOMEventTargetWrapperCache*>( const_cast<Telephony*>(this)); } @@ -104,20 +104,20 @@ public: void RemoveCall(TelephonyCall* aCall) { NS_ASSERTION(mCalls.Contains(aCall), "Didn't know about this one!"); mCalls.RemoveElement(aCall); mCallsArray = nsnull; } - nsITelephone* - Telephone() const + nsIRadioInterfaceLayer* + RIL() const { - return mTelephone; + return mRIL; } nsPIDOMWindow* Owner() const { return mOwner; } @@ -132,25 +132,25 @@ private: : mActiveCall(nsnull), mCallsArray(nsnull), mRooted(false) { } ~Telephony(); void SwitchActiveCall(TelephonyCall* aCall); - class TelephoneCallback : public nsITelephoneCallback + class RILTelephonyCallback : public nsIRILTelephonyCallback { Telephony* mTelephony; public: NS_DECL_ISUPPORTS - NS_FORWARD_NSITELEPHONECALLBACK(mTelephony->) + NS_FORWARD_NSIRILTELEPHONYCALLBACK(mTelephony->) - TelephoneCallback(Telephony* aTelephony) + RILTelephonyCallback(Telephony* aTelephony) : mTelephony(aTelephony) { NS_ASSERTION(mTelephony, "Null pointer!"); } }; }; END_TELEPHONY_NAMESPACE
--- a/dom/telephony/TelephonyCall.cpp +++ b/dom/telephony/TelephonyCall.cpp @@ -70,61 +70,61 @@ TelephonyCall::Create(Telephony* aTeleph void TelephonyCall::ChangeStateInternal(PRUint16 aCallState, bool aFireEvents) { nsRefPtr<TelephonyCall> kungFuDeathGrip(this); nsString stateString; switch (aCallState) { - case nsITelephone::CALL_STATE_DIALING: + case nsIRadioInterfaceLayer::CALL_STATE_DIALING: stateString.AssignLiteral("dialing"); break; - case nsITelephone::CALL_STATE_RINGING: + case nsIRadioInterfaceLayer::CALL_STATE_RINGING: stateString.AssignLiteral("ringing"); break; - case nsITelephone::CALL_STATE_BUSY: + case nsIRadioInterfaceLayer::CALL_STATE_BUSY: stateString.AssignLiteral("busy"); break; - case nsITelephone::CALL_STATE_CONNECTING: + case nsIRadioInterfaceLayer::CALL_STATE_CONNECTING: stateString.AssignLiteral("connecting"); break; - case nsITelephone::CALL_STATE_CONNECTED: + case nsIRadioInterfaceLayer::CALL_STATE_CONNECTED: stateString.AssignLiteral("connected"); break; - case nsITelephone::CALL_STATE_HOLDING: + case nsIRadioInterfaceLayer::CALL_STATE_HOLDING: stateString.AssignLiteral("holding"); break; - case nsITelephone::CALL_STATE_HELD: + case nsIRadioInterfaceLayer::CALL_STATE_HELD: stateString.AssignLiteral("held"); break; - case nsITelephone::CALL_STATE_RESUMING: + case nsIRadioInterfaceLayer::CALL_STATE_RESUMING: stateString.AssignLiteral("resuming"); break; - case nsITelephone::CALL_STATE_DISCONNECTING: + case nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTING: stateString.AssignLiteral("disconnecting"); break; - case nsITelephone::CALL_STATE_DISCONNECTED: + case nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED: stateString.AssignLiteral("disconnected"); break; - case nsITelephone::CALL_STATE_INCOMING: + case nsIRadioInterfaceLayer::CALL_STATE_INCOMING: stateString.AssignLiteral("incoming"); break; default: NS_NOTREACHED("Unknown state!"); } mState = stateString; mCallState = aCallState; - if (aCallState == nsITelephone::CALL_STATE_DIALING) { + if (aCallState == nsIRadioInterfaceLayer::CALL_STATE_DIALING) { mOutgoing = true; } - if (aCallState == nsITelephone::CALL_STATE_DISCONNECTED) { + if (aCallState == nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED) { NS_ASSERTION(mLive, "Should be live!"); mTelephony->RemoveCall(this); mLive = false; } else if (!mLive) { mTelephony->AddCall(this); mLive = true; } @@ -203,43 +203,43 @@ TelephonyCall::GetState(nsAString& aStat { aState.Assign(mState); return NS_OK; } NS_IMETHODIMP TelephonyCall::Answer() { - if (mCallState != nsITelephone::CALL_STATE_INCOMING) { + if (mCallState != nsIRadioInterfaceLayer::CALL_STATE_INCOMING) { NS_WARNING("Answer on non-incoming call ignored!"); return NS_OK; } - nsresult rv = mTelephony->Telephone()->AnswerCall(mCallIndex); + nsresult rv = mTelephony->RIL()->AnswerCall(mCallIndex); NS_ENSURE_SUCCESS(rv, rv); - ChangeStateInternal(nsITelephone::CALL_STATE_CONNECTING, true); + ChangeStateInternal(nsIRadioInterfaceLayer::CALL_STATE_CONNECTING, true); return NS_OK; } NS_IMETHODIMP TelephonyCall::HangUp() { - if (mCallState == nsITelephone::CALL_STATE_DISCONNECTING || - mCallState == nsITelephone::CALL_STATE_DISCONNECTED) { + if (mCallState == nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTING || + mCallState == nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTED) { NS_WARNING("HangUp on previously disconnected call ignored!"); return NS_OK; } - nsresult rv = mCallState == nsITelephone::CALL_STATE_INCOMING ? - mTelephony->Telephone()->RejectCall(mCallIndex) : - mTelephony->Telephone()->HangUp(mCallIndex); + nsresult rv = mCallState == nsIRadioInterfaceLayer::CALL_STATE_INCOMING ? + mTelephony->RIL()->RejectCall(mCallIndex) : + mTelephony->RIL()->HangUp(mCallIndex); NS_ENSURE_SUCCESS(rv, rv); - ChangeStateInternal(nsITelephone::CALL_STATE_DISCONNECTING, true); + ChangeStateInternal(nsIRadioInterfaceLayer::CALL_STATE_DISCONNECTING, true); return NS_OK; } NS_IMPL_EVENT_HANDLER(TelephonyCall, statechange) NS_IMPL_EVENT_HANDLER(TelephonyCall, dialing) NS_IMPL_EVENT_HANDLER(TelephonyCall, ringing) NS_IMPL_EVENT_HANDLER(TelephonyCall, busy) NS_IMPL_EVENT_HANDLER(TelephonyCall, connecting)
--- a/dom/telephony/TelephonyCall.h +++ b/dom/telephony/TelephonyCall.h @@ -38,17 +38,17 @@ * ***** END LICENSE BLOCK ***** */ #ifndef mozilla_dom_telephony_telephonycall_h__ #define mozilla_dom_telephony_telephonycall_h__ #include "TelephonyCommon.h" #include "nsIDOMTelephonyCall.h" -#include "nsITelephone.h" +#include "nsIRadioInterfaceLayer.h" class nsPIDOMWindow; BEGIN_TELEPHONY_NAMESPACE class TelephonyCall : public nsDOMEventTargetWrapperCache, public nsIDOMTelephonyCall { @@ -126,17 +126,17 @@ public: IsOutgoing() const { return mOutgoing; } private: TelephonyCall() : mCallIndex(kOutgoingPlaceholderCallIndex), - mCallState(nsITelephone::CALL_STATE_UNKNOWN), mLive(false), mOutgoing(false) + mCallState(nsIRadioInterfaceLayer::CALL_STATE_UNKNOWN), mLive(false), mOutgoing(false) { } ~TelephonyCall() { } void ChangeStateInternal(PRUint16 aCallState, bool aFireEvents); };
--- a/dom/tests/mochitest/chrome/focus_frameset.html +++ b/dom/tests/mochitest/chrome/focus_frameset.html @@ -9,14 +9,14 @@ SimpleTest.waitForFocus(function () open <frameset rows="30%, 70%"> <frame src="data:text/html,<html id='f1' ><body id='framebody1'><input id='f2'><body></html>"> <frameset cols="30%, 33%, 34%"> <frame src="data:text/html,<html id='f3'><body id='framebody2'><input id='f4'><body></html>"> <frame src="data:text/html,<html id='f5'><body id='framebody3'><input id='f6' tabindex='2'><body></html>"> <frame src="data:text/html,<html id='f7'><body id='framebody4'><input id='f8'><body></html>"> </frameset> <noframes> - <input id="unusued1"/> - <input id="unusued2" tabindex="1"/> + <input id="unused1"/> + <input id="unused2" tabindex="1"/> </noframes> </frameset> </html>
--- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -108,17 +108,18 @@ public class GeckoAppShell // helper methods public static native void setSurfaceView(GeckoSurfaceView sv); public static native void putenv(String map); public static native void onResume(); public static native void onLowMemory(); public static native void callObserver(String observerKey, String topic, String data); public static native void removeObserver(String observerKey); - public static native void loadLibs(String apkName, boolean shouldExtract); + public static native void loadGeckoLibsNative(String apkName); + public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract); public static native void onChangeNetworkLinkStatus(String status); public static native void reportJavaCrash(String stack); public static native void processNextNativeEvent(); public static native void notifyBatteryChange(double aLevel, boolean aCharging, double aRemainingTime); public static native void notifySmsReceived(String aSender, String aBody, long aTimestamp); @@ -391,17 +392,18 @@ public class GeckoAppShell Iterator cacheFiles = Arrays.asList(files).iterator(); while (cacheFiles.hasNext()) { File libFile = (File)cacheFiles.next(); if (libFile.getName().endsWith(".so")) libFile.delete(); } } } - loadLibs(apkName, extractLibs); + loadSQLiteLibsNative(apkName, extractLibs); + loadGeckoLibsNative(apkName); } private static void putLocaleEnv() { GeckoAppShell.putenv("LANG=" + Locale.getDefault().toString()); NumberFormat nf = NumberFormat.getInstance(); if (nf instanceof DecimalFormat) { DecimalFormat df = (DecimalFormat)nf; DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
--- a/gfx/2d/DrawTargetCG.cpp +++ b/gfx/2d/DrawTargetCG.cpp @@ -111,16 +111,17 @@ DrawTargetCG::DrawTargetCG() DrawTargetCG::~DrawTargetCG() { // We need to conditionally release these because Init can fail without initializing these. if (mColorSpace) CGColorSpaceRelease(mColorSpace); if (mCg) CGContextRelease(mCg); + free(mData); } TemporaryRef<SourceSurface> DrawTargetCG::Snapshot() { RefPtr<SourceSurfaceCG> newSurf = new SourceSurfaceCG(CGBitmapContextCreateImage(mCg)); return newSurf; } @@ -840,17 +841,16 @@ DrawTargetCG::Init(const IntSize &aSize, CGBitmapInfo bitinfo; bitinfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst; // XXX: currently we allocate ourselves so that we can easily return a gfxImageSurface // we might not need to later if once we don't need to support gfxImageSurface //XXX: currently Init implicitly clears, that can often be a waste of time - // XXX: leaked mData = calloc(mSize.height * stride, 1); // XXX: what should we do if this fails? mCg = CGBitmapContextCreate (mData, mSize.width, mSize.height, bitsPerComponent, stride, mColorSpace,
--- a/gfx/layers/Makefile.in +++ b/gfx/layers/Makefile.in @@ -126,22 +126,29 @@ CPPSRCS += \ ThebesLayerD3D10.cpp \ $(NULL) endif endif EXPORTS_NAMESPACES = gfxipc mozilla/layers EXPORTS_gfxipc = ShadowLayerUtils.h EXPORTS_mozilla/layers =\ + CompositorCocoaWidgetHelper.h \ + CompositorChild.h \ + CompositorParent.h \ ShadowLayers.h \ ShadowLayersChild.h \ ShadowLayersParent.h \ + ShadowLayersManager.h \ $(NULL) CPPSRCS += \ + CompositorCocoaWidgetHelper.cpp \ + CompositorChild.cpp \ + CompositorParent.cpp \ ShadowLayers.cpp \ ShadowLayerChild.cpp \ ShadowLayersChild.cpp \ ShadowLayerParent.cpp \ ShadowLayersParent.cpp \ $(NULL) ifdef MOZ_X11 #{
--- a/gfx/layers/d3d10/ImageLayerD3D10.cpp +++ b/gfx/layers/d3d10/ImageLayerD3D10.cpp @@ -45,48 +45,52 @@ namespace mozilla { namespace layers { static already_AddRefed<ID3D10Texture2D> SurfaceToTexture(ID3D10Device *aDevice, gfxASurface *aSurface, const gfxIntSize &aSize) { - if (aSurface && aSurface->GetType() == gfxASurface::SurfaceTypeD2D) { + if (!aSurface) { + return NULL; + } + + if (aSurface->GetType() == gfxASurface::SurfaceTypeD2D) { void *data = aSurface->GetData(&gKeyD3D10Texture); if (data) { nsRefPtr<ID3D10Texture2D> texture = static_cast<ID3D10Texture2D*>(data); ID3D10Device *dev; texture->GetDevice(&dev); if (dev == aDevice) { return texture.forget(); } } } nsRefPtr<gfxImageSurface> imageSurface = aSurface->GetAsImageSurface(); if (!imageSurface) { imageSurface = new gfxImageSurface(aSize, gfxASurface::ImageFormatARGB32); - + nsRefPtr<gfxContext> context = new gfxContext(imageSurface); context->SetSource(aSurface); context->SetOperator(gfxContext::OPERATOR_SOURCE); context->Paint(); } D3D10_SUBRESOURCE_DATA data; - + CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, imageSurface->GetSize().width, imageSurface->GetSize().height, 1, 1); desc.Usage = D3D10_USAGE_IMMUTABLE; - + data.pSysMem = imageSurface->Data(); data.SysMemPitch = imageSurface->Stride(); nsRefPtr<ID3D10Texture2D> texture; HRESULT hr = aDevice->CreateTexture2D(&desc, &data, getter_AddRefs(texture)); if (FAILED(hr)) { LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("Failed to create texture for image surface"),
new file mode 100644 --- /dev/null +++ b/gfx/layers/ipc/CompositorChild.cpp @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set sw=2 ts=2 et tw=80 : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Content App. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benoit Girard <bgirard@mozilla.com> + * Ali Juma <ajuma@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "CompositorChild.h" +#include "CompositorParent.h" +#include "LayerManagerOGL.h" +#include "mozilla/layers/ShadowLayersChild.h" + +using mozilla::layers::ShadowLayersChild; + +namespace mozilla { +namespace layers { + +CompositorChild::CompositorChild(LayerManager *aLayerManager) + : mLayerManager(aLayerManager) +{ + MOZ_COUNT_CTOR(CompositorChild); +} + +CompositorChild::~CompositorChild() +{ + MOZ_COUNT_DTOR(CompositorChild); +} + +void +CompositorChild::Destroy() +{ + mLayerManager = NULL; + size_t numChildren = ManagedPLayersChild().Length(); + NS_ABORT_IF_FALSE(0 == numChildren || 1 == numChildren, + "compositor must only have 0 or 1 layer forwarder"); + + if (numChildren) { + ShadowLayersChild* layers = + static_cast<ShadowLayersChild*>(ManagedPLayersChild()[0]); + layers->Destroy(); + } + SendStop(); +} + +PLayersChild* +CompositorChild::AllocPLayers(const LayersBackend &backend) +{ + return new ShadowLayersChild(); +} + +bool +CompositorChild::DeallocPLayers(PLayersChild* actor) +{ + delete actor; + return true; +} + +} // namespace layers +} // namespace mozilla +
new file mode 100644 --- /dev/null +++ b/gfx/layers/ipc/CompositorChild.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set sw=4 ts=8 et tw=80 : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Content App. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benoit Girard <bgirard@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_layers_CompositorChild_h +#define mozilla_layers_CompositorChild_h + +#include "mozilla/layers/PCompositorChild.h" + +namespace mozilla { +namespace layers { + +class LayerManager; +class CompositorParent; + +class CompositorChild : public PCompositorChild +{ + NS_INLINE_DECL_REFCOUNTING(CompositorChild) +public: + CompositorChild(LayerManager *aLayerManager); + virtual ~CompositorChild(); + + void Destroy(); + +protected: + virtual PLayersChild* AllocPLayers(const LayersBackend &aBackend); + virtual bool DeallocPLayers(PLayersChild *aChild); + +private: + nsRefPtr<LayerManager> mLayerManager; + + DISALLOW_EVIL_CONSTRUCTORS(CompositorChild); +}; + +} // layers +} // mozilla + +#endif // mozilla_layers_CompositorChild_h
new file mode 100644 --- /dev/null +++ b/gfx/layers/ipc/CompositorCocoaWidgetHelper.cpp @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 et tw=80 : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Content App. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benoit Girard <bgirard@mozilla.com> + * Ali Juma <ajuma@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "CompositorParent.h" +#include "CompositorCocoaWidgetHelper.h" +#include "nsDebug.h" + +namespace mozilla { +namespace layers { +namespace compositor { + +LayerManager* +GetLayerManager(CompositorParent* aParent) +{ + return aParent->GetLayerManager(); +} + + +} +} +}
new file mode 100644 --- /dev/null +++ b/gfx/layers/ipc/CompositorCocoaWidgetHelper.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set sw=4 ts=8 et tw=80 : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Content App. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benoit Girard <bgirard@mozilla.com> + * Ali Juma <ajuma@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_layers_CompositorCocoaWidgetHelper_h +#define mozilla_layers_CompositorCocoaWidgetHelper_h + +// Note we can't include IPDL-generated headers here, since this file is being +// used as a workaround for Bug 719036. + +namespace mozilla { +namespace layers { + +class CompositorParent; +class LayerManager; + +namespace compositor { + +// Needed when we cannot directly include CompositorParent.h since it includes +// an IPDL-generated header (e.g. in widget/cocoa/nsChildView.mm; see Bug 719036). +LayerManager* GetLayerManager(CompositorParent* aParent); + +} +} +} +#endif // mozilla_layers_CompositorCocoaWidgetHelper_h +
new file mode 100644 --- /dev/null +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -0,0 +1,159 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set sw=2 ts=2 et tw=80 : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Content App. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benoit Girard <bgirard@mozilla.com> + * Ali Juma <ajuma@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "CompositorParent.h" +#include "ShadowLayersParent.h" +#include "LayerManagerOGL.h" +#include "nsIWidget.h" + +namespace mozilla { +namespace layers { + +CompositorParent::CompositorParent(nsIWidget* aWidget) + : mStopped(false), mWidget(aWidget) +{ + MOZ_COUNT_CTOR(CompositorParent); +} + +CompositorParent::~CompositorParent() +{ + MOZ_COUNT_DTOR(CompositorParent); +} + +void +CompositorParent::Destroy() +{ + NS_ABORT_IF_FALSE(ManagedPLayersParent().Length() == 0, + "CompositorParent destroyed before managed PLayersParent"); + + // Ensure that the layer manager is destroyed on the compositor thread. + mLayerManager = NULL; +} + +bool +CompositorParent::RecvStop() +{ + mStopped = true; + Destroy(); + return true; +} + +void +CompositorParent::ScheduleComposition() +{ + CancelableTask *composeTask = NewRunnableMethod(this, &CompositorParent::Composite); + MessageLoop::current()->PostTask(FROM_HERE, composeTask); +} + +void +CompositorParent::Composite() +{ + if (mStopped || !mLayerManager) { + return; + } + + mLayerManager->EndEmptyTransaction(); +} + +// Go down shadow layer tree, setting properties to match their non-shadow +// counterparts. +static void +SetShadowProperties(Layer* aLayer) +{ + // FIXME: Bug 717688 -- Do these updates in ShadowLayersParent::RecvUpdate. + ShadowLayer* shadow = aLayer->AsShadowLayer(); + shadow->SetShadowTransform(aLayer->GetTransform()); + shadow->SetShadowVisibleRegion(aLayer->GetVisibleRegion()); + shadow->SetShadowClipRect(aLayer->GetClipRect()); + + for (Layer* child = aLayer->GetFirstChild(); + child; child = child->GetNextSibling()) { + SetShadowProperties(child); + } +} + +void +CompositorParent::ShadowLayersUpdated() +{ + const nsTArray<PLayersParent*>& shadowParents = ManagedPLayersParent(); + NS_ABORT_IF_FALSE(shadowParents.Length() <= 1, + "can only support at most 1 ShadowLayersParent"); + if (shadowParents.Length()) { + Layer* root = static_cast<ShadowLayersParent*>(shadowParents[0])->GetRoot(); + mLayerManager->SetRoot(root); + SetShadowProperties(root); + } + ScheduleComposition(); +} + +PLayersParent* +CompositorParent::AllocPLayers(const LayersBackend &backendType) +{ + if (backendType == LayerManager::LAYERS_OPENGL) { + nsRefPtr<LayerManagerOGL> layerManager = new LayerManagerOGL(mWidget); + mWidget = NULL; + mLayerManager = layerManager; + + if (!layerManager->Initialize()) { + NS_ERROR("Failed to init OGL Layers"); + return NULL; + } + + ShadowLayerManager* slm = layerManager->AsShadowManager(); + if (!slm) { + return NULL; + } + return new ShadowLayersParent(slm, this); + } else { + NS_ERROR("Unsupported backend selected for Async Compositor"); + return NULL; + } +} + +bool +CompositorParent::DeallocPLayers(PLayersParent* actor) +{ + delete actor; + return true; +} + +} // namespace layers +} // namespace mozilla +
new file mode 100644 --- /dev/null +++ b/gfx/layers/ipc/CompositorParent.h @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set sw=4 ts=8 et tw=80 : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Content App. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benoit Girard <bgirard@mozilla.com> + * Ali Juma <ajuma@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_layers_CompositorParent_h +#define mozilla_layers_CompositorParent_h + +#include "mozilla/layers/PCompositorParent.h" +#include "mozilla/layers/PLayersParent.h" +#include "ShadowLayersManager.h" + +class nsIWidget; + +namespace mozilla { +namespace layers { + +class LayerManager; + +class CompositorParent : public PCompositorParent, + public ShadowLayersManager +{ + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorParent) +public: + CompositorParent(nsIWidget* aWidget); + virtual ~CompositorParent(); + + virtual bool RecvStop() MOZ_OVERRIDE; + + virtual void ShadowLayersUpdated() MOZ_OVERRIDE; + void Destroy(); + + LayerManager* GetLayerManager() { return mLayerManager; } + +protected: + virtual PLayersParent* AllocPLayers(const LayersBackend &backendType); + virtual bool DeallocPLayers(PLayersParent* aLayers); + +private: + void ScheduleComposition(); + void Composite(); + + nsRefPtr<LayerManager> mLayerManager; + bool mStopped; + nsIWidget* mWidget; + + DISALLOW_EVIL_CONSTRUCTORS(CompositorParent); +}; + +} // layers +} // mozilla + +#endif // mozilla_layers_CompositorParent_h
new file mode 100644 --- /dev/null +++ b/gfx/layers/ipc/PCompositor.ipdl @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benoit Girard <bgirard@mozilla.com> + * Ali Juma <ajuma@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +include protocol PLayers; + +using mozilla::LayersBackend; +using mozilla::null_t; + +namespace mozilla { +namespace layers { + +/** + * The PCompositor protocol is used to manage communication between + * the main thread and the compositor thread context. It's primary + * purpose is to manage the PLayers sub protocol. + */ +// This should really be 'sync', but we're using 'rpc' as a workaround +// for Bug 716631. +rpc protocol PCompositor +{ + // A Compositor manages a single Layer Manager (PLayers) + manages PLayers; + +parent: + + // Clean up in preparation for destruction. + sync Stop(); + + sync PLayers(LayersBackend backend); +}; + +} // layers +} // mozilla
--- a/gfx/layers/ipc/PLayers.ipdl +++ b/gfx/layers/ipc/PLayers.ipdl @@ -33,17 +33,17 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -//include protocol PCompositor; +include protocol PCompositor; include protocol PLayer; include protocol PRenderFrame; include "gfxipc/ShadowLayerUtils.h"; using gfx3DMatrix; using gfxRGBA; using nsIntPoint; @@ -221,17 +221,17 @@ struct OpThebesBufferSwap { union EditReply { OpBufferSwap; OpThebesBufferSwap; OpImageSwap; }; sync protocol PLayers { - manager PRenderFrame /*or PCompositor or PMedia or PPlugin*/; + manager PRenderFrame or PCompositor; manages PLayer; parent: async PLayer(); sync Update(Edit[] cset) returns (EditReply[] reply);
new file mode 100644 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayersManager.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set sw=4 ts=8 et tw=80 : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Content App. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ali Juma <ajuma@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_layers_ShadowLayersManager_h +#define mozilla_layers_ShadowLayersManager_h + +namespace mozilla { + +namespace layout { +class RenderFrameParent; +} + +namespace layers { + +class CompositorParent; + +class ShadowLayersManager +{ + +public: + virtual void ShadowLayersUpdated() = 0; +}; + +} // layers +} // mozilla + +#endif // mozilla_layers_ShadowLayersManager_h
--- a/gfx/layers/ipc/ShadowLayersParent.cpp +++ b/gfx/layers/ipc/ShadowLayersParent.cpp @@ -42,16 +42,17 @@ #include "ShadowLayersParent.h" #include "ShadowLayerParent.h" #include "ShadowLayers.h" #include "mozilla/unused.h" #include "mozilla/layout/RenderFrameParent.h" +#include "CompositorParent.h" #include "gfxSharedImageSurface.h" #include "ImageLayers.h" typedef std::vector<mozilla::layers::EditReply> EditReplyVector; using mozilla::layout::RenderFrameParent; @@ -116,21 +117,21 @@ ShadowContainer(const OpRemoveChild& op) static ShadowLayerParent* ShadowChild(const OpRemoveChild& op) { return cast(op.childLayerParent()); } //-------------------------------------------------- // ShadowLayersParent -ShadowLayersParent::ShadowLayersParent(ShadowLayerManager* aManager) - : mDestroyed(false) +ShadowLayersParent::ShadowLayersParent(ShadowLayerManager* aManager, + ShadowLayersManager* aLayersManager) + : mLayerManager(aManager), mShadowLayersManager(aLayersManager), mDestroyed(false) { MOZ_COUNT_CTOR(ShadowLayersParent); - mLayerManager = aManager; } ShadowLayersParent::~ShadowLayersParent() { MOZ_COUNT_DTOR(ShadowLayersParent); } void @@ -377,17 +378,17 @@ ShadowLayersParent::RecvUpdate(const Inf reply->AppendElements(&replyv.front(), replyv.size()); } // Ensure that any pending operations involving back and front // buffers have completed, so that neither process stomps on the // other's buffer contents. ShadowLayerManager::PlatformSyncBeforeReplyUpdate(); - Frame()->ShadowLayersUpdated(); + mShadowLayersManager->ShadowLayersUpdated(); return true; } PLayerParent* ShadowLayersParent::AllocPLayer() { return new ShadowLayerParent(); @@ -395,22 +396,16 @@ ShadowLayersParent::AllocPLayer() bool ShadowLayersParent::DeallocPLayer(PLayerParent* actor) { delete actor; return true; } -RenderFrameParent* -ShadowLayersParent::Frame() -{ - return static_cast<RenderFrameParent*>(Manager()); -} - void ShadowLayersParent::DestroySharedSurface(gfxSharedImageSurface* aSurface) { layer_manager()->DestroySharedSurface(aSurface, this); } void ShadowLayersParent::DestroySharedSurface(SurfaceDescriptor* aSurface)
--- a/gfx/layers/ipc/ShadowLayersParent.h +++ b/gfx/layers/ipc/ShadowLayersParent.h @@ -38,16 +38,17 @@ * * ***** END LICENSE BLOCK ***** */ #ifndef mozilla_layers_ShadowLayersParent_h #define mozilla_layers_ShadowLayersParent_h #include "mozilla/layers/PLayersParent.h" #include "ShadowLayers.h" +#include "ShadowLayersManager.h" namespace mozilla { namespace layout { class RenderFrameParent; } namespace layers { @@ -58,17 +59,17 @@ class ShadowLayerManager; class ShadowLayersParent : public PLayersParent, public ISurfaceDeAllocator { typedef mozilla::layout::RenderFrameParent RenderFrameParent; typedef InfallibleTArray<Edit> EditArray; typedef InfallibleTArray<EditReply> EditReplyArray; public: - ShadowLayersParent(ShadowLayerManager* aManager); + ShadowLayersParent(ShadowLayerManager* aManager, ShadowLayersManager* aLayersManager); ~ShadowLayersParent(); void Destroy(); ShadowLayerManager* layer_manager() const { return mLayerManager; } ContainerLayer* GetRoot() const { return mRoot; } @@ -78,19 +79,18 @@ public: protected: NS_OVERRIDE virtual bool RecvUpdate(const EditArray& cset, EditReplyArray* reply); NS_OVERRIDE virtual PLayerParent* AllocPLayer(); NS_OVERRIDE virtual bool DeallocPLayer(PLayerParent* actor); private: - RenderFrameParent* Frame(); - nsRefPtr<ShadowLayerManager> mLayerManager; + ShadowLayersManager* mShadowLayersManager; // Hold the root because it might be grafted under various // containers in the "real" layer tree nsRefPtr<ContainerLayer> mRoot; // When the widget/frame/browser stuff in this process begins its // destruction process, we need to Disconnect() all the currently // live shadow layers, because some of them might be orphaned from // the layer tree. This happens in Destroy() above. After we // Destroy() ourself, there's a window in which that information
--- a/gfx/layers/ipc/ipdl.mk +++ b/gfx/layers/ipc/ipdl.mk @@ -1,4 +1,5 @@ IPDLSRCS = \ + PCompositor.ipdl \ PLayer.ipdl \ PLayers.ipdl \ $(NULL)
--- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -1405,18 +1405,16 @@ ContainerState::ProcessDisplayItems(cons // Note that items without their own layers can't be skipped this // way, since their ThebesLayer may decide it wants to draw them // into its buffer even if they're currently covered. if (itemVisibleRect.IsEmpty() && layerState != LAYER_ACTIVE_EMPTY) { InvalidateForLayerChange(item, nsnull); continue; } - aClip.RemoveRoundedCorners(); - // Just use its layer. nsRefPtr<Layer> ownLayer = item->BuildLayer(mBuilder, mManager, mParameters); if (!ownLayer) { InvalidateForLayerChange(item, ownLayer); continue; } // If it's not a ContainerLayer, we need to apply the scale transform @@ -1430,20 +1428,23 @@ ContainerState::ProcessDisplayItems(cons ownLayer->SetIsFixedPosition(!nsLayoutUtils::ScrolledByViewportScrolling( activeScrolledRoot, mBuilder)); // Update that layer's clip and visible rects. NS_ASSERTION(ownLayer->Manager() == mManager, "Wrong manager"); NS_ASSERTION(!ownLayer->HasUserData(&gLayerManagerUserData), "We shouldn't have a FrameLayerBuilder-managed layer here!"); + NS_ASSERTION(aClip.mHaveClipRect || + aClip.mRoundedClipRects.IsEmpty(), + "If we have rounded rects, we must have a clip rect"); // It has its own layer. Update that layer's clip and visible rects. if (aClip.mHaveClipRect) { ownLayer->IntersectClipRect( - aClip.mClipRect.ScaleToNearestPixels( + aClip.NonRoundedIntersection().ScaleToNearestPixels( mParameters.mXScale, mParameters.mYScale, appUnitsPerDevPixel)); } ThebesLayerData* data = GetTopThebesLayerData(); if (data) { data->mVisibleAboveRegion.Or(data->mVisibleAboveRegion, itemVisibleRect); data->mVisibleAboveRegion.SimplifyOutward(4); // Add the entire bounds rect to the mDrawAboveRegion. // The visible region may be excluding opaque content above the @@ -2359,17 +2360,16 @@ FrameLayerBuilder::Clip::IsRectClippedBy } } return false; } nsRect FrameLayerBuilder::Clip::NonRoundedIntersection() const { - NS_ASSERTION(!mRoundedClipRects.IsEmpty(), "no rounded clip rects?"); nsRect result = mClipRect; for (PRUint32 i = 0, iEnd = mRoundedClipRects.Length(); i < iEnd; ++i) { result.IntersectRect(result, mRoundedClipRects[i].mRect); } return result; }
--- a/layout/ipc/RenderFrameParent.cpp +++ b/layout/ipc/RenderFrameParent.cpp @@ -643,17 +643,17 @@ RenderFrameParent::AllocPLayers(LayerMan } LayerManager* lm = GetLayerManager(); ShadowLayerManager* slm = lm->AsShadowManager(); if (!slm) { *aBackendType = LayerManager::LAYERS_NONE; return nsnull; } *aBackendType = lm->GetBackendType(); - return new ShadowLayersParent(slm); + return new ShadowLayersParent(slm, this); } bool RenderFrameParent::DeallocPLayers(PLayersParent* aLayers) { delete aLayers; return true; }
--- a/layout/ipc/RenderFrameParent.h +++ b/layout/ipc/RenderFrameParent.h @@ -37,16 +37,17 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef mozilla_layout_RenderFrameParent_h #define mozilla_layout_RenderFrameParent_h #include "mozilla/layout/PRenderFrameParent.h" +#include "mozilla/layers/ShadowLayersManager.h" #include <map> #include "nsDisplayList.h" #include "Layers.h" class nsContentView; class nsFrameLoader; class nsSubDocumentFrame; @@ -54,17 +55,18 @@ class nsSubDocumentFrame; namespace mozilla { namespace layers { class ShadowLayersParent; } namespace layout { -class RenderFrameParent : public PRenderFrameParent +class RenderFrameParent : public PRenderFrameParent, + public mozilla::layers::ShadowLayersManager { typedef mozilla::layers::FrameMetrics FrameMetrics; typedef mozilla::layers::ContainerLayer ContainerLayer; typedef mozilla::layers::Layer Layer; typedef mozilla::layers::LayerManager LayerManager; typedef mozilla::layers::ShadowLayersParent ShadowLayersParent; typedef FrameMetrics::ViewID ViewID; @@ -79,17 +81,17 @@ public: /** * Helper function for getting a non-owning reference to a scrollable. * @param aId The ID of the frame. */ nsContentView* GetContentView(ViewID aId = FrameMetrics::ROOT_SCROLL_ID); void ContentViewScaleChanged(nsContentView* aView); - void ShadowLayersUpdated(); + virtual void ShadowLayersUpdated() MOZ_OVERRIDE; NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder, nsSubDocumentFrame* aFrame, const nsRect& aDirtyRect, const nsDisplayListSet& aLists); already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/718521-ref.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> +<head> +<title>Rounded rectangle clipping test</title> +<style> +.clipround { + left:100px; + top:100px; + position:absolute; + width:700px; + height:380px; + overflow:hidden; + border-radius:45px; +} +.greendiv { + width:300px; + height:230px; + background-color:#008000; + position:absolute; +} +#nrcDiv0 { + left:0px; + top:0px; +} +#nrcDiv1 { + left:320px; + top:0px; +} +#nrcDiv2 { + left:0px; + top:240px; +} +</style> +</head> + +<body> +<div class="clipround"> +<div id="nrcDiv0" class="greendiv"></div> +</div> +<div class="clipround"> +<div id="nrcDiv1" class="greendiv"></div> +</div> +<div class="clipround"> +<div id="nrcDiv2" class="greendiv"></div> +</div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/718521.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<html> +<head> +<title>Rounded rectangle clipping test</title> +<style> +.clipround { + left:100px; + top:100px; + position:absolute; + width:700px; + height:380px; + overflow:hidden; + border-radius:45px; +} +canvas { + position:absolute; +} +#nrcCanvas0 { + left:0px; + top:0px; +} +#nrcCanvas1 { + left:320px; + top:0px; +} +#nrcCanvas2 { + left:0px; + top:240px; +} +</style> +</head> + +<body> +<div class="clipround"> +<canvas id="nrcCanvas0" width="320" height="260"></canvas> +<canvas id="nrcCanvas1" width="320" height="260"></canvas> +<canvas id="nrcCanvas2" width="320" height="260"></canvas> +</div> + +<script> + drawShapes('nrcCanvas0'); + drawShapes('nrcCanvas1'); + drawShapes('nrcCanvas2'); + + function drawShapes(elName) { + var ctxt = document.getElementById(elName).getContext('2d'); + ctxt.fillStyle = '#008000'; + ctxt.fillRect(0, 0, 300, 230); + } +</script> +</body> +</html>
--- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1683,8 +1683,9 @@ needs-focus != 703186-1.html 703186-2.ht == 711359-1.html 711359-1-ref.html == 712849-1.html 712849-1-ref.html == 713856-static.html 713856-ref.html == 713856-dynamic.html 713856-ref.html == 714519-1-as.html 714519-1-ref.html == 714519-1-q.html 714519-1-ref.html == 714519-2-as.html 714519-2-ref.html == 714519-2-q.html 714519-2-ref.html +fuzzy == 718521.html 718521-ref.html
--- a/mobile/android/base/AboutHomeContent.java +++ b/mobile/android/base/AboutHomeContent.java @@ -168,17 +168,17 @@ public class AboutHomeContent extends Sc void init(final Activity activity) { mInflater.inflate(R.layout.abouthome_content, this); final Runnable generateCursorsRunnable = new Runnable() { public void run() { if (mCursor != null) activity.stopManagingCursor(mCursor); ContentResolver resolver = GeckoApp.mAppContext.getContentResolver(); - mCursor = BrowserDB.filter(resolver, "", NUMBER_OF_TOP_SITES_PORTRAIT, "about:%"); + mCursor = BrowserDB.getTopSites(resolver, NUMBER_OF_TOP_SITES_PORTRAIT); activity.startManagingCursor(mCursor); mTopSitesAdapter = new TopSitesCursorAdapter(activity, R.layout.abouthome_topsite_item, mCursor, new String[] { URLColumns.TITLE, URLColumns.THUMBNAIL }, new int[] { R.id.title, R.id.thumbnail });
--- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -888,17 +888,18 @@ abstract public class GeckoApp handleSecurityChange(tabId, mode); } else if (event.equals("Content:StateChange")) { final int tabId = message.getInt("tabID"); int state = message.getInt("state"); Log.i(LOGTAG, "State - " + state); if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) { if ((state & GeckoAppShell.WPL_STATE_START) != 0) { Log.i(LOGTAG, "Got a document start"); - handleDocumentStart(tabId); + final boolean showProgress = message.getBoolean("showProgress"); + handleDocumentStart(tabId, showProgress); } else if ((state & GeckoAppShell.WPL_STATE_STOP) != 0) { Log.i(LOGTAG, "Got a document stop"); handleDocumentStop(tabId); } } } else if (event.equals("Content:LoadError")) { final int tabId = message.getInt("tabID"); final String uri = message.getString("uri"); @@ -1176,29 +1177,30 @@ abstract public class GeckoApp mBrowserToolbar.setProgressVisibility(tab.isLoading()); mDoorHangerPopup.updatePopup(); mBrowserToolbar.setShadowVisibility(!(tab.getURL().startsWith("about:"))); } } }); } - void handleDocumentStart(int tabId) { + void handleDocumentStart(int tabId, final boolean showProgress) { final Tab tab = Tabs.getInstance().getTab(tabId); if (tab == null) return; tab.setLoading(true); tab.updateSecurityMode("unknown"); mMainHandler.post(new Runnable() { public void run() { if (Tabs.getInstance().isSelectedTab(tab)) { mBrowserToolbar.setSecurityMode(tab.getSecurityMode()); - mBrowserToolbar.setProgressVisibility(true); + if (showProgress) + mBrowserToolbar.setProgressVisibility(true); } onTabsChanged(tab); } }); } void handleDocumentStop(int tabId) { final Tab tab = Tabs.getInstance().getTab(tabId); @@ -2094,19 +2096,20 @@ abstract public class GeckoApp } return status; } private void checkMigrateProfile() { File profileDir = getProfileDir(); if (profileDir != null) { Log.i(LOGTAG, "checking profile migration in: " + profileDir.getAbsolutePath()); + final GeckoApp app = GeckoApp.mAppContext; + GeckoAppShell.ensureSQLiteLibsLoaded(app.getApplication().getPackageResourcePath()); ProfileMigrator profileMigrator = - new ProfileMigrator(GeckoApp.mAppContext.getContentResolver(), - profileDir); + new ProfileMigrator(app.getContentResolver(), profileDir); profileMigrator.launchBackground(); } } private SynchronousQueue<String> mFilePickerResult = new SynchronousQueue<String>(); public String showFilePicker(String aMimeType) { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE);
--- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -107,16 +107,17 @@ public class GeckoAppShell static public final int WPL_STATE_STOP = 0x00000010; static public final int WPL_STATE_IS_DOCUMENT = 0x00020000; static public final int WPL_STATE_IS_NETWORK = 0x00040000; static private File sCacheFile = null; static private int sFreeSpace = -1; static File sHomeDir = null; static private int sDensityDpi = 0; + private static Boolean sSQLiteLibsLoaded = false; private static HashMap<String, ArrayList<GeckoEventListener>> mEventListeners; /* The Android-side API: API methods that Android calls */ // Initialization methods public static native void nativeInit(); public static native void nativeRun(String args); @@ -124,17 +125,18 @@ public class GeckoAppShell // helper methods // public static native void setSurfaceView(GeckoSurfaceView sv); public static native void setSoftwareLayerClient(GeckoSoftwareLayerClient client); public static native void putenv(String map); public static native void onResume(); public static native void onLowMemory(); public static native void callObserver(String observerKey, String topic, String data); public static native void removeObserver(String observerKey); - public static native void loadLibs(String apkName, boolean shouldExtract); + public static native void loadGeckoLibsNative(String apkName); + public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract); public static native void onChangeNetworkLinkStatus(String status); public static native void reportJavaCrash(String stack); public static void notifyUriVisited(String uri) { sendEventToGecko(new GeckoEvent(GeckoEvent.VISTITED, uri)); } public static native void processNextNativeEvent(); @@ -305,17 +307,17 @@ public class GeckoAppShell from.delete(); } catch(Exception e) { Log.e(LOGTAG, "error trying to move file", e); } return retVal; } // java-side stuff - public static void loadGeckoLibs(String apkName) { + public static boolean loadLibsSetup(String apkName) { // The package data lib directory isn't placed in ld.so's // search path, so we have to manually load libraries that // libxul will depend on. Not ideal. GeckoApp geckoApp = GeckoApp.mAppContext; String homeDir; sHomeDir = GeckoDirProvider.getFilesDir(geckoApp); homeDir = sHomeDir.getPath(); @@ -410,17 +412,33 @@ public class GeckoAppShell Iterator cacheFiles = Arrays.asList(files).iterator(); while (cacheFiles.hasNext()) { File libFile = (File)cacheFiles.next(); if (libFile.getName().endsWith(".so")) libFile.delete(); } } } - loadLibs(apkName, extractLibs); + return extractLibs; + } + + public static void ensureSQLiteLibsLoaded(String apkName) { + if (sSQLiteLibsLoaded) + return; + synchronized(sSQLiteLibsLoaded) { + if (sSQLiteLibsLoaded) + return; + loadSQLiteLibsNative(apkName, loadLibsSetup(apkName)); + sSQLiteLibsLoaded = true; + } + } + + public static void loadGeckoLibs(String apkName) { + boolean extractLibs = loadLibsSetup(apkName); + loadGeckoLibsNative(apkName); } private static void putLocaleEnv() { GeckoAppShell.putenv("LANG=" + Locale.getDefault().toString()); NumberFormat nf = NumberFormat.getInstance(); if (nf instanceof DecimalFormat) { DecimalFormat df = (DecimalFormat)nf; DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
--- a/mobile/android/base/GeckoThread.java +++ b/mobile/android/base/GeckoThread.java @@ -81,18 +81,19 @@ public class GeckoThread extends Thread libs[i].delete(); } } } // At some point while loading the gecko libs our default locale gets set // so just save it to locale here and reset it as default after the join Locale locale = Locale.getDefault(); - GeckoAppShell.loadGeckoLibs( - app.getApplication().getPackageResourcePath()); + String resourcePath = app.getApplication().getPackageResourcePath(); + GeckoAppShell.ensureSQLiteLibsLoaded(resourcePath); + GeckoAppShell.loadGeckoLibs(resourcePath); Locale.setDefault(locale); Resources res = app.getBaseContext().getResources(); Configuration config = res.getConfiguration(); config.locale = locale; res.updateConfiguration(config, res.getDisplayMetrics()); Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - runGecko");
--- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -84,16 +84,19 @@ FENNEC_JAVA_FILES = \ GeckoInputConnection.java \ GeckoPreferences.java \ GeckoStateListDrawable.java \ GeckoThread.java \ GlobalHistory.java \ LinkPreference.java \ ProfileMigrator.java \ PromptService.java \ + sqlite/ByteBufferInputStream.java \ + sqlite/SQLiteBridge.java \ + sqlite/SQLiteBridgeException.java \ SurfaceLockInfo.java \ Tab.java \ Tabs.java \ TabsTray.java \ gfx/BitmapUtils.java \ gfx/BufferedCairoImage.java \ gfx/CairoGLInfo.java \ gfx/CairoImage.java \
--- a/mobile/android/base/ProfileMigrator.java +++ b/mobile/android/base/ProfileMigrator.java @@ -10,17 +10,17 @@ * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Android code. * * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2009-2010 + * Portions created by the Initial Developer are Copyright (C) 2011-2012 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Gian-Carlo Pascutto <gpascutto@mozilla.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), @@ -33,100 +33,93 @@ * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ package org.mozilla.gecko; import org.mozilla.gecko.db.BrowserDB; +import org.mozilla.gecko.sqlite.ByteBufferInputStream; +import org.mozilla.gecko.sqlite.SQLiteBridge; +import org.mozilla.gecko.sqlite.SQLiteBridgeException; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.database.sqlite.SQLiteStatement; import android.content.ContentResolver; import android.database.Cursor; +import android.database.SQLException; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.provider.Browser; import android.util.Log; -import android.webkit.WebIconDatabase; -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.File; +import java.nio.ByteBuffer; import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; import java.util.Map; import java.util.HashMap; import java.util.Date; import java.util.List; import java.util.Iterator; public class ProfileMigrator { private static final String LOGTAG = "ProfMigr"; private File mProfileDir; private ContentResolver mCr; - private SQLiteDatabase mDb; /* Amount of Android history entries we will remember to prevent moving their last access date backwards. */ private static final int MAX_HISTORY_TO_CHECK = 1000; /* These queries are derived from the low-level Places schema https://developer.mozilla.org/en/The_Places_database */ - final String bookmarkQuery = "SELECT places.url AS a_url, " + private final String bookmarkQuery = "SELECT places.url AS a_url, " + "places.title AS a_title FROM " + "(moz_places as places JOIN moz_bookmarks as bookmarks ON " + "places.id = bookmarks.fk) WHERE places.hidden <> 1 " + "ORDER BY bookmarks.dateAdded"; - // Don't ask why. Just curse along at the Android devs. - final String bookmarkUrl = "a_url"; - final String bookmarkTitle = "a_title"; + // Result column of relevant data + private final String bookmarkUrl = "a_url"; + private final String bookmarkTitle = "a_title"; - final String historyQuery = + private final String historyQuery = "SELECT places.url AS a_url, places.title AS a_title, " + "history.visit_date AS a_date FROM " + "(moz_historyvisits AS history JOIN moz_places AS places ON " + "places.id = history.place_id) WHERE places.hidden <> 1 " + "ORDER BY history.visit_date DESC"; - final String historyUrl = "a_url"; - final String historyTitle = "a_title"; - final String historyDate = "a_date"; + private final String historyUrl = "a_url"; + private final String historyTitle = "a_title"; + private final String historyDate = "a_date"; - final String faviconQuery = + private final String faviconQuery = "SELECT places.url AS a_url, favicon.data AS a_data, " + "favicon.mime_type AS a_mime FROM (moz_places AS places JOIN " + "moz_favicons AS favicon ON places.favicon_id = favicon.id)"; - final String faviconUrl = "a_url"; - final String faviconData = "a_data"; - final String faviconMime = "a_mime"; + private final String faviconUrl = "a_url"; + private final String faviconData = "a_data"; + private final String faviconMime = "a_mime"; public ProfileMigrator(ContentResolver cr, File profileDir) { mProfileDir = profileDir; mCr = cr; } public void launchBackground() { // Work around http://code.google.com/p/android/issues/detail?id=11291 - // The WebIconDatabase needs to be initialized within the UI thread so - // just request the instance here. - WebIconDatabase.getInstance(); - - PlacesTask placesTask = new PlacesTask(); - new Thread(placesTask).start(); + // WebIconDatabase needs to be initialized within a looper thread. + GeckoAppShell.getHandler().post(new PlacesTask()); } private class PlacesTask implements Runnable { // Get a list of the last times an URL was accessed protected Map<String, Long> gatherAndroidHistory() { Map<String, Long> history = new HashMap<String, Long>(); Cursor cursor = BrowserDB.getRecentHistory(mCr, MAX_HISTORY_TO_CHECK); @@ -172,47 +165,34 @@ public class ProfileMigrator { BrowserDB.updateVisitedHistory(mCr, url); BrowserDB.updateHistoryDate(mCr, url, date); if (title != null) { BrowserDB.updateHistoryTitle(mCr, url, title); } } } - protected void migrateHistory(SQLiteDatabase db) { + protected void migrateHistory(SQLiteBridge db) { Map<String, Long> androidHistory = gatherAndroidHistory(); final ArrayList<String> placesHistory = new ArrayList<String>(); - Cursor cursor = null; try { - cursor = - db.rawQuery(historyQuery, new String[] { }); - final int urlCol = - cursor.getColumnIndexOrThrow(historyUrl); - final int titleCol = - cursor.getColumnIndexOrThrow(historyTitle); - final int dateCol = - cursor.getColumnIndexOrThrow(historyDate); + ArrayList<Object[]> queryResult = db.query(historyQuery); + final int urlCol = db.getColumnIndex(historyUrl); + final int titleCol = db.getColumnIndex(historyTitle); + final int dateCol = db.getColumnIndex(historyDate); - cursor.moveToFirst(); - while (!cursor.isAfterLast()) { - String url = cursor.getString(urlCol); - String title = cursor.getString(titleCol); - // Convert from us (Places) to ms (Java, Android) - long date = cursor.getLong(dateCol) / (long)1000; + for (Object[] resultRow: queryResult) { + String url = (String)resultRow[urlCol]; + String title = (String)resultRow[titleCol]; + long date = Long.parseLong((String)(resultRow[dateCol])) / (long)1000; addHistory(androidHistory, url, title, date); placesHistory.add(url); - cursor.moveToNext(); } - - cursor.close(); - } catch (SQLiteException e) { - if (cursor != null) { - cursor.close(); - } + } catch (SQLiteBridgeException e) { Log.i(LOGTAG, "Failed to get bookmarks: " + e.getMessage()); return; } // GlobalHistory access communicates with Gecko // and must run on its thread GeckoAppShell.getHandler().post(new Runnable() { public void run() { for (String url : placesHistory) { @@ -226,129 +206,94 @@ public class ProfileMigrator { if (!BrowserDB.isBookmark(mCr, url)) { if (title == null) { title = url; } BrowserDB.addBookmark(mCr, title, url); } } - protected void migrateBookmarks(SQLiteDatabase db) { - Cursor cursor = null; + protected void migrateBookmarks(SQLiteBridge db) { try { - cursor = db.rawQuery(bookmarkQuery, - new String[] {}); - if (cursor.getCount() > 0) { - final int urlCol = - cursor.getColumnIndexOrThrow(bookmarkUrl); - final int titleCol = - cursor.getColumnIndexOrThrow(bookmarkTitle); + ArrayList<Object[]> queryResult = db.query(bookmarkQuery); + final int urlCol = db.getColumnIndex(bookmarkUrl); + final int titleCol = db.getColumnIndex(bookmarkTitle); - cursor.moveToFirst(); - while (!cursor.isAfterLast()) { - String url = cursor.getString(urlCol); - String title = cursor.getString(titleCol); - addBookmark(url, title); - cursor.moveToNext(); - } + for (Object[] resultRow: queryResult) { + String url = (String)resultRow[urlCol]; + String title = (String)resultRow[titleCol]; + addBookmark(url, title); } - cursor.close(); - } catch (SQLiteException e) { - if (cursor != null) { - cursor.close(); - } + } catch (SQLiteBridgeException e) { Log.i(LOGTAG, "Failed to get bookmarks: " + e.getMessage()); return; } } - protected void addFavicon(String url, String mime, byte[] data) { - ByteArrayInputStream byteStream = new ByteArrayInputStream(data); + protected void addFavicon(String url, String mime, ByteBuffer data) { + ByteBufferInputStream byteStream = new ByteBufferInputStream(data); BitmapDrawable image = (BitmapDrawable) Drawable.createFromStream(byteStream, "src"); if (image != null) { try { BrowserDB.updateFaviconForUrl(mCr, url, image); - } catch (SQLiteException e) { + } catch (SQLException e) { Log.i(LOGTAG, "Migrating favicon failed: " + mime + " URL: " + url + " error:" + e.getMessage()); } } } - protected void migrateFavicons(SQLiteDatabase db) { - Cursor cursor = null; + protected void migrateFavicons(SQLiteBridge db) { try { - cursor = db.rawQuery(faviconQuery, - new String[] {}); - if (cursor.getCount() > 0) { - final int urlCol = - cursor.getColumnIndexOrThrow(faviconUrl); - final int dataCol = - cursor.getColumnIndexOrThrow(faviconData); - final int mimeCol = - cursor.getColumnIndexOrThrow(faviconMime); + ArrayList<Object[]> queryResult = db.query(faviconQuery); + final int urlCol = db.getColumnIndex(faviconUrl); + final int mimeCol = db.getColumnIndex(faviconMime); + final int dataCol = db.getColumnIndex(faviconData); - cursor.moveToFirst(); - while (!cursor.isAfterLast()) { - String url = cursor.getString(urlCol); - String mime = cursor.getString(mimeCol); - byte[] data = cursor.getBlob(dataCol); - addFavicon(url, mime, data); - cursor.moveToNext(); - } + for (Object[] resultRow: queryResult) { + String url = (String)resultRow[urlCol]; + String mime = (String)resultRow[mimeCol]; + ByteBuffer dataBuff = (ByteBuffer)resultRow[dataCol]; + addFavicon(url, mime, dataBuff); } - cursor.close(); - } catch (SQLiteException e) { - if (cursor != null) { - cursor.close(); - } + } catch (SQLiteBridgeException e) { Log.i(LOGTAG, "Failed to get favicons: " + e.getMessage()); return; } } - SQLiteDatabase openPlaces(String dbPath) throws SQLiteException { - /* http://stackoverflow.com/questions/2528489/no-such-table-android-metadata-whats-the-problem */ - SQLiteDatabase db = SQLiteDatabase.openDatabase(dbPath, - null, - SQLiteDatabase.OPEN_READONLY | - SQLiteDatabase.NO_LOCALIZED_COLLATORS); - - return db; - } - protected void migratePlaces(File aFile) { String dbPath = aFile.getPath() + "/places.sqlite"; String dbPathWal = aFile.getPath() + "/places.sqlite-wal"; String dbPathShm = aFile.getPath() + "/places.sqlite-shm"; Log.i(LOGTAG, "Opening path: " + dbPath); File dbFile = new File(dbPath); if (!dbFile.exists()) { Log.i(LOGTAG, "No database"); return; } File dbFileWal = new File(dbPathWal); File dbFileShm = new File(dbPathShm); - SQLiteDatabase db = null; + SQLiteBridge db = null; try { - db = openPlaces(dbPath); + db = new SQLiteBridge(dbPath); migrateBookmarks(db); migrateHistory(db); migrateFavicons(db); db.close(); // Clean up dbFile.delete(); dbFileWal.delete(); dbFileShm.delete(); Log.i(LOGTAG, "Profile migration finished"); - } catch (SQLiteException e) { + } catch (SQLiteBridgeException e) { if (db != null) { db.close(); } Log.i(LOGTAG, "Error on places database:" + e.getMessage()); return; } }
--- a/mobile/android/base/db/AndroidBrowserDB.java +++ b/mobile/android/base/db/AndroidBrowserDB.java @@ -58,39 +58,57 @@ public class AndroidBrowserDB implements private static final String URL_COLUMN_ID = "_id"; private static final String URL_COLUMN_THUMBNAIL = "thumbnail"; // Only available on Android >= 11 private static final String URL_COLUMN_DELETED = "deleted"; private static final Uri BOOKMARKS_CONTENT_URI_POST_11 = Uri.parse("content://com.android.browser/bookmarks"); - public Cursor filter(ContentResolver cr, CharSequence constraint, int limit, CharSequence urlFilter) { + private Cursor filterAllSites(ContentResolver cr, String[] projection, CharSequence constraint, int limit, CharSequence urlFilter) { Cursor c = cr.query(Browser.BOOKMARKS_URI, - new String[] { URL_COLUMN_ID, - BookmarkColumns.URL, - BookmarkColumns.TITLE, - BookmarkColumns.FAVICON, - URL_COLUMN_THUMBNAIL }, + projection, // The length restriction on URL is for the same reason as in the general bookmark query // (see comment earlier in this file). (urlFilter != null ? "(" + Browser.BookmarkColumns.URL + " NOT LIKE ? ) AND " : "" ) + "(" + Browser.BookmarkColumns.URL + " LIKE ? OR " + Browser.BookmarkColumns.TITLE + " LIKE ?)" + " AND LENGTH(" + Browser.BookmarkColumns.URL + ") > 0", urlFilter == null ? new String[] {"%" + constraint.toString() + "%", "%" + constraint.toString() + "%"} : new String[] {urlFilter.toString(), "%" + constraint.toString() + "%", "%" + constraint.toString() + "%"}, // ORDER BY is number of visits times a multiplier from 1 - 120 of how recently the site // was accessed with a site accessed today getting 120 and a site accessed 119 or more // days ago getting 1 Browser.BookmarkColumns.VISITS + " * MAX(1, (" + Browser.BookmarkColumns.DATE + " - " + System.currentTimeMillis() + ") / 86400000 + 120) DESC LIMIT " + limit); return new AndroidDBCursor(c); } + public Cursor filter(ContentResolver cr, CharSequence constraint, int limit) { + return filterAllSites(cr, + new String[] { URL_COLUMN_ID, + BookmarkColumns.URL, + BookmarkColumns.TITLE, + BookmarkColumns.FAVICON }, + constraint, + limit, + null); + } + + public Cursor getTopSites(ContentResolver cr, int limit) { + return filterAllSites(cr, + new String[] { URL_COLUMN_ID, + BookmarkColumns.URL, + BookmarkColumns.TITLE, + URL_COLUMN_THUMBNAIL }, + "", + limit, + BrowserDB.ABOUT_PAGES_URL_FILTER); + } + public void updateVisitedHistory(ContentResolver cr, String uri) { Browser.updateVisitedHistory(cr, uri, true); } public void updateHistoryTitle(ContentResolver cr, String uri, String title) { ContentValues values = new ContentValues(); values.put(Browser.BookmarkColumns.TITLE, title);
--- a/mobile/android/base/db/BrowserDB.java +++ b/mobile/android/base/db/BrowserDB.java @@ -37,28 +37,32 @@ package org.mozilla.gecko.db; import android.content.ContentResolver; import android.database.Cursor; import android.graphics.drawable.BitmapDrawable; public class BrowserDB { + public static String ABOUT_PAGES_URL_FILTER = "about:%"; + public static interface URLColumns { public static String URL = "url"; public static String TITLE = "title"; public static String FAVICON = "favicon"; public static String THUMBNAIL = "thumbnail"; public static String DATE_LAST_VISITED = "date-last-visited"; } private static BrowserDBIface sDb; public interface BrowserDBIface { - public Cursor filter(ContentResolver cr, CharSequence constraint, int limit, CharSequence urlFilter); + public Cursor filter(ContentResolver cr, CharSequence constraint, int limit); + + public Cursor getTopSites(ContentResolver cr, int limit); public void updateVisitedHistory(ContentResolver cr, String uri); public void updateHistoryTitle(ContentResolver cr, String uri, String title); public void updateHistoryDate(ContentResolver cr, String uri, long date); public Cursor getAllVisitedHistory(ContentResolver cr); @@ -82,22 +86,22 @@ public class BrowserDB { public void updateThumbnailForUrl(ContentResolver cr, String uri, BitmapDrawable thumbnail); } static { // Forcing local DB no option to switch to Android DB for now sDb = new LocalBrowserDB(BrowserContract.DEFAULT_PROFILE); } - public static Cursor filter(ContentResolver cr, CharSequence constraint, int limit, CharSequence urlFilter) { - return sDb.filter(cr, constraint, limit, urlFilter); + public static Cursor filter(ContentResolver cr, CharSequence constraint, int limit) { + return sDb.filter(cr, constraint, limit); } - public static Cursor filter(ContentResolver cr, CharSequence constraint, int limit) { - return sDb.filter(cr, constraint, limit, null); + public static Cursor getTopSites(ContentResolver cr, int limit) { + return sDb.getTopSites(cr, limit); } public static void updateVisitedHistory(ContentResolver cr, String uri) { sDb.updateVisitedHistory(cr, uri); } public static void updateHistoryTitle(ContentResolver cr, String uri, String title) { sDb.updateHistoryTitle(cr, uri, title);
--- a/mobile/android/base/db/LocalBrowserDB.java +++ b/mobile/android/base/db/LocalBrowserDB.java @@ -75,36 +75,54 @@ public class LocalBrowserDB implements B return uri.buildUpon().appendQueryParameter(BrowserContract.PARAM_PROFILE, mProfile). appendQueryParameter(BrowserContract.PARAM_LIMIT, String.valueOf(limit)).build(); } private Uri appendProfile(Uri uri) { return uri.buildUpon().appendQueryParameter(BrowserContract.PARAM_PROFILE, mProfile).build(); } - public Cursor filter(ContentResolver cr, CharSequence constraint, int limit, CharSequence urlFilter) { + private Cursor filterAllSites(ContentResolver cr, String[] projection, CharSequence constraint, int limit, CharSequence urlFilter) { Cursor c = cr.query(appendProfileAndLimit(History.CONTENT_URI, limit), - new String[] { History._ID, - History.URL, - History.TITLE, - History.FAVICON, - History.THUMBNAIL }, + projection, (urlFilter != null ? "(" + History.URL + " NOT LIKE ? ) AND " : "" ) + "(" + History.URL + " LIKE ? OR " + History.TITLE + " LIKE ?)", urlFilter == null ? new String[] {"%" + constraint.toString() + "%", "%" + constraint.toString() + "%"} : new String[] {urlFilter.toString(), "%" + constraint.toString() + "%", "%" + constraint.toString() + "%"}, // ORDER BY is number of visits times a multiplier from 1 - 120 of how recently the site // was accessed with a site accessed today getting 120 and a site accessed 119 or more // days ago getting 1 History.VISITS + " * MAX(1, (" + History.DATE_LAST_VISITED + " - " + System.currentTimeMillis() + ") / 86400000 + 120) DESC"); return new LocalDBCursor(c); } + public Cursor filter(ContentResolver cr, CharSequence constraint, int limit) { + return filterAllSites(cr, + new String[] { History._ID, + History.URL, + History.TITLE, + History.FAVICON }, + constraint, + limit, + null); + } + + public Cursor getTopSites(ContentResolver cr, int limit) { + return filterAllSites(cr, + new String[] { History._ID, + History.URL, + History.TITLE, + History.THUMBNAIL }, + "", + limit, + BrowserDB.ABOUT_PAGES_URL_FILTER); + } + private void truncateHistory(ContentResolver cr) { Cursor cursor = null; try { cursor = cr.query(appendProfile(History.CONTENT_URI), new String[] { History._ID }, null, null,
--- a/mobile/android/base/resources/layout/awesomebar_search.xml +++ b/mobile/android/base/resources/layout/awesomebar_search.xml @@ -7,17 +7,17 @@ android:id="@+id/awesomebar_text" style="@style/AddressBar.Button" android:layout_width="fill_parent" android:layout_alignParentBottom="true" android:layout_centerVertical="true" android:paddingLeft="15dip" android:paddingRight="40dip" android:hint="@string/awesomebar_default_text" - android:inputType="textUri" + android:inputType="textUri|textNoSuggestions" android:imeOptions="actionSearch" android:singleLine="true" android:gravity="center_vertical|left"> <requestFocus/> </view> <ImageButton android:id="@+id/awesomebar_button" style="@style/AddressBar.ImageButton"
new file mode 100644 --- /dev/null +++ b/mobile/android/base/sqlite/ByteBufferInputStream.java @@ -0,0 +1,71 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Android code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011-2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Gian-Carlo Pascutto <gpascutto@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.gecko.sqlite; + +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +/* + * Helper class to make the ByteBuffers returned by SQLite BLOB + * easier to use. + */ +public class ByteBufferInputStream extends InputStream { + private ByteBuffer mByteBuffer; + + public ByteBufferInputStream(ByteBuffer aByteBuffer) { + mByteBuffer = aByteBuffer; + } + + @Override + public synchronized int read() throws IOException { + if (!mByteBuffer.hasRemaining()) { + return -1; + } + return mByteBuffer.get(); + } + + @Override + public synchronized int read(byte[] aBytes, int aOffset, int aLen) + throws IOException { + int toRead = Math.min(aLen, mByteBuffer.remaining()); + mByteBuffer.get(aBytes, aOffset, toRead); + return toRead; + } +}
new file mode 100644 --- /dev/null +++ b/mobile/android/base/sqlite/SQLiteBridge.java @@ -0,0 +1,101 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Android code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Gian-Carlo Pascutto <gpascutto@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.gecko.sqlite; + +import org.mozilla.gecko.sqlite.SQLiteBridgeException; +import android.util.Log; + +import java.lang.String; +import java.util.ArrayList; +import java.util.HashMap; + +/* + * This class allows using the mozsqlite3 library included with Firefox + * to read SQLite databases, instead of the Android SQLiteDataBase API, + * which might use whatever outdated DB is present on the Android system. + */ +public class SQLiteBridge { + private static final String LOGTAG = "SQLiteBridge"; + // Path to the database. We reopen it every query. + private String mDb; + // Remember column names from last query result. + private ArrayList<String> mColumns; + + // JNI code in $(topdir)/mozglue/android/.. + private static native void sqliteCall(String aDb, String aQuery, + String[] aParams, + ArrayList<String> aColumns, + ArrayList<Object[]> aRes) + throws SQLiteBridgeException; + + // Takes the path to the database we want to access. + public SQLiteBridge(String aDb) throws SQLiteBridgeException { + mDb = aDb; + } + + // Do an SQL query without parameters + public ArrayList<Object[]> query(String aQuery) throws SQLiteBridgeException { + String[] params = new String[0]; + return query(aQuery, params); + } + + // Do an SQL query, substituting the parameters in the query with the passed + // parameters. The parameters are subsituded in order, so named parameters + // are not supported. + // The result is returned as an ArrayList<Object[]>, with each + // row being an entry in the ArrayList, and each column being one Object + // in the Object[] array. The columns are of type null, + // direct ByteBuffer (BLOB), or String (everything else). + public ArrayList<Object[]> query(String aQuery, String[] aParams) + throws SQLiteBridgeException { + ArrayList<Object[]> result = new ArrayList<Object[]>(); + mColumns = new ArrayList<String>(); + sqliteCall(mDb, aQuery, aParams, mColumns, result); + return result; + } + + // Gets the index in the row Object[] for the given column name. + // Returns -1 if not found. + public int getColumnIndex(String aColumnName) { + return mColumns.lastIndexOf(aColumnName); + } + + public void close() { + // nop, provided for API compatibility with SQLiteDatabase. + } +} \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/mobile/android/base/sqlite/SQLiteBridgeException.java @@ -0,0 +1,47 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Android code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Gian-Carlo Pascutto <gpascutto@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.gecko.sqlite; + +public class SQLiteBridgeException extends Exception { + static final long serialVersionUID = 1L; + + public SQLiteBridgeException() {} + public SQLiteBridgeException(String msg) { + super(msg); + } +} \ No newline at end of file
--- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -421,16 +421,22 @@ var BrowserApp = { aParams = aParams || {}; let flags = "flags" in aParams ? aParams.flags : Ci.nsIWebNavigation.LOAD_FLAGS_NONE; let postData = ("postData" in aParams && aParams.postData) ? aParams.postData.value : null; let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null; let charset = "charset" in aParams ? aParams.charset : null; + if ("showProgress" in aParams) { + let tab = this.getTabForBrowser(aBrowser); + if (tab) + tab.showProgress = aParams.showProgress; + } + try { aBrowser.loadURIWithFlags(aURI, flags, referrerURI, charset, postData); } catch(e) { let tab = this.getTabForBrowser(aBrowser); if (tab) { let message = { gecko: { type: "Content:LoadError", @@ -820,16 +826,21 @@ var BrowserApp = { // inheriting the currently loaded document's principal. let params = { selected: true, parentId: ("parentId" in data) ? data.parentId : -1, flags: Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_OWNER }; let url = this.getSearchOrFixupURI(data); + + // Don't show progress throbber for about:home + if (url == "about:home") + params.showProgress = false; + if (aTopic == "Tab:Add") this.addTab(url, params); else this.loadURI(url, browser, params); } else if (aTopic == "Tab:Select") { this.selectTab(this.getTabForId(parseInt(aData))); } else if (aTopic == "Tab:Close") { this.closeTab(this.getTabForId(parseInt(aData))); @@ -1292,16 +1303,17 @@ let gTabIDFactory = 0; // get created with the right size rather than being 1x1 let gScreenWidth = 1; let gScreenHeight = 1; function Tab(aURL, aParams) { this.browser = null; this.vbox = null; this.id = 0; + this.showProgress = true; this.create(aURL, aParams); this._viewport = { x: 0, y: 0, width: gScreenWidth, height: gScreenHeight, offsetX: 0, offsetY: 0, pageWidth: gScreenWidth, pageHeight: gScreenHeight, zoom: 1.0 }; this.viewportExcess = { x: 0, y: 0 }; this.documentIdForCurrentViewport = null; this.userScrollPos = { x: 0, y: 0 }; this._pluginsToPlay = []; this._pluginOverlayShowing = false; @@ -1363,16 +1375,19 @@ Tab.prototype = { Services.obs.addObserver(this, "document-shown", false); if (!aParams.delayLoad) { let flags = "flags" in aParams ? aParams.flags : Ci.nsIWebNavigation.LOAD_FLAGS_NONE; let postData = ("postData" in aParams && aParams.postData) ? aParams.postData.value : null; let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null; let charset = "charset" in aParams ? aParams.charset : null; + // This determines whether or not we show the progress throbber in the urlbar + this.showProgress = "showProgress" in aParams ? aParams.showProgress : true; + try { this.browser.loadURIWithFlags(aURL, flags, referrerURI, charset, postData); } catch(e) { let message = { gecko: { type: "Content:LoadError", tabID: this.id, uri: this.browser.currentURI.spec, @@ -1744,21 +1759,24 @@ Tab.prototype = { if (browser) uri = browser.currentURI.spec; let message = { gecko: { type: "Content:StateChange", tabID: this.id, uri: uri, - state: aStateFlags + state: aStateFlags, + showProgress: this.showProgress } }; - sendMessageToJava(message); + + // Reset showProgress after state change + this.showProgress = true; } }, onLocationChange: function(aWebProgress, aRequest, aLocationURI, aFlags) { let contentWin = aWebProgress.DOMWindow; if (contentWin != contentWin.top) return;
--- a/mozglue/android/APKOpen.cpp +++ b/mozglue/android/APKOpen.cpp @@ -57,16 +57,18 @@ #include <unistd.h> #include <zlib.h> #include <linux/ashmem.h> #include "dlfcn.h" #include "APKOpen.h" #include <sys/time.h> #include <sys/resource.h> #include "Zip.h" +#include "sqlite3.h" +#include "SQLiteBridge.h" /* Android headers don't define RUSAGE_THREAD */ #ifndef RUSAGE_THREAD #define RUSAGE_THREAD 1 #endif enum StartupEvent { #define mozilla_StartupTimeline_Event(ev, z) ev, @@ -279,33 +281,34 @@ SHELL_WRAPPER1(setSoftwareLayerClient, j SHELL_WRAPPER0(onResume) SHELL_WRAPPER0(onLowMemory) SHELL_WRAPPER3(callObserver, jstring, jstring, jstring) SHELL_WRAPPER1(removeObserver, jstring) SHELL_WRAPPER1(onChangeNetworkLinkStatus, jstring) SHELL_WRAPPER1(reportJavaCrash, jstring) SHELL_WRAPPER0(executeNextRunnable) SHELL_WRAPPER1(cameraCallbackBridge, jbyteArray) -SHELL_WRAPPER3(notifyBatteryChange, jdouble, jboolean, jdouble); -SHELL_WRAPPER3(notifySmsReceived, jstring, jstring, jlong); -SHELL_WRAPPER0(bindWidgetTexture); -SHELL_WRAPPER3_WITH_RETURN(saveMessageInSentbox, jint, jstring, jstring, jlong); -SHELL_WRAPPER6(notifySmsSent, jint, jstring, jstring, jlong, jint, jlong); -SHELL_WRAPPER4(notifySmsDelivered, jint, jstring, jstring, jlong); -SHELL_WRAPPER3(notifySmsSendFailed, jint, jint, jlong); -SHELL_WRAPPER7(notifyGetSms, jint, jstring, jstring, jstring, jlong, jint, jlong); -SHELL_WRAPPER3(notifyGetSmsFailed, jint, jint, jlong); -SHELL_WRAPPER3(notifySmsDeleted, jboolean, jint, jlong); -SHELL_WRAPPER3(notifySmsDeleteFailed, jint, jint, jlong); -SHELL_WRAPPER2(notifyNoMessageInList, jint, jlong); -SHELL_WRAPPER8(notifyListCreated, jint, jint, jstring, jstring, jstring, jlong, jint, jlong); -SHELL_WRAPPER7(notifyGotNextMessage, jint, jstring, jstring, jstring, jlong, jint, jlong); -SHELL_WRAPPER3(notifyReadingMessageListFailed, jint, jint, jlong); +SHELL_WRAPPER3(notifyBatteryChange, jdouble, jboolean, jdouble) +SHELL_WRAPPER3(notifySmsReceived, jstring, jstring, jlong) +SHELL_WRAPPER0(bindWidgetTexture) +SHELL_WRAPPER3_WITH_RETURN(saveMessageInSentbox, jint, jstring, jstring, jlong) +SHELL_WRAPPER6(notifySmsSent, jint, jstring, jstring, jlong, jint, jlong) +SHELL_WRAPPER4(notifySmsDelivered, jint, jstring, jstring, jlong) +SHELL_WRAPPER3(notifySmsSendFailed, jint, jint, jlong) +SHELL_WRAPPER7(notifyGetSms, jint, jstring, jstring, jstring, jlong, jint, jlong) +SHELL_WRAPPER3(notifyGetSmsFailed, jint, jint, jlong) +SHELL_WRAPPER3(notifySmsDeleted, jboolean, jint, jlong) +SHELL_WRAPPER3(notifySmsDeleteFailed, jint, jint, jlong) +SHELL_WRAPPER2(notifyNoMessageInList, jint, jlong) +SHELL_WRAPPER8(notifyListCreated, jint, jint, jstring, jstring, jstring, jlong, jint, jlong) +SHELL_WRAPPER7(notifyGotNextMessage, jint, jstring, jstring, jstring, jlong, jint, jlong) +SHELL_WRAPPER3(notifyReadingMessageListFailed, jint, jint, jlong) static void * xul_handle = NULL; +static void * sqlite_handle = NULL; static time_t apk_mtime = 0; #ifdef DEBUG extern "C" int extractLibs = 1; #else extern "C" int extractLibs = 0; #endif static void @@ -613,44 +616,40 @@ report_mapping(char *name, void *base, u char * entry = strstr(file_ids, name); if (entry) info->file_id = strndup(entry + strlen(name) + 1, 32); } extern "C" void simple_linker_init(void); static void -loadLibs(const char *apkName) +loadGeckoLibs(const char *apkName) { chdir(getenv("GRE_HOME")); - simple_linker_init(); - struct stat status; if (!stat(apkName, &status)) apk_mtime = status.st_mtime; struct timeval t0, t1; gettimeofday(&t0, 0); struct rusage usage1; getrusage(RUSAGE_THREAD, &usage1); Zip *zip = new Zip(apkName); - lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping)); #ifdef MOZ_CRASHREPORTER file_ids = (char *)extractBuf("lib.id", zip); #endif #define MOZLOAD(name) mozload("lib" name ".so", zip) MOZLOAD("mozalloc"); MOZLOAD("nspr4"); MOZLOAD("plc4"); MOZLOAD("plds4"); - MOZLOAD("mozsqlite3"); MOZLOAD("nssutil3"); MOZLOAD("nss3"); MOZLOAD("ssl3"); MOZLOAD("smime3"); xul_handle = MOZLOAD("xul"); MOZLOAD("xpcom"); MOZLOAD("nssckbi"); MOZLOAD("freebl3"); @@ -707,30 +706,77 @@ loadLibs(const char *apkName) (usage2.ru_utime.tv_sec - usage1.ru_utime.tv_sec)*1000 + (usage2.ru_utime.tv_usec - usage1.ru_utime.tv_usec)/1000, (usage2.ru_stime.tv_sec - usage1.ru_stime.tv_sec)*1000 + (usage2.ru_stime.tv_usec - usage1.ru_stime.tv_usec)/1000, usage2.ru_majflt-usage1.ru_majflt); StartupTimeline_Record(LINKER_INITIALIZED, &t0); StartupTimeline_Record(LIBRARIES_LOADED, &t1); } -extern "C" NS_EXPORT void JNICALL -Java_org_mozilla_gecko_GeckoAppShell_loadLibs(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName, jboolean jShouldExtract) +static void loadSQLiteLibs(const char *apkName) { - if (jShouldExtract) - extractLibs = 1; + chdir(getenv("GRE_HOME")); + + simple_linker_init(); + + struct stat status; + if (!stat(apkName, &status)) + apk_mtime = status.st_mtime; + + Zip *zip = new Zip(apkName); + lib_mapping = (struct mapping_info *)calloc(MAX_MAPPING_INFO, sizeof(*lib_mapping)); + +#ifdef MOZ_CRASHREPORTER + file_ids = (char *)extractBuf("lib.id", zip); +#endif + +#define MOZLOAD(name) mozload("lib" name ".so", zip) + sqlite_handle = MOZLOAD("mozsqlite3"); +#undef MOZLOAD + delete zip; + +#ifdef MOZ_CRASHREPORTER + free(file_ids); + file_ids = NULL; +#endif + + if (!sqlite_handle) + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libmozsqlite3!"); + + setup_sqlite_functions(sqlite_handle); +} + +extern "C" NS_EXPORT void JNICALL +Java_org_mozilla_gecko_GeckoAppShell_loadGeckoLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) +{ const char* str; - // XXX: java doesn't give us true UTF8, we should figure out something + // XXX: java doesn't give us true UTF8, we should figure out something // better to do here str = jenv->GetStringUTFChars(jApkName, NULL); if (str == NULL) return; - loadLibs(str); + loadGeckoLibs(str); + jenv->ReleaseStringUTFChars(jApkName, str); +} + +extern "C" NS_EXPORT void JNICALL +Java_org_mozilla_gecko_GeckoAppShell_loadSQLiteLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName, jboolean jShouldExtract) { + if (jShouldExtract) + extractLibs = 1; + + const char* str; + // XXX: java doesn't give us true UTF8, we should figure out something + // better to do here + str = jenv->GetStringUTFChars(jApkName, NULL); + if (str == NULL) + return; + + loadSQLiteLibs(str); jenv->ReleaseStringUTFChars(jApkName, str); } typedef int GeckoProcessType; typedef int nsresult; extern "C" NS_EXPORT int ChildProcessInit(int argc, char* argv[]) @@ -740,17 +786,18 @@ ChildProcessInit(int argc, char* argv[]) if (strcmp(argv[i], "-greomni")) continue; i = i + 1; break; } fillLibCache(argv[argc - 1]); - loadLibs(argv[i]); + loadSQLiteLibs(argv[i]); + loadGeckoLibs(argv[i]); // don't pass the last arg - it's only recognized by the lib cache argc--; typedef GeckoProcessType (*XRE_StringToChildProcessType_t)(char*); typedef nsresult (*XRE_InitChildProcess_t)(int, char**, GeckoProcessType); XRE_StringToChildProcessType_t fXRE_StringToChildProcessType = (XRE_StringToChildProcessType_t)__wrap_dlsym(xul_handle, "XRE_StringToChildProcessType");
--- a/mozglue/android/Makefile.in +++ b/mozglue/android/Makefile.in @@ -48,20 +48,22 @@ FORCE_STATIC_LIB = 1 DEFINES += \ -DANDROID_PACKAGE_NAME='"$(ANDROID_PACKAGE_NAME)"' \ $(NULL) CPPSRCS = \ nsGeckoUtils.cpp \ APKOpen.cpp \ + SQLiteBridge.cpp \ $(NULL) LOCAL_INCLUDES += -I$(srcdir)/../linker LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/components/startup +LOCAL_INCLUDES += -I$(topsrcdir)/db/sqlite3/src ifdef MOZ_OLD_LINKER LOCAL_INCLUDES += -I$(topsrcdir)/other-licenses/android ifeq ($(CPU_ARCH),arm) DEFINES += -DANDROID_ARM_LINKER endif endif EXPORTS = APKOpen.h
new file mode 100644 --- /dev/null +++ b/mozglue/android/SQLiteBridge.cpp @@ -0,0 +1,312 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Android code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Gian-Carlo Pascutto <gpascutto@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include <stdlib.h> +#include <jni.h> +#include <android/log.h> +#include "dlfcn.h" +#include "APKOpen.h" +#include "SQLiteBridge.h" + +#ifdef DEBUG +#define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x) +#else +#define LOG(x...) +#endif + +#define SQLITE_WRAPPER_INT(name) name ## _t f_ ## name; + +SQLITE_WRAPPER_INT(sqlite3_open) +SQLITE_WRAPPER_INT(sqlite3_errmsg) +SQLITE_WRAPPER_INT(sqlite3_prepare_v2) +SQLITE_WRAPPER_INT(sqlite3_bind_parameter_count) +SQLITE_WRAPPER_INT(sqlite3_bind_text) +SQLITE_WRAPPER_INT(sqlite3_step) +SQLITE_WRAPPER_INT(sqlite3_column_count) +SQLITE_WRAPPER_INT(sqlite3_finalize) +SQLITE_WRAPPER_INT(sqlite3_close) +SQLITE_WRAPPER_INT(sqlite3_column_name) +SQLITE_WRAPPER_INT(sqlite3_column_type) +SQLITE_WRAPPER_INT(sqlite3_column_blob) +SQLITE_WRAPPER_INT(sqlite3_column_bytes) +SQLITE_WRAPPER_INT(sqlite3_column_text) + +void setup_sqlite_functions(void *sqlite_handle) +{ +#define GETFUNC(name) f_ ## name = (name ## _t) __wrap_dlsym(sqlite_handle, #name) + GETFUNC(sqlite3_open); + GETFUNC(sqlite3_errmsg); + GETFUNC(sqlite3_prepare_v2); + GETFUNC(sqlite3_bind_parameter_count); + GETFUNC(sqlite3_bind_text); + GETFUNC(sqlite3_step); + GETFUNC(sqlite3_column_count); + GETFUNC(sqlite3_finalize); + GETFUNC(sqlite3_close); + GETFUNC(sqlite3_column_name); + GETFUNC(sqlite3_column_type); + GETFUNC(sqlite3_column_blob); + GETFUNC(sqlite3_column_bytes); + GETFUNC(sqlite3_column_text); +#undef GETFUNC +} + +static bool initialized = false; +static jclass stringClass; +static jclass objectClass; +static jclass byteBufferClass; +static jclass arrayListClass; +static jmethodID jByteBufferAllocateDirect; +static jmethodID jArrayListAdd; +static jobject jNull; + +static void +JNI_Throw(JNIEnv* jenv, const char* name, const char* msg) +{ + jclass cls = jenv->FindClass(name); + if (cls == NULL) { + LOG("Couldn't find exception class (or exception pending)\n"); + return; + } + int rc = jenv->ThrowNew(cls, msg); + if (rc < 0) { + LOG("Error throwing exception\n"); + } + jenv->DeleteLocalRef(cls); +} + +static void +JNI_Setup(JNIEnv* jenv) +{ + if (initialized) return; + + objectClass = jenv->FindClass("java/lang/Object"); + stringClass = jenv->FindClass("java/lang/String"); + byteBufferClass = jenv->FindClass("java/nio/ByteBuffer"); + arrayListClass = jenv->FindClass("java/util/ArrayList"); + jNull = jenv->NewGlobalRef(NULL); + + if (stringClass == NULL || objectClass == NULL + || byteBufferClass == NULL || arrayListClass == NULL) { + LOG("Error finding classes"); + JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException", + "FindClass error"); + return; + } + + // public static ByteBuffer allocateDirect(int capacity) + jByteBufferAllocateDirect = + jenv->GetStaticMethodID(byteBufferClass, "allocateDirect", "(I)Ljava/nio/ByteBuffer;"); + // boolean add(Object o) + jArrayListAdd = + jenv->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); + + if (jByteBufferAllocateDirect == NULL || jArrayListAdd == NULL) { + LOG("Error finding methods"); + JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException", + "GetMethodId error"); + return; + } + + initialized = true; +} + +extern "C" NS_EXPORT void JNICALL +Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass, + jstring jDb, + jstring jQuery, + jobjectArray jParams, + jobject jColumns, + jobject jArrayList) +{ + JNI_Setup(jenv); + + const char* queryStr; + queryStr = jenv->GetStringUTFChars(jQuery, NULL); + + const char* dbPath; + dbPath = jenv->GetStringUTFChars(jDb, NULL); + + const char *pzTail; + sqlite3_stmt *ppStmt; + sqlite3 *db; + int rc; + rc = f_sqlite3_open(dbPath, &db); + jenv->ReleaseStringUTFChars(jDb, dbPath); + + if (rc != SQLITE_OK) { + LOG("Can't open database: %s\n", f_sqlite3_errmsg(db)); + goto error_close; + } + + rc = f_sqlite3_prepare_v2(db, queryStr, -1, &ppStmt, &pzTail); + if (rc != SQLITE_OK || ppStmt == NULL) { + LOG("Can't prepare statement: %s\n", f_sqlite3_errmsg(db)); + goto error_close; + } + jenv->ReleaseStringUTFChars(jQuery, queryStr); + + // Check if number of parameters matches + jsize numPars; + numPars = jenv->GetArrayLength(jParams); + int sqlNumPars; + sqlNumPars = f_sqlite3_bind_parameter_count(ppStmt); + if (numPars != sqlNumPars) { + LOG("Passed parameter count (%d) doesn't match SQL parameter count (%d)\n", + numPars, sqlNumPars); + goto error_close; + } + // Bind parameters, if any + if (numPars > 0) { + for (int i = 0; i < numPars; i++) { + jobject jObjectParam = jenv->GetObjectArrayElement(jParams, i); + // IsInstanceOf or isAssignableFrom? String is final, so IsInstanceOf + // should be OK. + jboolean isString = jenv->IsInstanceOf(jObjectParam, stringClass); + if (isString != JNI_TRUE) { + LOG("Parameter is not of String type"); + goto error_close; + } + jstring jStringParam = (jstring)jObjectParam; + const char* paramStr = jenv->GetStringUTFChars(jStringParam, NULL); + // SQLite parameters index from 1. + rc = f_sqlite3_bind_text(ppStmt, i + 1, paramStr, -1, SQLITE_TRANSIENT); + jenv->ReleaseStringUTFChars(jStringParam, paramStr); + if (rc != SQLITE_OK) { + LOG("Error binding query parameter"); + goto error_close; + } + } + } + + // Execute the query and step through the results + rc = f_sqlite3_step(ppStmt); + if (rc != SQLITE_ROW && rc != SQLITE_DONE) { + LOG("Can't step statement: (%d) %s\n", rc, f_sqlite3_errmsg(db)); + goto error_close; + } + + // Get the column names + int cols; + cols = f_sqlite3_column_count(ppStmt); + for (int i = 0; i < cols; i++) { + const char* colName = f_sqlite3_column_name(ppStmt, i); + jstring jStr = jenv->NewStringUTF(colName); + jenv->CallBooleanMethod(jColumns, jArrayListAdd, jStr); + jenv->DeleteLocalRef(jStr); + } + + // For each row, add an Object[] to the passed ArrayList, + // with that containing either String or ByteArray objects + // containing the columns + do { + // Process row + // Construct Object[] + jobjectArray jRow = jenv->NewObjectArray(cols, + objectClass, + NULL); + if (jRow == NULL) { + LOG("Can't allocate jRow Object[]\n"); + goto error_close; + } + + for (int i = 0; i < cols; i++) { + int colType = f_sqlite3_column_type(ppStmt, i); + if (colType == SQLITE_BLOB) { + // Treat as blob + const void* blob = f_sqlite3_column_blob(ppStmt, i); + int colLen = f_sqlite3_column_bytes(ppStmt, i); + + // Construct ByteBuffer of correct size + jobject jByteBuffer = + jenv->CallStaticObjectMethod(byteBufferClass, + jByteBufferAllocateDirect, + colLen); + if (jByteBuffer == NULL) { + goto error_close; + } + + // Get its backing array + void* bufferArray = jenv->GetDirectBufferAddress(jByteBuffer); + if (bufferArray == NULL) { + LOG("Failure calling GetDirectBufferAddress\n"); + goto error_close; + } + memcpy(bufferArray, blob, colLen); + + jenv->SetObjectArrayElement(jRow, i, jByteBuffer); + jenv->DeleteLocalRef(jByteBuffer); + } else if (colType == SQLITE_NULL) { + jenv->SetObjectArrayElement(jRow, i, jNull); + } else { + // Treat everything else as text + const char* txt = (const char*)f_sqlite3_column_text(ppStmt, i); + jstring jStr = jenv->NewStringUTF(txt); + jenv->SetObjectArrayElement(jRow, i, jStr); + jenv->DeleteLocalRef(jStr); + } + } + + // Append Object[] to ArrayList<Object[]> + // JNI doesn't know about the generic, so use Object[] as Object + jenv->CallBooleanMethod(jArrayList, jArrayListAdd, jRow); + + // Clean up + jenv->DeleteLocalRef(jRow); + + // Get next row + rc = f_sqlite3_step(ppStmt); + // Real error? + if (rc != SQLITE_ROW && rc != SQLITE_DONE) { + LOG("Can't re-step statement:(%d) %s\n", rc, f_sqlite3_errmsg(db)); + goto error_close; + } + } while (rc != SQLITE_DONE); + + rc = f_sqlite3_finalize(ppStmt); + if (rc != SQLITE_OK) { + LOG("Can't finalize statement: %s\n", f_sqlite3_errmsg(db)); + goto error_close; + } + + f_sqlite3_close(db); + return; + +error_close: + f_sqlite3_close(db); + JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException", "SQLite error"); + return; +}
new file mode 100644 --- /dev/null +++ b/mozglue/android/SQLiteBridge.h @@ -0,0 +1,63 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Android code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Gian-Carlo Pascutto <gpascutto@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef SQLiteBridge_h +#define SQLiteBridge_h + +#include "sqlite3.h" + +void setup_sqlite_functions(void *sqlite_handle); + +#define SQLITE_WRAPPER(name, return_type, args...) \ +typedef return_type (*name ## _t)(args); \ +extern name ## _t f_ ## name; + +SQLITE_WRAPPER(sqlite3_open, int, const char*, sqlite3**) +SQLITE_WRAPPER(sqlite3_errmsg, const char*, sqlite3*) +SQLITE_WRAPPER(sqlite3_prepare_v2, int, sqlite3*, const char*, int, sqlite3_stmt**, const char**) +SQLITE_WRAPPER(sqlite3_bind_parameter_count, int, sqlite3_stmt*) +SQLITE_WRAPPER(sqlite3_bind_text, int, sqlite3_stmt*, int, const char*, int, void(*)(void*)) +SQLITE_WRAPPER(sqlite3_step, int, sqlite3_stmt*) +SQLITE_WRAPPER(sqlite3_column_count, int, sqlite3_stmt*) +SQLITE_WRAPPER(sqlite3_finalize, int, sqlite3_stmt*) +SQLITE_WRAPPER(sqlite3_close, int, sqlite3*) +SQLITE_WRAPPER(sqlite3_column_name, const char*, sqlite3_stmt*, int) +SQLITE_WRAPPER(sqlite3_column_type, int, sqlite3_stmt*, int) +SQLITE_WRAPPER(sqlite3_column_blob, const void*, sqlite3_stmt*, int) +SQLITE_WRAPPER(sqlite3_column_bytes, int, sqlite3_stmt*, int) +SQLITE_WRAPPER(sqlite3_column_text, const unsigned char*, sqlite3_stmt*, int) + +#endif /* SQLiteBridge_h */
--- a/mozglue/build/Makefile.in +++ b/mozglue/build/Makefile.in @@ -51,16 +51,17 @@ ifdef MOZ_MEMORY SHARED_LIBRARY_LIBS = $(call EXPAND_LIBNAME_PATH,jemalloc,$(DEPTH)/memory/jemalloc) else # Temporary, until bug 662814 lands VISIBILITY_FLAGS = CPPSRCS = dummy.cpp endif # Build mozglue as a shared lib on Windows, OSX and Android. +# If this is ever changed, update MOZ_SHARED_MOZGLUE in browser/installer/Makefile.in ifneq (,$(filter WINNT Darwin Android,$(OS_TARGET))) FORCE_SHARED_LIB = 1 else FORCE_STATIC_LIB = 1 endif MOZ_GLUE_LDFLAGS = # Don't link against ourselves
--- a/testing/mochitest/tests/SimpleTest/SimpleTest.js +++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js @@ -606,17 +606,17 @@ SimpleTest.waitForClipboard = function(a if (++SimpleTest.waitForClipboard_polls > 50) { // Log the failure. SimpleTest.ok(false, "Timed out while polling clipboard for pasted data."); reset(); failureFn(); return; } - data = SpecialPowers.getClipboardData(flavor); + var data = SpecialPowers.getClipboardData(flavor); if (validatorFn(data)) { // Don't show the success message when waiting for preExpectedVal if (preExpectedVal) preExpectedVal = null; else SimpleTest.ok(true, "Clipboard has the correct value"); reset();
--- a/testing/mochitest/tests/SimpleTest/specialpowersAPI.js +++ b/testing/mochitest/tests/SimpleTest/specialpowersAPI.js @@ -929,17 +929,17 @@ SpecialPowersAPI.prototype = { }, openDialog: function(win, args) { return win.openDialog.apply(win, args); }, // :jdm gets credit for this. ex: getPrivilegedProps(window, 'location.href'); getPrivilegedProps: function(obj, props) { - parts = props.split('.'); + var parts = props.split('.'); for (var i = 0; i < parts.length; i++) { var p = parts[i]; if (obj[p]) { obj = obj[p]; } else { return null; }
--- a/toolkit/library/Makefile.in +++ b/toolkit/library/Makefile.in @@ -419,16 +419,17 @@ ifeq (android,$(MOZ_WIDGET_TOOLKIT)) OS_LIBS += -lGLESv2 endif ifeq (gonk,$(MOZ_WIDGET_TOOLKIT)) OS_LIBS += \ -lui \ -lmedia \ -lhardware_legacy \ + -lhardware \ $(NULL) endif EXTRA_DEPS += \ $(topsrcdir)/intl/unicharutil/util/objs.mk \ $(topsrcdir)/rdf/util/src/objs.mk \ $(NULL)
--- a/toolkit/mozapps/update/common/uachelper.cpp +++ b/toolkit/mozapps/update/common/uachelper.cpp @@ -43,19 +43,23 @@ typedef BOOL (WINAPI *LPWTSQueryUserToke // See the MSDN documentation with title: Privilege Constants // At the time of this writing, this documentation is located at: // http://msdn.microsoft.com/en-us/library/windows/desktop/bb530716%28v=vs.85%29.aspx LPCTSTR UACHelper::PrivsToDisable[] = { SE_ASSIGNPRIMARYTOKEN_NAME, SE_AUDIT_NAME, SE_BACKUP_NAME, - // From testing ReadDirectoryChanges still succeeds even with a low - // integrity process with the following privilege disabled. - SE_CHANGE_NOTIFY_NAME, + // CreateProcess will succeed but the app will fail to launch on some WinXP + // machines if SE_CHANGE_NOTIFY_NAME is disabled. In particular this happens + // for limited user accounts on those machines. The define is kept here as a + // reminder that it should never be re-added. + // This permission is for directory watching but also from MSDN: "This + // privilege also causes the system to skip all traversal access checks." + // SE_CHANGE_NOTIFY_NAME, SE_CREATE_GLOBAL_NAME, SE_CREATE_PAGEFILE_NAME, SE_CREATE_PERMANENT_NAME, SE_CREATE_SYMBOLIC_LINK_NAME, SE_CREATE_TOKEN_NAME, SE_DEBUG_NAME, SE_ENABLE_DELEGATION_NAME, SE_IMPERSONATE_NAME,
--- a/uriloader/exthandler/mac/nsOSHelperAppService.mm +++ b/uriloader/exthandler/mac/nsOSHelperAppService.mm @@ -243,16 +243,87 @@ nsresult nsOSHelperAppService::GetFileTo NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } NS_IMETHODIMP nsOSHelperAppService::GetFromTypeAndExtension(const nsACString& aType, const nsACString& aFileExt, nsIMIMEInfo ** aMIMEInfo) { return nsExternalHelperAppService::GetFromTypeAndExtension(aType, aFileExt, aMIMEInfo); } +// Returns the MIME types an application bundle explicitly claims to handle. +// Returns NULL if aAppRef doesn't explicitly claim to handle any MIME types. +// If the return value is non-NULL, the caller is responsible for freeing it. +// This isn't necessarily the same as the MIME types the application bundle +// is registered to handle in the Launch Services database. (For example +// the Preview application is normally registered to handle the application/pdf +// MIME type, even though it doesn't explicitly claim to handle *any* MIME +// types in its Info.plist. This is probably because Preview does explicitly +// claim to handle the com.adobe.pdf UTI, and Launch Services somehow +// translates this into a claim to support the application/pdf MIME type. +// Launch Services doesn't provide any APIs (documented or undocumented) to +// query which MIME types a given application is registered to handle. So any +// app that wants this information (e.g. the Default Apps pref pane) needs to +// iterate through the entire Launch Services database -- a process which can +// take several seconds.) +static CFArrayRef GetMIMETypesHandledByApp(FSRef *aAppRef) +{ + CFURLRef appURL = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aAppRef); + if (!appURL) { + return NULL; + } + CFDictionaryRef infoDict = ::CFBundleCopyInfoDictionaryForURL(appURL); + ::CFRelease(appURL); + if (!infoDict) { + return NULL; + } + CFTypeRef cfObject = ::CFDictionaryGetValue(infoDict, CFSTR("CFBundleDocumentTypes")); + ::CFRelease(infoDict); + if (!cfObject || (::CFGetTypeID(cfObject) != ::CFArrayGetTypeID())) { + return NULL; + } + + CFArrayRef docTypes = static_cast<CFArrayRef>(cfObject); + CFIndex docTypesCount = ::CFArrayGetCount(docTypes); + if (docTypesCount == 0) { + return NULL; + } + + CFMutableArrayRef mimeTypes = + ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + for (CFIndex i = 0; i < docTypesCount; ++i) { + cfObject = ::CFArrayGetValueAtIndex(docTypes, i); + if (!cfObject || (::CFGetTypeID(cfObject) != ::CFDictionaryGetTypeID())) { + continue; + } + CFDictionaryRef typeDict = static_cast<CFDictionaryRef>(cfObject); + + // When this key is present (on OS X 10.5 and later), its contents + // take precedence over CFBundleTypeMIMETypes (and CFBundleTypeExtensions + // and CFBundleTypeOSTypes). + cfObject = ::CFDictionaryGetValue(typeDict, CFSTR("LSItemContentTypes")); + if (cfObject && (::CFGetTypeID(cfObject) == ::CFArrayGetTypeID())) { + continue; + } + + cfObject = ::CFDictionaryGetValue(typeDict, CFSTR("CFBundleTypeMIMETypes")); + if (!cfObject || (::CFGetTypeID(cfObject) != ::CFArrayGetTypeID())) { + continue; + } + CFArrayRef mimeTypeHolder = static_cast<CFArrayRef>(cfObject); + CFArrayAppendArray(mimeTypes, mimeTypeHolder, + ::CFRangeMake(0, ::CFArrayGetCount(mimeTypeHolder))); + } + + if (!::CFArrayGetCount(mimeTypes)) { + ::CFRelease(mimeTypes); + mimeTypes = NULL; + } + return mimeTypes; +} + // aMIMEType and aFileExt might not match, If they don't we set *aFound to // false and return a minimal nsIMIMEInfo structure. already_AddRefed<nsIMIMEInfo> nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, bool * aFound) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL; @@ -276,71 +347,113 @@ nsOSHelperAppService::GetMIMEInfoFromOS( OSStatus err; bool haveAppForType = false; bool haveAppForExt = false; bool typeAppIsDefault = false; bool extAppIsDefault = false; FSRef typeAppFSRef; FSRef extAppFSRef; + CFStringRef cfMIMEType = NULL; + if (!aMIMEType.IsEmpty()) { CFURLRef appURL = NULL; // CFStringCreateWithCString() can fail even if we're not out of memory -- // for example if the 'cStr' parameter is something very wierd (like "ÿÿ~" // aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's // specified in the 'encoding' parameter. See bug 548719. - CFStringRef CFType = ::CFStringCreateWithCString(NULL, flatType.get(), kCFStringEncodingUTF8); - if (CFType) { - err = ::LSCopyApplicationForMIMEType(CFType, kLSRolesAll, &appURL); + cfMIMEType = ::CFStringCreateWithCString(NULL, flatType.get(), + kCFStringEncodingUTF8); + if (cfMIMEType) { + err = ::LSCopyApplicationForMIMEType(cfMIMEType, kLSRolesAll, &appURL); if ((err == noErr) && appURL && ::CFURLGetFSRef(appURL, &typeAppFSRef)) { haveAppForType = true; PR_LOG(mLog, PR_LOG_DEBUG, ("LSCopyApplicationForMIMEType found a default application\n")); } - if (appURL) + if (appURL) { ::CFRelease(appURL); - ::CFRelease(CFType); + } } } if (!aFileExt.IsEmpty()) { // CFStringCreateWithCString() can fail even if we're not out of memory -- // for example if the 'cStr' parameter is something very wierd (like "ÿÿ~" // aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's // specified in the 'encoding' parameter. See bug 548719. - CFStringRef CFExt = ::CFStringCreateWithCString(NULL, flatExt.get(), kCFStringEncodingUTF8); - if (CFExt) { - err = ::LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, CFExt, + CFStringRef cfExt = ::CFStringCreateWithCString(NULL, flatExt.get(), kCFStringEncodingUTF8); + if (cfExt) { + err = ::LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, cfExt, kLSRolesAll, &extAppFSRef, nsnull); if (err == noErr) { haveAppForExt = true; PR_LOG(mLog, PR_LOG_DEBUG, ("LSGetApplicationForInfo found a default application\n")); } - ::CFRelease(CFExt); + ::CFRelease(cfExt); } } if (haveAppForType && haveAppForExt) { // Do aMIMEType and aFileExt match? if (::FSCompareFSRefs((const FSRef *) &typeAppFSRef, (const FSRef *) &extAppFSRef) == noErr) { typeAppIsDefault = true; *aFound = true; } } else if (haveAppForType) { // If aFileExt isn't empty, it doesn't match aMIMEType. if (aFileExt.IsEmpty()) { typeAppIsDefault = true; *aFound = true; } } else if (haveAppForExt) { - // If aMIMEType isn't empty, it doesn't match aFileExt. + // If aMIMEType isn't empty, it doesn't match aFileExt, which should mean + // that we haven't found a matching app. But make an exception for an app + // that also explicitly claims to handle aMIMEType, or which doesn't claim + // to handle any MIME types. This helps work around the following Apple + // design flaw: + // + // Launch Services is somewhat unreliable about registering Apple apps to + // handle MIME types. Probably this is because Apple has officially + // deprecated support for MIME types (in favor of UTIs). As a result, + // most of Apple's own apps don't explicitly claim to handle any MIME + // types (instead they claim to handle one or more UTIs). So Launch + // Services must contain logic to translate support for a given UTI into + // support for one or more MIME types, and it doesn't always do this + // correctly. For example DiskImageMounter isn't (by default) registered + // to handle the application/x-apple-diskimage MIME type. See bug 675356. + // + // Apple has also deprecated support for file extensions, and Apple apps + // also don't register to handle them. But for some reason Launch Services + // is (apparently) better about translating support for a given UTI into + // support for one or more file extensions. It's not at all clear why. if (aMIMEType.IsEmpty()) { extAppIsDefault = true; *aFound = true; + } else { + CFArrayRef extAppMIMETypes = GetMIMETypesHandledByApp(&extAppFSRef); + if (extAppMIMETypes) { + if (cfMIMEType) { + if (::CFArrayContainsValue(extAppMIMETypes, + ::CFRangeMake(0, ::CFArrayGetCount(extAppMIMETypes)), + cfMIMEType)) { + extAppIsDefault = true; + *aFound = true; + } + } + ::CFRelease(extAppMIMETypes); + } else { + extAppIsDefault = true; + *aFound = true; + } } } + if (cfMIMEType) { + ::CFRelease(cfMIMEType); + } + if (aMIMEType.IsEmpty()) { if (haveAppForExt) { // If aMIMEType is empty and we've found a default app for aFileExt, try // to get the MIME type from aFileExt. (It might also be worth doing // this when aMIMEType isn't empty but haveAppForType is false -- but // the doc for this method says that if we have a MIME type (in // aMIMEType), we need to give it preference.) NSURLFileTypeMappings *map = [NSURLFileTypeMappings sharedMappings]; @@ -377,36 +490,36 @@ nsOSHelperAppService::GetMIMEInfoFromOS( nsCOMPtr<nsILocalFileMac> app(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); if (!app) { NS_RELEASE(mimeInfoMac); [localPool release]; return nsnull; } - CFStringRef CFAppName = NULL; + CFStringRef cfAppName = NULL; if (typeAppIsDefault) { app->InitWithFSRef(&typeAppFSRef); ::LSCopyItemAttribute((const FSRef *) &typeAppFSRef, kLSRolesAll, - kLSItemDisplayName, (CFTypeRef *) &CFAppName); + kLSItemDisplayName, (CFTypeRef *) &cfAppName); } else { app->InitWithFSRef(&extAppFSRef); ::LSCopyItemAttribute((const FSRef *) &extAppFSRef, kLSRolesAll, - kLSItemDisplayName, (CFTypeRef *) &CFAppName); + kLSItemDisplayName, (CFTypeRef *) &cfAppName); } - if (CFAppName) { + if (cfAppName) { nsAutoTArray<UniChar, 255> buffer; - CFIndex appNameLength = ::CFStringGetLength(CFAppName); + CFIndex appNameLength = ::CFStringGetLength(cfAppName); buffer.SetLength(appNameLength); - ::CFStringGetCharacters(CFAppName, CFRangeMake(0, appNameLength), + ::CFStringGetCharacters(cfAppName, CFRangeMake(0, appNameLength), buffer.Elements()); nsAutoString appName; appName.Assign(buffer.Elements(), appNameLength); mimeInfoMac->SetDefaultDescription(appName); - ::CFRelease(CFAppName); + ::CFRelease(cfAppName); } mimeInfoMac->SetDefaultApplication(app); mimeInfoMac->SetPreferredAction(nsIMIMEInfo::useSystemDefault); } else { mimeInfoMac->SetPreferredAction(nsIMIMEInfo::saveToDisk); } @@ -419,31 +532,32 @@ nsOSHelperAppService::GetMIMEInfoFromOS( NSString *typeStr = [NSString stringWithCString:mimeType.get() encoding:NSASCIIStringEncoding]; NSString *extStr = map ? [map preferredExtensionForMIMEType:typeStr] : NULL; if (extStr) { nsCAutoString preferredExt; preferredExt.Assign((char *)[extStr cStringUsingEncoding:NSASCIIStringEncoding]); mimeInfoMac->AppendExtension(preferredExt); } - CFStringRef CFType = ::CFStringCreateWithCString(NULL, mimeType.get(), kCFStringEncodingUTF8); - CFStringRef CFTypeDesc = NULL; - if (::LSCopyKindStringForMIMEType(CFType, &CFTypeDesc) == noErr) { + CFStringRef cfType = ::CFStringCreateWithCString(NULL, mimeType.get(), kCFStringEncodingUTF8); + CFStringRef cfTypeDesc = NULL; + if (::LSCopyKindStringForMIMEType(cfType, &cfTypeDesc) == noErr) { nsAutoTArray<UniChar, 255> buffer; - CFIndex typeDescLength = ::CFStringGetLength(CFTypeDesc); + CFIndex typeDescLength = ::CFStringGetLength(cfTypeDesc); buffer.SetLength(typeDescLength); - ::CFStringGetCharacters(CFTypeDesc, CFRangeMake(0, typeDescLength), + ::CFStringGetCharacters(cfTypeDesc, CFRangeMake(0, typeDescLength), buffer.Elements()); nsAutoString typeDesc; typeDesc.Assign(buffer.Elements(), typeDescLength); mimeInfoMac->SetDescription(typeDesc); } - if (CFTypeDesc) - ::CFRelease(CFTypeDesc); - ::CFRelease(CFType); + if (cfTypeDesc) { + ::CFRelease(cfTypeDesc); + } + ::CFRelease(cfType); } PR_LOG(mLog, PR_LOG_DEBUG, ("OS gave us: type '%s' found '%i'\n", mimeType.get(), *aFound)); [localPool release]; return mimeInfoMac; NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
--- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -114,16 +114,20 @@ class gfxASurface; class nsChildView; class nsCocoaWindow; union nsPluginPort; namespace mozilla { namespace gl { class TextureImage; } + +namespace layers { +class LayerManagerOGL; +} } #ifndef NP_NO_CARBON enum { // Currently focused ChildView (while this TSM document is active). // Transient (only set while TSMProcessRawKeyEvent() is processing a key // event), and the ChildView will be retained and released around the call // to TSMProcessRawKeyEvent() -- so it can be weak. @@ -290,16 +294,19 @@ typedef NSInteger NSEventGestureAxis; float mCumulativeRotation; BOOL mDidForceRefreshOpenGL; // Support for fluid swipe tracking. #ifdef __LP64__ BOOL *mSwipeAnimationCancelled; #endif + + // Whether this uses off-main-thread compositing. + BOOL mUsingOMTCompositor; } // class initialization + (void)initialize; // these are sent to the first responder when the window key status changes - (void)viewsWindowDidBecomeKey; - (void)viewsWindowDidResignKey; @@ -322,16 +329,18 @@ typedef NSInteger NSEventGestureAxis; - (void) _surfaceNeedsUpdate:(NSNotification*)notification; - (BOOL)isPluginView; // Are we processing an NSLeftMouseDown event that will fail to click through? // If so, we shouldn't focus or unfocus a plugin. - (BOOL)isInFailingLeftClickThrough; +- (void)setGLContext:(NSOpenGLContext *)aGLContext; + // Simple gestures support // // XXX - The swipeWithEvent, beginGestureWithEvent, magnifyWithEvent, // rotateWithEvent, and endGestureWithEvent methods are part of a // PRIVATE interface exported by nsResponder and reverse-engineering // was necessary to obtain the methods' prototypes. Thus, Apple may // change the interface in the future without notice. // @@ -343,16 +352,18 @@ typedef NSInteger NSEventGestureAxis; - (void)rotateWithEvent:(NSEvent *)anEvent; - (void)endGestureWithEvent:(NSEvent *)anEvent; // Support for fluid swipe tracking. #ifdef __LP64__ - (void)maybeTrackScrollEventAsSwipe:(NSEvent *)anEvent scrollOverflow:(PRInt32)overflow; #endif + +- (void)setUsingOMTCompositor:(BOOL)aUseOMTC; @end class ChildViewMouseTracker { public: static void MouseMoved(NSEvent* aEvent); static void MouseScrolled(NSEvent* aEvent); @@ -428,17 +439,17 @@ public: static bool ConvertStatus(nsEventStatus aStatus) { return aStatus == nsEventStatus_eConsumeNoDefault; } NS_IMETHOD DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus); virtual bool GetShouldAccelerate(); NS_IMETHOD SetCursor(nsCursor aCursor); NS_IMETHOD SetCursor(imgIContainer* aCursor, PRUint32 aHotspotX, PRUint32 aHotspotY); - + NS_IMETHOD CaptureRollupEvents(nsIRollupListener * aListener, bool aDoCapture, bool aConsumeRollupEvent); NS_IMETHOD SetTitle(const nsAString& title); NS_IMETHOD GetAttention(PRInt32 aCycleCount); virtual bool HasPendingInputEvent(); NS_IMETHOD ActivateNativeMenuItemAt(const nsAString& indexString); @@ -481,16 +492,17 @@ public: // Mac specific methods virtual bool DispatchWindowEvent(nsGUIEvent& event); #ifdef ACCESSIBILITY already_AddRefed<nsAccessible> GetDocumentAccessible(); #endif + virtual void CreateCompositor(); virtual gfxASurface* GetThebesSurface(); virtual void DrawOver(LayerManager* aManager, nsIntRect aRect); virtual void UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries); NS_IMETHOD BeginSecureKeyboardInput(); NS_IMETHOD EndSecureKeyboardInput();
--- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -82,16 +82,17 @@ #endif #include "gfxContext.h" #include "gfxQuartzSurface.h" #include "nsRegion.h" #include "Layers.h" #include "LayerManagerOGL.h" #include "GLContext.h" +#include "mozilla/layers/CompositorCocoaWidgetHelper.h" #include "mozilla/Preferences.h" #include <dlfcn.h> #include <ApplicationServices/ApplicationServices.h> #include "sampler.h" @@ -1767,16 +1768,32 @@ NSView<mozView>* nsChildView::GetEditorV if (view) editorView = view; } return editorView; } #pragma mark - +void +nsChildView::CreateCompositor() +{ + nsBaseWidget::CreateCompositor(); + if (mCompositorChild) { + LayerManagerOGL *manager = + static_cast<LayerManagerOGL*>(compositor::GetLayerManager(mCompositorParent)); + + NSOpenGLContext *glContext = + (NSOpenGLContext *) manager->gl()->GetNativeData(GLContext::NativeGLContext); + + [(ChildView *)mView setGLContext:glContext]; + [(ChildView *)mView setUsingOMTCompositor:true]; + } +} + gfxASurface* nsChildView::GetThebesSurface() { if (!mTempThebesSurface) { mTempThebesSurface = new gfxQuartzSurface(gfxSize(1, 1), gfxASurface::ImageFormatARGB32); } return mTempThebesSurface; @@ -2091,16 +2108,26 @@ NSEvent* gLastDragMouseDownEvent = nil; NS_OBJC_BEGIN_TRY_ABORT_BLOCK; [mGLContext clearDrawable]; [mGLContext setView:self]; NS_OBJC_END_TRY_ABORT_BLOCK; } +- (void)setGLContext:(NSOpenGLContext *)aGLContext +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + mGLContext = aGLContext; + [mGLContext retain]; + + NS_OBJC_END_TRY_ABORT_BLOCK; +} + - (void)dealloc { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; [mGLContext release]; [mPendingDirtyRects release]; [mLastMouseDownEvent release]; [mClickThroughMouseDownEvent release]; @@ -2541,23 +2568,28 @@ NSEvent* gLastDragMouseDownEvent = nil; if ([cview isPluginView] && [cview pluginDrawingModel] == NPDrawingModelQuickDraw) { NSRect frame = [view frame]; paintEvent.region.Sub(paintEvent.region, nsIntRect(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height)); } } #endif - if (mGeckoChild->GetLayerManager(nsnull)->GetBackendType() == LayerManager::LAYERS_OPENGL) { - LayerManagerOGL *manager = static_cast<LayerManagerOGL*>(mGeckoChild->GetLayerManager(nsnull)); - manager->SetClippingRegion(paintEvent.region); + LayerManager *layerManager = mGeckoChild->GetLayerManager(nsnull); + if (layerManager->GetBackendType() == LayerManager::LAYERS_OPENGL) { + NSOpenGLContext *glContext; + + LayerManagerOGL *manager = static_cast<LayerManagerOGL*>(layerManager); + manager->SetClippingRegion(paintEvent.region); + glContext = (NSOpenGLContext *)manager->gl()->GetNativeData(mozilla::gl::GLContext::NativeGLContext); + if (!mGLContext) { - mGLContext = (NSOpenGLContext *)manager->gl()->GetNativeData(mozilla::gl::GLContext::NativeGLContext); - [mGLContext retain]; + [self setGLContext:glContext]; } + mGeckoChild->DispatchWindowEvent(paintEvent); // Force OpenGL to refresh the very first time we draw. This works around a // Mac OS X bug that stops windows updating on OS X when we use OpenGL. if (!mDidForceRefreshOpenGL) { [self performSelector:@selector(forceRefreshOpenGL) withObject:nil afterDelay:0]; mDidForceRefreshOpenGL = YES; } @@ -2587,16 +2619,26 @@ NSEvent* gLastDragMouseDownEvent = nil; nsAutoRetainCocoaObject kungFuDeathGrip(self); bool painted; { nsBaseWidget::AutoLayerManagerSetup setupLayerManager(mGeckoChild, targetContext, BasicLayerManager::BUFFER_NONE); painted = mGeckoChild->DispatchWindowEvent(paintEvent); } + // Force OpenGL to refresh the very first time we draw. This works around a + // Mac OS X bug that stops windows updating on OS X when we use OpenGL. + if (painted && !mDidForceRefreshOpenGL && + layerManager->AsShadowManager() && mUsingOMTCompositor) { + if (!mDidForceRefreshOpenGL) { + [self performSelector:@selector(forceRefreshOpenGL) withObject:nil afterDelay:0]; + mDidForceRefreshOpenGL = YES; + } + } + if (!painted && [self isOpaque]) { // Gecko refused to draw, but we've claimed to be opaque, so we have to // draw something--fill with white. CGContextSetRGBFillColor(aContext, 1, 1, 1, 1); CGContextFillRect(aContext, CGRectMake(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height)); } @@ -3124,16 +3166,22 @@ NSEvent* gLastDragMouseDownEvent = nil; // We keep a pointer to the __block variable (animationCanceled) so we // can cancel our block handler at any time. Note: We must assign // &animationCanceled after our block creation and copy -- its address // isn't resolved until then! mSwipeAnimationCancelled = &animationCancelled; } #endif // #ifdef __LP64__ +- (void)setUsingOMTCompositor:(BOOL)aUseOMTC +{ + mUsingOMTCompositor = aUseOMTC; +} + + // Returning NO from this method only disallows ordering on mousedown - in order // to prevent it for mouseup too, we need to call [NSApp preventWindowOrdering] // when handling the mousedown event. - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent*)aEvent { // Always using system-provided window ordering for normal windows. if (![[self window] isKindOfClass:[PopupWindow class]]) return NO;
--- a/widget/xpwidgets/nsBaseWidget.cpp +++ b/widget/xpwidgets/nsBaseWidget.cpp @@ -33,16 +33,18 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "mozilla/Util.h" +#include "mozilla/layers/CompositorChild.h" +#include "mozilla/layers/CompositorParent.h" #include "nsBaseWidget.h" #include "nsDeviceContext.h" #include "nsCOMPtr.h" #include "nsGfxCIID.h" #include "nsWidgetsCID.h" #include "nsServiceManagerUtils.h" #include "nsIScreenManager.h" #include "nsAppDirectoryServiceDefs.h" @@ -50,31 +52,34 @@ #include "nsIContent.h" #include "nsIServiceManager.h" #include "mozilla/Preferences.h" #include "BasicLayers.h" #include "LayerManagerOGL.h" #include "nsIXULRuntime.h" #include "nsIGfxInfo.h" #include "npapi.h" +#include "base/thread.h" #ifdef DEBUG #include "nsIObserver.h" static void debug_RegisterPrefCallbacks(); static bool debug_InSecureKeyboardInputMode = false; #endif #ifdef NOISY_WIDGET_LEAKS static PRInt32 gNumWidgets; #endif using namespace mozilla::layers; using namespace mozilla; +using base::Thread; +using mozilla::ipc::AsyncChannel; nsIContent* nsBaseWidget::mLastRollup = nsnull; // nsBaseWidget NS_IMPL_ISUPPORTS1(nsBaseWidget, nsIWidget) nsAutoRollup::nsAutoRollup() @@ -99,16 +104,17 @@ nsAutoRollup::~nsAutoRollup() //------------------------------------------------------------------------- nsBaseWidget::nsBaseWidget() : mClientData(nsnull) , mViewWrapperPtr(nsnull) , mEventCallback(nsnull) , mViewCallback(nsnull) , mContext(nsnull) +, mCompositorThread(nsnull) , mCursor(eCursor_standard) , mWindowType(eWindowType_child) , mBorderStyle(eBorderStyle_none) , mOnDestroyCalled(false) , mUseAcceleratedRendering(false) , mTemporarilyUseBasicLayerManager(false) , mBounds(0,0,0,0) , mOriginalBounds(nsnull) @@ -137,16 +143,22 @@ nsBaseWidget::~nsBaseWidget() { if (mLayerManager && mLayerManager->GetBackendType() == LayerManager::LAYERS_BASIC) { static_cast<BasicLayerManager*>(mLayerManager.get())->ClearRetainerWidget(); } if (mLayerManager) { mLayerManager->Destroy(); + mLayerManager = nsnull; + } + + if (mCompositorChild) { + mCompositorChild->Destroy(); + delete mCompositorThread; } #ifdef NOISY_WIDGET_LEAKS gNumWidgets--; printf("WIDGETS- = %d\n", gNumWidgets); #endif NS_IF_RELEASE(mContext); @@ -809,36 +821,81 @@ nsBaseWidget::GetShouldAccelerate() if (accelerateByDefault) return true; /* use the window acceleration flag */ return mUseAcceleratedRendering; } +void nsBaseWidget::CreateCompositor() +{ + mCompositorParent = new CompositorParent(this); + mCompositorThread = new Thread("CompositorThread"); + if (mCompositorThread->Start()) { + LayerManager* lm = CreateBasicLayerManager(); + MessageLoop *childMessageLoop = mCompositorThread->message_loop(); + mCompositorChild = new CompositorChild(lm); + AsyncChannel *parentChannel = mCompositorParent->GetIPCChannel(); + AsyncChannel::Side childSide = mozilla::ipc::AsyncChannel::Child; + mCompositorChild->Open(parentChannel, childMessageLoop, childSide); + PLayersChild* shadowManager = + mCompositorChild->SendPLayersConstructor(LayerManager::LAYERS_OPENGL); + + if (shadowManager) { + ShadowLayerForwarder* lf = lm->AsShadowForwarder(); + if (!lf) { + delete lm; + mCompositorChild = nsnull; + } + lf->SetShadowManager(shadowManager); + lf->SetParentBackendType(LayerManager::LAYERS_OPENGL); + + mLayerManager = lm; + } else { + NS_WARNING("fail to construct LayersChild"); + delete lm; + mCompositorChild = nsnull; + } + } +} + LayerManager* nsBaseWidget::GetLayerManager(PLayersChild* aShadowManager, LayersBackend aBackendHint, LayerManagerPersistence aPersistence, bool* aAllowRetaining) { if (!mLayerManager) { mUseAcceleratedRendering = GetShouldAccelerate(); if (mUseAcceleratedRendering) { - nsRefPtr<LayerManagerOGL> layerManager = new LayerManagerOGL(this); - /** - * XXX - On several OSes initialization is expected to fail for now. - * If we'd get a none-basic layer manager they'd crash. This is ok though - * since on those platforms it will fail. Anyone implementing new - * platforms on LayerManagerOGL should ensure their widget is able to - * deal with it though! - */ - if (layerManager->Initialize()) { - mLayerManager = layerManager; + + // Try to use an async compositor first, if possible + bool useCompositor = + Preferences::GetBool("layers.offmainthreadcomposition.enabled", false); + if (useCompositor) { + // e10s uses the parameter to pass in the shadow manager from the TabChild + // so we don't expect to see it there since this doesn't support e10s. + NS_ASSERTION(aShadowManager == nsnull, "Async Compositor not supported with e10s"); + CreateCompositor(); + } + + if (!mLayerManager) { + nsRefPtr<LayerManagerOGL> layerManager = new LayerManagerOGL(this); + /** + * XXX - On several OSes initialization is expected to fail for now. + * If we'd get a non-basic layer manager they'd crash. This is ok though + * since on those platforms it will fail. Anyone implementing new + * platforms on LayerManagerOGL should ensure their widget is able to + * deal with it though! + */ + if (layerManager->Initialize()) { + mLayerManager = layerManager; + } } } if (!mLayerManager) { mBasicLayerManager = mLayerManager = CreateBasicLayerManager(); } } if (mTemporarilyUseBasicLayerManager && !mBasicLayerManager) { mBasicLayerManager = CreateBasicLayerManager();
--- a/widget/xpwidgets/nsBaseWidget.h +++ b/widget/xpwidgets/nsBaseWidget.h @@ -46,31 +46,45 @@ #include "nsGUIEvent.h" #include "nsAutoPtr.h" #include "BasicLayers.h" class nsIContent; class nsAutoRollup; class gfxContext; +namespace mozilla { +namespace layers { +class CompositorChild; +class CompositorParent; +} +} + +namespace base { +class Thread; +} + /** * Common widget implementation used as base class for native * or crossplatform implementations of Widgets. * All cross-platform behavior that all widgets need to implement * should be placed in this class. * (Note: widget implementations are not required to use this * class, but it gives them a head start.) */ class nsBaseWidget : public nsIWidget { friend class nsAutoRollup; protected: typedef mozilla::layers::BasicLayerManager BasicLayerManager; + typedef mozilla::layers::CompositorChild CompositorChild; + typedef mozilla::layers::CompositorParent CompositorParent; + typedef base::Thread Thread; public: nsBaseWidget(); virtual ~nsBaseWidget(); NS_DECL_ISUPPORTS // nsIWidget interface @@ -112,16 +126,17 @@ public: NS_IMETHOD HideWindowChrome(bool aShouldHide); NS_IMETHOD MakeFullScreen(bool aFullScreen); virtual nsDeviceContext* GetDeviceContext(); virtual LayerManager* GetLayerManager(PLayersChild* aShadowManager = nsnull, LayersBackend aBackendHint = LayerManager::LAYERS_NONE, LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT, bool* aAllowRetaining = nsnull); + virtual void CreateCompositor(); virtual void DrawOver(LayerManager* aManager, nsIntRect aRect) {} virtual void UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries) {} virtual gfxASurface* GetThebesSurface(); NS_IMETHOD SetModal(bool aModal); NS_IMETHOD SetWindowClass(const nsAString& xulWinType); NS_IMETHOD MoveClient(PRInt32 aX, PRInt32 aY); NS_IMETHOD ResizeClient(PRInt32 aWidth, PRInt32 aHeight, bool aRepaint); NS_IMETHOD ResizeClient(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight, bool aRepaint); @@ -263,16 +278,19 @@ protected: protected: void* mClientData; ViewWrapper* mViewWrapperPtr; EVENT_CALLBACK mEventCallback; EVENT_CALLBACK mViewCallback; nsDeviceContext* mContext; nsRefPtr<LayerManager> mLayerManager; nsRefPtr<LayerManager> mBasicLayerManager; + nsRefPtr<CompositorChild> mCompositorChild; + nsRefPtr<CompositorParent> mCompositorParent; + Thread* mCompositorThread; nscolor mBackground; nscolor mForeground; nsCursor mCursor; nsWindowType mWindowType; nsBorderStyle mBorderStyle; bool mOnDestroyCalled; bool mUseAcceleratedRendering; bool mTemporarilyUseBasicLayerManager;
--- a/xpcom/glue/nsDeque.cpp +++ b/xpcom/glue/nsDeque.cpp @@ -345,16 +345,32 @@ void* nsDeque::PeekFront() { void* nsDeque::ObjectAt(PRInt32 aIndex) const { void* result=0; if ((aIndex>=0) && (aIndex<mSize)) { result=mData[modulus(mOrigin + aIndex, mCapacity)]; } return result; } +void* nsDeque::RemoveObjectAt(PRInt32 aIndex) { + if ((aIndex<0) || (aIndex>=mSize)) { + return 0; + } + void* result=mData[modulus(mOrigin + aIndex, mCapacity)]; + + // "Shuffle down" all elements in the array by 1, overwritting the element + // being removed. + for (PRInt32 i=aIndex; i<mSize; i++) { + mData[modulus(mOrigin + i, mCapacity)] = mData[modulus(mOrigin + i + 1, mCapacity)]; + } + mSize--; + + return result; +} + /** * Create and return an iterator pointing to * the beginning of the queue. Note that this * takes the circular buffer semantics into account. * * @return new deque iterator, init'ed to 1st item */ nsDequeIterator nsDeque::Begin() const{
--- a/xpcom/glue/nsDeque.h +++ b/xpcom/glue/nsDeque.h @@ -148,16 +148,24 @@ class NS_COM_GLUE nsDeque { * Retrieve the i'th member from the deque without removing it. * * @param index of desired item * @return i'th element in list */ void* ObjectAt(int aIndex) const; /** + * Removes and returns the i'th member from the deque. + * + * @param index of desired item + * @return the element which was removed + */ + void* RemoveObjectAt(int aIndex); + + /** * Remove all items from container without destroying them. * * @return *this */ nsDeque& Empty(); /** * Remove and delete all items from container.
--- a/xpcom/glue/nsHashKeys.h +++ b/xpcom/glue/nsHashKeys.h @@ -219,16 +219,49 @@ public: } enum { ALLOW_MEMMOVE = true }; private: nsCOMPtr<nsISupports> mSupports; }; /** + * hashkey wrapper using refcounted * KeyType + * + * @see nsTHashtable::EntryType for specification + */ +template<class T> +class nsRefPtrHashKey : public PLDHashEntryHdr +{ +public: + typedef T* KeyType; + typedef const T* KeyTypePointer; + + nsRefPtrHashKey(const T* key) : + mKey(const_cast<T*>(key)) { } + nsRefPtrHashKey(const nsRefPtrHashKey& toCopy) : + mKey(toCopy.mKey) { } + ~nsRefPtrHashKey() { } + + KeyType GetKey() const { return mKey; } + + bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; } + + static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; } + static PLDHashNumber HashKey(KeyTypePointer aKey) + { + return NS_PTR_TO_INT32(aKey) >>2; + } + enum { ALLOW_MEMMOVE = true }; + +private: + nsRefPtr<T> mKey; +}; + +/** * hashkey wrapper using T* KeyType * * @see nsTHashtable::EntryType for specification */ template<class T> class nsPtrHashKey : public PLDHashEntryHdr { public:
--- a/xpcom/tests/Makefile.in +++ b/xpcom/tests/Makefile.in @@ -84,16 +84,17 @@ SIMPLE_PROGRAMS := $(CPPSRCS:.cpp=$(BIN_ CPP_UNIT_TESTS = \ ShowAlignments.cpp \ ShowSSEConfig.cpp \ TestAutoPtr.cpp \ TestCOMArray.cpp \ TestCOMPtr.cpp \ TestCOMPtrEq.cpp \ + TestDeque.cpp \ TestFile.cpp \ TestHashtables.cpp \ TestID.cpp \ TestObserverArray.cpp \ TestObserverService.cpp \ TestPipe.cpp \ TestRefPtr.cpp \ TestTextFormatter.cpp \ @@ -104,17 +105,16 @@ CPP_UNIT_TESTS = \ ifdef MOZ_MEMORY CPP_UNIT_TESTS += TestJemalloc.cpp endif # XXX Make this tests work in libxul builds. #CPP_UNIT_TESTS += \ # TestArray.cpp \ # TestCRT.cpp \ -# TestDeque.cpp \ # TestEncoding.cpp \ # TestExpirationTracker.cpp \ # TestPipes.cpp \ # TestPriorityQueue.cpp \ # TestStorageStream.cpp \ # TestStrings.cpp \ # TestSynchronization.cpp \ # TestTArray.cpp \
--- a/xpcom/tests/TestDeque.cpp +++ b/xpcom/tests/TestDeque.cpp @@ -30,126 +30,244 @@ * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ +#include "TestHarness.h" #include "nsDeque.h" #include "nsCRT.h" #include <stdio.h> /************************************************************** Now define the token deallocator class... **************************************************************/ class _TestDeque { public: - _TestDeque() { - SelfTest(); - } - int SelfTest(); - nsresult OriginalTest(); - nsresult OriginalFlaw(); - nsresult AssignFlaw(); + int Test(); +private: + int OriginalTest(); + int OriginalFlaw(); + int AssignFlaw(); + int TestRemove(); }; static _TestDeque sTestDeque; class _Dealloc: public nsDequeFunctor { virtual void* operator()(void* aObject) { return 0; } }; +#define TEST(aCondition, aMsg) \ + if (!(aCondition)) { fail("TestDeque: "#aMsg); return 1; } + + /** * conduct automated self test for this class * * @param * @return */ -int _TestDeque::SelfTest() { +int _TestDeque::Test() { /* the old deque should have failed a bunch of these tests */ int results=0; results+=OriginalTest(); results+=OriginalFlaw(); results+=AssignFlaw(); + results+=TestRemove(); return results; } -nsresult _TestDeque::OriginalTest() { - int ints[200]; - int count=sizeof(ints)/sizeof(int); +int _TestDeque::OriginalTest() { + const int size = 200; + int ints[size]; int i=0; - int* temp; + int temp; nsDeque theDeque(new _Dealloc); //construct a simple one... - for (i=0;i<count;i++) { //initialize'em - ints[i]=10*(1+i); + // ints = [0...199] + for (i=0;i<size;i++) { //initialize'em + ints[i]=i; } + // queue = [0...69] for (i=0;i<70;i++) { theDeque.Push(&ints[i]); + temp=*(int*)theDeque.Peek(); + TEST(temp == i, "Verify end after push #1"); + TEST(theDeque.GetSize() == i + 1, "Verify size after push #1"); } - for (i=0;i<56;i++) { - temp=(int*)theDeque.Pop(); + TEST(theDeque.GetSize() == 70, "Verify overall size after pushes #1"); + // queue = [0...14] + for (i=1;i<=55;i++) { + temp=*(int*)theDeque.Pop(); + TEST(temp == 70-i, "Verify end after pop # 1"); + TEST(theDeque.GetSize() == 70 - i, "Verify size after pop # 1"); } + TEST(theDeque.GetSize() == 15, "Verify overall size after pops"); + + // queue = [0...14,0...54] for (i=0;i<55;i++) { theDeque.Push(&ints[i]); + temp=*(int*)theDeque.Peek(); + TEST(temp == i, "Verify end after push #2"); + TEST(theDeque.GetSize() == i + 15 + 1, "Verify size after push # 2"); } - for (i=0;i<35;i++) { - temp=(int*)theDeque.Pop(); + TEST(theDeque.GetSize() == 70, "Verify size after end of all pushes #2"); + + // queue = [0...14,0...19] + for (i=1;i<=35;i++) { + temp=*(int*)theDeque.Pop(); + TEST(temp == 55-i, "Verify end after pop # 2"); + TEST(theDeque.GetSize() == 70 - i, "Verify size after pop #2"); } + TEST(theDeque.GetSize() == 35, "Verify overall size after end of all pops #2"); + + // queue = [0...14,0...19,0...34] for (i=0;i<35;i++) { theDeque.Push(&ints[i]); + temp = *(int*)theDeque.Peek(); + TEST(temp == i, "Verify end after push # 3"); + TEST(theDeque.GetSize() == 35 + 1 + i, "Verify size after push #3"); } - for (i=0;i<38;i++) { - temp=(int*)theDeque.Pop(); + + // queue = [0...14,0...19] + for (i=0;i<35;i++) { + temp=*(int*)theDeque.Pop(); + TEST(temp == 34 - i, "Verify end after pop # 3"); } - return NS_OK; + + // queue = [0...14] + for (i=0;i<20;i++) { + temp=*(int*)theDeque.Pop(); + TEST(temp == 19 - i, "Verify end after pop # 4"); + } + + // queue = [] + for (i=0;i<15;i++) { + temp=*(int*)theDeque.Pop(); + TEST(temp == 14 - i, "Verify end after pop # 5"); + } + + TEST(theDeque.GetSize() == 0, "Deque should finish empty."); + + return 0; } -nsresult _TestDeque::OriginalFlaw() { +int _TestDeque::OriginalFlaw() { int ints[200]; int i=0; - int* temp; - nsDeque secondDeque(new _Dealloc); + int temp; + nsDeque d(new _Dealloc); /** * Test 1. Origin near end, semi full, call Peek(). * you start, mCapacity is 8 */ printf("fill array\n"); - for (i=32; i; --i) - ints[i]=i*3+10; - printf("push 6 times\n"); - for (i=0; i<6; i++) - secondDeque.Push(&ints[i]); - printf("popfront 4 times:\n"); - for (i=4; i; --i) { - temp=(int*)secondDeque.PopFront(); - printf("%d\t",*temp); + for (i=0; i<30; i++) + ints[i]=i; + + for (i=0; i<6; i++) { + d.Push(&ints[i]); + temp = *(int*)d.Peek(); + TEST(temp == i, "OriginalFlaw push #1"); + } + TEST(d.GetSize() == 6, "OriginalFlaw size check #1"); + + for (i=0; i<4; i++) { + temp=*(int*)d.PopFront(); + TEST(temp == i, "PopFront test"); + } + // d = [4,5] + TEST(d.GetSize() == 2, "OriginalFlaw size check #2"); + + for (i=0; i<4; i++) { + d.Push(&ints[6 + i]); } - printf("push 4 times\n"); - for (int j=4; j; --j) - secondDeque.Push(&ints[++i]); - printf("origin should now be about 4\n"); - printf("and size should be 6\n"); - printf("origin+size>capacity\n"); + // d = [4...9] + + for (i=4; i<=9; i++) { + temp=*(int*)d.PopFront(); + TEST(temp == i, "OriginalFlaw empty check"); + } + + return 0; +} - /*<akk> Oh, I see ... it's a circular buffer */ - printf("but the old code wasn't behaving accordingly.\n"); +int _TestDeque::AssignFlaw() { + nsDeque src(new _Dealloc),dest(new _Dealloc); + return 0; +} - /*right*/ - printf("we shouldn't crash or anything interesting, "); - - temp=(int*)secondDeque.Peek(); - printf("peek: %d\n",*temp); - return NS_OK; +static bool VerifyContents(const nsDeque& aDeque, const int* aContents, int aLength) { + for (int i=0; i<aLength; ++i) { + if (*(int*)aDeque.ObjectAt(i) != aContents[i]) { + return false; + } + } + return true; } -nsresult _TestDeque::AssignFlaw() { - nsDeque src(new _Dealloc),dest(new _Dealloc); - return NS_OK; +int _TestDeque::TestRemove() { + nsDeque d; + const int count = 10; + int ints[count]; + for (int i=0; i<count; i++) { + ints[i] = i; + } + + for (int i=0; i<6; i++) { + d.Push(&ints[i]); + } + // d = [0...5] + d.PopFront(); + d.PopFront(); + + // d = [2,5] + for (int i=2; i<=5; i++) { + int t = *(int*)d.ObjectAt(i-2); + TEST(t == i, "Verify ObjectAt()"); + } + + d.RemoveObjectAt(1); + // d == [2,4,5] + static const int t1[] = {2,4,5}; + TEST(VerifyContents(d, t1, 3), "verify contents t1"); + + d.PushFront(&ints[1]); + d.PushFront(&ints[0]); + d.PushFront(&ints[7]); + d.PushFront(&ints[6]); + // d == [6,7,0,1,2,4,5] // (0==mOrigin) + static const int t2[] = {6,7,0,1,2,4,5}; + TEST(VerifyContents(d, t2, 7), "verify contents t2"); + + d.RemoveObjectAt(1); + // d == [6,0,1,2,4,5] // (1==mOrigin) + static const int t3[] = {6,0,1,2,4,5}; + TEST(VerifyContents(d, t3, 6), "verify contents t3"); + + d.RemoveObjectAt(5); + // d == [6,0,1,2,4] // (1==mOrigin) + static const int t4[] = {6,0,1,2,4}; + TEST(VerifyContents(d, t4, 5), "verify contents t4"); + + d.RemoveObjectAt(0); + // d == [0,1,2,4] // (2==mOrigin) + static const int t5[] = {0,1,2,4}; + TEST(VerifyContents(d, t5, 4), "verify contents t5"); + + + return 0; } int main (void) { + ScopedXPCOM xpcom("TestTimers"); + NS_ENSURE_FALSE(xpcom.failed(), 1); + _TestDeque test; + int result = test.Test(); + TEST(result == 0, "All tests pass"); return 0; }