diff --git a/b2g/config/emulator-ics/config.json b/b2g/config/emulator-ics/config.json index dd38047af583..5df46a38238f 100644 --- a/b2g/config/emulator-ics/config.json +++ b/b2g/config/emulator-ics/config.json @@ -2,7 +2,7 @@ "config_version": 2, "tooltool_manifest": "releng-emulator-ics.tt", "mock_target": "mozilla-centos6-x86_64", - "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "glibc-devel.i686", "libstdc++.i686", "zlib-devel.i686", "ncurses-devel.i686", "libX11-devel.i686", "mesa-libGL-devel.i686", "mesa-libGL-devel", "libX11-devel", "git"], + "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "glibc-devel.i686", "libstdc++.i686", "zlib-devel.i686", "ncurses-devel.i686", "libX11-devel.i686", "mesa-libGL-devel.i686", "mesa-libGL-devel", "libX11-devel", "git", "libxml2"], "mock_files": [["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"]], "build_targets": ["droid", "package-emulator", "package-tests"], "upload_files": [ diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 22002b0a1f83..f60dbc382557 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -1,10 +1,17 @@ + + + + + + + @@ -12,13 +19,13 @@ - + - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 5de30a014ea0..5c3244147781 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -1,9 +1,15 @@ + + + + + + @@ -11,10 +17,10 @@ - + - + diff --git a/b2g/config/emulator/config.json b/b2g/config/emulator/config.json index 3466c524577c..e7b87238db8e 100644 --- a/b2g/config/emulator/config.json +++ b/b2g/config/emulator/config.json @@ -2,7 +2,7 @@ "config_version": 2, "tooltool_manifest": "releng-emulator.tt", "mock_target": "mozilla-centos6-x86_64", - "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "glibc-devel.i686", "libstdc++.i686", "zlib-devel.i686", "ncurses-devel.i686", "libX11-devel.i686", "mesa-libGL-devel.i686", "mesa-libGL-devel", "libX11-devel", "git"], + "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "glibc-devel.i686", "libstdc++.i686", "zlib-devel.i686", "ncurses-devel.i686", "libX11-devel.i686", "mesa-libGL-devel.i686", "mesa-libGL-devel", "libX11-devel", "git", "libxml2"], "mock_files": [["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"]], "build_targets": ["droid", "package-emulator", "package-tests"], "upload_files": [ diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 22002b0a1f83..f60dbc382557 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -1,10 +1,17 @@ + + + + + + + @@ -12,13 +19,13 @@ - + - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 77df4e2265cb..6902312d744d 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,9 @@ { - "revision": "d1729e9d8fcc010e42ef73befa93d3c07a4304c6", + "git": { + "remote": "", + "branch": "", + "revision": "" + }, + "revision": "8bb0cf53956e54999a5f876434207216d9d8982a", "repo_path": "/integration/gaia-central" } diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 352d27441e94..fe397d1e5821 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -1,9 +1,15 @@ + + + + + + @@ -11,12 +17,12 @@ - + - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 6f46c6eb56a9..3c455685bc9a 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -1,8 +1,13 @@ + + + + + @@ -10,7 +15,7 @@ - + diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml index fb7417dd7d9c..3fde82ef85b6 100644 --- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -1,10 +1,17 @@ + + + + + + + @@ -12,12 +19,12 @@ - + - + diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml index fb338a5e2200..80fb5ce7bba6 100644 --- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -1,9 +1,15 @@ + + + + + + @@ -11,12 +17,12 @@ - + - + diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml index 575c5b880932..8f03af415653 100644 --- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -1,9 +1,15 @@ + + + + + + @@ -11,10 +17,10 @@ - + - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 1b2421b81dcc..fbd9e19cd110 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -1,9 +1,15 @@ + + + + + + @@ -11,12 +17,12 @@ - + - + diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index b91e20e42380..98b584186004 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -389,7 +389,22 @@ panel[noactions] > richlistbox > richlistitem[type~="action"] > .ac-url-box > .a } #identity-icon-labels { - max-width: 12vw; + max-width: 18em; +} +@media (max-width: 700px) { + #identity-icon-labels { + max-width: 70px; + } +} +@media (max-width: 600px) { + #identity-icon-labels { + max-width: 60px; + } +} +@media (max-width: 500px) { + #identity-icon-labels { + max-width: 50px; + } } #identity-icon-country-label { diff --git a/browser/base/content/test/general/browser_windowopen_reflows.js b/browser/base/content/test/general/browser_windowopen_reflows.js index 8e72d0def00c..8d34636f79ec 100644 --- a/browser/base/content/test/general/browser_windowopen_reflows.js +++ b/browser/base/content/test/general/browser_windowopen_reflows.js @@ -25,15 +25,17 @@ const EXPECTED_REFLOWS = [ "ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm", ]; -if (Services.appinfo.OS == "Darwin") { - // TabsInTitlebar._update causes a reflow on OS X trying to do calculations +if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") { + // TabsInTitlebar._update causes a reflow on OS X and Windows trying to do calculations // since layout info is already dirty. This doesn't seem to happen before - // MozAfterPaint on other platforms. + // MozAfterPaint on Linux. EXPECTED_REFLOWS.push("rect@chrome://browser/content/browser.js|" + "TabsInTitlebar._update@chrome://browser/content/browser.js|" + "updateAppearance@chrome://browser/content/browser.js|" + "handleEvent@chrome://browser/content/tabbrowser.xml|"); +} +if (Services.appinfo.OS == "Darwin") { // _onOverflow causes a reflow getting widths. EXPECTED_REFLOWS.push("OverflowableToolbar.prototype._onOverflow@resource:///modules/CustomizableUI.jsm|" + "OverflowableToolbar.prototype.init@resource:///modules/CustomizableUI.jsm|" + diff --git a/browser/components/customizableui/content/panelUI.xml b/browser/components/customizableui/content/panelUI.xml index 5510e9ed5694..a55316ccd2b5 100644 --- a/browser/components/customizableui/content/panelUI.xml +++ b/browser/components/customizableui/content/panelUI.xml @@ -217,7 +217,7 @@ this._transitioning = false; }.bind(this)); - let newHeight = this._heightOfSubview(viewNode); + let newHeight = this._heightOfSubview(viewNode, this._subViews); this._viewContainer.style.height = newHeight + "px"; this._subViewObserver.observe(viewNode, { @@ -324,7 +324,7 @@ @@ -345,16 +345,50 @@ + 0) { + elementCS = win.getComputedStyle(element); + } + } + if (elementCS) { + // Include margins - but not borders or paddings because they + // were dealt with above. + height += parseFloat(elementCS.marginTop) + parseFloat(elementCS.marginBottom); + } + return height; + } + let win = aSubview.ownerDocument.defaultView; let body = aSubview.querySelector(".panel-subview-body"); - let height = body ? body.scrollHeight : aSubview.scrollHeight; + let height = getFullHeight(body || aSubview); if (body) { let header = aSubview.querySelector(".panel-subview-header"); let footer = aSubview.querySelector(".panel-subview-footer"); - height += (header ? header.scrollHeight : 0) + - (footer ? footer.scrollHeight : 0); + height += (header ? getFullHeight(header) : 0) + + (footer ? getFullHeight(footer) : 0); } - return height; + if (aContainerToCheck) { + let containerCS = win.getComputedStyle(aContainerToCheck); + height += parseFloat(containerCS.paddingTop) + parseFloat(containerCS.paddingBottom); + } + return Math.round(height); ]]> diff --git a/browser/components/customizableui/src/CustomizeMode.jsm b/browser/components/customizableui/src/CustomizeMode.jsm index 9664c019e8b1..2eaf9e07f1cb 100644 --- a/browser/components/customizableui/src/CustomizeMode.jsm +++ b/browser/components/customizableui/src/CustomizeMode.jsm @@ -292,6 +292,12 @@ CustomizeMode.prototype = { return; } + if (this.resetting) { + LOG("Attempted to exit while we're resetting. " + + "We'll exit after resetting has finished."); + return; + } + this._handler.isExitingCustomizeMode = true; CustomizableUI.removeListener(this); @@ -885,6 +891,9 @@ CustomizeMode.prototype = { this._updateEmptyPaletteNotice(); this._showPanelCustomizationPlaceholders(); this.resetting = false; + if (!this._wantToBeInCustomizeMode) { + this.exit(); + } }.bind(this)).then(null, ERROR); }, diff --git a/browser/devtools/app-manager/app-projects.js b/browser/devtools/app-manager/app-projects.js index fb1658b15fe6..885a9bc52a83 100644 --- a/browser/devtools/app-manager/app-projects.js +++ b/browser/devtools/app-manager/app-projects.js @@ -2,7 +2,7 @@ const {Cc,Ci,Cu} = require("chrome"); const ObservableObject = require("devtools/shared/observable-object"); const promise = require("sdk/core/promise"); -const {EventEmitter} = Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js"); const {generateUUID} = Cc['@mozilla.org/uuid-generator;1'].getService(Ci.nsIUUIDGenerator); /** diff --git a/browser/devtools/app-manager/content/projects.js b/browser/devtools/app-manager/content/projects.js index 6304681cf76b..913d3b71874e 100644 --- a/browser/devtools/app-manager/content/projects.js +++ b/browser/devtools/app-manager/content/projects.js @@ -16,7 +16,7 @@ const {Services} = Cu.import("resource://gre/modules/Services.jsm"); const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm"); const {installHosted, installPackaged, getTargetForApp, reloadApp, launchApp, closeApp} = require("devtools/app-actor-front"); -const {EventEmitter} = Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js"); const promise = require("sdk/core/promise"); diff --git a/browser/devtools/app-manager/content/utils.js b/browser/devtools/app-manager/content/utils.js index de82a2e874ad..6907ee823a18 100644 --- a/browser/devtools/app-manager/content/utils.js +++ b/browser/devtools/app-manager/content/utils.js @@ -16,7 +16,7 @@ let Utils = (function() { const {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); const {require} = devtools; - const EventEmitter = require("devtools/shared/event-emitter"); + const EventEmitter = require("devtools/toolkit/event-emitter"); function _createSetEventForwarder(key, finalStore) { diff --git a/browser/devtools/commandline/BuiltinCommands.jsm b/browser/devtools/commandline/BuiltinCommands.jsm index bc638e1e318d..0f39f52292b1 100644 --- a/browser/devtools/commandline/BuiltinCommands.jsm +++ b/browser/devtools/commandline/BuiltinCommands.jsm @@ -17,7 +17,7 @@ let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").P Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/devtools/gcli.jsm"); -Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools; let Telemetry = devtools.require("devtools/shared/telemetry"); diff --git a/browser/devtools/debugger/debugger-controller.js b/browser/devtools/debugger/debugger-controller.js index 316d69d7a1e4..b4d8b7d861df 100644 --- a/browser/devtools/debugger/debugger-controller.js +++ b/browser/devtools/debugger/debugger-controller.js @@ -90,7 +90,7 @@ const FRAME_TYPE = { Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/devtools/dbg-client.jsm"); -Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); Cu.import("resource:///modules/devtools/SimpleListWidget.jsm"); Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm"); Cu.import("resource:///modules/devtools/SideMenuWidget.jsm"); diff --git a/browser/devtools/debugger/panel.js b/browser/devtools/debugger/panel.js index 8b1609398857..511f66c78cf2 100644 --- a/browser/devtools/debugger/panel.js +++ b/browser/devtools/debugger/panel.js @@ -7,7 +7,7 @@ const { Cc, Ci, Cu, Cr } = require("chrome"); const promise = require("sdk/core/promise"); -const EventEmitter = require("devtools/shared/event-emitter"); +const EventEmitter = require("devtools/toolkit/event-emitter"); const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {}); diff --git a/browser/devtools/framework/gDevTools.jsm b/browser/devtools/framework/gDevTools.jsm index ab52bf129931..e90c39cd3a26 100644 --- a/browser/devtools/framework/gDevTools.jsm +++ b/browser/devtools/framework/gDevTools.jsm @@ -10,7 +10,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise; Cu.import("resource://gre/modules/devtools/Loader.jsm"); diff --git a/browser/devtools/framework/selection.js b/browser/devtools/framework/selection.js index 978e2a2d4654..be8b3778874b 100644 --- a/browser/devtools/framework/selection.js +++ b/browser/devtools/framework/selection.js @@ -7,7 +7,7 @@ "use strict"; const {Cu, Ci} = require("chrome"); -let EventEmitter = require("devtools/shared/event-emitter"); +let EventEmitter = require("devtools/toolkit/event-emitter"); /** * API diff --git a/browser/devtools/framework/sidebar.js b/browser/devtools/framework/sidebar.js index 9f1e9b83490e..6d0898911c6b 100644 --- a/browser/devtools/framework/sidebar.js +++ b/browser/devtools/framework/sidebar.js @@ -9,7 +9,7 @@ const {Cu} = require("chrome"); Cu.import("resource://gre/modules/Services.jsm"); var promise = require("sdk/core/promise"); -var EventEmitter = require("devtools/shared/event-emitter"); +var EventEmitter = require("devtools/toolkit/event-emitter"); var Telemetry = require("devtools/shared/telemetry"); const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; diff --git a/browser/devtools/framework/target.js b/browser/devtools/framework/target.js index cfa0d7ba34d5..13fc8fd28377 100644 --- a/browser/devtools/framework/target.js +++ b/browser/devtools/framework/target.js @@ -7,7 +7,7 @@ const {Cc, Ci, Cu} = require("chrome"); var promise = require("sdk/core/promise"); -var EventEmitter = require("devtools/shared/event-emitter"); +var EventEmitter = require("devtools/toolkit/event-emitter"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer", @@ -352,6 +352,8 @@ TabTarget.prototype = { * Setup listeners for remote debugging, updating existing ones as necessary. */ _setupRemoteListeners: function TabTarget__setupRemoteListeners() { + this.client.addListener("closed", this.destroy); + this._onTabDetached = (aType, aPacket) => { // We have to filter message to ensure that this detach is for this tab if (aPacket.from == this._form.actor) { @@ -384,6 +386,7 @@ TabTarget.prototype = { * Teardown listeners for remote debugging. */ _teardownRemoteListeners: function TabTarget__teardownRemoteListeners() { + this.client.removeListener("closed", this.destroy); this.client.removeListener("tabNavigated", this._onTabNavigated); this.client.removeListener("tabDetached", this._onTabDetached); }, diff --git a/browser/devtools/framework/test/browser.ini b/browser/devtools/framework/test/browser.ini index 7b202a678e9f..9803331ee3fb 100644 --- a/browser/devtools/framework/test/browser.ini +++ b/browser/devtools/framework/test/browser.ini @@ -10,6 +10,7 @@ support-files = [browser_keybindings.js] [browser_new_activation_workflow.js] [browser_target_events.js] +[browser_target_remote.js] [browser_toolbox_dynamic_registration.js] [browser_toolbox_highlight.js] [browser_toolbox_hosts.js] diff --git a/browser/devtools/framework/test/browser_devtools_api.js b/browser/devtools/framework/test/browser_devtools_api.js index 79bb39e0d7d2..938c3a3c238d 100644 --- a/browser/devtools/framework/test/browser_devtools_api.js +++ b/browser/devtools/framework/test/browser_devtools_api.js @@ -7,7 +7,7 @@ const Cu = Components.utils; const toolId = "test-tool"; let tempScope = {}; -Cu.import("resource:///modules/devtools/shared/event-emitter.js", tempScope); +Cu.import("resource://gre/modules/devtools/event-emitter.js", tempScope); let EventEmitter = tempScope.EventEmitter; function test() { diff --git a/browser/devtools/framework/test/browser_target_remote.js b/browser/devtools/framework/test/browser_target_remote.js new file mode 100644 index 000000000000..7f0f7d762cba --- /dev/null +++ b/browser/devtools/framework/test/browser_target_remote.js @@ -0,0 +1,39 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +let { DebuggerServer } = + Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {}); +let { DebuggerClient } = + Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {}); +let { devtools } = + Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); + +// Ensure target is closed if client is closed directly +function test() { + waitForExplicitFinish(); + + if (!DebuggerServer.initialized) { + DebuggerServer.init(function () { return true; }); + DebuggerServer.addBrowserActors(); + } + + var client = new DebuggerClient(DebuggerServer.connectPipe()); + client.connect(() => { + client.listTabs(response => { + let options = { + form: response, + client: client, + chrome: true + }; + + devtools.TargetFactory.forRemoteTab(options).then(target => { + target.on("close", () => { + ok(true, "Target was closed"); + DebuggerServer.destroy(); + finish(); + }); + client.close(); + }); + }); + }); +} diff --git a/browser/devtools/framework/toolbox-hosts.js b/browser/devtools/framework/toolbox-hosts.js index 3ca4a81ab68e..c9173deb0d6c 100644 --- a/browser/devtools/framework/toolbox-hosts.js +++ b/browser/devtools/framework/toolbox-hosts.js @@ -7,7 +7,7 @@ const {Cu} = require("chrome"); let promise = require("sdk/core/promise"); -let EventEmitter = require("devtools/shared/event-emitter"); +let EventEmitter = require("devtools/toolkit/event-emitter"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource:///modules/devtools/DOMHelpers.jsm"); diff --git a/browser/devtools/framework/toolbox-options.js b/browser/devtools/framework/toolbox-options.js index 7a23da61d0ca..06b6f2154d08 100644 --- a/browser/devtools/framework/toolbox-options.js +++ b/browser/devtools/framework/toolbox-options.js @@ -7,7 +7,7 @@ const {Cu, Cc, Ci} = require("chrome"); let promise = require("sdk/core/promise"); -let EventEmitter = require("devtools/shared/event-emitter"); +let EventEmitter = require("devtools/toolkit/event-emitter"); Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import("resource://gre/modules/Services.jsm"); diff --git a/browser/devtools/framework/toolbox.js b/browser/devtools/framework/toolbox.js index be4427e9e041..61ca6dcc96a0 100644 --- a/browser/devtools/framework/toolbox.js +++ b/browser/devtools/framework/toolbox.js @@ -11,7 +11,7 @@ const MAX_ZOOM = 2; let {Cc, Ci, Cu} = require("chrome"); let promise = require("sdk/core/promise"); -let EventEmitter = require("devtools/shared/event-emitter"); +let EventEmitter = require("devtools/toolkit/event-emitter"); let Telemetry = require("devtools/shared/telemetry"); let HUDService = require("devtools/webconsole/hudservice"); diff --git a/browser/devtools/inspector/inspector-panel.js b/browser/devtools/inspector/inspector-panel.js index af93a2c4f1ce..abbfe7babcf0 100644 --- a/browser/devtools/inspector/inspector-panel.js +++ b/browser/devtools/inspector/inspector-panel.js @@ -9,7 +9,7 @@ const {Cc, Ci, Cu, Cr} = require("chrome"); Cu.import("resource://gre/modules/Services.jsm"); let promise = require("sdk/core/promise"); -let EventEmitter = require("devtools/shared/event-emitter"); +let EventEmitter = require("devtools/toolkit/event-emitter"); let {CssLogic} = require("devtools/styleinspector/css-logic"); loader.lazyGetter(this, "MarkupView", () => require("devtools/markupview/markup-view").MarkupView); diff --git a/browser/devtools/markupview/html-editor.js b/browser/devtools/markupview/html-editor.js index 32517147c987..6f3b7609fab4 100644 --- a/browser/devtools/markupview/html-editor.js +++ b/browser/devtools/markupview/html-editor.js @@ -7,7 +7,7 @@ const {Cu} = require("chrome"); const Editor = require("devtools/sourceeditor/editor"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); exports.HTMLEditor = HTMLEditor; @@ -183,4 +183,4 @@ HTMLEditor.prototype = { this.hide(false); this.container.parentNode.removeChild(this.container); } -}; \ No newline at end of file +}; diff --git a/browser/devtools/markupview/markup-view.js b/browser/devtools/markupview/markup-view.js index 71a06d700817..f990eb1edc3c 100644 --- a/browser/devtools/markupview/markup-view.js +++ b/browser/devtools/markupview/markup-view.js @@ -22,7 +22,7 @@ const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); const {HTMLEditor} = require("devtools/markupview/html-editor"); const promise = require("sdk/core/promise"); const {Tooltip} = require("devtools/shared/widgets/Tooltip"); -const EventEmitter = require("devtools/shared/event-emitter"); +const EventEmitter = require("devtools/toolkit/event-emitter"); Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm"); Cu.import("resource://gre/modules/devtools/Templater.jsm"); diff --git a/browser/devtools/netmonitor/netmonitor-controller.js b/browser/devtools/netmonitor/netmonitor-controller.js index 2ea6e2d58067..d418b30ea1c6 100644 --- a/browser/devtools/netmonitor/netmonitor-controller.js +++ b/browser/devtools/netmonitor/netmonitor-controller.js @@ -106,7 +106,7 @@ Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require; const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise; -const EventEmitter = require("devtools/shared/event-emitter"); +const EventEmitter = require("devtools/toolkit/event-emitter"); const Editor = require("devtools/sourceeditor/editor"); const {Tooltip} = require("devtools/shared/widgets/Tooltip"); diff --git a/browser/devtools/netmonitor/panel.js b/browser/devtools/netmonitor/panel.js index 1efb4c409282..7f1a5c750f67 100644 --- a/browser/devtools/netmonitor/panel.js +++ b/browser/devtools/netmonitor/panel.js @@ -7,7 +7,7 @@ const { Cc, Ci, Cu, Cr } = require("chrome"); const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {}); -const EventEmitter = require("devtools/shared/event-emitter"); +const EventEmitter = require("devtools/toolkit/event-emitter"); function NetMonitorPanel(iframeWindow, toolbox) { this.panelWin = iframeWindow; diff --git a/browser/devtools/profiler/cleopatra.js b/browser/devtools/profiler/cleopatra.js index fa065255cd77..33ddeb296a80 100644 --- a/browser/devtools/profiler/cleopatra.js +++ b/browser/devtools/profiler/cleopatra.js @@ -6,7 +6,7 @@ let { Cu } = require("chrome"); let { defer } = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise; -let EventEmitter = require("devtools/shared/event-emitter"); +let EventEmitter = require("devtools/toolkit/event-emitter"); const { PROFILE_IDLE, PROFILE_COMPLETED, PROFILE_RUNNING } = require("devtools/profiler/consts"); diff --git a/browser/devtools/profiler/controller.js b/browser/devtools/profiler/controller.js index 8c23948e82c3..4588fb518c66 100644 --- a/browser/devtools/profiler/controller.js +++ b/browser/devtools/profiler/controller.js @@ -20,7 +20,7 @@ if (isJSM) { const { L10N_BUNDLE } = require("devtools/profiler/consts"); -var EventEmitter = require("devtools/shared/event-emitter"); +var EventEmitter = require("devtools/toolkit/event-emitter"); Cu.import("resource://gre/modules/devtools/dbg-client.jsm"); Cu.import("resource://gre/modules/devtools/Console.jsm"); diff --git a/browser/devtools/profiler/panel.js b/browser/devtools/profiler/panel.js index 498447c8f6c9..5eb434e3710b 100644 --- a/browser/devtools/profiler/panel.js +++ b/browser/devtools/profiler/panel.js @@ -16,7 +16,7 @@ const { const { TextEncoder } = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {}); -var EventEmitter = require("devtools/shared/event-emitter"); +var EventEmitter = require("devtools/toolkit/event-emitter"); var Cleopatra = require("devtools/profiler/cleopatra"); var Sidebar = require("devtools/profiler/sidebar"); var ProfilerController = require("devtools/profiler/controller"); diff --git a/browser/devtools/profiler/sidebar.js b/browser/devtools/profiler/sidebar.js index 1da216695f1b..f8149eb3ca2d 100644 --- a/browser/devtools/profiler/sidebar.js +++ b/browser/devtools/profiler/sidebar.js @@ -5,7 +5,7 @@ "use strict"; let { Cu } = require("chrome"); -let EventEmitter = require("devtools/shared/event-emitter"); +let EventEmitter = require("devtools/toolkit/event-emitter"); Cu.import("resource:///modules/devtools/SideMenuWidget.jsm"); Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); diff --git a/browser/devtools/responsivedesign/responsivedesign.jsm b/browser/devtools/responsivedesign/responsivedesign.jsm index a8c6953599c5..9a17aa4bd5cb 100644 --- a/browser/devtools/responsivedesign/responsivedesign.jsm +++ b/browser/devtools/responsivedesign/responsivedesign.jsm @@ -11,7 +11,7 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource:///modules/devtools/gDevTools.jsm"); Cu.import("resource:///modules/devtools/FloatingScrollbars.jsm"); -Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); var require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require; let Telemetry = require("devtools/shared/telemetry"); diff --git a/browser/devtools/scratchpad/scratchpad-panel.js b/browser/devtools/scratchpad/scratchpad-panel.js index 293f02307cd8..cb4b7d695652 100644 --- a/browser/devtools/scratchpad/scratchpad-panel.js +++ b/browser/devtools/scratchpad/scratchpad-panel.js @@ -6,7 +6,7 @@ "use strict"; const {Cu} = require("chrome"); -const EventEmitter = require("devtools/shared/event-emitter"); +const EventEmitter = require("devtools/toolkit/event-emitter"); const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); diff --git a/browser/devtools/shadereditor/panel.js b/browser/devtools/shadereditor/panel.js index 1a516b105d8a..1238c9b63414 100644 --- a/browser/devtools/shadereditor/panel.js +++ b/browser/devtools/shadereditor/panel.js @@ -7,7 +7,7 @@ const { Cc, Ci, Cu, Cr } = require("chrome"); const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise; -const EventEmitter = require("devtools/shared/event-emitter"); +const EventEmitter = require("devtools/toolkit/event-emitter"); const { WebGLFront } = require("devtools/server/actors/webgl"); function ShaderEditorPanel(iframeWindow, toolbox) { diff --git a/browser/devtools/shadereditor/shadereditor.js b/browser/devtools/shadereditor/shadereditor.js index 97de3fea325a..6820e0745743 100644 --- a/browser/devtools/shadereditor/shadereditor.js +++ b/browser/devtools/shadereditor/shadereditor.js @@ -14,7 +14,7 @@ Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require; const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise; -const EventEmitter = require("devtools/shared/event-emitter"); +const EventEmitter = require("devtools/toolkit/event-emitter"); const {Tooltip} = require("devtools/shared/widgets/Tooltip"); const Editor = require("devtools/sourceeditor/editor"); diff --git a/browser/devtools/shared/DeveloperToolbar.jsm b/browser/devtools/shared/DeveloperToolbar.jsm index ea853cb55e87..0047e927aa45 100644 --- a/browser/devtools/shared/DeveloperToolbar.jsm +++ b/browser/devtools/shared/DeveloperToolbar.jsm @@ -35,7 +35,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "require", "resource://gre/modules/devtools/Require.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter", - "resource:///modules/devtools/shared/event-emitter.js"); + "resource://gre/modules/devtools/event-emitter.js"); XPCOMUtils.defineLazyGetter(this, "prefBranch", function() { let prefService = Cc["@mozilla.org/preferences-service;1"] diff --git a/browser/devtools/shared/inplace-editor.js b/browser/devtools/shared/inplace-editor.js index ffbe1a1668d7..958ab1382fc8 100644 --- a/browser/devtools/shared/inplace-editor.js +++ b/browser/devtools/shared/inplace-editor.js @@ -40,7 +40,7 @@ const FOCUS_BACKWARD = Ci.nsIFocusManager.MOVEFOCUS_BACKWARD; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); /** * Mark a span editable. |editableField| will listen for the span to diff --git a/browser/devtools/shared/observable-object.js b/browser/devtools/shared/observable-object.js index fffcf32681b1..c18d668a9361 100644 --- a/browser/devtools/shared/observable-object.js +++ b/browser/devtools/shared/observable-object.js @@ -30,7 +30,7 @@ "use strict"; -const EventEmitter = require("devtools/shared/event-emitter"); +const EventEmitter = require("devtools/toolkit/event-emitter"); function ObservableObject(object = {}) { EventEmitter.decorate(this); diff --git a/browser/devtools/shared/test/browser.ini b/browser/devtools/shared/test/browser.ini index 36a3a3b87910..1b0632330ffc 100644 --- a/browser/devtools/shared/test/browser.ini +++ b/browser/devtools/shared/test/browser.ini @@ -9,7 +9,6 @@ support-files = leakhunt.js [browser_css_color.js] -[browser_eventemitter_basic.js] [browser_layoutHelpers.js] [browser_observableobject.js] [browser_outputparser.js] diff --git a/browser/devtools/shared/test/browser_eventemitter_basic.js b/browser/devtools/shared/test/browser_eventemitter_basic.js deleted file mode 100644 index 70995a6b895b..000000000000 --- a/browser/devtools/shared/test/browser_eventemitter_basic.js +++ /dev/null @@ -1,154 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -const promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise; -const EventEmitter = Cu.import("resource:///modules/devtools/shared/event-emitter.js", {}).EventEmitter; - -function test() { - waitForExplicitFinish(); - - testEmitter(); - testEmitter({}); - - Task.spawn(testPromise).then(null, ok.bind(null, false)).then(finish); -} - -function testEmitter(aObject) { - let emitter; - - if (aObject) { - emitter = aObject; - EventEmitter.decorate(emitter); - } else { - emitter = new EventEmitter(); - } - - ok(emitter, "We have an event emitter"); - - emitter.on("next", next); - emitter.emit("next", "abc", "def"); - - let beenHere1 = false; - function next(eventName, str1, str2) { - is(eventName, "next", "Got event"); - is(str1, "abc", "Argument 1 is correct"); - is(str2, "def", "Argument 2 is correct"); - - ok(!beenHere1, "first time in next callback"); - beenHere1 = true; - - emitter.off("next", next); - - emitter.emit("next"); - - emitter.once("onlyonce", onlyOnce); - - emitter.emit("onlyonce"); - emitter.emit("onlyonce"); - } - - let beenHere2 = false; - function onlyOnce() { - ok(!beenHere2, "\"once\" listner has been called once"); - beenHere2 = true; - emitter.emit("onlyonce"); - - killItWhileEmitting(); - } - - function killItWhileEmitting() { - function c1() { - ok(true, "c1 called"); - } - function c2() { - ok(true, "c2 called"); - emitter.off("tick", c3); - } - function c3() { - ok(false, "c3 should not be called"); - } - function c4() { - ok(true, "c4 called"); - } - - emitter.on("tick", c1); - emitter.on("tick", c2); - emitter.on("tick", c3); - emitter.on("tick", c4); - - emitter.emit("tick"); - - offAfterOnce(); - } - - function offAfterOnce() { - let enteredC1 = false; - - function c1() { - enteredC1 = true; - } - - emitter.once("oao", c1); - emitter.off("oao", c1); - - emitter.emit("oao"); - - ok(!enteredC1, "c1 should not be called"); - } -} - -function testPromise() { - let emitter = new EventEmitter(); - let p = emitter.once("thing"); - - // Check that the promise is only resolved once event though we - // emit("thing") more than once - let firstCallbackCalled = false; - let check1 = p.then(arg => { - is(firstCallbackCalled, false, "first callback called only once"); - firstCallbackCalled = true; - is(arg, "happened", "correct arg in promise"); - return "rval from c1"; - }); - - emitter.emit("thing", "happened", "ignored"); - - // Check that the promise is resolved asynchronously - let secondCallbackCalled = false; - let check2 = p.then(arg => { - ok(true, "second callback called"); - is(arg, "happened", "correct arg in promise"); - secondCallbackCalled = true; - is(arg, "happened", "correct arg in promise (a second time)"); - return "rval from c2"; - }); - - // Shouldn't call any of the above listeners - emitter.emit("thing", "trashinate"); - - // Check that we can still separate events with different names - // and that it works with no parameters - let pfoo = emitter.once("foo"); - let pbar = emitter.once("bar"); - - let check3 = pfoo.then(arg => { - ok(arg === undefined, "no arg for foo event"); - return "rval from c3"; - }); - - pbar.then(() => { - ok(false, "pbar should not be called"); - }); - - emitter.emit("foo"); - - is(secondCallbackCalled, false, "second callback not called yet"); - - return promise.all([ check1, check2, check3 ]).then(args => { - is(args[0], "rval from c1", "callback 1 done good"); - is(args[1], "rval from c2", "callback 2 done good"); - is(args[2], "rval from c3", "callback 3 done good"); - }); -} diff --git a/browser/devtools/shared/widgets/BreadcrumbsWidget.jsm b/browser/devtools/shared/widgets/BreadcrumbsWidget.jsm index 578494492459..3a6f56c2fefb 100644 --- a/browser/devtools/shared/widgets/BreadcrumbsWidget.jsm +++ b/browser/devtools/shared/widgets/BreadcrumbsWidget.jsm @@ -11,7 +11,7 @@ const Cu = Components.utils; const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); -Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); this.EXPORTED_SYMBOLS = ["BreadcrumbsWidget"]; diff --git a/browser/devtools/shared/widgets/Chart.jsm b/browser/devtools/shared/widgets/Chart.jsm index 3dd0898ee6a6..96ec3affbf67 100644 --- a/browser/devtools/shared/widgets/Chart.jsm +++ b/browser/devtools/shared/widgets/Chart.jsm @@ -20,7 +20,7 @@ const HOVERED_SLICE_TRANSLATE_DISTANCE_RATIO = 20; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); -Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); this.EXPORTED_SYMBOLS = ["Chart"]; diff --git a/browser/devtools/shared/widgets/FastListWidget.js b/browser/devtools/shared/widgets/FastListWidget.js index cfed3dc8c360..ff1cce87e977 100644 --- a/browser/devtools/shared/widgets/FastListWidget.js +++ b/browser/devtools/shared/widgets/FastListWidget.js @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; -const EventEmitter = require("devtools/shared/event-emitter"); +const EventEmitter = require("devtools/toolkit/event-emitter"); const { Cu, Ci } = require("chrome"); const { ViewHelpers } = Cu.import("resource:///modules/devtools/ViewHelpers.jsm", {}); diff --git a/browser/devtools/shared/widgets/SideMenuWidget.jsm b/browser/devtools/shared/widgets/SideMenuWidget.jsm index 3b87006e190a..3a082411b3ca 100644 --- a/browser/devtools/shared/widgets/SideMenuWidget.jsm +++ b/browser/devtools/shared/widgets/SideMenuWidget.jsm @@ -9,7 +9,7 @@ const Ci = Components.interfaces; const Cu = Components.utils; Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); -Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); this.EXPORTED_SYMBOLS = ["SideMenuWidget"]; diff --git a/browser/devtools/shared/widgets/Spectrum.js b/browser/devtools/shared/widgets/Spectrum.js index fe861f31eaea..e2c4de5cda9c 100644 --- a/browser/devtools/shared/widgets/Spectrum.js +++ b/browser/devtools/shared/widgets/Spectrum.js @@ -4,7 +4,7 @@ "use strict"; -const EventEmitter = require("devtools/shared/event-emitter"); +const EventEmitter = require("devtools/toolkit/event-emitter"); /** * Spectrum creates a color picker widget in any container you give it. diff --git a/browser/devtools/shared/widgets/Tooltip.js b/browser/devtools/shared/widgets/Tooltip.js index da193781bf40..2002aa3c37f7 100644 --- a/browser/devtools/shared/widgets/Tooltip.js +++ b/browser/devtools/shared/widgets/Tooltip.js @@ -9,7 +9,7 @@ const promise = require("sdk/core/promise"); const IOService = Cc["@mozilla.org/network/io-service;1"] .getService(Ci.nsIIOService); const {Spectrum} = require("devtools/shared/widgets/Spectrum"); -const EventEmitter = require("devtools/shared/event-emitter"); +const EventEmitter = require("devtools/toolkit/event-emitter"); const {colorUtils} = require("devtools/css-color"); const Heritage = require("sdk/core/heritage"); const {CSSTransformPreviewer} = require("devtools/shared/widgets/CSSTransformPreviewer"); diff --git a/browser/devtools/shared/widgets/VariablesView.jsm b/browser/devtools/shared/widgets/VariablesView.jsm index d66f1d4791bb..cd2be0efa4d1 100644 --- a/browser/devtools/shared/widgets/VariablesView.jsm +++ b/browser/devtools/shared/widgets/VariablesView.jsm @@ -21,7 +21,7 @@ const ITEM_FLASH_DURATION = 300 // ms Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); -Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm"); Cu.import("resource://gre/modules/Task.jsm"); let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise; diff --git a/browser/devtools/sourceeditor/editor.js b/browser/devtools/sourceeditor/editor.js index 30b08aeb849d..387098923583 100644 --- a/browser/devtools/sourceeditor/editor.js +++ b/browser/devtools/sourceeditor/editor.js @@ -19,7 +19,7 @@ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.x const MAX_VERTICAL_OFFSET = 3; const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); -const events = require("devtools/shared/event-emitter"); +const events = require("devtools/toolkit/event-emitter"); Cu.import("resource://gre/modules/Services.jsm"); const L10N = Services.strings.createBundle(L10N_BUNDLE); diff --git a/browser/devtools/styleeditor/StyleEditorUI.jsm b/browser/devtools/styleeditor/StyleEditorUI.jsm index 73a2a588b439..73d2db34ea84 100644 --- a/browser/devtools/styleeditor/StyleEditorUI.jsm +++ b/browser/devtools/styleeditor/StyleEditorUI.jsm @@ -16,7 +16,7 @@ Cu.import("resource://gre/modules/PluralForm.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/osfile.jsm"); let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise; -Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); Cu.import("resource:///modules/devtools/gDevTools.jsm"); Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm"); Cu.import("resource:///modules/devtools/SplitView.jsm"); diff --git a/browser/devtools/styleeditor/StyleSheetEditor.jsm b/browser/devtools/styleeditor/StyleSheetEditor.jsm index 15bbe7ff466d..52203b46d667 100644 --- a/browser/devtools/styleeditor/StyleSheetEditor.jsm +++ b/browser/devtools/styleeditor/StyleSheetEditor.jsm @@ -21,7 +21,7 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/osfile.jsm"); -Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm"); const LOAD_ERROR = "error-load"; diff --git a/browser/devtools/styleeditor/styleeditor-panel.js b/browser/devtools/styleeditor/styleeditor-panel.js index d2d158052673..24f64820c161 100644 --- a/browser/devtools/styleeditor/styleeditor-panel.js +++ b/browser/devtools/styleeditor/styleeditor-panel.js @@ -10,7 +10,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); let promise = require("sdk/core/promise"); -let EventEmitter = require("devtools/shared/event-emitter"); +let EventEmitter = require("devtools/toolkit/event-emitter"); Cu.import("resource:///modules/devtools/StyleEditorUI.jsm"); Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm"); diff --git a/browser/devtools/styleeditor/utils.js b/browser/devtools/styleeditor/utils.js index 7079b9bddd60..d6f991ecb920 100644 --- a/browser/devtools/styleeditor/utils.js +++ b/browser/devtools/styleeditor/utils.js @@ -7,7 +7,7 @@ const {Cc, Ci, Cu, Cr} = require("chrome"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource:///modules/devtools/shared/event-emitter.js"); +Cu.import("resource://gre/modules/devtools/event-emitter.js"); exports.PREF_ORIG_SOURCES = "devtools.styleeditor.source-maps-enabled"; diff --git a/browser/devtools/styleinspector/computed-view.js b/browser/devtools/styleinspector/computed-view.js index f5f0a7018231..14cfe5f9806d 100644 --- a/browser/devtools/styleinspector/computed-view.js +++ b/browser/devtools/styleinspector/computed-view.js @@ -10,7 +10,7 @@ const ToolDefinitions = require("main").Tools; const {CssLogic} = require("devtools/styleinspector/css-logic"); const {ELEMENT_STYLE} = require("devtools/server/actors/styles"); const promise = require("sdk/core/promise"); -const {EventEmitter} = require("devtools/shared/event-emitter"); +const {EventEmitter} = require("devtools/toolkit/event-emitter"); const {OutputParser} = require("devtools/output-parser"); const {Tooltip} = require("devtools/shared/widgets/Tooltip"); const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/styleeditor/utils"); diff --git a/browser/devtools/tilt/tilt.js b/browser/devtools/tilt/tilt.js index bd41f0432381..2e243e024dac 100644 --- a/browser/devtools/tilt/tilt.js +++ b/browser/devtools/tilt/tilt.js @@ -10,7 +10,7 @@ const {Cu} = require("chrome"); let {TiltVisualizer} = require("devtools/tilt/tilt-visualizer"); let TiltGL = require("devtools/tilt/tilt-gl"); let TiltUtils = require("devtools/tilt/tilt-utils"); -let EventEmitter = require("devtools/shared/event-emitter"); +let EventEmitter = require("devtools/toolkit/event-emitter"); let Telemetry = require("devtools/shared/telemetry"); Cu.import("resource://gre/modules/Services.jsm"); diff --git a/browser/devtools/webconsole/panel.js b/browser/devtools/webconsole/panel.js index d8b5c866bc8d..37a12501c411 100644 --- a/browser/devtools/webconsole/panel.js +++ b/browser/devtools/webconsole/panel.js @@ -9,7 +9,7 @@ const {Cc, Ci, Cu} = require("chrome"); loader.lazyImporter(this, "devtools", "resource://gre/modules/devtools/Loader.jsm"); loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise"); loader.lazyGetter(this, "HUDService", () => require("devtools/webconsole/hudservice")); -loader.lazyGetter(this, "EventEmitter", () => require("devtools/shared/event-emitter")); +loader.lazyGetter(this, "EventEmitter", () => require("devtools/toolkit/event-emitter")); loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm"); /** diff --git a/browser/devtools/webconsole/test/browser.ini b/browser/devtools/webconsole/test/browser.ini index cd13b63dbf75..d68628e76663 100644 --- a/browser/devtools/webconsole/test/browser.ini +++ b/browser/devtools/webconsole/test/browser.ini @@ -268,5 +268,6 @@ run-if = os == "mac" [browser_webconsole_output_04.js] [browser_webconsole_output_events.js] [browser_console_variables_view_highlighter.js] +[browser_webconsole_start_netmon_first.js] [browser_webconsole_console_trace_duplicates.js] [browser_webconsole_cd_iframe.js] diff --git a/browser/devtools/webconsole/test/browser_webconsole_start_netmon_first.js b/browser/devtools/webconsole/test/browser_webconsole_start_netmon_first.js new file mode 100644 index 000000000000..b648da021c74 --- /dev/null +++ b/browser/devtools/webconsole/test/browser_webconsole_start_netmon_first.js @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Check that the webconsole works if the network monitor is first opened, then +// the user switches to the webconsole. See bug 970914. + +function test() { + Task.spawn(runner).then(finishTest); + + function* runner() { + const {tab} = yield loadTab("data:text/html;charset=utf8,

hello"); + + const target = TargetFactory.forTab(tab); + const toolbox = yield gDevTools.showToolbox(target, "netmonitor"); + + const hud = yield openConsole(tab); + + hud.jsterm.execute("console.log('foobar bug970914')"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "console.log", + text: "foobar bug970914", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + let text = hud.outputNode.textContent; + isnot(text.indexOf("foobar bug970914"), -1, "console.log message confirmed"); + ok(!/logging API|disabled by a script/i.test(text), + "no warning about disabled console API"); + } +} + diff --git a/browser/devtools/webconsole/webconsole.js b/browser/devtools/webconsole/webconsole.js index c44f9b0d9a53..a02052501d53 100644 --- a/browser/devtools/webconsole/webconsole.js +++ b/browser/devtools/webconsole/webconsole.js @@ -15,7 +15,7 @@ loader.lazyServiceGetter(this, "clipboardHelper", "nsIClipboardHelper"); loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm"); loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise"); -loader.lazyGetter(this, "EventEmitter", () => require("devtools/shared/event-emitter")); +loader.lazyGetter(this, "EventEmitter", () => require("devtools/toolkit/event-emitter")); loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/autocomplete-popup").AutocompletePopup); loader.lazyGetter(this, "ToolSidebar", diff --git a/browser/extensions/pdfjs/README.mozilla b/browser/extensions/pdfjs/README.mozilla index d798ae852ad6..58c5fb59061b 100644 --- a/browser/extensions/pdfjs/README.mozilla +++ b/browser/extensions/pdfjs/README.mozilla @@ -1,4 +1,4 @@ This is the pdf.js project output, https://github.com/mozilla/pdf.js -Current extension version is: 0.8.990 +Current extension version is: 0.8.1041 diff --git a/browser/extensions/pdfjs/components/PdfStreamConverter.js b/browser/extensions/pdfjs/components/PdfStreamConverter.js index cc3b9aaf4a39..fa113c0a682d 100644 --- a/browser/extensions/pdfjs/components/PdfStreamConverter.js +++ b/browser/extensions/pdfjs/components/PdfStreamConverter.js @@ -541,10 +541,23 @@ var RangedChromeActions = (function RangedChromeActionsClosure() { }; originalRequest.visitRequestHeaders(httpHeaderVisitor); + var self = this; + var xhr_onreadystatechange = function xhr_onreadystatechange() { + if (this.readyState === 1) { // LOADING + var netChannel = this.channel; + if ('nsIPrivateBrowsingChannel' in Ci && + netChannel instanceof Ci.nsIPrivateBrowsingChannel) { + var docIsPrivate = self.isInPrivateBrowsing(); + netChannel.setPrivate(docIsPrivate); + } + } + }; var getXhr = function getXhr() { const XMLHttpRequest = Components.Constructor( '@mozilla.org/xmlextras/xmlhttprequest;1'); - return new XMLHttpRequest(); + var xhr = new XMLHttpRequest(); + xhr.addEventListener('readystatechange', xhr_onreadystatechange); + return xhr; }; this.networkManager = new NetworkManager(this.pdfUrl, { @@ -552,7 +565,6 @@ var RangedChromeActions = (function RangedChromeActionsClosure() { getXhr: getXhr }); - var self = this; // If we are in range request mode, this means we manually issued xhr // requests, which we need to abort when we leave the page domWindow.addEventListener('unload', function unload(e) { diff --git a/browser/extensions/pdfjs/content/build/pdf.js b/browser/extensions/pdfjs/content/build/pdf.js index 0c54a101062f..ad6fb6f12c46 100644 --- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -20,8 +20,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '0.8.990'; -PDFJS.build = '54f6291'; +PDFJS.version = '0.8.1041'; +PDFJS.build = '2188bcb'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -3309,7 +3309,7 @@ var LinkAnnotation = (function LinkAnnotationClosure() { if (isName(url)) { // Some bad PDFs do not put parentheses around relative URLs. url = '/' + url.name; - } else { + } else if (url) { url = addDefaultProtocolToUrl(url); } // TODO: pdf spec mentions urls can be relative to a Base @@ -3349,7 +3349,7 @@ var LinkAnnotation = (function LinkAnnotationClosure() { // Lets URLs beginning with 'www.' default to using the 'http://' protocol. function addDefaultProtocolToUrl(url) { - if (url.indexOf('www.') === 0) { + if (url && url.indexOf('www.') === 0) { return ('http://' + url); } return url; @@ -4086,6 +4086,7 @@ var WorkerTransport = (function WorkerTransportClosure() { messageHandler.on('GetDoc', function transportDoc(data) { var pdfInfo = data.pdfInfo; + this.numPages = data.pdfInfo.numPages; var pdfDocument = new PDFDocumentProxy(pdfInfo, this); this.pdfDocument = pdfDocument; this.workerReadyPromise.resolve(pdfDocument); @@ -4290,6 +4291,13 @@ var WorkerTransport = (function WorkerTransportClosure() { }, getPage: function WorkerTransport_getPage(pageNumber, promise) { + if (pageNumber <= 0 || pageNumber > this.numPages || + (pageNumber|0) !== pageNumber) { + var pagePromise = new PDFJS.LegacyPromise(); + pagePromise.reject(new Error('Invalid page request')); + return pagePromise; + } + var pageIndex = pageNumber - 1; if (pageIndex in this.pagePromises) return this.pagePromises[pageIndex]; diff --git a/browser/extensions/pdfjs/content/build/pdf.worker.js b/browser/extensions/pdfjs/content/build/pdf.worker.js index e031a7362043..5246bc266118 100644 --- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -20,8 +20,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '0.8.990'; -PDFJS.build = '54f6291'; +PDFJS.version = '0.8.1041'; +PDFJS.build = '2188bcb'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -3309,7 +3309,7 @@ var LinkAnnotation = (function LinkAnnotationClosure() { if (isName(url)) { // Some bad PDFs do not put parentheses around relative URLs. url = '/' + url.name; - } else { + } else if (url) { url = addDefaultProtocolToUrl(url); } // TODO: pdf spec mentions urls can be relative to a Base @@ -3349,7 +3349,7 @@ var LinkAnnotation = (function LinkAnnotationClosure() { // Lets URLs beginning with 'www.' default to using the 'http://' protocol. function addDefaultProtocolToUrl(url) { - if (url.indexOf('www.') === 0) { + if (url && url.indexOf('www.') === 0) { return ('http://' + url); } return url; @@ -3877,6 +3877,9 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { +// The maximum number of bytes fetched per range request +var RANGE_CHUNK_SIZE = 65536; + // TODO(mack): Make use of PDFJS.Util.inherit() when it becomes available var BasePdfManager = (function BasePdfManagerClosure() { function BasePdfManager() { @@ -3990,9 +3993,6 @@ var LocalPdfManager = (function LocalPdfManagerClosure() { })(); var NetworkPdfManager = (function NetworkPdfManagerClosure() { - - var CHUNK_SIZE = 65536; - function NetworkPdfManager(args, msgHandler) { this.msgHandler = msgHandler; @@ -4005,7 +4005,7 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() { disableAutoFetch: args.disableAutoFetch, initialData: args.initialData }; - this.streamManager = new ChunkedStreamManager(args.length, CHUNK_SIZE, + this.streamManager = new ChunkedStreamManager(args.length, RANGE_CHUNK_SIZE, args.url, params); this.pdfModel = new PDFDocument(this, this.streamManager.getStream(), @@ -31953,47 +31953,85 @@ var Lexer = (function LexerClosure() { nextChar: function Lexer_nextChar() { return (this.currentChar = this.stream.getByte()); }, + peekChar: function Lexer_peekChar() { + return this.stream.peekBytes(1)[0]; + }, getNumber: function Lexer_getNumber() { - var floating = false; var ch = this.currentChar; - var allDigits = ch >= 0x30 && ch <= 0x39; - var strBuf = this.strBuf; - strBuf.length = 0; - strBuf.push(String.fromCharCode(ch)); + var eNotation = false; + var divideBy = 0; // different from 0 if it's a floating point value + + var sign = 1; + + + if (ch === 0x2D) { // '-' + sign = -1; + ch = this.nextChar(); + } else if (ch === 0x2B) { // '+' + ch = this.nextChar(); + } + if (ch === 0x2E) { // '.' + divideBy = 10; + ch = this.nextChar(); + } + + if (ch < 0x30 || ch > 0x39) { // '0' - '9' + error('Invalid number: ' + String.fromCharCode(ch)); + return 0; + } + + var baseValue = ch - 0x30; // '0' + var powerValue = 0; + var powerValueSign = 1; + while ((ch = this.nextChar()) >= 0) { - if (ch >= 0x30 && ch <= 0x39) { // '0'-'9' - strBuf.push(String.fromCharCode(ch)); - } else if (ch === 0x2E && !floating) { // '.' - strBuf.push('.'); - floating = true; - allDigits = false; + if (0x30 <= ch && ch <= 0x39) { // '0' - '9' + var currentDigit = ch - 0x30; // '0' + if (eNotation) { // We are after an 'e' or 'E' + powerValue = powerValue * 10 + currentDigit; + } else { + if (divideBy !== 0) { // We are after a point + divideBy *= 10; + } + baseValue = baseValue * 10 + currentDigit; + } + } else if (ch === 0x2E) { // '.' + if (divideBy === 0) { + divideBy = 1; + } else { + // A number can have only one '.' + break; + } } else if (ch === 0x2D) { // '-' // ignore minus signs in the middle of numbers to match // Adobe's behavior warn('Badly formated number'); - allDigits = false; } else if (ch === 0x45 || ch === 0x65) { // 'E', 'e' - floating = true; - allDigits = false; + // 'E' can be either a scientific notation or the beginning of a new + // operator + var hasE = true; + ch = this.peekChar(); + if (ch === 0x2B || ch === 0x2D) { // '+', '-' + powerValueSign = (ch === 0x2D) ? -1 : 1; + this.nextChar(); // Consume the sign character + } else if (ch < 0x30 || ch > 0x39) { // '0' - '9' + // The 'E' must be the beginning of a new operator + break; + } + eNotation = true; } else { // the last character doesn't belong to us break; } } - var value; - if (allDigits) { - value = 0; - var charCodeOfZero = 48; // '0' - for (var i = 0, ii = strBuf.length; i < ii; i++) { - value = value * 10 + (strBuf[i].charCodeAt(0) - charCodeOfZero); - } - } else { - value = parseFloat(strBuf.join('')); - if (isNaN(value)) { - error('Invalid floating point number: ' + value); - } + + if (divideBy !== 0) { + baseValue /= divideBy; } - return value; + if (eNotation) { + baseValue *= Math.pow(10, powerValueSign * powerValue); + } + return sign * baseValue; }, getString: function Lexer_getString() { var numParen = 1; @@ -34948,6 +34986,13 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { if (!isInt(length)) { return; } + source.length = length; + if (length <= 2 * RANGE_CHUNK_SIZE) { + // The file size is smaller than the size of two chunks, so it does + // not make any sense to abort the request and retry with a range + // request. + return; + } // NOTE: by cancelling the full request, and then issuing range // requests, there will be an issue for sites where you can only @@ -34955,7 +35000,6 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { // server should not be returning that it can support range requests. networkManager.abortRequest(fullRequestXhrId); - source.length = length; try { pdfManager = new NetworkPdfManager(source, handler); pdfManagerPromise.resolve(pdfManager); @@ -35159,8 +35203,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { // Pre compile the pdf page and fetch the fonts/images. page.getOperatorList(handler).then(function(operatorList) { - info('page=%d - getOperatorList: time=%dms, len=%d', pageNum, - Date.now() - start, operatorList.fnArray.length); + info('page=' + pageNum + ' - getOperatorList: time=' + + (Date.now() - start) + 'ms, len=' + operatorList.fnArray.length); }, function(e) { @@ -35201,8 +35245,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { var start = Date.now(); page.extractTextContent().then(function(textContent) { deferred.resolve(textContent); - info('text indexing: page=%d - time=%dms', pageNum, - Date.now() - start); + info('text indexing: page=' + pageNum + ' - time=' + + (Date.now() - start) + 'ms'); }, function (e) { // Skip errored pages deferred.reject(e); @@ -35224,8 +35268,43 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { var consoleTimer = {}; +var workerConsole = { + log: function log() { + var args = Array.prototype.slice.call(arguments); + globalScope.postMessage({ + action: 'console_log', + data: args + }); + }, + + error: function error() { + var args = Array.prototype.slice.call(arguments); + globalScope.postMessage({ + action: 'console_error', + data: args + }); + throw 'pdf.js execution error'; + }, + + time: function time(name) { + consoleTimer[name] = Date.now(); + }, + + timeEnd: function timeEnd(name) { + var time = consoleTimer[name]; + if (!time) { + error('Unknown timer name ' + name); + } + this.log('Timer:', name, Date.now() - time); + } +}; + + // Worker thread? if (typeof window === 'undefined') { + if (!('console' in globalScope)) { + globalScope.console = workerConsole; + } // Listen for unsupported features so we can pass them on to the main thread. PDFJS.UnsupportedManager.listen(function (msg) { @@ -36914,18 +36993,18 @@ var JpxImage = (function JpxImageClosure() { } return ll; }; - Transform.prototype.expand = function expand(buffer, bufferPadding, step) { + Transform.prototype.extend = function extend(buffer, offset, size) { // Section F.3.7 extending... using max extension of 4 - var i1 = bufferPadding - 1, j1 = bufferPadding + 1; - var i2 = bufferPadding + step - 2, j2 = bufferPadding + step; - buffer[i1--] = buffer[j1++]; - buffer[j2++] = buffer[i2--]; + var i1 = offset - 1, j1 = offset + 1; + var i2 = offset + size - 2, j2 = offset + size; buffer[i1--] = buffer[j1++]; buffer[j2++] = buffer[i2--]; buffer[i1--] = buffer[j1++]; buffer[j2++] = buffer[i2--]; buffer[i1--] = buffer[j1++]; buffer[j2++] = buffer[i2--]; + buffer[i1] = buffer[j1]; + buffer[j2] = buffer[i2]; }; Transform.prototype.iterate = function Transform_iterate(ll, hl, lh, hh, u0, v0) { @@ -36938,32 +37017,35 @@ var JpxImage = (function JpxImageClosure() { var width = llWidth + hlWidth; var height = llHeight + lhHeight; var items = new Float32Array(width * height); - for (var i = 0, ii = llHeight; i < ii; i++) { + var i, j, k, l; + + for (i = 0; i < llHeight; i++) { var k = i * llWidth, l = i * 2 * width; - for (var j = 0, jj = llWidth; j < jj; j++, k++, l += 2) + for (var j = 0; j < llWidth; j++, k++, l += 2) { items[l] = llItems[k]; + } } - for (var i = 0, ii = hlHeight; i < ii; i++) { - var k = i * hlWidth, l = i * 2 * width + 1; - for (var j = 0, jj = hlWidth; j < jj; j++, k++, l += 2) + for (i = 0; i < hlHeight; i++) { + k = i * hlWidth, l = i * 2 * width + 1; + for (j = 0; j < hlWidth; j++, k++, l += 2) { items[l] = hlItems[k]; + } } - for (var i = 0, ii = lhHeight; i < ii; i++) { - var k = i * lhWidth, l = (i * 2 + 1) * width; - for (var j = 0, jj = lhWidth; j < jj; j++, k++, l += 2) + for (i = 0; i < lhHeight; i++) { + k = i * lhWidth, l = (i * 2 + 1) * width; + for (j = 0; j < lhWidth; j++, k++, l += 2) { items[l] = lhItems[k]; + } } - for (var i = 0, ii = hhHeight; i < ii; i++) { - var k = i * hhWidth, l = (i * 2 + 1) * width + 1; - for (var j = 0, jj = hhWidth; j < jj; j++, k++, l += 2) + for (i = 0; i < hhHeight; i++) { + k = i * hhWidth, l = (i * 2 + 1) * width + 1; + for (j = 0; j < hhWidth; j++, k++, l += 2) { items[l] = hhItems[k]; + } } var bufferPadding = 4; - var bufferLength = new Float32Array(Math.max(width, height) + - 2 * bufferPadding); - var buffer = new Float32Array(bufferLength); - var bufferOut = new Float32Array(bufferLength); + var rowBuffer = new Float32Array(width + 2 * bufferPadding); // Section F.3.4 HOR_SR for (var v = 0; v < height; v++) { @@ -36974,21 +37056,28 @@ var JpxImage = (function JpxImageClosure() { } continue; } - - var k = v * width; - var l = bufferPadding; - for (var u = 0; u < width; u++, k++, l++) - buffer[l] = items[k]; - - this.expand(buffer, bufferPadding, width); - this.filter(buffer, bufferPadding, width, u0, bufferOut); - k = v * width; - l = bufferPadding; - for (var u = 0; u < width; u++, k++, l++) - items[k] = bufferOut[l]; + rowBuffer.set(items.subarray(k, k + width), bufferPadding); + + this.extend(rowBuffer, bufferPadding, width); + this.filter(rowBuffer, bufferPadding, width, u0, rowBuffer); + + items.set(rowBuffer.subarray(bufferPadding, bufferPadding + width), k); } + // Accesses to the items array can take long, because it may not fit into + // CPU cache and has to be fetched from main memory. Since subsequent + // accesses to the items array are not local when reading columns, we + // have a cache miss every time. To reduce cache misses, get up to + // 'numBuffers' items at a time and store them into the individual + // buffers. The colBuffers should be small enough to fit into CPU cache. + var numBuffers = 16; + var colBuffers = []; + for (i = 0; i < numBuffers; i++) { + colBuffers.push(new Float32Array(height + 2 * bufferPadding)); + } + var b, currentBuffer = 0, ll = bufferPadding + height; + // Section F.3.5 VER_SR for (var u = 0; u < width; u++) { if (height == 1) { @@ -36999,19 +37088,33 @@ var JpxImage = (function JpxImageClosure() { continue; } - var k = u; - var l = bufferPadding; - for (var v = 0; v < height; v++, k += width, l++) - buffer[l] = items[k]; + // if we ran out of buffers, copy several image columns at once + if (currentBuffer === 0) { + numBuffers = Math.min(width - u, numBuffers); + for (k = u, l = bufferPadding; l < ll; k += width, l++) { + for (b = 0; b < numBuffers; b++) { + colBuffers[b][l] = items[k + b]; + } + } + currentBuffer = numBuffers; + } - this.expand(buffer, bufferPadding, height); - this.filter(buffer, bufferPadding, height, v0, bufferOut); + currentBuffer--; + var buffer = colBuffers[currentBuffer]; + this.extend(buffer, bufferPadding, height); + this.filter(buffer, bufferPadding, height, v0, buffer); - k = u; - l = bufferPadding; - for (var v = 0; v < height; v++, k += width, l++) - items[k] = bufferOut[l]; + // If this is last buffer in this group of buffers, flush all buffers. + if (currentBuffer === 0) { + k = u - numBuffers + 1; + for (l = bufferPadding; l < ll; k += width, l++) { + for (b = 0; b < numBuffers; b++) { + items[k + b] = colBuffers[b][l]; + } + } + } } + return { width: width, height: height, @@ -39413,17 +39516,16 @@ var JpegImage = (function jpegImage() { var blocksPerLine = component.blocksPerLine; var blocksPerColumn = component.blocksPerColumn; var samplesPerLine = blocksPerLine << 3; - var R = new Int32Array(64), r = new Uint8Array(64); + var R = new Int32Array(64); // A port of poppler's IDCT method which in turn is taken from: // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, // "Practical Fast 1-D DCT Algorithms with 11 Multiplications", // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, // 988-991. - function quantizeAndInverse(zz, dataOut, dataIn) { + function quantizeAndInverse(zz, p) { var qt = component.quantizationTable; var v0, v1, v2, v3, v4, v5, v6, v7, t; - var p = dataIn; var i; // dequant @@ -39507,7 +39609,7 @@ var JpegImage = (function jpegImage() { if (p[1*8 + col] == 0 && p[2*8 + col] == 0 && p[3*8 + col] == 0 && p[4*8 + col] == 0 && p[5*8 + col] == 0 && p[6*8 + col] == 0 && p[7*8 + col] == 0) { - t = (dctSqrt2 * dataIn[i+0] + 8192) >> 14; + t = (dctSqrt2 * p[i+0] + 8192) >> 14; p[0*8 + col] = t; p[1*8 + col] = t; p[2*8 + col] = t; @@ -39570,8 +39672,7 @@ var JpegImage = (function jpegImage() { // convert to 8-bit integers for (i = 0; i < 64; ++i) { - var sample = 128 + ((p[i] + 8) >> 4); - dataOut[i] = sample < 0 ? 0 : sample > 0xFF ? 0xFF : sample; + p[i] = clampTo8bit((p[i] + 2056) >> 4); } } @@ -39581,13 +39682,13 @@ var JpegImage = (function jpegImage() { for (i = 0; i < 8; i++) lines.push(new Uint8Array(samplesPerLine)); for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) { - quantizeAndInverse(component.blocks[blockRow][blockCol], r, R); + quantizeAndInverse(component.blocks[blockRow][blockCol], R); var offset = 0, sample = blockCol << 3; for (j = 0; j < 8; j++) { var line = lines[scanLine + j]; for (i = 0; i < 8; i++) - line[sample + i] = r[offset++]; + line[sample + i] = R[offset++]; } } } @@ -39648,7 +39749,7 @@ var JpegImage = (function jpegImage() { for (var i = 0; i < blocksPerColumnForMcu; i++) { var row = []; for (var j = 0; j < blocksPerLineForMcu; j++) - row.push(new Int32Array(64)); + row.push(new Int16Array(64)); blocks.push(row); } component.blocksPerLine = blocksPerLine; @@ -39852,41 +39953,35 @@ var JpegImage = (function jpegImage() { getData: function getData(width, height) { var scaleX = this.width / width, scaleY = this.height / height; - var component1, component2, component3, component4; - var component1Line, component2Line, component3Line, component4Line; - var x, y; + var component, componentLine, componentScaleX, componentScaleY; + var x, y, i; var offset = 0; var Y, Cb, Cr, K, C, M, Ye, R, G, B; var colorTransform; - var dataLength = width * height * this.components.length; + var numComponents = this.components.length; + var dataLength = width * height * numComponents; var data = new Uint8Array(dataLength); - switch (this.components.length) { - case 1: - component1 = this.components[0]; - for (y = 0; y < height; y++) { - component1Line = component1.lines[0 | (y * component1.scaleY * scaleY)]; - for (x = 0; x < width; x++) { - Y = component1Line[0 | (x * component1.scaleX * scaleX)]; - data[offset++] = Y; - } + // First construct image data ... + for (i = 0; i < numComponents; i++) { + component = this.components[i]; + componentScaleX = component.scaleX * scaleX; + componentScaleY = component.scaleY * scaleY; + offset = i; + for (y = 0; y < height; y++) { + componentLine = component.lines[0 | (y * componentScaleY)]; + for (x = 0; x < width; x++) { + data[offset] = componentLine[0 | (x * componentScaleX)]; + offset += numComponents; } - break; - case 2: - // PDF might compress two component data in custom colorspace - component1 = this.components[0]; - component2 = this.components[1]; - for (y = 0; y < height; y++) { - component1Line = component1.lines[0 | (y * component1.scaleY * scaleY)]; - component2Line = component2.lines[0 | (y * component2.scaleY * scaleY)]; - for (x = 0; x < width; x++) { - Y = component1Line[0 | (x * component1.scaleX * scaleX)]; - data[offset++] = Y; - Y = component2Line[0 | (x * component2.scaleX * scaleX)]; - data[offset++] = Y; - } - } - break; + } + } + + // ... then transform colors, if necessary + switch (numComponents) { + case 1: case 2: break; + // no color conversion for one or two compoenents + case 3: // The default transform for three components is true colorTransform = true; @@ -39896,31 +39991,19 @@ var JpegImage = (function jpegImage() { else if (typeof this.colorTransform !== 'undefined') colorTransform = !!this.colorTransform; - component1 = this.components[0]; - component2 = this.components[1]; - component3 = this.components[2]; - for (y = 0; y < height; y++) { - component1Line = component1.lines[0 | (y * component1.scaleY * scaleY)]; - component2Line = component2.lines[0 | (y * component2.scaleY * scaleY)]; - component3Line = component3.lines[0 | (y * component3.scaleY * scaleY)]; - for (x = 0; x < width; x++) { - if (!colorTransform) { - R = component1Line[0 | (x * component1.scaleX * scaleX)]; - G = component2Line[0 | (x * component2.scaleX * scaleX)]; - B = component3Line[0 | (x * component3.scaleX * scaleX)]; - } else { - Y = component1Line[0 | (x * component1.scaleX * scaleX)]; - Cb = component2Line[0 | (x * component2.scaleX * scaleX)]; - Cr = component3Line[0 | (x * component3.scaleX * scaleX)]; + if (colorTransform) { + for (i = 0; i < dataLength; i += numComponents) { + Y = data[i ]; + Cb = data[i + 1]; + Cr = data[i + 2]; - R = clampTo8bit(Y + 1.402 * (Cr - 128)); - G = clampTo8bit(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128)); - B = clampTo8bit(Y + 1.772 * (Cb - 128)); - } + R = clampTo8bit(Y + 1.402 * (Cr - 128)); + G = clampTo8bit(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128)); + B = clampTo8bit(Y + 1.772 * (Cb - 128)); - data[offset++] = R; - data[offset++] = G; - data[offset++] = B; + data[i ] = R; + data[i + 1] = G; + data[i + 2] = B; } } break; @@ -39933,35 +40016,20 @@ var JpegImage = (function jpegImage() { else if (typeof this.colorTransform !== 'undefined') colorTransform = !!this.colorTransform; - component1 = this.components[0]; - component2 = this.components[1]; - component3 = this.components[2]; - component4 = this.components[3]; - for (y = 0; y < height; y++) { - component1Line = component1.lines[0 | (y * component1.scaleY * scaleY)]; - component2Line = component2.lines[0 | (y * component2.scaleY * scaleY)]; - component3Line = component3.lines[0 | (y * component3.scaleY * scaleY)]; - component4Line = component4.lines[0 | (y * component4.scaleY * scaleY)]; - for (x = 0; x < width; x++) { - if (!colorTransform) { - C = component1Line[0 | (x * component1.scaleX * scaleX)]; - M = component2Line[0 | (x * component2.scaleX * scaleX)]; - Ye = component3Line[0 | (x * component3.scaleX * scaleX)]; - K = component4Line[0 | (x * component4.scaleX * scaleX)]; - } else { - Y = component1Line[0 | (x * component1.scaleX * scaleX)]; - Cb = component2Line[0 | (x * component2.scaleX * scaleX)]; - Cr = component3Line[0 | (x * component3.scaleX * scaleX)]; - K = component4Line[0 | (x * component4.scaleX * scaleX)]; + if (colorTransform) { + for (i = 0; i < dataLength; i += numComponents) { + Y = data[i]; + Cb = data[i + 1]; + Cr = data[i + 2]; - C = 255 - clampTo8bit(Y + 1.402 * (Cr - 128)); - M = 255 - clampTo8bit(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128)); - Ye = 255 - clampTo8bit(Y + 1.772 * (Cb - 128)); - } - data[offset++] = C; - data[offset++] = M; - data[offset++] = Ye; - data[offset++] = K; + C = 255 - clampTo8bit(Y + 1.402 * (Cr - 128)); + M = 255 - clampTo8bit(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128)); + Ye = 255 - clampTo8bit(Y + 1.772 * (Cb - 128)); + + data[i ] = C; + data[i + 1] = M; + data[i + 2] = Ye; + // K is unchanged } } break; @@ -39972,54 +40040,49 @@ var JpegImage = (function jpegImage() { }, copyToImageData: function copyToImageData(imageData) { var width = imageData.width, height = imageData.height; + var imageDataBytes = width * height * 4; var imageDataArray = imageData.data; var data = this.getData(width, height); - var i = 0, j = 0, x, y; + var i = 0, j = 0; var Y, K, C, M, R, G, B; switch (this.components.length) { case 1: - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - Y = data[i++]; + while (j < imageDataBytes) { + Y = data[i++]; - imageDataArray[j++] = Y; - imageDataArray[j++] = Y; - imageDataArray[j++] = Y; - imageDataArray[j++] = 255; - } + imageDataArray[j++] = Y; + imageDataArray[j++] = Y; + imageDataArray[j++] = Y; + imageDataArray[j++] = 255; } break; case 3: - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - R = data[i++]; - G = data[i++]; - B = data[i++]; + while (j < imageDataBytes) { + R = data[i++]; + G = data[i++]; + B = data[i++]; - imageDataArray[j++] = R; - imageDataArray[j++] = G; - imageDataArray[j++] = B; - imageDataArray[j++] = 255; - } + imageDataArray[j++] = R; + imageDataArray[j++] = G; + imageDataArray[j++] = B; + imageDataArray[j++] = 255; } break; case 4: - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - C = data[i++]; - M = data[i++]; - Y = data[i++]; - K = data[i++]; + while (j < imageDataBytes) { + C = data[i++]; + M = data[i++]; + Y = data[i++]; + K = data[i++]; - R = 255 - clampTo8bit(C * (1 - K / 255) + K); - G = 255 - clampTo8bit(M * (1 - K / 255) + K); - B = 255 - clampTo8bit(Y * (1 - K / 255) + K); + R = 255 - clampTo8bit(C * (1 - K / 255) + K); + G = 255 - clampTo8bit(M * (1 - K / 255) + K); + B = 255 - clampTo8bit(Y * (1 - K / 255) + K); - imageDataArray[j++] = R; - imageDataArray[j++] = G; - imageDataArray[j++] = B; - imageDataArray[j++] = 255; - } + imageDataArray[j++] = R; + imageDataArray[j++] = G; + imageDataArray[j++] = B; + imageDataArray[j++] = 255; } break; default: diff --git a/browser/extensions/pdfjs/content/web/images/findbarButton-next-rtl.png b/browser/extensions/pdfjs/content/web/images/findbarButton-next-rtl.png index 4b5551e23600..bef02743fc10 100644 Binary files a/browser/extensions/pdfjs/content/web/images/findbarButton-next-rtl.png and b/browser/extensions/pdfjs/content/web/images/findbarButton-next-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/findbarButton-next-rtl@2x.png b/browser/extensions/pdfjs/content/web/images/findbarButton-next-rtl@2x.png new file mode 100644 index 000000000000..1da6dc949cd6 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/findbarButton-next-rtl@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/findbarButton-next.png b/browser/extensions/pdfjs/content/web/images/findbarButton-next.png index f681e4ee44e4..de1d0fc901c2 100644 Binary files a/browser/extensions/pdfjs/content/web/images/findbarButton-next.png and b/browser/extensions/pdfjs/content/web/images/findbarButton-next.png differ diff --git a/browser/extensions/pdfjs/content/web/images/findbarButton-next@2x.png b/browser/extensions/pdfjs/content/web/images/findbarButton-next@2x.png new file mode 100644 index 000000000000..0250307c0d10 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/findbarButton-next@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/findbarButton-previous-rtl.png b/browser/extensions/pdfjs/content/web/images/findbarButton-previous-rtl.png index f681e4ee44e4..de1d0fc901c2 100644 Binary files a/browser/extensions/pdfjs/content/web/images/findbarButton-previous-rtl.png and b/browser/extensions/pdfjs/content/web/images/findbarButton-previous-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/findbarButton-previous-rtl@2x.png b/browser/extensions/pdfjs/content/web/images/findbarButton-previous-rtl@2x.png new file mode 100644 index 000000000000..0250307c0d10 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/findbarButton-previous-rtl@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/findbarButton-previous.png b/browser/extensions/pdfjs/content/web/images/findbarButton-previous.png index 4b5551e23600..bef02743fc10 100644 Binary files a/browser/extensions/pdfjs/content/web/images/findbarButton-previous.png and b/browser/extensions/pdfjs/content/web/images/findbarButton-previous.png differ diff --git a/browser/extensions/pdfjs/content/web/images/findbarButton-previous@2x.png b/browser/extensions/pdfjs/content/web/images/findbarButton-previous@2x.png new file mode 100644 index 000000000000..1da6dc949cd6 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/findbarButton-previous@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-documentProperties.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-documentProperties.png index a9e82db331e9..40925e25ace9 100644 Binary files a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-documentProperties.png and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-documentProperties.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-documentProperties@2x.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-documentProperties@2x.png new file mode 100644 index 000000000000..adb240eaad3c Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-documentProperties@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-firstPage.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-firstPage.png index 2c1076d467cf..e68846aa5f60 100644 Binary files a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-firstPage.png and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-firstPage.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-firstPage@2x.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-firstPage@2x.png new file mode 100644 index 000000000000..3ad8af517385 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-firstPage@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-handTool.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-handTool.png index 10845c695d7b..cb85a841b18b 100644 Binary files a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-handTool.png and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-handTool.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-handTool@2x.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-handTool@2x.png new file mode 100644 index 000000000000..5c13f77ff003 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-handTool@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-lastPage.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-lastPage.png index 55a9efe42f13..be763e0c4a02 100644 Binary files a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-lastPage.png and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-lastPage.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-lastPage@2x.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-lastPage@2x.png new file mode 100644 index 000000000000..8570984f2d99 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-lastPage@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCcw.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCcw.png index d23d84a5435e..675d6da2c09c 100644 Binary files a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCcw.png and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCcw.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCcw@2x.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCcw@2x.png new file mode 100644 index 000000000000..b9e74312270a Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCcw@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCw.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCw.png index f36bba81e041..e1c7598886bd 100644 Binary files a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCw.png and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCw.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCw@2x.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCw@2x.png new file mode 100644 index 000000000000..cb257b41c537 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCw@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-bookmark.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-bookmark.png index b27050e2116e..a187be6c9ba7 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-bookmark.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-bookmark.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-bookmark@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-bookmark@2x.png new file mode 100644 index 000000000000..4efbaa6758dc Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-bookmark@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-download.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-download.png index 7755eee779e5..eaab35f09e12 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-download.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-download.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-download@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-download@2x.png new file mode 100644 index 000000000000..896face455a6 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-download@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-menuArrows.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-menuArrows.png index 63a7c320e9a9..306eb43b8686 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-menuArrows.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-menuArrows.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-menuArrows@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-menuArrows@2x.png new file mode 100644 index 000000000000..f7570bc0d30d Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-menuArrows@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-openFile.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-openFile.png index e139f3017d63..b5cf1bd06139 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-openFile.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-openFile.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-openFile@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-openFile@2x.png new file mode 100644 index 000000000000..91ab76593ead Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-openFile@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown-rtl.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown-rtl.png index 9ba3667e9498..1957f79ab95a 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown-rtl.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown-rtl@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown-rtl@2x.png new file mode 100644 index 000000000000..16ebcb8ef1e9 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown-rtl@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown.png index 272a3ca8351f..8219ecf83c6b 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown@2x.png new file mode 100644 index 000000000000..758c01d8364c Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp-rtl.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp-rtl.png index da02bc69f7aa..98e7ce481c16 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp-rtl.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp-rtl@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp-rtl@2x.png new file mode 100644 index 000000000000..a01b02380b90 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp-rtl@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp.png index a4d6c8ea7512..fb9daa337656 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp@2x.png new file mode 100644 index 000000000000..a5cfd755b0b4 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-presentationMode.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-presentationMode.png index 2bd1c1dfe843..3ac21244dff2 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-presentationMode.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-presentationMode.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-presentationMode@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-presentationMode@2x.png new file mode 100644 index 000000000000..cada9e7918da Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-presentationMode@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-print.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-print.png index ccc62089faef..51275e54bee6 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-print.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-print.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-print@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-print@2x.png new file mode 100644 index 000000000000..53d18daf7825 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-print@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-search.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-search.png index e52370e68dca..f9b75579b1e0 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-search.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-search.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-search@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-search@2x.png new file mode 100644 index 000000000000..456b133248fd Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-search@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle-rtl.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle-rtl.png index 145316b8345d..8437095273c4 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle-rtl.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle-rtl@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle-rtl@2x.png new file mode 100644 index 000000000000..9d9bfa4f63d6 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle-rtl@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle.png index 02ed6c62ad8a..1f90f83da745 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle@2x.png new file mode 100644 index 000000000000..b066fe5cb0ef Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle-rtl.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle-rtl.png index c308668b12c8..6f85ec061ea1 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle-rtl.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle-rtl@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle-rtl@2x.png new file mode 100644 index 000000000000..291e006797fc Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle-rtl@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle.png index be9eceba944b..025dc9040e50 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle@2x.png new file mode 100644 index 000000000000..7f834df94000 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline-rtl.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline-rtl.png index 21ceb3b165cc..aaa9430211e1 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline-rtl.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline-rtl@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline-rtl@2x.png new file mode 100644 index 000000000000..3410f70dfa65 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline-rtl@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline.png index 9b2e2f5be21b..976365a50612 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline@2x.png new file mode 100644 index 000000000000..b6a197fdf33d Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewThumbnail.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewThumbnail.png index d71dd27711f6..584ba55881f9 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewThumbnail.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewThumbnail.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewThumbnail@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewThumbnail@2x.png new file mode 100644 index 000000000000..fb7db9383669 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewThumbnail@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomIn.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomIn.png index 670acd93f532..513d081bc2d2 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomIn.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomIn.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomIn@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomIn@2x.png new file mode 100644 index 000000000000..d5d49d5ff107 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomIn@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomOut.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomOut.png index 7991ee7a5070..156c26b941c1 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomOut.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomOut.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomOut@2x.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomOut@2x.png new file mode 100644 index 000000000000..959e1919d5a7 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomOut@2x.png differ diff --git a/browser/extensions/pdfjs/content/web/viewer.css b/browser/extensions/pdfjs/content/web/viewer.css index 88e3783b214a..145fec19f3ec 100644 --- a/browser/extensions/pdfjs/content/web/viewer.css +++ b/browser/extensions/pdfjs/content/web/viewer.css @@ -743,7 +743,6 @@ html[dir='rtl'] #findNext { .toolbarButton::before, .secondaryToolbarButton::before { /* All matching images have a size of 16x16 - * (except for the print button: 18x16) * All relevant containers have a size of 32x25 */ position: absolute; display: inline-block; @@ -816,14 +815,6 @@ html[dir='rtl'] .toolbarButton.pageDown::before { .toolbarButton.print::before, .secondaryToolbarButton.print::before { content: url(images/toolbarButton-print.png); - left: 6px; -} - -html[dir="ltr"] .secondaryToolbarButton.print::before { - left: 3px; -} -html[dir="rtl"] .secondaryToolbarButton.print::before { - right: 3px; } .toolbarButton.openFile::before, @@ -1489,7 +1480,6 @@ html[dir='rtl'] #documentPropertiesContainer .row > * { .grab-to-pan-grab { cursor: url("images/grab.cur"), move !important; - cursor: -moz-grab !important; cursor: grab !important; } .grab-to-pan-grab *:not(input):not(textarea):not(button):not(select):not(:link) { @@ -1498,7 +1488,6 @@ html[dir='rtl'] #documentPropertiesContainer .row > * { .grab-to-pan-grab:active, .grab-to-pan-grabbing { cursor: url("images/grabbing.cur"), move !important; - cursor: -moz-grabbing !important; cursor: grabbing !important; position: fixed; diff --git a/browser/extensions/pdfjs/content/web/viewer.js b/browser/extensions/pdfjs/content/web/viewer.js index db43c10eaad2..b4651756c8d0 100644 --- a/browser/extensions/pdfjs/content/web/viewer.js +++ b/browser/extensions/pdfjs/content/web/viewer.js @@ -4915,7 +4915,6 @@ document.addEventListener('DOMContentLoaded', function webViewerLoad(evt) { var file = window.location.href.split('#')[0]; - document.getElementById('openFile').setAttribute('hidden', 'true'); document.getElementById('secondaryOpenFile').setAttribute('hidden', 'true'); diff --git a/browser/themes/shared/customizableui/panelUIOverlay.inc.css b/browser/themes/shared/customizableui/panelUIOverlay.inc.css index 5bc23a9112a2..b21dcc368bcf 100644 --- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css +++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css @@ -722,10 +722,8 @@ toolbarpaletteitem[place="palette"] > #search-container { padding: .5em; margin: 0; -moz-box-flex: 1; - /* reduce the width with 2px for each button to compensate for two separators - of 3px. */ - min-width: calc(@menuPanelButtonWidth@ - 2px); - max-width: calc(@menuPanelButtonWidth@ - 2px); + min-width: calc(@menuPanelButtonWidth@); + max-width: calc(@menuPanelButtonWidth@); /* We'd prefer to use height: auto here but it leads to layout bugs in the panel. Cope: 1.2em for line height + 2 * .5em padding + margin on the label (2 * 2px) */ height: calc(2.2em + 4px); @@ -733,6 +731,14 @@ toolbarpaletteitem[place="palette"] > #search-container { -moz-box-orient: horizontal; } +#edit-controls@inAnyPanel@ > #copy-button, +#zoom-controls@inAnyPanel@ > #zoom-reset-button { + /* reduce the width with 2px for this button to compensate for two separators + of 1px. */ + min-width: calc(@menuPanelButtonWidth@ - 2px); + max-width: calc(@menuPanelButtonWidth@ - 2px); +} + #edit-controls@inAnyPanel@ > toolbarbutton[disabled] > .toolbarbutton-icon, #zoom-controls@inAnyPanel@ > toolbarbutton[disabled] > .toolbarbutton-icon { opacity: .25; @@ -762,8 +768,7 @@ toolbarpaletteitem[place="palette"] > #search-container { border-bottom-left-radius: 0; } -#edit-controls > separator, -#zoom-controls > separator { +.toolbaritem-combined-buttons > separator { -moz-appearance: none; width: 3px; -moz-box-align: stretch; @@ -775,6 +780,19 @@ toolbarpaletteitem[place="palette"] > #search-container { background-repeat: no-repeat; } +.toolbaritem-combined-buttons@inAnyPanel@ > separator { + margin: .5em 0; + width: 1px; + background: hsla(210,4%,10%,.15); + transition-property: margin; + transition-duration: 10ms; + transition-timing-function: ease; +} + +.toolbaritem-combined-buttons@inAnyPanel@:hover > separator { + margin: 0; +} + #widget-overflow > .panel-arrowcontainer > .panel-arrowcontent { padding: 0; } diff --git a/browser/themes/shared/devtools/app-manager/manifest-editor.inc.css b/browser/themes/shared/devtools/app-manager/manifest-editor.inc.css index 9e8820c2a240..a0e245e899a9 100644 --- a/browser/themes/shared/devtools/app-manager/manifest-editor.inc.css +++ b/browser/themes/shared/devtools/app-manager/manifest-editor.inc.css @@ -54,9 +54,11 @@ .manifest-editor .variables-view-delete, .manifest-editor .variables-view-delete:hover, .manifest-editor .variables-view-delete:active, +.manifest-editor .variable-or-property:focus .variables-view-delete, .manifest-editor .variables-view-add-property, .manifest-editor .variables-view-add-property:hover, -.manifest-editor .variables-view-add-property:active { +.manifest-editor .variables-view-add-property:active, +.manifest-editor .variable-or-property:focus .variables-view-add-property { list-style-image: none; -moz-image-region: initial; } diff --git a/browser/themes/shared/tab-selected.svg b/browser/themes/shared/tab-selected.svg index 9577d14078f8..260eecdaff6d 100644 --- a/browser/themes/shared/tab-selected.svg +++ b/browser/themes/shared/tab-selected.svg @@ -35,7 +35,8 @@ } %ifdef WINDOWS_AERO - @media (-moz-windows-default-theme) { + @media (-moz-windows-default-theme) and (-moz-os-version: windows-vista), + (-moz-windows-default-theme) and (-moz-os-version: windows-win7) { #tab-background-fill { background-color: @customToolbarColor@; } diff --git a/browser/themes/windows/Toolbar-XP.png b/browser/themes/windows/Toolbar-XP.png new file mode 100644 index 000000000000..dce0b0a7d994 Binary files /dev/null and b/browser/themes/windows/Toolbar-XP.png differ diff --git a/browser/themes/windows/Toolbar.png b/browser/themes/windows/Toolbar.png index dce0b0a7d994..57f6f7c7f499 100644 Binary files a/browser/themes/windows/Toolbar.png and b/browser/themes/windows/Toolbar.png differ diff --git a/browser/themes/windows/browser-aero.css b/browser/themes/windows/browser-aero.css index 9535dabe0cca..1002a206d042 100644 --- a/browser/themes/windows/browser-aero.css +++ b/browser/themes/windows/browser-aero.css @@ -22,7 +22,8 @@ } } -@media (-moz-windows-default-theme) { +@media (-moz-windows-default-theme) and (-moz-os-version: windows-vista), + (-moz-windows-default-theme) and (-moz-os-version: windows-win7) { #navigator-toolbox > toolbar:not(:-moz-lwtheme), #browser-bottombox:not(:-moz-lwtheme), .browserContainer > findbar { @@ -169,8 +170,6 @@ because of the border radius on the toolbar below the tab bar. */ #main-window[sizemode=normal] #nav-bar { border-top: 1px solid @toolbarShadowColor@; - border-top-left-radius: 2.5px; - border-top-right-radius: 2.5px; background-clip: padding-box; } @@ -197,7 +196,18 @@ #main-menubar:not(:-moz-lwtheme):not(:-moz-window-inactive) { background-color: rgba(255,255,255,.5); - border-radius: 4px; + } + + @media (-moz-os-version: windows-vista), + (-moz-os-version: windows-win7) { + #main-window[sizemode=normal] #nav-bar { + border-top-left-radius: 2.5px; + border-top-right-radius: 2.5px; + } + + #main-menubar:not(:-moz-lwtheme):not(:-moz-window-inactive) { + border-radius: 4px; + } } #ctrlTab-panel { diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index b81d1f872608..c615175dd1fd 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -785,6 +785,15 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { #home-button.bookmark-item { list-style-image: url("chrome://browser/skin/Toolbar.png"); } + +%ifndef WINDOWS_AERO +@media (-moz-windows-theme: luna-silver) { + #home-button.bookmark-item { + list-style-image: url("chrome://browser/skin/Toolbar-lunaSilver.png"); + } +} +%endif + #home-button.bookmark-item:-moz-lwtheme-brighttext { list-style-image: url("chrome://browser/skin/Toolbar-inverted.png"); } diff --git a/browser/themes/windows/customizableui/panelUIOverlay-aero.css b/browser/themes/windows/customizableui/panelUIOverlay-aero.css new file mode 100644 index 000000000000..871c4be6b5fb --- /dev/null +++ b/browser/themes/windows/customizableui/panelUIOverlay-aero.css @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +%define WINDOWS_AERO +%include panelUIOverlay.css +%undef WINDOWS_AERO diff --git a/browser/themes/windows/customizableui/panelUIOverlay.css b/browser/themes/windows/customizableui/panelUIOverlay.css index 0d370117ed1d..2c43be6f684d 100644 --- a/browser/themes/windows/customizableui/panelUIOverlay.css +++ b/browser/themes/windows/customizableui/panelUIOverlay.css @@ -34,3 +34,24 @@ .widget-overflow-list .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon { padding: 0 6px; } + +%ifdef WINDOWS_AERO +/* Win8 and beyond. */ +@media not all and (-moz-os-version: windows-vista) { + @media not all and (-moz-os-version: windows-win7) { + panelview .toolbarbutton-1, + .subviewbutton, + .widget-overflow-list .toolbarbutton-1, + .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button, + #edit-controls@inAnyPanel@ > toolbarbutton, + #zoom-controls@inAnyPanel@ > toolbarbutton { + border-radius: 0; + } + + #edit-controls@inAnyPanel@, + #zoom-controls@inAnyPanel@ { + border-radius: 0; + } + } +} +%endif diff --git a/browser/themes/windows/jar.mn b/browser/themes/windows/jar.mn index b7c7b1ea5c40..219fbf1a0681 100644 --- a/browser/themes/windows/jar.mn +++ b/browser/themes/windows/jar.mn @@ -72,7 +72,7 @@ browser.jar: skin/classic/browser/Secure24.png skin/classic/browser/setDesktopBackground.css skin/classic/browser/slowStartup-16.png - skin/classic/browser/Toolbar.png + skin/classic/browser/Toolbar.png (Toolbar-XP.png) skin/classic/browser/Toolbar-inverted.png skin/classic/browser/Toolbar-lunaSilver.png skin/classic/browser/toolbarbutton-dropdown-arrow.png @@ -390,8 +390,9 @@ browser.jar: skin/classic/aero/browser/Secure24.png (Secure24-aero.png) skin/classic/aero/browser/setDesktopBackground.css skin/classic/aero/browser/slowStartup-16.png - skin/classic/aero/browser/Toolbar.png (Toolbar-aero.png) + skin/classic/aero/browser/Toolbar.png skin/classic/aero/browser/Toolbar-inverted.png + skin/classic/aero/browser/Toolbar-aero.png skin/classic/aero/browser/toolbarbutton-dropdown-arrow.png skin/classic/aero/browser/toolbarbutton-dropdown-arrow-inverted.png skin/classic/aero/browser/urlbar-arrow.png @@ -410,7 +411,7 @@ browser.jar: skin/classic/aero/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png) skin/classic/aero/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png) skin/classic/aero/browser/customizableui/menuPanel-customizeFinish.png (../shared/customizableui/menuPanel-customizeFinish.png) -* skin/classic/aero/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css) +* skin/classic/aero/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay-aero.css) skin/classic/aero/browser/customizableui/subView-arrow-back-inverted.png (../shared/customizableui/subView-arrow-back-inverted.png) * skin/classic/aero/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay-aero.css) skin/classic/aero/browser/downloads/buttons.png (downloads/buttons-aero.png) @@ -642,5 +643,8 @@ browser.jar: skin/classic/aero/browser/devtools/tooltip/arrow-vertical-light.png (../shared/devtools/tooltip/arrow-vertical-light.png) skin/classic/aero/browser/devtools/tooltip/arrow-vertical-light@2x.png (../shared/devtools/tooltip/arrow-vertical-light@2x.png) +% override chrome://browser/skin/Toolbar.png chrome://browser/skin/Toolbar-aero.png os=WINNT osversion=6 +% override chrome://browser/skin/Toolbar.png chrome://browser/skin/Toolbar-aero.png os=WINNT osversion=6.1 + % override chrome://browser/skin/sync-horizontalbar.png chrome://browser/skin/sync-horizontalbar-XPVista7.png os=WINNT osversion<6.2 % override chrome://browser/skin/syncProgress-horizontalbar.png chrome://browser/skin/syncProgress-horizontalbar-XPVista7.png os=WINNT osversion<6.2 diff --git a/browser/themes/windows/places/organizer-aero.css b/browser/themes/windows/places/organizer-aero.css index 502573a463a1..2e4316fa94c6 100644 --- a/browser/themes/windows/places/organizer-aero.css +++ b/browser/themes/windows/places/organizer-aero.css @@ -36,7 +36,8 @@ } } -@media (-moz-windows-default-theme) { +@media (-moz-windows-default-theme) and (-moz-os-version: windows-vista), + (-moz-windows-default-theme) and (-moz-os-version: windows-win7) { #placesView, #infoPane, #placesList, diff --git a/browser/themes/windows/places/organizer.css b/browser/themes/windows/places/organizer.css index ae20999296c3..1eb37972ffa7 100644 --- a/browser/themes/windows/places/organizer.css +++ b/browser/themes/windows/places/organizer.css @@ -17,6 +17,15 @@ list-style-image: url("chrome://browser/skin/Toolbar.png"); } +%ifndef WINDOWS_AERO +@media (-moz-windows-theme: luna-silver) { + #back-button, + #forward-button { + list-style-image: url("chrome://browser/skin/Toolbar-lunaSilver.png"); + } +} +%endif + #back-button { -moz-image-region: rect(0, 54px, 18px, 36px); } diff --git a/configure.in b/configure.in index 896e5cf1102b..bb34c66426d2 100644 --- a/configure.in +++ b/configure.in @@ -249,7 +249,9 @@ if test -n "$gonkdir" ; then AC_DEFINE(MOZ_OMX_ENCODER) ;; 19) - GONK_INCLUDES="-I$gonkdir/frameworks/native/include" + GONK_INCLUDES="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include" + MOZ_B2G_CAMERA=1 + MOZ_OMX_DECODER=1 MOZ_B2G_BT=1 MOZ_B2G_BT_BLUEDROID=1 MOZ_NFC=1 diff --git a/dom/camera/GonkCameraHwMgr.cpp b/dom/camera/GonkCameraHwMgr.cpp index c71a4634e2c7..0cad6dc676e7 100644 --- a/dom/camera/GonkCameraHwMgr.cpp +++ b/dom/camera/GonkCameraHwMgr.cpp @@ -163,10 +163,17 @@ GonkCameraHardware::Init() mNativeWindow = new GonkNativeWindow(); mNativeWindow->setNewFrameCallback(this); mCamera->setListener(this); -#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 + +#if defined(MOZ_WIDGET_GONK) + +#if ANDROID_VERSION >= 19 + mCamera->setPreviewTarget(mNativeWindow->getBufferQueue()); +#elif (ANDROID_VERSION == 17) || (ANDROID_VERSION == 18) mCamera->setPreviewTexture(mNativeWindow->getBufferQueue()); #else mCamera->setPreviewTexture(mNativeWindow); +#endif + #endif return NS_OK; diff --git a/dom/wifi/WifiWorker.js b/dom/wifi/WifiWorker.js index 28ee9b0d9bec..400af127449f 100644 --- a/dom/wifi/WifiWorker.js +++ b/dom/wifi/WifiWorker.js @@ -296,7 +296,9 @@ var WifiManager = (function() { if (ok) debugEnabled = wanted; }); - p2pManager.setDebug(DEBUG); + if (p2pSupported && p2pManager) { + p2pManager.setDebug(DEBUG); + } } } @@ -2502,6 +2504,79 @@ WifiWorker.prototype = { }).bind(this)); }, + getWifiScanResults: function(callback) { + var count = 0; + var timer = null; + var self = this; + + self.waitForScan(waitForScanCallback); + doScan(); + function doScan() { + WifiManager.scan(true, function (ok) { + if (!ok) { + if (!timer) { + count = 0; + timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + } + + if (count++ >= 3) { + timer = null; + this.wantScanResults.splice(this.wantScanResults.indexOf(waitForScanCallback), 1); + callback.onfailure(); + return; + } + + // Else it's still running, continue waiting. + timer.initWithCallback(doScan, 10000, Ci.nsITimer.TYPE_ONE_SHOT); + return; + } + }); + } + + function waitForScanCallback(networks) { + if (networks === null) { + callback.onfailure(); + return; + } + + var wifiScanResults = new Array(); + var net; + for (let net in networks) { + let value = networks[net]; + wifiScanResults.push(transformResult(value)); + } + callback.onready(wifiScanResults.length, wifiScanResults); + } + + function transformResult(element) { + var result = new WifiScanResult(); + result.connected = false; + for (let id in element) { + if (id === "__exposedProps__") { + continue; + } + if (id === "security") { + result[id] = 0; + var security = element[id]; + for (let j = 0; j < security.length; j++) { + if (security[j] === "WPA-PSK") { + result[id] |= Ci.nsIWifiScanResult.WPA_PSK; + } else if (security[j] === "WPA-EAP") { + result[id] |= Ci.nsIWifiScanResult.WPA_EAP; + } else if (security[j] === "WEP") { + result[id] |= Ci.nsIWifiScanResult.WEP; + } else { + result[id] = 0; + } + } + } else { + result[id] = element[id]; + } + } + return result; + } + }, + getKnownNetworks: function(msg) { const message = "WifiManager:getKnownNetworks:Return"; if (!WifiManager.enabled) { diff --git a/ipc/moz.build b/ipc/moz.build index 471c23534f4c..ff5df8d550e7 100644 --- a/ipc/moz.build +++ b/ipc/moz.build @@ -21,9 +21,9 @@ if CONFIG['MOZ_NFC']: DIRS += ['nfc'] if CONFIG['MOZ_B2G_RIL'] or CONFIG['MOZ_B2G_BT'] or CONFIG['MOZ_NFC'] or CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': - DIRS += ['unixsocket'] + DIRS += ['unixfd', 'unixsocket'] if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': - DIRS += ['netd', 'keystore'] + DIRS += ['keystore', 'netd'] TOOL_DIRS += ['app'] diff --git a/ipc/unixfd/UnixFdWatcher.cpp b/ipc/unixfd/UnixFdWatcher.cpp new file mode 100644 index 000000000000..063a29373b27 --- /dev/null +++ b/ipc/unixfd/UnixFdWatcher.cpp @@ -0,0 +1,119 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "UnixFdWatcher.h" + +#ifdef CHROMIUM_LOG +#undef CHROMIUM_LOG +#endif + +#if defined(MOZ_WIDGET_GONK) +#include +#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "I/O", args); +#else +#include +#define IODEBUG true +#define CHROMIUM_LOG(args...) if (IODEBUG) printf(args); +#endif + +namespace mozilla { +namespace ipc { + +UnixFdWatcher::~UnixFdWatcher() +{ + NS_WARN_IF(IsOpen()); /* mFd should have been closed already */ +} + +void +UnixFdWatcher::Close() +{ + MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop); + + if (NS_WARN_IF(!IsOpen())) { + /* mFd should have been open */ + return; + } + OnClose(); + RemoveWatchers(READ_WATCHER|WRITE_WATCHER); + mFd.dispose(); +} + +void +UnixFdWatcher::AddWatchers(unsigned long aWatchers, bool aPersistent) +{ + MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop); + MOZ_ASSERT(IsOpen()); + + // Before we add a watcher, we need to remove it! Removing is always + // safe, but adding the same watcher twice can lead to endless loops + // inside libevent. + RemoveWatchers(aWatchers); + + if (aWatchers & READ_WATCHER) { + MessageLoopForIO::current()->WatchFileDescriptor( + mFd, + aPersistent, + MessageLoopForIO::WATCH_READ, + &mReadWatcher, + this); + } + if (aWatchers & WRITE_WATCHER) { + MessageLoopForIO::current()->WatchFileDescriptor( + mFd, + aPersistent, + MessageLoopForIO::WATCH_WRITE, + &mWriteWatcher, + this); + } +} + +void +UnixFdWatcher::RemoveWatchers(unsigned long aWatchers) +{ + MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop); + MOZ_ASSERT(IsOpen()); + + if (aWatchers & READ_WATCHER) { + mReadWatcher.StopWatchingFileDescriptor(); + } + if (aWatchers & WRITE_WATCHER) { + mWriteWatcher.StopWatchingFileDescriptor(); + } +} + +void +UnixFdWatcher::OnError(const char* aFunction, int aErrno) +{ + MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop); + + CHROMIUM_LOG("%s failed with error %d (%s)", + aFunction, aErrno, strerror(aErrno)); +} + +UnixFdWatcher::UnixFdWatcher(MessageLoop* aIOLoop) +: mIOLoop(aIOLoop) +{ + MOZ_ASSERT(mIOLoop); +} + +UnixFdWatcher::UnixFdWatcher(MessageLoop* aIOLoop, int aFd) +: mIOLoop(aIOLoop) +, mFd(aFd) +{ + MOZ_ASSERT(mIOLoop); +} + +void +UnixFdWatcher::SetFd(int aFd) +{ + MOZ_ASSERT(MessageLoopForIO::current() == mIOLoop); + MOZ_ASSERT(!IsOpen()); + + mFd = aFd; +} + +} +} diff --git a/ipc/unixfd/UnixFdWatcher.h b/ipc/unixfd/UnixFdWatcher.h new file mode 100644 index 000000000000..7a826fe34ab2 --- /dev/null +++ b/ipc/unixfd/UnixFdWatcher.h @@ -0,0 +1,62 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "base/message_loop.h" +#include "mozilla/FileUtils.h" + +namespace mozilla { +namespace ipc { + +class UnixFdWatcher : public MessageLoopForIO::Watcher +{ +public: + enum { + READ_WATCHER = 1<<0, + WRITE_WATCHER = 1<<1 + }; + + virtual ~UnixFdWatcher(); + + MessageLoop* GetIOLoop() const + { + return mIOLoop; + } + + int GetFd() const + { + return mFd; + } + + bool IsOpen() const + { + return GetFd() >= 0; + } + + virtual void Close(); + + void AddWatchers(unsigned long aWatchers, bool aPersistent); + void RemoveWatchers(unsigned long aWatchers); + + // Callback method that's run before closing the file descriptor + virtual void OnClose() {}; + + // Callback method that's run on POSIX errors + virtual void OnError(const char* aFunction, int aErrno); + +protected: + UnixFdWatcher(MessageLoop* aIOLoop); + UnixFdWatcher(MessageLoop* aIOLoop, int aFd); + void SetFd(int aFd); + +private: + MessageLoop* mIOLoop; + ScopedClose mFd; + MessageLoopForIO::FileDescriptorWatcher mReadWatcher; + MessageLoopForIO::FileDescriptorWatcher mWriteWatcher; +}; + +} +} diff --git a/ipc/unixfd/UnixFileWatcher.cpp b/ipc/unixfd/UnixFileWatcher.cpp new file mode 100644 index 000000000000..7bfffbca9efd --- /dev/null +++ b/ipc/unixfd/UnixFileWatcher.cpp @@ -0,0 +1,44 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include "UnixFileWatcher.h" + +namespace mozilla { +namespace ipc { + +UnixFileWatcher::~UnixFileWatcher() +{ +} + +nsresult +UnixFileWatcher::Open(const char* aFilename, int aFlags, mode_t aMode) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + + int fd = TEMP_FAILURE_RETRY(open(aFilename, aFlags, aMode)); + if (fd < 0) { + OnError("open", errno); + return NS_ERROR_FAILURE; + } + SetFd(fd); + OnOpened(); + + return NS_OK; +} + +UnixFileWatcher::UnixFileWatcher(MessageLoop* aIOLoop) +: UnixFdWatcher(aIOLoop) +{ +} + +UnixFileWatcher::UnixFileWatcher(MessageLoop* aIOLoop, int aFd) +: UnixFdWatcher(aIOLoop, aFd) +{ +} + +} +} diff --git a/ipc/unixfd/UnixFileWatcher.h b/ipc/unixfd/UnixFileWatcher.h new file mode 100644 index 000000000000..5c130d5ba669 --- /dev/null +++ b/ipc/unixfd/UnixFileWatcher.h @@ -0,0 +1,28 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "UnixFdWatcher.h" + +namespace mozilla { +namespace ipc { + +class UnixFileWatcher : public UnixFdWatcher +{ +public: + virtual ~UnixFileWatcher(); + + nsresult Open(const char* aFilename, int aFlags, mode_t aMode = 0); + + // Callback method for successful open requests + virtual void OnOpened() {}; + +protected: + UnixFileWatcher(MessageLoop* aIOLoop); + UnixFileWatcher(MessageLoop* aIOLoop, int aFd); +}; + +} +} diff --git a/ipc/unixfd/UnixSocketWatcher.cpp b/ipc/unixfd/UnixSocketWatcher.cpp new file mode 100644 index 000000000000..b0228a74d73d --- /dev/null +++ b/ipc/unixfd/UnixSocketWatcher.cpp @@ -0,0 +1,155 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include "UnixSocketWatcher.h" + +namespace mozilla { +namespace ipc { + +UnixSocketWatcher::~UnixSocketWatcher() +{ +} + +void UnixSocketWatcher::Close() +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + + mConnectionStatus = SOCKET_IS_DISCONNECTED; + UnixFdWatcher::Close(); +} + +nsresult +UnixSocketWatcher::Connect(const struct sockaddr* aAddr, socklen_t aAddrLen) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(IsOpen()); + MOZ_ASSERT(aAddr || !aAddrLen); + + // Select non-blocking IO. + if (TEMP_FAILURE_RETRY(fcntl(GetFd(), F_SETFL, O_NONBLOCK)) < 0) { + OnError("fcntl", errno); + return NS_ERROR_FAILURE; + } + + if (connect(GetFd(), aAddr, aAddrLen) < 0) { + if (errno == EINPROGRESS) { + // Select blocking IO again, since we've now at least queue'd the connect + // as nonblock. + int flags = TEMP_FAILURE_RETRY(fcntl(GetFd(), F_GETFL, 0)); + if (flags < 0) { + OnError("fcntl", errno); + return NS_ERROR_FAILURE; + } + if (TEMP_FAILURE_RETRY(fcntl(GetFd(), F_SETFL, flags&~O_NONBLOCK)) < 0) { + OnError("fcntl", errno); + return NS_ERROR_FAILURE; + } + mConnectionStatus = SOCKET_IS_CONNECTING; + // Set up a write watch to receive the connect signal + AddWatchers(WRITE_WATCHER, false); + } else { + OnError("connect", errno); + } + return NS_ERROR_FAILURE; + } + + mConnectionStatus = SOCKET_IS_CONNECTED; + OnConnected(); + + return NS_OK; +} + +nsresult +UnixSocketWatcher::Listen(const struct sockaddr* aAddr, socklen_t aAddrLen) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(IsOpen()); + MOZ_ASSERT(aAddr || !aAddrLen); + + if (bind(GetFd(), aAddr, aAddrLen) < 0) { + OnError("bind", errno); + return NS_ERROR_FAILURE; + } + if (listen(GetFd(), 1) < 0) { + OnError("listen", errno); + return NS_ERROR_FAILURE; + } + mConnectionStatus = SOCKET_IS_LISTENING; + OnListening(); + + return NS_OK; +} + +UnixSocketWatcher::UnixSocketWatcher(MessageLoop* aIOLoop) +: UnixFdWatcher(aIOLoop) +, mConnectionStatus(SOCKET_IS_DISCONNECTED) +{ +} + +UnixSocketWatcher::UnixSocketWatcher(MessageLoop* aIOLoop, int aFd, + ConnectionStatus aConnectionStatus) +: UnixFdWatcher(aIOLoop, aFd) +, mConnectionStatus(aConnectionStatus) +{ +} + +void +UnixSocketWatcher::SetSocket(int aFd, ConnectionStatus aConnectionStatus) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + + SetFd(aFd); + mConnectionStatus = aConnectionStatus; +} + +void +UnixSocketWatcher::OnFileCanReadWithoutBlocking(int aFd) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(aFd == GetFd()); + + if (mConnectionStatus == SOCKET_IS_CONNECTED) { + OnSocketCanReceiveWithoutBlocking(); + } else if (mConnectionStatus == SOCKET_IS_LISTENING) { + int fd = TEMP_FAILURE_RETRY(accept(GetFd(), NULL, NULL)); + if (fd < 0) { + OnError("accept", errno); + } else { + OnAccepted(fd); + } + } else { + NS_NOTREACHED("invalid connection state for reading"); + } +} + +void +UnixSocketWatcher::OnFileCanWriteWithoutBlocking(int aFd) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(aFd == GetFd()); + + if (mConnectionStatus == SOCKET_IS_CONNECTED) { + OnSocketCanSendWithoutBlocking(); + } else if (mConnectionStatus == SOCKET_IS_CONNECTING) { + RemoveWatchers(WRITE_WATCHER); + int error = 0; + socklen_t len = sizeof(error); + if (getsockopt(GetFd(), SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + OnError("getsockopt", errno); + } else if (error) { + OnError("connect", error); + } else { + mConnectionStatus = SOCKET_IS_CONNECTED; + OnConnected(); + } + } else { + NS_NOTREACHED("invalid connection state for writing"); + } +} + +} +} diff --git a/ipc/unixfd/UnixSocketWatcher.h b/ipc/unixfd/UnixSocketWatcher.h new file mode 100644 index 000000000000..3aaf8a8ab922 --- /dev/null +++ b/ipc/unixfd/UnixSocketWatcher.h @@ -0,0 +1,66 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "UnixFdWatcher.h" + +namespace mozilla { +namespace ipc { + +class UnixSocketWatcher : public UnixFdWatcher +{ +public: + enum ConnectionStatus { + SOCKET_IS_DISCONNECTED = 0, + SOCKET_IS_LISTENING, + SOCKET_IS_CONNECTING, + SOCKET_IS_CONNECTED + }; + + virtual ~UnixSocketWatcher(); + + virtual void Close() MOZ_OVERRIDE; + + ConnectionStatus GetConnectionStatus() const + { + return mConnectionStatus; + } + + // Connect to a peer + nsresult Connect(const struct sockaddr* aAddr, socklen_t aAddrLen); + + // Listen on socket for incomming connection requests + nsresult Listen(const struct sockaddr* aAddr, socklen_t aAddrLen); + + // Callback method for accepted connections + virtual void OnAccepted(int aFd) {}; + + // Callback method for successful connection requests + virtual void OnConnected() {}; + + // Callback method for successful listen requests + virtual void OnListening() {}; + + // Callback method for receiving from socket + virtual void OnSocketCanReceiveWithoutBlocking() {}; + + // Callback method for sending on socket + virtual void OnSocketCanSendWithoutBlocking() {}; + +protected: + UnixSocketWatcher(MessageLoop* aIOLoop); + UnixSocketWatcher(MessageLoop* aIOLoop, int aFd, + ConnectionStatus aConnectionStatus); + void SetSocket(int aFd, ConnectionStatus aConnectionStatus); + +private: + void OnFileCanReadWithoutBlocking(int aFd) MOZ_OVERRIDE; + void OnFileCanWriteWithoutBlocking(int aFd) MOZ_OVERRIDE; + + ConnectionStatus mConnectionStatus; +}; + +} +} diff --git a/ipc/unixfd/moz.build b/ipc/unixfd/moz.build new file mode 100644 index 000000000000..59f1f4ba71bc --- /dev/null +++ b/ipc/unixfd/moz.build @@ -0,0 +1,23 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS.mozilla.ipc += [ + 'UnixFdWatcher.h', + 'UnixFileWatcher.h', + 'UnixSocketWatcher.h' +] + +SOURCES += [ + 'UnixFdWatcher.cpp', + 'UnixFileWatcher.cpp', + 'UnixSocketWatcher.cpp' +] + +FAIL_ON_WARNINGS = True + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' diff --git a/ipc/unixsocket/UnixSocket.cpp b/ipc/unixsocket/UnixSocket.cpp index bce851ea1605..762b22ecfd15 100644 --- a/ipc/unixsocket/UnixSocket.cpp +++ b/ipc/unixsocket/UnixSocket.cpp @@ -16,6 +16,7 @@ #include "base/eintr_wrapper.h" #include "base/message_loop.h" +#include "mozilla/ipc/UnixSocketWatcher.h" #include "mozilla/Monitor.h" #include "mozilla/FileUtils.h" #include "nsString.h" @@ -27,30 +28,27 @@ static const size_t MAX_READ_SIZE = 1 << 16; #undef CHROMIUM_LOG #if defined(MOZ_WIDGET_GONK) #include -#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkDBus", args); +#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "I/O", args); #else -#define BTDEBUG true -#define CHROMIUM_LOG(args...) if (BTDEBUG) printf(args); +#define IODEBUG true +#define CHROMIUM_LOG(args...) if (IODEBUG) printf(args); #endif -static const int SOCKET_RETRY_TIME_MS = 1000; - namespace mozilla { namespace ipc { -class UnixSocketImpl : public MessageLoopForIO::Watcher +class UnixSocketImpl : public UnixSocketWatcher { public: - UnixSocketImpl(UnixSocketConsumer* aConsumer, UnixSocketConnector* aConnector, - const nsACString& aAddress, - SocketConnectionStatus aConnectionStatus) - : mConsumer(aConsumer) - , mIOLoop(nullptr) + UnixSocketImpl(MessageLoop* mIOLoop, + UnixSocketConsumer* aConsumer, UnixSocketConnector* aConnector, + const nsACString& aAddress) + : UnixSocketWatcher(mIOLoop) + , mConsumer(aConsumer) , mConnector(aConnector) , mShuttingDownOnIOThread(false) , mAddress(aAddress) , mDelayedConnectTask(nullptr) - , mConnectionStatus(aConnectionStatus) { } @@ -63,12 +61,7 @@ public: void QueueWriteData(UnixSocketRawData* aData) { mOutgoingQ.AppendElement(aData); - OnFileCanWriteWithoutBlocking(mFd); - } - - bool isFdValid() - { - return mFd > 0; + AddWatchers(WRITE_WATCHER, false); } bool IsShutdownOnMainThread() @@ -94,24 +87,11 @@ public: MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(!mShuttingDownOnIOThread); - mReadWatcher.StopWatchingFileDescriptor(); - mWriteWatcher.StopWatchingFileDescriptor(); + RemoveWatchers(READ_WATCHER|WRITE_WATCHER); mShuttingDownOnIOThread = true; } - void SetUpIO() - { - MOZ_ASSERT(!mIOLoop); - MOZ_ASSERT(mFd >= 0); - mIOLoop = MessageLoopForIO::current(); - mIOLoop->WatchFileDescriptor(mFd, - true, - MessageLoopForIO::WATCH_READ, - &mReadWatcher, - this); - } - void SetDelayedConnectTask(CancelableTask* aTask) { MOZ_ASSERT(NS_IsMainThread()); @@ -144,17 +124,12 @@ public: */ void Listen(); - /** - * Accept an incoming connection - */ - void Accept(); - /** * Set up flags on whatever our current file descriptor is. * * @return true if successful, false otherwise */ - bool SetSocketFlags(); + bool SetSocketFlags(int aFd); void GetSocketAddr(nsAString& aAddrStr) { @@ -173,53 +148,23 @@ public: */ RefPtr mConsumer; + void OnAccepted(int aFd) MOZ_OVERRIDE; + void OnConnected() MOZ_OVERRIDE; + void OnError(const char* aFunction, int aErrno) MOZ_OVERRIDE; + void OnListening() MOZ_OVERRIDE; + void OnSocketCanReceiveWithoutBlocking() MOZ_OVERRIDE; + void OnSocketCanSendWithoutBlocking() MOZ_OVERRIDE; + private: void FireSocketError(); - /** - * libevent triggered functions that reads data from socket when available and - * guarenteed non-blocking. Only to be called on IO thread. - * - * @param aFd File descriptor to read from - */ - virtual void OnFileCanReadWithoutBlocking(int aFd); - - /** - * libevent or developer triggered functions that writes data to socket when - * available and guarenteed non-blocking. Only to be called on IO thread. - * - * @param aFd File descriptor to read from - */ - virtual void OnFileCanWriteWithoutBlocking(int aFd); - - /** - * IO Loop pointer. Must be initalized and called from IO thread only. - */ - MessageLoopForIO* mIOLoop; - /** * Raw data queue. Must be pushed/popped from IO thread only. */ typedef nsTArray UnixSocketRawDataQueue; UnixSocketRawDataQueue mOutgoingQ; - /** - * Read watcher for libevent. Only to be accessed on IO Thread. - */ - MessageLoopForIO::FileDescriptorWatcher mReadWatcher; - - /** - * Write watcher for libevent. Only to be accessed on IO Thread. - */ - MessageLoopForIO::FileDescriptorWatcher mWriteWatcher; - - /** - * File descriptor to read from/write to. Connection happens on user provided - * thread. Read/write/close happens on IO thread. - */ - ScopedClose mFd; - /** * Connector object used to create the connection we are currently using. */ @@ -249,12 +194,6 @@ private: * Task member for delayed connect task. Should only be access on main thread. */ CancelableTask* mDelayedConnectTask; - - /** - * Socket connection status. Duplicate from UnixSocketConsumer. Should only - * be accessed on I/O thread. - */ - SocketConnectionStatus mConnectionStatus; }; template @@ -403,12 +342,13 @@ private: UnixSocketImpl* mImpl; }; -class SocketAcceptTask : public CancelableTask { +class SocketListenTask : public CancelableTask +{ virtual void Run(); UnixSocketImpl* mImpl; public: - SocketAcceptTask(UnixSocketImpl* aImpl) : mImpl(aImpl) { } + SocketListenTask(UnixSocketImpl* aImpl) : mImpl(aImpl) { } virtual void Cancel() { @@ -417,12 +357,12 @@ public: } }; -void SocketAcceptTask::Run() +void SocketListenTask::Run() { MOZ_ASSERT(!NS_IsMainThread()); if (mImpl) { - mImpl->Accept(); + mImpl->Listen(); } } @@ -478,7 +418,7 @@ void ShutdownSocketTask::Run() MOZ_ASSERT(!NS_IsMainThread()); // At this point, there should be no new events on the IO thread after this - // one with the possible exception of a SocketAcceptTask that + // one with the possible exception of a SocketListenTask that // ShutdownOnIOThread will cancel for us. We are now fully shut down, so we // can send a message to the main thread that will delete mImpl safely knowing // that no more tasks reference it. @@ -492,13 +432,10 @@ void ShutdownSocketTask::Run() void UnixSocketImpl::FireSocketError() { - MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); // Clean up watchers, statuses, fds - mReadWatcher.StopWatchingFileDescriptor(); - mWriteWatcher.StopWatchingFileDescriptor(); - mConnectionStatus = SOCKET_DISCONNECTED; - mFd.reset(-1); + Close(); // Tell the main thread we've errored nsRefPtr t = @@ -507,9 +444,9 @@ UnixSocketImpl::FireSocketError() } void -UnixSocketImpl::Accept() +UnixSocketImpl::Listen() { - MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); MOZ_ASSERT(mConnector); // This will set things we don't particularly care about, but it will hand @@ -520,122 +457,118 @@ UnixSocketImpl::Accept() return; } - if (mFd.get() < 0) { - mFd = mConnector->Create(); - if (mFd.get() < 0) { + if (!IsOpen()) { + int fd = mConnector->Create(); + if (fd < 0) { NS_WARNING("Cannot create socket fd!"); FireSocketError(); return; } + SetFd(fd); - if (!SetSocketFlags()) { + if (!SetSocketFlags(GetFd())) { NS_WARNING("Cannot set socket flags!"); FireSocketError(); return; } - if (bind(mFd.get(), (struct sockaddr*)&mAddr, mAddrSize)) { -#ifdef DEBUG - CHROMIUM_LOG("...bind(%d) gave errno %d", mFd.get(), errno); -#endif - FireSocketError(); - return; - } - - if (listen(mFd.get(), 1)) { -#ifdef DEBUG - CHROMIUM_LOG("...listen(%d) gave errno %d", mFd.get(), errno); -#endif - FireSocketError(); - return; - } - - if (!mConnector->SetUpListenSocket(mFd)) { - NS_WARNING("Could not set up listen socket!"); - FireSocketError(); - return; - } - + // calls OnListening on success, or OnError otherwise + nsresult rv = UnixSocketWatcher::Listen( + reinterpret_cast(&mAddr), mAddrSize); + NS_WARN_IF(NS_FAILED(rv)); } - - SetUpIO(); } void UnixSocketImpl::Connect() { - MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); MOZ_ASSERT(mConnector); - if (mFd.get() < 0) { - mFd = mConnector->Create(); - if (mFd.get() < 0) { + if (!IsOpen()) { + int fd = mConnector->Create(); + if (fd < 0) { NS_WARNING("Cannot create socket fd!"); FireSocketError(); return; } + SetFd(fd); } - int ret; - if (!mConnector->CreateAddr(false, mAddrSize, mAddr, mAddress.get())) { NS_WARNING("Cannot create socket address!"); FireSocketError(); return; } - // Select non-blocking IO. - if (-1 == fcntl(mFd.get(), F_SETFL, O_NONBLOCK)) { - NS_WARNING("Cannot set nonblock!"); - FireSocketError(); + // calls OnConnected() on success, or OnError() otherwise + nsresult rv = UnixSocketWatcher::Connect( + reinterpret_cast(&mAddr), mAddrSize); + NS_WARN_IF(NS_FAILED(rv)); +} + +bool +UnixSocketImpl::SetSocketFlags(int aFd) +{ + // Set socket addr to be reused even if kernel is still waiting to close + int n = 1; + setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); + + // Set close-on-exec bit. + int flags = fcntl(aFd, F_GETFD); + if (-1 == flags) { + return false; + } + + flags |= FD_CLOEXEC; + if (-1 == fcntl(aFd, F_SETFD, flags)) { + return false; + } + + return true; +} + +void +UnixSocketImpl::OnAccepted(int aFd) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_LISTENING); + + if (!mConnector->SetUp(aFd)) { + NS_WARNING("Could not set up socket!"); return; } - ret = connect(mFd.get(), (struct sockaddr*)&mAddr, mAddrSize); - - if (ret) { - if (errno == EINPROGRESS) { - // Select blocking IO again, since we've now at least queue'd the connect - // as nonblock. - int current_opts = fcntl(mFd.get(), F_GETFL, 0); - if (-1 == current_opts) { - NS_WARNING("Cannot get socket opts!"); - FireSocketError(); - return; - } - if (-1 == fcntl(mFd.get(), F_SETFL, current_opts & ~O_NONBLOCK)) { - NS_WARNING("Cannot set socket opts to blocking!"); - FireSocketError(); - return; - } - - // Set up a write watch to make sure we receive the connect signal - MessageLoopForIO::current()->WatchFileDescriptor( - mFd.get(), - false, - MessageLoopForIO::WATCH_WRITE, - &mWriteWatcher, - this); - -#ifdef DEBUG - CHROMIUM_LOG("UnixSocket Connection delayed!"); -#endif - return; - } -#if DEBUG - CHROMIUM_LOG("Socket connect errno=%d\n", errno); -#endif - FireSocketError(); + RemoveWatchers(READ_WATCHER|WRITE_WATCHER); + Close(); + SetSocket(aFd, SOCKET_IS_CONNECTED); + if (!SetSocketFlags(GetFd())) { return; } - if (!SetSocketFlags()) { + nsRefPtr t = + new OnSocketEventTask(this, OnSocketEventTask::CONNECT_SUCCESS); + NS_DispatchToMainThread(t); + + AddWatchers(READ_WATCHER, true); + if (!mOutgoingQ.IsEmpty()) { + AddWatchers(WRITE_WATCHER, false); + } +} + +void +UnixSocketImpl::OnConnected() +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED); + + if (!SetSocketFlags(GetFd())) { NS_WARNING("Cannot set socket flags!"); FireSocketError(); return; } - if (!mConnector->SetUp(mFd)) { + if (!mConnector->SetUp(GetFd())) { NS_WARNING("Could not set up socket!"); FireSocketError(); return; @@ -644,30 +577,125 @@ UnixSocketImpl::Connect() nsRefPtr t = new OnSocketEventTask(this, OnSocketEventTask::CONNECT_SUCCESS); NS_DispatchToMainThread(t); - mConnectionStatus = SOCKET_CONNECTED; - SetUpIO(); + AddWatchers(READ_WATCHER, true); + if (!mOutgoingQ.IsEmpty()) { + AddWatchers(WRITE_WATCHER, false); + } } -bool -UnixSocketImpl::SetSocketFlags() +void +UnixSocketImpl::OnListening() { - // Set socket addr to be reused even if kernel is still waiting to close - int n = 1; - setsockopt(mFd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_LISTENING); - // Set close-on-exec bit. - int flags = fcntl(mFd, F_GETFD); - if (-1 == flags) { - return false; + if (!mConnector->SetUpListenSocket(GetFd())) { + NS_WARNING("Could not set up listen socket!"); + FireSocketError(); + return; } - flags |= FD_CLOEXEC; - if (-1 == fcntl(mFd, F_SETFD, flags)) { - return false; - } + AddWatchers(READ_WATCHER, true); +} - return true; +void +UnixSocketImpl::OnError(const char* aFunction, int aErrno) +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + + UnixFdWatcher::OnError(aFunction, aErrno); + FireSocketError(); +} + +void +UnixSocketImpl::OnSocketCanReceiveWithoutBlocking() +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED); + + // Read all of the incoming data. + while (true) { + nsAutoPtr incoming(new UnixSocketRawData(MAX_READ_SIZE)); + + ssize_t ret = read(GetFd(), incoming->mData, incoming->mSize); + if (ret <= 0) { + if (ret == -1) { + if (errno == EINTR) { + continue; // retry system call when interrupted + } + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return; // no data available: return and re-poll + } + +#ifdef DEBUG + NS_WARNING("Cannot read from network"); +#endif + // else fall through to error handling on other errno's + } + + // We're done with our descriptors. Ensure that spurious events don't + // cause us to end up back here. + RemoveWatchers(READ_WATCHER|WRITE_WATCHER); + nsRefPtr t = new RequestClosingSocketTask(this); + NS_DispatchToMainThread(t); + return; + } + + incoming->mSize = ret; + nsRefPtr t = + new SocketReceiveTask(this, incoming.forget()); + NS_DispatchToMainThread(t); + + // If ret is less than MAX_READ_SIZE, there's no + // more data in the socket for us to read now. + if (ret < ssize_t(MAX_READ_SIZE)) { + return; + } + } +} + +void +UnixSocketImpl::OnSocketCanSendWithoutBlocking() +{ + MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop()); + MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED); + + // Try to write the bytes of mCurrentRilRawData. If all were written, continue. + // + // Otherwise, save the byte position of the next byte to write + // within mCurrentWriteOffset, and request another write when the + // system won't block. + // + while (true) { + UnixSocketRawData* data; + if (mOutgoingQ.IsEmpty()) { + return; + } + data = mOutgoingQ.ElementAt(0); + const uint8_t *toWrite; + toWrite = data->mData; + + while (data->mCurrentWriteOffset < data->mSize) { + ssize_t write_amount = data->mSize - data->mCurrentWriteOffset; + ssize_t written; + written = write (GetFd(), toWrite + data->mCurrentWriteOffset, + write_amount); + if (written > 0) { + data->mCurrentWriteOffset += written; + } + if (written != write_amount) { + break; + } + } + + if (data->mCurrentWriteOffset != data->mSize) { + AddWatchers(WRITE_WATCHER, false); + return; + } + mOutgoingQ.RemoveElementAt(0); + delete data; + } } UnixSocketConsumer::UnixSocketConsumer() : mImpl(nullptr) @@ -737,166 +765,6 @@ UnixSocketConsumer::CloseSocket() NotifyDisconnect(); } -void -UnixSocketImpl::OnFileCanReadWithoutBlocking(int aFd) -{ - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(!mShuttingDownOnIOThread); - - if (mConnectionStatus == SOCKET_CONNECTED) { - // Read all of the incoming data. - while (true) { - nsAutoPtr incoming(new UnixSocketRawData(MAX_READ_SIZE)); - - ssize_t ret = read(aFd, incoming->mData, incoming->mSize); - if (ret <= 0) { - if (ret == -1) { - if (errno == EINTR) { - continue; // retry system call when interrupted - } - if (errno == EAGAIN || errno == EWOULDBLOCK) { - return; // no data available: return and re-poll - } - -#ifdef DEBUG - NS_WARNING("Cannot read from network"); -#endif - // else fall through to error handling on other errno's - } - - // We're done with our descriptors. Ensure that spurious events don't - // cause us to end up back here. - mReadWatcher.StopWatchingFileDescriptor(); - mWriteWatcher.StopWatchingFileDescriptor(); - nsRefPtr t = new RequestClosingSocketTask(this); - NS_DispatchToMainThread(t); - return; - } - - incoming->mSize = ret; - nsRefPtr t = - new SocketReceiveTask(this, incoming.forget()); - NS_DispatchToMainThread(t); - - // If ret is less than MAX_READ_SIZE, there's no - // more data in the socket for us to read now. - if (ret < ssize_t(MAX_READ_SIZE)) { - return; - } - } - - MOZ_CRASH("We returned early"); - } else if (mConnectionStatus == SOCKET_LISTENING) { - int client_fd = accept(mFd.get(), (struct sockaddr*)&mAddr, &mAddrSize); - - if (client_fd < 0) { - return; - } - - if (!mConnector->SetUp(client_fd)) { - NS_WARNING("Could not set up socket!"); - return; - } - - mReadWatcher.StopWatchingFileDescriptor(); - mWriteWatcher.StopWatchingFileDescriptor(); - - mFd.reset(client_fd); - if (!SetSocketFlags()) { - return; - } - - mIOLoop = nullptr; - - nsRefPtr t = - new OnSocketEventTask(this, OnSocketEventTask::CONNECT_SUCCESS); - NS_DispatchToMainThread(t); - mConnectionStatus = SOCKET_CONNECTED; - - SetUpIO(); - } -} - -void -UnixSocketImpl::OnFileCanWriteWithoutBlocking(int aFd) -{ - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(!mShuttingDownOnIOThread); - - MOZ_ASSERT(aFd >= 0); - if (mConnectionStatus == SOCKET_CONNECTED) { - // Try to write the bytes of mCurrentRilRawData. If all were written, continue. - // - // Otherwise, save the byte position of the next byte to write - // within mCurrentWriteOffset, and request another write when the - // system won't block. - // - while (true) { - UnixSocketRawData* data; - if (mOutgoingQ.IsEmpty()) { - return; - } - data = mOutgoingQ.ElementAt(0); - const uint8_t *toWrite; - toWrite = data->mData; - - while (data->mCurrentWriteOffset < data->mSize) { - ssize_t write_amount = data->mSize - data->mCurrentWriteOffset; - ssize_t written; - written = write (aFd, toWrite + data->mCurrentWriteOffset, - write_amount); - if (written > 0) { - data->mCurrentWriteOffset += written; - } - if (written != write_amount) { - break; - } - } - - if (data->mCurrentWriteOffset != data->mSize) { - MessageLoopForIO::current()->WatchFileDescriptor( - aFd, - false, - MessageLoopForIO::WATCH_WRITE, - &mWriteWatcher, - this); - return; - } - mOutgoingQ.RemoveElementAt(0); - delete data; - } - } else if (mConnectionStatus == SOCKET_CONNECTING) { - int error, ret; - socklen_t len = sizeof(error); - ret = getsockopt(mFd.get(), SOL_SOCKET, SO_ERROR, &error, &len); - - if (ret || error) { - NS_WARNING("getsockopt failure on async socket connect!"); - FireSocketError(); - return; - } - - if (!SetSocketFlags()) { - NS_WARNING("Cannot set socket flags!"); - FireSocketError(); - return; - } - - if (!mConnector->SetUp(mFd)) { - NS_WARNING("Could not set up socket!"); - FireSocketError(); - return; - } - - nsRefPtr t = - new OnSocketEventTask(this, OnSocketEventTask::CONNECT_SUCCESS); - NS_DispatchToMainThread(t); - mConnectionStatus = SOCKET_CONNECTED; - - SetUpIO(); - } -} - void UnixSocketConsumer::GetSocketAddr(nsAString& aAddrStr) { @@ -951,8 +819,8 @@ UnixSocketConsumer::ConnectSocket(UnixSocketConnector* aConnector, } nsCString addr(aAddress); - mImpl = new UnixSocketImpl(this, connector.forget(), addr, SOCKET_CONNECTING); MessageLoop* ioLoop = XRE_GetIOMessageLoop(); + mImpl = new UnixSocketImpl(ioLoop, this, connector.forget(), addr); mConnectionStatus = SOCKET_CONNECTING; if (aDelayMs > 0) { SocketDelayedConnectTask* connectTask = new SocketDelayedConnectTask(mImpl); @@ -977,11 +845,11 @@ UnixSocketConsumer::ListenSocket(UnixSocketConnector* aConnector) return false; } - mImpl = new UnixSocketImpl(this, connector.forget(), EmptyCString(), - SOCKET_LISTENING); + mImpl = new UnixSocketImpl(XRE_GetIOMessageLoop(), this, connector.forget(), + EmptyCString()); mConnectionStatus = SOCKET_LISTENING; XRE_GetIOMessageLoop()->PostTask(FROM_HERE, - new SocketAcceptTask(mImpl)); + new SocketListenTask(mImpl)); return true; } diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index e613bdea5162..32aa3a373504 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -5107,7 +5107,7 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct, animIterationCount.unit == eCSSUnit_Unset) { animation->SetIterationCount(1.0f); } else if (animIterationCount.list) { - switch(animIterationCount.list->mValue.GetUnit()) { + switch (animIterationCount.list->mValue.GetUnit()) { case eCSSUnit_Enumerated: NS_ABORT_IF_FALSE(animIterationCount.list->mValue.GetIntValue() == NS_STYLE_ANIMATION_ITERATION_COUNT_INFINITE, @@ -8373,7 +8373,7 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct, case eCSSUnit_ListDep: { svgReset->mFilters.Clear(); const nsCSSValueList* cur = filterValue->GetListValue(); - while(cur) { + while (cur) { nsStyleFilter styleFilter; if (!SetStyleFilterToCSSValue(&styleFilter, cur->mValue, aContext, mPresContext, canStoreInRuleTree)) { diff --git a/services/common/Makefile.in b/services/common/Makefile.in index df3d037e2b27..6d4e0928e192 100644 --- a/services/common/Makefile.in +++ b/services/common/Makefile.in @@ -3,7 +3,8 @@ # You can obtain one at http://mozilla.org/MPL/2.0/. modules := \ - hawk.js \ + hawkclient.js \ + hawkrequest.js \ storageservice.js \ stringbundle.js \ tokenserverclient.js \ diff --git a/services/common/hawk.js b/services/common/hawkclient.js similarity index 96% rename from services/common/hawk.js rename to services/common/hawkclient.js index 3c1d4d8e4b9e..a9a7a3fb3d4b 100644 --- a/services/common/hawk.js +++ b/services/common/hawkclient.js @@ -31,7 +31,7 @@ const {interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/FxAccountsCommon.js"); Cu.import("resource://services-common/utils.js"); Cu.import("resource://services-crypto/utils.js"); -Cu.import("resource://services-common/rest.js"); +Cu.import("resource://services-common/hawkrequest.js"); Cu.import("resource://gre/modules/Promise.jsm"); /* @@ -65,12 +65,18 @@ this.HawkClient.prototype = { * A string describing the error */ _constructError: function(restResponse, errorString) { - return { + let errorObj = { error: errorString, message: restResponse.statusText, code: restResponse.status, errno: restResponse.status }; + let retryAfter = restResponse.headers["retry-after"]; + retryAfter = retryAfter ? parseInt(retryAfter) : retryAfter; + if (retryAfter) { + errorObj.retryAfter = retryAfter; + } + return errorObj; }, /* diff --git a/services/common/hawkrequest.js b/services/common/hawkrequest.js new file mode 100644 index 000000000000..baafeefddebb --- /dev/null +++ b/services/common/hawkrequest.js @@ -0,0 +1,145 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +this.EXPORTED_SYMBOLS = [ + "HAWKAuthenticatedRESTRequest", +]; + +Cu.import("resource://gre/modules/Preferences.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Log.jsm"); +Cu.import("resource://services-common/rest.js"); + +XPCOMUtils.defineLazyModuleGetter(this, "CryptoUtils", + "resource://services-crypto/utils.js"); + +const Prefs = new Preferences("services.common.rest."); + +/** + * Single-use HAWK-authenticated HTTP requests to RESTish resources. + * + * @param uri + * (String) URI for the RESTRequest constructor + * + * @param credentials + * (Object) Optional credentials for computing HAWK authentication + * header. + * + * @param payloadObj + * (Object) Optional object to be converted to JSON payload + * + * @param extra + * (Object) Optional extra params for HAWK header computation. + * Valid properties are: + * + * now: , + * localtimeOffsetMsec: + * + * extra.localtimeOffsetMsec is the value in milliseconds that must be added to + * the local clock to make it agree with the server's clock. For instance, if + * the local clock is two minutes ahead of the server, the time offset in + * milliseconds will be -120000. + */ + +this.HAWKAuthenticatedRESTRequest = + function HawkAuthenticatedRESTRequest(uri, credentials, extra={}) { + RESTRequest.call(this, uri); + + this.credentials = credentials; + this.now = extra.now || Date.now(); + this.localtimeOffsetMsec = extra.localtimeOffsetMsec || 0; + this._log.trace("local time, offset: " + this.now + ", " + (this.localtimeOffsetMsec)); + + // Expose for testing + this._intl = getIntl(); +}; +HAWKAuthenticatedRESTRequest.prototype = { + __proto__: RESTRequest.prototype, + + dispatch: function dispatch(method, data, onComplete, onProgress) { + let contentType = "text/plain"; + if (method == "POST" || method == "PUT") { + contentType = "application/json"; + } + if (this.credentials) { + let options = { + now: this.now, + localtimeOffsetMsec: this.localtimeOffsetMsec, + credentials: this.credentials, + payload: data && JSON.stringify(data) || "", + contentType: contentType, + }; + let header = CryptoUtils.computeHAWK(this.uri, method, options); + this.setHeader("Authorization", header.field); + this._log.trace("hawk auth header: " + header.field); + } + + this.setHeader("Content-Type", contentType); + + this.setHeader("Accept-Language", this._intl.accept_languages); + + return RESTRequest.prototype.dispatch.call( + this, method, data, onComplete, onProgress + ); + } +}; + +// With hawk request, we send the user's accepted-languages with each request. +// To keep the number of times we read this pref at a minimum, maintain the +// preference in a stateful object that notices and updates itself when the +// pref is changed. +this.Intl = function Intl() { + // We won't actually query the pref until the first time we need it + this._accepted = ""; + this._everRead = false; + this._log = Log.repository.getLogger("Services.common.RESTRequest"); + this._log.level = Log.Level[Prefs.get("log.logger.rest.request")]; + this.init(); +}; + +this.Intl.prototype = { + init: function() { + Services.prefs.addObserver("intl.accept_languages", this, false); + }, + + uninit: function() { + Services.prefs.removeObserver("intl.accept_languages", this); + }, + + observe: function(subject, topic, data) { + this.readPref(); + }, + + readPref: function() { + this._everRead = true; + try { + this._accepted = Services.prefs.getComplexValue( + "intl.accept_languages", Ci.nsIPrefLocalizedString).data; + } catch (err) { + this._log.error("Error reading intl.accept_languages pref: " + CommonUtils.exceptionStr(err)); + } + }, + + get accept_languages() { + if (!this._everRead) { + this.readPref(); + } + return this._accepted; + }, +}; + +// Singleton getter for Intl, creating an instance only when we first need it. +let intl = null; +function getIntl() { + if (!intl) { + intl = new Intl(); + } + return intl; +} + diff --git a/services/common/rest.js b/services/common/rest.js index 67773f338cc0..e00e24dbc099 100644 --- a/services/common/rest.js +++ b/services/common/rest.js @@ -10,7 +10,6 @@ this.EXPORTED_SYMBOLS = [ "RESTRequest", "RESTResponse", "TokenAuthenticatedRESTRequest", - "HAWKAuthenticatedRESTRequest", ]; #endif @@ -725,75 +724,3 @@ TokenAuthenticatedRESTRequest.prototype = { }, }; -/** - * Single-use HAWK-authenticated HTTP requests to RESTish resources. - * - * @param uri - * (String) URI for the RESTRequest constructor - * - * @param credentials - * (Object) Optional credentials for computing HAWK authentication - * header. - * - * @param payloadObj - * (Object) Optional object to be converted to JSON payload - * - * @param extra - * (Object) Optional extra params for HAWK header computation. - * Valid properties are: - * - * now: , - * localtimeOffsetMsec: - * - * extra.localtimeOffsetMsec is the value in milliseconds that must be added to - * the local clock to make it agree with the server's clock. For instance, if - * the local clock is two minutes ahead of the server, the time offset in - * milliseconds will be -120000. - */ -this.HAWKAuthenticatedRESTRequest = - function HawkAuthenticatedRESTRequest(uri, credentials, extra={}) { - RESTRequest.call(this, uri); - - this.credentials = credentials; - this.now = extra.now || Date.now(); - this.localtimeOffsetMsec = extra.localtimeOffsetMsec || 0; - this._log.trace("local time, offset: " + this.now + ", " + (this.localtimeOffsetMsec)); -}; -HAWKAuthenticatedRESTRequest.prototype = { - __proto__: RESTRequest.prototype, - - dispatch: function dispatch(method, data, onComplete, onProgress) { - let contentType = "text/plain"; - if (method == "POST" || method == "PUT") { - contentType = "application/json"; - } - if (this.credentials) { - let options = { - now: this.now, - localtimeOffsetMsec: this.localtimeOffsetMsec, - credentials: this.credentials, - payload: data && JSON.stringify(data) || "", - contentType: contentType, - }; - let header = CryptoUtils.computeHAWK(this.uri, method, options); - this.setHeader("Authorization", header.field); - this._log.trace("hawk auth header: " + header.field); - } - - this.setHeader("Content-Type", contentType); - - try { - let acceptLanguage = Services.prefs.getComplexValue( - "intl.accept_languages", Ci.nsIPrefLocalizedString).data; - if (acceptLanguage) { - this.setHeader("Accept-Language", acceptLanguage); - } - } catch (err) { - this._log.error("Error reading intl.accept_languages pref: " + CommonUtils.exceptionStr(err)); - } - - return RESTRequest.prototype.dispatch.call( - this, method, data, onComplete, onProgress - ); - } -}; diff --git a/services/common/tests/unit/test_hawk.js b/services/common/tests/unit/test_hawkclient.js similarity index 99% rename from services/common/tests/unit/test_hawk.js rename to services/common/tests/unit/test_hawkclient.js index ea9c50fd211a..3d47b964ce7f 100644 --- a/services/common/tests/unit/test_hawk.js +++ b/services/common/tests/unit/test_hawkclient.js @@ -4,7 +4,7 @@ "use strict"; Cu.import("resource://gre/modules/Promise.jsm"); -Cu.import("resource://services-common/hawk.js"); +Cu.import("resource://services-common/hawkclient.js"); const SECOND_MS = 1000; const MINUTE_MS = SECOND_MS * 60; diff --git a/services/common/tests/unit/test_hawkrequest.js b/services/common/tests/unit/test_hawkrequest.js new file mode 100644 index 000000000000..067287843fe3 --- /dev/null +++ b/services/common/tests/unit/test_hawkrequest.js @@ -0,0 +1,203 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +Cu.import("resource://gre/modules/Log.jsm"); +Cu.import("resource://services-common/utils.js"); +Cu.import("resource://services-common/hawkrequest.js"); + +function do_register_cleanup() { + Services.prefs.resetUserPrefs(); + + // remove the pref change listener + let hawk = new HAWKAuthenticatedRESTRequest("https://example.com"); + hawk._intl.uninit(); +} + +function run_test() { + Log.repository.getLogger("Services.Common.RESTRequest").level = + Log.Level.Trace; + initTestLogging("Trace"); + + run_next_test(); +} + + +add_test(function test_intl_accept_language() { + let testCount = 0; + let languages = [ + "zu-NP;vo", // Nepalese dialect of Zulu, defaulting to Volapük + "fa-CG;ik", // Congolese dialect of Farsei, defaulting to Inupiaq + ]; + + function setLanguage(lang) { + let acceptLanguage = Cc["@mozilla.org/supports-string;1"] + .createInstance(Ci.nsISupportsString); + acceptLanguage.data = lang; + Services.prefs.setComplexValue( + "intl.accept_languages", Ci.nsISupportsString, acceptLanguage); + } + + let hawk = new HAWKAuthenticatedRESTRequest("https://example.com"); + + Services.prefs.addObserver("intl.accept_languages", nextTest, false); + setLanguage(languages[testCount]); + + function nextTest() { + CommonUtils.nextTick(function() { + if (testCount < 2) { + do_check_eq(hawk._intl.accept_languages, languages[testCount]); + + testCount += 1; + setLanguage(languages[testCount]); + nextTest(); + return; + } + Services.prefs.removeObserver("intl.accept_languages", nextTest); + run_next_test(); + return; + }); + } +}); + +add_test(function test_hawk_authenticated_request() { + let onProgressCalled = false; + let postData = {your: "data"}; + + // An arbitrary date - Feb 2, 1971. It ends in a bunch of zeroes to make our + // computation with the hawk timestamp easier, since hawk throws away the + // millisecond values. + let then = 34329600000; + + let clockSkew = 120000; + let timeOffset = -1 * clockSkew; + let localTime = then + clockSkew; + + // Set the accept-languages pref to the Nepalese dialect of Zulu. + let acceptLanguage = Cc['@mozilla.org/supports-string;1'].createInstance(Ci.nsISupportsString); + acceptLanguage.data = 'zu-NP'; // omit trailing ';', which our HTTP libs snip + Services.prefs.setComplexValue('intl.accept_languages', Ci.nsISupportsString, acceptLanguage); + + let credentials = { + id: "eyJleHBpcmVzIjogMTM2NTAxMDg5OC4x", + key: "qTZf4ZFpAMpMoeSsX3zVRjiqmNs=", + algorithm: "sha256" + }; + + let server = httpd_setup({ + "/elysium": function(request, response) { + do_check_true(request.hasHeader("Authorization")); + + // check that the header timestamp is our arbitrary system date, not + // today's date. Note that hawk header timestamps are in seconds, not + // milliseconds. + let authorization = request.getHeader("Authorization"); + let tsMS = parseInt(/ts="(\d+)"/.exec(authorization)[1], 10) * 1000; + do_check_eq(tsMS, then); + + // This testing can be a little wonky. In an environment where + // pref("intl.accept_languages") === 'en-US, en' + // the header is sent as: + // 'en-US,en;q=0.5' + // hence our fake value for acceptLanguage. + let lang = request.getHeader("Accept-Language"); + do_check_eq(lang, acceptLanguage); + + let message = "yay"; + response.setStatusLine(request.httpVersion, 200, "OK"); + response.bodyOutputStream.write(message, message.length); + } + }); + + function onProgress() { + onProgressCalled = true; + } + + function onComplete(error) { + do_check_eq(200, this.response.status); + do_check_eq(this.response.body, "yay"); + do_check_true(onProgressCalled); + + Services.prefs.resetUserPrefs(); + let pref = Services.prefs.getComplexValue( + "intl.accept_languages", Ci.nsIPrefLocalizedString); + do_check_neq(acceptLanguage.data, pref.data); + + server.stop(run_next_test); + } + + let url = server.baseURI + "/elysium"; + let extra = { + now: localTime, + localtimeOffsetMsec: timeOffset + }; + + let request = new HAWKAuthenticatedRESTRequest(url, credentials, extra); + + // Allow hawk._intl to respond to the language pref change + CommonUtils.nextTick(function() { + request.post(postData, onComplete, onProgress); + }); +}); + +add_test(function test_hawk_language_pref_changed() { + let languages = [ + "zu-NP", // Nepalese dialect of Zulu + "fa-CG", // Congolese dialect of Farsi + ]; + + let credentials = { + id: "eyJleHBpcmVzIjogMTM2NTAxMDg5OC4x", + key: "qTZf4ZFpAMpMoeSsX3zVRjiqmNs=", + algorithm: "sha256", + }; + + function setLanguage(lang) { + let acceptLanguage = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); + acceptLanguage.data = lang; + Services.prefs.setComplexValue("intl.accept_languages", Ci.nsISupportsString, acceptLanguage); + } + + let server = httpd_setup({ + "/foo": function(request, response) { + do_check_eq(languages[1], request.getHeader("Accept-Language")); + + response.setStatusLine(request.httpVersion, 200, "OK"); + }, + }); + + let url = server.baseURI + "/foo"; + let postData = {}; + let request; + + setLanguage(languages[0]); + + // A new request should create the stateful object for tracking the current + // language. + request = new HAWKAuthenticatedRESTRequest(url, credentials); + CommonUtils.nextTick(testFirstLanguage); + + function testFirstLanguage() { + do_check_eq(languages[0], request._intl.accept_languages); + + // Change the language pref ... + setLanguage(languages[1]); + CommonUtils.nextTick(testRequest); + } + + function testRequest() { + // Change of language pref should be picked up, which we can see on the + // server by inspecting the request headers. + request = new HAWKAuthenticatedRESTRequest(url, credentials); + request.post({}, function(error) { + do_check_null(error); + do_check_eq(200, this.response.status); + + Services.prefs.resetUserPrefs(); + + server.stop(run_next_test); + }); + } +}); + diff --git a/services/common/tests/unit/test_restrequest.js b/services/common/tests/unit/test_restrequest.js index b6970a2901c1..9ad01b483ef6 100644 --- a/services/common/tests/unit/test_restrequest.js +++ b/services/common/tests/unit/test_restrequest.js @@ -1,13 +1,13 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/Log.jsm"); Cu.import("resource://services-common/rest.js"); Cu.import("resource://services-common/utils.js"); -//DEBUG = true; - function run_test() { Log.repository.getLogger("Services.Common.RESTRequest").level = Log.Level.Trace; @@ -838,79 +838,3 @@ add_test(function test_not_sending_cookie() { }); }); -add_test(function test_hawk_authenticated_request() { - do_test_pending(); - - let onProgressCalled = false; - let postData = {your: "data"}; - - // An arbitrary date - Feb 2, 1971. It ends in a bunch of zeroes to make our - // computation with the hawk timestamp easier, since hawk throws away the - // millisecond values. - let then = 34329600000; - - let clockSkew = 120000; - let timeOffset = -1 * clockSkew; - let localTime = then + clockSkew; - - // Set the accept-languages pref to the Nepalese dialect of Zulu. - let acceptLanguage = Cc['@mozilla.org/supports-string;1'].createInstance(Ci.nsISupportsString); - acceptLanguage.data = 'zu-NP'; // omit trailing ';', which our HTTP libs snip - Services.prefs.setComplexValue('intl.accept_languages', Ci.nsISupportsString, acceptLanguage); - - let credentials = { - id: "eyJleHBpcmVzIjogMTM2NTAxMDg5OC4x", - key: "qTZf4ZFpAMpMoeSsX3zVRjiqmNs=", - algorithm: "sha256" - }; - - let server = httpd_setup({ - "/elysium": function(request, response) { - do_check_true(request.hasHeader("Authorization")); - - // check that the header timestamp is our arbitrary system date, not - // today's date. Note that hawk header timestamps are in seconds, not - // milliseconds. - let authorization = request.getHeader("Authorization"); - let tsMS = parseInt(/ts="(\d+)"/.exec(authorization)[1], 10) * 1000; - do_check_eq(tsMS, then); - - // This testing can be a little wonky. In an environment where - // pref("intl.accept_languages") === 'en-US, en' - // the header is sent as: - // 'en-US,en;q=0.5' - // hence our fake value for acceptLanguage. - let lang = request.getHeader('Accept-Language'); - do_check_eq(lang, acceptLanguage); - - let message = "yay"; - response.setStatusLine(request.httpVersion, 200, "OK"); - response.bodyOutputStream.write(message, message.length); - } - }); - - function onProgress() { - onProgressCalled = true; - } - - function onComplete(error) { - do_check_eq(200, this.response.status); - do_check_eq(this.response.body, "yay"); - do_check_true(onProgressCalled); - do_test_finished(); - server.stop(run_next_test); - } - - let url = server.baseURI + "/elysium"; - let extra = { - now: localTime, - localtimeOffsetMsec: timeOffset - }; - let request = new HAWKAuthenticatedRESTRequest(url, credentials, extra); - request.post(postData, onComplete, onProgress); - - Services.prefs.resetUserPrefs(); - let pref = Services.prefs.getComplexValue('intl.accept_languages', - Ci.nsIPrefLocalizedString); - do_check_neq(acceptLanguage.data, pref.data); -}); diff --git a/services/common/tests/unit/xpcshell.ini b/services/common/tests/unit/xpcshell.ini index 5b2c10eb8c4c..ae1b89253559 100644 --- a/services/common/tests/unit/xpcshell.ini +++ b/services/common/tests/unit/xpcshell.ini @@ -25,7 +25,8 @@ firefox-appdir = browser [test_async_querySpinningly.js] [test_bagheera_server.js] [test_bagheera_client.js] -[test_hawk.js] +[test_hawkclient.js] +[test_hawkrequest.js] [test_observers.js] [test_restrequest.js] [test_tokenauthenticatedrequest.js] diff --git a/services/fxaccounts/FxAccountsClient.jsm b/services/fxaccounts/FxAccountsClient.jsm index 9dd33ac8eace..c396c97e5179 100644 --- a/services/fxaccounts/FxAccountsClient.jsm +++ b/services/fxaccounts/FxAccountsClient.jsm @@ -10,7 +10,7 @@ Cu.import("resource://gre/modules/Log.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://services-common/utils.js"); -Cu.import("resource://services-common/hawk.js"); +Cu.import("resource://services-common/hawkclient.js"); Cu.import("resource://services-crypto/utils.js"); Cu.import("resource://gre/modules/FxAccountsCommon.js"); Cu.import("resource://gre/modules/Credentials.jsm"); @@ -27,6 +27,10 @@ this.FxAccountsClient = function(host = HOST) { // The FxA auth server expects requests to certain endpoints to be authorized // using Hawk. this.hawk = new HawkClient(host); + + // Manage server backoff state. C.f. + // https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#backoff-protocol + this.backoffError = null; }; this.FxAccountsClient.prototype = { @@ -306,6 +310,10 @@ this.FxAccountsClient.prototype = { }; }, + _clearBackoff: function() { + this.backoffError = null; + }, + /** * A general method for sending raw API calls to the FxA auth server. * All request bodies and responses are JSON. @@ -332,6 +340,13 @@ this.FxAccountsClient.prototype = { _request: function hawkRequest(path, method, credentials, jsonPayload) { let deferred = Promise.defer(); + // We were asked to back off. + if (this.backoffError) { + log.debug("Received new request during backoff, re-rejecting."); + deferred.reject(this.backoffError); + return deferred.promise; + } + this.hawk.request(path, method, credentials, jsonPayload).then( (responseText) => { try { @@ -345,6 +360,17 @@ this.FxAccountsClient.prototype = { (error) => { log.error("error " + method + "ing " + path + ": " + JSON.stringify(error)); + if (error.retryAfter) { + log.debug("Received backoff response; caching error as flag."); + this.backoffError = error; + // Schedule clearing of cached-error-as-flag. + CommonUtils.namedTimer( + this._clearBackoff, + error.retryAfter * 1000, + this, + "fxaBackoffTimer" + ); + } deferred.reject(error); } ); diff --git a/services/fxaccounts/tests/mochitest/test_invalidEmailCase.html b/services/fxaccounts/tests/mochitest/test_invalidEmailCase.html index 2425ebf9bc85..5721b9d02ebf 100644 --- a/services/fxaccounts/tests/mochitest/test_invalidEmailCase.html +++ b/services/fxaccounts/tests/mochitest/test_invalidEmailCase.html @@ -29,7 +29,7 @@ Components.utils.import("resource://gre/modules/Promise.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/FxAccounts.jsm"); Components.utils.import("resource://gre/modules/FxAccountsClient.jsm"); -Components.utils.import("resource://services-common/hawk.js"); +Components.utils.import("resource://services-common/hawkclient.js"); const TEST_SERVER = "http://mochi.test:8888/chrome/services/fxaccounts/tests/mochitest/file_invalidEmailCase.sjs?path="; diff --git a/services/fxaccounts/tests/xpcshell/test_client.js b/services/fxaccounts/tests/xpcshell/test_client.js index 67c649b96a01..9158f7b80e81 100644 --- a/services/fxaccounts/tests/xpcshell/test_client.js +++ b/services/fxaccounts/tests/xpcshell/test_client.js @@ -102,6 +102,53 @@ add_task(function test_500_error() { yield deferredStop(server); }); +add_task(function test_backoffError() { + let method = "GET"; + let server = httpd_setup({ + "/retryDelay": function(request, response) { + response.setHeader("Retry-After", "30"); + response.setStatusLine(request.httpVersion, 429, "Client has sent too many requests"); + let message = "

Ooops!

"; + response.bodyOutputStream.write(message, message.length); + }, + "/duringDelayIShouldNotBeCalled": function(request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + let jsonMessage = "{\"working\": \"yes\"}"; + response.bodyOutputStream.write(jsonMessage, jsonMessage.length); + }, + }); + + let client = new FxAccountsClient(server.baseURI); + + // Retry-After header sets client.backoffError + do_check_eq(client.backoffError, null); + try { + yield client._request("/retryDelay", method); + } catch (e) { + do_check_eq(429, e.code); + do_check_eq(30, e.retryAfter); + do_check_neq(typeof(client.fxaBackoffTimer), "undefined"); + do_check_neq(client.backoffError, null); + } + // While delay is in effect, client short-circuits any requests + // and re-rejects with previous error. + try { + yield client._request("/duringDelayIShouldNotBeCalled", method); + throw new Error("I should not be reached"); + } catch (e) { + do_check_eq(e.retryAfter, 30); + do_check_eq(e.message, "Client has sent too many requests"); + do_check_neq(client.backoffError, null); + } + // Once timer fires, client nulls error out and HTTP calls work again. + client._clearBackoff(); + let result = yield client._request("/duringDelayIShouldNotBeCalled", method); + do_check_eq(client.backoffError, null); + do_check_eq(result.working, "yes"); + + yield deferredStop(server); +}); + add_task(function test_signUp() { let creationMessage = JSON.stringify({ uid: "uid", diff --git a/services/sync/tests/unit/test_browserid_identity.js b/services/sync/tests/unit/test_browserid_identity.js index 8125f0bd2d37..63d5a2012143 100644 --- a/services/sync/tests/unit/test_browserid_identity.js +++ b/services/sync/tests/unit/test_browserid_identity.js @@ -8,7 +8,7 @@ Cu.import("resource://services-sync/util.js"); Cu.import("resource://services-common/utils.js"); Cu.import("resource://services-crypto/utils.js"); Cu.import("resource://testing-common/services/sync/utils.js"); -Cu.import("resource://services-common/hawk.js"); +Cu.import("resource://services-common/hawkclient.js"); Cu.import("resource://gre/modules/FxAccounts.jsm"); Cu.import("resource://gre/modules/FxAccountsClient.jsm"); Cu.import("resource://gre/modules/FxAccountsCommon.js"); diff --git a/testing/mozbase/mozlog/mozlog/__init__.py b/testing/mozbase/mozlog/mozlog/__init__.py index 3314a73e6441..30d2026ed567 100644 --- a/testing/mozbase/mozlog/mozlog/__init__.py +++ b/testing/mozbase/mozlog/mozlog/__init__.py @@ -9,8 +9,7 @@ It simply wraps Python's logging_ module and adds a few convenience methods for logging test results and events. The structured submodule takes a different approach and implements a -JSON-based logging protocol designed for recording test results. -""" +JSON-based logging protocol designed for recording test results.""" from logger import * from loglistener import LogMessageServer @@ -22,3 +21,4 @@ except ImportError: # Structured logging doesn't work on python 2.6 which is still used on some # legacy test machines; https://bugzilla.mozilla.org/show_bug.cgi?id=864866 pass + diff --git a/testing/mozbase/mozlog/mozlog/structured/commandline.py b/testing/mozbase/mozlog/mozlog/structured/commandline.py index 8b5ac25e72e4..8db2719681c7 100644 --- a/testing/mozbase/mozlog/mozlog/structured/commandline.py +++ b/testing/mozbase/mozlog/mozlog/structured/commandline.py @@ -15,6 +15,7 @@ log_formatters = { 'xunit': (formatters.XUnitFormatter, "xUnit compatible XML"), 'html': (formatters.HTMLFormatter, "HTML report"), 'mach': (formatters.MachFormatter, "Uncolored mach-like output"), + 'mach_terminal': (formatters.MachTerminalFormatter, "Colored mach-like output for use in a tty"), } @@ -42,7 +43,7 @@ def add_logging_group(parser): "and takes a filename to write that format to, " "or '-' to write to stdout.") for name, (cls, help_str) in log_formatters.iteritems(): - group.add_argument("--log-" + name, type=log_file, + group.add_argument("--log-" + name, action="append", type=log_file, help=help_str) @@ -51,8 +52,8 @@ def setup_logging(suite, args, defaults): Configure a structuredlogger based on command line arguments. :param suite: The name of the testsuite being run - :param args: The Namespace object produced by argparse from parsing - command line arguments from a parser with logging arguments. + :param args: A dictionary of {argument_name:value} produced from + parsing the command line arguments for the application :param defaults: A dictionary of {formatter name: output stream} to apply when there is no logging supplied on the command line. @@ -62,14 +63,15 @@ def setup_logging(suite, args, defaults): prefix = "log_" found = False found_stdout_logger = False - for name, value in args.iteritems(): - if name.startswith(prefix) and value is not None: - found = True - if value == sys.stdout: - found_stdout_logger = True - formatter_cls = log_formatters[name[len(prefix):]][0] - logger.add_handler(handlers.StreamHandler(stream=value, - formatter=formatter_cls())) + for name, values in args.iteritems(): + if name.startswith(prefix) and values is not None: + for value in values: + found = True + if value == sys.stdout: + found_stdout_logger = True + formatter_cls = log_formatters[name[len(prefix):]][0] + logger.add_handler(handlers.StreamHandler(stream=value, + formatter=formatter_cls())) #If there is no user-specified logging, go with the default options if not found: diff --git a/testing/mozbase/mozlog/mozlog/structured/formatters/__init__.py b/testing/mozbase/mozlog/mozlog/structured/formatters/__init__.py index 2b298fa50f5c..63c3f9994473 100644 --- a/testing/mozbase/mozlog/mozlog/structured/formatters/__init__.py +++ b/testing/mozbase/mozlog/mozlog/structured/formatters/__init__.py @@ -8,4 +8,5 @@ from xunit import XUnitFormatter from html import HTMLFormatter from machformatter import MachFormatter, MachTerminalFormatter -JSONFormatter = lambda: json.dumps +def JSONFormatter(): + return lambda x: json.dumps(x) + "\n" diff --git a/testing/mozbase/mozlog/mozlog/structured/formatters/html/html.py b/testing/mozbase/mozlog/mozlog/structured/formatters/html/html.py index 34580993d368..f2186dce2f52 100755 --- a/testing/mozbase/mozlog/mozlog/structured/formatters/html/html.py +++ b/testing/mozbase/mozlog/mozlog/structured/formatters/html/html.py @@ -4,6 +4,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. import sys +import json import datetime import os diff --git a/testing/mozbase/mozlog/mozlog/structured/formatters/machformatter.py b/testing/mozbase/mozlog/mozlog/structured/formatters/machformatter.py index c556db5f96cf..d136ca9e4fec 100644 --- a/testing/mozbase/mozlog/mozlog/structured/formatters/machformatter.py +++ b/testing/mozbase/mozlog/mozlog/structured/formatters/machformatter.py @@ -29,7 +29,7 @@ class BaseMachFormatter(base.BaseFormatter): return "%s %s\n" % (self.generic_formatter(data), s) def _get_test_id(self, data): - test_id = data["test"] + test_id = data.get("test") if isinstance(test_id, list): test_id = tuple(test_id) return test_id @@ -62,12 +62,13 @@ class BaseMachFormatter(base.BaseFormatter): def test_status(self, data): test = self._get_test_id(data) if test not in self.status_buffer: - self.buffer[test] = {"count": 0, "unexpected": 0, "pass": 0} - self.buffer[test]["count"] += 1 + self.status_buffer[test] = {"count": 0, "unexpected": 0, "pass": 0} + self.status_buffer[test]["count"] += 1 + if "expected" in data: - self.buffer[test]["unexpected"] += 1 + self.status_buffer[test]["unexpected"] += 1 if data["status"] == "PASS": - self.buffer[test]["pass"] += 1 + self.status_buffer[test]["pass"] += 1 def process_output(self, data): return '"%s" (pid:%s command:%s)' % (data["data"], @@ -132,9 +133,9 @@ class MachTerminalFormatter(BaseMachFormatter): def __call__(self, data): s = BaseMachFormatter.__call__(self, data) if s is not None: - t = self.terminal.blue(format_seconds(self._time(entry))) + t = self.terminal.blue(format_seconds(self._time(data))) - return '%s %s' % (t, self._colorize(entry, s)) + return '%s %s' % (t, self._colorize(data, s)) def _colorize(self, data, s): if self.terminal is None: @@ -155,6 +156,8 @@ class MachTerminalFormatter(BaseMachFormatter): if color is not None: result = color(s[:len_action]) + s[len_action:] + else: + result = s return result diff --git a/testing/mozbase/mozlog/mozlog/structured/handlers/__init__.py b/testing/mozbase/mozlog/mozlog/structured/handlers/__init__.py index 74c11a05b9e8..7881575d5cd5 100644 --- a/testing/mozbase/mozlog/mozlog/structured/handlers/__init__.py +++ b/testing/mozbase/mozlog/mozlog/structured/handlers/__init__.py @@ -4,6 +4,8 @@ from threading import Lock +from ..structuredlog import log_levels + class BaseHandler(object): def __init__(self, formatter=str): diff --git a/testing/mozbase/mozlog/mozlog/structured/structuredlog.py b/testing/mozbase/mozlog/mozlog/structured/structuredlog.py index 6d6a9815b3c6..52d849729826 100644 --- a/testing/mozbase/mozlog/mozlog/structured/structuredlog.py +++ b/testing/mozbase/mozlog/mozlog/structured/structuredlog.py @@ -104,12 +104,15 @@ class StructuredLogger(object): all_data.update(data) return all_data - def suite_start(self, tests): + def suite_start(self, tests, run_info=None): """Log a suite_start message :param tests: List of test identifiers that will be run in the suite. """ - self._log_data("suite_start", {"tests": tests}) + data = {"tests": tests} + if run_info is not None: + data["run_info"] = run_info + self._log_data("suite_start", data) def suite_end(self): """Log a suite_end message""" @@ -171,8 +174,7 @@ class StructuredLogger(object): self._log_data("test_end", data) def process_output(self, process, data, command=None): - """ - Log output from a managed process. + """Log output from a managed process. :param process: A unique identifier for the process producing the output (typically the pid) diff --git a/toolkit/components/places/PlacesUtils.jsm b/toolkit/components/places/PlacesUtils.jsm index c4e8add93b4b..20dfc6d40c8e 100644 --- a/toolkit/components/places/PlacesUtils.jsm +++ b/toolkit/components/places/PlacesUtils.jsm @@ -1953,7 +1953,7 @@ TransactionItemCache.prototype = { get index() this._index != null ? this._index : PlacesUtils.bookmarks.DEFAULT_INDEX, set annotations(v) - this._annotations = Array.isArray(v) ? JSON.parse(JSON.stringify(v)) : null, + this._annotations = Array.isArray(v) ? Cu.cloneInto(v, {}) : null, get annotations() this._annotations || null, set tags(v) diff --git a/toolkit/devtools/Loader.jsm b/toolkit/devtools/Loader.jsm index 4b9531eda2f3..565df161ca90 100644 --- a/toolkit/devtools/Loader.jsm +++ b/toolkit/devtools/Loader.jsm @@ -58,6 +58,7 @@ BuiltinProvider.prototype = { "": "resource://gre/modules/commonjs/", "main": "resource:///modules/devtools/main.js", "devtools": "resource:///modules/devtools", + "devtools/toolkit": "resource://gre/modules/devtools", "devtools/server": "resource://gre/modules/devtools/server", "devtools/toolkit/webconsole": "resource://gre/modules/devtools/toolkit/webconsole", "devtools/app-actor-front": "resource://gre/modules/devtools/app-actor-front.js", @@ -106,6 +107,7 @@ SrcdirProvider.prototype = { let toolkitDir = OS.Path.join(srcdir, "toolkit", "devtools"); let mainURI = this.fileURI(OS.Path.join(devtoolsDir, "main.js")); let devtoolsURI = this.fileURI(devtoolsDir); + let toolkitURI = this.fileURI(toolkitDir); let serverURI = this.fileURI(OS.Path.join(toolkitDir, "server")); let webconsoleURI = this.fileURI(OS.Path.join(toolkitDir, "webconsole")); let appActorURI = this.fileURI(OS.Path.join(toolkitDir, "apps", "app-actor-front.js")); @@ -127,6 +129,7 @@ SrcdirProvider.prototype = { "": "resource://gre/modules/commonjs/", "main": mainURI, "devtools": devtoolsURI, + "devtools/toolkit": toolkitURI, "devtools/server": serverURI, "devtools/toolkit/webconsole": webconsoleURI, "devtools/app-actor-front": appActorURI, diff --git a/toolkit/devtools/apps/Devices.jsm b/toolkit/devtools/apps/Devices.jsm index 0b9f6fbc7128..dbd565e60211 100644 --- a/toolkit/devtools/apps/Devices.jsm +++ b/toolkit/devtools/apps/Devices.jsm @@ -4,7 +4,7 @@ "use strict"; -Components.utils.import("resource:///modules/devtools/shared/event-emitter.js"); +Components.utils.import("resource://gre/modules/devtools/event-emitter.js"); const EXPORTED_SYMBOLS = ["Devices"]; diff --git a/toolkit/devtools/apps/Simulator.jsm b/toolkit/devtools/apps/Simulator.jsm index f1fbefc32f43..d430599932d1 100644 --- a/toolkit/devtools/apps/Simulator.jsm +++ b/toolkit/devtools/apps/Simulator.jsm @@ -4,7 +4,7 @@ "use strict"; -Components.utils.import("resource:///modules/devtools/shared/event-emitter.js"); +Components.utils.import("resource://gre/modules/devtools/event-emitter.js"); const EXPORTED_SYMBOLS = ["Simulator"]; diff --git a/toolkit/devtools/client/connection-manager.js b/toolkit/devtools/client/connection-manager.js index a104f6e4db98..65f813e1e15d 100644 --- a/toolkit/devtools/client/connection-manager.js +++ b/toolkit/devtools/client/connection-manager.js @@ -8,7 +8,7 @@ const {Cc, Ci, Cu} = require("chrome"); const {setTimeout, clearTimeout} = require('sdk/timers'); -const EventEmitter = require("devtools/shared/event-emitter"); +const EventEmitter = require("devtools/toolkit/event-emitter"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/devtools/dbg-client.jsm"); diff --git a/toolkit/devtools/client/dbg-client.jsm b/toolkit/devtools/client/dbg-client.jsm index 8fb8078589c9..abf9a5ea1b14 100644 --- a/toolkit/devtools/client/dbg-client.jsm +++ b/toolkit/devtools/client/dbg-client.jsm @@ -479,11 +479,6 @@ DebuggerClient.prototype = { */ attachConsole: function (aConsoleActor, aListeners, aOnResponse) { - if (this._consoleClients.has(aConsoleActor)) { - setTimeout(() => aOnResponse({}, this._consoleClients.get(aConsoleActor)), 0); - return; - } - let packet = { to: aConsoleActor, type: "startListeners", @@ -493,8 +488,12 @@ DebuggerClient.prototype = { this.request(packet, (aResponse) => { let consoleClient; if (!aResponse.error) { - consoleClient = new WebConsoleClient(this, aConsoleActor); - this._consoleClients.set(aConsoleActor, consoleClient); + if (this._consoleClients.has(aConsoleActor)) { + consoleClient = this._consoleClients.get(aConsoleActor); + } else { + consoleClient = new WebConsoleClient(this, aConsoleActor); + this._consoleClients.set(aConsoleActor, consoleClient); + } } aOnResponse(aResponse, consoleClient); }); diff --git a/browser/devtools/shared/event-emitter.js b/toolkit/devtools/event-emitter.js similarity index 100% rename from browser/devtools/shared/event-emitter.js rename to toolkit/devtools/event-emitter.js diff --git a/toolkit/devtools/server/actors/webbrowser.js b/toolkit/devtools/server/actors/webbrowser.js index 651ab7c2fcf9..37318b63d553 100644 --- a/toolkit/devtools/server/actors/webbrowser.js +++ b/toolkit/devtools/server/actors/webbrowser.js @@ -1045,7 +1045,7 @@ BrowserAddonActor.prototype = { function DebuggerProgressListener(aBrowserTabActor) { this._tabActor = aBrowserTabActor; this._tabActor._tabbrowser.addProgressListener(this); - let EventEmitter = devtools.require("devtools/shared/event-emitter"); + let EventEmitter = devtools.require("devtools/toolkit/event-emitter"); EventEmitter.decorate(this); } diff --git a/toolkit/devtools/tests/mochitest/chrome.ini b/toolkit/devtools/tests/mochitest/chrome.ini index c6dccbfd5feb..e6c0a7b65cde 100644 --- a/toolkit/devtools/tests/mochitest/chrome.ini +++ b/toolkit/devtools/tests/mochitest/chrome.ini @@ -1 +1,2 @@ +[test_eventemitter_basic.html] [test_loader_paths.html] diff --git a/toolkit/devtools/tests/mochitest/test_eventemitter_basic.html b/toolkit/devtools/tests/mochitest/test_eventemitter_basic.html new file mode 100644 index 000000000000..e4e8752da462 --- /dev/null +++ b/toolkit/devtools/tests/mochitest/test_eventemitter_basic.html @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + diff --git a/toolkit/modules/TelemetryTimestamps.jsm b/toolkit/modules/TelemetryTimestamps.jsm index 7a17b877078a..4973e6c1f137 100644 --- a/toolkit/modules/TelemetryTimestamps.jsm +++ b/toolkit/modules/TelemetryTimestamps.jsm @@ -4,6 +4,8 @@ this.EXPORTED_SYMBOLS = ["TelemetryTimestamps"]; +const Cu = Components.utils; + /** * This module's purpose is to collect timestamps for important * application-specific events. @@ -46,7 +48,7 @@ this.TelemetryTimestamps = { * to attach to the telemetry submission. */ get: function TT_get() { - // Return a copy of the object by passing it through JSON. - return JSON.parse(JSON.stringify(timeStamps)); + // Return a copy of the object. + return Cu.cloneInto(timeStamps, {}); } }; diff --git a/widget/gonk/nativewindow/FakeSurfaceComposer.cpp b/widget/gonk/nativewindow/FakeSurfaceComposer.cpp index fddd6b933a34..82297ae9f2a6 100644 --- a/widget/gonk/nativewindow/FakeSurfaceComposer.cpp +++ b/widget/gonk/nativewindow/FakeSurfaceComposer.cpp @@ -59,6 +59,12 @@ sp FakeSurfaceComposer::createDisplay(const String8& displayName, return nullptr; } +#if ANDROID_VERSION >= 19 +void FakeSurfaceComposer::destroyDisplay(const sp& display) +{ +} +#endif + sp FakeSurfaceComposer::getBuiltInDisplay(int32_t id) { return nullptr; } @@ -91,6 +97,15 @@ status_t FakeSurfaceComposer::captureScreen(const sp& display, return INVALID_OPERATION; } +#if ANDROID_VERSION >= 19 +status_t FakeSurfaceComposer::captureScreen(const sp& display, + const sp& producer, + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ) { + return INVALID_OPERATION; +} +#endif + void FakeSurfaceComposer::blank(const sp& display) { } diff --git a/widget/gonk/nativewindow/FakeSurfaceComposer.h b/widget/gonk/nativewindow/FakeSurfaceComposer.h index 87534e2c12a2..ae47b5c0cb1d 100644 --- a/widget/gonk/nativewindow/FakeSurfaceComposer.h +++ b/widget/gonk/nativewindow/FakeSurfaceComposer.h @@ -45,6 +45,11 @@ public: // Instantiate MediaResourceManagerService and register to service manager. // If service manager is not present, wait until service manager becomes present. static void instantiate(); +#if ANDROID_VERSION >= 19 + virtual void destroyDisplay(const sp& display); + virtual status_t captureScreen(const sp& display, const sp& producer, + uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, uint32_t maxLayerZ); +#endif private: FakeSurfaceComposer(); diff --git a/widget/gonk/nativewindow/GonkBufferQueue.h b/widget/gonk/nativewindow/GonkBufferQueue.h index cc40a13cca7a..75cedbef12d3 100755 --- a/widget/gonk/nativewindow/GonkBufferQueue.h +++ b/widget/gonk/nativewindow/GonkBufferQueue.h @@ -1,12 +1,10 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * Copyright (C) 2013 Mozilla Foundation +/* Copyright 2013 Mozilla Foundation and Mozilla contributors * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -15,648 +13,8 @@ * limitations under the License. */ -#ifndef NATIVEWINDOW_GONKBUFFERQUEUE_H -#define NATIVEWINDOW_GONKBUFFERQUEUE_H - -#include -#if ANDROID_VERSION == 17 -#include -#else -#include +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19 +# include "GonkBufferQueueKK.h" +#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 +# include "GonkBufferQueueJB.h" #endif - -#include -#include - -#include -#include -#include - -#include "mozilla/layers/LayersSurfaces.h" - -#if ANDROID_VERSION == 17 -#define IGraphicBufferProducer ISurfaceTexture -#endif - -namespace android { -// ---------------------------------------------------------------------------- - -#if ANDROID_VERSION == 17 -class GonkBufferQueue : public BnSurfaceTexture { -#else -class GonkBufferQueue : public BnGraphicBufferProducer { -#endif - typedef mozilla::layers::SurfaceDescriptor SurfaceDescriptor; - -public: - enum { MIN_UNDEQUEUED_BUFFERS = 2 }; - enum { NUM_BUFFER_SLOTS = 32 }; - enum { NO_CONNECTED_API = 0 }; - enum { INVALID_BUFFER_SLOT = -1 }; - enum { STALE_BUFFER_SLOT = 1, NO_BUFFER_AVAILABLE }; - - // When in async mode we reserve two slots in order to guarantee that the - // producer and consumer can run asynchronously. - enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 }; - - // ConsumerListener is the interface through which the GonkBufferQueue notifies - // the consumer of events that the consumer may wish to react to. Because - // the consumer will generally have a mutex that is locked during calls from - // the consumer to the GonkBufferQueue, these calls from the GonkBufferQueue to the - // consumer *MUST* be called only when the GonkBufferQueue mutex is NOT locked. - struct ConsumerListener : public virtual RefBase { - // onFrameAvailable is called from queueBuffer each time an additional - // frame becomes available for consumption. This means that frames that - // are queued while in asynchronous mode only trigger the callback if no - // previous frames are pending. Frames queued while in synchronous mode - // always trigger the callback. - // - // This is called without any lock held and can be called concurrently - // by multiple threads. - virtual void onFrameAvailable() = 0; - - // onBuffersReleased is called to notify the buffer consumer that the - // GonkBufferQueue has released its references to one or more GraphicBuffers - // contained in its slots. The buffer consumer should then call - // GonkBufferQueue::getReleasedBuffers to retrieve the list of buffers - // - // This is called without any lock held and can be called concurrently - // by multiple threads. - virtual void onBuffersReleased() = 0; - }; - - // ProxyConsumerListener is a ConsumerListener implementation that keeps a weak - // reference to the actual consumer object. It forwards all calls to that - // consumer object so long as it exists. - // - // This class exists to avoid having a circular reference between the - // GonkBufferQueue object and the consumer object. The reason this can't be a weak - // reference in the GonkBufferQueue class is because we're planning to expose the - // consumer side of a GonkBufferQueue as a binder interface, which doesn't support - // weak references. - class ProxyConsumerListener : public GonkBufferQueue::ConsumerListener { - public: - - ProxyConsumerListener(const wp& consumerListener); - virtual ~ProxyConsumerListener(); - virtual void onFrameAvailable(); - virtual void onBuffersReleased(); - - private: - - // mConsumerListener is a weak reference to the ConsumerListener. This is - // the raison d'etre of ProxyConsumerListener. - wp mConsumerListener; - }; - - - // GonkBufferQueue manages a pool of gralloc memory slots to be used by - // producers and consumers. allowSynchronousMode specifies whether or not - // synchronous mode can be enabled by the producer. allocator is used to - // allocate all the needed gralloc buffers. - GonkBufferQueue(bool allowSynchronousMode = true, - const sp& allocator = NULL); - virtual ~GonkBufferQueue(); - - // Query native window attributes. The "what" values are enumerated in - // window.h (e.g. NATIVE_WINDOW_FORMAT). - virtual int query(int what, int* value); - - // setBufferCount updates the number of available buffer slots. If this - // method succeeds, buffer slots will be both unallocated and owned by - // the GonkBufferQueue object (i.e. they are not owned by the producer or - // consumer). - // - // This will fail if the producer has dequeued any buffers, or if - // bufferCount is invalid. bufferCount must generally be a value - // between the minimum undequeued buffer count and NUM_BUFFER_SLOTS - // (inclusive). It may also be set to zero (the default) to indicate - // that the producer does not wish to set a value. The minimum value - // can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - // ...). - // - // This may only be called by the producer. The consumer will be told - // to discard buffers through the onBuffersReleased callback. - virtual status_t setBufferCount(int bufferCount); - - // requestBuffer returns the GraphicBuffer for slot N. - // - // In normal operation, this is called the first time slot N is returned - // by dequeueBuffer. It must be called again if dequeueBuffer returns - // flags indicating that previously-returned buffers are no longer valid. - virtual status_t requestBuffer(int slot, sp* buf); - - // dequeueBuffer gets the next buffer slot index for the producer to use. - // If a buffer slot is available then that slot index is written to the - // location pointed to by the buf argument and a status of OK is returned. - // If no slot is available then a status of -EBUSY is returned and buf is - // unmodified. - // - // The fence parameter will be updated to hold the fence associated with - // the buffer. The contents of the buffer must not be overwritten until the - // fence signals. If the fence is Fence::NO_FENCE, the buffer may be - // written immediately. - // - // The width and height parameters must be no greater than the minimum of - // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). - // An error due to invalid dimensions might not be reported until - // updateTexImage() is called. If width and height are both zero, the - // default values specified by setDefaultBufferSize() are used instead. - // - // The pixel formats are enumerated in graphics.h, e.g. - // HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format - // will be used. - // - // The usage argument specifies gralloc buffer usage flags. The values - // are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These - // will be merged with the usage flags specified by setConsumerUsageBits. - // - // The return value may be a negative error value or a non-negative - // collection of flags. If the flags are set, the return values are - // valid, but additional actions must be performed. - // - // If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the - // producer must discard cached GraphicBuffer references for the slot - // returned in buf. - // If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer - // must discard cached GraphicBuffer references for all slots. - // - // In both cases, the producer will need to call requestBuffer to get a - // GraphicBuffer handle for the returned slot. -#if ANDROID_VERSION == 17 - virtual status_t dequeueBuffer(int *buf, sp& fence, - uint32_t width, uint32_t height, uint32_t format, uint32_t usage) { - return dequeueBuffer(buf, &fence, width, height, format, usage); - } -#endif - - virtual status_t dequeueBuffer(int *buf, sp* fence, - uint32_t width, uint32_t height, uint32_t format, uint32_t usage); - - // queueBuffer returns a filled buffer to the GonkBufferQueue. - // - // Additional data is provided in the QueueBufferInput struct. Notably, - // a timestamp must be provided for the buffer. The timestamp is in - // nanoseconds, and must be monotonically increasing. Its other semantics - // (zero point, etc) are producer-specific and should be documented by the - // producer. - // - // The caller may provide a fence that signals when all rendering - // operations have completed. Alternatively, NO_FENCE may be used, - // indicating that the buffer is ready immediately. - // - // Some values are returned in the output struct: the current settings - // for default width and height, the current transform hint, and the - // number of queued buffers. - virtual status_t queueBuffer(int buf, - const QueueBufferInput& input, QueueBufferOutput* output); - - // cancelBuffer returns a dequeued buffer to the GonkBufferQueue, but doesn't - // queue it for use by the consumer. - // - // The buffer will not be overwritten until the fence signals. The fence - // will usually be the one obtained from dequeueBuffer. -#if ANDROID_VERSION == 17 - virtual void cancelBuffer(int buf, sp fence); -#else - virtual void cancelBuffer(int buf, const sp& fence); -#endif - - // setSynchronousMode sets whether dequeueBuffer is synchronous or - // asynchronous. In synchronous mode, dequeueBuffer blocks until - // a buffer is available, the currently bound buffer can be dequeued and - // queued buffers will be acquired in order. In asynchronous mode, - // a queued buffer may be replaced by a subsequently queued buffer. - // - // The default mode is asynchronous. - virtual status_t setSynchronousMode(bool enabled); - - // connect attempts to connect a producer API to the GonkBufferQueue. This - // must be called before any other IGraphicBufferProducer methods are - // called except for getAllocator. A consumer must already be connected. - // - // This method will fail if connect was previously called on the - // GonkBufferQueue and no corresponding disconnect call was made (i.e. if - // it's still connected to a producer). - // - // APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU). - virtual status_t connect(int api, QueueBufferOutput* output); - - // disconnect attempts to disconnect a producer API from the GonkBufferQueue. - // Calling this method will cause any subsequent calls to other - // IGraphicBufferProducer methods to fail except for getAllocator and connect. - // Successfully calling connect after this will allow the other methods to - // succeed again. - // - // This method will fail if the the GonkBufferQueue is not currently - // connected to the specified producer API. - virtual status_t disconnect(int api); - - // dump our state in a String - virtual void dump(String8& result) const; - virtual void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const; - - // public facing structure for BufferSlot - struct BufferItem { - - BufferItem() - : - mSurfaceDescriptor(SurfaceDescriptor()), - mTransform(0), - mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mTimestamp(0), - mFrameNumber(0), - mBuf(INVALID_BUFFER_SLOT) { - mCrop.makeInvalid(); - } - // mGraphicBuffer points to the buffer allocated for this slot, or is NULL - // if the buffer in this slot has been acquired in the past (see - // BufferSlot.mAcquireCalled). - sp mGraphicBuffer; - - // mSurfaceDescriptor is the token to remotely allocated GraphicBuffer. - SurfaceDescriptor mSurfaceDescriptor; - - // mCrop is the current crop rectangle for this buffer slot. - Rect mCrop; - - // mTransform is the current transform flags for this buffer slot. - uint32_t mTransform; - - // mScalingMode is the current scaling mode for this buffer slot. - uint32_t mScalingMode; - - // mTimestamp is the current timestamp for this buffer slot. This gets - // to set by queueBuffer each time this slot is queued. - int64_t mTimestamp; - - // mFrameNumber is the number of the queued frame for this slot. - uint64_t mFrameNumber; - - // mBuf is the slot index of this buffer - int mBuf; - - // mFence is a fence that will signal when the buffer is idle. - sp mFence; - }; - - // The following public functions are the consumer-facing interface - - // acquireBuffer attempts to acquire ownership of the next pending buffer in - // the GonkBufferQueue. If no buffer is pending then it returns -EINVAL. If a - // buffer is successfully acquired, the information about the buffer is - // returned in BufferItem. If the buffer returned had previously been - // acquired then the BufferItem::mGraphicBuffer field of buffer is set to - // NULL and it is assumed that the consumer still holds a reference to the - // buffer. - status_t acquireBuffer(BufferItem *buffer); - - // releaseBuffer releases a buffer slot from the consumer back to the - // GonkBufferQueue. This may be done while the buffer's contents are still - // being accessed. The fence will signal when the buffer is no longer - // in use. - // - // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free - // any references to the just-released buffer that it might have, as if it - // had received a onBuffersReleased() call with a mask set for the released - // buffer. - // - // Note that the dependencies on EGL will be removed once we switch to using - // the Android HW Sync HAL. - status_t releaseBuffer(int buf, const sp& releaseFence); - - // consumerConnect connects a consumer to the GonkBufferQueue. Only one - // consumer may be connected, and when that consumer disconnects the - // GonkBufferQueue is placed into the "abandoned" state, causing most - // interactions with the GonkBufferQueue by the producer to fail. - // - // consumer may not be NULL. - status_t consumerConnect(const sp& consumer); - - // consumerDisconnect disconnects a consumer from the GonkBufferQueue. All - // buffers will be freed and the GonkBufferQueue is placed in the "abandoned" - // state, causing most interactions with the GonkBufferQueue by the producer to - // fail. - status_t consumerDisconnect(); - - // getReleasedBuffers sets the value pointed to by slotMask to a bit mask - // indicating which buffer slots have been released by the GonkBufferQueue - // but have not yet been released by the consumer. - // - // This should be called from the onBuffersReleased() callback. - status_t getReleasedBuffers(uint32_t* slotMask); - - // setDefaultBufferSize is used to set the size of buffers returned by - // dequeueBuffer when a width and height of zero is requested. Default - // is 1x1. - status_t setDefaultBufferSize(uint32_t w, uint32_t h); - - // setDefaultMaxBufferCount sets the default value for the maximum buffer - // count (the initial default is 2). If the producer has requested a - // buffer count using setBufferCount, the default buffer count will only - // take effect if the producer sets the count back to zero. - // - // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. - status_t setDefaultMaxBufferCount(int bufferCount); - - // setMaxAcquiredBufferCount sets the maximum number of buffers that can - // be acquired by the consumer at one time (default 1). This call will - // fail if a producer is connected to the GonkBufferQueue. - status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); - - // isSynchronousMode returns whether the GonkBufferQueue is currently in - // synchronous mode. - bool isSynchronousMode() const; - - // setConsumerName sets the name used in logging - void setConsumerName(const String8& name); - - // setDefaultBufferFormat allows the GonkBufferQueue to create - // GraphicBuffers of a defaultFormat if no format is specified - // in dequeueBuffer. Formats are enumerated in graphics.h; the - // initial default is HAL_PIXEL_FORMAT_RGBA_8888. - status_t setDefaultBufferFormat(uint32_t defaultFormat); - - // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. - // These are merged with the bits passed to dequeueBuffer. The values are - // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0. - status_t setConsumerUsageBits(uint32_t usage); - - // setTransformHint bakes in rotation to buffers so overlays can be used. - // The values are enumerated in window.h, e.g. - // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform). - status_t setTransformHint(uint32_t hint); - - int getGeneration(); - - SurfaceDescriptor *getSurfaceDescriptorFromBuffer(ANativeWindowBuffer* buffer); - -private: - // releaseBufferFreeListUnlocked releases the resources in the freeList; - // this must be called with mMutex unlocked. - void releaseBufferFreeListUnlocked(nsTArray& freeList); - - // freeBufferLocked frees the GraphicBuffer and sync resources for the - // given slot. - //void freeBufferLocked(int index); - - // freeAllBuffersLocked frees the GraphicBuffer and sync resources for - // all slots. - //void freeAllBuffersLocked(); - void freeAllBuffersLocked(nsTArray& freeList); - - // setDefaultMaxBufferCountLocked sets the maximum number of buffer slots - // that will be used if the producer does not override the buffer slot - // count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. - // The initial default is 2. - status_t setDefaultMaxBufferCountLocked(int count); - - // getMinBufferCountLocked returns the minimum number of buffers allowed - // given the current GonkBufferQueue state. - int getMinMaxBufferCountLocked() const; - - // getMinUndequeuedBufferCountLocked returns the minimum number of buffers - // that must remain in a state other than DEQUEUED. - int getMinUndequeuedBufferCountLocked() const; - - // getMaxBufferCountLocked returns the maximum number of buffers that can - // be allocated at once. This value depends upon the following member - // variables: - // - // mSynchronousMode - // mMaxAcquiredBufferCount - // mDefaultMaxBufferCount - // mOverrideMaxBufferCount - // - // Any time one of these member variables is changed while a producer is - // connected, mDequeueCondition must be broadcast. - int getMaxBufferCountLocked() const; - - struct BufferSlot { - - BufferSlot() - : mSurfaceDescriptor(SurfaceDescriptor()), - mBufferState(BufferSlot::FREE), - mRequestBufferCalled(false), - mTransform(0), - mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mTimestamp(0), - mFrameNumber(0), - mAcquireCalled(false), - mNeedsCleanupOnRelease(false) { - mCrop.makeInvalid(); - } - - // mGraphicBuffer points to the buffer allocated for this slot or is NULL - // if no buffer has been allocated. - sp mGraphicBuffer; - - // mSurfaceDescriptor is the token to remotely allocated GraphicBuffer. - SurfaceDescriptor mSurfaceDescriptor; - - // BufferState represents the different states in which a buffer slot - // can be. All slots are initially FREE. - enum BufferState { - // FREE indicates that the buffer is available to be dequeued - // by the producer. The buffer may be in use by the consumer for - // a finite time, so the buffer must not be modified until the - // associated fence is signaled. - // - // The slot is "owned" by GonkBufferQueue. It transitions to DEQUEUED - // when dequeueBuffer is called. - FREE = 0, - - // DEQUEUED indicates that the buffer has been dequeued by the - // producer, but has not yet been queued or canceled. The - // producer may modify the buffer's contents as soon as the - // associated ready fence is signaled. - // - // The slot is "owned" by the producer. It can transition to - // QUEUED (via queueBuffer) or back to FREE (via cancelBuffer). - DEQUEUED = 1, - - // QUEUED indicates that the buffer has been filled by the - // producer and queued for use by the consumer. The buffer - // contents may continue to be modified for a finite time, so - // the contents must not be accessed until the associated fence - // is signaled. - // - // The slot is "owned" by GonkBufferQueue. It can transition to - // ACQUIRED (via acquireBuffer) or to FREE (if another buffer is - // queued in asynchronous mode). - QUEUED = 2, - - // ACQUIRED indicates that the buffer has been acquired by the - // consumer. As with QUEUED, the contents must not be accessed - // by the consumer until the fence is signaled. - // - // The slot is "owned" by the consumer. It transitions to FREE - // when releaseBuffer is called. - ACQUIRED = 3 - }; - - // mBufferState is the current state of this buffer slot. - BufferState mBufferState; - - // mRequestBufferCalled is used for validating that the producer did - // call requestBuffer() when told to do so. Technically this is not - // needed but useful for debugging and catching producer bugs. - bool mRequestBufferCalled; - - // mCrop is the current crop rectangle for this buffer slot. - Rect mCrop; - - // mTransform is the current transform flags for this buffer slot. - // (example: NATIVE_WINDOW_TRANSFORM_ROT_90) - uint32_t mTransform; - - // mScalingMode is the current scaling mode for this buffer slot. - // (example: NATIVE_WINDOW_SCALING_MODE_FREEZE) - uint32_t mScalingMode; - - // mTimestamp is the current timestamp for this buffer slot. This gets - // to set by queueBuffer each time this slot is queued. - int64_t mTimestamp; - - // mFrameNumber is the number of the queued frame for this slot. This - // is used to dequeue buffers in LRU order (useful because buffers - // may be released before their release fence is signaled). - uint64_t mFrameNumber; - - // mEglFence is the EGL sync object that must signal before the buffer - // associated with this buffer slot may be dequeued. It is initialized - // to EGL_NO_SYNC_KHR when the buffer is created and may be set to a - // new sync object in releaseBuffer. (This is deprecated in favor of - // mFence, below.) - //EGLSyncKHR mEglFence; - - // mFence is a fence which will signal when work initiated by the - // previous owner of the buffer is finished. When the buffer is FREE, - // the fence indicates when the consumer has finished reading - // from the buffer, or when the producer has finished writing if it - // called cancelBuffer after queueing some writes. When the buffer is - // QUEUED, it indicates when the producer has finished filling the - // buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been - // passed to the consumer or producer along with ownership of the - // buffer, and mFence is set to NO_FENCE. - sp mFence; - - // Indicates whether this buffer has been seen by a consumer yet - bool mAcquireCalled; - - // Indicates whether this buffer needs to be cleaned up by the - // consumer. This is set when a buffer in ACQUIRED state is freed. - // It causes releaseBuffer to return STALE_BUFFER_SLOT. - bool mNeedsCleanupOnRelease; - }; - - // mSlots is the array of buffer slots that must be mirrored on the - // producer side. This allows buffer ownership to be transferred between - // the producer and consumer without sending a GraphicBuffer over binder. - // The entire array is initialized to NULL at construction time, and - // buffers are allocated for a slot when requestBuffer is called with - // that slot's index. - BufferSlot mSlots[NUM_BUFFER_SLOTS]; - - // mDefaultWidth holds the default width of allocated buffers. It is used - // in dequeueBuffer() if a width and height of zero is specified. - uint32_t mDefaultWidth; - - // mDefaultHeight holds the default height of allocated buffers. It is used - // in dequeueBuffer() if a width and height of zero is specified. - uint32_t mDefaultHeight; - - // mMaxAcquiredBufferCount is the number of buffers that the consumer may - // acquire at one time. It defaults to 1 and can be changed by the - // consumer via the setMaxAcquiredBufferCount method, but this may only be - // done when no producer is connected to the GonkBufferQueue. - // - // This value is used to derive the value returned for the - // MIN_UNDEQUEUED_BUFFERS query by the producer. - int mMaxAcquiredBufferCount; - - // mDefaultMaxBufferCount is the default limit on the number of buffers - // that will be allocated at one time. This default limit is set by the - // consumer. The limit (as opposed to the default limit) may be - // overridden by the producer. - int mDefaultMaxBufferCount; - - // mOverrideMaxBufferCount is the limit on the number of buffers that will - // be allocated at one time. This value is set by the image producer by - // calling setBufferCount. The default is zero, which means the producer - // doesn't care about the number of buffers in the pool. In that case - // mDefaultMaxBufferCount is used as the limit. - int mOverrideMaxBufferCount; - - // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to - // allocate new GraphicBuffer objects. - sp mGraphicBufferAlloc; - - // mConsumerListener is used to notify the connected consumer of - // asynchronous events that it may wish to react to. It is initially set - // to NULL and is written by consumerConnect and consumerDisconnect. - sp mConsumerListener; - - // mSynchronousMode whether we're in synchronous mode or not - bool mSynchronousMode; - - // mAllowSynchronousMode whether we allow synchronous mode or not. Set - // when the GonkBufferQueue is created (by the consumer). - const bool mAllowSynchronousMode; - - // mConnectedApi indicates the producer API that is currently connected - // to this GonkBufferQueue. It defaults to NO_CONNECTED_API (= 0), and gets - // updated by the connect and disconnect methods. - int mConnectedApi; - - // mDequeueCondition condition used for dequeueBuffer in synchronous mode - mutable Condition mDequeueCondition; - - // mQueue is a FIFO of queued buffers used in synchronous mode - typedef Vector Fifo; - Fifo mQueue; - - // mAbandoned indicates that the GonkBufferQueue will no longer be used to - // consume image buffers pushed to it using the IGraphicBufferProducer - // interface. It is initialized to false, and set to true in the - // consumerDisconnect method. A GonkBufferQueue that has been abandoned will - // return the NO_INIT error from all IGraphicBufferProducer methods - // capable of returning an error. - bool mAbandoned; - - // mConsumerName is a string used to identify the GonkBufferQueue in log - // messages. It is set by the setConsumerName method. - String8 mConsumerName; - - // mMutex is the mutex used to prevent concurrent access to the member - // variables of GonkBufferQueue objects. It must be locked whenever the - // member variables are accessed. - mutable Mutex mMutex; - - // mFrameCounter is the free running counter, incremented on every - // successful queueBuffer call. - uint64_t mFrameCounter; - - // mBufferHasBeenQueued is true once a buffer has been queued. It is - // reset when something causes all buffers to be freed (e.g. changing the - // buffer count). - bool mBufferHasBeenQueued; - - // mDefaultBufferFormat can be set so it will override - // the buffer format when it isn't specified in dequeueBuffer - uint32_t mDefaultBufferFormat; - - // mConsumerUsageBits contains flags the consumer wants for GraphicBuffers - uint32_t mConsumerUsageBits; - - // mTransformHint is used to optimize for screen rotations - uint32_t mTransformHint; - - // mGeneration is the current generation of buffer slots - uint32_t mGeneration; -}; - -// ---------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_GUI_BUFFERQUEUE_H diff --git a/widget/gonk/nativewindow/GonkBufferQueue.cpp b/widget/gonk/nativewindow/GonkBufferQueueJB.cpp similarity index 99% rename from widget/gonk/nativewindow/GonkBufferQueue.cpp rename to widget/gonk/nativewindow/GonkBufferQueueJB.cpp index 865cb66d2afa..7dd73f78ada3 100755 --- a/widget/gonk/nativewindow/GonkBufferQueue.cpp +++ b/widget/gonk/nativewindow/GonkBufferQueueJB.cpp @@ -26,7 +26,7 @@ #include "mozilla/layers/ImageBridgeChild.h" -#include "GonkBufferQueue.h" +#include "GonkBufferQueueJB.h" // Macros for including the GonkBufferQueue name in log messages #define ST_LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) diff --git a/widget/gonk/nativewindow/GonkBufferQueueJB.h b/widget/gonk/nativewindow/GonkBufferQueueJB.h new file mode 100755 index 000000000000..2db78f2eb979 --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueJB.h @@ -0,0 +1,662 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NATIVEWINDOW_GONKBUFFERQUEUE_JB_H +#define NATIVEWINDOW_GONKBUFFERQUEUE_JB_H + +#include +#if ANDROID_VERSION == 17 +#include +#else +#include +#endif + +#include +#include + +#include +#include +#include + +#include "mozilla/layers/LayersSurfaces.h" + +#if ANDROID_VERSION == 17 +#define IGraphicBufferProducer ISurfaceTexture +#endif + +namespace android { +// ---------------------------------------------------------------------------- + +#if ANDROID_VERSION == 17 +class GonkBufferQueue : public BnSurfaceTexture { +#else +class GonkBufferQueue : public BnGraphicBufferProducer { +#endif + typedef mozilla::layers::SurfaceDescriptor SurfaceDescriptor; + +public: + enum { MIN_UNDEQUEUED_BUFFERS = 2 }; + enum { NUM_BUFFER_SLOTS = 32 }; + enum { NO_CONNECTED_API = 0 }; + enum { INVALID_BUFFER_SLOT = -1 }; + enum { STALE_BUFFER_SLOT = 1, NO_BUFFER_AVAILABLE }; + + // When in async mode we reserve two slots in order to guarantee that the + // producer and consumer can run asynchronously. + enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 }; + + // ConsumerListener is the interface through which the GonkBufferQueue notifies + // the consumer of events that the consumer may wish to react to. Because + // the consumer will generally have a mutex that is locked during calls from + // the consumer to the GonkBufferQueue, these calls from the GonkBufferQueue to the + // consumer *MUST* be called only when the GonkBufferQueue mutex is NOT locked. + struct ConsumerListener : public virtual RefBase { + // onFrameAvailable is called from queueBuffer each time an additional + // frame becomes available for consumption. This means that frames that + // are queued while in asynchronous mode only trigger the callback if no + // previous frames are pending. Frames queued while in synchronous mode + // always trigger the callback. + // + // This is called without any lock held and can be called concurrently + // by multiple threads. + virtual void onFrameAvailable() = 0; + + // onBuffersReleased is called to notify the buffer consumer that the + // GonkBufferQueue has released its references to one or more GraphicBuffers + // contained in its slots. The buffer consumer should then call + // GonkBufferQueue::getReleasedBuffers to retrieve the list of buffers + // + // This is called without any lock held and can be called concurrently + // by multiple threads. + virtual void onBuffersReleased() = 0; + }; + + // ProxyConsumerListener is a ConsumerListener implementation that keeps a weak + // reference to the actual consumer object. It forwards all calls to that + // consumer object so long as it exists. + // + // This class exists to avoid having a circular reference between the + // GonkBufferQueue object and the consumer object. The reason this can't be a weak + // reference in the GonkBufferQueue class is because we're planning to expose the + // consumer side of a GonkBufferQueue as a binder interface, which doesn't support + // weak references. + class ProxyConsumerListener : public GonkBufferQueue::ConsumerListener { + public: + + ProxyConsumerListener(const wp& consumerListener); + virtual ~ProxyConsumerListener(); + virtual void onFrameAvailable(); + virtual void onBuffersReleased(); + + private: + + // mConsumerListener is a weak reference to the ConsumerListener. This is + // the raison d'etre of ProxyConsumerListener. + wp mConsumerListener; + }; + + + // GonkBufferQueue manages a pool of gralloc memory slots to be used by + // producers and consumers. allowSynchronousMode specifies whether or not + // synchronous mode can be enabled by the producer. allocator is used to + // allocate all the needed gralloc buffers. + GonkBufferQueue(bool allowSynchronousMode = true, + const sp& allocator = NULL); + virtual ~GonkBufferQueue(); + + // Query native window attributes. The "what" values are enumerated in + // window.h (e.g. NATIVE_WINDOW_FORMAT). + virtual int query(int what, int* value); + + // setBufferCount updates the number of available buffer slots. If this + // method succeeds, buffer slots will be both unallocated and owned by + // the GonkBufferQueue object (i.e. they are not owned by the producer or + // consumer). + // + // This will fail if the producer has dequeued any buffers, or if + // bufferCount is invalid. bufferCount must generally be a value + // between the minimum undequeued buffer count and NUM_BUFFER_SLOTS + // (inclusive). It may also be set to zero (the default) to indicate + // that the producer does not wish to set a value. The minimum value + // can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + // ...). + // + // This may only be called by the producer. The consumer will be told + // to discard buffers through the onBuffersReleased callback. + virtual status_t setBufferCount(int bufferCount); + + // requestBuffer returns the GraphicBuffer for slot N. + // + // In normal operation, this is called the first time slot N is returned + // by dequeueBuffer. It must be called again if dequeueBuffer returns + // flags indicating that previously-returned buffers are no longer valid. + virtual status_t requestBuffer(int slot, sp* buf); + + // dequeueBuffer gets the next buffer slot index for the producer to use. + // If a buffer slot is available then that slot index is written to the + // location pointed to by the buf argument and a status of OK is returned. + // If no slot is available then a status of -EBUSY is returned and buf is + // unmodified. + // + // The fence parameter will be updated to hold the fence associated with + // the buffer. The contents of the buffer must not be overwritten until the + // fence signals. If the fence is Fence::NO_FENCE, the buffer may be + // written immediately. + // + // The width and height parameters must be no greater than the minimum of + // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). + // An error due to invalid dimensions might not be reported until + // updateTexImage() is called. If width and height are both zero, the + // default values specified by setDefaultBufferSize() are used instead. + // + // The pixel formats are enumerated in graphics.h, e.g. + // HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format + // will be used. + // + // The usage argument specifies gralloc buffer usage flags. The values + // are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These + // will be merged with the usage flags specified by setConsumerUsageBits. + // + // The return value may be a negative error value or a non-negative + // collection of flags. If the flags are set, the return values are + // valid, but additional actions must be performed. + // + // If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the + // producer must discard cached GraphicBuffer references for the slot + // returned in buf. + // If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer + // must discard cached GraphicBuffer references for all slots. + // + // In both cases, the producer will need to call requestBuffer to get a + // GraphicBuffer handle for the returned slot. +#if ANDROID_VERSION == 17 + virtual status_t dequeueBuffer(int *buf, sp& fence, + uint32_t width, uint32_t height, uint32_t format, uint32_t usage) { + return dequeueBuffer(buf, &fence, width, height, format, usage); + } +#endif + + virtual status_t dequeueBuffer(int *buf, sp* fence, + uint32_t width, uint32_t height, uint32_t format, uint32_t usage); + + // queueBuffer returns a filled buffer to the GonkBufferQueue. + // + // Additional data is provided in the QueueBufferInput struct. Notably, + // a timestamp must be provided for the buffer. The timestamp is in + // nanoseconds, and must be monotonically increasing. Its other semantics + // (zero point, etc) are producer-specific and should be documented by the + // producer. + // + // The caller may provide a fence that signals when all rendering + // operations have completed. Alternatively, NO_FENCE may be used, + // indicating that the buffer is ready immediately. + // + // Some values are returned in the output struct: the current settings + // for default width and height, the current transform hint, and the + // number of queued buffers. + virtual status_t queueBuffer(int buf, + const QueueBufferInput& input, QueueBufferOutput* output); + + // cancelBuffer returns a dequeued buffer to the GonkBufferQueue, but doesn't + // queue it for use by the consumer. + // + // The buffer will not be overwritten until the fence signals. The fence + // will usually be the one obtained from dequeueBuffer. +#if ANDROID_VERSION == 17 + virtual void cancelBuffer(int buf, sp fence); +#else + virtual void cancelBuffer(int buf, const sp& fence); +#endif + + // setSynchronousMode sets whether dequeueBuffer is synchronous or + // asynchronous. In synchronous mode, dequeueBuffer blocks until + // a buffer is available, the currently bound buffer can be dequeued and + // queued buffers will be acquired in order. In asynchronous mode, + // a queued buffer may be replaced by a subsequently queued buffer. + // + // The default mode is asynchronous. + virtual status_t setSynchronousMode(bool enabled); + + // connect attempts to connect a producer API to the GonkBufferQueue. This + // must be called before any other IGraphicBufferProducer methods are + // called except for getAllocator. A consumer must already be connected. + // + // This method will fail if connect was previously called on the + // GonkBufferQueue and no corresponding disconnect call was made (i.e. if + // it's still connected to a producer). + // + // APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU). + virtual status_t connect(int api, QueueBufferOutput* output); + + // disconnect attempts to disconnect a producer API from the GonkBufferQueue. + // Calling this method will cause any subsequent calls to other + // IGraphicBufferProducer methods to fail except for getAllocator and connect. + // Successfully calling connect after this will allow the other methods to + // succeed again. + // + // This method will fail if the the GonkBufferQueue is not currently + // connected to the specified producer API. + virtual status_t disconnect(int api); + + // dump our state in a String + virtual void dump(String8& result) const; + virtual void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const; + + // public facing structure for BufferSlot + struct BufferItem { + + BufferItem() + : + mSurfaceDescriptor(SurfaceDescriptor()), + mTransform(0), + mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mTimestamp(0), + mFrameNumber(0), + mBuf(INVALID_BUFFER_SLOT) { + mCrop.makeInvalid(); + } + // mGraphicBuffer points to the buffer allocated for this slot, or is NULL + // if the buffer in this slot has been acquired in the past (see + // BufferSlot.mAcquireCalled). + sp mGraphicBuffer; + + // mSurfaceDescriptor is the token to remotely allocated GraphicBuffer. + SurfaceDescriptor mSurfaceDescriptor; + + // mCrop is the current crop rectangle for this buffer slot. + Rect mCrop; + + // mTransform is the current transform flags for this buffer slot. + uint32_t mTransform; + + // mScalingMode is the current scaling mode for this buffer slot. + uint32_t mScalingMode; + + // mTimestamp is the current timestamp for this buffer slot. This gets + // to set by queueBuffer each time this slot is queued. + int64_t mTimestamp; + + // mFrameNumber is the number of the queued frame for this slot. + uint64_t mFrameNumber; + + // mBuf is the slot index of this buffer + int mBuf; + + // mFence is a fence that will signal when the buffer is idle. + sp mFence; + }; + + // The following public functions are the consumer-facing interface + + // acquireBuffer attempts to acquire ownership of the next pending buffer in + // the GonkBufferQueue. If no buffer is pending then it returns -EINVAL. If a + // buffer is successfully acquired, the information about the buffer is + // returned in BufferItem. If the buffer returned had previously been + // acquired then the BufferItem::mGraphicBuffer field of buffer is set to + // NULL and it is assumed that the consumer still holds a reference to the + // buffer. + status_t acquireBuffer(BufferItem *buffer); + + // releaseBuffer releases a buffer slot from the consumer back to the + // GonkBufferQueue. This may be done while the buffer's contents are still + // being accessed. The fence will signal when the buffer is no longer + // in use. + // + // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free + // any references to the just-released buffer that it might have, as if it + // had received a onBuffersReleased() call with a mask set for the released + // buffer. + // + // Note that the dependencies on EGL will be removed once we switch to using + // the Android HW Sync HAL. + status_t releaseBuffer(int buf, const sp& releaseFence); + + // consumerConnect connects a consumer to the GonkBufferQueue. Only one + // consumer may be connected, and when that consumer disconnects the + // GonkBufferQueue is placed into the "abandoned" state, causing most + // interactions with the GonkBufferQueue by the producer to fail. + // + // consumer may not be NULL. + status_t consumerConnect(const sp& consumer); + + // consumerDisconnect disconnects a consumer from the GonkBufferQueue. All + // buffers will be freed and the GonkBufferQueue is placed in the "abandoned" + // state, causing most interactions with the GonkBufferQueue by the producer to + // fail. + status_t consumerDisconnect(); + + // getReleasedBuffers sets the value pointed to by slotMask to a bit mask + // indicating which buffer slots have been released by the GonkBufferQueue + // but have not yet been released by the consumer. + // + // This should be called from the onBuffersReleased() callback. + status_t getReleasedBuffers(uint32_t* slotMask); + + // setDefaultBufferSize is used to set the size of buffers returned by + // dequeueBuffer when a width and height of zero is requested. Default + // is 1x1. + status_t setDefaultBufferSize(uint32_t w, uint32_t h); + + // setDefaultMaxBufferCount sets the default value for the maximum buffer + // count (the initial default is 2). If the producer has requested a + // buffer count using setBufferCount, the default buffer count will only + // take effect if the producer sets the count back to zero. + // + // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. + status_t setDefaultMaxBufferCount(int bufferCount); + + // setMaxAcquiredBufferCount sets the maximum number of buffers that can + // be acquired by the consumer at one time (default 1). This call will + // fail if a producer is connected to the GonkBufferQueue. + status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); + + // isSynchronousMode returns whether the GonkBufferQueue is currently in + // synchronous mode. + bool isSynchronousMode() const; + + // setConsumerName sets the name used in logging + void setConsumerName(const String8& name); + + // setDefaultBufferFormat allows the GonkBufferQueue to create + // GraphicBuffers of a defaultFormat if no format is specified + // in dequeueBuffer. Formats are enumerated in graphics.h; the + // initial default is HAL_PIXEL_FORMAT_RGBA_8888. + status_t setDefaultBufferFormat(uint32_t defaultFormat); + + // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. + // These are merged with the bits passed to dequeueBuffer. The values are + // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0. + status_t setConsumerUsageBits(uint32_t usage); + + // setTransformHint bakes in rotation to buffers so overlays can be used. + // The values are enumerated in window.h, e.g. + // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform). + status_t setTransformHint(uint32_t hint); + + int getGeneration(); + + SurfaceDescriptor *getSurfaceDescriptorFromBuffer(ANativeWindowBuffer* buffer); + +private: + // releaseBufferFreeListUnlocked releases the resources in the freeList; + // this must be called with mMutex unlocked. + void releaseBufferFreeListUnlocked(nsTArray& freeList); + + // freeBufferLocked frees the GraphicBuffer and sync resources for the + // given slot. + //void freeBufferLocked(int index); + + // freeAllBuffersLocked frees the GraphicBuffer and sync resources for + // all slots. + //void freeAllBuffersLocked(); + void freeAllBuffersLocked(nsTArray& freeList); + + // setDefaultMaxBufferCountLocked sets the maximum number of buffer slots + // that will be used if the producer does not override the buffer slot + // count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. + // The initial default is 2. + status_t setDefaultMaxBufferCountLocked(int count); + + // getMinBufferCountLocked returns the minimum number of buffers allowed + // given the current GonkBufferQueue state. + int getMinMaxBufferCountLocked() const; + + // getMinUndequeuedBufferCountLocked returns the minimum number of buffers + // that must remain in a state other than DEQUEUED. + int getMinUndequeuedBufferCountLocked() const; + + // getMaxBufferCountLocked returns the maximum number of buffers that can + // be allocated at once. This value depends upon the following member + // variables: + // + // mSynchronousMode + // mMaxAcquiredBufferCount + // mDefaultMaxBufferCount + // mOverrideMaxBufferCount + // + // Any time one of these member variables is changed while a producer is + // connected, mDequeueCondition must be broadcast. + int getMaxBufferCountLocked() const; + + struct BufferSlot { + + BufferSlot() + : mSurfaceDescriptor(SurfaceDescriptor()), + mBufferState(BufferSlot::FREE), + mRequestBufferCalled(false), + mTransform(0), + mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mTimestamp(0), + mFrameNumber(0), + mAcquireCalled(false), + mNeedsCleanupOnRelease(false) { + mCrop.makeInvalid(); + } + + // mGraphicBuffer points to the buffer allocated for this slot or is NULL + // if no buffer has been allocated. + sp mGraphicBuffer; + + // mSurfaceDescriptor is the token to remotely allocated GraphicBuffer. + SurfaceDescriptor mSurfaceDescriptor; + + // BufferState represents the different states in which a buffer slot + // can be. All slots are initially FREE. + enum BufferState { + // FREE indicates that the buffer is available to be dequeued + // by the producer. The buffer may be in use by the consumer for + // a finite time, so the buffer must not be modified until the + // associated fence is signaled. + // + // The slot is "owned" by GonkBufferQueue. It transitions to DEQUEUED + // when dequeueBuffer is called. + FREE = 0, + + // DEQUEUED indicates that the buffer has been dequeued by the + // producer, but has not yet been queued or canceled. The + // producer may modify the buffer's contents as soon as the + // associated ready fence is signaled. + // + // The slot is "owned" by the producer. It can transition to + // QUEUED (via queueBuffer) or back to FREE (via cancelBuffer). + DEQUEUED = 1, + + // QUEUED indicates that the buffer has been filled by the + // producer and queued for use by the consumer. The buffer + // contents may continue to be modified for a finite time, so + // the contents must not be accessed until the associated fence + // is signaled. + // + // The slot is "owned" by GonkBufferQueue. It can transition to + // ACQUIRED (via acquireBuffer) or to FREE (if another buffer is + // queued in asynchronous mode). + QUEUED = 2, + + // ACQUIRED indicates that the buffer has been acquired by the + // consumer. As with QUEUED, the contents must not be accessed + // by the consumer until the fence is signaled. + // + // The slot is "owned" by the consumer. It transitions to FREE + // when releaseBuffer is called. + ACQUIRED = 3 + }; + + // mBufferState is the current state of this buffer slot. + BufferState mBufferState; + + // mRequestBufferCalled is used for validating that the producer did + // call requestBuffer() when told to do so. Technically this is not + // needed but useful for debugging and catching producer bugs. + bool mRequestBufferCalled; + + // mCrop is the current crop rectangle for this buffer slot. + Rect mCrop; + + // mTransform is the current transform flags for this buffer slot. + // (example: NATIVE_WINDOW_TRANSFORM_ROT_90) + uint32_t mTransform; + + // mScalingMode is the current scaling mode for this buffer slot. + // (example: NATIVE_WINDOW_SCALING_MODE_FREEZE) + uint32_t mScalingMode; + + // mTimestamp is the current timestamp for this buffer slot. This gets + // to set by queueBuffer each time this slot is queued. + int64_t mTimestamp; + + // mFrameNumber is the number of the queued frame for this slot. This + // is used to dequeue buffers in LRU order (useful because buffers + // may be released before their release fence is signaled). + uint64_t mFrameNumber; + + // mEglFence is the EGL sync object that must signal before the buffer + // associated with this buffer slot may be dequeued. It is initialized + // to EGL_NO_SYNC_KHR when the buffer is created and may be set to a + // new sync object in releaseBuffer. (This is deprecated in favor of + // mFence, below.) + //EGLSyncKHR mEglFence; + + // mFence is a fence which will signal when work initiated by the + // previous owner of the buffer is finished. When the buffer is FREE, + // the fence indicates when the consumer has finished reading + // from the buffer, or when the producer has finished writing if it + // called cancelBuffer after queueing some writes. When the buffer is + // QUEUED, it indicates when the producer has finished filling the + // buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been + // passed to the consumer or producer along with ownership of the + // buffer, and mFence is set to NO_FENCE. + sp mFence; + + // Indicates whether this buffer has been seen by a consumer yet + bool mAcquireCalled; + + // Indicates whether this buffer needs to be cleaned up by the + // consumer. This is set when a buffer in ACQUIRED state is freed. + // It causes releaseBuffer to return STALE_BUFFER_SLOT. + bool mNeedsCleanupOnRelease; + }; + + // mSlots is the array of buffer slots that must be mirrored on the + // producer side. This allows buffer ownership to be transferred between + // the producer and consumer without sending a GraphicBuffer over binder. + // The entire array is initialized to NULL at construction time, and + // buffers are allocated for a slot when requestBuffer is called with + // that slot's index. + BufferSlot mSlots[NUM_BUFFER_SLOTS]; + + // mDefaultWidth holds the default width of allocated buffers. It is used + // in dequeueBuffer() if a width and height of zero is specified. + uint32_t mDefaultWidth; + + // mDefaultHeight holds the default height of allocated buffers. It is used + // in dequeueBuffer() if a width and height of zero is specified. + uint32_t mDefaultHeight; + + // mMaxAcquiredBufferCount is the number of buffers that the consumer may + // acquire at one time. It defaults to 1 and can be changed by the + // consumer via the setMaxAcquiredBufferCount method, but this may only be + // done when no producer is connected to the GonkBufferQueue. + // + // This value is used to derive the value returned for the + // MIN_UNDEQUEUED_BUFFERS query by the producer. + int mMaxAcquiredBufferCount; + + // mDefaultMaxBufferCount is the default limit on the number of buffers + // that will be allocated at one time. This default limit is set by the + // consumer. The limit (as opposed to the default limit) may be + // overridden by the producer. + int mDefaultMaxBufferCount; + + // mOverrideMaxBufferCount is the limit on the number of buffers that will + // be allocated at one time. This value is set by the image producer by + // calling setBufferCount. The default is zero, which means the producer + // doesn't care about the number of buffers in the pool. In that case + // mDefaultMaxBufferCount is used as the limit. + int mOverrideMaxBufferCount; + + // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to + // allocate new GraphicBuffer objects. + sp mGraphicBufferAlloc; + + // mConsumerListener is used to notify the connected consumer of + // asynchronous events that it may wish to react to. It is initially set + // to NULL and is written by consumerConnect and consumerDisconnect. + sp mConsumerListener; + + // mSynchronousMode whether we're in synchronous mode or not + bool mSynchronousMode; + + // mAllowSynchronousMode whether we allow synchronous mode or not. Set + // when the GonkBufferQueue is created (by the consumer). + const bool mAllowSynchronousMode; + + // mConnectedApi indicates the producer API that is currently connected + // to this GonkBufferQueue. It defaults to NO_CONNECTED_API (= 0), and gets + // updated by the connect and disconnect methods. + int mConnectedApi; + + // mDequeueCondition condition used for dequeueBuffer in synchronous mode + mutable Condition mDequeueCondition; + + // mQueue is a FIFO of queued buffers used in synchronous mode + typedef Vector Fifo; + Fifo mQueue; + + // mAbandoned indicates that the GonkBufferQueue will no longer be used to + // consume image buffers pushed to it using the IGraphicBufferProducer + // interface. It is initialized to false, and set to true in the + // consumerDisconnect method. A GonkBufferQueue that has been abandoned will + // return the NO_INIT error from all IGraphicBufferProducer methods + // capable of returning an error. + bool mAbandoned; + + // mConsumerName is a string used to identify the GonkBufferQueue in log + // messages. It is set by the setConsumerName method. + String8 mConsumerName; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of GonkBufferQueue objects. It must be locked whenever the + // member variables are accessed. + mutable Mutex mMutex; + + // mFrameCounter is the free running counter, incremented on every + // successful queueBuffer call. + uint64_t mFrameCounter; + + // mBufferHasBeenQueued is true once a buffer has been queued. It is + // reset when something causes all buffers to be freed (e.g. changing the + // buffer count). + bool mBufferHasBeenQueued; + + // mDefaultBufferFormat can be set so it will override + // the buffer format when it isn't specified in dequeueBuffer + uint32_t mDefaultBufferFormat; + + // mConsumerUsageBits contains flags the consumer wants for GraphicBuffers + uint32_t mConsumerUsageBits; + + // mTransformHint is used to optimize for screen rotations + uint32_t mTransformHint; + + // mGeneration is the current generation of buffer slots + uint32_t mGeneration; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_BUFFERQUEUE_H diff --git a/widget/gonk/nativewindow/GonkBufferQueueKK.cpp b/widget/gonk/nativewindow/GonkBufferQueueKK.cpp new file mode 100644 index 000000000000..7fb21e7fb8a8 --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueKK.cpp @@ -0,0 +1,1287 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "GonkBufferQueue" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#define LOG_NDEBUG 0 + +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES + +#include +#include +#include +#include +#include "mozilla/layers/ImageBridgeChild.h" +#include "GonkBufferQueueKK.h" + +// Macros for including the GonkBufferQueue name in log messages +#define ST_LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +#define ST_LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#define ST_LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define ST_LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) +#define ST_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) + +#define ATRACE_BUFFER_INDEX(index) + +using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::layers; + +namespace android { + +// Get an ID that's unique within this process. +static int32_t createProcessUniqueId() { + static volatile int32_t globalCounter = 0; + return android_atomic_inc(&globalCounter); +} + +static const char* scalingModeName(int scalingMode) { + switch (scalingMode) { + case NATIVE_WINDOW_SCALING_MODE_FREEZE: return "FREEZE"; + case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: return "SCALE_TO_WINDOW"; + case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: return "SCALE_CROP"; + default: return "Unknown"; + } +} + +GonkBufferQueue::GonkBufferQueue(bool allowSynchronousMode, + const sp& allocator) : + mDefaultWidth(1), + mDefaultHeight(1), + mMaxAcquiredBufferCount(1), + mDefaultMaxBufferCount(2), + mOverrideMaxBufferCount(0), +// mSynchronousMode(true), // GonkBufferQueue always works in sync mode. + mConsumerControlledByApp(false), + mDequeueBufferCannotBlock(false), + mUseAsyncBuffer(true), + mConnectedApi(NO_CONNECTED_API), + mAbandoned(false), + mFrameCounter(0), + mBufferHasBeenQueued(false), + mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888), + mConsumerUsageBits(0), + mTransformHint(0), + mGeneration(0) +{ + // Choose a name using the PID and a process-unique ID. + mConsumerName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); + + ST_LOGV("GonkBufferQueue"); +} + +GonkBufferQueue::~GonkBufferQueue() { + ST_LOGV("~GonkBufferQueue"); +} + +status_t GonkBufferQueue::setDefaultMaxBufferCountLocked(int count) { + if (count < 2 || count > NUM_BUFFER_SLOTS) + return BAD_VALUE; + + mDefaultMaxBufferCount = count; + mDequeueCondition.broadcast(); + + return NO_ERROR; +} + +void GonkBufferQueue::setConsumerName(const String8& name) { + Mutex::Autolock lock(mMutex); + mConsumerName = name; +} + +status_t GonkBufferQueue::setDefaultBufferFormat(uint32_t defaultFormat) { + Mutex::Autolock lock(mMutex); + mDefaultBufferFormat = defaultFormat; + return NO_ERROR; +} + +status_t GonkBufferQueue::setConsumerUsageBits(uint32_t usage) { + Mutex::Autolock lock(mMutex); + mConsumerUsageBits = usage; + return NO_ERROR; +} + +status_t GonkBufferQueue::setTransformHint(uint32_t hint) { + ST_LOGV("setTransformHint: %02x", hint); + Mutex::Autolock lock(mMutex); + mTransformHint = hint; + return NO_ERROR; +} + +int GonkBufferQueue::getGeneration() { + return mGeneration; +} + +mozilla::layers::SurfaceDescriptor* +GonkBufferQueue::getSurfaceDescriptorFromBuffer(ANativeWindowBuffer* buffer) +{ + Mutex::Autolock _l(mMutex); + if (buffer == NULL) { + ST_LOGE("getSlotFromBufferLocked: encountered NULL buffer"); + return nullptr; + } + + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (mSlots[i].mGraphicBuffer != NULL && mSlots[i].mGraphicBuffer->handle == buffer->handle) { + return &mSlots[i].mSurfaceDescriptor; + } + } + ST_LOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle); + return nullptr; +} + +status_t GonkBufferQueue::setBufferCount(int bufferCount) { + ST_LOGV("setBufferCount: count=%d", bufferCount); + + sp listener; + nsAutoTArray freeList; + { + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("setBufferCount: GonkBufferQueue has been abandoned!"); + return NO_INIT; + } + if (bufferCount > NUM_BUFFER_SLOTS) { + ST_LOGE("setBufferCount: bufferCount too large (max %d)", + NUM_BUFFER_SLOTS); + return BAD_VALUE; + } + + // Error out if the user has dequeued buffers + for (int i=0 ; ionBuffersReleased(); + } + + return NO_ERROR; +} + +int GonkBufferQueue::query(int what, int* outValue) +{ + ATRACE_CALL(); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("query: GonkBufferQueue has been abandoned!"); + return NO_INIT; + } + + int value; + switch (what) { + case NATIVE_WINDOW_WIDTH: + value = mDefaultWidth; + break; + case NATIVE_WINDOW_HEIGHT: + value = mDefaultHeight; + break; + case NATIVE_WINDOW_FORMAT: + value = mDefaultBufferFormat; + break; + case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: + value = getMinUndequeuedBufferCount(false); + break; + case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: + value = (mQueue.size() >= 2); + break; + case NATIVE_WINDOW_CONSUMER_USAGE_BITS: + value = mConsumerUsageBits; + break; + default: + return BAD_VALUE; + } + outValue[0] = value; + return NO_ERROR; +} + +status_t GonkBufferQueue::requestBuffer(int slot, sp* buf) { + ATRACE_CALL(); + ST_LOGV("requestBuffer: slot=%d", slot); + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("requestBuffer: GonkBufferQueue has been abandoned!"); + return NO_INIT; + } + if (slot < 0 || slot >= NUM_BUFFER_SLOTS) { + ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d", + NUM_BUFFER_SLOTS, slot); + return BAD_VALUE; + } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { + ST_LOGE("requestBuffer: slot %d is not owned by the client (state=%d)", + slot, mSlots[slot].mBufferState); + return BAD_VALUE; + } + mSlots[slot].mRequestBufferCalled = true; + *buf = mSlots[slot].mGraphicBuffer; + return NO_ERROR; +} + +status_t GonkBufferQueue::dequeueBuffer(int *outBuf, sp* outFence, bool async, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { + ATRACE_CALL(); + ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage); + + if ((w && !h) || (!w && h)) { + ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); + return BAD_VALUE; + } + + status_t returnFlags(OK); + int buf = INVALID_BUFFER_SLOT; + uint32_t generation; + SurfaceDescriptor descOld; + + { // Scope for the lock + Mutex::Autolock lock(mMutex); + generation = mGeneration; + + if (format == 0) { + format = mDefaultBufferFormat; + } + // turn on usage bits the consumer requested + usage |= mConsumerUsageBits; + + int found = -1; + bool tryAgain = true; + while (tryAgain) { + if (mAbandoned) { + ST_LOGE("dequeueBuffer: GonkBufferQueue has been abandoned!"); + return NO_INIT; + } + + const int maxBufferCount = getMaxBufferCountLocked(async); + if (async && mOverrideMaxBufferCount) { + // FIXME: some drivers are manually setting the buffer-count (which they + // shouldn't), so we do this extra test here to handle that case. + // This is TEMPORARY, until we get this fixed. + if (mOverrideMaxBufferCount < maxBufferCount) { + ST_LOGE("dequeueBuffer: async mode is invalid with buffercount override"); + return BAD_VALUE; + } + } + + // Free up any buffers that are in slots beyond the max buffer + // count. + //for (int i = maxBufferCount; i < NUM_BUFFER_SLOTS; i++) { + // assert(mSlots[i].mBufferState == BufferSlot::FREE); + // if (mSlots[i].mGraphicBuffer != NULL) { + // freeBufferLocked(i); + // returnFlags |= IGraphicBufferProducer::RELEASE_ALL_BUFFERS; + // } + //} + + // look for a free buffer to give to the client + found = INVALID_BUFFER_SLOT; + int dequeuedCount = 0; + int acquiredCount = 0; + for (int i = 0; i < maxBufferCount; i++) { + const int state = mSlots[i].mBufferState; + switch (state) { + case BufferSlot::DEQUEUED: + dequeuedCount++; + break; + case BufferSlot::ACQUIRED: + acquiredCount++; + break; + case BufferSlot::FREE: + /* We return the oldest of the free buffers to avoid + * stalling the producer if possible. This is because + * the consumer may still have pending reads of the + * buffers in flight. + */ + if ((found < 0) || + mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) { + found = i; + } + break; + } + } + + // clients are not allowed to dequeue more than one buffer + // if they didn't set a buffer count. + if (!mOverrideMaxBufferCount && dequeuedCount) { + ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " + "setting the buffer count"); + return -EINVAL; + } + + // See whether a buffer has been queued since the last + // setBufferCount so we know whether to perform the min undequeued + // buffers check below. + if (mBufferHasBeenQueued) { + // make sure the client is not trying to dequeue more buffers + // than allowed. + const int newUndequeuedCount = maxBufferCount - (dequeuedCount+1); + const int minUndequeuedCount = getMinUndequeuedBufferCount(async); + if (newUndequeuedCount < minUndequeuedCount) { + ST_LOGE("dequeueBuffer: min undequeued buffer count (%d) " + "exceeded (dequeued=%d undequeudCount=%d)", + minUndequeuedCount, dequeuedCount, + newUndequeuedCount); + return -EBUSY; + } + } + + // If no buffer is found, wait for a buffer to be released or for + // the max buffer count to change. + tryAgain = found == INVALID_BUFFER_SLOT; + if (tryAgain) { + // return an error if we're in "cannot block" mode (producer and consumer + // are controlled by the application) -- however, the consumer is allowed + // to acquire briefly an extra buffer (which could cause us to have to wait here) + // and that's okay because we know the wait will be brief (it happens + // if we dequeue a buffer while the consumer has acquired one but not released + // the old one yet -- for e.g.: see GLConsumer::updateTexImage()). + if (mDequeueBufferCannotBlock && (acquiredCount <= mMaxAcquiredBufferCount)) { + ST_LOGE("dequeueBuffer: would block! returning an error instead."); + return WOULD_BLOCK; + } + mDequeueCondition.wait(mMutex); + } + } + + + if (found == INVALID_BUFFER_SLOT) { + // This should not happen. + ST_LOGE("dequeueBuffer: no available buffer slots"); + return -EBUSY; + } + + buf = found; + *outBuf = found; + + const bool useDefaultSize = !w && !h; + if (useDefaultSize) { + // use the default size + w = mDefaultWidth; + h = mDefaultHeight; + } + + mSlots[buf].mBufferState = BufferSlot::DEQUEUED; + + const sp& buffer(mSlots[buf].mGraphicBuffer); + if ((buffer == NULL) || + (uint32_t(buffer->width) != w) || + (uint32_t(buffer->height) != h) || + (uint32_t(buffer->format) != format) || + ((uint32_t(buffer->usage) & usage) != usage)) + { + mSlots[buf].mAcquireCalled = false; + mSlots[buf].mGraphicBuffer = NULL; + mSlots[buf].mRequestBufferCalled = false; + mSlots[buf].mFence = Fence::NO_FENCE; + descOld = mSlots[buf].mSurfaceDescriptor; + mSlots[buf].mSurfaceDescriptor = SurfaceDescriptor(); + + returnFlags |= IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION; + } + + + if (CC_UNLIKELY(mSlots[buf].mFence == NULL)) { + ST_LOGE("dequeueBuffer: about to return a NULL fence from mSlot. " + "buf=%d, w=%d, h=%d, format=%d", + buf, buffer->width, buffer->height, buffer->format); + } + *outFence = mSlots[buf].mFence; + mSlots[buf].mFence = Fence::NO_FENCE; + } // end lock scope + + SurfaceDescriptor desc; + ImageBridgeChild* ibc; + sp graphicBuffer; + if (returnFlags & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + status_t error; + ibc = ImageBridgeChild::GetSingleton(); + ST_LOGD("dequeueBuffer: about to alloc surface descriptor"); + ibc->AllocSurfaceDescriptorGralloc(IntSize(w, h), + format, + usage, + &desc); + // We can only use a gralloc buffer here. If we didn't get + // one back, something went wrong. + ST_LOGD("dequeueBuffer: got surface descriptor"); + if (SurfaceDescriptor::TSurfaceDescriptorGralloc != desc.type()) { + MOZ_ASSERT(SurfaceDescriptor::T__None == desc.type()); + ST_LOGE("dequeueBuffer: failed to alloc gralloc buffer"); + return -ENOMEM; + } + graphicBuffer = GrallocBufferActor::GetFrom(desc.get_SurfaceDescriptorGralloc()); + error = graphicBuffer->initCheck(); + if (error != NO_ERROR) { + ST_LOGE("dequeueBuffer: createGraphicBuffer failed with error %d", error); + return error; + } + + bool tooOld = false; + { // Scope for the lock + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + + if (generation == mGeneration) { + mSlots[buf].mGraphicBuffer = graphicBuffer; + mSlots[buf].mSurfaceDescriptor = desc; + mSlots[buf].mSurfaceDescriptor.get_SurfaceDescriptorGralloc().external() = true; + ST_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf, + mSlots[buf].mGraphicBuffer->handle); + } else { + tooOld = true; + } + } + + if (IsSurfaceDescriptorValid(descOld)) { + ibc->DeallocSurfaceDescriptorGralloc(descOld); + } + + if (tooOld) { + ibc->DeallocSurfaceDescriptorGralloc(desc); + } + } + + ST_LOGV("dequeueBuffer: returning slot=%d/%llu buf=%p flags=%#x", *outBuf, + mSlots[*outBuf].mFrameNumber, + mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); + + return returnFlags; +} + +status_t GonkBufferQueue::queueBuffer(int buf, + const QueueBufferInput& input, QueueBufferOutput* output) { + ATRACE_CALL(); + + Rect crop; + uint32_t transform; + int scalingMode; + int64_t timestamp; + bool isAutoTimestamp; + bool async; + sp fence; + + input.deflate(×tamp, &isAutoTimestamp, &crop, &scalingMode, &transform, + &async, &fence); + + if (fence == NULL) { + ST_LOGE("queueBuffer: fence is NULL"); + return BAD_VALUE; + } + + ST_LOGV("queueBuffer: slot=%d time=%#llx crop=[%d,%d,%d,%d] tr=%#x " + "scale=%s", + buf, timestamp, crop.left, crop.top, crop.right, crop.bottom, + transform, scalingModeName(scalingMode)); + + switch (scalingMode) { + case NATIVE_WINDOW_SCALING_MODE_FREEZE: + case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: + case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: + case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP: + break; + default: + ST_LOGE("unknown scaling mode: %d", scalingMode); + return -EINVAL; + } + + sp listener; + + { // scope for the lock + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("queueBuffer: GonkBufferQueue has been abandoned!"); + return NO_INIT; + } + + const int maxBufferCount = getMaxBufferCountLocked(async); + if (async && mOverrideMaxBufferCount) { + // FIXME: some drivers are manually setting the buffer-count (which they + // shouldn't), so we do this extra test here to handle that case. + // This is TEMPORARY, until we get this fixed. + if (mOverrideMaxBufferCount < maxBufferCount) { + ST_LOGE("queueBuffer: async mode is invalid with buffercount override"); + return BAD_VALUE; + } + } + if (buf < 0 || buf >= maxBufferCount) { + ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d", + maxBufferCount, buf); + return -EINVAL; + } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + ST_LOGE("queueBuffer: slot %d is not owned by the client " + "(state=%d)", buf, mSlots[buf].mBufferState); + return -EINVAL; + } else if (!mSlots[buf].mRequestBufferCalled) { + ST_LOGE("queueBuffer: slot %d was enqueued without requesting a " + "buffer", buf); + return -EINVAL; + } + + ST_LOGV("queueBuffer: slot=%d/%llu time=%#llx crop=[%d,%d,%d,%d] " + "tr=%#x scale=%s", + buf, mFrameCounter + 1, timestamp, + crop.left, crop.top, crop.right, crop.bottom, + transform, scalingModeName(scalingMode)); + + const sp& graphicBuffer(mSlots[buf].mGraphicBuffer); + Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight()); + Rect croppedCrop; + crop.intersect(bufferRect, &croppedCrop); + if (croppedCrop != crop) { + ST_LOGE("queueBuffer: crop rect is not contained within the " + "buffer in slot %d", buf); + return -EINVAL; + } + + mSlots[buf].mFence = fence; + mSlots[buf].mBufferState = BufferSlot::QUEUED; + mFrameCounter++; + mSlots[buf].mFrameNumber = mFrameCounter; + + BufferItem item; + item.mAcquireCalled = mSlots[buf].mAcquireCalled; + item.mGraphicBuffer = mSlots[buf].mGraphicBuffer; + item.mCrop = crop; + item.mTransform = transform & ~NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; + item.mTransformToDisplayInverse = bool(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY); + item.mScalingMode = scalingMode; + item.mTimestamp = timestamp; + item.mIsAutoTimestamp = isAutoTimestamp; + item.mFrameNumber = mFrameCounter; + item.mBuf = buf; + item.mFence = fence; + item.mIsDroppable = mDequeueBufferCannotBlock || async; + + if (mQueue.empty()) { + // when the queue is empty, we can ignore "mDequeueBufferCannotBlock", and + // simply queue this buffer. + mQueue.push_back(item); + listener = mConsumerListener; + } else { + // when the queue is not empty, we need to look at the front buffer + // state and see if we need to replace it. + Fifo::iterator front(mQueue.begin()); + if (front->mIsDroppable) { + // buffer slot currently queued is marked free if still tracked + if (stillTracking(front)) { + mSlots[front->mBuf].mBufferState = BufferSlot::FREE; + // reset the frame number of the freed buffer so that it is the first in + // line to be dequeued again. + mSlots[front->mBuf].mFrameNumber = 0; + } + // and we record the new buffer in the queued list + *front = item; + } else { + mQueue.push_back(item); + listener = mConsumerListener; + } + } + + mBufferHasBeenQueued = true; + mDequeueCondition.broadcast(); + + output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, + mQueue.size()); + + } // scope for the lock + + // call back without lock held + if (listener != 0) { + listener->onFrameAvailable(); + } + return NO_ERROR; +} + +void GonkBufferQueue::cancelBuffer(int buf, const sp& fence) { + ATRACE_CALL(); + ST_LOGV("cancelBuffer: slot=%d", buf); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGW("cancelBuffer: GonkBufferQueue has been abandoned!"); + return; + } + + if (buf < 0 || buf >= NUM_BUFFER_SLOTS) { + ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", + NUM_BUFFER_SLOTS, buf); + return; + } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", + buf, mSlots[buf].mBufferState); + return; + } else if (fence == NULL) { + ST_LOGE("cancelBuffer: fence is NULL"); + return; + } + mSlots[buf].mBufferState = BufferSlot::FREE; + mSlots[buf].mFrameNumber = 0; + mSlots[buf].mFence = fence; + mDequeueCondition.broadcast(); +} + + +status_t GonkBufferQueue::connect(const sp& token, + int api, bool producerControlledByApp, QueueBufferOutput* output) { + ATRACE_CALL(); + ST_LOGV("connect: api=%d producerControlledByApp=%s", api, + producerControlledByApp ? "true" : "false"); + Mutex::Autolock lock(mMutex); + +retry: + if (mAbandoned) { + ST_LOGE("connect: GonkBufferQueue has been abandoned!"); + return NO_INIT; + } + + if (mConsumerListener == NULL) { + ST_LOGE("connect: GonkBufferQueue has no consumer!"); + return NO_INIT; + } + + if (mConnectedApi != NO_CONNECTED_API) { + ST_LOGE("connect: already connected (cur=%d, req=%d)", + mConnectedApi, api); + return -EINVAL; + } + + // If we disconnect and reconnect quickly, we can be in a state where our slots are + // empty but we have many buffers in the queue. This can cause us to run out of + // memory if we outrun the consumer. Wait here if it looks like we have too many + // buffers queued up. + int maxBufferCount = getMaxBufferCountLocked(false); // worst-case, i.e. largest value + if (mQueue.size() > (size_t) maxBufferCount) { + // TODO: make this bound tighter? + ST_LOGV("queue size is %d, waiting", mQueue.size()); + mDequeueCondition.wait(mMutex); + goto retry; + } + + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + mConnectedApi = api; + output->inflate(mDefaultWidth, mDefaultHeight, mTransformHint, mQueue.size()); + + // set-up a death notification so that we can disconnect + // automatically when/if the remote producer dies. + if (token != NULL && token->remoteBinder() != NULL) { + status_t err = token->linkToDeath(static_cast(this)); + if (err == NO_ERROR) { + mConnectedProducerToken = token; + } else { + ALOGE("linkToDeath failed: %s (%d)", strerror(-err), err); + } + } + break; + default: + err = -EINVAL; + break; + } + + mBufferHasBeenQueued = false; + mDequeueBufferCannotBlock = mConsumerControlledByApp && producerControlledByApp; + + return err; +} + +void GonkBufferQueue::binderDied(const wp& who) { + // If we're here, it means that a producer we were connected to died. + // We're GUARANTEED that we still are connected to it because it has no other way + // to get disconnected -- or -- we wouldn't be here because we're removing this + // callback upon disconnect. Therefore, it's okay to read mConnectedApi without + // synchronization here. + int api = mConnectedApi; + this->disconnect(api); +} + +status_t GonkBufferQueue::disconnect(int api) { + ATRACE_CALL(); + ST_LOGV("disconnect: api=%d", api); + + int err = NO_ERROR; + sp listener; + nsAutoTArray freeList; + + { // Scope for the lock + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + // it is not really an error to disconnect after the surface + // has been abandoned, it should just be a no-op. + return NO_ERROR; + } + + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mConnectedApi == api) { + freeAllBuffersLocked(freeList); + mConnectedApi = NO_CONNECTED_API; + mDequeueCondition.broadcast(); + listener = mConsumerListener; + } else { + ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)", + mConnectedApi, api); + err = -EINVAL; + } + break; + default: + ST_LOGE("disconnect: unknown API %d", api); + err = -EINVAL; + break; + } + } + releaseBufferFreeListUnlocked(freeList); + + if (listener != NULL) { + listener->onBuffersReleased(); + } + + return err; +} + +void GonkBufferQueue::dump(String8& result, const char* prefix) const { + Mutex::Autolock _l(mMutex); + + String8 fifo; + int fifoSize = 0; + Fifo::const_iterator i(mQueue.begin()); + while (i != mQueue.end()) { + fifo.appendFormat("%02d:%p crop=[%d,%d,%d,%d], " + "xform=0x%02x, time=%#llx, scale=%s\n", + i->mBuf, i->mGraphicBuffer.get(), + i->mCrop.left, i->mCrop.top, i->mCrop.right, + i->mCrop.bottom, i->mTransform, i->mTimestamp, + scalingModeName(i->mScalingMode) + ); + i++; + fifoSize++; + } + + + result.appendFormat( + "%s-BufferQueue mMaxAcquiredBufferCount=%d, mDequeueBufferCannotBlock=%d, default-size=[%dx%d], " + "default-format=%d, transform-hint=%02x, FIFO(%d)={%s}\n", + prefix, mMaxAcquiredBufferCount, mDequeueBufferCannotBlock, mDefaultWidth, + mDefaultHeight, mDefaultBufferFormat, mTransformHint, + fifoSize, fifo.string()); + + struct { + const char * operator()(int state) const { + switch (state) { + case BufferSlot::DEQUEUED: return "DEQUEUED"; + case BufferSlot::QUEUED: return "QUEUED"; + case BufferSlot::FREE: return "FREE"; + case BufferSlot::ACQUIRED: return "ACQUIRED"; + default: return "Unknown"; + } + } + } stateName; + + // just trim the free buffers to not spam the dump + int maxBufferCount = 0; + for (int i=NUM_BUFFER_SLOTS-1 ; i>=0 ; i--) { + const BufferSlot& slot(mSlots[i]); + if ((slot.mBufferState != BufferSlot::FREE) || (slot.mGraphicBuffer != NULL)) { + maxBufferCount = i+1; + break; + } + } + + for (int i=0 ; i& buf(slot.mGraphicBuffer); + result.appendFormat( + "%s%s[%02d:%p] state=%-8s", + prefix, (slot.mBufferState == BufferSlot::ACQUIRED)?">":" ", i, buf.get(), + stateName(slot.mBufferState) + ); + + if (buf != NULL) { + result.appendFormat( + ", %p [%4ux%4u:%4u,%3X]", + buf->handle, buf->width, buf->height, buf->stride, + buf->format); + } + result.append("\n"); + } +} + +void GonkBufferQueue::releaseBufferFreeListUnlocked(nsTArray& freeList) +{ + // This function MUST ONLY be called with mMutex unlocked; else there + // is a risk of deadlock with the ImageBridge thread. + + ST_LOGD("releaseBufferFreeListUnlocked: E"); + ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton(); + + for (uint32_t i = 0; i < freeList.Length(); ++i) { + ibc->DeallocSurfaceDescriptorGralloc(freeList[i]); + } + + freeList.Clear(); + ST_LOGD("releaseBufferFreeListUnlocked: X"); +} + +void GonkBufferQueue::freeAllBuffersLocked(nsTArray& freeList) +{ + ALOGW_IF(!mQueue.isEmpty(), + "freeAllBuffersLocked called but mQueue is not empty"); + ++mGeneration; + mQueue.clear(); + mBufferHasBeenQueued = false; + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (mSlots[i].mGraphicBuffer.get() && mSlots[i].mBufferState != BufferSlot::ACQUIRED) { + SurfaceDescriptor* desc = freeList.AppendElement(); + *desc = mSlots[i].mSurfaceDescriptor; + } + mSlots[i].mGraphicBuffer = 0; + mSlots[i].mSurfaceDescriptor = SurfaceDescriptor(); + if (mSlots[i].mBufferState == BufferSlot::ACQUIRED) { + mSlots[i].mNeedsCleanupOnRelease = true; + } + mSlots[i].mBufferState = BufferSlot::FREE; + mSlots[i].mFrameNumber = 0; + mSlots[i].mAcquireCalled = false; + // destroy fence as GonkBufferQueue now takes ownership + mSlots[i].mFence = Fence::NO_FENCE; + } +} + +status_t GonkBufferQueue::acquireBuffer(BufferItem *buffer, nsecs_t expectedPresent) { + ATRACE_CALL(); + Mutex::Autolock _l(mMutex); + + // Check that the consumer doesn't currently have the maximum number of + // buffers acquired. We allow the max buffer count to be exceeded by one + // buffer, so that the consumer can successfully set up the newly acquired + // buffer before releasing the old one. + int numAcquiredBuffers = 0; + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (mSlots[i].mBufferState == BufferSlot::ACQUIRED) { + numAcquiredBuffers++; + } + } + if (numAcquiredBuffers >= mMaxAcquiredBufferCount+1) { + ST_LOGE("acquireBuffer: max acquired buffer count reached: %d (max=%d)", + numAcquiredBuffers, mMaxAcquiredBufferCount); + return INVALID_OPERATION; + } + + // check if queue is empty + // In asynchronous mode the list is guaranteed to be one buffer + // deep, while in synchronous mode we use the oldest buffer. + if (mQueue.empty()) { + return NO_BUFFER_AVAILABLE; + } + + Fifo::iterator front(mQueue.begin()); + + // If expectedPresent is specified, we may not want to return a buffer yet. + // If it's specified and there's more than one buffer queued, we may + // want to drop a buffer. + if (expectedPresent != 0) { + const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second + + // The "expectedPresent" argument indicates when the buffer is expected + // to be presented on-screen. If the buffer's desired-present time + // is earlier (less) than expectedPresent, meaning it'll be displayed + // on time or possibly late if we show it ASAP, we acquire and return + // it. If we don't want to display it until after the expectedPresent + // time, we return PRESENT_LATER without acquiring it. + // + // To be safe, we don't defer acquisition if expectedPresent is + // more than one second in the future beyond the desired present time + // (i.e. we'd be holding the buffer for a long time). + // + // NOTE: code assumes monotonic time values from the system clock are + // positive. + + // Start by checking to see if we can drop frames. We skip this check + // if the timestamps are being auto-generated by Surface -- if the + // app isn't generating timestamps explicitly, they probably don't + // want frames to be discarded based on them. + while (mQueue.size() > 1 && !mQueue[0].mIsAutoTimestamp) { + // If entry[1] is timely, drop entry[0] (and repeat). We apply + // an additional criteria here: we only drop the earlier buffer if + // our desiredPresent falls within +/- 1 second of the expected + // present. Otherwise, bogus desiredPresent times (e.g. 0 or + // a small relative timestamp), which normally mean "ignore the + // timestamp and acquire immediately", would cause us to drop + // frames. + // + // We may want to add an additional criteria: don't drop the + // earlier buffer if entry[1]'s fence hasn't signaled yet. + // + // (Vector front is [0], back is [size()-1]) + const BufferItem& bi(mQueue[1]); + nsecs_t desiredPresent = bi.mTimestamp; + if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC || + desiredPresent > expectedPresent) { + // This buffer is set to display in the near future, or + // desiredPresent is garbage. Either way we don't want to + // drop the previous buffer just to get this on screen sooner. + ST_LOGV("pts nodrop: des=%lld expect=%lld (%lld) now=%lld", + desiredPresent, expectedPresent, desiredPresent - expectedPresent, + systemTime(CLOCK_MONOTONIC)); + break; + } + ST_LOGV("pts drop: queue1des=%lld expect=%lld size=%d", + desiredPresent, expectedPresent, mQueue.size()); + if (stillTracking(front)) { + // front buffer is still in mSlots, so mark the slot as free + mSlots[front->mBuf].mBufferState = BufferSlot::FREE; + } + mQueue.erase(front); + front = mQueue.begin(); + } + + // See if the front buffer is due. + nsecs_t desiredPresent = front->mTimestamp; + if (desiredPresent > expectedPresent && + desiredPresent < expectedPresent + MAX_REASONABLE_NSEC) { + ST_LOGV("pts defer: des=%lld expect=%lld (%lld) now=%lld", + desiredPresent, expectedPresent, desiredPresent - expectedPresent, + systemTime(CLOCK_MONOTONIC)); + return PRESENT_LATER; + } + + ST_LOGV("pts accept: des=%lld expect=%lld (%lld) now=%lld", + desiredPresent, expectedPresent, desiredPresent - expectedPresent, + systemTime(CLOCK_MONOTONIC)); + } + + int buf = front->mBuf; + buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer; + buffer->mSurfaceDescriptor = mSlots[buf].mSurfaceDescriptor; + buffer->mFrameNumber = mSlots[buf].mFrameNumber; + buffer->mBuf = buf; + buffer->mFence = mSlots[buf].mFence; + ATRACE_BUFFER_INDEX(buf); + + ST_LOGV("acquireBuffer: acquiring { slot=%d/%llu, buffer=%p }", + front->mBuf, front->mFrameNumber, + front->mGraphicBuffer->handle); + // if front buffer still being tracked update slot state + if (stillTracking(front)) { + mSlots[buf].mAcquireCalled = true; + mSlots[buf].mNeedsCleanupOnRelease = false; + mSlots[buf].mBufferState = BufferSlot::ACQUIRED; + mSlots[buf].mFence = Fence::NO_FENCE; + } + + // If the buffer has previously been acquired by the consumer, set + // mGraphicBuffer to NULL to avoid unnecessarily remapping this + // buffer on the consumer side. + //if (buffer->mAcquireCalled) { + // buffer->mGraphicBuffer = NULL; + //} + + mQueue.erase(front); + mDequeueCondition.broadcast(); + + return NO_ERROR; +} + +status_t GonkBufferQueue::releaseBuffer(int buf, uint64_t frameNumber, const sp& fence) { + ATRACE_CALL(); + + if (buf == INVALID_BUFFER_SLOT || fence == NULL) { + return BAD_VALUE; + } + + Mutex::Autolock _l(mMutex); + + // If the frame number has changed because buffer has been reallocated, + // we can ignore this releaseBuffer for the old buffer. + //if (frameNumber != mSlots[buf].mFrameNumber) { + // return STALE_BUFFER_SLOT; + //} + + + // Internal state consistency checks: + // Make sure this buffers hasn't been queued while we were owning it (acquired) + Fifo::iterator front(mQueue.begin()); + Fifo::const_iterator const end(mQueue.end()); + while (front != end) { + if (front->mBuf == buf) { + LOG_ALWAYS_FATAL("[%s] received new buffer(#%lld) on slot #%d that has not yet been " + "acquired", mConsumerName.string(), frameNumber, buf); + break; // never reached + } + front++; + } + + // The buffer can now only be released if its in the acquired state + if (mSlots[buf].mBufferState == BufferSlot::ACQUIRED) { + mSlots[buf].mFence = fence; + mSlots[buf].mBufferState = BufferSlot::FREE; + } else if (mSlots[buf].mNeedsCleanupOnRelease) { + ST_LOGV("releasing a stale buf %d its state was %d", buf, mSlots[buf].mBufferState); + mSlots[buf].mNeedsCleanupOnRelease = false; + return STALE_BUFFER_SLOT; + } else { + ST_LOGE("attempted to release buf %d but its state was %d", buf, mSlots[buf].mBufferState); + return -EINVAL; + } + + mDequeueCondition.broadcast(); + return NO_ERROR; +} + +status_t GonkBufferQueue::consumerConnect(const sp& consumerListener, + bool controlledByApp) { + ST_LOGV("consumerConnect controlledByApp=%s", + controlledByApp ? "true" : "false"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("consumerConnect: GonkBufferQueue has been abandoned!"); + return NO_INIT; + } + if (consumerListener == NULL) { + ST_LOGE("consumerConnect: consumerListener may not be NULL"); + return BAD_VALUE; + } + + mConsumerListener = consumerListener; + mConsumerControlledByApp = controlledByApp; + + return NO_ERROR; +} + +status_t GonkBufferQueue::consumerDisconnect() { + ST_LOGV("consumerDisconnect"); + nsAutoTArray freeList; + { + Mutex::Autolock lock(mMutex); + + if (mConsumerListener == NULL) { + ST_LOGE("consumerDisconnect: No consumer is connected!"); + return -EINVAL; + } + + mAbandoned = true; + mConsumerListener = NULL; + mQueue.clear(); + freeAllBuffersLocked(freeList); + } + releaseBufferFreeListUnlocked(freeList); + mDequeueCondition.broadcast(); + + return NO_ERROR; +} + +status_t GonkBufferQueue::getReleasedBuffers(uint32_t* slotMask) { + ST_LOGV("getReleasedBuffers"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("getReleasedBuffers: GonkBufferQueue has been abandoned!"); + return NO_INIT; + } + + uint32_t mask = 0; + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (!mSlots[i].mAcquireCalled) { + mask |= 1 << i; + } + } + + // Remove buffers in flight (on the queue) from the mask where acquire has + // been called, as the consumer will not receive the buffer address, so + // it should not free these slots. + Fifo::iterator front(mQueue.begin()); + while (front != mQueue.end()) { + if (front->mAcquireCalled) + mask &= ~(1 << front->mBuf); + front++; + } + + *slotMask = mask; + + ST_LOGV("getReleasedBuffers: returning mask %#x", mask); + return NO_ERROR; +} + +status_t GonkBufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h) { + ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h); + if (!w || !h) { + ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)", + w, h); + return BAD_VALUE; + } + + Mutex::Autolock lock(mMutex); + mDefaultWidth = w; + mDefaultHeight = h; + return NO_ERROR; +} + +status_t GonkBufferQueue::setDefaultMaxBufferCount(int bufferCount) { + ATRACE_CALL(); + Mutex::Autolock lock(mMutex); + return setDefaultMaxBufferCountLocked(bufferCount); +} + +status_t GonkBufferQueue::disableAsyncBuffer() { + ATRACE_CALL(); + Mutex::Autolock lock(mMutex); + if (mConsumerListener != NULL) { + ST_LOGE("disableAsyncBuffer: consumer already connected!"); + return INVALID_OPERATION; + } + mUseAsyncBuffer = false; + return NO_ERROR; +} + +status_t GonkBufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) { + ATRACE_CALL(); + Mutex::Autolock lock(mMutex); + if (maxAcquiredBuffers < 1 || maxAcquiredBuffers > MAX_MAX_ACQUIRED_BUFFERS) { + ST_LOGE("setMaxAcquiredBufferCount: invalid count specified: %d", + maxAcquiredBuffers); + return BAD_VALUE; + } + if (mConnectedApi != NO_CONNECTED_API) { + return INVALID_OPERATION; + } + mMaxAcquiredBufferCount = maxAcquiredBuffers; + return NO_ERROR; +} + +int GonkBufferQueue::getMinUndequeuedBufferCount(bool async) const { + // if dequeueBuffer is allowed to error out, we don't have to + // add an extra buffer. + if (!mUseAsyncBuffer) + return mMaxAcquiredBufferCount; + + // we're in async mode, or we want to prevent the app to + // deadlock itself, we throw-in an extra buffer to guarantee it. + if (mDequeueBufferCannotBlock || async) + return mMaxAcquiredBufferCount + 1; + + return mMaxAcquiredBufferCount; +} + +int GonkBufferQueue::getMinMaxBufferCountLocked(bool async) const { + return getMinUndequeuedBufferCount(async) + 1; +} + +int GonkBufferQueue::getMaxBufferCountLocked(bool async) const { + int minMaxBufferCount = getMinMaxBufferCountLocked(async); + + int maxBufferCount = mDefaultMaxBufferCount; + if (maxBufferCount < minMaxBufferCount) { + maxBufferCount = minMaxBufferCount; + } + if (mOverrideMaxBufferCount != 0) { + assert(mOverrideMaxBufferCount >= minMaxBufferCount); + maxBufferCount = mOverrideMaxBufferCount; + } + + // Any buffers that are dequeued by the producer or sitting in the queue + // waiting to be consumed need to have their slots preserved. Such + // buffers will temporarily keep the max buffer count up until the slots + // no longer need to be preserved. + for (int i = maxBufferCount; i < NUM_BUFFER_SLOTS; i++) { + BufferSlot::BufferState state = mSlots[i].mBufferState; + if (state == BufferSlot::QUEUED || state == BufferSlot::DEQUEUED) { + maxBufferCount = i + 1; + } + } + + return maxBufferCount; +} + +bool GonkBufferQueue::stillTracking(const BufferItem *item) const { + const BufferSlot &slot = mSlots[item->mBuf]; + + ST_LOGV("stillTracking?: item: { slot=%d/%llu, buffer=%p }, " + "slot: { slot=%d/%llu, buffer=%p }", + item->mBuf, item->mFrameNumber, + (item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0), + item->mBuf, slot.mFrameNumber, + (slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0)); + + // Compare item with its original buffer slot. We can check the slot + // as the buffer would not be moved to a different slot by the producer. + return (slot.mGraphicBuffer != NULL && + item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle); +} + +GonkBufferQueue::ProxyConsumerListener::ProxyConsumerListener( + const wp& consumerListener): + mConsumerListener(consumerListener) {} + +GonkBufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {} + +void GonkBufferQueue::ProxyConsumerListener::onFrameAvailable() { + sp listener(mConsumerListener.promote()); + if (listener != NULL) { + listener->onFrameAvailable(); + } +} + +void GonkBufferQueue::ProxyConsumerListener::onBuffersReleased() { + sp listener(mConsumerListener.promote()); + if (listener != NULL) { + listener->onBuffersReleased(); + } +} + +}; // namespace android diff --git a/widget/gonk/nativewindow/GonkBufferQueueKK.h b/widget/gonk/nativewindow/GonkBufferQueueKK.h new file mode 100644 index 000000000000..412028f3acdf --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueKK.h @@ -0,0 +1,576 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NATIVEWINDOW_GONKBUFFERQUEUE_KK_H +#define NATIVEWINDOW_GONKBUFFERQUEUE_KK_H + +#include +#include +#include +#include "IGonkGraphicBufferConsumer.h" + +#include +#include + +#include +#include +#include + +#include "mozilla/layers/LayersSurfaces.h" + +namespace android { +// ---------------------------------------------------------------------------- + +class GonkBufferQueue : public BnGraphicBufferProducer, + public BnGonkGraphicBufferConsumer, + private IBinder::DeathRecipient { + typedef mozilla::layers::SurfaceDescriptor SurfaceDescriptor; + +public: + enum { MIN_UNDEQUEUED_BUFFERS = 2 }; + enum { NUM_BUFFER_SLOTS = 32 }; + enum { NO_CONNECTED_API = 0 }; + enum { INVALID_BUFFER_SLOT = -1 }; + enum { STALE_BUFFER_SLOT = 1, NO_BUFFER_AVAILABLE, PRESENT_LATER }; + + // When in async mode we reserve two slots in order to guarantee that the + // producer and consumer can run asynchronously. + enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 }; + + // for backward source compatibility + typedef ::android::ConsumerListener ConsumerListener; + + // ProxyConsumerListener is a ConsumerListener implementation that keeps a weak + // reference to the actual consumer object. It forwards all calls to that + // consumer object so long as it exists. + // + // This class exists to avoid having a circular reference between the + // GonkBufferQueue object and the consumer object. The reason this can't be a weak + // reference in the GonkBufferQueue class is because we're planning to expose the + // consumer side of a GonkBufferQueue as a binder interface, which doesn't support + // weak references. + class ProxyConsumerListener : public BnConsumerListener { + public: + ProxyConsumerListener(const wp& consumerListener); + virtual ~ProxyConsumerListener(); + virtual void onFrameAvailable(); + virtual void onBuffersReleased(); + private: + // mConsumerListener is a weak reference to the IConsumerListener. This is + // the raison d'etre of ProxyConsumerListener. + wp mConsumerListener; + }; + + + // BufferQueue manages a pool of gralloc memory slots to be used by + // producers and consumers. allocator is used to allocate all the + // needed gralloc buffers. + GonkBufferQueue(bool allowSynchronousMode = true, + const sp& allocator = NULL); + virtual ~GonkBufferQueue(); + + /* + * IBinder::DeathRecipient interface + */ + + virtual void binderDied(const wp& who); + + /* + * IGraphicBufferProducer interface + */ + + // Query native window attributes. The "what" values are enumerated in + // window.h (e.g. NATIVE_WINDOW_FORMAT). + virtual int query(int what, int* value); + + // setBufferCount updates the number of available buffer slots. If this + // method succeeds, buffer slots will be both unallocated and owned by + // the GonkBufferQueue object (i.e. they are not owned by the producer or + // consumer). + // + // This will fail if the producer has dequeued any buffers, or if + // bufferCount is invalid. bufferCount must generally be a value + // between the minimum undequeued buffer count and NUM_BUFFER_SLOTS + // (inclusive). It may also be set to zero (the default) to indicate + // that the producer does not wish to set a value. The minimum value + // can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + // ...). + // + // This may only be called by the producer. The consumer will be told + // to discard buffers through the onBuffersReleased callback. + virtual status_t setBufferCount(int bufferCount); + + // requestBuffer returns the GraphicBuffer for slot N. + // + // In normal operation, this is called the first time slot N is returned + // by dequeueBuffer. It must be called again if dequeueBuffer returns + // flags indicating that previously-returned buffers are no longer valid. + virtual status_t requestBuffer(int slot, sp* buf); + + // dequeueBuffer gets the next buffer slot index for the producer to use. + // If a buffer slot is available then that slot index is written to the + // location pointed to by the buf argument and a status of OK is returned. + // If no slot is available then a status of -EBUSY is returned and buf is + // unmodified. + // + // The fence parameter will be updated to hold the fence associated with + // the buffer. The contents of the buffer must not be overwritten until the + // fence signals. If the fence is Fence::NO_FENCE, the buffer may be + // written immediately. + // + // The width and height parameters must be no greater than the minimum of + // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). + // An error due to invalid dimensions might not be reported until + // updateTexImage() is called. If width and height are both zero, the + // default values specified by setDefaultBufferSize() are used instead. + // + // The pixel formats are enumerated in graphics.h, e.g. + // HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format + // will be used. + // + // The usage argument specifies gralloc buffer usage flags. The values + // are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These + // will be merged with the usage flags specified by setConsumerUsageBits. + // + // The return value may be a negative error value or a non-negative + // collection of flags. If the flags are set, the return values are + // valid, but additional actions must be performed. + // + // If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the + // producer must discard cached GraphicBuffer references for the slot + // returned in buf. + // If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer + // must discard cached GraphicBuffer references for all slots. + // + // In both cases, the producer will need to call requestBuffer to get a + // GraphicBuffer handle for the returned slot. + virtual status_t dequeueBuffer(int *buf, sp* fence, bool async, + uint32_t width, uint32_t height, uint32_t format, uint32_t usage); + + // queueBuffer returns a filled buffer to the GonkBufferQueue. + // + // Additional data is provided in the QueueBufferInput struct. Notably, + // a timestamp must be provided for the buffer. The timestamp is in + // nanoseconds, and must be monotonically increasing. Its other semantics + // (zero point, etc) are producer-specific and should be documented by the + // producer. + // + // The caller may provide a fence that signals when all rendering + // operations have completed. Alternatively, NO_FENCE may be used, + // indicating that the buffer is ready immediately. + // + // Some values are returned in the output struct: the current settings + // for default width and height, the current transform hint, and the + // number of queued buffers. + virtual status_t queueBuffer(int buf, + const QueueBufferInput& input, QueueBufferOutput* output); + + // cancelBuffer returns a dequeued buffer to the GonkBufferQueue, but doesn't + // queue it for use by the consumer. + // + // The buffer will not be overwritten until the fence signals. The fence + // will usually be the one obtained from dequeueBuffer. + virtual void cancelBuffer(int buf, const sp& fence); + + // connect attempts to connect a producer API to the GonkBufferQueue. This + // must be called before any other IGraphicBufferProducer methods are + // called except for getAllocator. A consumer must already be connected. + // + // This method will fail if connect was previously called on the + // GonkBufferQueue and no corresponding disconnect call was made (i.e. if + // it's still connected to a producer). + // + // APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU). + virtual status_t connect(const sp& token, + int api, bool producerControlledByApp, QueueBufferOutput* output); + + // disconnect attempts to disconnect a producer API from the GonkBufferQueue. + // Calling this method will cause any subsequent calls to other + // IGraphicBufferProducer methods to fail except for getAllocator and connect. + // Successfully calling connect after this will allow the other methods to + // succeed again. + // + // This method will fail if the the GonkBufferQueue is not currently + // connected to the specified producer API. + virtual status_t disconnect(int api); + + /* + * IGraphicBufferConsumer interface + */ + + // acquireBuffer attempts to acquire ownership of the next pending buffer in + // the GonkBufferQueue. If no buffer is pending then it returns -EINVAL. If a + // buffer is successfully acquired, the information about the buffer is + // returned in BufferItem. If the buffer returned had previously been + // acquired then the BufferItem::mGraphicBuffer field of buffer is set to + // NULL and it is assumed that the consumer still holds a reference to the + // buffer. + // + // If presentWhen is nonzero, it indicates the time when the buffer will + // be displayed on screen. If the buffer's timestamp is farther in the + // future, the buffer won't be acquired, and PRESENT_LATER will be + // returned. The presentation time is in nanoseconds, and the time base + // is CLOCK_MONOTONIC. + virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen); + + // releaseBuffer releases a buffer slot from the consumer back to the + // GonkBufferQueue. This may be done while the buffer's contents are still + // being accessed. The fence will signal when the buffer is no longer + // in use. frameNumber is used to indentify the exact buffer returned. + // + // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free + // any references to the just-released buffer that it might have, as if it + // had received a onBuffersReleased() call with a mask set for the released + // buffer. + // + // Note that the dependencies on EGL will be removed once we switch to using + // the Android HW Sync HAL. + virtual status_t releaseBuffer(int buf, uint64_t frameNumber, + const sp& releaseFence); + + // consumerConnect connects a consumer to the GonkBufferQueue. Only one + // consumer may be connected, and when that consumer disconnects the + // GonkBufferQueue is placed into the "abandoned" state, causing most + // interactions with the GonkBufferQueue by the producer to fail. + // controlledByApp indicates whether the consumer is controlled by + // the application. + // + // consumer may not be NULL. + virtual status_t consumerConnect(const sp& consumer, bool controlledByApp); + + // consumerDisconnect disconnects a consumer from the GonkBufferQueue. All + // buffers will be freed and the GonkBufferQueue is placed in the "abandoned" + // state, causing most interactions with the GonkBufferQueue by the producer to + // fail. + virtual status_t consumerDisconnect(); + + // getReleasedBuffers sets the value pointed to by slotMask to a bit mask + // indicating which buffer slots have been released by the GonkBufferQueue + // but have not yet been released by the consumer. + // + // This should be called from the onBuffersReleased() callback. + virtual status_t getReleasedBuffers(uint32_t* slotMask); + + // setDefaultBufferSize is used to set the size of buffers returned by + // dequeueBuffer when a width and height of zero is requested. Default + // is 1x1. + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h); + + // setDefaultMaxBufferCount sets the default value for the maximum buffer + // count (the initial default is 2). If the producer has requested a + // buffer count using setBufferCount, the default buffer count will only + // take effect if the producer sets the count back to zero. + // + // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. + virtual status_t setDefaultMaxBufferCount(int bufferCount); + + // disableAsyncBuffer disables the extra buffer used in async mode + // (when both producer and consumer have set their "isControlledByApp" + // flag) and has dequeueBuffer() return WOULD_BLOCK instead. + // + // This can only be called before consumerConnect(). + virtual status_t disableAsyncBuffer(); + + // setMaxAcquiredBufferCount sets the maximum number of buffers that can + // be acquired by the consumer at one time (default 1). This call will + // fail if a producer is connected to the GonkBufferQueue. + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); + + // setConsumerName sets the name used in logging + virtual void setConsumerName(const String8& name); + + // setDefaultBufferFormat allows the GonkBufferQueue to create + // GraphicBuffers of a defaultFormat if no format is specified + // in dequeueBuffer. Formats are enumerated in graphics.h; the + // initial default is HAL_PIXEL_FORMAT_RGBA_8888. + virtual status_t setDefaultBufferFormat(uint32_t defaultFormat); + + // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. + // These are merged with the bits passed to dequeueBuffer. The values are + // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0. + virtual status_t setConsumerUsageBits(uint32_t usage); + + // setTransformHint bakes in rotation to buffers so overlays can be used. + // The values are enumerated in window.h, e.g. + // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform). + virtual status_t setTransformHint(uint32_t hint); + + // dump our state in a String + virtual void dump(String8& result, const char* prefix) const; + + int getGeneration(); + SurfaceDescriptor *getSurfaceDescriptorFromBuffer(ANativeWindowBuffer* buffer); + +private: + // releaseBufferFreeListUnlocked releases the resources in the freeList; + // this must be called with mMutex unlocked. + void releaseBufferFreeListUnlocked(nsTArray& freeList); + + // freeBufferLocked frees the GraphicBuffer and sync resources for the + // given slot. + //void freeBufferLocked(int index); + + // freeAllBuffersLocked frees the GraphicBuffer and sync resources for + // all slots. + //void freeAllBuffersLocked(); + void freeAllBuffersLocked(nsTArray& freeList); + + // setDefaultMaxBufferCountLocked sets the maximum number of buffer slots + // that will be used if the producer does not override the buffer slot + // count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. + // The initial default is 2. + status_t setDefaultMaxBufferCountLocked(int count); + + // getMinUndequeuedBufferCount returns the minimum number of buffers + // that must remain in a state other than DEQUEUED. + // The async parameter tells whether we're in asynchronous mode. + int getMinUndequeuedBufferCount(bool async) const; + + // getMinBufferCountLocked returns the minimum number of buffers allowed + // given the current GonkBufferQueue state. + // The async parameter tells whether we're in asynchronous mode. + int getMinMaxBufferCountLocked(bool async) const; + + // getMaxBufferCountLocked returns the maximum number of buffers that can + // be allocated at once. This value depends upon the following member + // variables: + // + // mDequeueBufferCannotBlock + // mMaxAcquiredBufferCount + // mDefaultMaxBufferCount + // mOverrideMaxBufferCount + // async parameter + // + // Any time one of these member variables is changed while a producer is + // connected, mDequeueCondition must be broadcast. + int getMaxBufferCountLocked(bool async) const; + + // stillTracking returns true iff the buffer item is still being tracked + // in one of the slots. + bool stillTracking(const BufferItem *item) const; + + struct BufferSlot { + + BufferSlot() + : mSurfaceDescriptor(SurfaceDescriptor()), + mBufferState(BufferSlot::FREE), + mRequestBufferCalled(false), + mFrameNumber(0), + mAcquireCalled(false), + mNeedsCleanupOnRelease(false) { + } + + // mGraphicBuffer points to the buffer allocated for this slot or is NULL + // if no buffer has been allocated. + sp mGraphicBuffer; + + // mSurfaceDescriptor is the token to remotely allocated GraphicBuffer. + SurfaceDescriptor mSurfaceDescriptor; + + // BufferState represents the different states in which a buffer slot + // can be. All slots are initially FREE. + enum BufferState { + // FREE indicates that the buffer is available to be dequeued + // by the producer. The buffer may be in use by the consumer for + // a finite time, so the buffer must not be modified until the + // associated fence is signaled. + // + // The slot is "owned" by GonkBufferQueue. It transitions to DEQUEUED + // when dequeueBuffer is called. + FREE = 0, + + // DEQUEUED indicates that the buffer has been dequeued by the + // producer, but has not yet been queued or canceled. The + // producer may modify the buffer's contents as soon as the + // associated ready fence is signaled. + // + // The slot is "owned" by the producer. It can transition to + // QUEUED (via queueBuffer) or back to FREE (via cancelBuffer). + DEQUEUED = 1, + + // QUEUED indicates that the buffer has been filled by the + // producer and queued for use by the consumer. The buffer + // contents may continue to be modified for a finite time, so + // the contents must not be accessed until the associated fence + // is signaled. + // + // The slot is "owned" by GonkBufferQueue. It can transition to + // ACQUIRED (via acquireBuffer) or to FREE (if another buffer is + // queued in asynchronous mode). + QUEUED = 2, + + // ACQUIRED indicates that the buffer has been acquired by the + // consumer. As with QUEUED, the contents must not be accessed + // by the consumer until the fence is signaled. + // + // The slot is "owned" by the consumer. It transitions to FREE + // when releaseBuffer is called. + ACQUIRED = 3 + }; + + // mBufferState is the current state of this buffer slot. + BufferState mBufferState; + + // mRequestBufferCalled is used for validating that the producer did + // call requestBuffer() when told to do so. Technically this is not + // needed but useful for debugging and catching producer bugs. + bool mRequestBufferCalled; + + // mFrameNumber is the number of the queued frame for this slot. This + // is used to dequeue buffers in LRU order (useful because buffers + // may be released before their release fence is signaled). + uint64_t mFrameNumber; + + // mFence is a fence which will signal when work initiated by the + // previous owner of the buffer is finished. When the buffer is FREE, + // the fence indicates when the consumer has finished reading + // from the buffer, or when the producer has finished writing if it + // called cancelBuffer after queueing some writes. When the buffer is + // QUEUED, it indicates when the producer has finished filling the + // buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been + // passed to the consumer or producer along with ownership of the + // buffer, and mFence is set to NO_FENCE. + sp mFence; + + // Indicates whether this buffer has been seen by a consumer yet + bool mAcquireCalled; + + // Indicates whether this buffer needs to be cleaned up by the + // consumer. This is set when a buffer in ACQUIRED state is freed. + // It causes releaseBuffer to return STALE_BUFFER_SLOT. + bool mNeedsCleanupOnRelease; + }; + + // mSlots is the array of buffer slots that must be mirrored on the + // producer side. This allows buffer ownership to be transferred between + // the producer and consumer without sending a GraphicBuffer over binder. + // The entire array is initialized to NULL at construction time, and + // buffers are allocated for a slot when requestBuffer is called with + // that slot's index. + BufferSlot mSlots[NUM_BUFFER_SLOTS]; + + // mDefaultWidth holds the default width of allocated buffers. It is used + // in dequeueBuffer() if a width and height of zero is specified. + uint32_t mDefaultWidth; + + // mDefaultHeight holds the default height of allocated buffers. It is used + // in dequeueBuffer() if a width and height of zero is specified. + uint32_t mDefaultHeight; + + // mMaxAcquiredBufferCount is the number of buffers that the consumer may + // acquire at one time. It defaults to 1 and can be changed by the + // consumer via the setMaxAcquiredBufferCount method, but this may only be + // done when no producer is connected to the GonkBufferQueue. + // + // This value is used to derive the value returned for the + // MIN_UNDEQUEUED_BUFFERS query by the producer. + int mMaxAcquiredBufferCount; + + // mDefaultMaxBufferCount is the default limit on the number of buffers + // that will be allocated at one time. This default limit is set by the + // consumer. The limit (as opposed to the default limit) may be + // overridden by the producer. + int mDefaultMaxBufferCount; + + // mOverrideMaxBufferCount is the limit on the number of buffers that will + // be allocated at one time. This value is set by the image producer by + // calling setBufferCount. The default is zero, which means the producer + // doesn't care about the number of buffers in the pool. In that case + // mDefaultMaxBufferCount is used as the limit. + int mOverrideMaxBufferCount; + + // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to + // allocate new GraphicBuffer objects. + sp mGraphicBufferAlloc; + + // mConsumerListener is used to notify the connected consumer of + // asynchronous events that it may wish to react to. It is initially set + // to NULL and is written by consumerConnect and consumerDisconnect. + sp mConsumerListener; + + // mConsumerControlledByApp whether the connected consumer is controlled by the + // application. + bool mConsumerControlledByApp; + + // mDequeueBufferCannotBlock whether dequeueBuffer() isn't allowed to block. + // this flag is set during connect() when both consumer and producer are controlled + // by the application. + bool mDequeueBufferCannotBlock; + + // mUseAsyncBuffer whether an extra buffer is used in async mode to prevent + // dequeueBuffer() from ever blocking. + bool mUseAsyncBuffer; + + // mConnectedApi indicates the producer API that is currently connected + // to this GonkBufferQueue. It defaults to NO_CONNECTED_API (= 0), and gets + // updated by the connect and disconnect methods. + int mConnectedApi; + + // mDequeueCondition condition used for dequeueBuffer in synchronous mode + mutable Condition mDequeueCondition; + + // mQueue is a FIFO of queued buffers used in synchronous mode + typedef Vector Fifo; + Fifo mQueue; + + // mAbandoned indicates that the GonkBufferQueue will no longer be used to + // consume image buffers pushed to it using the IGraphicBufferProducer + // interface. It is initialized to false, and set to true in the + // consumerDisconnect method. A GonkBufferQueue that has been abandoned will + // return the NO_INIT error from all IGraphicBufferProducer methods + // capable of returning an error. + bool mAbandoned; + + // mConsumerName is a string used to identify the GonkBufferQueue in log + // messages. It is set by the setConsumerName method. + String8 mConsumerName; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of GonkBufferQueue objects. It must be locked whenever the + // member variables are accessed. + mutable Mutex mMutex; + + // mFrameCounter is the free running counter, incremented on every + // successful queueBuffer call, and buffer allocation. + uint64_t mFrameCounter; + + // mBufferHasBeenQueued is true once a buffer has been queued. It is + // reset when something causes all buffers to be freed (e.g. changing the + // buffer count). + bool mBufferHasBeenQueued; + + // mDefaultBufferFormat can be set so it will override + // the buffer format when it isn't specified in dequeueBuffer + uint32_t mDefaultBufferFormat; + + // mConsumerUsageBits contains flags the consumer wants for GraphicBuffers + uint32_t mConsumerUsageBits; + + // mTransformHint is used to optimize for screen rotations + uint32_t mTransformHint; + + // mConnectedProducerToken is used to set a binder death notification on the producer + sp mConnectedProducerToken; + + // mGeneration is the current generation of buffer slots + uint32_t mGeneration; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_BUFFERQUEUE_H diff --git a/widget/gonk/nativewindow/GonkConsumerBase.cpp b/widget/gonk/nativewindow/GonkConsumerBaseJB.cpp similarity index 99% rename from widget/gonk/nativewindow/GonkConsumerBase.cpp rename to widget/gonk/nativewindow/GonkConsumerBaseJB.cpp index 6f3d5968f17e..4b2b3b42a5be 100755 --- a/widget/gonk/nativewindow/GonkConsumerBase.cpp +++ b/widget/gonk/nativewindow/GonkConsumerBaseJB.cpp @@ -27,7 +27,7 @@ #include #include -#include "GonkConsumerBase.h" +#include "GonkConsumerBaseJB.h" // Macros for including the GonkConsumerBase name in log messages #define CB_LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) diff --git a/widget/gonk/nativewindow/GonkConsumerBase.h b/widget/gonk/nativewindow/GonkConsumerBaseJB.h similarity index 98% rename from widget/gonk/nativewindow/GonkConsumerBase.h rename to widget/gonk/nativewindow/GonkConsumerBaseJB.h index bd0ce9ad8019..8f523af372a3 100755 --- a/widget/gonk/nativewindow/GonkConsumerBase.h +++ b/widget/gonk/nativewindow/GonkConsumerBaseJB.h @@ -15,8 +15,8 @@ * limitations under the License. */ -#ifndef NATIVEWINDOW_GONKCONSUMERBASE_H -#define NATIVEWINDOW_GONKCONSUMERBASE_H +#ifndef NATIVEWINDOW_GONKCONSUMERBASE_JB_H +#define NATIVEWINDOW_GONKCONSUMERBASE_JB_H #include @@ -24,7 +24,7 @@ #include #include -#include "GonkBufferQueue.h" +#include "GonkBufferQueueJB.h" namespace android { // ---------------------------------------------------------------------------- diff --git a/widget/gonk/nativewindow/GonkConsumerBaseKK.cpp b/widget/gonk/nativewindow/GonkConsumerBaseKK.cpp new file mode 100644 index 000000000000..342ba52bd2db --- /dev/null +++ b/widget/gonk/nativewindow/GonkConsumerBaseKK.cpp @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2013 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "GonkConsumerBase" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#define EGL_EGLEXT_PROTOTYPES + +#include + +#include +#include +#include + +#include "GonkConsumerBaseKK.h" + +// Macros for including the GonkConsumerBase name in log messages +#define CB_LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +#define CB_LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#define CB_LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define CB_LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) +#define CB_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) + +namespace android { + +// Get an ID that's unique within this process. +static int32_t createProcessUniqueId() { + static volatile int32_t globalCounter = 0; + return android_atomic_inc(&globalCounter); +} + +GonkConsumerBase::GonkConsumerBase(const sp& bufferQueue, bool controlledByApp) : + mAbandoned(false), + mConsumer(bufferQueue) { + // Choose a name using the PID and a process-unique ID. + mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); + + // Note that we can't create an sp<...>(this) in a ctor that will not keep a + // reference once the ctor ends, as that would cause the refcount of 'this' + // dropping to 0 at the end of the ctor. Since all we need is a wp<...> + // that's what we create. + wp listener = static_cast(this); + sp proxy = new GonkBufferQueue::ProxyConsumerListener(listener); + + status_t err = mConsumer->consumerConnect(proxy, controlledByApp); + if (err != NO_ERROR) { + CB_LOGE("GonkConsumerBase: error connecting to GonkBufferQueue: %s (%d)", + strerror(-err), err); + } else { + mConsumer->setConsumerName(mName); + } +} + +GonkConsumerBase::~GonkConsumerBase() { + CB_LOGV("~GonkConsumerBase"); + Mutex::Autolock lock(mMutex); + + // Verify that abandon() has been called before we get here. This should + // be done by GonkConsumerBase::onLastStrongRef(), but it's possible for a + // derived class to override that method and not call + // GonkConsumerBase::onLastStrongRef(). + LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~GonkConsumerBase was called, but the " + "consumer is not abandoned!", mName.string()); +} + +void GonkConsumerBase::onLastStrongRef(const void* id) { + abandon(); +} + +void GonkConsumerBase::freeBufferLocked(int slotIndex) { + CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); + mSlots[slotIndex].mGraphicBuffer = 0; + mSlots[slotIndex].mFence = Fence::NO_FENCE; + mSlots[slotIndex].mFrameNumber = 0; +} + +// Used for refactoring, should not be in final interface +sp GonkConsumerBase::getBufferQueue() const { + Mutex::Autolock lock(mMutex); + return mConsumer; +} + +void GonkConsumerBase::onFrameAvailable() { + CB_LOGV("onFrameAvailable"); + + sp listener; + { // scope for the lock + Mutex::Autolock lock(mMutex); + listener = mFrameAvailableListener.promote(); + } + + if (listener != NULL) { + CB_LOGV("actually calling onFrameAvailable"); + listener->onFrameAvailable(); + } +} + +void GonkConsumerBase::onBuffersReleased() { + Mutex::Autolock lock(mMutex); + + CB_LOGV("onBuffersReleased"); + + if (mAbandoned) { + // Nothing to do if we're already abandoned. + return; + } + + uint32_t mask = 0; + mConsumer->getReleasedBuffers(&mask); + for (int i = 0; i < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) { + if (mask & (1 << i)) { + freeBufferLocked(i); + } + } +} + +void GonkConsumerBase::abandon() { + CB_LOGV("abandon"); + Mutex::Autolock lock(mMutex); + + if (!mAbandoned) { + abandonLocked(); + mAbandoned = true; + } +} + +void GonkConsumerBase::abandonLocked() { + CB_LOGV("abandonLocked"); + for (int i =0; i < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) { + freeBufferLocked(i); + } + // disconnect from the BufferQueue + mConsumer->consumerDisconnect(); + mConsumer.clear(); +} + +void GonkConsumerBase::setFrameAvailableListener( + const wp& listener) { + CB_LOGV("setFrameAvailableListener"); + Mutex::Autolock lock(mMutex); + mFrameAvailableListener = listener; +} + +void GonkConsumerBase::dump(String8& result) const { + dump(result, ""); +} + +void GonkConsumerBase::dump(String8& result, const char* prefix) const { + Mutex::Autolock _l(mMutex); + dumpLocked(result, prefix); +} + +void GonkConsumerBase::dumpLocked(String8& result, const char* prefix) const { + result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned)); + + if (!mAbandoned) { + mConsumer->dump(result, prefix); + } +} + +status_t GonkConsumerBase::acquireBufferLocked(IGonkGraphicBufferConsumer::BufferItem *item, + nsecs_t presentWhen) { + status_t err = mConsumer->acquireBuffer(item, presentWhen); + if (err != NO_ERROR) { + return err; + } + + if (item->mGraphicBuffer != NULL) { + mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer; + } + + mSlots[item->mBuf].mFrameNumber = item->mFrameNumber; + mSlots[item->mBuf].mFence = item->mFence; + + CB_LOGV("acquireBufferLocked: -> slot=%d", item->mBuf); + + return OK; +} + +status_t GonkConsumerBase::addReleaseFence(int slot, + const sp graphicBuffer, const sp& fence) { + Mutex::Autolock lock(mMutex); + return addReleaseFenceLocked(slot, graphicBuffer, fence); +} + +status_t GonkConsumerBase::addReleaseFenceLocked(int slot, + const sp graphicBuffer, const sp& fence) { + CB_LOGV("addReleaseFenceLocked: slot=%d", slot); + + // If consumer no longer tracks this graphicBuffer, we can safely + // drop this fence, as it will never be received by the producer. + if (!stillTracking(slot, graphicBuffer)) { + return OK; + } + + if (!mSlots[slot].mFence.get()) { + mSlots[slot].mFence = fence; + } else { + sp mergedFence = Fence::merge( + String8::format("%.28s:%d", mName.string(), slot), + mSlots[slot].mFence, fence); + if (!mergedFence.get()) { + CB_LOGE("failed to merge release fences"); + // synchronization is broken, the best we can do is hope fences + // signal in order so the new fence will act like a union + mSlots[slot].mFence = fence; + return BAD_VALUE; + } + mSlots[slot].mFence = mergedFence; + } + + return OK; +} + +status_t GonkConsumerBase::releaseBufferLocked(int slot, const sp graphicBuffer) { + // If consumer no longer tracks this graphicBuffer (we received a new + // buffer on the same slot), the buffer producer is definitely no longer + // tracking it. + if (!stillTracking(slot, graphicBuffer)) { + return OK; + } + + CB_LOGV("releaseBufferLocked: slot=%d/%llu", + slot, mSlots[slot].mFrameNumber); + status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, mSlots[slot].mFence); + if (err == GonkBufferQueue::STALE_BUFFER_SLOT) { + freeBufferLocked(slot); + } + + mSlots[slot].mFence = Fence::NO_FENCE; + + return err; +} + +bool GonkConsumerBase::stillTracking(int slot, + const sp graphicBuffer) { + if (slot < 0 || slot >= GonkBufferQueue::NUM_BUFFER_SLOTS) { + return false; + } + return (mSlots[slot].mGraphicBuffer != NULL && + mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle); +} + +} // namespace android diff --git a/widget/gonk/nativewindow/GonkConsumerBaseKK.h b/widget/gonk/nativewindow/GonkConsumerBaseKK.h new file mode 100644 index 000000000000..e198ad843586 --- /dev/null +++ b/widget/gonk/nativewindow/GonkConsumerBaseKK.h @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2013 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NATIVEWINDOW_GONKCONSUMERBASE_KK_H +#define NATIVEWINDOW_GONKCONSUMERBASE_KK_H + +#include + +#include +#include +#include +#include + +#include "GonkBufferQueueKK.h" + +namespace android { +// ---------------------------------------------------------------------------- + +class String8; + +// GonkConsumerBase is a base class for GonkBufferQueue consumer end-points. It +// handles common tasks like management of the connection to the GonkBufferQueue +// and the buffer pool. +class GonkConsumerBase : public virtual RefBase, + protected ConsumerListener { +public: + struct FrameAvailableListener : public virtual RefBase { + // onFrameAvailable() is called each time an additional frame becomes + // available for consumption. This means that frames that are queued + // while in asynchronous mode only trigger the callback if no previous + // frames are pending. Frames queued while in synchronous mode always + // trigger the callback. + // + // This is called without any lock held and can be called concurrently + // by multiple threads. + virtual void onFrameAvailable() = 0; + }; + + virtual ~GonkConsumerBase(); + + // abandon frees all the buffers and puts the GonkConsumerBase into the + // 'abandoned' state. Once put in this state the GonkConsumerBase can never + // leave it. When in the 'abandoned' state, all methods of the + // IGraphicBufferProducer interface will fail with the NO_INIT error. + // + // Note that while calling this method causes all the buffers to be freed + // from the perspective of the the GonkConsumerBase, if there are additional + // references on the buffers (e.g. if a buffer is referenced by a client + // or by OpenGL ES as a texture) then those buffer will remain allocated. + void abandon(); + + // set the name of the GonkConsumerBase that will be used to identify it in + // log messages. + void setName(const String8& name); + + // getBufferQueue returns the GonkBufferQueue object to which this + // GonkConsumerBase is connected. + sp getBufferQueue() const; + + // dump writes the current state to a string. Child classes should add + // their state to the dump by overriding the dumpLocked method, which is + // called by these methods after locking the mutex. + void dump(String8& result) const; + void dump(String8& result, const char* prefix) const; + + // setFrameAvailableListener sets the listener object that will be notified + // when a new frame becomes available. + void setFrameAvailableListener(const wp& listener); + +private: + GonkConsumerBase(const GonkConsumerBase&); + void operator=(const GonkConsumerBase&); + +protected: + + // GonkConsumerBase constructs a new GonkConsumerBase object to consume image + // buffers from the given GonkBufferQueue. + GonkConsumerBase(const sp& bufferQueue, bool controlledByApp = false); + + // onLastStrongRef gets called by RefBase just before the dtor of the most + // derived class. It is used to clean up the buffers so that GonkConsumerBase + // can coordinate the clean-up by calling into virtual methods implemented + // by the derived classes. This would not be possible from the + // ConsuemrBase dtor because by the time that gets called the derived + // classes have already been destructed. + // + // This methods should not need to be overridden by derived classes, but + // if they are overridden the GonkConsumerBase implementation must be called + // from the derived class. + virtual void onLastStrongRef(const void* id); + + // Implementation of the GonkBufferQueue::ConsumerListener interface. These + // calls are used to notify the GonkConsumerBase of asynchronous events in the + // GonkBufferQueue. These methods should not need to be overridden by derived + // classes, but if they are overridden the GonkConsumerBase implementation + // must be called from the derived class. + virtual void onFrameAvailable(); + virtual void onBuffersReleased(); + + // freeBufferLocked frees up the given buffer slot. If the slot has been + // initialized this will release the reference to the GraphicBuffer in that + // slot. Otherwise it has no effect. + // + // Derived classes should override this method to clean up any state they + // keep per slot. If it is overridden, the derived class's implementation + // must call GonkConsumerBase::freeBufferLocked. + // + // This method must be called with mMutex locked. + virtual void freeBufferLocked(int slotIndex); + + // abandonLocked puts the GonkBufferQueue into the abandoned state, causing + // all future operations on it to fail. This method rather than the public + // abandon method should be overridden by child classes to add abandon- + // time behavior. + // + // Derived classes should override this method to clean up any object + // state they keep (as opposed to per-slot state). If it is overridden, + // the derived class's implementation must call GonkConsumerBase::abandonLocked. + // + // This method must be called with mMutex locked. + virtual void abandonLocked(); + + // dumpLocked dumps the current state of the GonkConsumerBase object to the + // result string. Each line is prefixed with the string pointed to by the + // prefix argument. The buffer argument points to a buffer that may be + // used for intermediate formatting data, and the size of that buffer is + // indicated by the size argument. + // + // Derived classes should override this method to dump their internal + // state. If this method is overridden the derived class's implementation + // should call GonkConsumerBase::dumpLocked. + // + // This method must be called with mMutex locked. + virtual void dumpLocked(String8& result, const char* prefix) const; + + // acquireBufferLocked fetches the next buffer from the GonkBufferQueue and + // updates the buffer slot for the buffer returned. + // + // Derived classes should override this method to perform any + // initialization that must take place the first time a buffer is assigned + // to a slot. If it is overridden the derived class's implementation must + // call GonkConsumerBase::acquireBufferLocked. + virtual status_t acquireBufferLocked(IGonkGraphicBufferConsumer::BufferItem *item, + nsecs_t presentWhen); + + // releaseBufferLocked relinquishes control over a buffer, returning that + // control to the GonkBufferQueue. + // + // Derived classes should override this method to perform any cleanup that + // must take place when a buffer is released back to the GonkBufferQueue. If + // it is overridden the derived class's implementation must call + // GonkConsumerBase::releaseBufferLocked. + virtual status_t releaseBufferLocked(int slot, const sp graphicBuffer); + + // returns true iff the slot still has the graphicBuffer in it. + bool stillTracking(int slot, const sp graphicBuffer); + + // addReleaseFence* adds the sync points associated with a fence to the set + // of sync points that must be reached before the buffer in the given slot + // may be used after the slot has been released. This should be called by + // derived classes each time some asynchronous work is kicked off that + // references the buffer. + status_t addReleaseFence(int slot, + const sp graphicBuffer, const sp& fence); + status_t addReleaseFenceLocked(int slot, + const sp graphicBuffer, const sp& fence); + + // Slot contains the information and object references that + // GonkConsumerBase maintains about a GonkBufferQueue buffer slot. + struct Slot { + // mGraphicBuffer is the Gralloc buffer store in the slot or NULL if + // no Gralloc buffer is in the slot. + sp mGraphicBuffer; + + // mFence is a fence which will signal when the buffer associated with + // this buffer slot is no longer being used by the consumer and can be + // overwritten. The buffer can be dequeued before the fence signals; + // the producer is responsible for delaying writes until it signals. + sp mFence; + + // the frame number of the last acquired frame for this slot + uint64_t mFrameNumber; + }; + + // mSlots stores the buffers that have been allocated by the GonkBufferQueue + // for each buffer slot. It is initialized to null pointers, and gets + // filled in with the result of GonkBufferQueue::acquire when the + // client dequeues a buffer from a + // slot that has not yet been used. The buffer allocated to a slot will also + // be replaced if the requested buffer usage or geometry differs from that + // of the buffer allocated to a slot. + Slot mSlots[GonkBufferQueue::NUM_BUFFER_SLOTS]; + + // mAbandoned indicates that the GonkBufferQueue will no longer be used to + // consume images buffers pushed to it using the IGraphicBufferProducer + // interface. It is initialized to false, and set to true in the abandon + // method. A GonkBufferQueue that has been abandoned will return the NO_INIT + // error from all IGonkConsumerBase methods capable of returning an error. + bool mAbandoned; + + // mName is a string used to identify the GonkConsumerBase in log messages. + // It can be set by the setName method. + String8 mName; + + // mFrameAvailableListener is the listener object that will be called when a + // new frame becomes available. If it is not NULL it will be called from + // queueBuffer. + wp mFrameAvailableListener; + + // The GonkConsumerBase has-a GonkBufferQueue and is responsible for creating this object + // if none is supplied + sp mConsumer; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of GonkConsumerBase objects. It must be locked whenever the + // member variables are accessed or when any of the *Locked methods are + // called. + // + // This mutex is intended to be locked by derived classes. + mutable Mutex mMutex; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // NATIVEWINDOW_GONKCONSUMERBASE_H diff --git a/widget/gonk/nativewindow/GonkNativeWindow.h b/widget/gonk/nativewindow/GonkNativeWindow.h index 5cad6498469f..dfe6a154b998 100644 --- a/widget/gonk/nativewindow/GonkNativeWindow.h +++ b/widget/gonk/nativewindow/GonkNativeWindow.h @@ -13,7 +13,9 @@ * limitations under the License. */ -#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19 +# include "GonkNativeWindowKK.h" +#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 # include "GonkNativeWindowJB.h" #elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION == 15 # include "GonkNativeWindowICS.h" diff --git a/widget/gonk/nativewindow/GonkNativeWindowClient.h b/widget/gonk/nativewindow/GonkNativeWindowClient.h index ce33c24819b0..3b0c8d7261c2 100644 --- a/widget/gonk/nativewindow/GonkNativeWindowClient.h +++ b/widget/gonk/nativewindow/GonkNativeWindowClient.h @@ -13,7 +13,9 @@ * limitations under the License. */ -#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19 +# include "GonkNativeWindowClientKK.h" +#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 # include "GonkNativeWindowClientJB.h" #elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION == 15 # include "GonkNativeWindowClientICS.h" diff --git a/widget/gonk/nativewindow/GonkNativeWindowClientJB.cpp b/widget/gonk/nativewindow/GonkNativeWindowClientJB.cpp index a003ff32e182..9e2c9fc9d0c1 100755 --- a/widget/gonk/nativewindow/GonkNativeWindowClientJB.cpp +++ b/widget/gonk/nativewindow/GonkNativeWindowClientJB.cpp @@ -286,6 +286,7 @@ int GonkNativeWindowClient::queueBuffer(android_native_buffer_t* buffer, int fen sp fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); IGraphicBufferProducer::QueueBufferOutput output; + IGraphicBufferProducer::QueueBufferInput input(timestamp, crop, mScalingMode, mTransform, fence); status_t err = mBufferProducer->queueBuffer(i, input, &output); @@ -496,8 +497,10 @@ int GonkNativeWindowClient::dispatchUnlockAndPost(va_list args) { int GonkNativeWindowClient::connect(int api) { ALOGV("GonkNativeWindowClient::connect"); + Mutex::Autolock lock(mMutex); IGraphicBufferProducer::QueueBufferOutput output; + int err = mBufferProducer->connect(api, &output); if (err == NO_ERROR) { uint32_t numPendingBuffers = 0; diff --git a/widget/gonk/nativewindow/GonkNativeWindowClientKK.cpp b/widget/gonk/nativewindow/GonkNativeWindowClientKK.cpp new file mode 100644 index 000000000000..6cbf8b6451b9 --- /dev/null +++ b/widget/gonk/nativewindow/GonkNativeWindowClientKK.cpp @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2013 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "GonkNativeWindowClient" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#include + +#include + +#include +#include + +#include + +#include "GonkNativeWindowClientKK.h" + +namespace android { + +GonkNativeWindowClient::GonkNativeWindowClient( + const sp& bufferProducer, + bool controlledByApp) + : mGraphicBufferProducer(bufferProducer) +{ + // Initialize the ANativeWindow function pointers. + ANativeWindow::setSwapInterval = hook_setSwapInterval; + ANativeWindow::dequeueBuffer = hook_dequeueBuffer; + ANativeWindow::cancelBuffer = hook_cancelBuffer; + ANativeWindow::queueBuffer = hook_queueBuffer; + ANativeWindow::query = hook_query; + ANativeWindow::perform = hook_perform; + + ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED; + ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED; + ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED; + ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED; + + const_cast(ANativeWindow::minSwapInterval) = 0; + const_cast(ANativeWindow::maxSwapInterval) = 1; + + mReqWidth = 0; + mReqHeight = 0; + mReqFormat = 0; + mReqUsage = 0; + mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; + mCrop.clear(); + mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; + mTransform = 0; + mDefaultWidth = 0; + mDefaultHeight = 0; + mUserWidth = 0; + mUserHeight = 0; + mTransformHint = 0; + mConsumerRunningBehind = false; + mConnectedToCpu = false; + mProducerControlledByApp = controlledByApp; + mSwapIntervalZero = false; +} + +GonkNativeWindowClient::~GonkNativeWindowClient() { + if (mConnectedToCpu) { + GonkNativeWindowClient::disconnect(NATIVE_WINDOW_API_CPU); + } +} + +sp GonkNativeWindowClient::getIGraphicBufferProducer() const { + return mGraphicBufferProducer; +} + +int GonkNativeWindowClient::hook_setSwapInterval(ANativeWindow* window, int interval) { + GonkNativeWindowClient* c = getSelf(window); + return c->setSwapInterval(interval); +} + +int GonkNativeWindowClient::hook_dequeueBuffer(ANativeWindow* window, + ANativeWindowBuffer** buffer, int* fenceFd) { + GonkNativeWindowClient* c = getSelf(window); + return c->dequeueBuffer(buffer, fenceFd); +} + +int GonkNativeWindowClient::hook_cancelBuffer(ANativeWindow* window, + ANativeWindowBuffer* buffer, int fenceFd) { + GonkNativeWindowClient* c = getSelf(window); + return c->cancelBuffer(buffer, fenceFd); +} + +int GonkNativeWindowClient::hook_queueBuffer(ANativeWindow* window, + ANativeWindowBuffer* buffer, int fenceFd) { + GonkNativeWindowClient* c = getSelf(window); + return c->queueBuffer(buffer, fenceFd); +} + +int GonkNativeWindowClient::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer** buffer) { + GonkNativeWindowClient* c = getSelf(window); + ANativeWindowBuffer* buf; + int fenceFd = -1; + int result = c->dequeueBuffer(&buf, &fenceFd); + sp fence(new Fence(fenceFd)); + int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED"); + if (waitResult != OK) { + ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", + waitResult); + c->cancelBuffer(buf, -1); + return waitResult; + } + *buffer = buf; + return result; +} + +int GonkNativeWindowClient::hook_cancelBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer) { + GonkNativeWindowClient* c = getSelf(window); + return c->cancelBuffer(buffer, -1); +} + +int GonkNativeWindowClient::hook_lockBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer) { + GonkNativeWindowClient* c = getSelf(window); + return c->lockBuffer_DEPRECATED(buffer); +} + +int GonkNativeWindowClient::hook_queueBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer) { + GonkNativeWindowClient* c = getSelf(window); + return c->queueBuffer(buffer, -1); +} + +int GonkNativeWindowClient::hook_query(const ANativeWindow* window, + int what, int* value) { + const GonkNativeWindowClient* c = getSelf(window); + return c->query(what, value); +} + +int GonkNativeWindowClient::hook_perform(ANativeWindow* window, int operation, ...) { + va_list args; + va_start(args, operation); + GonkNativeWindowClient* c = getSelf(window); + return c->perform(operation, args); +} + +int GonkNativeWindowClient::setSwapInterval(int interval) { + ATRACE_CALL(); + // EGL specification states: + // interval is silently clamped to minimum and maximum implementation + // dependent values before being stored. + + if (interval < minSwapInterval) + interval = minSwapInterval; + + if (interval > maxSwapInterval) + interval = maxSwapInterval; + + return NO_ERROR; +} + +int GonkNativeWindowClient::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::dequeueBuffer"); + Mutex::Autolock lock(mMutex); + int buf = -1; + int reqW = mReqWidth ? mReqWidth : mUserWidth; + int reqH = mReqHeight ? mReqHeight : mUserHeight; + sp fence; + status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, mSwapIntervalZero, + reqW, reqH, mReqFormat, mReqUsage); + if (result < 0) { + ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer(%d, %d, %d, %d)" + "failed: %d", mReqWidth, mReqHeight, mReqFormat, mReqUsage, + result); + return result; + } + sp& gbuf(mSlots[buf].buffer); + + // this should never happen + ALOGE_IF(fence == NULL, "Surface::dequeueBuffer: received null Fence! buf=%d", buf); + + if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) { + freeAllBuffers(); + } + + if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) { + result = mGraphicBufferProducer->requestBuffer(buf, &gbuf); + if (result != NO_ERROR) { + ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result); + return result; + } + } + + if (fence->isValid()) { + *fenceFd = fence->dup(); + if (*fenceFd == -1) { + ALOGE("dequeueBuffer: error duping fence: %d", errno); + // dup() should never fail; something is badly wrong. Soldier on + // and hope for the best; the worst that should happen is some + // visible corruption that lasts until the next frame. + } + } else { + *fenceFd = -1; + } + + *buffer = gbuf.get(); + return OK; +} + +int GonkNativeWindowClient::cancelBuffer(android_native_buffer_t* buffer, + int fenceFd) { + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::cancelBuffer"); + Mutex::Autolock lock(mMutex); + int i = getSlotFromBufferLocked(buffer); + if (i < 0) { + return i; + } + sp fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + mGraphicBufferProducer->cancelBuffer(i, fence); + return OK; +} + +int GonkNativeWindowClient::getSlotFromBufferLocked( + android_native_buffer_t* buffer) const { + bool dumpedState = false; + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (mSlots[i].buffer != NULL && + mSlots[i].buffer->handle == buffer->handle) { + return i; + } + } + ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle); + return BAD_VALUE; +} + +int GonkNativeWindowClient::lockBuffer_DEPRECATED(android_native_buffer_t* buffer) { + ALOGV("GonkNativeWindowClient::lockBuffer"); + Mutex::Autolock lock(mMutex); + return OK; +} + +int GonkNativeWindowClient::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::queueBuffer"); + Mutex::Autolock lock(mMutex); + int64_t timestamp; + bool isAutoTimestamp = false; + if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) { + timestamp = systemTime(SYSTEM_TIME_MONOTONIC); + isAutoTimestamp = true; + ALOGV("GonkNativeWindowClient::queueBuffer making up timestamp: %.2f ms", + timestamp / 1000000.f); + } else { + timestamp = mTimestamp; + } + int i = getSlotFromBufferLocked(buffer); + if (i < 0) { + return i; + } + + + // Make sure the crop rectangle is entirely inside the buffer. + Rect crop; + mCrop.intersect(Rect(buffer->width, buffer->height), &crop); + + sp fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + IGraphicBufferProducer::QueueBufferOutput output; + IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp, + crop, mScalingMode, mTransform, mSwapIntervalZero, fence); + status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); + if (err != OK) { + ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err); + } + uint32_t numPendingBuffers = 0; + output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint, + &numPendingBuffers); + + mConsumerRunningBehind = (numPendingBuffers >= 2); + + return err; +} + +int GonkNativeWindowClient::query(int what, int* value) const { + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::query"); + { // scope for the lock + Mutex::Autolock lock(mMutex); + switch (what) { + case NATIVE_WINDOW_FORMAT: + if (mReqFormat) { + *value = mReqFormat; + return NO_ERROR; + } + break; + case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: { + //sp composer( + // ComposerService::getComposerService()); + //if (composer->authenticateSurfaceTexture(mGraphicBufferProducer)) { + // *value = 1; + //} else { + *value = 0; + //} + return NO_ERROR; + } + case NATIVE_WINDOW_CONCRETE_TYPE: + *value = NATIVE_WINDOW_SURFACE; + return NO_ERROR; + case NATIVE_WINDOW_DEFAULT_WIDTH: + *value = mUserWidth ? mUserWidth : mDefaultWidth; + return NO_ERROR; + case NATIVE_WINDOW_DEFAULT_HEIGHT: + *value = mUserHeight ? mUserHeight : mDefaultHeight; + return NO_ERROR; + case NATIVE_WINDOW_TRANSFORM_HINT: + *value = mTransformHint; + return NO_ERROR; + case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: { + status_t err = NO_ERROR; + if (!mConsumerRunningBehind) { + *value = 0; + } else { + err = mGraphicBufferProducer->query(what, value); + if (err == NO_ERROR) { + mConsumerRunningBehind = *value; + } + } + return err; + } + } + } + return mGraphicBufferProducer->query(what, value); +} + +int GonkNativeWindowClient::perform(int operation, va_list args) +{ + int res = NO_ERROR; + switch (operation) { + case NATIVE_WINDOW_CONNECT: + // deprecated. must return NO_ERROR. + break; + case NATIVE_WINDOW_DISCONNECT: + // deprecated. must return NO_ERROR. + break; + case NATIVE_WINDOW_SET_USAGE: + res = dispatchSetUsage(args); + break; + case NATIVE_WINDOW_SET_CROP: + res = dispatchSetCrop(args); + break; + case NATIVE_WINDOW_SET_BUFFER_COUNT: + res = dispatchSetBufferCount(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: + res = dispatchSetBuffersGeometry(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: + res = dispatchSetBuffersTransform(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP: + res = dispatchSetBuffersTimestamp(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: + res = dispatchSetBuffersDimensions(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS: + res = dispatchSetBuffersUserDimensions(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_FORMAT: + res = dispatchSetBuffersFormat(args); + break; + case NATIVE_WINDOW_LOCK: + res = dispatchLock(args); + break; + case NATIVE_WINDOW_UNLOCK_AND_POST: + res = dispatchUnlockAndPost(args); + break; + case NATIVE_WINDOW_SET_SCALING_MODE: + res = dispatchSetScalingMode(args); + break; + case NATIVE_WINDOW_API_CONNECT: + res = dispatchConnect(args); + break; + case NATIVE_WINDOW_API_DISCONNECT: + res = dispatchDisconnect(args); + break; + default: + res = NAME_NOT_FOUND; + break; + } + return res; +} + +int GonkNativeWindowClient::dispatchConnect(va_list args) { + int api = va_arg(args, int); + return connect(api); +} + +int GonkNativeWindowClient::dispatchDisconnect(va_list args) { + int api = va_arg(args, int); + return disconnect(api); +} + +int GonkNativeWindowClient::dispatchSetUsage(va_list args) { + int usage = va_arg(args, int); + return setUsage(usage); +} + +int GonkNativeWindowClient::dispatchSetCrop(va_list args) { + android_native_rect_t const* rect = va_arg(args, android_native_rect_t*); + return setCrop(reinterpret_cast(rect)); +} + +int GonkNativeWindowClient::dispatchSetBufferCount(va_list args) { + size_t bufferCount = va_arg(args, size_t); + return setBufferCount(bufferCount); +} + +int GonkNativeWindowClient::dispatchSetBuffersGeometry(va_list args) { + int w = va_arg(args, int); + int h = va_arg(args, int); + int f = va_arg(args, int); + int err = setBuffersDimensions(w, h); + if (err != 0) { + return err; + } + return setBuffersFormat(f); +} + +int GonkNativeWindowClient::dispatchSetBuffersDimensions(va_list args) { + int w = va_arg(args, int); + int h = va_arg(args, int); + return setBuffersDimensions(w, h); +} + +int GonkNativeWindowClient::dispatchSetBuffersUserDimensions(va_list args) { + int w = va_arg(args, int); + int h = va_arg(args, int); + return setBuffersUserDimensions(w, h); +} + +int GonkNativeWindowClient::dispatchSetBuffersFormat(va_list args) { + int f = va_arg(args, int); + return setBuffersFormat(f); +} + +int GonkNativeWindowClient::dispatchSetScalingMode(va_list args) { + int m = va_arg(args, int); + return setScalingMode(m); +} + +int GonkNativeWindowClient::dispatchSetBuffersTransform(va_list args) { + int transform = va_arg(args, int); + return setBuffersTransform(transform); +} + +int GonkNativeWindowClient::dispatchSetBuffersTimestamp(va_list args) { + int64_t timestamp = va_arg(args, int64_t); + return setBuffersTimestamp(timestamp); +} + +int GonkNativeWindowClient::dispatchLock(va_list args) { + ANativeWindow_Buffer* outBuffer = va_arg(args, ANativeWindow_Buffer*); + ARect* inOutDirtyBounds = va_arg(args, ARect*); + return lock(outBuffer, inOutDirtyBounds); +} + +int GonkNativeWindowClient::dispatchUnlockAndPost(va_list args) { + return unlockAndPost(); +} + + +int GonkNativeWindowClient::connect(int api) { + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::connect"); + static sp sLife = new BBinder(); + Mutex::Autolock lock(mMutex); + IGraphicBufferProducer::QueueBufferOutput output; + int err = mGraphicBufferProducer->connect(sLife, api, true, &output); + if (err == NO_ERROR) { + uint32_t numPendingBuffers = 0; + output.deflate(&mDefaultWidth, &mDefaultHeight, &mTransformHint, + &numPendingBuffers); + mConsumerRunningBehind = (numPendingBuffers >= 2); + } + if (!err && api == NATIVE_WINDOW_API_CPU) { + mConnectedToCpu = true; + } + return err; +} + + +int GonkNativeWindowClient::disconnect(int api) { + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::disconnect"); + Mutex::Autolock lock(mMutex); + freeAllBuffers(); + int err = mGraphicBufferProducer->disconnect(api); + if (!err) { + mReqFormat = 0; + mReqWidth = 0; + mReqHeight = 0; + mReqUsage = 0; + mCrop.clear(); + mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; + mTransform = 0; + if (api == NATIVE_WINDOW_API_CPU) { + mConnectedToCpu = false; + } + } + return err; +} + +int GonkNativeWindowClient::setUsage(uint32_t reqUsage) +{ + ALOGV("GonkNativeWindowClient::setUsage"); + Mutex::Autolock lock(mMutex); + mReqUsage = reqUsage; + return OK; +} + +int GonkNativeWindowClient::setCrop(Rect const* rect) +{ + ATRACE_CALL(); + + Rect realRect; + if (rect == NULL || rect->isEmpty()) { + realRect.clear(); + } else { + realRect = *rect; + } + + ALOGV("GonkNativeWindowClient::setCrop rect=[%d %d %d %d]", + realRect.left, realRect.top, realRect.right, realRect.bottom); + + Mutex::Autolock lock(mMutex); + mCrop = realRect; + return NO_ERROR; +} + +int GonkNativeWindowClient::setBufferCount(int bufferCount) +{ + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::setBufferCount"); + Mutex::Autolock lock(mMutex); + + status_t err = mGraphicBufferProducer->setBufferCount(bufferCount); + ALOGE_IF(err, "IGraphicBufferProducer::setBufferCount(%d) returned %s", + bufferCount, strerror(-err)); + + if (err == NO_ERROR) { + freeAllBuffers(); + } + + return err; +} + +int GonkNativeWindowClient::setBuffersDimensions(int w, int h) +{ + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::setBuffersDimensions"); + + if (w<0 || h<0) + return BAD_VALUE; + + if ((w && !h) || (!w && h)) + return BAD_VALUE; + + Mutex::Autolock lock(mMutex); + mReqWidth = w; + mReqHeight = h; + return NO_ERROR; +} + +int GonkNativeWindowClient::setBuffersUserDimensions(int w, int h) +{ + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::setBuffersUserDimensions"); + + if (w<0 || h<0) + return BAD_VALUE; + + if ((w && !h) || (!w && h)) + return BAD_VALUE; + + Mutex::Autolock lock(mMutex); + mUserWidth = w; + mUserHeight = h; + return NO_ERROR; +} + +int GonkNativeWindowClient::setBuffersFormat(int format) +{ + ALOGV("GonkNativeWindowClient::setBuffersFormat"); + + if (format<0) + return BAD_VALUE; + + Mutex::Autolock lock(mMutex); + mReqFormat = format; + return NO_ERROR; +} + +int GonkNativeWindowClient::setScalingMode(int mode) +{ + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::setScalingMode(%d)", mode); + + switch (mode) { + case NATIVE_WINDOW_SCALING_MODE_FREEZE: + case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: + case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: + break; + default: + ALOGE("unknown scaling mode: %d", mode); + return BAD_VALUE; + } + + Mutex::Autolock lock(mMutex); + mScalingMode = mode; + return NO_ERROR; +} + +int GonkNativeWindowClient::setBuffersTransform(int transform) +{ + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::setBuffersTransform"); + Mutex::Autolock lock(mMutex); + mTransform = transform; + return NO_ERROR; +} + +int GonkNativeWindowClient::setBuffersTimestamp(int64_t timestamp) +{ + ALOGV("GonkNativeWindowClient::setBuffersTimestamp"); + Mutex::Autolock lock(mMutex); + mTimestamp = timestamp; + return NO_ERROR; +} + +void GonkNativeWindowClient::freeAllBuffers() { + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + mSlots[i].buffer = 0; + } +} + +// ---------------------------------------------------------------------- +// the lock/unlock APIs must be used from the same thread + +// ---------------------------------------------------------------------------- + +status_t GonkNativeWindowClient::lock( + ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) +{ + return INVALID_OPERATION; +} + +status_t GonkNativeWindowClient::unlockAndPost() +{ + return INVALID_OPERATION; +} + +}; // namespace android diff --git a/widget/gonk/nativewindow/GonkNativeWindowClientKK.h b/widget/gonk/nativewindow/GonkNativeWindowClientKK.h new file mode 100644 index 000000000000..1ced95b9f008 --- /dev/null +++ b/widget/gonk/nativewindow/GonkNativeWindowClientKK.h @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2013 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_KK_H +#define NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_KK_H + +#include + +#include +#include + +#include +#include +#include + +#include "GonkBufferQueue.h" + +struct ANativeWindow_Buffer; + +namespace android { + +/* + * An implementation of ANativeWindow that feeds graphics buffers into a + * BufferQueue. + * + * This is typically used by programs that want to render frames through + * some means (maybe OpenGL, a software renderer, or a hardware decoder) + * and have the frames they create forwarded to SurfaceFlinger for + * compositing. For example, a video decoder could render a frame and call + * eglSwapBuffers(), which invokes ANativeWindow callbacks defined by + * GonkNativeWindowClient. GonkNativeWindowClient then forwards the buffers through Binder IPC + * to the BufferQueue's producer interface, providing the new frame to a + * consumer such as GLConsumer. + */ +class GonkNativeWindowClient + : public ANativeObjectBase +{ +public: + + /* + * creates a GonkNativeWindowClient from the given IGraphicBufferProducer (which concrete + * implementation is a BufferQueue). + * + * GonkNativeWindowClient is mainly state-less while it's disconnected, it can be + * viewed as a glorified IGraphicBufferProducer holder. It's therefore + * safe to create other GonkNativeWindowClients from the same IGraphicBufferProducer. + * + * However, once a GonkNativeWindowClient is connected, it'll prevent other GonkNativeWindowClients + * referring to the same IGraphicBufferProducer to become connected and + * therefore prevent them to be used as actual producers of buffers. + * + * the controlledByApp flag indicates that this Surface (producer) is + * controlled by the application. This flag is used at connect time. + */ + GonkNativeWindowClient(const sp& bufferProducer, bool controlledByApp = false); + + /* getIGraphicBufferProducer() returns the IGraphicBufferProducer this + * GonkNativeWindowClient was created with. Usually it's an error to use the + * IGraphicBufferProducer while the GonkNativeWindowClient is connected. + */ + sp getIGraphicBufferProducer() const; + + /* convenience function to check that the given surface is non NULL as + * well as its IGraphicBufferProducer */ + static bool isValid(const sp& surface) { + return surface != NULL && surface->getIGraphicBufferProducer() != NULL; + } + +protected: + virtual ~GonkNativeWindowClient(); + +private: + // can't be copied + GonkNativeWindowClient& operator = (const GonkNativeWindowClient& rhs); + GonkNativeWindowClient(const GonkNativeWindowClient& rhs); + + // ANativeWindow hooks + static int hook_cancelBuffer(ANativeWindow* window, + ANativeWindowBuffer* buffer, int fenceFd); + static int hook_dequeueBuffer(ANativeWindow* window, + ANativeWindowBuffer** buffer, int* fenceFd); + static int hook_perform(ANativeWindow* window, int operation, ...); + static int hook_query(const ANativeWindow* window, int what, int* value); + static int hook_queueBuffer(ANativeWindow* window, + ANativeWindowBuffer* buffer, int fenceFd); + static int hook_setSwapInterval(ANativeWindow* window, int interval); + + static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer); + static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer** buffer); + static int hook_lockBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer); + static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer); + + int dispatchConnect(va_list args); + int dispatchDisconnect(va_list args); + int dispatchSetBufferCount(va_list args); + int dispatchSetBuffersGeometry(va_list args); + int dispatchSetBuffersDimensions(va_list args); + int dispatchSetBuffersUserDimensions(va_list args); + int dispatchSetBuffersFormat(va_list args); + int dispatchSetScalingMode(va_list args); + int dispatchSetBuffersTransform(va_list args); + int dispatchSetBuffersTimestamp(va_list args); + int dispatchSetCrop(va_list args); + int dispatchSetPostTransformCrop(va_list args); + int dispatchSetUsage(va_list args); + int dispatchLock(va_list args); + int dispatchUnlockAndPost(va_list args); + +protected: + virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); + virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd); + virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd); + virtual int perform(int operation, va_list args); + virtual int query(int what, int* value) const; + virtual int setSwapInterval(int interval); + + virtual int lockBuffer_DEPRECATED(ANativeWindowBuffer* buffer); + + virtual int connect(int api); + virtual int disconnect(int api); + virtual int setBufferCount(int bufferCount); + virtual int setBuffersDimensions(int w, int h); + virtual int setBuffersUserDimensions(int w, int h); + virtual int setBuffersFormat(int format); + virtual int setScalingMode(int mode); + virtual int setBuffersTransform(int transform); + virtual int setBuffersTimestamp(int64_t timestamp); + virtual int setCrop(Rect const* rect); + virtual int setUsage(uint32_t reqUsage); + +public: + virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds); + virtual int unlockAndPost(); + +protected: + enum { NUM_BUFFER_SLOTS = GonkBufferQueue::NUM_BUFFER_SLOTS }; + enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 }; + +private: + void freeAllBuffers(); + int getSlotFromBufferLocked(android_native_buffer_t* buffer) const; + + struct BufferSlot { + sp buffer; + Region dirtyRegion; + }; + + // mSurfaceTexture is the interface to the surface texture server. All + // operations on the surface texture client ultimately translate into + // interactions with the server using this interface. + // TODO: rename to mBufferProducer + sp mGraphicBufferProducer; + + // mSlots stores the buffers that have been allocated for each buffer slot. + // It is initialized to null pointers, and gets filled in with the result of + // IGraphicBufferProducer::requestBuffer when the client dequeues a buffer from a + // slot that has not yet been used. The buffer allocated to a slot will also + // be replaced if the requested buffer usage or geometry differs from that + // of the buffer allocated to a slot. + BufferSlot mSlots[NUM_BUFFER_SLOTS]; + + // mReqWidth is the buffer width that will be requested at the next dequeue + // operation. It is initialized to 1. + uint32_t mReqWidth; + + // mReqHeight is the buffer height that will be requested at the next + // dequeue operation. It is initialized to 1. + uint32_t mReqHeight; + + // mReqFormat is the buffer pixel format that will be requested at the next + // deuque operation. It is initialized to PIXEL_FORMAT_RGBA_8888. + uint32_t mReqFormat; + + // mReqUsage is the set of buffer usage flags that will be requested + // at the next deuque operation. It is initialized to 0. + uint32_t mReqUsage; + + // mTimestamp is the timestamp that will be used for the next buffer queue + // operation. It defaults to NATIVE_WINDOW_TIMESTAMP_AUTO, which means that + // a timestamp is auto-generated when queueBuffer is called. + int64_t mTimestamp; + + // mCrop is the crop rectangle that will be used for the next buffer + // that gets queued. It is set by calling setCrop. + Rect mCrop; + + // mScalingMode is the scaling mode that will be used for the next + // buffers that get queued. It is set by calling setScalingMode. + int mScalingMode; + + // mTransform is the transform identifier that will be used for the next + // buffer that gets queued. It is set by calling setTransform. + uint32_t mTransform; + + // mDefaultWidth is default width of the buffers, regardless of the + // native_window_set_buffers_dimensions call. + uint32_t mDefaultWidth; + + // mDefaultHeight is default height of the buffers, regardless of the + // native_window_set_buffers_dimensions call. + uint32_t mDefaultHeight; + + // mUserWidth, if non-zero, is an application-specified override + // of mDefaultWidth. This is lower priority than the width set by + // native_window_set_buffers_dimensions. + uint32_t mUserWidth; + + // mUserHeight, if non-zero, is an application-specified override + // of mDefaultHeight. This is lower priority than the height set + // by native_window_set_buffers_dimensions. + uint32_t mUserHeight; + + // mTransformHint is the transform probably applied to buffers of this + // window. this is only a hint, actual transform may differ. + uint32_t mTransformHint; + + // mProducerControlledByApp whether this buffer producer is controlled + // by the application + bool mProducerControlledByApp; + + // mSwapIntervalZero set if we should drop buffers at queue() time to + // achieve an asynchronous swap interval + bool mSwapIntervalZero; + + // mConsumerRunningBehind whether the consumer is running more than + // one buffer behind the producer. + mutable bool mConsumerRunningBehind; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of GonkNativeWindowClient objects. It must be locked whenever the + // member variables are accessed. + mutable Mutex mMutex; + + // must be used from the lock/unlock thread + sp mLockedBuffer; + sp mPostedBuffer; + bool mConnectedToCpu; + + // must be accessed from lock/unlock thread only + Region mDirtyRegion; +}; + +}; // namespace android + +#endif // NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_JB_H diff --git a/widget/gonk/nativewindow/GonkNativeWindowJB.h b/widget/gonk/nativewindow/GonkNativeWindowJB.h index a3ec57b33f54..090a3ac6604d 100755 --- a/widget/gonk/nativewindow/GonkNativeWindowJB.h +++ b/widget/gonk/nativewindow/GonkNativeWindowJB.h @@ -24,7 +24,7 @@ #include #include "CameraCommon.h" -#include "GonkConsumerBase.h" +#include "GonkConsumerBaseJB.h" #include "GrallocImages.h" #include "mozilla/layers/ImageBridgeChild.h" #include "mozilla/layers/LayersSurfaces.h" diff --git a/widget/gonk/nativewindow/GonkNativeWindowKK.cpp b/widget/gonk/nativewindow/GonkNativeWindowKK.cpp new file mode 100644 index 000000000000..888d8cdfd974 --- /dev/null +++ b/widget/gonk/nativewindow/GonkNativeWindowKK.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "GonkNativeWindow" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include + +#include "GonkNativeWindowKK.h" +#include "GrallocImages.h" + +#define BI_LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +#define BI_LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#define BI_LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define BI_LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) +#define BI_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) + +using namespace mozilla::layers; + +namespace android { + +GonkNativeWindow::GonkNativeWindow() : + GonkConsumerBase(new GonkBufferQueue(true), false) +{ + mConsumer->setMaxAcquiredBufferCount(GonkBufferQueue::MIN_UNDEQUEUED_BUFFERS); +} + +GonkNativeWindow::GonkNativeWindow(const sp& bq, + uint32_t consumerUsage, int bufferCount, bool controlledByApp) : + GonkConsumerBase(bq, controlledByApp) +{ + mConsumer->setConsumerUsageBits(consumerUsage); + mConsumer->setMaxAcquiredBufferCount(bufferCount); +} + +GonkNativeWindow::~GonkNativeWindow() { +} + +void GonkNativeWindow::setName(const String8& name) { + Mutex::Autolock _l(mMutex); + mName = name; + mConsumer->setConsumerName(name); +} + +status_t GonkNativeWindow::acquireBuffer(BufferItem *item, + nsecs_t presentWhen, bool waitForFence) { + status_t err; + + if (!item) return BAD_VALUE; + + Mutex::Autolock _l(mMutex); + + err = acquireBufferLocked(item, presentWhen); + if (err != OK) { + if (err != NO_BUFFER_AVAILABLE) { + BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); + } + return err; + } + + if (waitForFence) { + err = item->mFence->waitForever("GonkNativeWindow::acquireBuffer"); + if (err != OK) { + BI_LOGE("Failed to wait for fence of acquired buffer: %s (%d)", + strerror(-err), err); + return err; + } + } + + item->mGraphicBuffer = mSlots[item->mBuf].mGraphicBuffer; + + return OK; +} + +status_t GonkNativeWindow::releaseBuffer(const BufferItem &item, + const sp& releaseFence) { + status_t err; + + Mutex::Autolock _l(mMutex); + + err = addReleaseFenceLocked(item.mBuf, item.mGraphicBuffer, releaseFence); + + err = releaseBufferLocked(item.mBuf, item.mGraphicBuffer); + if (err != OK) { + BI_LOGE("Failed to release buffer: %s (%d)", + strerror(-err), err); + } + return err; +} + +status_t GonkNativeWindow::setDefaultBufferSize(uint32_t w, uint32_t h) { + Mutex::Autolock _l(mMutex); + return mConsumer->setDefaultBufferSize(w, h); +} + +status_t GonkNativeWindow::setDefaultBufferFormat(uint32_t defaultFormat) { + Mutex::Autolock _l(mMutex); + return mConsumer->setDefaultBufferFormat(defaultFormat); +} + +already_AddRefed +GonkNativeWindow::getCurrentBuffer() +{ + Mutex::Autolock _l(mMutex); + BufferItem item; + + // In asynchronous mode the list is guaranteed to be one buffer + // deep, while in synchronous mode we use the oldest buffer. + status_t err = acquireBufferLocked(&item, 0); //??? + + if (err != NO_ERROR) { + return NULL; + } + + nsRefPtr ret = + new CameraGraphicBuffer(this, item.mBuf, mConsumer->getGeneration(), item.mSurfaceDescriptor); + + return ret.forget(); +} + +bool +GonkNativeWindow::returnBuffer(uint32_t aIndex, uint32_t aGeneration) { + BI_LOGD("GonkNativeWindow::returnBuffer: slot=%d (generation=%d)", aIndex, aGeneration); + Mutex::Autolock lock(mMutex); + + if (aGeneration != mConsumer->getGeneration()) { + BI_LOGD("returnBuffer: buffer is from generation %d (current is %d)", + aGeneration, mConsumer->getGeneration()); + return false; + } + status_t err = releaseBufferLocked(aIndex, mSlots[aIndex].mGraphicBuffer); + + if (err != NO_ERROR) { + return false; + } + return true; +} + +mozilla::layers::SurfaceDescriptor * +GonkNativeWindow::getSurfaceDescriptorFromBuffer(ANativeWindowBuffer* buffer) +{ + Mutex::Autolock lock(mMutex); + + return mConsumer->getSurfaceDescriptorFromBuffer(buffer); +} +void GonkNativeWindow::setNewFrameCallback( + GonkNativeWindowNewFrameCallback* aCallback) { + BI_LOGD("setNewFrameCallback"); + Mutex::Autolock lock(mMutex); + mNewFrameCallback = aCallback; +} + +void GonkNativeWindow::onFrameAvailable() { + GonkConsumerBase::onFrameAvailable(); + + if (mNewFrameCallback) { + mNewFrameCallback->OnNewFrame(); + } +} + +} // namespace android diff --git a/widget/gonk/nativewindow/GonkNativeWindowKK.h b/widget/gonk/nativewindow/GonkNativeWindowKK.h new file mode 100644 index 000000000000..5ef0be60b0fc --- /dev/null +++ b/widget/gonk/nativewindow/GonkNativeWindowKK.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NATIVEWINDOW_GONKNATIVEWINDOW_KK_H +#define NATIVEWINDOW_GONKNATIVEWINDOW_KK_H + +#include +#include +#include +#include + +#include "CameraCommon.h" +#include "GonkConsumerBaseKK.h" +#include "GrallocImages.h" +#include "IGonkGraphicBufferConsumer.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/LayersSurfaces.h" + +namespace mozilla { +namespace layers { + class PGrallocBufferChild; +} +} + +namespace android { + +// The user of GonkNativeWindow who wants to receive notification of +// new frames should implement this interface. +class GonkNativeWindowNewFrameCallback { +public: + virtual void OnNewFrame() = 0; +}; + +/** + * GonkNativeWindow is a GonkBufferQueue consumer endpoint that allows clients + * access to the whole BufferItem entry from GonkBufferQueue. Multiple buffers may + * be acquired at once, to be used concurrently by the client. This consumer can + * operate either in synchronous or asynchronous mode. + */ +class GonkNativeWindow: public GonkConsumerBase +{ + typedef mozilla::layers::GraphicBufferLocked GraphicBufferLocked; + typedef mozilla::layers::SurfaceDescriptor SurfaceDescriptor; + public: + typedef GonkConsumerBase::FrameAvailableListener FrameAvailableListener; + typedef IGonkGraphicBufferConsumer::BufferItem BufferItem; + + enum { INVALID_BUFFER_SLOT = GonkBufferQueue::INVALID_BUFFER_SLOT }; + enum { NO_BUFFER_AVAILABLE = GonkBufferQueue::NO_BUFFER_AVAILABLE }; + + // Create a new buffer item consumer. The consumerUsage parameter determines + // the consumer usage flags passed to the graphics allocator. The + // bufferCount parameter specifies how many buffers can be locked for user + // access at the same time. + // controlledByApp tells whether this consumer is controlled by the + // application. + GonkNativeWindow(); + GonkNativeWindow(const sp& bq, uint32_t consumerUsage, + int bufferCount = GonkBufferQueue::MIN_UNDEQUEUED_BUFFERS, + bool controlledByApp = false); + + virtual ~GonkNativeWindow(); + + // set the name of the GonkNativeWindow that will be used to identify it in + // log messages. + void setName(const String8& name); + + // Gets the next graphics buffer from the producer, filling out the + // passed-in BufferItem structure. Returns NO_BUFFER_AVAILABLE if the queue + // of buffers is empty, and INVALID_OPERATION if the maximum number of + // buffers is already acquired. + // + // Only a fixed number of buffers can be acquired at a time, determined by + // the construction-time bufferCount parameter. If INVALID_OPERATION is + // returned by acquireBuffer, then old buffers must be returned to the + // queue by calling releaseBuffer before more buffers can be acquired. + // + // If waitForFence is true, and the acquired BufferItem has a valid fence object, + // acquireBuffer will wait on the fence with no timeout before returning. + status_t acquireBuffer(BufferItem *item, nsecs_t presentWhen, + bool waitForFence = true); + + // Returns an acquired buffer to the queue, allowing it to be reused. Since + // only a fixed number of buffers may be acquired at a time, old buffers + // must be released by calling releaseBuffer to ensure new buffers can be + // acquired by acquireBuffer. Once a BufferItem is released, the caller must + // not access any members of the BufferItem, and should immediately remove + // all of its references to the BufferItem itself. + status_t releaseBuffer(const BufferItem &item, + const sp& releaseFence = Fence::NO_FENCE); + + // setDefaultBufferSize is used to set the size of buffers returned by + // requestBuffers when a with and height of zero is requested. + status_t setDefaultBufferSize(uint32_t w, uint32_t h); + + // setDefaultBufferFormat allows the BufferQueue to create + // GraphicBuffers of a defaultFormat if no format is specified + // in dequeueBuffer + status_t setDefaultBufferFormat(uint32_t defaultFormat); + + // Get next frame from the queue, caller owns the returned buffer. + already_AddRefed getCurrentBuffer(); + + // Return the buffer to the queue and mark it as FREE. After that + // the buffer is useable again for the decoder. + bool returnBuffer(uint32_t index, uint32_t generation); + + SurfaceDescriptor* getSurfaceDescriptorFromBuffer(ANativeWindowBuffer* buffer); + + void setNewFrameCallback(GonkNativeWindowNewFrameCallback* aCallback); + +protected: + virtual void onFrameAvailable(); + +private: + GonkNativeWindowNewFrameCallback* mNewFrameCallback; +}; + +// CameraGraphicBuffer maintains the buffer returned from GonkNativeWindow +class CameraGraphicBuffer : public mozilla::layers::GraphicBufferLocked +{ + typedef mozilla::layers::SurfaceDescriptor SurfaceDescriptor; + typedef mozilla::layers::ImageBridgeChild ImageBridgeChild; + +public: + CameraGraphicBuffer(GonkNativeWindow* aNativeWindow, + uint32_t aIndex, + uint32_t aGeneration, + SurfaceDescriptor aBuffer) + : GraphicBufferLocked(aBuffer) + , mNativeWindow(aNativeWindow) + , mIndex(aIndex) + , mGeneration(aGeneration) + , mLocked(true) + { + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + } + + virtual ~CameraGraphicBuffer() + { + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + } + + // Unlock either returns the buffer to the native window or + // destroys the buffer if the window is already released. + virtual void Unlock() MOZ_OVERRIDE + { + if (mLocked) { + // The window might have been destroyed. The buffer is no longer + // valid at that point. + sp window = mNativeWindow.promote(); + if (window.get() && window->returnBuffer(mIndex, mGeneration)) { + mLocked = false; + } else { + // If the window doesn't exist any more, release the buffer + // directly. + ImageBridgeChild *ibc = ImageBridgeChild::GetSingleton(); + ibc->DeallocSurfaceDescriptorGralloc(mSurfaceDescriptor); + } + } + } + +protected: + wp mNativeWindow; + uint32_t mIndex; + uint32_t mGeneration; + bool mLocked; +}; + +} // namespace android + +#endif // NATIVEWINDOW_GONKNATIVEWINDOW_JB_H diff --git a/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.cpp b/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.cpp new file mode 100644 index 000000000000..a246aac69949 --- /dev/null +++ b/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.cpp @@ -0,0 +1,481 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define EGL_EGLEXT_PROTOTYPES + +#include +#include + +#include + +#include +#include + +#include +#include "IGonkGraphicBufferConsumer.h" + +#include +#include + +#include + +namespace android { +// --------------------------------------------------------------------------- + +IGonkGraphicBufferConsumer::BufferItem::BufferItem() : + mTransform(0), + mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mTimestamp(0), + mIsAutoTimestamp(false), + mFrameNumber(0), + mBuf(INVALID_BUFFER_SLOT), + mIsDroppable(false), + mAcquireCalled(false), + mTransformToDisplayInverse(false), + mSurfaceDescriptor(SurfaceDescriptor()) { + mCrop.makeInvalid(); +} + +size_t IGonkGraphicBufferConsumer::BufferItem::getPodSize() const { + size_t c = sizeof(mCrop) + + sizeof(mTransform) + + sizeof(mScalingMode) + + sizeof(mTimestamp) + + sizeof(mIsAutoTimestamp) + + sizeof(mFrameNumber) + + sizeof(mBuf) + + sizeof(mIsDroppable) + + sizeof(mAcquireCalled) + + sizeof(mTransformToDisplayInverse); + return c; +} + +size_t IGonkGraphicBufferConsumer::BufferItem::getFlattenedSize() const { + size_t c = 0; + if (mGraphicBuffer != 0) { + c += mGraphicBuffer->getFlattenedSize(); + FlattenableUtils::align<4>(c); + } + if (mFence != 0) { + c += mFence->getFlattenedSize(); + FlattenableUtils::align<4>(c); + } + return sizeof(int32_t) + c + getPodSize(); +} + +size_t IGonkGraphicBufferConsumer::BufferItem::getFdCount() const { + size_t c = 0; + if (mGraphicBuffer != 0) { + c += mGraphicBuffer->getFdCount(); + } + if (mFence != 0) { + c += mFence->getFdCount(); + } + return c; +} + +status_t IGonkGraphicBufferConsumer::BufferItem::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { + + // make sure we have enough space + if (count < BufferItem::getFlattenedSize()) { + return NO_MEMORY; + } + + // content flags are stored first + uint32_t& flags = *static_cast(buffer); + + // advance the pointer + FlattenableUtils::advance(buffer, size, sizeof(uint32_t)); + + flags = 0; + if (mGraphicBuffer != 0) { + status_t err = mGraphicBuffer->flatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + flags |= 1; + } + if (mFence != 0) { + status_t err = mFence->flatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + flags |= 2; + } + + // check we have enough space (in case flattening the fence/graphicbuffer lied to us) + if (size < getPodSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, mCrop); + FlattenableUtils::write(buffer, size, mTransform); + FlattenableUtils::write(buffer, size, mScalingMode); + FlattenableUtils::write(buffer, size, mTimestamp); + FlattenableUtils::write(buffer, size, mIsAutoTimestamp); + FlattenableUtils::write(buffer, size, mFrameNumber); + FlattenableUtils::write(buffer, size, mBuf); + FlattenableUtils::write(buffer, size, mIsDroppable); + FlattenableUtils::write(buffer, size, mAcquireCalled); + FlattenableUtils::write(buffer, size, mTransformToDisplayInverse); + + return NO_ERROR; +} + +status_t IGonkGraphicBufferConsumer::BufferItem::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { + + if (size < sizeof(uint32_t)) + return NO_MEMORY; + + uint32_t flags = 0; + FlattenableUtils::read(buffer, size, flags); + + if (flags & 1) { + mGraphicBuffer = new GraphicBuffer(); + status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + } + + if (flags & 2) { + mFence = new Fence(); + status_t err = mFence->unflatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + } + + // check we have enough space + if (size < getPodSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, mCrop); + FlattenableUtils::read(buffer, size, mTransform); + FlattenableUtils::read(buffer, size, mScalingMode); + FlattenableUtils::read(buffer, size, mTimestamp); + FlattenableUtils::read(buffer, size, mIsAutoTimestamp); + FlattenableUtils::read(buffer, size, mFrameNumber); + FlattenableUtils::read(buffer, size, mBuf); + FlattenableUtils::read(buffer, size, mIsDroppable); + FlattenableUtils::read(buffer, size, mAcquireCalled); + FlattenableUtils::read(buffer, size, mTransformToDisplayInverse); + + return NO_ERROR; +} + +// --------------------------------------------------------------------------- + +enum { + ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION, + RELEASE_BUFFER, + CONSUMER_CONNECT, + CONSUMER_DISCONNECT, + GET_RELEASED_BUFFERS, + SET_DEFAULT_BUFFER_SIZE, + SET_DEFAULT_MAX_BUFFER_COUNT, + DISABLE_ASYNC_BUFFER, + SET_MAX_ACQUIRED_BUFFER_COUNT, + SET_CONSUMER_NAME, + SET_DEFAULT_BUFFER_FORMAT, + SET_CONSUMER_USAGE_BITS, + SET_TRANSFORM_HINT, + DUMP, +}; + +class BpGonkGraphicBufferConsumer : public BpInterface +{ +public: + BpGonkGraphicBufferConsumer(const sp& impl) + : BpInterface(impl) + { + } + + virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt64(presentWhen); + status_t result = remote()->transact(ACQUIRE_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.read(*buffer); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t releaseBuffer(int buf, uint64_t frameNumber, + const sp& releaseFence) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(buf); + data.writeInt64(frameNumber); + data.write(*releaseFence); + status_t result = remote()->transact(RELEASE_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t consumerConnect(const sp& consumer, bool controlledByApp) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeStrongBinder(consumer->asBinder()); + data.writeInt32(controlledByApp); + status_t result = remote()->transact(CONSUMER_CONNECT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t consumerDisconnect() { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + status_t result = remote()->transact(CONSUMER_DISCONNECT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t getReleasedBuffers(uint32_t* slotMask) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + status_t result = remote()->transact(GET_RELEASED_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + *slotMask = reply.readInt32(); + return reply.readInt32(); + } + + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(w); + data.writeInt32(h); + status_t result = remote()->transact(SET_DEFAULT_BUFFER_SIZE, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setDefaultMaxBufferCount(int bufferCount) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(bufferCount); + status_t result = remote()->transact(SET_DEFAULT_MAX_BUFFER_COUNT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t disableAsyncBuffer() { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + status_t result = remote()->transact(DISABLE_ASYNC_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(maxAcquiredBuffers); + status_t result = remote()->transact(SET_MAX_ACQUIRED_BUFFER_COUNT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual void setConsumerName(const String8& name) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeString8(name); + remote()->transact(SET_CONSUMER_NAME, data, &reply); + } + + virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(defaultFormat); + status_t result = remote()->transact(SET_DEFAULT_BUFFER_FORMAT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setConsumerUsageBits(uint32_t usage) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(usage); + status_t result = remote()->transact(SET_CONSUMER_USAGE_BITS, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setTransformHint(uint32_t hint) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(hint); + status_t result = remote()->transact(SET_TRANSFORM_HINT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual void dump(String8& result, const char* prefix) const { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeString8(result); + data.writeString8(String8(prefix ? prefix : "")); + remote()->transact(DUMP, data, &reply); + reply.readString8(); + } +}; + +IMPLEMENT_META_INTERFACE(GonkGraphicBufferConsumer, "android.gui.IGonkGraphicBufferConsumer"); +// ---------------------------------------------------------------------- + +status_t BnGonkGraphicBufferConsumer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case ACQUIRE_BUFFER: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + BufferItem item; + int64_t presentWhen = data.readInt64(); + status_t result = acquireBuffer(&item, presentWhen); + status_t err = reply->write(item); + if (err) return err; + reply->writeInt32(result); + return NO_ERROR; + } break; + case RELEASE_BUFFER: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + int buf = data.readInt32(); + uint64_t frameNumber = data.readInt64(); + sp releaseFence = new Fence(); + status_t err = data.read(*releaseFence); + if (err) return err; + status_t result = releaseBuffer(buf, frameNumber, releaseFence); + reply->writeInt32(result); + return NO_ERROR; + } break; + + case CONSUMER_CONNECT: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + sp consumer = IConsumerListener::asInterface( data.readStrongBinder() ); + bool controlledByApp = data.readInt32(); + status_t result = consumerConnect(consumer, controlledByApp); + reply->writeInt32(result); + return NO_ERROR; + } break; + + case CONSUMER_DISCONNECT: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + status_t result = consumerDisconnect(); + reply->writeInt32(result); + return NO_ERROR; + } break; + case GET_RELEASED_BUFFERS: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint32_t slotMask; + status_t result = getReleasedBuffers(&slotMask); + reply->writeInt32(slotMask); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_DEFAULT_BUFFER_SIZE: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + status_t result = setDefaultBufferSize(w, h); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_DEFAULT_MAX_BUFFER_COUNT: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint32_t bufferCount = data.readInt32(); + status_t result = setDefaultMaxBufferCount(bufferCount); + reply->writeInt32(result); + return NO_ERROR; + } break; + case DISABLE_ASYNC_BUFFER: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + status_t result = disableAsyncBuffer(); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_MAX_ACQUIRED_BUFFER_COUNT: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint32_t maxAcquiredBuffers = data.readInt32(); + status_t result = setMaxAcquiredBufferCount(maxAcquiredBuffers); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_CONSUMER_NAME: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + setConsumerName( data.readString8() ); + return NO_ERROR; + } break; + case SET_DEFAULT_BUFFER_FORMAT: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint32_t defaultFormat = data.readInt32(); + status_t result = setDefaultBufferFormat(defaultFormat); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_CONSUMER_USAGE_BITS: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint32_t usage = data.readInt32(); + status_t result = setConsumerUsageBits(usage); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_TRANSFORM_HINT: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint32_t hint = data.readInt32(); + status_t result = setTransformHint(hint); + reply->writeInt32(result); + return NO_ERROR; + } break; + + case DUMP: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + String8 result = data.readString8(); + String8 prefix = data.readString8(); + static_cast(this)->dump(result, prefix); + reply->writeString8(result); + return NO_ERROR; + } + } + return BBinder::onTransact(code, data, reply, flags); +} + +}; // namespace android diff --git a/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.h b/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.h new file mode 100644 index 000000000000..1d43a3dc4c56 --- /dev/null +++ b/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.h @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_GUI_IGONKGRAPHICBUFFERCONSUMER_H +#define ANDROID_GUI_IGONKGRAPHICBUFFERCONSUMER_H + +#include +#include + +#include +#include +#include + +#include +#include + +#include "mozilla/layers/LayersSurfaces.h" + +namespace android { +// ---------------------------------------------------------------------------- + +class IConsumerListener; +class GraphicBuffer; +class Fence; + +class IGonkGraphicBufferConsumer : public IInterface { + typedef mozilla::layers::SurfaceDescriptor SurfaceDescriptor; +public: + + // public facing structure for BufferSlot + class BufferItem : public Flattenable { + friend class Flattenable; + size_t getPodSize() const; + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + + public: + enum { INVALID_BUFFER_SLOT = -1 }; + BufferItem(); + + // mGraphicBuffer points to the buffer allocated for this slot, or is NULL + // if the buffer in this slot has been acquired in the past (see + // BufferSlot.mAcquireCalled). + sp mGraphicBuffer; + + // mFence is a fence that will signal when the buffer is idle. + sp mFence; + + // mCrop is the current crop rectangle for this buffer slot. + Rect mCrop; + + // mTransform is the current transform flags for this buffer slot. + uint32_t mTransform; + + // mScalingMode is the current scaling mode for this buffer slot. + uint32_t mScalingMode; + + // mTimestamp is the current timestamp for this buffer slot. This gets + // to set by queueBuffer each time this slot is queued. + int64_t mTimestamp; + + // mIsAutoTimestamp indicates whether mTimestamp was generated + // automatically when the buffer was queued. + bool mIsAutoTimestamp; + + // mFrameNumber is the number of the queued frame for this slot. + uint64_t mFrameNumber; + + // mBuf is the slot index of this buffer + int mBuf; + + // mIsDroppable whether this buffer was queued with the + // property that it can be replaced by a new buffer for the purpose of + // making sure dequeueBuffer() won't block. + // i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer + // was queued. + bool mIsDroppable; + + // Indicates whether this buffer has been seen by a consumer yet + bool mAcquireCalled; + + // Indicates this buffer must be transformed by the inverse transform of the screen + // it is displayed onto. This is applied after mTransform. + bool mTransformToDisplayInverse; + + // mSurfaceDescriptor is the token to remotely allocated GraphicBuffer. + SurfaceDescriptor mSurfaceDescriptor; + }; + + + // acquireBuffer attempts to acquire ownership of the next pending buffer in + // the BufferQueue. If no buffer is pending then it returns -EINVAL. If a + // buffer is successfully acquired, the information about the buffer is + // returned in BufferItem. If the buffer returned had previously been + // acquired then the BufferItem::mGraphicBuffer field of buffer is set to + // NULL and it is assumed that the consumer still holds a reference to the + // buffer. + // + // If presentWhen is nonzero, it indicates the time when the buffer will + // be displayed on screen. If the buffer's timestamp is farther in the + // future, the buffer won't be acquired, and PRESENT_LATER will be + // returned. The presentation time is in nanoseconds, and the time base + // is CLOCK_MONOTONIC. + virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) = 0; + + // releaseBuffer releases a buffer slot from the consumer back to the + // BufferQueue. This may be done while the buffer's contents are still + // being accessed. The fence will signal when the buffer is no longer + // in use. frameNumber is used to indentify the exact buffer returned. + // + // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free + // any references to the just-released buffer that it might have, as if it + // had received a onBuffersReleased() call with a mask set for the released + // buffer. + // + // Note that the dependencies on EGL will be removed once we switch to using + // the Android HW Sync HAL. + virtual status_t releaseBuffer(int buf, uint64_t frameNumber, const sp& releaseFence) = 0; + + // consumerConnect connects a consumer to the BufferQueue. Only one + // consumer may be connected, and when that consumer disconnects the + // BufferQueue is placed into the "abandoned" state, causing most + // interactions with the BufferQueue by the producer to fail. + // controlledByApp indicates whether the consumer is controlled by + // the application. + // + // consumer may not be NULL. + virtual status_t consumerConnect(const sp& consumer, bool controlledByApp) = 0; + + // consumerDisconnect disconnects a consumer from the BufferQueue. All + // buffers will be freed and the BufferQueue is placed in the "abandoned" + // state, causing most interactions with the BufferQueue by the producer to + // fail. + virtual status_t consumerDisconnect() = 0; + + // getReleasedBuffers sets the value pointed to by slotMask to a bit mask + // indicating which buffer slots have been released by the BufferQueue + // but have not yet been released by the consumer. + // + // This should be called from the onBuffersReleased() callback. + virtual status_t getReleasedBuffers(uint32_t* slotMask) = 0; + + // setDefaultBufferSize is used to set the size of buffers returned by + // dequeueBuffer when a width and height of zero is requested. Default + // is 1x1. + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0; + + // setDefaultMaxBufferCount sets the default value for the maximum buffer + // count (the initial default is 2). If the producer has requested a + // buffer count using setBufferCount, the default buffer count will only + // take effect if the producer sets the count back to zero. + // + // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. + virtual status_t setDefaultMaxBufferCount(int bufferCount) = 0; + + // disableAsyncBuffer disables the extra buffer used in async mode + // (when both producer and consumer have set their "isControlledByApp" + // flag) and has dequeueBuffer() return WOULD_BLOCK instead. + // + // This can only be called before consumerConnect(). + virtual status_t disableAsyncBuffer() = 0; + + // setMaxAcquiredBufferCount sets the maximum number of buffers that can + // be acquired by the consumer at one time (default 1). This call will + // fail if a producer is connected to the BufferQueue. + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0; + + // setConsumerName sets the name used in logging + virtual void setConsumerName(const String8& name) = 0; + + // setDefaultBufferFormat allows the BufferQueue to create + // GraphicBuffers of a defaultFormat if no format is specified + // in dequeueBuffer. Formats are enumerated in graphics.h; the + // initial default is HAL_PIXEL_FORMAT_RGBA_8888. + virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) = 0; + + // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. + // These are merged with the bits passed to dequeueBuffer. The values are + // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0. + virtual status_t setConsumerUsageBits(uint32_t usage) = 0; + + // setTransformHint bakes in rotation to buffers so overlays can be used. + // The values are enumerated in window.h, e.g. + // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform). + virtual status_t setTransformHint(uint32_t hint) = 0; + + // dump state into a string + virtual void dump(String8& result, const char* prefix) const = 0; + +public: + DECLARE_META_INTERFACE(GonkGraphicBufferConsumer); +}; + +// ---------------------------------------------------------------------------- + +class BnGonkGraphicBufferConsumer : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H diff --git a/widget/gonk/nativewindow/moz.build b/widget/gonk/nativewindow/moz.build index 06111ff8d499..33ee66d6f89f 100644 --- a/widget/gonk/nativewindow/moz.build +++ b/widget/gonk/nativewindow/moz.build @@ -15,14 +15,23 @@ # limitations under the License. EXPORTS += [ + 'GonkBufferQueue.h', 'GonkNativeWindow.h', 'GonkNativeWindowClient.h', ] -if CONFIG['ANDROID_VERSION'] in ('17', '18'): +if CONFIG['ANDROID_VERSION'] == '19': EXPORTS += [ - 'GonkBufferQueue.h', - 'GonkConsumerBase.h', + 'GonkBufferQueueKK.h', + 'GonkConsumerBaseKK.h', + 'GonkNativeWindowClientKK.h', + 'GonkNativeWindowKK.h', + 'IGonkGraphicBufferConsumer.h', + ] +elif CONFIG['ANDROID_VERSION'] in ('17', '18'): + EXPORTS += [ + 'GonkBufferQueueJB.h', + 'GonkConsumerBaseJB.h', 'GonkNativeWindowClientJB.h', 'GonkNativeWindowJB.h', ] @@ -33,10 +42,18 @@ elif CONFIG['ANDROID_VERSION'] == '15': ] if CONFIG['MOZ_B2G_CAMERA'] or CONFIG['MOZ_OMX_DECODER']: - if CONFIG['ANDROID_VERSION'] in ('17', '18'): + if CONFIG['ANDROID_VERSION'] == '19': SOURCES += [ - 'GonkBufferQueue.cpp', - 'GonkConsumerBase.cpp', + 'GonkBufferQueueKK.cpp', + 'GonkConsumerBaseKK.cpp', + 'GonkNativeWindowClientKK.cpp', + 'GonkNativeWindowKK.cpp', + 'IGonkGraphicBufferConsumer.cpp', + ] + elif CONFIG['ANDROID_VERSION'] in ('17', '18'): + SOURCES += [ + 'GonkBufferQueueJB.cpp', + 'GonkConsumerBaseJB.cpp', 'GonkNativeWindowClientJB.cpp', 'GonkNativeWindowJB.cpp', ]